1 /*
2  * Copyright (C) 2016 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 android.app.Service;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothGatt;
22 import android.bluetooth.BluetoothGattCharacteristic;
23 import android.bluetooth.BluetoothGattDescriptor;
24 import android.bluetooth.BluetoothGattServer;
25 import android.bluetooth.BluetoothGattServerCallback;
26 import android.bluetooth.BluetoothGattService;
27 import android.bluetooth.BluetoothManager;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.le.AdvertiseCallback;
30 import android.bluetooth.le.AdvertiseData;
31 import android.bluetooth.le.AdvertiseSettings;
32 import android.bluetooth.le.BluetoothLeAdvertiser;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.ParcelUuid;
38 import android.util.Log;
39 
40 import java.util.Arrays;
41 import java.util.UUID;
42 
43 public class BleEncryptedServerService extends Service {
BleEncryptedServerService()44     public BleEncryptedServerService() {
45     }
46     public static final boolean DEBUG = true;
47     public static final String TAG = "BleEncryptedServer";
48 
49     public static final String INTENT_BLUETOOTH_DISABLED =
50             "com.android.cts.verifier.bluetooth.encripted.intent.BLUETOOTH_DISABLED";
51 
52     public static final String ACTION_CONNECT_WITH_SECURE =
53             "com.android.cts.verifier.bluetooth.encripted.action.ACTION_CONNECT_WITH_SECURE";
54     public static final String ACTION_CONNECT_WITHOUT_SECURE =
55             "com.android.cts.verifier.bluetooth.encripted.action.ACTION_CONNECT_WITHOUT_SECURE";
56 
57     public static final String INTENT_WAIT_WRITE_ENCRYPTED_CHARACTERISTIC =
58             "com.android.cts.verifier.bluetooth.encripted.intent.WAIT_WRITE_ENCRYPTED_CHARACTERISTIC";
59     public static final String INTENT_WAIT_READ_ENCRYPTED_CHARACTERISTIC =
60             "com.android.cts.verifier.bluetooth.encripted.intent.WAIT_READ_ENCRYPTED_CHARACTERISTIC";
61     public static final String INTENT_WAIT_WRITE_ENCRYPTED_DESCRIPTOR =
62             "com.android.cts.verifier.bluetooth.encripted.intent.WAIT_WRITE_ENCRYPTED_DESCRIPTOR";
63     public static final String INTENT_WAIT_READ_ENCRYPTED_DESCRIPTOR =
64             "com.android.cts.verifier.bluetooth.encripted.intent.WAIT_READ_ENCRYPTED_DESCRIPTOR";
65 
66     private static final UUID SERVICE_UUID =
67             UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
68     private static final UUID CHARACTERISTIC_UUID =
69             UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
70     private static final UUID DESCRIPTOR_UUID =
71             UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
72 
73     private static final UUID CHARACTERISTIC_ENCRYPTED_WRITE_UUID =
74             UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
75     private static final UUID CHARACTERISTIC_ENCRYPTED_READ_UUID =
76             UUID.fromString("00009995-0000-1000-8000-00805f9b34fb");
77     private static final UUID DESCRIPTOR_ENCRYPTED_WRITE_UUID =
78             UUID.fromString("00009994-0000-1000-8000-00805f9b34fb");
79     private static final UUID DESCRIPTOR_ENCRYPTED_READ_UUID =
80             UUID.fromString("00009993-0000-1000-8000-00805f9b34fb");
81 
82     public static final UUID ADV_SERVICE_UUID=
83             UUID.fromString("00002222-0000-1000-8000-00805f9b34fb");
84 
85     private static final int CONN_INTERVAL = 150;   // connection interval 150ms
86 
87     public static final String EXTRA_SECURE = "SECURE";
88     public static final String WRITE_CHARACTERISTIC = "WRITE_CHAR";
89     public static final String READ_CHARACTERISTIC = "READ_CHAR";
90     public static final String WRITE_DESCRIPTOR = "WRITE_DESC";
91     public static final String READ_DESCRIPTOR = "READ_DESC";
92 
93     public static final String WRITE_VALUE = "ENC_SERVER_TEST";
94 
95     private BluetoothManager mBluetoothManager;
96     private BluetoothGattServer mGattServer;
97     private BluetoothGattService mService;
98     private BluetoothDevice mDevice;
99     private BluetoothLeAdvertiser mAdvertiser;
100     private boolean mSecure;
101     private String mTarget;
102     private Handler mHandler;
103     private Runnable mResetValuesTask;
104 
105     @Override
onCreate()106     public void onCreate() {
107         super.onCreate();
108 
109         mHandler = new Handler();
110         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
111         mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
112         mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
113         mService = createService();
114         if ((mGattServer != null) && (mAdvertiser != null)) {
115             mGattServer.addService(mService);
116         }
117         mDevice = null;
118         mSecure = false;
119         if (!mBluetoothManager.getAdapter().isEnabled()) {
120             notifyBluetoothDisabled();
121         } else if (mGattServer == null) {
122             notifyOpenFail();
123         } else if (mAdvertiser == null) {
124             notifyAdvertiseUnsupported();
125         } else {
126             startAdvertise();
127         }
128 
129         resetValues();
130     }
131 
132     @Override
onDestroy()133     public void onDestroy() {
134         super.onDestroy();
135         stopAdvertise();
136         if (mGattServer == null) {
137             return;
138         }
139         if (mDevice != null) {
140             mGattServer.cancelConnection(mDevice);
141         }
142         mGattServer.clearServices();
143         mGattServer.close();
144     }
145 
146     @Override
onBind(Intent intent)147     public IBinder onBind(Intent intent) {
148         return null;
149     }
150 
151     @Override
onStartCommand(Intent intent, int flags, int startId)152     public int onStartCommand(Intent intent, int flags, int startId) {
153         String action = intent.getAction();
154         if (action != null) {
155             switch (action) {
156             case ACTION_CONNECT_WITH_SECURE:
157                 mSecure = true;
158                 break;
159             case ACTION_CONNECT_WITHOUT_SECURE:
160                 mSecure = false;
161                 break;
162             }
163         }
164         return START_NOT_STICKY;
165     }
166 
167     /**
168      * Sets default value to characteristic and descriptor.
169      *
170      * Set operation will be done after connection interval.
171      * (If set values immediately, multiple read/write operations may fail.)
172      */
resetValues()173     private synchronized void resetValues() {
174         // cancel pending task
175         if (mResetValuesTask != null) {
176             mHandler.removeCallbacks(mResetValuesTask);
177             mResetValuesTask = null;
178         }
179 
180         // reserve task
181         mResetValuesTask = new Runnable() {
182             @Override
183             public void run() {
184                 BluetoothGattCharacteristic characteristic = mService.getCharacteristic(CHARACTERISTIC_ENCRYPTED_READ_UUID);
185                 characteristic.setValue(WRITE_VALUE.getBytes());
186                 characteristic = mService.getCharacteristic(CHARACTERISTIC_UUID);
187                 characteristic.getDescriptor(DESCRIPTOR_ENCRYPTED_READ_UUID).setValue(WRITE_VALUE.getBytes());
188             }
189         };
190         mHandler.postDelayed(mResetValuesTask, CONN_INTERVAL);
191     }
192 
notifyBluetoothDisabled()193     private void notifyBluetoothDisabled() {
194         Intent intent = new Intent(INTENT_BLUETOOTH_DISABLED);
195         sendBroadcast(intent);
196     }
197 
notifyOpenFail()198     private void notifyOpenFail() {
199         if (DEBUG) {
200             Log.d(TAG, "notifyOpenFail");
201         }
202         Intent intent = new Intent(BleServerService.BLE_OPEN_FAIL);
203         sendBroadcast(intent);
204     }
205 
notifyAdvertiseUnsupported()206     private void notifyAdvertiseUnsupported() {
207         if (DEBUG) {
208             Log.d(TAG, "notifyAdvertiseUnsupported");
209         }
210         Intent intent = new Intent(BleServerService.BLE_ADVERTISE_UNSUPPORTED);
211         sendBroadcast(intent);
212     }
213 
notifyConnected()214     private void notifyConnected() {
215         if (DEBUG) {
216             Log.d(TAG, "notifyConnected");
217         }
218         resetValues();
219     }
220 
notifyDisconnected()221     private void notifyDisconnected() {
222         if (DEBUG) {
223             Log.d(TAG, "notifyDisconnected");
224         }
225     }
226 
notifyServiceAdded()227     private void notifyServiceAdded() {
228         if (DEBUG) {
229             Log.d(TAG, "notifyServiceAdded");
230         }
231     }
232 
notifyCharacteristicWriteRequest()233     private void notifyCharacteristicWriteRequest() {
234         if (DEBUG) {
235             Log.d(TAG, "notifyCharacteristicWriteRequest");
236         }
237         Intent intent = new Intent(INTENT_WAIT_WRITE_ENCRYPTED_CHARACTERISTIC);
238         sendBroadcast(intent);
239         resetValues();
240     }
241 
notifyCharacteristicReadRequest()242     private void notifyCharacteristicReadRequest() {
243         if (DEBUG) {
244             Log.d(TAG, "notifyCharacteristicReadRequest");
245         }
246         Intent intent = new Intent(INTENT_WAIT_READ_ENCRYPTED_CHARACTERISTIC);
247         sendBroadcast(intent);
248         resetValues();
249     }
250 
notifyDescriptorWriteRequest()251     private void notifyDescriptorWriteRequest() {
252         if (DEBUG) {
253             Log.d(TAG, "notifyDescriptorWriteRequest");
254         }
255         Intent intent = new Intent(INTENT_WAIT_WRITE_ENCRYPTED_DESCRIPTOR);
256         sendBroadcast(intent);
257         resetValues();
258     }
259 
notifyDescriptorReadRequest()260     private void notifyDescriptorReadRequest() {
261         if (DEBUG) {
262             Log.d(TAG, "notifyDescriptorReadRequest");
263         }
264         Intent intent = new Intent(INTENT_WAIT_READ_ENCRYPTED_DESCRIPTOR);
265         sendBroadcast(intent);
266         resetValues();
267     }
268 
createService()269     private BluetoothGattService createService() {
270         BluetoothGattService service =
271                 new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
272         // add characteristic to service
273         //   property: 0x0A (read, write)
274         //   permission: 0x11 (read, write)
275         BluetoothGattCharacteristic characteristic =
276                 new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
277 
278         BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
279         characteristic.addDescriptor(descriptor);
280 
281         // Encrypted Descriptor
282         descriptor = new BluetoothGattDescriptor(DESCRIPTOR_ENCRYPTED_READ_UUID, 0x02);
283         characteristic.addDescriptor(descriptor);
284         descriptor = new BluetoothGattDescriptor(DESCRIPTOR_ENCRYPTED_WRITE_UUID, 0x20);
285         characteristic.addDescriptor(descriptor);
286         service.addCharacteristic(characteristic);
287 
288         // Encrypted Characteristic
289         characteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ENCRYPTED_READ_UUID, 0x0A, 0x02);
290         descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
291         characteristic.addDescriptor(descriptor);
292         service.addCharacteristic(characteristic);
293         characteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ENCRYPTED_WRITE_UUID, 0x0A, 0x20);
294         descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
295         characteristic.addDescriptor(descriptor);
296         service.addCharacteristic(characteristic);
297 
298         return service;
299     }
300 
301     private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
302         @Override
303         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
304             if (DEBUG) {
305                 Log.d(TAG, "onConnectionStateChange: newState=" + newState);
306             }
307             if (status == BluetoothGatt.GATT_SUCCESS) {
308                 if (newState == BluetoothProfile.STATE_CONNECTED) {
309                     mDevice = device;
310                     notifyConnected();
311                 } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
312                     notifyDisconnected();
313                     mDevice = null;
314                     mTarget = null;
315                 }
316             }
317         }
318 
319         @Override
320         public void onServiceAdded(int status, BluetoothGattService service) {
321             if (DEBUG) {
322                 Log.d(TAG, "onServiceAdded()");
323             }
324             if (status == BluetoothGatt.GATT_SUCCESS) {
325                 notifyServiceAdded();
326             }
327         }
328 
329         String mPriority = null;
330 
331         @Override
332         public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
333                                                  BluetoothGattCharacteristic characteristic,
334                                                  boolean preparedWrite, boolean responseNeeded,
335                                                  int offset, byte[] value) {
336             int status = BluetoothGatt.GATT_SUCCESS;
337             if (mGattServer == null) {
338                 if (DEBUG) {
339                     Log.d(TAG, "GattServer is null, return");
340                 }
341                 return;
342             }
343             if (DEBUG) {
344                 Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite);
345             }
346             if (characteristic.getUuid().equals(CHARACTERISTIC_ENCRYPTED_WRITE_UUID)) {
347                 if (mSecure) {
348                     characteristic.setValue(value);
349                     if (Arrays.equals(BleEncryptedClientService.WRITE_VALUE.getBytes(), characteristic.getValue())) {
350                         notifyCharacteristicWriteRequest();
351                     } else {
352                         status = BluetoothGatt.GATT_FAILURE;
353                     }
354                 } else {
355                     // will not occur
356                     status = BluetoothGatt.GATT_FAILURE;
357                 }
358             } else if (characteristic.getUuid().equals(CHARACTERISTIC_UUID)) {
359                 mTarget = new String(value);
360                 characteristic.setValue(value);
361             }
362 
363             if (responseNeeded) {
364                 mGattServer.sendResponse(device, requestId, status, offset, value);
365             }
366         }
367 
368         @Override
369         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
370             int status = BluetoothGatt.GATT_SUCCESS;
371             if (mGattServer == null) {
372                 if (DEBUG) {
373                     Log.d(TAG, "GattServer is null, return");
374                 }
375                 return;
376             }
377             if (DEBUG) {
378                 Log.d(TAG, "onCharacteristicReadRequest()");
379             }
380             if (characteristic.getUuid().equals(CHARACTERISTIC_ENCRYPTED_READ_UUID)) {
381                 if (mSecure) {
382                     notifyCharacteristicReadRequest();
383                 }
384             }
385             mGattServer.sendResponse(device, requestId, status, offset, characteristic.getValue());
386         }
387 
388         @Override
389         public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
390             int status = BluetoothGatt.GATT_SUCCESS;
391             if (mGattServer == null) {
392                 if (DEBUG) {
393                     Log.d(TAG, "GattServer is null, return");
394                 }
395                 return;
396             }
397             if (DEBUG) {
398                 Log.d(TAG, "onDescriptorReadRequest():");
399             }
400 
401             if (descriptor.getUuid().equals(DESCRIPTOR_ENCRYPTED_READ_UUID)) {
402                 if (mSecure) {
403                     notifyDescriptorReadRequest();
404                 }
405             }
406             Log.d(TAG, "  status = " + status);
407             mGattServer.sendResponse(device, requestId, status, offset, descriptor.getValue());
408         }
409 
410         @Override
411         public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
412             int status = BluetoothGatt.GATT_SUCCESS;
413             if (mGattServer == null) {
414                 if (DEBUG) {
415                     Log.d(TAG, "GattServer is null, return");
416                 }
417                 return;
418             }
419 
420             if (DEBUG) {
421                 Log.d(TAG, "onDescriptorWriteRequest: preparedWrite=" + preparedWrite + ", responseNeeded= " + responseNeeded);
422             }
423 
424             if (descriptor.getUuid().equals(DESCRIPTOR_ENCRYPTED_WRITE_UUID)) {
425                 if (mSecure) {
426                     descriptor.setValue(value);
427                     if (Arrays.equals(BleEncryptedClientService.WRITE_VALUE.getBytes(), descriptor.getValue())) {
428                         notifyDescriptorWriteRequest();
429                     } else {
430                         status = BluetoothGatt.GATT_FAILURE;
431                     }
432                 } else {
433                     // will not occur
434                     status = BluetoothGatt.GATT_FAILURE;
435                 }
436             }
437 
438             if (responseNeeded) {
439                 mGattServer.sendResponse(device, requestId, status, offset, value);
440             }
441         }
442     };
443 
444     private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
445         @Override
446         public void onStartFailure(int errorCode) {
447             super.onStartFailure(errorCode);
448             if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
449                 notifyAdvertiseUnsupported();
450             } else {
451                 notifyOpenFail();
452             }
453         }
454     };
455 
startAdvertise()456     private void startAdvertise() {
457         if (DEBUG) {
458             Log.d(TAG, "startAdvertise");
459         }
460         AdvertiseData data = new AdvertiseData.Builder()
461                 .addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1, 2, 3})
462                 .addServiceUuid(new ParcelUuid(ADV_SERVICE_UUID))
463                 .build();
464         AdvertiseSettings setting = new AdvertiseSettings.Builder()
465                 .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
466                 .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
467                 .setConnectable(true)
468                 .build();
469         mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
470     }
471 
stopAdvertise()472     private void stopAdvertise() {
473         if (DEBUG) {
474             Log.d(TAG, "stopAdvertise");
475         }
476         if (mAdvertiser != null) {
477             mAdvertiser.stopAdvertising(mAdvertiseCallback);
478         }
479     }
480 }
481