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