[LeAudio] Add LocalBluetoothLeAudioContentMetadata and LocalBluetoothLeBroadcastMetadata

Test: manual
Bug: 222676140
Change-Id: I3722ce537cc6e2b8a85d6390a432881e7fccef87
This commit is contained in:
Zoey Chen
2022-03-10 15:17:53 +08:00
parent 26493a5ccc
commit 8764c4115b
3 changed files with 397 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 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.android.settingslib.bluetooth;
public final class BluetoothBroadcastUtils {
static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
// BluetoothLeBroadcastMetadata
static final String PREFIX_BT_ADDRESS_TYPE = "T:";
static final String PREFIX_BT_DEVICE = "D:";
static final String PREFIX_BT_ADVERTISING_SID = "AS:";
static final String PREFIX_BT_BROADCAST_ID = "B:";
static final String PREFIX_BT_SYNC_INTERVAL = "SI:";
static final String PREFIX_BT_IS_ENCRYPTED = "E:";
static final String PREFIX_BT_BROADCAST_CODE = "C:";
static final String PREFIX_BT_PRESENTATION_DELAY = "D:";
static final String PREFIX_BT_SUBGROUPS = "G:";
static final String PREFIX_BT_ANDROID_VERSION = "V:";
// BluetoothLeBroadcastSubgroup
static final String PREFIX_BTSG_CODEC_ID = "CID:";
static final String PREFIX_BTSG_CODEC_CONFIG = "CC:";
static final String PREFIX_BTSG_AUDIO_CONTENT = "AC:";
static final String PREFIX_BTSG_CHANNEL_PREF = "CP:";
static final String PREFIX_BTSG_BROADCAST_CHANNEL = "BC:";
// BluetoothLeAudioCodecConfigMetadata
static final String PREFIX_BTCC_AUDIO_LOCATION = "AL:";
static final String PREFIX_BTCC_RAW_METADATA = "CCRM:";
// BluetoothLeAudioContentMetadata
static final String PREFIX_BTAC_PROGRAM_INFO = "PI:";
static final String PREFIX_BTAC_LANGUAGE = "L:";
static final String PREFIX_BTAC_RAW_METADATA = "ACRM:";
// BluetoothLeBroadcastChannel
static final String PREFIX_BTBC_CHANNEL_INDEX = "CI:";
static final String PREFIX_BTBC_CODEC_CONFIG = "BCCM:";
static final String DELIMITER_QR_CODE = ";";
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2022 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.android.settingslib.bluetooth;
import android.bluetooth.BluetoothLeAudioContentMetadata;
public class LocalBluetoothLeAudioContentMetadata {
private static final String TAG = "LocalBluetoothLeAudioContentMetadata";
private final BluetoothLeAudioContentMetadata mContentMetadata;
private final String mLanguage;
private final byte[] mRawMetadata;
private String mProgramInfo;
LocalBluetoothLeAudioContentMetadata(BluetoothLeAudioContentMetadata contentMetadata) {
mContentMetadata = contentMetadata;
mProgramInfo = contentMetadata.getProgramInfo();
mLanguage = contentMetadata.getLanguage();
mRawMetadata = contentMetadata.getRawMetadata();
}
public void setProgramInfo(String programInfo) {
mProgramInfo = programInfo;
}
public String getProgramInfo() {
return mProgramInfo;
}
}

View File

@@ -0,0 +1,298 @@
/*
* Copyright (C) 2022 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.android.settingslib.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastChannel;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LocalBluetoothLeBroadcastMetadata {
private static final boolean DEBUG = BluetoothUtils.D;
private static final String TAG = "LocalBluetoothLeBroadcastMetadata";
private static final String METADATA_START = "<";
private static final String METADATA_END = ">";
private static final String PATTERN_REGEX = "<(.*?)>";
private BluetoothLeBroadcastSubgroup mSubgroup;
private List<BluetoothLeBroadcastSubgroup> mSubgroupList;
// BluetoothLeBroadcastMetadata
// Optional: Identity address type
private int mSourceAddressType;
// Optional: Must use identity address
private BluetoothDevice mSourceDevice;
private int mSourceAdvertisingSid;
private int mBroadcastId;
private int mPaSyncInterval;
private int mPresentationDelayMicros;
private boolean mIsEncrypted;
private byte[] mBroadcastCode;
// BluetoothLeBroadcastSubgroup
private long mCodecId;
private BluetoothLeAudioContentMetadata mContentMetadata;
private BluetoothLeAudioCodecConfigMetadata mConfigMetadata;
private BluetoothLeBroadcastChannel mChannel;
// BluetoothLeAudioCodecConfigMetadata
private long mAudioLocation;
// BluetoothLeAudioContentMetadata
private String mLanguage;
private String mProgramInfo;
// BluetoothLeBroadcastChannel
private boolean mIsSelected;
private int mChannelIndex;
LocalBluetoothLeBroadcastMetadata(BluetoothLeBroadcastMetadata metadata) {
mSourceAddressType = metadata.getSourceAddressType();
mSourceDevice = metadata.getSourceDevice();
mSourceAdvertisingSid = metadata.getSourceAdvertisingSid();
mBroadcastId = metadata.getBroadcastId();
mPaSyncInterval = metadata.getPaSyncInterval();
mIsEncrypted = metadata.isEncrypted();
mBroadcastCode = metadata.getBroadcastCode();
mPresentationDelayMicros = metadata.getPresentationDelayMicros();
mSubgroupList = metadata.getSubgroups();
}
public void setBroadcastCode(byte[] code) {
mBroadcastCode = code;
}
public int getBroadcastId() {
return mBroadcastId;
}
public String convertToQrCodeString() {
return new StringBuilder()
.append(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)
.append(BluetoothBroadcastUtils.PREFIX_BT_ADDRESS_TYPE)
.append(METADATA_START).append(mSourceAddressType).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_DEVICE)
.append(METADATA_START).append(mSourceDevice).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_ADVERTISING_SID)
.append(METADATA_START).append(mSourceAdvertisingSid).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_ID)
.append(METADATA_START).append(mBroadcastId).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_SYNC_INTERVAL)
.append(METADATA_START).append(mPaSyncInterval).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_IS_ENCRYPTED)
.append(METADATA_START).append(mIsEncrypted).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_CODE)
.append(METADATA_START).append(Arrays.toString(mBroadcastCode)).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_PRESENTATION_DELAY)
.append(METADATA_START).append(mPresentationDelayMicros).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.append(BluetoothBroadcastUtils.PREFIX_BT_SUBGROUPS)
.append(METADATA_START).append(mSubgroupList).append(METADATA_END)
.append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
.toString();
}
/**
* Example : prefix is with the “BT:”, and end by the Android Version.
* BT:T:<1>;D:<00:11:22:AA:BB:CC>;AS:<1>;B:…;V:T;;
*
* @return BluetoothLeBroadcastMetadata
*/
public BluetoothLeBroadcastMetadata convertToBroadcastMetadata(String qrCodeString) {
if (DEBUG) {
Log.d(TAG, "Convert " + qrCodeString + "to BluetoothLeBroadcastMetadata");
}
Pattern pattern = Pattern.compile(PATTERN_REGEX);
Matcher match = pattern.matcher(qrCodeString);
if (match.find()) {
ArrayList<String> resultList = new ArrayList<>();
resultList.add(match.group(1));
mSourceAddressType = Integer.parseInt(resultList.get(0));
mSourceDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
resultList.get(1));
mSourceAdvertisingSid = Integer.parseInt(resultList.get(2));
mBroadcastId = Integer.parseInt(resultList.get(3));
mPaSyncInterval = Integer.parseInt(resultList.get(4));
mIsEncrypted = Boolean.valueOf(resultList.get(5));
mBroadcastCode = resultList.get(6).getBytes();
mPresentationDelayMicros = Integer.parseInt(resultList.get(7));
mSubgroup = convertToSubgroup(resultList.get(8));
if (DEBUG) {
Log.d(TAG, "Converted qrCodeString result: " + match.group());
}
return new BluetoothLeBroadcastMetadata.Builder()
.setSourceDevice(mSourceDevice, mSourceAddressType)
.setSourceAdvertisingSid(mSourceAdvertisingSid)
.setBroadcastId(mBroadcastId)
.setPaSyncInterval(mPaSyncInterval)
.setEncrypted(mIsEncrypted)
.setBroadcastCode(mBroadcastCode)
.setPresentationDelayMicros(mPresentationDelayMicros)
.addSubgroup(mSubgroup)
.build();
} else {
if (DEBUG) {
Log.d(TAG,
"The match fail, can not convert it to BluetoothLeBroadcastMetadata.");
}
return null;
}
}
private BluetoothLeBroadcastSubgroup convertToSubgroup(String subgroupString) {
if (DEBUG) {
Log.d(TAG, "Convert " + subgroupString + "to BluetoothLeBroadcastSubgroup");
}
Pattern pattern = Pattern.compile(PATTERN_REGEX);
Matcher match = pattern.matcher(subgroupString);
if (match.find()) {
ArrayList<String> resultList = new ArrayList<>();
resultList.add(match.group(1));
mCodecId = Long.getLong(resultList.get(0));
mConfigMetadata = convertToConfigMetadata(resultList.get(1));
mContentMetadata = convertToContentMetadata(resultList.get(2));
mChannel = convertToChannel(resultList.get(3), mConfigMetadata);
if (DEBUG) {
Log.d(TAG, "Converted subgroupString result: " + match.group());
}
return new BluetoothLeBroadcastSubgroup.Builder()
.setCodecId(mCodecId)
.setCodecSpecificConfig(mConfigMetadata)
.setContentMetadata(mContentMetadata)
.addChannel(mChannel)
.build();
} else {
if (DEBUG) {
Log.d(TAG,
"The match fail, can not convert it to BluetoothLeBroadcastSubgroup.");
}
return null;
}
}
private BluetoothLeAudioCodecConfigMetadata convertToConfigMetadata(
String configMetadataString) {
if (DEBUG) {
Log.d(TAG,
"Convert " + configMetadataString + "to BluetoothLeAudioCodecConfigMetadata");
}
Pattern pattern = Pattern.compile(PATTERN_REGEX);
Matcher match = pattern.matcher(configMetadataString);
if (match.find()) {
ArrayList<String> resultList = new ArrayList<>();
resultList.add(match.group(1));
mAudioLocation = Long.getLong(resultList.get(0));
if (DEBUG) {
Log.d(TAG, "Converted configMetadataString result: " + match.group());
}
return new BluetoothLeAudioCodecConfigMetadata.Builder()
.setAudioLocation(mAudioLocation)
.build();
} else {
if (DEBUG) {
Log.d(TAG,
"The match fail, can not convert it to "
+ "BluetoothLeAudioCodecConfigMetadata.");
}
return null;
}
}
private BluetoothLeAudioContentMetadata convertToContentMetadata(String contentMetadataString) {
if (DEBUG) {
Log.d(TAG, "Convert " + contentMetadataString + "to BluetoothLeAudioContentMetadata");
}
Pattern pattern = Pattern.compile(PATTERN_REGEX);
Matcher match = pattern.matcher(contentMetadataString);
if (match.find()) {
ArrayList<String> resultList = new ArrayList<>();
resultList.add(match.group(1));
mProgramInfo = resultList.get(0);
mLanguage = resultList.get(1);
if (DEBUG) {
Log.d(TAG, "Converted contentMetadataString result: " + match.group());
}
return new BluetoothLeAudioContentMetadata.Builder()
.setProgramInfo(mProgramInfo)
.setLanguage(mLanguage)
.build();
} else {
if (DEBUG) {
Log.d(TAG,
"The match fail, can not convert it to BluetoothLeAudioContentMetadata.");
}
return null;
}
}
private BluetoothLeBroadcastChannel convertToChannel(String channelString,
BluetoothLeAudioCodecConfigMetadata configMetadata) {
if (DEBUG) {
Log.d(TAG, "Convert " + channelString + "to BluetoothLeBroadcastChannel");
}
Pattern pattern = Pattern.compile(PATTERN_REGEX);
Matcher match = pattern.matcher(channelString);
if (match.find()) {
ArrayList<String> resultList = new ArrayList<>();
resultList.add(match.group(1));
mIsSelected = Boolean.valueOf(resultList.get(0));
mChannelIndex = Integer.parseInt(resultList.get(1));
if (DEBUG) {
Log.d(TAG, "Converted channelString result: " + match.group());
}
return new BluetoothLeBroadcastChannel.Builder()
.setSelected(mIsSelected)
.setChannelIndex(mChannelIndex)
.setCodecMetadata(configMetadata)
.build();
} else {
if (DEBUG) {
Log.d(TAG,
"The match fail, can not convert it to BluetoothLeBroadcastChannel.");
}
return null;
}
}
}