Home | History | Annotate | Download | only in comms
      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 package com.android.car.trust.comms;
     17 
     18 import android.app.Service;
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothGatt;
     22 import android.bluetooth.BluetoothGattCharacteristic;
     23 import android.bluetooth.BluetoothGattServer;
     24 import android.bluetooth.BluetoothGattServerCallback;
     25 import android.bluetooth.BluetoothGattService;
     26 import android.bluetooth.BluetoothManager;
     27 import android.bluetooth.BluetoothProfile;
     28 import android.bluetooth.le.AdvertiseCallback;
     29 import android.bluetooth.le.AdvertiseData;
     30 import android.bluetooth.le.AdvertiseSettings;
     31 import android.bluetooth.le.BluetoothLeAdvertiser;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.content.pm.PackageManager;
     35 import android.os.IBinder;
     36 import android.os.ParcelUuid;
     37 import android.util.Log;
     38 
     39 import java.util.HashSet;
     40 
     41 /**
     42  * A generic service to start a BLE
     43  */
     44 public abstract class SimpleBleServer extends Service {
     45 
     46     /**
     47      * Listener that is notified when the status of the BLE server changes.
     48      */
     49     public interface ConnectionListener {
     50         /**
     51          * Called when the GATT server is started and BLE is successfully advertising.
     52          */
     53         void onServerStarted();
     54 
     55         /**
     56          * Called when the BLE advertisement fails to start.
     57          *
     58          * @param errorCode Error code (see {@link AdvertiseCallback}#ADVERTISE_FAILED_* constants)
     59          */
     60         void onServerStartFailed(int errorCode);
     61 
     62         /**
     63          * Called when a device is connected.
     64          * @param device
     65          */
     66         void onDeviceConnected(BluetoothDevice device);
     67     }
     68 
     69     private static final String TAG = "SimpleBleServer";
     70 
     71     private BluetoothLeAdvertiser mAdvertiser;
     72     protected BluetoothGattServer mGattServer;
     73 
     74     private HashSet<ConnectionListener> mListeners = new HashSet<>();
     75 
     76     @Override
     77     public IBinder onBind(Intent intent) {
     78         // Override in child classes.
     79         return null;
     80     }
     81 
     82     /**
     83      * Starts the GATT server with the given {@link BluetoothGattService} and begins
     84      * advertising with the {@link ParcelUuid}.
     85      * @param advertiseUuid Service Uuid used in the {@link AdvertiseData}
     86      * @param service {@link BluetoothGattService} that will be discovered by clients
     87      */
     88     protected void start(ParcelUuid advertiseUuid, BluetoothGattService service) {
     89         if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
     90             Log.e(TAG, "System does not support BLE");
     91             return;
     92         }
     93 
     94         BluetoothManager btManager =
     95                 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
     96 
     97         mGattServer = btManager.openGattServer(this, mGattServerCallback);
     98         if (mGattServer == null) {
     99             Log.e(TAG, "Gatt Server not created");
    100             return;
    101         }
    102 
    103         // We only allow adding one service in this implementation. If multiple services need
    104         // to be added, then they need to be queued up and added only after
    105         // BluetoothGattServerCallback.onServiceAdded is called.
    106         mGattServer.addService(service);
    107 
    108         AdvertiseSettings settings = new AdvertiseSettings.Builder()
    109                 .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
    110                 .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
    111                 .setConnectable(true)
    112                 .build();
    113 
    114         AdvertiseData data = new AdvertiseData.Builder()
    115                 .setIncludeDeviceName(true)
    116                 .addServiceUuid(advertiseUuid)
    117                 .build();
    118 
    119         mAdvertiser
    120                 = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
    121 
    122         mAdvertiser.startAdvertising(settings, data, mAdvertisingCallback);
    123     }
    124 
    125     /**
    126      * Stops the advertiser and GATT server. This needs to be done to avoid leaks
    127      */
    128     protected void stop() {
    129         if (mAdvertiser != null) {
    130             mAdvertiser.stopAdvertising(mAdvertisingCallback);
    131             mAdvertiser.cleanup();
    132         }
    133 
    134         if (mGattServer != null) {
    135             mGattServer.clearServices();
    136             try {
    137                 for (BluetoothDevice d : mGattServer.getConnectedDevices()) {
    138                     mGattServer.cancelConnection(d);
    139                 }
    140             } catch (UnsupportedOperationException e) {
    141                 Log.e(TAG, "Error getting connected devices", e);
    142             } finally {
    143                 mGattServer.close();
    144             }
    145         }
    146 
    147         mListeners.clear();
    148     }
    149 
    150     @Override
    151     public void onDestroy() {
    152         stop();
    153         super.onDestroy();
    154     }
    155 
    156     public void addConnectionListener(ConnectionListener listener) {
    157         if (Log.isLoggable(TAG, Log.DEBUG)) {
    158             Log.d(TAG, "Adding connection listener");
    159         }
    160         mListeners.add(listener);
    161     }
    162 
    163     /**
    164      * Triggered when this BleService receives a write request from a remote
    165      * device. Sub-classes should implement how to handle requests.
    166      */
    167     public abstract void onCharacteristicWrite(final BluetoothDevice device, int requestId,
    168             BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
    169             responseNeeded, int offset, byte[] value);
    170 
    171     /**
    172      * Triggered when this BleService receives a read request from a remote device.
    173      */
    174     public abstract void onCharacteristicRead(BluetoothDevice device,
    175             int requestId, int offset, final BluetoothGattCharacteristic characteristic);
    176 
    177     private AdvertiseCallback mAdvertisingCallback = new AdvertiseCallback() {
    178         @Override
    179         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
    180             super.onStartSuccess(settingsInEffect);
    181             if (Log.isLoggable(TAG, Log.DEBUG)) {
    182                 Log.d(TAG, "Successfully started advertising service");
    183             }
    184             for (ConnectionListener listener : mListeners) {
    185                 listener.onServerStarted();
    186             }
    187         }
    188 
    189         @Override
    190         public void onStartFailure(int errorCode) {
    191             super.onStartFailure(errorCode);
    192             if (Log.isLoggable(TAG, Log.DEBUG)) {
    193                 Log.d(TAG, "Failed to advertise, errorCode: " + errorCode);
    194             }
    195             for (ConnectionListener listener : mListeners) {
    196                 listener.onServerStartFailed(errorCode);
    197             }
    198         }
    199     };
    200 
    201     private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
    202         @Override
    203         public void onConnectionStateChange(BluetoothDevice device,
    204                 final int status, final int newState) {
    205             if (Log.isLoggable(TAG, Log.DEBUG)) {
    206                 Log.d(TAG, "GattServer connection change status: "
    207                         + newState + " newState: "
    208                         + newState + " device name: " + device.getName());
    209             }
    210             if (newState == BluetoothProfile.STATE_CONNECTED) {
    211                 for (ConnectionListener listener : mListeners) {
    212                     listener.onDeviceConnected(device);
    213                 }
    214             }
    215         }
    216 
    217         @Override
    218         public void onServiceAdded(final int status, BluetoothGattService service) {
    219             if (Log.isLoggable(TAG, Log.DEBUG)) {
    220                 Log.d(TAG, "Service added status: " + status + " uuid: " + service.getUuid());
    221             }
    222         }
    223 
    224         @Override
    225         public void onCharacteristicReadRequest(BluetoothDevice device,
    226                 int requestId, int offset, final BluetoothGattCharacteristic characteristic) {
    227             if (Log.isLoggable(TAG, Log.DEBUG)) {
    228                 Log.d(TAG, "Read request for characteristic: " + characteristic.getUuid());
    229             }
    230             mGattServer.sendResponse(device, requestId,
    231                     BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
    232             SimpleBleServer.
    233                     this.onCharacteristicRead(device, requestId, offset, characteristic);
    234         }
    235 
    236         @Override
    237         public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId,
    238                 BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
    239                 responseNeeded, int offset, byte[] value) {
    240             if (Log.isLoggable(TAG, Log.DEBUG)) {
    241                 Log.d(TAG, "Write request for characteristic: " + characteristic.getUuid());
    242             }
    243             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
    244                     offset, value);
    245 
    246             SimpleBleServer.
    247                     this.onCharacteristicWrite(device, requestId, characteristic,
    248                     preparedWrite, responseNeeded, offset, value);
    249         }
    250     };
    251 }
    252