Home | History | Annotate | Download | only in bluetooth
      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 {
     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
    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
    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
    147     public IBinder onBind(Intent intent) {
    148         return null;
    149     }
    150 
    151     @Override
    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      */
    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 
    193     private void notifyBluetoothDisabled() {
    194         Intent intent = new Intent(INTENT_BLUETOOTH_DISABLED);
    195         sendBroadcast(intent);
    196     }
    197 
    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 
    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 
    214     private void notifyConnected() {
    215         if (DEBUG) {
    216             Log.d(TAG, "notifyConnected");
    217         }
    218         resetValues();
    219     }
    220 
    221     private void notifyDisconnected() {
    222         if (DEBUG) {
    223             Log.d(TAG, "notifyDisconnected");
    224         }
    225     }
    226 
    227     private void notifyServiceAdded() {
    228         if (DEBUG) {
    229             Log.d(TAG, "notifyServiceAdded");
    230         }
    231     }
    232 
    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 
    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 
    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 
    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 
    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 
    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 
    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