1 /* 2 * Copyright (C) 2017 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 static android.bluetooth.le.BluetoothLeUtils.getSyncTimeout; 20 21 import android.annotation.RequiresNoPermission; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.bluetooth.BluetoothAdapter; 25 import android.bluetooth.IBluetoothGatt; 26 import android.bluetooth.IBluetoothManager; 27 import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; 28 import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; 29 import android.content.AttributionSource; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.modules.utils.SynchronousResultReceiver; 34 35 import java.util.concurrent.TimeoutException; 36 37 /** 38 * This class provides a way to control single Bluetooth LE advertising instance. 39 * <p> 40 * To get an instance of {@link AdvertisingSet}, call the 41 * {@link BluetoothLeAdvertiser#startAdvertisingSet} method. 42 * 43 * @see AdvertiseData 44 */ 45 public final class AdvertisingSet { 46 private static final String TAG = "AdvertisingSet"; 47 48 private final IBluetoothGatt mGatt; 49 private int mAdvertiserId; 50 private AttributionSource mAttributionSource; 51 AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager, AttributionSource attributionSource)52 /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager, 53 AttributionSource attributionSource) { 54 mAdvertiserId = advertiserId; 55 mAttributionSource = attributionSource; 56 try { 57 mGatt = bluetoothManager.getBluetoothGatt(); 58 } catch (RemoteException e) { 59 Log.e(TAG, "Failed to get Bluetooth gatt - ", e); 60 throw new IllegalStateException("Failed to get Bluetooth"); 61 } 62 } 63 setAdvertiserId(int advertiserId)64 /* package */ void setAdvertiserId(int advertiserId) { 65 mAdvertiserId = advertiserId; 66 } 67 68 /** 69 * Enables Advertising. This method returns immediately, the operation status is 70 * delivered through {@code callback.onAdvertisingEnabled()}. 71 * 72 * @param enable whether the advertising should be enabled (true), or disabled (false) 73 * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 74 * (655,350 ms) 75 * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the 76 * controller shall attempt to send prior to terminating the extended advertising, even if the 77 * duration has not expired. Valid range is from 1 to 255. 78 */ 79 @RequiresLegacyBluetoothAdminPermission 80 @RequiresBluetoothAdvertisePermission 81 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) enableAdvertising(boolean enable, int duration, int maxExtendedAdvertisingEvents)82 public void enableAdvertising(boolean enable, int duration, 83 int maxExtendedAdvertisingEvents) { 84 try { 85 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 86 mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration, 87 maxExtendedAdvertisingEvents, mAttributionSource, recv); 88 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 89 } catch (TimeoutException | RemoteException e) { 90 Log.e(TAG, "remote exception - ", e); 91 } 92 } 93 94 /** 95 * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for 96 * specified AdvertisingSetParameters. This method returns immediately, the operation status is 97 * delivered through {@code callback.onAdvertisingDataSet()}. 98 * <p> 99 * Advertising data must be empty if non-legacy scannable advertising is used. 100 * 101 * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link 102 * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, 103 * three bytes will be added for flags. If the update takes place when the advertising set is 104 * enabled, the data can be maximum 251 bytes long. 105 */ 106 @RequiresLegacyBluetoothAdminPermission 107 @RequiresBluetoothAdvertisePermission 108 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setAdvertisingData(AdvertiseData advertiseData)109 public void setAdvertisingData(AdvertiseData advertiseData) { 110 try { 111 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 112 mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource, recv); 113 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 114 } catch (TimeoutException | RemoteException e) { 115 Log.e(TAG, "remote exception - ", e); 116 } 117 } 118 119 /** 120 * Set/update scan response data. Make sure that data doesn't exceed the size limit for 121 * specified AdvertisingSetParameters. This method returns immediately, the operation status 122 * is delivered through {@code callback.onScanResponseDataSet()}. 123 * 124 * @param scanResponse Scan response associated with the advertisement data. Size must not 125 * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place 126 * when the advertising set is enabled, the data can be maximum 251 bytes long. 127 */ 128 @RequiresLegacyBluetoothAdminPermission 129 @RequiresBluetoothAdvertisePermission 130 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setScanResponseData(AdvertiseData scanResponse)131 public void setScanResponseData(AdvertiseData scanResponse) { 132 try { 133 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 134 mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource, recv); 135 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 136 } catch (TimeoutException | RemoteException e) { 137 Log.e(TAG, "remote exception - ", e); 138 } 139 } 140 141 /** 142 * Update advertising parameters associated with this AdvertisingSet. Must be called when 143 * advertising is not active. This method returns immediately, the operation status is delivered 144 * through {@code callback.onAdvertisingParametersUpdated}. 145 * 146 * @param parameters advertising set parameters. 147 */ 148 @RequiresLegacyBluetoothAdminPermission 149 @RequiresBluetoothAdvertisePermission 150 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setAdvertisingParameters(AdvertisingSetParameters parameters)151 public void setAdvertisingParameters(AdvertisingSetParameters parameters) { 152 try { 153 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 154 mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource, recv); 155 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 156 } catch (TimeoutException | RemoteException e) { 157 Log.e(TAG, "remote exception - ", e); 158 } 159 } 160 161 /** 162 * Update periodic advertising parameters associated with this set. Must be called when 163 * periodic advertising is not enabled. This method returns immediately, the operation 164 * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}. 165 */ 166 @RequiresLegacyBluetoothAdminPermission 167 @RequiresBluetoothAdvertisePermission 168 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters)169 public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { 170 try { 171 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 172 mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource, 173 recv); 174 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 175 } catch (TimeoutException | RemoteException e) { 176 Log.e(TAG, "remote exception - ", e); 177 } 178 } 179 180 /** 181 * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters, 182 * or after advertising was started with periodic advertising data set. This method returns 183 * immediately, the operation status is delivered through 184 * {@code callback.onPeriodicAdvertisingDataSet()}. 185 * 186 * @param periodicData Periodic advertising data. Size must not exceed {@link 187 * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the 188 * periodic advertising is enabled for this set, the data can be maximum 251 bytes long. 189 */ 190 @RequiresLegacyBluetoothAdminPermission 191 @RequiresBluetoothAdvertisePermission 192 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setPeriodicAdvertisingData(AdvertiseData periodicData)193 public void setPeriodicAdvertisingData(AdvertiseData periodicData) { 194 try { 195 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 196 mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource, recv); 197 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 198 } catch (TimeoutException | RemoteException e) { 199 Log.e(TAG, "remote exception - ", e); 200 } 201 } 202 203 /** 204 * Used to enable/disable periodic advertising. This method returns immediately, the operation 205 * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. 206 * 207 * @param enable whether the periodic advertising should be enabled (true), or disabled 208 * (false). 209 */ 210 @RequiresLegacyBluetoothAdminPermission 211 @RequiresBluetoothAdvertisePermission 212 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) setPeriodicAdvertisingEnabled(boolean enable)213 public void setPeriodicAdvertisingEnabled(boolean enable) { 214 try { 215 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 216 mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource, recv); 217 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 218 } catch (TimeoutException | RemoteException e) { 219 Log.e(TAG, "remote exception - ", e); 220 } 221 } 222 223 /** 224 * Returns address associated with this advertising set. 225 * This method is exposed only for Bluetooth PTS tests, no app or system service 226 * should ever use it. 227 * 228 * @hide 229 */ 230 @RequiresBluetoothAdvertisePermission 231 @RequiresPermission(allOf = { 232 android.Manifest.permission.BLUETOOTH_ADVERTISE, 233 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 234 }) getOwnAddress()235 public void getOwnAddress() { 236 try { 237 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 238 mGatt.getOwnAddress(mAdvertiserId, mAttributionSource, recv); 239 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 240 } catch (TimeoutException | RemoteException e) { 241 Log.e(TAG, "remote exception - ", e); 242 } 243 } 244 245 /** 246 * Returns the advertiser ID associated with this advertising set. 247 * 248 * <p>This corresponds to the advertising set ID used at the HCI layer, in either LE Extended 249 * Advertising or Android-specific Multi-Advertising. 250 * 251 * @hide 252 */ 253 @RequiresNoPermission 254 @SystemApi getAdvertiserId()255 public int getAdvertiserId() { 256 return mAdvertiserId; 257 } 258 } 259