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 com.android.pmc; 18 19 20 import android.app.Service; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothGatt; 24 import android.bluetooth.BluetoothGattCharacteristic; 25 import android.bluetooth.BluetoothGattDescriptor; 26 import android.bluetooth.BluetoothGattServer; 27 import android.bluetooth.BluetoothGattServerCallback; 28 import android.bluetooth.BluetoothGattService; 29 import android.bluetooth.BluetoothManager; 30 import android.bluetooth.BluetoothProfile; 31 import android.bluetooth.le.AdvertiseCallback; 32 import android.bluetooth.le.AdvertiseData; 33 import android.bluetooth.le.AdvertiseSettings; 34 import android.bluetooth.le.BluetoothLeAdvertiser; 35 import android.content.Context; 36 import android.util.Log; 37 38 import java.util.UUID; 39 40 /** 41 * Class to implement Gatt Server functionalities 42 */ 43 public class GattServer { 44 public static final String TAG = "GATTS"; 45 46 private MyBleAdvertiser mBleAdvertiser; 47 private Context mContext; 48 private BluetoothManager mBluetoothManager; 49 private BluetoothGattServer mGattServer; 50 private MyGattServerCallback mGattServerCallBack; 51 private BluetoothGattService mGattService; 52 private static final String READABLE_DESC_UUID = "76d5ed92-ca81-4edb-bb6b-9f019665fb32"; 53 public static final String WRITABLE_CHAR_UUID = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"; 54 public static final String TEST_SERVICE_UUID = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B"; 55 56 /** 57 * Constructor 58 * 59 * @param context - System will provide a context 60 */ GattServer(Context context)61 public GattServer(Context context) { 62 Log.d(TAG, "Start GattServer()"); 63 mContext = context; 64 // Check if Bluetooth is enabled 65 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 66 67 if (bluetoothAdapter == null) { 68 Log.e(TAG, "BluetoothAdapter is Null"); 69 return; 70 } else { 71 if (!bluetoothAdapter.isEnabled()) { 72 Log.d(TAG, "BluetoothAdapter is NOT enabled, enable now"); 73 bluetoothAdapter.enable(); 74 if (!bluetoothAdapter.isEnabled()) { 75 Log.e(TAG, "Can't enable Bluetooth"); 76 return; 77 } 78 } 79 } 80 81 // Prepare data for GATT service 82 mBluetoothManager = (BluetoothManager) context.getSystemService( 83 Service.BLUETOOTH_SERVICE); 84 85 mGattServerCallBack = new MyGattServerCallback(); 86 87 BluetoothGattCharacteristic characteristic = 88 new BluetoothGattCharacteristic(UUID.fromString(WRITABLE_CHAR_UUID), 89 BluetoothGattCharacteristic.PROPERTY_WRITE 90 | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 91 BluetoothGattCharacteristic.PERMISSION_WRITE); 92 93 BluetoothGattDescriptor descriptor = 94 new BluetoothGattDescriptor(UUID.fromString(READABLE_DESC_UUID), 95 BluetoothGattDescriptor.PERMISSION_READ 96 | BluetoothGattDescriptor.PERMISSION_WRITE); 97 98 characteristic.addDescriptor(descriptor); 99 100 mGattService = new BluetoothGattService(UUID.fromString(TEST_SERVICE_UUID), 101 BluetoothGattService.SERVICE_TYPE_PRIMARY); 102 mGattService.addCharacteristic(characteristic); 103 104 // Create BLE Advertiser object 105 mBleAdvertiser = new MyBleAdvertiser(bluetoothAdapter); 106 Log.d(TAG, "End GattServer()"); 107 } 108 109 /** 110 * Function to be called to start Gatt Server 111 */ startGattServer()112 public void startGattServer() { 113 // Connect to Gatt Server 114 mGattServer = mBluetoothManager.openGattServer(mContext, mGattServerCallBack); 115 // Add GATT Service to Gatt Server 116 mGattServer.addService(mGattService); 117 // Start BLE Advertising here 118 mBleAdvertiser.startAdvertising(); 119 Log.d(TAG, "startGattServer finished"); 120 } 121 122 /** 123 * Class to provide callback for GATT server to handle GATT requests 124 */ 125 class MyGattServerCallback extends BluetoothGattServerCallback { 126 MyGattServerCallback()127 MyGattServerCallback() {} 128 129 @Override onServiceAdded(int status, BluetoothGattService service)130 public void onServiceAdded(int status, BluetoothGattService service) { 131 Log.d(TAG, "onServiceAdded: " + status); 132 } 133 134 @Override onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic)135 public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, 136 BluetoothGattCharacteristic characteristic) { 137 Log.d(TAG, "onCharacteristicReadRequest: " + characteristic); 138 } 139 140 @Override onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)141 public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, 142 BluetoothGattCharacteristic characteristic, boolean preparedWrite, 143 boolean responseNeeded, int offset, byte[] value) { 144 Log.d(TAG, "onCharacteristicWriteRequest requestId: " + requestId 145 + " preparedWrite: " + preparedWrite + " sendRespons back"); 146 147 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); 148 } 149 150 @Override onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor)151 public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, 152 BluetoothGattDescriptor descriptor) { 153 Log.d(TAG, "onDescriptorReadRequest requestId: " + requestId); 154 } 155 156 @Override onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)157 public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, 158 BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, 159 int offset, byte[] value) { 160 Log.d(TAG, "onDescriptorWriteRequest requestId: " + requestId + " preparedWrite: " 161 + preparedWrite); 162 } 163 164 @Override onExecuteWrite(BluetoothDevice device, int requestId, boolean execute)165 public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { 166 Log.d(TAG, "onExecuteWrite requestId: " + requestId + " execute: " + execute); 167 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null); 168 Log.d(TAG, "onExecuteWrite sendResponse back to GATT Client"); 169 } 170 171 @Override onNotificationSent(BluetoothDevice device, int status)172 public void onNotificationSent(BluetoothDevice device, int status) { 173 Log.d(TAG, "onNotificationSent " + status); 174 } 175 176 @Override onConnectionStateChange(BluetoothDevice device, int status, int newState)177 public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { 178 Log.d(TAG, "onConnectionStateChange status: " + status + " new state: " + newState); 179 if (newState == BluetoothProfile.STATE_CONNECTED) { 180 Log.d(TAG, "Connected to mac address " + device.getAddress() + " status " + status); 181 182 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 183 Log.d(TAG, "Disconnected from mac address " + device.getAddress() + " status " 184 + status); 185 } 186 } 187 188 @Override onMtuChanged(BluetoothDevice device, int mtu)189 public void onMtuChanged(BluetoothDevice device, int mtu) { 190 Log.d(TAG, "onMtuChanged: " + mtu); 191 } 192 } 193 194 /** 195 * Class to provide BLE Advertising functionalities 196 */ 197 class MyBleAdvertiser { 198 199 private BluetoothLeAdvertiser mAdvertiser; 200 private AdvertiseSettings mAdvertiseSettings; 201 private AdvertiseData mAdvertiseData; 202 private MyAdvertiseCallback mAdvertiseCallback; 203 204 /** 205 * Constructor 206 * @param bluetoothAdapter - Default BluetoothAdapter 207 */ MyBleAdvertiser(BluetoothAdapter bluetoothAdapter)208 MyBleAdvertiser(BluetoothAdapter bluetoothAdapter) { 209 // Prepare for BLE Advertisement 210 mAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); 211 mAdvertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true).build(); 212 mAdvertiseCallback = new MyAdvertiseCallback(); 213 mAdvertiseSettings = new AdvertiseSettings.Builder() 214 .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) 215 .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) 216 .setConnectable(true) 217 .setTimeout(0).build(); 218 } 219 220 /** 221 * Wrapper function to start BLE Advertising 222 */ startAdvertising()223 public void startAdvertising() { 224 mAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData, 225 mAdvertiseCallback); 226 } 227 228 /** 229 * Wrapper function to stop BLE Advertising 230 */ stopAdvertising()231 public void stopAdvertising() { 232 mAdvertiser.stopAdvertising(mAdvertiseCallback); 233 } 234 235 /** 236 * Class to provide callback to handle BLE Advertisement 237 */ 238 class MyAdvertiseCallback extends AdvertiseCallback { 239 private boolean mMaxReached; 240 // The lock object is used to synchronize mMaxReached 241 private final Object mLock = new Object(); 242 MyAdvertiseCallback()243 MyAdvertiseCallback() { 244 mMaxReached = false; 245 } 246 setMaxReached(boolean setMax)247 public void setMaxReached(boolean setMax) { 248 synchronized (mLock) { 249 mMaxReached = setMax; 250 } 251 } getMaxReached()252 public boolean getMaxReached() { 253 synchronized (mLock) { 254 return mMaxReached; 255 } 256 } 257 258 @Override onStartSuccess(AdvertiseSettings settingsInEffect)259 public void onStartSuccess(AdvertiseSettings settingsInEffect) { 260 Log.d(TAG, "bluetooth_le_advertisement onSuccess "); 261 if (getMaxReached()) { 262 Log.d(TAG, "Stop Advertising"); 263 mBleAdvertiser.stopAdvertising(); 264 } else { 265 Log.d(TAG, "Start Advertising"); 266 mBleAdvertiser.startAdvertising(); 267 } 268 } 269 270 @Override onStartFailure(int errorCode)271 public void onStartFailure(int errorCode) { 272 String errorString = "UNKNOWN_ERROR_CODE"; 273 if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) { 274 errorString = "ADVERTISE_FAILED_ALREADY_STARTED"; 275 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE) { 276 errorString = "ADVERTISE_FAILED_DATA_TOO_LARGE"; 277 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED) { 278 errorString = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED"; 279 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR) { 280 errorString = "ADVERTISE_FAILED_INTERNAL_ERROR"; 281 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { 282 errorString = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"; 283 setMaxReached(true); 284 } 285 Log.d(TAG, "bluetooth_le_advertisement onFailure: " + errorString); 286 } 287 } 288 } 289 } 290