| <component name="GradleSettings"> | <component name="GradleSettings"> | ||||
| <option name="linkedExternalProjectsSettings"> | <option name="linkedExternalProjectsSettings"> | ||||
| <GradleProjectSettings> | <GradleProjectSettings> | ||||
| <option name="testRunner" value="PLATFORM" /> | |||||
| <option name="testRunner" value="GRADLE" /> | |||||
| <option name="distributionType" value="DEFAULT_WRAPPED" /> | <option name="distributionType" value="DEFAULT_WRAPPED" /> | ||||
| <option name="externalProjectPath" value="$PROJECT_DIR$" /> | <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||||
| <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-5.1.1" /> | |||||
| <option name="gradleJvm" value="Embedded JDK" /> | |||||
| <option name="modules"> | <option name="modules"> | ||||
| <set> | <set> | ||||
| <option value="$PROJECT_DIR$" /> | <option value="$PROJECT_DIR$" /> | ||||
| <option value="$PROJECT_DIR$/app" /> | <option value="$PROJECT_DIR$/app" /> | ||||
| </set> | </set> | ||||
| </option> | </option> | ||||
| <option name="resolveModulePerSourceSet" value="false" /> | |||||
| <option name="useQualifiedModuleNames" value="true" /> | |||||
| </GradleProjectSettings> | </GradleProjectSettings> | ||||
| </option> | </option> | ||||
| </component> | </component> |
| <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" /> | <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" /> | ||||
| </configurations> | </configurations> | ||||
| </component> | </component> | ||||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | |||||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="corretto-1.8" project-jdk-type="JavaSDK"> | |||||
| <output url="file://$PROJECT_DIR$/build/classes" /> | <output url="file://$PROJECT_DIR$/build/classes" /> | ||||
| </component> | </component> | ||||
| <component name="ProjectType"> | <component name="ProjectType"> |
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="RunConfigurationProducerService"> | |||||
| <option name="ignoredProducers"> | |||||
| <set> | |||||
| <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> | |||||
| <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> | |||||
| <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> | |||||
| </set> | |||||
| </option> | |||||
| </component> | |||||
| </project> |
| package com.elinkthings.ailinksecrettooldemo; | |||||
| import android.os.ParcelUuid; | |||||
| import java.util.ArrayList; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * xing<br> | |||||
| * 2019/3/7<br> | |||||
| * 广播解析工具类 | |||||
| */ | |||||
| public class BleBroadcastUtils { | |||||
| private static final int DATA_TYPE_FLAGS = 0x01; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; | |||||
| private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; | |||||
| private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; | |||||
| private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; | |||||
| private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; | |||||
| private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16; | |||||
| private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20; | |||||
| private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21; | |||||
| private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; | |||||
| private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA_MOVE = 0xFE; | |||||
| private List<byte[]> mManufacturerByte; | |||||
| private byte[] mManufacturerByteMove; | |||||
| private List<ParcelUuid> mServiceUuids; | |||||
| private int mAdvertiseFlag = -1; | |||||
| private Map<ParcelUuid, byte[]> mServiceData; | |||||
| private String mDeviceName; | |||||
| public BleBroadcastUtils(byte[] scanRecord) { | |||||
| this(scanRecord, -1); | |||||
| } | |||||
| public BleBroadcastUtils(byte[] scanRecord, int manufacturerId) { | |||||
| mManufacturerByte=new ArrayList<>(); | |||||
| getBleData(scanRecord, manufacturerId); | |||||
| } | |||||
| // Helper method to extract bytes from byte array. | |||||
| private static byte[] extractBytes(byte[] scanRecord, int start, int length) { | |||||
| byte[] bytes = new byte[length]; | |||||
| System.arraycopy(scanRecord, start, bytes, 0, length); | |||||
| return bytes; | |||||
| } | |||||
| public List<byte[]> getManufacturerSpecificData() { | |||||
| return mManufacturerByte; | |||||
| } | |||||
| public byte[] getManufacturerByteMove() { | |||||
| return mManufacturerByteMove; | |||||
| } | |||||
| /** | |||||
| * 服务uuid | |||||
| * | |||||
| * @return {@link List}<{@link ParcelUuid}> | |||||
| */ | |||||
| public List<ParcelUuid> getServiceUuids() { | |||||
| return mServiceUuids; | |||||
| } | |||||
| /** | |||||
| * 广告标志 | |||||
| * | |||||
| * @return int | |||||
| */ | |||||
| public int getAdvertiseFlag() { | |||||
| return mAdvertiseFlag; | |||||
| } | |||||
| public String getDeviceName() { | |||||
| return mDeviceName; | |||||
| } | |||||
| /** | |||||
| * 服务数据 | |||||
| * | |||||
| * @return {@link Map}<{@link ParcelUuid}, {@link byte[]}> | |||||
| */ | |||||
| public Map<ParcelUuid, byte[]> getServiceData() { | |||||
| return mServiceData; | |||||
| } | |||||
| /** | |||||
| * 服务数据 | |||||
| * | |||||
| * @param serviceDataUuid 服务数据uuid | |||||
| * @return {@link byte[]} | |||||
| */ | |||||
| public byte[] getServiceData(final ParcelUuid serviceDataUuid) { | |||||
| //noinspection ConstantConditions | |||||
| if (serviceDataUuid == null || mServiceData == null) { | |||||
| return null; | |||||
| } | |||||
| return mServiceData.get(serviceDataUuid); | |||||
| } | |||||
| private void getBleData(byte[] scanRecord, int id) { | |||||
| int currentPos = 0; | |||||
| mServiceUuids = new ArrayList<>(); | |||||
| try { | |||||
| while (currentPos < scanRecord.length) { | |||||
| int length = scanRecord[currentPos++] & 0xFF; | |||||
| if (length == 0) | |||||
| break; | |||||
| int dataLength = length - 1; | |||||
| int fieldType = scanRecord[currentPos++] & 0xFF; | |||||
| switch (fieldType) { | |||||
| case DATA_TYPE_FLAGS: | |||||
| mAdvertiseFlag = scanRecord[currentPos] & 0xFF; | |||||
| break; | |||||
| case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: | |||||
| case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: | |||||
| parseServiceUuid(scanRecord, currentPos, dataLength, BluetoothUuid.UUID_BYTES_16_BIT, mServiceUuids); | |||||
| break; | |||||
| case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: | |||||
| case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: | |||||
| parseServiceUuid(scanRecord, currentPos, dataLength, BluetoothUuid.UUID_BYTES_32_BIT, mServiceUuids); | |||||
| break; | |||||
| case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: | |||||
| case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: | |||||
| parseServiceUuid(scanRecord, currentPos, dataLength, BluetoothUuid.UUID_BYTES_128_BIT, mServiceUuids); | |||||
| break; | |||||
| case DATA_TYPE_SERVICE_DATA_16_BIT: | |||||
| case DATA_TYPE_SERVICE_DATA_32_BIT: | |||||
| case DATA_TYPE_SERVICE_DATA_128_BIT: | |||||
| int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; | |||||
| if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) { | |||||
| serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; | |||||
| } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) { | |||||
| serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; | |||||
| } | |||||
| final byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, | |||||
| serviceUuidLength); | |||||
| final ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( | |||||
| serviceDataUuidBytes); | |||||
| final byte[] serviceDataArray = extractBytes(scanRecord, | |||||
| currentPos + serviceUuidLength, dataLength - serviceUuidLength); | |||||
| if (mServiceData == null) | |||||
| mServiceData = new HashMap<>(); | |||||
| mServiceData.put(serviceDataUuid, serviceDataArray); | |||||
| break; | |||||
| case DATA_TYPE_LOCAL_NAME_SHORT: | |||||
| case DATA_TYPE_LOCAL_NAME_COMPLETE: | |||||
| mDeviceName = new String(extractBytes(scanRecord, currentPos, dataLength)); | |||||
| break; | |||||
| case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: | |||||
| if (id == -1) { | |||||
| byte[] bytes = extractBytes(scanRecord, currentPos, dataLength); | |||||
| mManufacturerByte.add(bytes); | |||||
| } else { | |||||
| int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + (scanRecord[currentPos] & 0xFF); | |||||
| if (manufacturerId == id) { | |||||
| byte[] bytes = extractBytes(scanRecord, currentPos, dataLength); | |||||
| mManufacturerByte.add(bytes); | |||||
| } else { | |||||
| currentPos += dataLength; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA_MOVE: | |||||
| mManufacturerByteMove = extractBytes(scanRecord, currentPos, dataLength); | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| currentPos += dataLength; | |||||
| } | |||||
| } catch (Exception e) { | |||||
| mServiceUuids = null; | |||||
| mManufacturerByte = null; | |||||
| e.printStackTrace(); | |||||
| } | |||||
| if (mServiceUuids!=null&&mServiceUuids.isEmpty()) { | |||||
| mServiceUuids = null; | |||||
| } | |||||
| } | |||||
| // Parse service UUIDs. | |||||
| private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List<ParcelUuid> serviceUuids) { | |||||
| while (dataLength > 0) { | |||||
| byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength); | |||||
| serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); | |||||
| dataLength -= uuidLength; | |||||
| currentPos += uuidLength; | |||||
| } | |||||
| return currentPos; | |||||
| } | |||||
| } |
| /* | |||||
| * Copyright (C) 2009 The Android Open Source Project | |||||
| * | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.elinkthings.ailinksecrettooldemo; | |||||
| import android.os.ParcelUuid; | |||||
| import java.nio.ByteBuffer; | |||||
| import java.nio.ByteOrder; | |||||
| import java.util.Arrays; | |||||
| import java.util.HashSet; | |||||
| import java.util.UUID; | |||||
| /** | |||||
| * Static helper methods and constants to decode the ParcelUuid of remote devices. | |||||
| * @hide | |||||
| */ | |||||
| public final class BluetoothUuid { | |||||
| /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs | |||||
| * for the various services. | |||||
| * | |||||
| * The following 128 bit values are calculated as: | |||||
| * uuid * 2^96 + BASE_UUID | |||||
| */ | |||||
| public static final ParcelUuid AudioSink = | |||||
| ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid AudioSource = | |||||
| ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid AdvAudioDist = | |||||
| ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid HSP = | |||||
| ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid HSP_AG = | |||||
| ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid Handsfree = | |||||
| ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid Handsfree_AG = | |||||
| ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid AvrcpController = | |||||
| ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid AvrcpTarget = | |||||
| ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid ObexObjectPush = | |||||
| ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); | |||||
| public static final ParcelUuid Hid = | |||||
| ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); | |||||
| public static final ParcelUuid Hogp = | |||||
| ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); | |||||
| public static final ParcelUuid PANU = | |||||
| ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid NAP = | |||||
| ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid BNEP = | |||||
| ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid PBAP_PCE = | |||||
| ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid PBAP_PSE = | |||||
| ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid MAP = | |||||
| ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid MNS = | |||||
| ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid MAS = | |||||
| ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); | |||||
| public static final ParcelUuid BASE_UUID = | |||||
| ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); | |||||
| /** Length of bytes for 16 bit UUID */ | |||||
| public static final int UUID_BYTES_16_BIT = 2; | |||||
| /** Length of bytes for 32 bit UUID */ | |||||
| public static final int UUID_BYTES_32_BIT = 4; | |||||
| /** Length of bytes for 128 bit UUID */ | |||||
| public static final int UUID_BYTES_128_BIT = 16; | |||||
| public static final ParcelUuid[] RESERVED_UUIDS = { | |||||
| AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, | |||||
| ObexObjectPush, PANU, NAP, MAP, MNS, MAS}; | |||||
| public static boolean isAudioSource(ParcelUuid uuid) { | |||||
| return uuid.equals(AudioSource); | |||||
| } | |||||
| public static boolean isAudioSink(ParcelUuid uuid) { | |||||
| return uuid.equals(AudioSink); | |||||
| } | |||||
| public static boolean isAdvAudioDist(ParcelUuid uuid) { | |||||
| return uuid.equals(AdvAudioDist); | |||||
| } | |||||
| public static boolean isHandsfree(ParcelUuid uuid) { | |||||
| return uuid.equals(Handsfree); | |||||
| } | |||||
| public static boolean isHeadset(ParcelUuid uuid) { | |||||
| return uuid.equals(HSP); | |||||
| } | |||||
| public static boolean isAvrcpController(ParcelUuid uuid) { | |||||
| return uuid.equals(AvrcpController); | |||||
| } | |||||
| public static boolean isAvrcpTarget(ParcelUuid uuid) { | |||||
| return uuid.equals(AvrcpTarget); | |||||
| } | |||||
| public static boolean isInputDevice(ParcelUuid uuid) { | |||||
| return uuid.equals(Hid); | |||||
| } | |||||
| public static boolean isPanu(ParcelUuid uuid) { | |||||
| return uuid.equals(PANU); | |||||
| } | |||||
| public static boolean isNap(ParcelUuid uuid) { | |||||
| return uuid.equals(NAP); | |||||
| } | |||||
| public static boolean isBnep(ParcelUuid uuid) { | |||||
| return uuid.equals(BNEP); | |||||
| } | |||||
| public static boolean isMap(ParcelUuid uuid) { | |||||
| return uuid.equals(MAP); | |||||
| } | |||||
| public static boolean isMns(ParcelUuid uuid) { | |||||
| return uuid.equals(MNS); | |||||
| } | |||||
| public static boolean isMas(ParcelUuid uuid) { | |||||
| return uuid.equals(MAS); | |||||
| } | |||||
| /** | |||||
| * Returns true if ParcelUuid is present in uuidArray | |||||
| * | |||||
| * @param uuidArray - Array of ParcelUuids | |||||
| * @param uuid | |||||
| */ | |||||
| public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { | |||||
| if ((uuidArray == null || uuidArray.length == 0) && uuid == null) | |||||
| return true; | |||||
| if (uuidArray == null) | |||||
| return false; | |||||
| for (ParcelUuid element: uuidArray) { | |||||
| if (element.equals(uuid)) return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Returns true if there any common ParcelUuids in uuidA and uuidB. | |||||
| * | |||||
| * @param uuidA - List of ParcelUuids | |||||
| * @param uuidB - List of ParcelUuids | |||||
| * | |||||
| */ | |||||
| public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { | |||||
| if (uuidA == null && uuidB == null) return true; | |||||
| if (uuidA == null) { | |||||
| return uuidB.length == 0 ? true : false; | |||||
| } | |||||
| if (uuidB == null) { | |||||
| return uuidA.length == 0 ? true : false; | |||||
| } | |||||
| HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA)); | |||||
| for (ParcelUuid uuid: uuidB) { | |||||
| if (uuidSet.contains(uuid)) return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Returns true if all the ParcelUuids in ParcelUuidB are present in | |||||
| * ParcelUuidA | |||||
| * | |||||
| * @param uuidA - Array of ParcelUuidsA | |||||
| * @param uuidB - Array of ParcelUuidsB | |||||
| * | |||||
| */ | |||||
| public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { | |||||
| if (uuidA == null && uuidB == null) return true; | |||||
| if (uuidA == null) { | |||||
| return uuidB.length == 0 ? true : false; | |||||
| } | |||||
| if (uuidB == null) return true; | |||||
| HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA)); | |||||
| for (ParcelUuid uuid: uuidB) { | |||||
| if (!uuidSet.contains(uuid)) return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Extract the Service Identifier or the actual uuid from the Parcel Uuid. | |||||
| * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, | |||||
| * this function will return 110B | |||||
| * @param parcelUuid | |||||
| * @return the service identifier. | |||||
| */ | |||||
| public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { | |||||
| UUID uuid = parcelUuid.getUuid(); | |||||
| long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; | |||||
| return (int)value; | |||||
| } | |||||
| /** | |||||
| * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, | |||||
| * but the returned UUID is always in 128-bit format. | |||||
| * Note UUID is little endian in Bluetooth. | |||||
| * | |||||
| * @param uuidBytes Byte representation of uuid. | |||||
| * @return {@link ParcelUuid} parsed from bytes. | |||||
| * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. | |||||
| */ | |||||
| public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { | |||||
| if (uuidBytes == null) { | |||||
| throw new IllegalArgumentException("uuidBytes cannot be null"); | |||||
| } | |||||
| int length = uuidBytes.length; | |||||
| if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT && | |||||
| length != UUID_BYTES_128_BIT) { | |||||
| throw new IllegalArgumentException("uuidBytes length invalid - " + length); | |||||
| } | |||||
| // Construct a 128 bit UUID. | |||||
| if (length == UUID_BYTES_128_BIT) { | |||||
| ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); | |||||
| long msb = buf.getLong(8); | |||||
| long lsb = buf.getLong(0); | |||||
| return new ParcelUuid(new UUID(msb, lsb)); | |||||
| } | |||||
| // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. | |||||
| // 128_bit_value = uuid * 2^96 + BASE_UUID | |||||
| long shortUuid; | |||||
| if (length == UUID_BYTES_16_BIT) { | |||||
| shortUuid = uuidBytes[0] & 0xFF; | |||||
| shortUuid += (uuidBytes[1] & 0xFF) << 8; | |||||
| } else { | |||||
| shortUuid = uuidBytes[0] & 0xFF ; | |||||
| shortUuid += (uuidBytes[1] & 0xFF) << 8; | |||||
| shortUuid += (uuidBytes[2] & 0xFF) << 16; | |||||
| shortUuid += (uuidBytes[3] & 0xFF) << 24; | |||||
| } | |||||
| long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); | |||||
| long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); | |||||
| return new ParcelUuid(new UUID(msb, lsb)); | |||||
| } | |||||
| /** | |||||
| * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. | |||||
| * | |||||
| * @param parcelUuid | |||||
| * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. | |||||
| */ | |||||
| public static boolean is16BitUuid(ParcelUuid parcelUuid) { | |||||
| UUID uuid = parcelUuid.getUuid(); | |||||
| if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { | |||||
| return false; | |||||
| } | |||||
| return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); | |||||
| } | |||||
| /** | |||||
| * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid. | |||||
| * | |||||
| * @param parcelUuid | |||||
| * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. | |||||
| */ | |||||
| public static boolean is32BitUuid(ParcelUuid parcelUuid) { | |||||
| UUID uuid = parcelUuid.getUuid(); | |||||
| if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { | |||||
| return false; | |||||
| } | |||||
| if (is16BitUuid(parcelUuid)) { | |||||
| return false; | |||||
| } | |||||
| return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L); | |||||
| } | |||||
| } |
| import android.os.Message; | import android.os.Message; | ||||
| import android.os.ParcelUuid; | import android.os.ParcelUuid; | ||||
| import android.provider.Settings; | import android.provider.Settings; | ||||
| import android.util.Log; | |||||
| import android.util.SparseArray; | |||||
| import android.view.View; | import android.view.View; | ||||
| import android.widget.AdapterView; | import android.widget.AdapterView; | ||||
| import android.widget.ArrayAdapter; | import android.widget.ArrayAdapter; | ||||
| public class MainActivity extends AppCompatActivity implements View.OnClickListener, OnAnalyticalListener { | public class MainActivity extends AppCompatActivity implements View.OnClickListener, OnAnalyticalListener { | ||||
| private static final String TAG = "MainActivity"; | |||||
| /** | /** | ||||
| * 停止搜索 | * 停止搜索 | ||||
| */ | */ | ||||
| for (byte aB : b) { | for (byte aB : b) { | ||||
| int a = aB & 0XFF; | int a = aB & 0XFF; | ||||
| stmp = Integer.toHexString(a); | stmp = Integer.toHexString(a); | ||||
| if (stmp.length() == 1) | |||||
| if (stmp.length() == 1) { | |||||
| hs = hs + "0" + stmp + " "; | hs = hs + "0" + stmp + " "; | ||||
| else | |||||
| } else { | |||||
| hs = hs + stmp + " "; | hs = hs + stmp + " "; | ||||
| } | |||||
| } | } | ||||
| return hs; | return hs; | ||||
| } | } | ||||
| List<ScanFilter> filters = new ArrayList<>(); | List<ScanFilter> filters = new ArrayList<>(); | ||||
| if (scanUUID != null && scanUUID.length > 0) { | if (scanUUID != null && scanUUID.length > 0) { | ||||
| for (UUID uuid : scanUUID) { | for (UUID uuid : scanUUID) { | ||||
| ScanFilter filter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(uuid)).build(); | |||||
| ScanFilter filter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(uuid)) | |||||
| .build(); | |||||
| filters.add(filter); | filters.add(filter); | ||||
| } | } | ||||
| } | } | ||||
| if (mScanCallback == null) | |||||
| if (mScanCallback == null) { | |||||
| mScanCallback = new MyScanCallback(); | mScanCallback = new MyScanCallback(); | ||||
| ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); | |||||
| mBluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, mScanCallback); | |||||
| } | |||||
| ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) | |||||
| .build(); | |||||
| mBluetoothAdapter.getBluetoothLeScanner() | |||||
| .startScan(filters, settings, mScanCallback); | |||||
| } else { | } else { | ||||
| mBluetoothAdapter.startLeScan(mLeScanCallback); | mBluetoothAdapter.startLeScan(mLeScanCallback); | ||||
| } | } | ||||
| public void onScanResult(int callbackType, ScanResult result) { | public void onScanResult(int callbackType, ScanResult result) { | ||||
| super.onScanResult(callbackType, result); | super.onScanResult(callbackType, result); | ||||
| String address = result.getDevice().getAddress(); | String address = result.getDevice().getAddress(); | ||||
| saveScanData(address); | |||||
| // SparseArray<byte[]> manufacturerSpecificData = result.getScanRecord().getManufacturerSpecificData();//获取自定义厂商数据列表,正常情况只有1个 | |||||
| // if (manufacturerSpecificData.size() > 0) { | |||||
| // byte[] data = manufacturerSpecificData.get(0); | |||||
| // decryptBroadcastExample(data); | |||||
| // } | |||||
| // saveScanData(address); | |||||
| //获取自定义厂商数据列表,正常情况只有1个 | |||||
| BleBroadcastUtils bleBroadcastUtils = new BleBroadcastUtils(result.getScanRecord().getBytes()); | |||||
| List<byte[]> manufacturerSpecificData = bleBroadcastUtils.getManufacturerSpecificData(); | |||||
| if (manufacturerSpecificData.size() > 0) { | |||||
| byte[] data = manufacturerSpecificData.get(0); | |||||
| if (data != null) { | |||||
| decryptBroadcastExample(data); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| * 解密广播数据参考例子 | * 解密广播数据参考例子 | ||||
| * | |||||
| * @param data 自定义厂商数据 | * @param data 自定义厂商数据 | ||||
| */ | */ | ||||
| private void decryptBroadcastExample(byte[] data){ | |||||
| byte[] decryptData= mAnalyticalDataUtil.decryptBroadcast(data);//获取解密后的数据 | |||||
| private void decryptBroadcastExample(byte[] data) { | |||||
| byte[] decryptData = mAnalyticalDataUtil.decryptBroadcast(data);//获取解密后的数据 | |||||
| Log.i(TAG, "decryptBroadcastExample: " + byte2HexStr(decryptData)); | |||||
| } | } | ||||
| jcenter() | jcenter() | ||||
| } | } | ||||
| dependencies { | dependencies { | ||||
| classpath "com.android.tools.build:gradle:4.0.1" | |||||
| classpath 'com.android.tools.build:gradle:7.1.2' | |||||
| // NOTE: Do not place your application dependencies here; they belong | // NOTE: Do not place your application dependencies here; they belong | ||||
| // in the individual module build.gradle files | // in the individual module build.gradle files |
| #Thu Apr 22 19:01:02 CST 2021 | |||||
| #Fri Dec 01 15:14:20 CST 2023 | |||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip | |||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip |