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.Arrays;
20 import java.util.UUID;
21 import java.util.List;
22 
23 import android.app.Service;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothGatt;
27 import android.bluetooth.BluetoothGattCallback;
28 import android.bluetooth.BluetoothGattCharacteristic;
29 import android.bluetooth.BluetoothGattDescriptor;
30 import android.bluetooth.BluetoothGattService;
31 import android.bluetooth.BluetoothManager;
32 import android.bluetooth.BluetoothProfile;
33 import android.bluetooth.le.BluetoothLeScanner;
34 import android.bluetooth.le.ScanCallback;
35 import android.bluetooth.le.ScanFilter;
36 import android.bluetooth.le.ScanResult;
37 import android.bluetooth.le.ScanSettings;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.ParcelUuid;
43 import android.util.Log;
44 import android.widget.Toast;
45 
46 public class BleClientService extends Service {
47 
48     public static final boolean DEBUG = true;
49     public static final String TAG = "BleClientService";
50 
51     public static final int COMMAND_CONNECT = 0;
52     public static final int COMMAND_DISCONNECT = 1;
53     public static final int COMMAND_DISCOVER_SERVICE = 2;
54     public static final int COMMAND_READ_RSSI = 3;
55     public static final int COMMAND_WRITE_CHARACTERISTIC = 4;
56     public static final int COMMAND_READ_CHARACTERISTIC = 5;
57     public static final int COMMAND_WRITE_DESCRIPTOR = 6;
58     public static final int COMMAND_READ_DESCRIPTOR = 7;
59     public static final int COMMAND_SET_NOTIFICATION = 8;
60     public static final int COMMAND_BEGIN_WRITE = 9;
61     public static final int COMMAND_EXECUTE_WRITE = 10;
62     public static final int COMMAND_ABORT_RELIABLE = 11;
63     public static final int COMMAND_SCAN_START = 12;
64     public static final int COMMAND_SCAN_STOP = 13;
65 
66     public static final String BLE_BLUETOOTH_CONNECTED =
67             "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_CONNECTED";
68     public static final String BLE_BLUETOOTH_DISCONNECTED =
69             "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISCONNECTED";
70     public static final String BLE_SERVICES_DISCOVERED =
71             "com.android.cts.verifier.bluetooth.BLE_SERVICES_DISCOVERED";
72     public static final String BLE_CHARACTERISTIC_READ =
73             "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ";
74     public static final String BLE_CHARACTERISTIC_WRITE =
75             "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE";
76     public static final String BLE_CHARACTERISTIC_CHANGED =
77             "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_CHANGED";
78     public static final String BLE_DESCRIPTOR_READ =
79             "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_READ";
80     public static final String BLE_DESCRIPTOR_WRITE =
81             "com.android.cts.verifier.bluetooth.BLE_DESCRIPTOR_WRITE";
82     public static final String BLE_RELIABLE_WRITE_COMPLETED =
83             "com.android.cts.verifier.bluetooth.BLE_RELIABLE_WRITE_COMPLETED";
84     public static final String BLE_READ_REMOTE_RSSI =
85             "com.android.cts.verifier.bluetooth.BLE_READ_REMOTE_RSSI";
86 
87     public static final String EXTRA_COMMAND =
88             "com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
89     public static final String EXTRA_WRITE_VALUE =
90             "com.android.cts.verifier.bluetooth.EXTRA_WRITE_VALUE";
91     public static final String EXTRA_BOOL =
92             "com.android.cts.verifier.bluetooth.EXTRA_BOOL";
93     public static final String EXTRA_CHARACTERISTIC_VALUE =
94             "com.android.cts.verifier.bluetooth.EXTRA_CHARACTERISTIC_VALUE";
95     public static final String EXTRA_DESCRIPTOR_VALUE =
96             "com.android.cts.verifier.bluetooth.EXTRA_DESCRIPTOR_VALUE";
97     public static final String EXTRA_RSSI_VALUE =
98             "com.android.cts.verifier.bluetooth.EXTRA_RSSI_VALUE";
99     public static final String EXTRA_ERROR_MESSAGE =
100             "com.android.cts.verifier.bluetooth.EXTRA_ERROR_MESSAGE";
101 
102     private static final UUID SERVICE_UUID =
103             UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
104     private static final UUID CHARACTERISTIC_UUID =
105             UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
106     private static final UUID UPDATE_CHARACTERISTIC_UUID =
107             UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
108     private static final UUID DESCRIPTOR_UUID =
109             UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
110 
111     private static final String WRITE_VALUE = "TEST";
112 
113     private BluetoothManager mBluetoothManager;
114     private BluetoothAdapter mBluetoothAdapter;
115     private BluetoothDevice mDevice;
116     private BluetoothGatt mBluetoothGatt;
117     private BluetoothLeScanner mScanner;
118     private Handler mHandler;
119     private Context mContext;
120 
121     @Override
onCreate()122     public void onCreate() {
123         super.onCreate();
124 
125         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
126         mBluetoothAdapter = mBluetoothManager.getAdapter();
127         mScanner = mBluetoothAdapter.getBluetoothLeScanner();
128         mHandler = new Handler();
129         mContext = this;
130         startScan();
131     }
132 
133     @Override
onStartCommand(Intent intent, int flags, int startId)134     public int onStartCommand(Intent intent, int flags, int startId) {
135         return START_NOT_STICKY;
136     }
137 
138     @Override
onBind(Intent intent)139     public IBinder onBind(Intent intent) {
140         return null;
141     }
142 
143     @Override
onDestroy()144     public void onDestroy() {
145         super.onDestroy();
146         mBluetoothGatt.disconnect();
147         mBluetoothGatt.close();
148         mBluetoothGatt = null;
149         stopScan();
150     }
151 
writeCharacteristic(String writeValue)152     private void writeCharacteristic(String writeValue) {
153         BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
154         if (characteristic == null) return;
155         characteristic.setValue(writeValue);
156         mBluetoothGatt.writeCharacteristic(characteristic);
157     }
158 
readCharacteristic()159     private void readCharacteristic() {
160         BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
161         if (characteristic != null) mBluetoothGatt.readCharacteristic(characteristic);
162     }
163 
writeDescriptor(String writeValue)164     private void writeDescriptor(String writeValue) {
165         BluetoothGattDescriptor descriptor = getDescriptor();
166         if (descriptor == null) return;
167         descriptor.setValue(writeValue.getBytes());
168         mBluetoothGatt.writeDescriptor(descriptor);
169     }
170 
readDescriptor()171     private void readDescriptor() {
172         BluetoothGattDescriptor descriptor = getDescriptor();
173         if (descriptor != null) mBluetoothGatt.readDescriptor(descriptor);
174     }
175 
setNotification(boolean enable)176     private void setNotification(boolean enable) {
177         BluetoothGattCharacteristic characteristic = getCharacteristic(UPDATE_CHARACTERISTIC_UUID);
178         if (characteristic != null)
179             mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
180     }
181 
notifyError(String message)182     private void notifyError(String message) {
183         showMessage(message);
184     }
185 
notifyConnected()186     private void notifyConnected() {
187         showMessage("BLE connected");
188         Intent intent = new Intent(BLE_BLUETOOTH_CONNECTED);
189         sendBroadcast(intent);
190     }
191 
notifyDisconnected()192     private void notifyDisconnected() {
193         showMessage("BLE disconnected");
194         Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED);
195         sendBroadcast(intent);
196     }
197 
notifyServicesDiscovered()198     private void notifyServicesDiscovered() {
199         showMessage("Service discovered");
200         Intent intent = new Intent(BLE_SERVICES_DISCOVERED);
201         sendBroadcast(intent);
202     }
203 
notifyCharacteristicRead(String value)204     private void notifyCharacteristicRead(String value) {
205         showMessage("Characteristic read: " + value);
206         Intent intent = new Intent(BLE_CHARACTERISTIC_READ);
207         intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
208         sendBroadcast(intent);
209     }
210 
notifyCharacteristicWrite(String value)211     private void notifyCharacteristicWrite(String value) {
212         showMessage("Characteristic write: " + value);
213         Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE);
214         sendBroadcast(intent);
215     }
216 
notifyCharacteristicChanged(String value)217     private void notifyCharacteristicChanged(String value) {
218         showMessage("Characteristic changed: " + value);
219         Intent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);
220         intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
221         sendBroadcast(intent);
222     }
223 
notifyDescriptorRead(String value)224     private void notifyDescriptorRead(String value) {
225         showMessage("Descriptor read: " + value);
226         Intent intent = new Intent(BLE_DESCRIPTOR_READ);
227         intent.putExtra(EXTRA_DESCRIPTOR_VALUE, value);
228         sendBroadcast(intent);
229     }
230 
notifyDescriptorWrite(String value)231     private void notifyDescriptorWrite(String value) {
232         showMessage("Descriptor write: " + value);
233         Intent intent = new Intent(BLE_DESCRIPTOR_WRITE);
234         sendBroadcast(intent);
235     }
236 
notifyReliableWriteCompleted()237     private void notifyReliableWriteCompleted() {
238         showMessage("Reliable write compelte");
239         Intent intent = new Intent(BLE_RELIABLE_WRITE_COMPLETED);
240         sendBroadcast(intent);
241     }
242 
notifyReadRemoteRssi(int rssi)243     private void notifyReadRemoteRssi(int rssi) {
244         showMessage("Remote rssi read: " + rssi);
245         Intent intent = new Intent(BLE_READ_REMOTE_RSSI);
246         intent.putExtra(EXTRA_RSSI_VALUE, rssi);
247         sendBroadcast(intent);
248     }
249 
getService()250     private BluetoothGattService getService() {
251         if (mBluetoothGatt == null) return null;
252 
253         BluetoothGattService service = mBluetoothGatt.getService(SERVICE_UUID);
254         if (service == null) {
255             showMessage("Service not found");
256             return null;
257         }
258         return service;
259     }
260 
getCharacteristic(UUID uuid)261     private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
262         BluetoothGattService service = getService();
263         if (service == null) return null;
264 
265         BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
266         if (characteristic == null) {
267             showMessage("Characteristic not found");
268             return null;
269         }
270         return characteristic;
271     }
272 
getDescriptor()273     private BluetoothGattDescriptor getDescriptor() {
274         BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
275         if (characteristic == null) return null;
276 
277         BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
278         if (descriptor == null) {
279             showMessage("Descriptor not found");
280             return null;
281         }
282         return descriptor;
283     }
284 
showMessage(final String msg)285     private void showMessage(final String msg) {
286         mHandler.post(new Runnable() {
287             public void run() {
288                 Toast.makeText(BleClientService.this, msg, Toast.LENGTH_SHORT).show();
289             }
290         });
291     }
292 
sleep(int millis)293     private void sleep(int millis) {
294         try {
295             Thread.sleep(millis);
296         } catch (InterruptedException e) {
297             Log.e(TAG, "Error in thread sleep", e);
298         }
299     }
300 
reliableWrite()301     private void reliableWrite() {
302         mBluetoothGatt.beginReliableWrite();
303         sleep(1000);
304         writeCharacteristic(WRITE_VALUE);
305         sleep(1000);
306         if (!mBluetoothGatt.executeReliableWrite()) {
307             Log.w(TAG, "reliable write failed");
308         }
309         sleep(1000);
310         mBluetoothGatt.abortReliableWrite();
311     }
312 
313     private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
314         @Override
315         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
316             if (DEBUG) Log.d(TAG, "onConnectionStateChange");
317             if (status == BluetoothGatt.GATT_SUCCESS) {
318                 if (newState == BluetoothProfile.STATE_CONNECTED) {
319                     notifyConnected();
320                     stopScan();
321                     sleep(1000);
322                     mBluetoothGatt.discoverServices();
323                 } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
324                     notifyDisconnected();
325                 }
326             } else {
327                 showMessage("Failed to connect");
328             }
329         }
330 
331         @Override
332         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
333             if (DEBUG) Log.d(TAG, "onServiceDiscovered");
334             if ((status == BluetoothGatt.GATT_SUCCESS) &&
335                 (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
336                 notifyServicesDiscovered();
337                 sleep(1000);
338                 writeCharacteristic(WRITE_VALUE);
339             }
340         }
341 
342         @Override
343         public void onCharacteristicWrite(BluetoothGatt gatt,
344                                           BluetoothGattCharacteristic characteristic, int status) {
345             String value = characteristic.getStringValue(0);
346             if (DEBUG) Log.d(TAG, "onCharacteristicWrite: characteristic.val="
347                     + value + " status=" + status);
348             BluetoothGattCharacteristic mCharacteristic = getCharacteristic(CHARACTERISTIC_UUID);
349             if ((status == BluetoothGatt.GATT_SUCCESS) &&
350                 (value.equals(mCharacteristic.getStringValue(0)))) {
351                 notifyCharacteristicWrite(value);
352                 sleep(1000);
353                 readCharacteristic();
354             } else {
355                 notifyError("Failed to write characteristic: " + value);
356             }
357         }
358 
359         @Override
360         public void onCharacteristicRead(BluetoothGatt gatt,
361                                          BluetoothGattCharacteristic characteristic, int status) {
362             if (DEBUG) Log.d(TAG, "onCharacteristicRead");
363             if ((status == BluetoothGatt.GATT_SUCCESS) &&
364                 (characteristic.getUuid().equals(CHARACTERISTIC_UUID))) {
365                 notifyCharacteristicRead(characteristic.getStringValue(0));
366                 sleep(1000);
367                 writeDescriptor(WRITE_VALUE);
368             } else {
369                 notifyError("Failed to read characteristic");
370             }
371         }
372 
373         @Override
374         public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
375                                       int status) {
376             if (DEBUG) Log.d(TAG, "onDescriptorWrite");
377             if ((status == BluetoothGatt.GATT_SUCCESS) &&
378                 (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
379                 notifyDescriptorWrite(new String(descriptor.getValue()));
380                 sleep(1000);
381                 readDescriptor();
382             } else {
383                 notifyError("Failed to write descriptor");
384             }
385         }
386 
387         @Override
388         public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
389                                      int status) {
390             if (DEBUG) Log.d(TAG, "onDescriptorRead");
391             if ((status == BluetoothGatt.GATT_SUCCESS) &&
392                 (descriptor.getUuid() != null) &&
393                 (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
394                 notifyDescriptorRead(new String(descriptor.getValue()));
395                 sleep(1000);
396                 setNotification(true);
397             } else {
398                 notifyError("Failed to read descriptor");
399             }
400         }
401 
402         @Override
403         public void onCharacteristicChanged(BluetoothGatt gatt,
404                                             BluetoothGattCharacteristic characteristic) {
405             if (DEBUG) Log.d(TAG, "onCharacteristicChanged");
406             if ((characteristic.getUuid() != null) &&
407                 (characteristic.getUuid().equals(UPDATE_CHARACTERISTIC_UUID))) {
408                 notifyCharacteristicChanged(characteristic.getStringValue(0));
409                 setNotification(false);
410                 sleep(1000);
411                 mBluetoothGatt.readRemoteRssi();
412             }
413         }
414 
415         @Override
416         public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
417             if (DEBUG) Log.d(TAG, "onReliableWriteComplete: " + status);
418             if (status == BluetoothGatt.GATT_SUCCESS) {
419                 notifyReliableWriteCompleted();
420             } else {
421                 notifyError("Failed to complete reliable write: " + status);
422             }
423             sleep(1000);
424             mBluetoothGatt.disconnect();
425         }
426 
427         @Override
428         public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
429             if (DEBUG) Log.d(TAG, "onReadRemoteRssi");
430             if (status == BluetoothGatt.GATT_SUCCESS) {
431                 notifyReadRemoteRssi(rssi);
432             } else {
433                 notifyError("Failed to read remote rssi");
434             }
435             sleep(1000);
436             reliableWrite();
437         }
438     };
439 
440     private final ScanCallback mScanCallback = new ScanCallback() {
441         @Override
442         public void onScanResult(int callbackType, ScanResult result) {
443             if (mBluetoothGatt == null) {
444                 mBluetoothGatt = result.getDevice().connectGatt(mContext, false, mGattCallbacks);
445             }
446         }
447     };
448 
startScan()449     private void startScan() {
450         if (DEBUG) Log.d(TAG, "startScan");
451         List<ScanFilter> filter = Arrays.asList(new ScanFilter.Builder().setServiceUuid(
452                 new ParcelUuid(BleServerService.ADV_SERVICE_UUID)).build());
453         ScanSettings setting = new ScanSettings.Builder()
454                 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
455         mScanner.startScan(filter, setting, mScanCallback);
456     }
457 
stopScan()458     private void stopScan() {
459         if (DEBUG) Log.d(TAG, "stopScan");
460         mScanner.stopScan(mScanCallback);
461     }
462 }
463