922 lines
34 KiB
Java
922 lines
34 KiB
Java
![]() |
/**
|
||
|
* Copyright (C) 2016 The Android Open Source Project
|
||
|
*
|
||
|
* <p>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
|
||
|
*
|
||
|
* <p>http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* <p>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 static java.util.Objects.requireNonNull;
|
||
|
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
|
||
|
/**
|
||
|
* Out Of Band Data for Bluetooth device pairing.
|
||
|
*
|
||
|
* <p>This object represents optional data obtained from a remote device through an out-of-band
|
||
|
* channel (eg. NFC, QR).
|
||
|
*
|
||
|
* <p>References: <a
|
||
|
* href="https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf">NFC
|
||
|
* AD Forum SSP 1.1 (AD)</a>
|
||
|
*
|
||
|
* <p>Core Specification Supplement (CSS) V9
|
||
|
*
|
||
|
* <p>There are several BR/EDR Examples
|
||
|
*
|
||
|
* <p>Negotiated Handover: Bluetooth Carrier Configuration Record: - OOB Data Length - Device
|
||
|
* Address - Class of Device - Simple Pairing Hash C - Simple Pairing Randomizer R - Service Class
|
||
|
* UUID - Bluetooth Local Name
|
||
|
*
|
||
|
* <p>Static Handover: Bluetooth Carrier Configuration Record: - OOB Data Length - Device Address -
|
||
|
* Class of Device - Service Class UUID - Bluetooth Local Name
|
||
|
*
|
||
|
* <p>Simplified Tag Format for Single BT Carrier: Bluetooth OOB Data Record: - OOB Data Length -
|
||
|
* Device Address - Class of Device - Service Class UUID - Bluetooth Local Name
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public final class OobData implements Parcelable {
|
||
|
|
||
|
private static final String TAG = "OobData";
|
||
|
|
||
|
/** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
|
||
|
@SystemApi public static final int OOB_LENGTH_OCTETS = 2;
|
||
|
|
||
|
/**
|
||
|
* The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). (AD 3.1.2)
|
||
|
* (CSS 1.6.2)
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi public static final int DEVICE_ADDRESS_OCTETS = 7;
|
||
|
|
||
|
/** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
|
||
|
@SystemApi public static final int CLASS_OF_DEVICE_OCTETS = 3;
|
||
|
|
||
|
/** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
|
||
|
@SystemApi public static final int CONFIRMATION_OCTETS = 16;
|
||
|
|
||
|
/** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
|
||
|
@SystemApi public static final int RANDOMIZER_OCTETS = 16;
|
||
|
|
||
|
/** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_ROLE_OCTETS = 1;
|
||
|
|
||
|
/** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
|
||
|
@SystemApi public static final int LE_TK_OCTETS = 16;
|
||
|
|
||
|
/** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
|
||
|
@SystemApi public static final int LE_APPEARANCE_OCTETS = 2;
|
||
|
|
||
|
/** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
|
||
|
|
||
|
// Le Roles
|
||
|
/** @hide */
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(
|
||
|
prefix = {"LE_DEVICE_ROLE_"},
|
||
|
value = {
|
||
|
LE_DEVICE_ROLE_PERIPHERAL_ONLY,
|
||
|
LE_DEVICE_ROLE_CENTRAL_ONLY,
|
||
|
LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
|
||
|
LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
|
||
|
})
|
||
|
public @interface LeRole {}
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
|
||
|
|
||
|
// Le Flags
|
||
|
/** @hide */
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(
|
||
|
prefix = {"LE_FLAG_"},
|
||
|
value = {
|
||
|
LE_FLAG_LIMITED_DISCOVERY_MODE,
|
||
|
LE_FLAG_GENERAL_DISCOVERY_MODE,
|
||
|
LE_FLAG_BREDR_NOT_SUPPORTED,
|
||
|
LE_FLAG_SIMULTANEOUS_CONTROLLER,
|
||
|
LE_FLAG_SIMULTANEOUS_HOST
|
||
|
})
|
||
|
public @interface LeFlag {}
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
|
||
|
|
||
|
/** @hide */
|
||
|
@SystemApi public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
|
||
|
|
||
|
/**
|
||
|
* Builds an {@link OobData} object and validates that the required combination of values are
|
||
|
* present to create the LE specific OobData type.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final class LeBuilder {
|
||
|
|
||
|
/**
|
||
|
* It is recommended that this Hash C is generated anew for each pairing.
|
||
|
*
|
||
|
* <p>It should be noted that on passive NFC this isn't possible as the data is static and
|
||
|
* immutable.
|
||
|
*/
|
||
|
private byte[] mConfirmationHash = null;
|
||
|
|
||
|
/**
|
||
|
* Optional, but adds more validity to the pairing.
|
||
|
*
|
||
|
* <p>If not present a value of 0 is assumed.
|
||
|
*/
|
||
|
private byte[] mRandomizerHash =
|
||
|
new byte[] {
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* The Bluetooth Device user-friendly name presented over Bluetooth Technology.
|
||
|
*
|
||
|
* <p>This is the name that may be displayed to the device user as part of the UI.
|
||
|
*/
|
||
|
private byte[] mDeviceName = null;
|
||
|
|
||
|
/**
|
||
|
* Sets the Bluetooth Device name to be used for UI purposes.
|
||
|
*
|
||
|
* <p>Optional attribute.
|
||
|
*
|
||
|
* @param deviceName byte array representing the name, may be 0 in length, not null.
|
||
|
* @return {@link OobData#ClassicBuilder}
|
||
|
* @throws NullPointerException if deviceName is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
|
||
|
requireNonNull(deviceName);
|
||
|
this.mDeviceName = deviceName;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The Bluetooth Device Address is the address to which the OOB data belongs.
|
||
|
*
|
||
|
* <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
|
||
|
*
|
||
|
* <p>Address is encoded in Little Endian order.
|
||
|
*
|
||
|
* <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
|
||
|
*/
|
||
|
private final byte[] mDeviceAddressWithType;
|
||
|
|
||
|
/**
|
||
|
* During an LE connection establishment, one must be in the Peripheral mode and the other
|
||
|
* in the Central role.
|
||
|
*
|
||
|
* <p>Possible Values: {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
|
||
|
* {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported {@link
|
||
|
* LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; Peripheral
|
||
|
* Preferred {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central
|
||
|
* Preferred 0x04 - 0xFF Reserved
|
||
|
*/
|
||
|
private final @LeRole int mLeDeviceRole;
|
||
|
|
||
|
/**
|
||
|
* Temporary key value from the Security Manager.
|
||
|
*
|
||
|
* <p>Must be {@link LE_TK_OCTETS} in size
|
||
|
*/
|
||
|
private byte[] mLeTemporaryKey = null;
|
||
|
|
||
|
/**
|
||
|
* Defines the representation of the external appearance of the device.
|
||
|
*
|
||
|
* <p>For example, a mouse, remote control, or keyboard.
|
||
|
*
|
||
|
* <p>Used for visual on discovering device to represent icon/string/etc...
|
||
|
*/
|
||
|
private byte[] mLeAppearance = null;
|
||
|
|
||
|
/**
|
||
|
* Contains which discoverable mode to use, BR/EDR support and capability.
|
||
|
*
|
||
|
* <p>Possible LE Flags: {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable
|
||
|
* Mode. {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. {@link
|
||
|
* LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of LMP Feature Mask
|
||
|
* Definitions. {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to Same
|
||
|
* Device Capable (Controller). Bit 49 of LMP Feature Mask Definitions. {@link
|
||
|
* LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to Same Device Capable (Host). Bit
|
||
|
* 55 of LMP Feature Mask Definitions. <b>0x05- 0x07 Reserved</b>
|
||
|
*/
|
||
|
private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
|
||
|
|
||
|
/**
|
||
|
* Main creation method for creating a LE version of {@link OobData}.
|
||
|
*
|
||
|
* <p>This object will allow the caller to call {@link LeBuilder#build()} to build the data
|
||
|
* object or add any option information to the builder.
|
||
|
*
|
||
|
* @param deviceAddressWithType the LE device address plus the address type (7 octets); not
|
||
|
* null.
|
||
|
* @param leDeviceRole whether the device supports Peripheral, Central, Both including
|
||
|
* preference; not null. (1 octet)
|
||
|
* @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets of
|
||
|
* data. Data is derived from controller/host stack and is required for pairing OOB.
|
||
|
* <p>Possible Values: {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
|
||
|
* {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported {@link
|
||
|
* LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; Peripheral
|
||
|
* Preferred {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported;
|
||
|
* Central Preferred 0x04 - 0xFF Reserved
|
||
|
* @throws IllegalArgumentException if any of the values fail to be set.
|
||
|
* @throws NullPointerException if any argument is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public LeBuilder(
|
||
|
@NonNull byte[] confirmationHash,
|
||
|
@NonNull byte[] deviceAddressWithType,
|
||
|
@LeRole int leDeviceRole) {
|
||
|
requireNonNull(confirmationHash);
|
||
|
requireNonNull(deviceAddressWithType);
|
||
|
if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"confirmationHash must be "
|
||
|
+ OobData.CONFIRMATION_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mConfirmationHash = confirmationHash;
|
||
|
if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"confirmationHash must be "
|
||
|
+ OobData.DEVICE_ADDRESS_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mDeviceAddressWithType = deviceAddressWithType;
|
||
|
if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
|
||
|
|| leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
|
||
|
throw new IllegalArgumentException("leDeviceRole must be a valid value.");
|
||
|
}
|
||
|
this.mLeDeviceRole = leDeviceRole;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the Temporary Key value to be used by the LE Security Manager during LE pairing.
|
||
|
*
|
||
|
* @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6, Part
|
||
|
* A 1.8 for a detailed description.
|
||
|
* @return {@link OobData#Builder}
|
||
|
* @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
|
||
|
* @throws NullinterException if leTemporaryKey is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
|
||
|
requireNonNull(leTemporaryKey);
|
||
|
if (leTemporaryKey.length != LE_TK_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"leTemporaryKey must be " + LE_TK_OCTETS + " octets in length.");
|
||
|
}
|
||
|
this.mLeTemporaryKey = leTemporaryKey;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
|
||
|
* of data. Data is derived from controller/host stack and is required for pairing OOB.
|
||
|
* Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
|
||
|
* @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
|
||
|
* @throws NullPointerException if randomizerHash is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
|
||
|
requireNonNull(randomizerHash);
|
||
|
if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"randomizerHash must be "
|
||
|
+ OobData.RANDOMIZER_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mRandomizerHash = randomizerHash;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the LE Flags necessary for the pairing scenario or discovery mode.
|
||
|
*
|
||
|
* @param leFlags enum value representing the 1 octet of data about discovery modes.
|
||
|
* <p>Possible LE Flags: {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable
|
||
|
* Mode. {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. {@link
|
||
|
* LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of LMP Feature Mask
|
||
|
* Definitions. {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
|
||
|
* Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions. {@link
|
||
|
* LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to Same Device Capable (Host).
|
||
|
* Bit 55 of LMP Feature Mask Definitions. 0x05- 0x07 Reserved
|
||
|
* @throws IllegalArgumentException for invalid flag
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public LeBuilder setLeFlags(@LeFlag int leFlags) {
|
||
|
if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
|
||
|
throw new IllegalArgumentException("leFlags must be a valid value.");
|
||
|
}
|
||
|
this.mLeFlags = leFlags;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validates and builds the {@link OobData} object for LE Security.
|
||
|
*
|
||
|
* @return {@link OobData} with given builder values
|
||
|
* @throws IllegalStateException if either of the 2 required fields were not set.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public OobData build() {
|
||
|
final OobData oob =
|
||
|
new OobData(
|
||
|
this.mDeviceAddressWithType,
|
||
|
this.mLeDeviceRole,
|
||
|
this.mConfirmationHash);
|
||
|
|
||
|
// If we have values, set them, otherwise use default
|
||
|
oob.mLeTemporaryKey =
|
||
|
(this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
|
||
|
oob.mLeAppearance =
|
||
|
(this.mLeAppearance != null) ? this.mLeAppearance : oob.mLeAppearance;
|
||
|
oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
|
||
|
oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
|
||
|
oob.mRandomizerHash = this.mRandomizerHash;
|
||
|
return oob;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Builds an {@link OobData} object and validates that the required combination of values are
|
||
|
* present to create the Classic specific OobData type.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final class ClassicBuilder {
|
||
|
// Used by both Classic and LE
|
||
|
/**
|
||
|
* It is recommended that this Hash C is generated anew for each pairing.
|
||
|
*
|
||
|
* <p>It should be noted that on passive NFC this isn't possible as the data is static and
|
||
|
* immutable.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private byte[] mConfirmationHash = null;
|
||
|
|
||
|
/**
|
||
|
* Optional, but adds more validity to the pairing.
|
||
|
*
|
||
|
* <p>If not present a value of 0 is assumed.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private byte[] mRandomizerHash =
|
||
|
new byte[] {
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* The Bluetooth Device user-friendly name presented over Bluetooth Technology.
|
||
|
*
|
||
|
* <p>This is the name that may be displayed to the device user as part of the UI.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private byte[] mDeviceName = null;
|
||
|
|
||
|
/**
|
||
|
* This length value provides the absolute length of total OOB data block used for Bluetooth
|
||
|
* BR/EDR
|
||
|
*
|
||
|
* <p>OOB communication, which includes the length field itself and the Bluetooth Device
|
||
|
* Address.
|
||
|
*
|
||
|
* <p>The minimum length that may be represented in this field is 8.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private final byte[] mClassicLength;
|
||
|
|
||
|
/**
|
||
|
* The Bluetooth Device Address is the address to which the OOB data belongs.
|
||
|
*
|
||
|
* <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
|
||
|
*
|
||
|
* <p>Address is encoded in Little Endian order.
|
||
|
*
|
||
|
* <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private final byte[] mDeviceAddressWithType;
|
||
|
|
||
|
/**
|
||
|
* Class of Device information is to be used to provide a graphical representation to the
|
||
|
* user as part of UI involving operations.
|
||
|
*
|
||
|
* <p>This is not to be used to determine a particular service can be used.
|
||
|
*
|
||
|
* <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
private byte[] mClassOfDevice = null;
|
||
|
|
||
|
/**
|
||
|
* Main creation method for creating a Classic version of {@link OobData}.
|
||
|
*
|
||
|
* <p>This object will allow the caller to call {@link ClassicBuilder#build()} to build the
|
||
|
* data object or add any option information to the builder.
|
||
|
*
|
||
|
* @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
|
||
|
* octets of data. Data is derived from controller/host stack and is required for
|
||
|
* pairing OOB.
|
||
|
* @param classicLength byte array representing the length of data from 8-65535 across 2
|
||
|
* octets (0xXXXX).
|
||
|
* @param deviceAddressWithType byte array representing the Bluetooth Address of the device
|
||
|
* that owns the OOB data. (i.e. the originator) [6 octets]
|
||
|
* @throws IllegalArgumentException if any of the values fail to be set.
|
||
|
* @throws NullPointerException if any argument is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public ClassicBuilder(
|
||
|
@NonNull byte[] confirmationHash,
|
||
|
@NonNull byte[] classicLength,
|
||
|
@NonNull byte[] deviceAddressWithType) {
|
||
|
requireNonNull(confirmationHash);
|
||
|
requireNonNull(classicLength);
|
||
|
requireNonNull(deviceAddressWithType);
|
||
|
if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"confirmationHash must be "
|
||
|
+ OobData.CONFIRMATION_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mConfirmationHash = confirmationHash;
|
||
|
if (classicLength.length != OOB_LENGTH_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"classicLength must be " + OOB_LENGTH_OCTETS + " octets in length.");
|
||
|
}
|
||
|
this.mClassicLength = classicLength;
|
||
|
if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"deviceAddressWithType must be "
|
||
|
+ DEVICE_ADDRESS_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mDeviceAddressWithType = deviceAddressWithType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
|
||
|
* of data. Data is derived from controller/host stack and is required for pairing OOB.
|
||
|
* Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
|
||
|
* @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
|
||
|
* @throws NullPointerException if randomizerHash is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
|
||
|
requireNonNull(randomizerHash);
|
||
|
if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"randomizerHash must be "
|
||
|
+ OobData.RANDOMIZER_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mRandomizerHash = randomizerHash;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the Bluetooth Device name to be used for UI purposes.
|
||
|
*
|
||
|
* <p>Optional attribute.
|
||
|
*
|
||
|
* @param deviceName byte array representing the name, may be 0 in length, not null.
|
||
|
* @return {@link OobData#ClassicBuilder}
|
||
|
* @throws NullPointerException if deviceName is null
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
|
||
|
requireNonNull(deviceName);
|
||
|
this.mDeviceName = deviceName;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the Bluetooth Class of Device; used for UI purposes only.
|
||
|
*
|
||
|
* <p>Not an indicator of available services!
|
||
|
*
|
||
|
* <p>Optional attribute.
|
||
|
*
|
||
|
* @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
|
||
|
* @return {@link OobData#ClassicBuilder}
|
||
|
* @throws IllegalArgumentException if length is not equal to {@link
|
||
|
* OobData#CLASS_OF_DEVICE_OCTETS} octets.
|
||
|
* @throws NullPointerException if classOfDevice is null.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
|
||
|
requireNonNull(classOfDevice);
|
||
|
if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"classOfDevice must be "
|
||
|
+ OobData.CLASS_OF_DEVICE_OCTETS
|
||
|
+ " octets in length.");
|
||
|
}
|
||
|
this.mClassOfDevice = classOfDevice;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validates and builds the {@link OobData} object for Classic Security.
|
||
|
*
|
||
|
* @return {@link OobData} with previously given builder values.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public OobData build() {
|
||
|
final OobData oob =
|
||
|
new OobData(
|
||
|
this.mClassicLength,
|
||
|
this.mDeviceAddressWithType,
|
||
|
this.mConfirmationHash);
|
||
|
// If we have values, set them, otherwise use default
|
||
|
oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
|
||
|
oob.mClassOfDevice =
|
||
|
(this.mClassOfDevice != null) ? this.mClassOfDevice : oob.mClassOfDevice;
|
||
|
oob.mRandomizerHash = this.mRandomizerHash;
|
||
|
return oob;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Members (Defaults for Optionals must be set or Parceling fails on NPE)
|
||
|
// Both
|
||
|
private final byte[] mDeviceAddressWithType;
|
||
|
private final byte[] mConfirmationHash;
|
||
|
private byte[] mRandomizerHash =
|
||
|
new byte[] {
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||
|
};
|
||
|
// Default the name to "Bluetooth Device"
|
||
|
private byte[] mDeviceName =
|
||
|
new byte[] {
|
||
|
// Bluetooth
|
||
|
0x42,
|
||
|
0x6c,
|
||
|
0x75,
|
||
|
0x65,
|
||
|
0x74,
|
||
|
0x6f,
|
||
|
0x6f,
|
||
|
0x74,
|
||
|
0x68,
|
||
|
// <space>Device
|
||
|
0x20,
|
||
|
0x44,
|
||
|
0x65,
|
||
|
0x76,
|
||
|
0x69,
|
||
|
0x63,
|
||
|
0x65
|
||
|
};
|
||
|
|
||
|
// Classic
|
||
|
private final byte[] mClassicLength;
|
||
|
private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
|
||
|
|
||
|
// LE
|
||
|
private final @LeRole int mLeDeviceRole;
|
||
|
private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
|
||
|
private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
|
||
|
private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
|
||
|
|
||
|
/**
|
||
|
* @return byte array representing the MAC address of a bluetooth device. The Address is 6
|
||
|
* octets long with a 1 octet address type associated with the address.
|
||
|
* <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address
|
||
|
* Type. For LE there are more choices for Address Type.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public byte[] getDeviceAddressWithType() {
|
||
|
return mDeviceAddressWithType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return byte array representing the confirmationHash value which is used to confirm the
|
||
|
* identity to the controller.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public byte[] getConfirmationHash() {
|
||
|
return mConfirmationHash;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return byte array representing the randomizerHash value which is used to verify the identity
|
||
|
* of the controller.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public byte[] getRandomizerHash() {
|
||
|
return mRandomizerHash;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Device Name used for displaying name in UI.
|
||
|
* <p>Also, this will be populated with the LE Local Name if the data is for LE.
|
||
|
* @hide
|
||
|
*/
|
||
|
@Nullable
|
||
|
@SystemApi
|
||
|
public byte[] getDeviceName() {
|
||
|
return mDeviceName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return byte array representing the oob data length which is the length of all of the data
|
||
|
* including these octets.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public byte[] getClassicLength() {
|
||
|
return mClassicLength;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return byte array representing the class of device for UI display.
|
||
|
* <p>Does not indicate services available; for display only.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
public byte[] getClassOfDevice() {
|
||
|
return mClassOfDevice;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Temporary Key used for LE pairing.
|
||
|
* @hide
|
||
|
*/
|
||
|
@Nullable
|
||
|
@SystemApi
|
||
|
public byte[] getLeTemporaryKey() {
|
||
|
return mLeTemporaryKey;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Appearance used for LE pairing. For use in UI situations when determining what sort
|
||
|
* of icons or text to display regarding the device.
|
||
|
* @hide
|
||
|
*/
|
||
|
@Nullable
|
||
|
@SystemApi
|
||
|
public byte[] getLeAppearance() {
|
||
|
return mLeAppearance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Flags used to determining discoverable mode to use, BR/EDR Support, and Capability.
|
||
|
* <p>Possible LE Flags: {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable
|
||
|
* Mode. {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. {@link
|
||
|
* LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of LMP Feature Mask
|
||
|
* Definitions. {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to Same
|
||
|
* Device Capable (Controller). Bit 49 of LMP Feature Mask Definitions. {@link
|
||
|
* LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to Same Device Capable (Host). Bit
|
||
|
* 55 of LMP Feature Mask Definitions. <b>0x05- 0x07 Reserved</b>
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
@LeFlag
|
||
|
public int getLeFlags() {
|
||
|
return mLeFlags;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the supported and preferred roles of the LE device.
|
||
|
* <p>Possible Values: {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
|
||
|
* {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported {@link
|
||
|
* LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; Peripheral
|
||
|
* Preferred {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central
|
||
|
* Preferred 0x04 - 0xFF Reserved
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
@SystemApi
|
||
|
@LeRole
|
||
|
public int getLeDeviceRole() {
|
||
|
return mLeDeviceRole;
|
||
|
}
|
||
|
|
||
|
/** Classic Security Constructor */
|
||
|
private OobData(
|
||
|
@NonNull byte[] classicLength,
|
||
|
@NonNull byte[] deviceAddressWithType,
|
||
|
@NonNull byte[] confirmationHash) {
|
||
|
mClassicLength = classicLength;
|
||
|
mDeviceAddressWithType = deviceAddressWithType;
|
||
|
mConfirmationHash = confirmationHash;
|
||
|
mLeDeviceRole = -1; // Satisfy final
|
||
|
}
|
||
|
|
||
|
/** LE Security Constructor */
|
||
|
private OobData(
|
||
|
@NonNull byte[] deviceAddressWithType,
|
||
|
@LeRole int leDeviceRole,
|
||
|
@NonNull byte[] confirmationHash) {
|
||
|
mDeviceAddressWithType = deviceAddressWithType;
|
||
|
mLeDeviceRole = leDeviceRole;
|
||
|
mConfirmationHash = confirmationHash;
|
||
|
mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
|
||
|
}
|
||
|
|
||
|
private OobData(Parcel in) {
|
||
|
// Both
|
||
|
mDeviceAddressWithType = in.createByteArray();
|
||
|
mConfirmationHash = in.createByteArray();
|
||
|
mRandomizerHash = in.createByteArray();
|
||
|
mDeviceName = in.createByteArray();
|
||
|
|
||
|
// Classic
|
||
|
mClassicLength = in.createByteArray();
|
||
|
mClassOfDevice = in.createByteArray();
|
||
|
|
||
|
// LE
|
||
|
mLeDeviceRole = in.readInt();
|
||
|
mLeTemporaryKey = in.createByteArray();
|
||
|
mLeAppearance = in.createByteArray();
|
||
|
mLeFlags = in.readInt();
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||
|
// Both
|
||
|
// Required
|
||
|
out.writeByteArray(mDeviceAddressWithType);
|
||
|
// Required
|
||
|
out.writeByteArray(mConfirmationHash);
|
||
|
// Optional
|
||
|
out.writeByteArray(mRandomizerHash);
|
||
|
// Optional
|
||
|
out.writeByteArray(mDeviceName);
|
||
|
|
||
|
// Classic
|
||
|
// Required
|
||
|
out.writeByteArray(mClassicLength);
|
||
|
// Optional
|
||
|
out.writeByteArray(mClassOfDevice);
|
||
|
|
||
|
// LE
|
||
|
// Required
|
||
|
out.writeInt(mLeDeviceRole);
|
||
|
// Required
|
||
|
out.writeByteArray(mLeTemporaryKey);
|
||
|
// Optional
|
||
|
out.writeByteArray(mLeAppearance);
|
||
|
// Optional
|
||
|
out.writeInt(mLeFlags);
|
||
|
}
|
||
|
|
||
|
// For Parcelable
|
||
|
public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
|
||
|
new Parcelable.Creator<OobData>() {
|
||
|
public OobData createFromParcel(Parcel in) {
|
||
|
return new OobData(in);
|
||
|
}
|
||
|
|
||
|
public OobData[] newArray(int size) {
|
||
|
return new OobData[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @return a {@link String} representation of the OobData object.
|
||
|
* @hide
|
||
|
*/
|
||
|
@Override
|
||
|
@NonNull
|
||
|
public String toString() {
|
||
|
return "OobData: \n\t"
|
||
|
// Both
|
||
|
+ "Device Address With Type: "
|
||
|
+ toHexString(mDeviceAddressWithType)
|
||
|
+ "\n\t"
|
||
|
+ "Confirmation: "
|
||
|
+ toHexString(mConfirmationHash)
|
||
|
+ "\n\t"
|
||
|
+ "Randomizer: "
|
||
|
+ toHexString(mRandomizerHash)
|
||
|
+ "\n\t"
|
||
|
+ "Device Name: "
|
||
|
+ toHexString(mDeviceName)
|
||
|
+ "\n\t"
|
||
|
// Classic
|
||
|
+ "OobData Length: "
|
||
|
+ toHexString(mClassicLength)
|
||
|
+ "\n\t"
|
||
|
+ "Class of Device: "
|
||
|
+ toHexString(mClassOfDevice)
|
||
|
+ "\n\t"
|
||
|
// LE
|
||
|
+ "LE Device Role: "
|
||
|
+ toHexString(mLeDeviceRole)
|
||
|
+ "\n\t"
|
||
|
+ "LE Temporary Key: "
|
||
|
+ toHexString(mLeTemporaryKey)
|
||
|
+ "\n\t"
|
||
|
+ "LE Appearance: "
|
||
|
+ toHexString(mLeAppearance)
|
||
|
+ "\n\t"
|
||
|
+ "LE Flags: "
|
||
|
+ toHexString(mLeFlags)
|
||
|
+ "\n\t";
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
private String toHexString(int b) {
|
||
|
return toHexString(new byte[] {(byte) b});
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
private String toHexString(byte[] array) {
|
||
|
if (array == null) return "null";
|
||
|
StringBuilder builder = new StringBuilder(array.length * 2);
|
||
|
for (byte b : array) {
|
||
|
builder.append(String.format("%02x", b));
|
||
|
}
|
||
|
return builder.toString();
|
||
|
}
|
||
|
}
|