1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.bass_client; 18 19 import static com.android.bluetooth.flags.Flags.leaudioBassScanWithInternalScanController; 20 21 import android.bluetooth.BluetoothUtils; 22 import android.bluetooth.BluetoothUtils.TypeValueEntry; 23 import android.bluetooth.le.ScanFilter; 24 import android.bluetooth.le.ScanRecord; 25 import android.bluetooth.le.ScanResult; 26 import android.os.ParcelUuid; 27 import android.util.Log; 28 29 import java.nio.charset.StandardCharsets; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Map; 33 34 /** Bass Utility functions */ 35 class BassUtils { 36 private static final String TAG = BassUtils.class.getSimpleName(); 37 containUuid(List<ScanFilter> filters, ParcelUuid uuid)38 static boolean containUuid(List<ScanFilter> filters, ParcelUuid uuid) { 39 for (ScanFilter filter : filters) { 40 if (leaudioBassScanWithInternalScanController()) { 41 if (filter.getServiceDataUuid().equals(uuid)) { 42 return true; 43 } 44 } else { 45 if (filter.getServiceUuid().equals(uuid)) { 46 return true; 47 } 48 } 49 } 50 return false; 51 } 52 parseBroadcastId(byte[] broadcastIdBytes)53 static int parseBroadcastId(byte[] broadcastIdBytes) { 54 int broadcastId; 55 broadcastId = (0x00FF0000 & (broadcastIdBytes[2] << 16)); 56 broadcastId |= (0x0000FF00 & (broadcastIdBytes[1] << 8)); 57 broadcastId |= (0x000000FF & broadcastIdBytes[0]); 58 return broadcastId; 59 } 60 getBroadcastId(ScanResult scanResult)61 static Integer getBroadcastId(ScanResult scanResult) { 62 if (scanResult == null) { 63 Log.e(TAG, "Null scan result"); 64 return BassConstants.INVALID_BROADCAST_ID; 65 } 66 67 return getBroadcastId(scanResult.getScanRecord()); 68 } 69 getBroadcastId(ScanRecord scanRecord)70 static Integer getBroadcastId(ScanRecord scanRecord) { 71 if (scanRecord == null) { 72 Log.e(TAG, "Null scan record"); 73 return BassConstants.INVALID_BROADCAST_ID; 74 } 75 76 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 77 if (listOfUuids == null) { 78 Log.e(TAG, "Null service data"); 79 return BassConstants.INVALID_BROADCAST_ID; 80 } 81 82 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) { 83 byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID); 84 return BassUtils.parseBroadcastId(bId); 85 } else { 86 Log.e(TAG, "No broadcast Id in service data"); 87 } 88 89 return BassConstants.INVALID_BROADCAST_ID; 90 } 91 getPublicBroadcastData(ScanRecord scanRecord)92 static PublicBroadcastData getPublicBroadcastData(ScanRecord scanRecord) { 93 if (scanRecord == null) { 94 Log.e(TAG, "Null scan record"); 95 return null; 96 } 97 98 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 99 if (listOfUuids == null) { 100 Log.e(TAG, "Null service data"); 101 return null; 102 } 103 104 if (listOfUuids.containsKey(BassConstants.PUBLIC_BROADCAST_UUID)) { 105 byte[] pbAnnouncement = listOfUuids.get(BassConstants.PUBLIC_BROADCAST_UUID); 106 return PublicBroadcastData.parsePublicBroadcastData(pbAnnouncement); 107 } else { 108 log("No public broadcast data in service data"); 109 } 110 111 return null; 112 } 113 getBroadcastName(ScanRecord scanRecord)114 static String getBroadcastName(ScanRecord scanRecord) { 115 if (scanRecord == null) { 116 Log.e(TAG, "Null scan record"); 117 return null; 118 } 119 byte[] rawBytes = scanRecord.getBytes(); 120 List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes); 121 if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) { 122 Log.e(TAG, "Invalid LTV entries in Scan record"); 123 return null; 124 } 125 126 String broadcastName = null; 127 for (TypeValueEntry entry : entries) { 128 // Only use the first value of each type 129 if (broadcastName == null && entry.getType() == BassConstants.BCAST_NAME_AD_TYPE) { 130 byte[] bytes = entry.getValue(); 131 int len = bytes.length; 132 if (len < BassConstants.BCAST_NAME_LEN_MIN 133 || len > BassConstants.BCAST_NAME_LEN_MAX) { 134 Log.e(TAG, "Invalid broadcast name length in Scan record" + len); 135 return null; 136 } 137 broadcastName = new String(bytes, StandardCharsets.UTF_8); 138 } 139 } 140 return broadcastName; 141 } 142 log(String msg)143 static void log(String msg) { 144 Log.d(TAG, msg); 145 } 146 printByteArray(byte[] array)147 static void printByteArray(byte[] array) { 148 log("Entire byte Array as string: " + Arrays.toString(array)); 149 } 150 } 151