Home | History | Annotate | Download | only in pmc
      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 
     17 package com.android.pmc;
     18 
     19 
     20 import android.app.Service;
     21 import android.bluetooth.BluetoothAdapter;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.bluetooth.BluetoothGatt;
     24 import android.bluetooth.BluetoothGattCharacteristic;
     25 import android.bluetooth.BluetoothGattDescriptor;
     26 import android.bluetooth.BluetoothGattServer;
     27 import android.bluetooth.BluetoothGattServerCallback;
     28 import android.bluetooth.BluetoothGattService;
     29 import android.bluetooth.BluetoothManager;
     30 import android.bluetooth.BluetoothProfile;
     31 import android.bluetooth.le.AdvertiseCallback;
     32 import android.bluetooth.le.AdvertiseData;
     33 import android.bluetooth.le.AdvertiseSettings;
     34 import android.bluetooth.le.BluetoothLeAdvertiser;
     35 import android.content.Context;
     36 import android.util.Log;
     37 
     38 import java.util.UUID;
     39 
     40 /**
     41  * Class to implement Gatt Server functionalities
     42  */
     43 public class GattServer {
     44     public static final String TAG = "GATTS";
     45 
     46     private MyBleAdvertiser mBleAdvertiser;
     47     private Context mContext;
     48     private BluetoothManager mBluetoothManager;
     49     private BluetoothGattServer mGattServer;
     50     private MyGattServerCallback mGattServerCallBack;
     51     private BluetoothGattService mGattService;
     52     private static final String READABLE_DESC_UUID = "76d5ed92-ca81-4edb-bb6b-9f019665fb32";
     53     public static final String WRITABLE_CHAR_UUID = "aa7edd5a-4d1d-4f0e-883a-d145616a1630";
     54     public static final String TEST_SERVICE_UUID = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B";
     55 
     56     /**
     57      * Constructor
     58      *
     59      * @param context - System will provide a context
     60      */
     61     public GattServer(Context context) {
     62         Log.d(TAG, "Start GattServer()");
     63         mContext = context;
     64         // Check if Bluetooth is enabled
     65         BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     66 
     67         if (bluetoothAdapter == null) {
     68             Log.e(TAG, "BluetoothAdapter is Null");
     69             return;
     70         } else {
     71             if (!bluetoothAdapter.isEnabled()) {
     72                 Log.d(TAG, "BluetoothAdapter is NOT enabled, enable now");
     73                 bluetoothAdapter.enable();
     74                 if (!bluetoothAdapter.isEnabled()) {
     75                     Log.e(TAG, "Can't enable Bluetooth");
     76                     return;
     77                 }
     78             }
     79         }
     80 
     81         // Prepare data for GATT service
     82         mBluetoothManager = (BluetoothManager) context.getSystemService(
     83                                 Service.BLUETOOTH_SERVICE);
     84 
     85         mGattServerCallBack = new MyGattServerCallback();
     86 
     87         BluetoothGattCharacteristic characteristic =
     88                     new BluetoothGattCharacteristic(UUID.fromString(WRITABLE_CHAR_UUID),
     89                     BluetoothGattCharacteristic.PROPERTY_WRITE
     90                     | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
     91                     BluetoothGattCharacteristic.PERMISSION_WRITE);
     92 
     93         BluetoothGattDescriptor descriptor =
     94                     new BluetoothGattDescriptor(UUID.fromString(READABLE_DESC_UUID),
     95                     BluetoothGattDescriptor.PERMISSION_READ
     96                     | BluetoothGattDescriptor.PERMISSION_WRITE);
     97 
     98         characteristic.addDescriptor(descriptor);
     99 
    100         mGattService = new BluetoothGattService(UUID.fromString(TEST_SERVICE_UUID),
    101                     BluetoothGattService.SERVICE_TYPE_PRIMARY);
    102         mGattService.addCharacteristic(characteristic);
    103 
    104         // Create BLE Advertiser object
    105         mBleAdvertiser = new MyBleAdvertiser(bluetoothAdapter);
    106         Log.d(TAG, "End GattServer()");
    107     }
    108 
    109     /**
    110      * Function to be called to start Gatt Server
    111      */
    112     public void startGattServer() {
    113         // Connect to Gatt Server
    114         mGattServer = mBluetoothManager.openGattServer(mContext, mGattServerCallBack);
    115         // Add GATT Service to Gatt Server
    116         mGattServer.addService(mGattService);
    117         // Start BLE Advertising here
    118         mBleAdvertiser.startAdvertising();
    119         Log.d(TAG, "startGattServer finished");
    120     }
    121 
    122     /**
    123      * Class to provide callback for GATT server to handle GATT requests
    124      */
    125     class MyGattServerCallback extends BluetoothGattServerCallback {
    126 
    127         MyGattServerCallback() {}
    128 
    129         @Override
    130         public void onServiceAdded(int status, BluetoothGattService service) {
    131             Log.d(TAG, "onServiceAdded: " + status);
    132         }
    133 
    134         @Override
    135         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
    136                 BluetoothGattCharacteristic characteristic) {
    137             Log.d(TAG, "onCharacteristicReadRequest: " + characteristic);
    138         }
    139 
    140         @Override
    141         public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
    142                 BluetoothGattCharacteristic characteristic, boolean preparedWrite,
    143                 boolean responseNeeded, int offset, byte[] value) {
    144             Log.d(TAG, "onCharacteristicWriteRequest requestId: " + requestId
    145                         + " preparedWrite: " + preparedWrite + " sendRespons back");
    146 
    147             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
    148         }
    149 
    150         @Override
    151         public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset,
    152                 BluetoothGattDescriptor descriptor) {
    153             Log.d(TAG, "onDescriptorReadRequest requestId: " + requestId);
    154         }
    155 
    156         @Override
    157         public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
    158                 BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded,
    159                 int offset, byte[] value) {
    160             Log.d(TAG, "onDescriptorWriteRequest requestId: " + requestId + " preparedWrite: "
    161                     + preparedWrite);
    162         }
    163 
    164         @Override
    165         public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
    166             Log.d(TAG, "onExecuteWrite requestId: " + requestId + " execute: " + execute);
    167             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
    168             Log.d(TAG, "onExecuteWrite sendResponse back to GATT Client");
    169         }
    170 
    171         @Override
    172         public void onNotificationSent(BluetoothDevice device, int status) {
    173             Log.d(TAG, "onNotificationSent " + status);
    174         }
    175 
    176         @Override
    177         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
    178             Log.d(TAG, "onConnectionStateChange status: " + status + " new state: " + newState);
    179             if (newState == BluetoothProfile.STATE_CONNECTED) {
    180                 Log.d(TAG, "Connected to mac address " + device.getAddress() + " status " + status);
    181 
    182             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
    183                 Log.d(TAG, "Disconnected from mac address " + device.getAddress() + " status "
    184                         + status);
    185             }
    186         }
    187 
    188         @Override
    189         public void onMtuChanged(BluetoothDevice device, int mtu) {
    190             Log.d(TAG, "onMtuChanged: " + mtu);
    191         }
    192     }
    193 
    194     /**
    195      * Class to provide BLE Advertising functionalities
    196      */
    197     class MyBleAdvertiser {
    198 
    199         private BluetoothLeAdvertiser mAdvertiser;
    200         private AdvertiseSettings mAdvertiseSettings;
    201         private AdvertiseData mAdvertiseData;
    202         private MyAdvertiseCallback mAdvertiseCallback;
    203 
    204         /**
    205          * Constructor
    206          * @param bluetoothAdapter - Default BluetoothAdapter
    207          */
    208         MyBleAdvertiser(BluetoothAdapter bluetoothAdapter) {
    209             // Prepare for BLE Advertisement
    210             mAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
    211             mAdvertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true).build();
    212             mAdvertiseCallback = new MyAdvertiseCallback();
    213             mAdvertiseSettings = new AdvertiseSettings.Builder()
    214                                  .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
    215                                  .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
    216                                  .setConnectable(true)
    217                                  .setTimeout(0).build();
    218         }
    219 
    220         /**
    221          * Wrapper function to start BLE Advertising
    222          */
    223         public void startAdvertising() {
    224             mAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData,
    225                         mAdvertiseCallback);
    226         }
    227 
    228         /**
    229          * Wrapper function to stop BLE Advertising
    230          */
    231         public void stopAdvertising() {
    232             mAdvertiser.stopAdvertising(mAdvertiseCallback);
    233         }
    234 
    235         /**
    236          * Class to provide callback to handle BLE Advertisement
    237          */
    238         class MyAdvertiseCallback extends AdvertiseCallback {
    239             private boolean mMaxReached;
    240             // The lock object is used to synchronize mMaxReached
    241             private final Object mLock = new Object();
    242 
    243             MyAdvertiseCallback() {
    244                 mMaxReached = false;
    245             }
    246 
    247             public void setMaxReached(boolean setMax) {
    248                 synchronized (mLock) {
    249                     mMaxReached = setMax;
    250                 }
    251             }
    252             public boolean getMaxReached() {
    253                 synchronized (mLock) {
    254                     return mMaxReached;
    255                 }
    256             }
    257 
    258             @Override
    259             public void onStartSuccess(AdvertiseSettings settingsInEffect) {
    260                 Log.d(TAG, "bluetooth_le_advertisement onSuccess ");
    261                 if (getMaxReached()) {
    262                     Log.d(TAG, "Stop Advertising");
    263                     mBleAdvertiser.stopAdvertising();
    264                 } else {
    265                     Log.d(TAG, "Start Advertising");
    266                     mBleAdvertiser.startAdvertising();
    267                 }
    268             }
    269 
    270             @Override
    271             public void onStartFailure(int errorCode) {
    272                 String errorString = "UNKNOWN_ERROR_CODE";
    273                 if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) {
    274                     errorString = "ADVERTISE_FAILED_ALREADY_STARTED";
    275                 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE) {
    276                     errorString = "ADVERTISE_FAILED_DATA_TOO_LARGE";
    277                 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
    278                     errorString = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED";
    279                 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR) {
    280                     errorString = "ADVERTISE_FAILED_INTERNAL_ERROR";
    281                 } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
    282                     errorString = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS";
    283                     setMaxReached(true);
    284                 }
    285                 Log.d(TAG, "bluetooth_le_advertisement onFailure: " + errorString);
    286             }
    287         }
    288     }
    289 }
    290