| @@ -4,18 +4,16 @@ | |||
| <component name="GradleSettings"> | |||
| <option name="linkedExternalProjectsSettings"> | |||
| <GradleProjectSettings> | |||
| <option name="testRunner" value="PLATFORM" /> | |||
| <option name="testRunner" value="GRADLE" /> | |||
| <option name="distributionType" value="DEFAULT_WRAPPED" /> | |||
| <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"> | |||
| <set> | |||
| <option value="$PROJECT_DIR$" /> | |||
| <option value="$PROJECT_DIR$/app" /> | |||
| </set> | |||
| </option> | |||
| <option name="resolveModulePerSourceSet" value="false" /> | |||
| <option name="useQualifiedModuleNames" value="true" /> | |||
| </GradleProjectSettings> | |||
| </option> | |||
| </component> | |||
| @@ -5,7 +5,7 @@ | |||
| <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" /> | |||
| </configurations> | |||
| </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" /> | |||
| </component> | |||
| <component name="ProjectType"> | |||
| @@ -1,12 +0,0 @@ | |||
| <?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> | |||
| @@ -0,0 +1,208 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -0,0 +1,303 @@ | |||
| /* | |||
| * 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); | |||
| } | |||
| } | |||
| @@ -26,6 +26,8 @@ import android.os.Looper; | |||
| import android.os.Message; | |||
| import android.os.ParcelUuid; | |||
| import android.provider.Settings; | |||
| import android.util.Log; | |||
| import android.util.SparseArray; | |||
| import android.view.View; | |||
| import android.widget.AdapterView; | |||
| import android.widget.ArrayAdapter; | |||
| @@ -48,6 +50,8 @@ import androidx.core.app.ActivityCompat; | |||
| public class MainActivity extends AppCompatActivity implements View.OnClickListener, OnAnalyticalListener { | |||
| private static final String TAG = "MainActivity"; | |||
| /** | |||
| * 停止搜索 | |||
| */ | |||
| @@ -161,10 +165,11 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| for (byte aB : b) { | |||
| int a = aB & 0XFF; | |||
| stmp = Integer.toHexString(a); | |||
| if (stmp.length() == 1) | |||
| if (stmp.length() == 1) { | |||
| hs = hs + "0" + stmp + " "; | |||
| else | |||
| } else { | |||
| hs = hs + stmp + " "; | |||
| } | |||
| } | |||
| return hs; | |||
| } | |||
| @@ -260,14 +265,18 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| List<ScanFilter> filters = new ArrayList<>(); | |||
| if (scanUUID != null && scanUUID.length > 0) { | |||
| 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); | |||
| } | |||
| } | |||
| if (mScanCallback == null) | |||
| if (mScanCallback == null) { | |||
| 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 { | |||
| mBluetoothAdapter.startLeScan(mLeScanCallback); | |||
| } | |||
| @@ -327,12 +336,18 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| public void onScanResult(int callbackType, ScanResult result) { | |||
| super.onScanResult(callbackType, result); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -353,10 +368,12 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| /** | |||
| * 解密广播数据参考例子 | |||
| * | |||
| * @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)); | |||
| } | |||
| @@ -5,7 +5,7 @@ buildscript { | |||
| jcenter() | |||
| } | |||
| 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 | |||
| // in the individual module build.gradle files | |||
| @@ -1,6 +1,6 @@ | |||
| #Thu Apr 22 19:01:02 CST 2021 | |||
| #Fri Dec 01 15:14:20 CST 2023 | |||
| distributionBase=GRADLE_USER_HOME | |||
| distributionPath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip | |||
| zipStoreBase=GRADLE_USER_HOME | |||
| zipStorePath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip | |||