<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 |