1 /*
2  * Copyright (C) 2013 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.cts.verifier.bluetooth;
18 
19 import java.util.Date;
20 import java.util.List;
21 import java.util.Timer;
22 import java.util.TimerTask;
23 import java.util.UUID;
24 
25 import android.app.Service;
26 import android.bluetooth.BluetoothAdapter;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothGatt;
29 import android.bluetooth.BluetoothGattCharacteristic;
30 import android.bluetooth.BluetoothGattDescriptor;
31 import android.bluetooth.BluetoothGattServer;
32 import android.bluetooth.BluetoothGattServerCallback;
33 import android.bluetooth.BluetoothGattService;
34 import android.bluetooth.BluetoothManager;
35 import android.bluetooth.BluetoothProfile;
36 import android.bluetooth.le.AdvertiseCallback;
37 import android.bluetooth.le.AdvertiseData;
38 import android.bluetooth.le.AdvertiseSettings;
39 import android.bluetooth.le.BluetoothLeAdvertiser;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.os.Handler;
43 import android.os.IBinder;
44 import android.os.ParcelUuid;
45 import android.util.Log;
46 import android.widget.Toast;
47 
48 public class BleServerService extends Service {
49 
50     public static final boolean DEBUG = true;
51     public static final String TAG = "BleServerService";
52 
53     public static final int COMMAND_ADD_SERVICE = 0;
54     public static final int COMMAND_WRITE_CHARACTERISTIC = 1;
55     public static final int COMMAND_WRITE_DESCRIPTOR = 2;
56 
57     public static final String BLE_SERVER_CONNECTED =
58             "com.android.cts.verifier.bluetooth.BLE_SERVER_CONNECTED";
59     public static final String BLE_SERVER_DISCONNECTED =
60             "com.android.cts.verifier.bluetooth.BLE_SERVER_DISCONNECTED";
61     public static final String BLE_SERVICE_ADDED =
62             "com.android.cts.verifier.bluetooth.BLE_SERVICE_ADDED";
63     public static final String BLE_CHARACTERISTIC_READ_REQUEST =
64             "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_REQUEST";
65     public static final String BLE_CHARACTERISTIC_WRITE_REQUEST =
66             "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE_REQUEST";
67     public static final String BLE_DESCRIPTOR_READ_REQUEST =
68             "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ_REQUEST";
69     public static final String BLE_DESCRIPTOR_WRITE_REQUEST =
70             "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE_REQUEST";
71     public static final String BLE_EXECUTE_WRITE =
72             "com.android.cts.verifier.bluetooth.BLE_EXECUTE_WRITE";
73     public static final String BLE_OPEN_FAIL =
74             "com.android.cts.verifier.bluetooth.BLE_OPEN_FAIL";
75 
76     private static final UUID SERVICE_UUID =
77             UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
78     private static final UUID CHARACTERISTIC_UUID =
79             UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
80     private static final UUID UPDATE_CHARACTERISTIC_UUID =
81             UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
82     private static final UUID DESCRIPTOR_UUID =
83             UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
84     public static final UUID ADV_SERVICE_UUID=
85             UUID.fromString("00003333-0000-1000-8000-00805f9b34fb");
86 
87     private BluetoothManager mBluetoothManager;
88     private BluetoothGattServer mGattServer;
89     private BluetoothGattService mService;
90     private BluetoothDevice mDevice;
91     private Timer mNotificationTimer;
92     private Handler mHandler;
93     private String mReliableWriteValue;
94     private BluetoothLeAdvertiser mAdvertiser;
95 
96     @Override
onCreate()97     public void onCreate() {
98         super.onCreate();
99 
100         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
101         mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
102         mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
103         mService = createService();
104         if (mGattServer != null) {
105             mGattServer.addService(mService);
106         }
107         mDevice = null;
108         mReliableWriteValue = null;
109 
110         mHandler = new Handler();
111         if (mGattServer == null) {
112             notifyOpenFail();
113         }
114     }
115 
116     @Override
onStartCommand(Intent intent, int flags, int startId)117     public int onStartCommand(Intent intent, int flags, int startId) {
118         startAdvertise();
119         return START_NOT_STICKY;
120     }
121 
122     @Override
onBind(Intent intent)123     public IBinder onBind(Intent intent) {
124         return null;
125     }
126 
127     @Override
onDestroy()128     public void onDestroy() {
129         super.onDestroy();
130         stopAdvertise();
131         if (mGattServer == null) {
132            return;
133         }
134         if (mDevice != null) mGattServer.cancelConnection(mDevice);
135         mGattServer.close();
136     }
137 
writeCharacteristic(String writeValue)138     private void writeCharacteristic(String writeValue) {
139         BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
140         if (characteristic != null) return;
141         characteristic.setValue(writeValue);
142     }
143 
writeDescriptor(String writeValue)144     private void writeDescriptor(String writeValue) {
145         BluetoothGattDescriptor descriptor = getDescriptor();
146         if (descriptor == null) return;
147         descriptor.setValue(writeValue.getBytes());
148     }
149 
notifyOpenFail()150     private void notifyOpenFail() {
151         if (DEBUG) Log.d(TAG, "notifyOpenFail");
152         Intent intent = new Intent(BLE_OPEN_FAIL);
153         sendBroadcast(intent);
154     }
155 
notifyConnected()156     private void notifyConnected() {
157         if (DEBUG) Log.d(TAG, "notifyConnected");
158         Intent intent = new Intent(BLE_SERVER_CONNECTED);
159         sendBroadcast(intent);
160     }
161 
notifyDisconnected()162     private void notifyDisconnected() {
163         if (DEBUG) Log.d(TAG, "notifyDisconnected");
164         Intent intent = new Intent(BLE_SERVER_DISCONNECTED);
165         sendBroadcast(intent);
166     }
167 
notifyServiceAdded()168     private void notifyServiceAdded() {
169         if (DEBUG) Log.d(TAG, "notifyServiceAdded");
170         Intent intent = new Intent(BLE_SERVICE_ADDED);
171         sendBroadcast(intent);
172     }
173 
notifyCharacteristicReadRequest()174     private void notifyCharacteristicReadRequest() {
175         if (DEBUG) Log.d(TAG, "notifyCharacteristicReadRequest");
176         Intent intent = new Intent(BLE_CHARACTERISTIC_READ_REQUEST);
177         sendBroadcast(intent);
178     }
179 
notifyCharacteristicWriteRequest()180     private void notifyCharacteristicWriteRequest() {
181         if (DEBUG) Log.d(TAG, "notifyCharacteristicWriteRequest");
182         Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE_REQUEST);
183         sendBroadcast(intent);
184     }
185 
notifyDescriptorReadRequest()186     private void notifyDescriptorReadRequest() {
187         if (DEBUG) Log.d(TAG, "notifyDescriptorReadRequest");
188         Intent intent = new Intent(BLE_DESCRIPTOR_READ_REQUEST);
189         sendBroadcast(intent);
190     }
191 
notifyDescriptorWriteRequest()192     private void notifyDescriptorWriteRequest() {
193         if (DEBUG) Log.d(TAG, "notifyDescriptorWriteRequest");
194         Intent intent = new Intent(BLE_DESCRIPTOR_WRITE_REQUEST);
195         sendBroadcast(intent);
196     }
197 
notifyExecuteWrite()198     private void notifyExecuteWrite() {
199         if (DEBUG) Log.d(TAG, "notifyExecuteWrite");
200         Intent intent = new Intent(BLE_EXECUTE_WRITE);
201         sendBroadcast(intent);
202     }
203 
getCharacteristic(UUID uuid)204     private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
205         BluetoothGattCharacteristic characteristic =
206                 mService.getCharacteristic(uuid);
207         if (characteristic == null) {
208             showMessage("Characteristic not found");
209             return null;
210         }
211         return characteristic;
212     }
213 
getDescriptor()214     private BluetoothGattDescriptor getDescriptor() {
215         BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
216         if (characteristic == null) return null;
217 
218         BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
219         if (descriptor == null) {
220             showMessage("Descriptor not found");
221             return null;
222         }
223         return descriptor;
224     }
225 
createService()226     private BluetoothGattService createService() {
227         BluetoothGattService service =
228                 new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
229         BluetoothGattCharacteristic characteristic =
230                 new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
231         BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
232         characteristic.addDescriptor(descriptor);
233         service.addCharacteristic(characteristic);
234 
235         BluetoothGattCharacteristic notiCharacteristic =
236                 new BluetoothGattCharacteristic(UPDATE_CHARACTERISTIC_UUID, 0x10, 0x00);
237         service.addCharacteristic(notiCharacteristic);
238 
239         return service;
240     }
241 
beginNotification()242     private void beginNotification() {
243         TimerTask task = new TimerTask() {
244             @Override
245             public void run() {
246                 if (mGattServer == null) {
247                     if (DEBUG) Log.d(TAG, "GattServer is null, return");
248                     return;
249                 }
250                 BluetoothGattCharacteristic characteristic =
251                         mService.getCharacteristic(UPDATE_CHARACTERISTIC_UUID);
252                 if (characteristic == null) return;
253 
254                 String date = (new Date()).toString();
255                 characteristic.setValue(date);
256                 mGattServer.notifyCharacteristicChanged(mDevice, characteristic, false);
257             }
258         };
259         mNotificationTimer = new Timer();
260         mNotificationTimer.schedule(task, 0, 1000);
261     }
262 
stopNotification()263     private void stopNotification() {
264         if (mNotificationTimer == null) return;
265         mNotificationTimer.cancel();
266         mNotificationTimer = null;
267     }
268 
showMessage(final String msg)269     private void showMessage(final String msg) {
270         mHandler.post(new Runnable() {
271             public void run() {
272                 Toast.makeText(BleServerService.this, msg, Toast.LENGTH_SHORT).show();
273             }
274         });
275     }
276 
277     private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
278         @Override
279         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
280             if (DEBUG) Log.d(TAG, "onConnectionStateChange: newState=" + newState);
281             if (status == BluetoothGatt.GATT_SUCCESS) {
282                 if (newState == BluetoothProfile.STATE_CONNECTED) {
283                     mDevice = device;
284                     notifyConnected();
285                     beginNotification();
286                 } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
287                     stopNotification();
288                     notifyDisconnected();
289                     mDevice = null;
290                 }
291             }
292         }
293 
294         @Override
295         public void onServiceAdded(int status, BluetoothGattService service) {
296             if (DEBUG) Log.d(TAG, "onServiceAdded()");
297             if (status == BluetoothGatt.GATT_SUCCESS) notifyServiceAdded();
298         }
299 
300         @Override
301         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
302                 int offset, BluetoothGattCharacteristic characteristic) {
303             if (mGattServer == null) {
304                 if (DEBUG) Log.d(TAG, "GattServer is null, return");
305                 return;
306             }
307             if (DEBUG) Log.d(TAG, "onCharacteristicReadRequest()");
308 
309             notifyCharacteristicReadRequest();
310             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0,
311                                      characteristic.getValue());
312         }
313 
314         @Override
315         public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
316                 BluetoothGattCharacteristic characteristic,
317                 boolean preparedWrite, boolean responseNeeded,
318                 int offset, byte[] value) {
319             if (mGattServer == null) {
320                 if (DEBUG) Log.d(TAG, "GattServer is null, return");
321                 return;
322             }
323             if (DEBUG) Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite);
324 
325             notifyCharacteristicWriteRequest();
326             if (preparedWrite) mReliableWriteValue = new String(value);
327             else characteristic.setValue(value);
328 
329             if (responseNeeded)
330                 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
331         }
332 
333         @Override
334         public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
335                 int offset, BluetoothGattDescriptor descriptor) {
336             if (mGattServer == null) {
337                 if (DEBUG) Log.d(TAG, "GattServer is null, return");
338                 return;
339             }
340             if (DEBUG) Log.d(TAG, "onDescriptorReadRequest(): (descriptor == getDescriptor())="
341                                   + (descriptor == getDescriptor()));
342 
343             notifyDescriptorReadRequest();
344             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0,
345                                      descriptor.getValue());
346         }
347 
348         @Override
349         public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
350                 BluetoothGattDescriptor descriptor,
351                 boolean preparedWrite, boolean responseNeeded,
352                 int offset,  byte[] value) {
353             if (mGattServer == null) {
354                 if (DEBUG) Log.d(TAG, "GattServer is null, return");
355                 return;
356             }
357             if (DEBUG) Log.d(TAG, "onDescriptorWriteRequest(): (descriptor == getDescriptor())="
358                                   + (descriptor == getDescriptor()));
359 
360             notifyDescriptorWriteRequest();
361             descriptor.setValue(value);
362             if (responseNeeded)
363                 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
364         }
365 
366         @Override
367         public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
368             if (mGattServer == null) {
369                 if (DEBUG) Log.d(TAG, "GattServer is null, return");
370                 return;
371             }
372             if (DEBUG) Log.d(TAG, "onExecuteWrite");
373             if (execute) {
374                 notifyExecuteWrite();
375                 getCharacteristic(CHARACTERISTIC_UUID).setValue(mReliableWriteValue);
376                 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
377             }
378         }
379     };
380 
startAdvertise()381     private void startAdvertise() {
382         if (DEBUG) Log.d(TAG, "startAdvertise");
383         AdvertiseData data = new AdvertiseData.Builder()
384             .addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1,2,3})
385             .addServiceUuid(new ParcelUuid(ADV_SERVICE_UUID))
386             .build();
387         AdvertiseSettings setting = new AdvertiseSettings.Builder()
388             .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
389             .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
390             .setConnectable(true)
391             .build();
392         mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
393     }
394 
stopAdvertise()395     private void stopAdvertise() {
396         if (DEBUG) Log.d(TAG, "stopAdvertise");
397         mAdvertiser.stopAdvertising(mAdvertiseCallback);
398     }
399 
400     private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){};
401 }
402 
403