[Web Bluetooth]: Add manufacturer data to bluetooth-internals page
This CL adds a new column "Manufacturer Data" to the internal about:bluetooth-internals page so that developers can inspect manufacturer specific data from nearby Bluetooth devices. Change-Id: I40fb78cf62cd285bdcb88ee9c6b98ff7f3b375a8 Bug: 707635 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2860010 Reviewed-by: David Roger <droger@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Reviewed-by: Reilly Grant <reillyg@chromium.org> Commit-Queue: François Beaufort <beaufort.francois@gmail.com> Cr-Commit-Position: refs/heads/master@{#880890}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
03b39ef321
commit
613b3bf2a0
chrome
browser
resources
test
data
webui
bluetooth_internals
device/bluetooth
@ -255,6 +255,7 @@ generate_grd("build_grd") {
|
||||
"device_collection.js",
|
||||
"device_details_page.js",
|
||||
"device_table.js",
|
||||
"device_utils.js",
|
||||
"devices_page.js",
|
||||
"expandable_list.js",
|
||||
"bluetooth_internals.html",
|
||||
|
@ -65,6 +65,7 @@
|
||||
<th data-field="address">Address</th>
|
||||
<th data-field="rssi.value">Latest RSSI</th>
|
||||
<th data-field="services.length">Services</th>
|
||||
<th data-field="manufacturerDataMap">Manufacturer Data</th>
|
||||
<th data-field="isGattConnected">GATT Connection State</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -12,6 +12,7 @@ import {$} from 'chrome://resources/js/util.m.js';
|
||||
|
||||
import {connectToDevice} from './device_broker.js';
|
||||
import {ConnectionStatus} from './device_collection.js';
|
||||
import {formatManufacturerDataMap} from './device_utils.js';
|
||||
import {ObjectFieldSet} from './object_fieldset.js';
|
||||
import {Page} from './page.js';
|
||||
import {ServiceList} from './service_list.js';
|
||||
@ -27,6 +28,7 @@ const PROPERTY_NAMES = {
|
||||
isGattConnected: 'GATT Connected',
|
||||
'rssi.value': 'Latest RSSI',
|
||||
'services.length': 'Services',
|
||||
manufacturerDataMap: 'Manufacturer Data',
|
||||
};
|
||||
|
||||
/**
|
||||
@ -166,12 +168,16 @@ export class DeviceDetailsPage extends Page {
|
||||
serviceCount = services.length;
|
||||
}
|
||||
|
||||
const manufacturerDataMapText =
|
||||
formatManufacturerDataMap(this.deviceInfo.manufacturerDataMap);
|
||||
|
||||
const deviceViewObj = {
|
||||
name: this.deviceInfo.nameForDisplay,
|
||||
address: this.deviceInfo.address,
|
||||
isGattConnected: connectedText,
|
||||
'rssi.value': rssiValue,
|
||||
'services.length': serviceCount,
|
||||
manufacturerDataMap: manufacturerDataMapText,
|
||||
};
|
||||
|
||||
this.deviceFieldSet_.setObject(deviceViewObj);
|
||||
|
@ -10,14 +10,16 @@ import {define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
|
||||
import {$} from 'chrome://resources/js/util.m.js';
|
||||
|
||||
import {DeviceCollection} from './device_collection.js';
|
||||
import {formatManufacturerDataMap} from './device_utils.js';
|
||||
|
||||
const COLUMNS = {
|
||||
NAME: 0,
|
||||
ADDRESS: 1,
|
||||
RSSI: 2,
|
||||
SERVICES: 3,
|
||||
CONNECTION_STATE: 4,
|
||||
LINKS: 5,
|
||||
MANUFACTURER_DATA: 3,
|
||||
SERVICES: 4,
|
||||
CONNECTION_STATE: 5,
|
||||
LINKS: 6,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -230,6 +232,8 @@ DeviceTable.prototype = {
|
||||
|
||||
if (propName == 'isGattConnected') {
|
||||
obj = obj ? 'Connected' : 'Not Connected';
|
||||
} else if (propName == 'manufacturerDataMap') {
|
||||
obj = formatManufacturerDataMap(obj);
|
||||
}
|
||||
|
||||
const cell = row.cells[i];
|
||||
|
19
chrome/browser/resources/bluetooth_internals/device_utils.js
Normal file
19
chrome/browser/resources/bluetooth_internals/device_utils.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/**
|
||||
* Format in a user readable way device manufacturer data map. Keys are
|
||||
* Bluetooth company identifiers (unsigned short), values are bytes.
|
||||
* @param {Map<string, array<number>>} manufacturerDataMap
|
||||
* @return {string}
|
||||
*/
|
||||
export function formatManufacturerDataMap(manufacturerDataMap) {
|
||||
return Object.entries(manufacturerDataMap)
|
||||
.map(([key, value]) => {
|
||||
const companyIdentifier = parseInt(key).toString(16).padStart(4, '0');
|
||||
const data = value.map(v => v.toString(16).padStart(2, '0')).join('');
|
||||
return `0x${companyIdentifier} 0x${data}`;
|
||||
})
|
||||
.join(' | ');
|
||||
}
|
@ -93,11 +93,13 @@ suite('bluetooth_internals', function() {
|
||||
var addressColumn = deviceRow.children[1];
|
||||
var rssiColumn = deviceRow.children[2];
|
||||
var servicesColumn = deviceRow.children[3];
|
||||
var manufacturerDataColumn = deviceRow.children[4];
|
||||
|
||||
expectTrue(!!nameForDisplayColumn);
|
||||
expectTrue(!!addressColumn);
|
||||
expectTrue(!!rssiColumn);
|
||||
expectTrue(!!servicesColumn);
|
||||
expectTrue(!!manufacturerDataColumn);
|
||||
|
||||
adapterBroker.deviceChanged(deviceInfo);
|
||||
|
||||
@ -114,6 +116,28 @@ suite('bluetooth_internals', function() {
|
||||
} else {
|
||||
expectEquals('Unknown', servicesColumn.textContent);
|
||||
}
|
||||
|
||||
if (deviceInfo.manufacturerDataMap) {
|
||||
expectEquals(
|
||||
formatManufacturerDataMap(deviceInfo.manufacturerDataMap),
|
||||
manufacturerDataColumn.textContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format in a user readable way device manufacturer data map. Keys are
|
||||
* Bluetooth company identifiers (unsigned short), values are bytes.
|
||||
* @param {Map<string, array<number>>} manufacturerDataMap
|
||||
* @return {string}
|
||||
*/
|
||||
function formatManufacturerDataMap(manufacturerDataMap) {
|
||||
return Object.entries(manufacturerDataMap)
|
||||
.map(([key, value]) => {
|
||||
const companyIdentifier = parseInt(key).toString(16).padStart(4, '0');
|
||||
const data = value.map(v => v.toString(16).padStart(2, '0')).join('');
|
||||
return `0x${companyIdentifier} 0x${data}`;
|
||||
})
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -454,6 +478,7 @@ suite('bluetooth_internals', function() {
|
||||
'isGattConnected',
|
||||
'rssi.value',
|
||||
'services.length',
|
||||
'manufacturerDataMap',
|
||||
].forEach(function(propName) {
|
||||
var valueCell =
|
||||
detailsPage.querySelector('fieldset [data-field="' + propName + '"]');
|
||||
@ -468,6 +493,8 @@ suite('bluetooth_internals', function() {
|
||||
|
||||
if (propName === 'isGattConnected') {
|
||||
value = value ? 'Connected' : 'Not Connected';
|
||||
} else if (propName === 'manufacturerDataMap') {
|
||||
value = formatManufacturerDataMap(value);
|
||||
}
|
||||
|
||||
if (typeof (value) === 'boolean') {
|
||||
|
@ -205,6 +205,7 @@ export function fakeDeviceInfo1() {
|
||||
nameForDisplay: 'AAA',
|
||||
rssi: {value: -40},
|
||||
isGattConnected: false,
|
||||
manufacturerDataMap: {'1': [1, 2], '2': [3, 4]},
|
||||
serviceDataMap: {},
|
||||
services: [],
|
||||
};
|
||||
@ -221,6 +222,7 @@ export function fakeDeviceInfo2() {
|
||||
nameForDisplay: 'BBB',
|
||||
rssi: null,
|
||||
isGattConnected: false,
|
||||
manufacturerDataMap: {},
|
||||
serviceDataMap: {},
|
||||
services: [],
|
||||
};
|
||||
@ -236,6 +238,7 @@ export function fakeDeviceInfo3() {
|
||||
address: 'CC:CC:84:96:92:84',
|
||||
name: 'CCC',
|
||||
nameForDisplay: 'CCC',
|
||||
manufacturerDataMap: {},
|
||||
serviceDataMap: {},
|
||||
isGattConnected: false,
|
||||
};
|
||||
|
@ -45,6 +45,9 @@ mojom::DeviceInfoPtr Device::ConstructDeviceInfoStruct(
|
||||
device_info->rssi->value = device->GetInquiryRSSI().value();
|
||||
}
|
||||
|
||||
for (auto const& it : device->GetManufacturerData())
|
||||
device_info->manufacturer_data_map.insert_or_assign(it.first, it.second);
|
||||
|
||||
for (auto const& it : device->GetServiceData())
|
||||
device_info->service_data_map.insert_or_assign(it.first, it.second);
|
||||
|
||||
|
@ -68,6 +68,10 @@ struct DeviceInfo {
|
||||
bool is_gatt_connected;
|
||||
RSSIWrapper? rssi;
|
||||
|
||||
// Important: the blobs associated with each key are arbitrary and untrusted.
|
||||
// Please refer to the note on "The Rule of 2" at the top of this file.
|
||||
map<uint16, array<uint8>> manufacturer_data_map;
|
||||
|
||||
// Important: the blobs associated with each UUID are arbitrary and untrusted.
|
||||
// Please refer to the note on "The Rule of 2" at the top of this file.
|
||||
map<UUID, array<uint8>> service_data_map;
|
||||
|
Reference in New Issue
Block a user