1 /* 2 * Copyright (C) 2014 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 android.bluetooth.le; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.util.SparseArray; 21 22 import java.time.Duration; 23 import java.util.Arrays; 24 import java.util.Iterator; 25 import java.util.Map; 26 import java.util.Objects; 27 import java.util.Set; 28 import java.util.UUID; 29 30 /** 31 * Helper class for Bluetooth LE utils. 32 * 33 * @hide 34 */ 35 public class BluetoothLeUtils { 36 37 /** 38 * Timeout value for synchronous binder call 39 */ 40 private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5); 41 42 /** 43 * @return timeout value for synchronous binder call 44 */ getSyncTimeout()45 static Duration getSyncTimeout() { 46 return SYNC_CALLS_TIMEOUT; 47 } 48 49 /** 50 * Returns a string composed from a byte array. 51 */ toString(byte[] data)52 static <T> String toString(byte[] data) { 53 if (data == null) { 54 return "null"; 55 } 56 if (data.length == 0) { 57 return "{}"; 58 } 59 StringBuilder buffer = new StringBuilder(); 60 buffer.append('{'); 61 for (int i = 0; i < data.length; i++) { 62 buffer.append(data[i]); 63 if ((i + 1) < data.length) { 64 buffer.append(", "); 65 } 66 } 67 buffer.append('}'); 68 return buffer.toString(); 69 } 70 71 /** 72 * Returns a string composed from a {@link SparseArray}. 73 */ toString(SparseArray<byte[]> array)74 static String toString(SparseArray<byte[]> array) { 75 if (array == null) { 76 return "null"; 77 } 78 if (array.size() == 0) { 79 return "{}"; 80 } 81 StringBuilder buffer = new StringBuilder(); 82 buffer.append('{'); 83 for (int i = 0; i < array.size(); ++i) { 84 buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i))); 85 } 86 buffer.append('}'); 87 return buffer.toString(); 88 } 89 90 /** 91 * Returns a string composed from a {@link Map}. 92 */ toString(Map<T, byte[]> map)93 static <T> String toString(Map<T, byte[]> map) { 94 if (map == null) { 95 return "null"; 96 } 97 if (map.isEmpty()) { 98 return "{}"; 99 } 100 StringBuilder buffer = new StringBuilder(); 101 buffer.append('{'); 102 Iterator<Map.Entry<T, byte[]>> it = map.entrySet().iterator(); 103 while (it.hasNext()) { 104 Map.Entry<T, byte[]> entry = it.next(); 105 Object key = entry.getKey(); 106 buffer.append(key).append("=").append(Arrays.toString(map.get(key))); 107 if (it.hasNext()) { 108 buffer.append(", "); 109 } 110 } 111 buffer.append('}'); 112 return buffer.toString(); 113 } 114 115 /** 116 * Check whether two {@link SparseArray} equal. 117 */ equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray)118 static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) { 119 if (array == otherArray) { 120 return true; 121 } 122 if (array == null || otherArray == null) { 123 return false; 124 } 125 if (array.size() != otherArray.size()) { 126 return false; 127 } 128 129 // Keys are guaranteed in ascending order when indices are in ascending order. 130 for (int i = 0; i < array.size(); ++i) { 131 if (array.keyAt(i) != otherArray.keyAt(i) 132 || !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { 133 return false; 134 } 135 } 136 return true; 137 } 138 139 /** 140 * Check whether two {@link Map} equal. 141 */ equals(Map<T, byte[]> map, Map<T, byte[]> otherMap)142 static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) { 143 if (map == otherMap) { 144 return true; 145 } 146 if (map == null || otherMap == null) { 147 return false; 148 } 149 if (map.size() != otherMap.size()) { 150 return false; 151 } 152 Set<T> keys = map.keySet(); 153 if (!keys.equals(otherMap.keySet())) { 154 return false; 155 } 156 for (T key : keys) { 157 if (!Objects.deepEquals(map.get(key), otherMap.get(key))) { 158 return false; 159 } 160 } 161 return true; 162 } 163 164 /** 165 * Ensure Bluetooth is turned on. 166 * 167 * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link 168 * BluetoothAdapter#STATE_ON}. 169 */ checkAdapterStateOn(BluetoothAdapter adapter)170 static void checkAdapterStateOn(BluetoothAdapter adapter) { 171 if (adapter == null || !adapter.isLeEnabled()) { 172 throw new IllegalStateException("BT Adapter is not turned ON"); 173 } 174 } 175 176 /** 177 * Compares two UUIDs with a UUID mask. 178 * 179 * @param data first {@link #UUID} to compare. 180 * @param uuid second {@link #UUID} to compare. 181 * @param mask mask {@link #UUID}. 182 * @return true if both UUIDs are equals when masked, false otherwise. 183 */ maskedEquals(UUID data, UUID uuid, UUID mask)184 static boolean maskedEquals(UUID data, UUID uuid, UUID mask) { 185 if (mask == null) { 186 return Objects.equals(data, uuid); 187 } 188 return (data.getLeastSignificantBits() & mask.getLeastSignificantBits()) 189 == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) 190 && (data.getMostSignificantBits() & mask.getMostSignificantBits()) 191 == (uuid.getMostSignificantBits() & mask.getMostSignificantBits()); 192 } 193 } 194