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 package com.android.car.trust.comms; 17 18 import android.app.Service; 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothGatt; 22 import android.bluetooth.BluetoothGattCharacteristic; 23 import android.bluetooth.BluetoothGattServer; 24 import android.bluetooth.BluetoothGattServerCallback; 25 import android.bluetooth.BluetoothGattService; 26 import android.bluetooth.BluetoothManager; 27 import android.bluetooth.BluetoothProfile; 28 import android.bluetooth.le.AdvertiseCallback; 29 import android.bluetooth.le.AdvertiseData; 30 import android.bluetooth.le.AdvertiseSettings; 31 import android.bluetooth.le.BluetoothLeAdvertiser; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.PackageManager; 35 import android.os.IBinder; 36 import android.os.ParcelUuid; 37 import android.util.Log; 38 39 import java.util.HashSet; 40 41 /** 42 * A generic service to start a BLE 43 */ 44 public abstract class SimpleBleServer extends Service { 45 46 /** 47 * Listener that is notified when the status of the BLE server changes. 48 */ 49 public interface ConnectionListener { 50 /** 51 * Called when the GATT server is started and BLE is successfully advertising. 52 */ 53 void onServerStarted(); 54 55 /** 56 * Called when the BLE advertisement fails to start. 57 * 58 * @param errorCode Error code (see {@link AdvertiseCallback}#ADVERTISE_FAILED_* constants) 59 */ 60 void onServerStartFailed(int errorCode); 61 62 /** 63 * Called when a device is connected. 64 * @param device 65 */ 66 void onDeviceConnected(BluetoothDevice device); 67 } 68 69 private static final String TAG = "SimpleBleServer"; 70 71 private BluetoothLeAdvertiser mAdvertiser; 72 protected BluetoothGattServer mGattServer; 73 74 private HashSet<ConnectionListener> mListeners = new HashSet<>(); 75 76 @Override 77 public IBinder onBind(Intent intent) { 78 // Override in child classes. 79 return null; 80 } 81 82 /** 83 * Starts the GATT server with the given {@link BluetoothGattService} and begins 84 * advertising with the {@link ParcelUuid}. 85 * @param advertiseUuid Service Uuid used in the {@link AdvertiseData} 86 * @param service {@link BluetoothGattService} that will be discovered by clients 87 */ 88 protected void start(ParcelUuid advertiseUuid, BluetoothGattService service) { 89 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 90 Log.e(TAG, "System does not support BLE"); 91 return; 92 } 93 94 BluetoothManager btManager = 95 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 96 97 mGattServer = btManager.openGattServer(this, mGattServerCallback); 98 if (mGattServer == null) { 99 Log.e(TAG, "Gatt Server not created"); 100 return; 101 } 102 103 // We only allow adding one service in this implementation. If multiple services need 104 // to be added, then they need to be queued up and added only after 105 // BluetoothGattServerCallback.onServiceAdded is called. 106 mGattServer.addService(service); 107 108 AdvertiseSettings settings = new AdvertiseSettings.Builder() 109 .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) 110 .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) 111 .setConnectable(true) 112 .build(); 113 114 AdvertiseData data = new AdvertiseData.Builder() 115 .setIncludeDeviceName(true) 116 .addServiceUuid(advertiseUuid) 117 .build(); 118 119 mAdvertiser 120 = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser(); 121 122 mAdvertiser.startAdvertising(settings, data, mAdvertisingCallback); 123 } 124 125 /** 126 * Stops the advertiser and GATT server. This needs to be done to avoid leaks 127 */ 128 protected void stop() { 129 if (mAdvertiser != null) { 130 mAdvertiser.stopAdvertising(mAdvertisingCallback); 131 mAdvertiser.cleanup(); 132 } 133 134 if (mGattServer != null) { 135 mGattServer.clearServices(); 136 try { 137 for (BluetoothDevice d : mGattServer.getConnectedDevices()) { 138 mGattServer.cancelConnection(d); 139 } 140 } catch (UnsupportedOperationException e) { 141 Log.e(TAG, "Error getting connected devices", e); 142 } finally { 143 mGattServer.close(); 144 } 145 } 146 147 mListeners.clear(); 148 } 149 150 @Override 151 public void onDestroy() { 152 stop(); 153 super.onDestroy(); 154 } 155 156 public void addConnectionListener(ConnectionListener listener) { 157 if (Log.isLoggable(TAG, Log.DEBUG)) { 158 Log.d(TAG, "Adding connection listener"); 159 } 160 mListeners.add(listener); 161 } 162 163 /** 164 * Triggered when this BleService receives a write request from a remote 165 * device. Sub-classes should implement how to handle requests. 166 */ 167 public abstract void onCharacteristicWrite(final BluetoothDevice device, int requestId, 168 BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean 169 responseNeeded, int offset, byte[] value); 170 171 /** 172 * Triggered when this BleService receives a read request from a remote device. 173 */ 174 public abstract void onCharacteristicRead(BluetoothDevice device, 175 int requestId, int offset, final BluetoothGattCharacteristic characteristic); 176 177 private AdvertiseCallback mAdvertisingCallback = new AdvertiseCallback() { 178 @Override 179 public void onStartSuccess(AdvertiseSettings settingsInEffect) { 180 super.onStartSuccess(settingsInEffect); 181 if (Log.isLoggable(TAG, Log.DEBUG)) { 182 Log.d(TAG, "Successfully started advertising service"); 183 } 184 for (ConnectionListener listener : mListeners) { 185 listener.onServerStarted(); 186 } 187 } 188 189 @Override 190 public void onStartFailure(int errorCode) { 191 super.onStartFailure(errorCode); 192 if (Log.isLoggable(TAG, Log.DEBUG)) { 193 Log.d(TAG, "Failed to advertise, errorCode: " + errorCode); 194 } 195 for (ConnectionListener listener : mListeners) { 196 listener.onServerStartFailed(errorCode); 197 } 198 } 199 }; 200 201 private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() { 202 @Override 203 public void onConnectionStateChange(BluetoothDevice device, 204 final int status, final int newState) { 205 if (Log.isLoggable(TAG, Log.DEBUG)) { 206 Log.d(TAG, "GattServer connection change status: " 207 + newState + " newState: " 208 + newState + " device name: " + device.getName()); 209 } 210 if (newState == BluetoothProfile.STATE_CONNECTED) { 211 for (ConnectionListener listener : mListeners) { 212 listener.onDeviceConnected(device); 213 } 214 } 215 } 216 217 @Override 218 public void onServiceAdded(final int status, BluetoothGattService service) { 219 if (Log.isLoggable(TAG, Log.DEBUG)) { 220 Log.d(TAG, "Service added status: " + status + " uuid: " + service.getUuid()); 221 } 222 } 223 224 @Override 225 public void onCharacteristicReadRequest(BluetoothDevice device, 226 int requestId, int offset, final BluetoothGattCharacteristic characteristic) { 227 if (Log.isLoggable(TAG, Log.DEBUG)) { 228 Log.d(TAG, "Read request for characteristic: " + characteristic.getUuid()); 229 } 230 mGattServer.sendResponse(device, requestId, 231 BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); 232 SimpleBleServer. 233 this.onCharacteristicRead(device, requestId, offset, characteristic); 234 } 235 236 @Override 237 public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId, 238 BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean 239 responseNeeded, int offset, byte[] value) { 240 if (Log.isLoggable(TAG, Log.DEBUG)) { 241 Log.d(TAG, "Write request for characteristic: " + characteristic.getUuid()); 242 } 243 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 244 offset, value); 245 246 SimpleBleServer. 247 this.onCharacteristicWrite(device, requestId, characteristic, 248 preparedWrite, responseNeeded, offset, value); 249 } 250 }; 251 } 252