/* * 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 android.bluetooth; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import com.android.bluetooth.flags.Flags; 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. Bluetooth service * UUIDs are defined in the SDP section of the Bluetooth Assigned Numbers document. The constant 128 * bit values in this class are calculated as: uuid * 2^96 + {@link #BASE_UUID}. * * @hide */ @SystemApi @SuppressLint("AndroidFrameworkBluetoothPermission") public final class BluetoothUuid { /** * UUID corresponding to the Audio sink role (also referred to as the A2DP sink role). * * @hide */ @NonNull @SystemApi public static final ParcelUuid A2DP_SINK = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Audio source role (also referred to as the A2DP source role). * * @hide */ @NonNull @SystemApi public static final ParcelUuid A2DP_SOURCE = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Advanced Audio Distribution Profile (A2DP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid ADV_AUDIO_DIST = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Headset Profile (HSP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Headset Profile (HSP) Audio Gateway role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Hands-Free Profile (HFP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid HFP = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Hands-Free Profile (HFP) Audio Gateway role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid HFP_AG = ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Audio Video Remote Control Profile (AVRCP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid AVRCP = ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Audio Video Remote Control Profile (AVRCP) controller role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid AVRCP_CONTROLLER = ParcelUuid.fromString("0000110F-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Audio Video Remote Control Profile (AVRCP) target role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid AVRCP_TARGET = ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the OBject EXchange (OBEX) Object Push Profile (OPP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid OBEX_OBJECT_PUSH = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); /** * UUID corresponding to the Human Interface Device (HID) profile. * * @hide */ @NonNull @SystemApi public static final ParcelUuid HID = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); /** * UUID corresponding to the Human Interface Device over GATT Profile (HOGP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid HOGP = ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); /** * UUID corresponding to the Personal Area Network User (PANU) role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Network Access Point (NAP) role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Bluetooth Network Encapsulation Protocol (BNEP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Phonebook Access Profile (PBAP) client role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid PBAP_PCE = ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Phonebook Access Profile (PBAP) server role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Message Access Profile (MAP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid MAP = ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Message Notification Server (MNS) role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid MNS = ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Message Access Server (MAS) role. * * @hide */ @NonNull @SystemApi public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Sim Access Profile (SAP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Hearing Aid Profile. * * @hide */ @NonNull @SystemApi public static final ParcelUuid HEARING_AID = ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); /** * UUID corresponding to the Hearing Access Service (HAS). * * @hide */ @NonNull @SystemApi public static final ParcelUuid HAS = ParcelUuid.fromString("00001854-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Made For iPhone/iPod/iPad Hearing Aid Service (MFi HAS). * * @hide */ @NonNull @SystemApi @FlaggedApi(Flags.FLAG_MFI_HAS_UUID) public static final ParcelUuid MFI_HAS = ParcelUuid.fromString("7D74F4BD-C74A-4431-862C-CCE884371592"); /** * UUID corresponding to Audio Stream Control (also known as Bluetooth Low Energy Audio). * * @hide */ @NonNull @SystemApi public static final ParcelUuid LE_AUDIO = ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Device Identification Profile (DIP). * * @hide */ @NonNull @SystemApi public static final ParcelUuid DIP = ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Volume Control Service. * * @hide */ @NonNull @SystemApi public static final ParcelUuid VOLUME_CONTROL = ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Generic Media Control Service. * * @hide */ @NonNull @SystemApi public static final ParcelUuid GENERIC_MEDIA_CONTROL = ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Media Control Service. * * @hide */ @NonNull @SystemApi public static final ParcelUuid MEDIA_CONTROL = ParcelUuid.fromString("00001848-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Coordinated Set Identification Service. * * @hide */ @NonNull @SystemApi public static final ParcelUuid COORDINATED_SET = ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Common Audio Service. * * @hide */ @NonNull @SystemApi public static final ParcelUuid CAP = ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB"); /** * UUID corresponding to the Broadcast Audio Scan Service (also known as LE Audio Broadcast * Assistant). * * @hide */ @NonNull public static final ParcelUuid BATTERY = ParcelUuid.fromString("0000180F-0000-1000-8000-00805F9B34FB"); /** @hide */ @NonNull @SystemApi public static final ParcelUuid BASS = ParcelUuid.fromString("0000184F-0000-1000-8000-00805F9B34FB"); /** * Telephony and Media Audio Profile (TMAP) UUID * * @hide */ @NonNull public static final ParcelUuid TMAP = ParcelUuid.fromString("00001855-0000-1000-8000-00805F9B34FB"); /** @hide */ @NonNull @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); /** * Length of bytes for 16 bit UUID * * @hide */ @SystemApi public static final int UUID_BYTES_16_BIT = 2; /** * Length of bytes for 32 bit UUID * * @hide */ @SystemApi public static final int UUID_BYTES_32_BIT = 4; /** * Length of bytes for 128 bit UUID * * @hide */ @SystemApi public static final int UUID_BYTES_128_BIT = 16; /** * Returns true if there any common ParcelUuids in uuidA and uuidB. * * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids * @hide */ @SystemApi public static boolean containsAnyUuid( @Nullable ParcelUuid[] uuidA, @Nullable ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; if (uuidA == null) { return uuidB.length == 0; } if (uuidB == null) { return uuidA.length == 0; } HashSet uuidSet = new HashSet(Arrays.asList(uuidA)); for (ParcelUuid uuid : uuidB) { if (uuidSet.contains(uuid)) return true; } return false; } /** * 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 */ private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 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. * @hide */ @NonNull @SystemApi public static ParcelUuid parseUuidFrom(@Nullable 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)); } /** * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or * 128-bit UUID, Note returned value is little endian (Bluetooth). * * @param uuid uuid to parse. * @return shortest representation of {@code uuid} as bytes. * @throws IllegalArgumentException If the {@code uuid} is null. * @hide */ public static byte[] uuidToBytes(ParcelUuid uuid) { if (uuid == null) { throw new IllegalArgumentException("uuid cannot be null"); } if (is16BitUuid(uuid)) { byte[] uuidBytes = new byte[UUID_BYTES_16_BIT]; int uuidVal = getServiceIdentifierFromParcelUuid(uuid); uuidBytes[0] = (byte) (uuidVal & 0xFF); uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); return uuidBytes; } if (is32BitUuid(uuid)) { byte[] uuidBytes = new byte[UUID_BYTES_32_BIT]; int uuidVal = getServiceIdentifierFromParcelUuid(uuid); uuidBytes[0] = (byte) (uuidVal & 0xFF); uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16); uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24); return uuidBytes; } // Construct a 128 bit UUID. long msb = uuid.getUuid().getMostSignificantBits(); long lsb = uuid.getUuid().getLeastSignificantBits(); byte[] uuidBytes = new byte[UUID_BYTES_128_BIT]; ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); buf.putLong(8, msb); buf.putLong(0, lsb); return uuidBytes; } /** * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. * * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. * @hide */ @UnsupportedAppUsage 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. * * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. * @hide */ @UnsupportedAppUsage 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); } private BluetoothUuid() {} }