• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 查找设备
2
3<!--Kit: Connectivity Kit-->
4<!--Subsystem: Communication-->
5<!--Owner: @enjoy_sunshine-->
6<!--Designer: @chengguohong; @tangjia15-->
7<!--Tester: @wangfeng517-->
8<!--Adviser: @zhang_yixin13-->
9
10## 简介
11本指南主要提供了BLE扫描和BLE广播相关操作的开发指导。可以实现发现周边BLE设备和其他设备发现本机设备的场景。
12
13## 开发步骤
14
15### 申请蓝牙权限
16需要申请权限ohos.permission.ACCESS_BLUETOOTH。如何配置和申请权限,请参考[声明权限](../../security/AccessToken/declare-permissions.md)和[向用户申请授权](../../security/AccessToken/request-user-authorization.md)。
17
18### 导入所需API模块
19导入ble和错误码模块。
20```ts
21import { ble } from '@kit.ConnectivityKit';
22import { BusinessError } from '@kit.BasicServicesKit';
23```
24
25### BLE扫描流程
26
27**1. 订阅扫描结果上报事件**<br>
28- 推荐使用API version 15开始支持的扫描方式,该方式支持应用发起和管理多路扫描。该方式支持的上报事件请参考[on('BLEDeviceFind')](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#onbledevicefind15)。
29```ts
30// 定义扫描结果上报回调函数
31function onReceiveEvent(scanReport: ble.ScanReport) {
32  console.info('BLE scan device find result: '+ JSON.stringify(scanReport));
33}
34
35// 创建ble扫描实例,可以管理该实例下创建的扫描流程
36let bleScanner: ble.BleScanner = ble.createBleScanner();
37
38try {
39  // 发起订阅
40  bleScanner.on('BLEDeviceFind', onReceiveEvent);
41} catch (err) {
42  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
43}
44```
45
46- API version 14及以前支持的扫描方式只支持应用发起单路扫描。该方式支持的上报事件请参考[ble.on('BLEDeviceFind')](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bleonbledevicefind)。
47```ts
48// 定义扫描结果上报回调函数
49function onReceiveEvent(data: Array<ble.ScanResult>) {
50  console.info('BLE scan device find result: '+ JSON.stringify(data));
51}
52
53try {
54  // 发起订阅
55  ble.on('BLEDeviceFind', onReceiveEvent);
56} catch (err) {
57  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
58}
59```
60
61- 如何解析扫描到的广播报文,具体可参考本章节[完整示例](#完整示例)。
62
63**2. 发起扫描**<br>
64通过BLE扫描周边其他设备发出的BLE广播,可以发现或者查找到应用需要的目标设备,适用于查找设备场景。
65
66若本机设备扫描到可连接的BLE广播,则可以和该设备进行通用属性协议(Generic Attribute Profile,GATT)的连接和数据传输,此时本机设备角色也被称为GATT客户端。具体操作请参考[连接和传输数据](gatt-development-guide.md)。
67
68- 推荐使用API version 15开始支持的扫描方式,该方式支持应用发起和管理多路扫描。可通过[createBleScanner](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blecreateblescanner15)创建扫描实例[BleScanner](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blescanner15),并调用[startScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#startscan15)。
69```ts
70// 创建ble扫描实例
71let bleScanner: ble.BleScanner = ble.createBleScanner();
72
73// 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件
74let manufactureId = 4567;
75let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]);
76let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]);
77let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器
78  manufactureId: manufactureId,
79  manufactureData: manufactureData.buffer,
80  manufactureDataMask: manufactureDataMask.buffer
81};
82
83// 构造扫描配置参数
84let scanOptions: ble.ScanOptions = {
85  interval: 0,
86  dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
87  matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
88}
89
90try {
91  // 发起扫描
92  bleScanner.startScan([scanFilter], scanOptions);
93  console.info('startBleScan success');
94} catch (err) {
95  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
96}
97```
98
99- API version 14及以前支持的扫描方式只支持应用发起单路扫描。若要再次发起扫描,必须先停止上一路的扫描流程。详情请见[ble.startBLEScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartblescan)。
100```ts
101// 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件
102let manufactureId = 4567;
103let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]);
104let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]);
105let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器
106  manufactureId: manufactureId,
107  manufactureData: manufactureData.buffer,
108  manufactureDataMask: manufactureDataMask.buffer
109};
110
111// 构造扫描配置参数
112let scanOptions: ble.ScanOptions = {
113  interval: 0,
114  dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
115  matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
116}
117try {
118  // 发起扫描
119  ble.startBLEScan([scanFilter], scanOptions);
120  console.info('startBleScan success');
121} catch (err) {
122  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
123}
124```
125
126**3. 停止扫描**<br>
127扫描流程会消耗蓝牙硬件资源和影响设备功耗。当应用不再需要该扫描时,需要主动停止。
128
129- 搭配API version 15开始支持的多路扫描方式。详情请见[stopScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#stopscan15)。
130```ts
131// 定义扫描结果上报回调函数
132function onReceiveEvent(scanReport: ble.ScanReport) {
133  console.info('BLE scan device find result: '+ JSON.stringify(scanReport));
134}
135
136// 创建ble扫描实例
137let bleScanner: ble.BleScanner = ble.createBleScanner();
138
139try {
140  bleScanner.off('BLEDeviceFind', onReceiveEvent);
141  // 停止扫描
142  bleScanner.stopScan();
143  console.info('stopBleScan success');
144} catch (err) {
145  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
146}
147```
148
149- 搭配API version 14及以前支持的单路扫描方式。详情请见[ble.stopBLEScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopblescan)。
150```ts
151// 定义扫描结果上报回调函数
152function onReceiveEvent(data: Array<ble.ScanResult>) {
153  console.info('BLE scan device find result: '+ JSON.stringify(data));
154}
155
156try {
157  // 取消订阅
158  ble.off('BLEDeviceFind', onReceiveEvent);
159  // 停止扫描
160  ble.stopBLEScan();
161} catch (err) {
162  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
163}
164```
165
166### BLE广播流程
167本机设备发送BLE广播后,可以实现被其他设备发现的功能。
168
169若本机设备发送的是可连接广播,则可以接受其他设备发起的通用属性协议(Generic Attribute Profile,GATT)连接,此时本机设备角色也被称为GATT服务端。具体操作请参考[连接和传输数据](gatt-development-guide.md)。
170
171推荐使用API version 11及以后开始支持的广播操作方式。
172
173**1. 订阅广播状态上报事件**<br>
174搭配API version 11开始支持的广播操作方式。
175```ts
176function onReceiveEvent(data: ble.AdvertisingStateChangeInfo) {
177    console.info('bluetooth advertising state = ' + JSON.stringify(data));
178}
179
180try {
181    ble.on('advertisingStateChange', onReceiveEvent);
182} catch (err) {
183    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
184}
185```
186
187**2. 启动广播**<br>
188- 推荐使用API version 11开始支持的广播操作方式。支持在不释放相关广播资源情况下,多次操作启动或者停止指定标识的广播,且支持设置广播持续发送的时间。<br>
189相关API请参考[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising11)和[ble.enableAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bleenableadvertising11)。<br>
190首次启动广播接口[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising11)会分配广播相关资源,从API version 15开始,该接口支持应用多次调用,实现启动多路广播的功能,并通过不同的广播标识进行管理。
191```ts
192// 设置广播发送的参数
193let setting: ble.AdvertiseSetting = {
194    interval: 160,
195    txPower: 0,
196    connectable: true // 发送支持连接的广播
197};
198// 构造广播数据
199let manufactureValueBuffer = new Uint8Array(4);
200manufactureValueBuffer[0] = 1;
201manufactureValueBuffer[1] = 2;
202manufactureValueBuffer[2] = 3;
203manufactureValueBuffer[3] = 4;
204let serviceValueBuffer = new Uint8Array(4);
205serviceValueBuffer[0] = 5;
206serviceValueBuffer[1] = 6;
207serviceValueBuffer[2] = 7;
208serviceValueBuffer[3] = 8;
209let manufactureDataUnit: ble.ManufactureData = {
210  manufactureId: 4567,
211  manufactureValue: manufactureValueBuffer.buffer
212};
213let serviceDataUnit1: ble.ServiceData = {
214  serviceUuid: "00001999-0000-1000-8000-00805f9b34fb",
215  serviceValue: serviceValueBuffer.buffer
216};
217let serviceDataUnit2: ble.ServiceData = {
218  serviceUuid: "19991999-0000-1000-8000-00805f9b34fb",
219  serviceValue: serviceValueBuffer.buffer
220};
221let advData: ble.AdvertiseData = {
222  serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"],
223  manufactureData: [manufactureDataUnit],
224  serviceData: [],
225  includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节,使得广播启动失败
226};
227let advResponse: ble.AdvertiseData = {
228  serviceUuids: [],
229  manufactureData: [],
230  serviceData: [serviceDataUnit1, serviceDataUnit2]
231};
232// 构造广播启动完整参数AdvertisingParams
233let advertisingParams: ble.AdvertisingParams = {
234  advertisingSettings: setting,
235  advertisingData: advData, // 注意: 广播报文长度不能超过31个字节
236  advertisingResponse: advResponse, // 注意: 广播报文长度不能超过31个字节
237  duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会停止,但分配的广播资源还在,可重新启动发送
238}
239
240let advHandle = 0xFF; // 定义广播标识
241
242// 首次启动广播,蓝牙子系统会分配相关资源,包括应用获取到的广播的标识ID
243try {
244  ble.startAdvertising(advertisingParams, (err, outAdvHandle) => {
245    if (err) {
246      return;
247    } else {
248      advHandle = outAdvHandle;
249      console.info("advHandle: " + advHandle);
250    }
251  });
252} catch (err) {
253    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
254}
255
256// 构造启动广播参数
257let advertisingEnableParams: ble.AdvertisingEnableParams = {
258  advertisingId: advHandle, // 使用首次启动广播时获取到的广播标识ID
259  duration: 300
260}
261try {
262  // 再次启动
263  ble.enableAdvertising(advertisingEnableParams, (err) => {
264    if (err) {
265      return;
266    }
267  });
268} catch (err) {
269  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
270}
271```
272
273- API version 10及以前支持的广播操作方式只支持应用启动单路广播。若要再次启动广播,必须先停止上一路的广播流程。详情请见[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising)。
274```ts
275// 设置广播发送的参数
276let setting: ble.AdvertiseSetting = {
277    interval: 160,
278    txPower: 0,
279    connectable: true
280};
281// 构造广播数据
282let manufactureValueBuffer = new Uint8Array(4);
283manufactureValueBuffer[0] = 1;
284manufactureValueBuffer[1] = 2;
285manufactureValueBuffer[2] = 3;
286manufactureValueBuffer[3] = 4;
287let serviceValueBuffer = new Uint8Array(4);
288serviceValueBuffer[0] = 5;
289serviceValueBuffer[1] = 6;
290serviceValueBuffer[2] = 7;
291serviceValueBuffer[3] = 8;
292let manufactureDataUnit: ble.ManufactureData = {
293  manufactureId: 4567,
294  manufactureValue: manufactureValueBuffer.buffer
295};
296let serviceDataUnit1: ble.ServiceData = {
297  serviceUuid: "00001999-0000-1000-8000-00805f9b34fb",
298  serviceValue: serviceValueBuffer.buffer
299};
300let serviceDataUnit2: ble.ServiceData = {
301  serviceUuid: "19991999-0000-1000-8000-00805f9b34fb",
302  serviceValue: serviceValueBuffer.buffer
303};
304let advData: ble.AdvertiseData = {
305  serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"],
306  manufactureData: [manufactureDataUnit],
307  serviceData: [],
308  includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节
309};
310let advResponse: ble.AdvertiseData = {
311  serviceUuids: [],
312  manufactureData: [],
313  serviceData: [serviceDataUnit1, serviceDataUnit2]
314};
315try {
316  // 启动广播
317  ble.startAdvertising(setting, advData ,advResponse);
318} catch (err) {
319  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
320}
321```
322
323**3. 停止广播**<br>
324广播流程会消耗蓝牙硬件资源和影响设备功耗。当应用不再需要该广播时,需要主动停止。
325
326- 搭配API version 11开始支持的广播方式。相关API请参考[ble.disableAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bledisableadvertising11)和[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising11)。<br>
327完全停止广播接口[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising11)会释放所有广播资源,因此首次启动广播分配的广播标识将无效。
328```ts
329let advHandle = 1; // 注意:该值是首次启动广播时获取到的广播标识,此处是伪代码ID
330
331// 构造停止广播参数
332let advertisingDisableParams: ble.AdvertisingDisableParams = {
333    advertisingId: advHandle // 使用首次启动广播时获取到的广播标识ID
334}
335try {
336  // 停止
337  ble.disableAdvertising(advertisingDisableParams, (err) => {
338    if (err) {
339      return;
340    }
341  });
342} catch (err) {
343  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
344}
345
346try {
347  // 完全停止
348  ble.stopAdvertising(advHandle, (err) => {
349    if (err) {
350      return;
351    }
352  });
353} catch (err) {
354  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
355}
356
357```
358
359- 搭配API version 11及以前支持的单路广播方式。相关API请参考[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising)。
360```ts
361try {
362  // 停止
363  ble.stopAdvertising();
364} catch (err) {
365  console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
366}
367```
368
369## 完整示例
370
371### BLE扫描流程
372```ts
373import { ble } from '@kit.ConnectivityKit';
374import { BusinessError } from '@ohos.base';
375
376const TAG: string = 'BleAdvertisingManager';
377
378// 参考蓝牙标准协议规范Core Assigned Numbers
379const BLE_ADV_TYPE_FLAG = 0x01;
380const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE = 0x02;
381const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE = 0x03;
382const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE = 0x04;
383const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE = 0x05;
384const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE = 0x06;
385const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE = 0x07;
386const BLE_ADV_TYPE_LOCAL_NAME_SHORT = 0x08;
387const BLE_ADV_TYPE_LOCAL_NAME_COMPLETE = 0x09;
388const BLE_ADV_TYPE_TX_POWER_LEVEL = 0x0A;
389const BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14;
390const BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15;
391const BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F;
392const BLE_ADV_TYPE_16_BIT_SERVICE_DATA = 0x16;
393const BLE_ADV_TYPE_32_BIT_SERVICE_DATA = 0x20;
394const BLE_ADV_TYPE_128_BIT_SERVICE_DATA = 0x21;
395const BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
396
397const BLUETOOTH_UUID_16_BIT_LENGTH = 2;
398const BLUETOOTH_UUID_32_BIT_LENGTH = 4;
399const BLUETOOTH_UUID_128_BIT_LENGTH = 16;
400
401const BLUETOOTH_MANUFACTURE_ID_LENGTH = 2;
402
403export class BleScanManager {
404  bleScanner: ble.BleScanner = ble.createBleScanner();
405
406  // 1. 定义扫描结果上报回调函数
407  onReceiveEvent = (scanReport: ble.ScanReport) => {
408    console.info(TAG, 'BLE scan device find result: '+ JSON.stringify(scanReport));
409    if (scanReport.scanResult.length > 0) {
410      console.info(TAG, 'BLE scan result: ' + scanReport.scanResult[0].deviceId);
411      this.parseScanResult(scanReport.scanResult[0].data);
412    }
413  };
414
415  public parseScanResult(data: ArrayBuffer) {
416    let advData = new Uint8Array(data);
417    if (advData.byteLength == 0) {
418      console.warn(TAG, 'adv data length is 0');
419      return;
420    }
421
422    let advertiseFlags: number = -1;
423    let txPowerLevel: number = -1;
424    let localName: string = '';
425    let serviceUuids: string[] = [];
426    let serviceSolicitationUuids: string[] = [];
427    let manufactureSpecificDatas: Record<number, Uint8Array> = {};
428    let serviceDatas: Record<string, Uint8Array> = {};
429
430    let curPos = 0;
431    while (curPos < advData.byteLength) {
432      let length = advData[curPos++]; // 获取当前广播类型的长度(length+data),curPos指向下一个位置
433      if (length == 0) {
434        break;
435      }
436
437      // 获取当前广播类型内容长度(data)
438      let advDataLength = length - 1;
439
440      // 获取当前广播类型,curPos指向下一个位置,从该位置解析实际内容,参考Core Specification Supplement, PartA
441      let advDataType = advData[curPos++];
442      switch (advDataType) {
443        case BLE_ADV_TYPE_FLAG:
444          advertiseFlags = advData[curPos];
445          break;
446        case BLE_ADV_TYPE_LOCAL_NAME_SHORT:
447        case BLE_ADV_TYPE_LOCAL_NAME_COMPLETE:
448          localName = advData.slice(curPos, curPos + advDataLength).toString();
449          break;
450        case BLE_ADV_TYPE_TX_POWER_LEVEL:
451          txPowerLevel = advData[curPos];
452          break;
453        case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE:
454        case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE:
455          this.parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids);
456          break;
457        case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE:
458        case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE:
459          this.parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids);
460          break;
461        case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE:
462        case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE:
463          this.parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids);
464          break;
465        case BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS:
466          this.parseServiceSolicitationUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength,
467            advData, serviceSolicitationUuids);
468          break;
469        case BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS:
470          this.parseServiceSolicitationUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength,
471            advData, serviceSolicitationUuids);
472          break;
473        case BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS:
474          this.parseServiceSolicitationUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength,
475            advData, serviceSolicitationUuids);
476          break;
477        case BLE_ADV_TYPE_16_BIT_SERVICE_DATA:
478          this.parseServiceData(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas);
479          break;
480        case BLE_ADV_TYPE_32_BIT_SERVICE_DATA:
481          this.parseServiceData(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas);
482          break;
483        case BLE_ADV_TYPE_128_BIT_SERVICE_DATA:
484          this.parseServiceData(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas);
485          break;
486        case BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA:
487          this.parseManufactureData(curPos, advDataLength, advData, manufactureSpecificDatas);
488          break;
489        default:
490          break;
491      }
492      curPos += advDataLength; // curPos指向下一个字段类型
493    }
494    console.info(TAG, 'advertiseFlags: ' + advertiseFlags);
495    console.info(TAG, 'txPowerLevel: ' + txPowerLevel);
496    console.info(TAG, 'localName: ' + localName);
497    console.info(TAG, 'serviceUuids: ' + JSON.stringify(serviceUuids));
498    console.info(TAG, 'serviceSolicitationUuids: ' + JSON.stringify(serviceSolicitationUuids));
499    console.info(TAG, 'manufactureSpecificDatas: ' + JSON.stringify(manufactureSpecificDatas));
500    console.info(TAG, 'serviceDatas: ' + JSON.stringify(serviceDatas));
501  }
502
503  private parseServiceUuid(uuidLength: number, curPos: number, advDataLength: number,
504    advData: Uint8Array, serviceUuids: string[]) {
505    while (advDataLength > 0) {
506      let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength);
507      serviceUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData));
508      advDataLength -= uuidLength;
509      curPos += uuidLength;
510    }
511  }
512
513  private parseServiceSolicitationUuid(uuidLength: number, curPos: number, advDataLength: number,
514    advData: Uint8Array, serviceSolicitationUuids: string[]) {
515    while (advDataLength > 0) {
516      let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength);
517      serviceSolicitationUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData));
518      advDataLength -= uuidLength;
519      curPos += uuidLength;
520    }
521  }
522
523  private getUuidFromUint8Array(uuidLength: number, uuidData: Uint8Array): string {
524    let uuid = "";
525    let temp: string = "";
526    for (let i = uuidLength - 1; i > -1; i--) {
527      temp += uuidData[i].toString(16).padStart(2, "0");
528    }
529    switch (uuidLength) {
530      case BLUETOOTH_UUID_16_BIT_LENGTH:
531        uuid = `0000${temp}-0000-1000-8000-00805F9B34FB`;
532        break;
533      case BLUETOOTH_UUID_32_BIT_LENGTH:
534        uuid = `${temp}-0000-1000-8000-00805F9B34FB`;
535        break;
536      case BLUETOOTH_UUID_128_BIT_LENGTH:
537        uuid = `${temp.substring(0, 8)}-${temp.substring(8, 12)}-${temp.substring(12, 16)}-${temp.substring(16, 20)}-${temp.substring(20, 32)}`;
538        break;
539      default:
540        break;
541    }
542    return uuid;
543  }
544
545  private parseServiceData(uuidLength: number, curPos: number, advDataLength: number,
546    advData: Uint8Array, serviceDatas: Record<number, Uint8Array>) {
547    let uuid: Uint8Array = advData.slice(curPos, curPos + uuidLength);
548    let data: Uint8Array = advData.slice(curPos + uuidLength, curPos + advDataLength);
549    serviceDatas[this.getUuidFromUint8Array(uuidLength, uuid)] = data;
550  }
551
552  private parseManufactureData(curPos: number, advDataLength: number,
553    advData: Uint8Array, manufactureSpecificDatas: Record<number, Uint8Array>) {
554    let manufactureId: number = (advData[curPos + 1] << 8) + advData[curPos];
555    let data: Uint8Array = advData.slice(curPos + BLUETOOTH_MANUFACTURE_ID_LENGTH, curPos + advDataLength);
556    manufactureSpecificDatas[manufactureId] = data;
557  }
558
559  // 2. 开启扫描
560  public startScan() {
561    // 2.1 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件
562    let manufactureId = 4567;
563    let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]);
564    let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]);
565    let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器
566      manufactureId: manufactureId,
567      manufactureData: manufactureData.buffer,
568      manufactureDataMask: manufactureDataMask.buffer
569    };
570
571    // 2.2 构造扫描配置参数
572    let scanOptions: ble.ScanOptions = {
573      interval: 0,
574      dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
575      matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
576    }
577    try {
578      // 发起订阅
579      this.bleScanner.on('BLEDeviceFind', this.onReceiveEvent);
580      // 发起扫描
581      this.bleScanner.startScan([scanFilter], scanOptions);
582      console.info('startBleScan success');
583    } catch (err) {
584      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
585    }
586  }
587
588  // 3. 关闭扫描
589  public stopScan() {
590    try {
591      // 取消订阅
592      this.bleScanner.off('BLEDeviceFind', this.onReceiveEvent);
593      // 停止扫描
594      this.bleScanner.stopScan();
595      console.info('stopBleScan success');
596    } catch (err) {
597      console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
598    }
599  }
600}
601
602let bleScanManager = new BleScanManager();
603export default bleScanManager as BleScanManager;
604```
605
606### BLE广播流程
607```ts
608import { ble } from '@kit.ConnectivityKit';
609import { BusinessError } from '@kit.BasicServicesKit';
610
611const TAG: string = 'BleAdvertisingManager';
612
613export class BleAdvertisingManager {
614  private advHandle: number = 0xFF; // 初始的无效值
615
616  // 1. 定义广播状态上报事件
617  onReceiveEvent = (data: ble.AdvertisingStateChangeInfo) => {
618    console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data));
619    AppStorage.setOrCreate('advertiserState', data.state);
620  };
621
622  // 2. 首次启动广播
623  public async startAdvertising() {
624    // 2.1 设置广播发送的参数
625    let setting: ble.AdvertiseSetting = {
626      interval: 160,
627      txPower: 0,
628      connectable: true
629    };
630    // 2.2 构造广播数据
631    let manufactureValueBuffer = new Uint8Array(4);
632    manufactureValueBuffer[0] = 1;
633    manufactureValueBuffer[1] = 2;
634    manufactureValueBuffer[2] = 3;
635    manufactureValueBuffer[3] = 4;
636    let serviceValueBuffer = new Uint8Array(4);
637    serviceValueBuffer[0] = 5;
638    serviceValueBuffer[1] = 6;
639    serviceValueBuffer[2] = 7;
640    serviceValueBuffer[3] = 8;
641    let manufactureDataUnit: ble.ManufactureData = {
642      manufactureId: 4567,
643      manufactureValue: manufactureValueBuffer.buffer
644    };
645    let serviceDataUnit1: ble.ServiceData = {
646      serviceUuid: "00001999-0000-1000-8000-00805f9b34fb",
647      serviceValue: serviceValueBuffer.buffer
648    };
649    let serviceDataUnit2: ble.ServiceData = {
650      serviceUuid: "19991999-0000-1000-8000-00805f9b34fb",
651      serviceValue: serviceValueBuffer.buffer
652    };
653    let advData: ble.AdvertiseData = {
654      serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"],
655      manufactureData: [manufactureDataUnit],
656      serviceData: [],
657      includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节,使得广播启动失败
658    };
659    let advResponse: ble.AdvertiseData = {
660      serviceUuids: [],
661      manufactureData: [],
662      serviceData: [serviceDataUnit1, serviceDataUnit2]
663    };
664    // 2.3 构造广播启动完整参数AdvertisingParams
665    let advertisingParams: ble.AdvertisingParams = {
666      advertisingSettings: setting,
667      advertisingData: advData, // 注意: 广播报文长度不能超过31个字节
668      advertisingResponse: advResponse, // 注意: 广播报文长度不能超过31个字节
669      duration: 0 // 可选参数,若参数大于0,则广播发送一段时间后会停止,但分配的广播资源还在,可重新启动发送
670    }
671
672    // 2.4 首次启动广播,蓝牙子系统会分配相关资源,包括应用获取到的广播标识ID
673    try {
674      ble.on('advertisingStateChange', this.onReceiveEvent);
675      this.advHandle = await ble.startAdvertising(advertisingParams);
676    } catch (err) {
677      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
678    }
679  }
680
681  // 3. 停止指定标识的广播,即首次启动时分配的标识,停止后,该路广播资源仍然存在
682  public async disableAdvertising() {
683    // 3.1 构造停止广播参数
684    let advertisingDisableParams: ble.AdvertisingDisableParams = {
685      advertisingId: this.advHandle // 使用首次启动广播时获取到的广播标识ID
686    }
687    try {
688      // 3.2 停止
689      await ble.disableAdvertising(advertisingDisableParams);
690    } catch (err) {
691      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
692    }
693  }
694
695  // 4. 启动指定标识的广播,即首次启动时分配的标识
696  public async enableAdvertising(enableDuration: number) {
697    // 4.1 构造启动广播参数
698    let advertisingEnableParams: ble.AdvertisingEnableParams = {
699      advertisingId: this.advHandle, // 使用首次启动广播时获取到的广播标识ID
700      duration: enableDuration
701    }
702    try {
703      // 4.2 再次启动
704      await ble.enableAdvertising(advertisingEnableParams);
705    } catch (err) {
706      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
707    }
708  }
709
710  // 5. 完全停止广播,释放广播资源
711  public async stopAdvertising() {
712    try {
713      await ble.stopAdvertising(this.advHandle);
714      ble.off('advertisingStateChange', this.onReceiveEvent);
715    } catch (err) {
716      console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
717    }
718  }
719}
720
721let bleAdvertisingManager = new BleAdvertisingManager();
722export default bleAdvertisingManager as BleAdvertisingManager;
723```