| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.pmc; |
| |
| |
| import android.app.Service; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.BluetoothDevice; |
| import android.bluetooth.BluetoothGatt; |
| import android.bluetooth.BluetoothGattCharacteristic; |
| import android.bluetooth.BluetoothGattDescriptor; |
| import android.bluetooth.BluetoothGattServer; |
| import android.bluetooth.BluetoothGattServerCallback; |
| import android.bluetooth.BluetoothGattService; |
| import android.bluetooth.BluetoothManager; |
| import android.bluetooth.BluetoothProfile; |
| import android.bluetooth.le.AdvertiseCallback; |
| import android.bluetooth.le.AdvertiseData; |
| import android.bluetooth.le.AdvertiseSettings; |
| import android.bluetooth.le.BluetoothLeAdvertiser; |
| import android.content.Context; |
| import android.util.Log; |
| |
| import java.util.UUID; |
| |
| /** |
| * Class to implement Gatt Server functionalities |
| */ |
| public class GattServer { |
| public static final String TAG = "GATTS"; |
| |
| private MyBleAdvertiser mBleAdvertiser; |
| private Context mContext; |
| private BluetoothManager mBluetoothManager; |
| private BluetoothGattServer mGattServer; |
| private MyGattServerCallback mGattServerCallBack; |
| private BluetoothGattService mGattService; |
| private static final String READABLE_DESC_UUID = "76d5ed92-ca81-4edb-bb6b-9f019665fb32"; |
| public static final String WRITABLE_CHAR_UUID = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"; |
| public static final String TEST_SERVICE_UUID = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B"; |
| |
| /** |
| * Constructor |
| * |
| * @param context - System will provide a context |
| */ |
| public GattServer(Context context) { |
| mContext = context; |
| // Check if Bluetooth is enabled |
| BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); |
| |
| if (bluetoothAdapter == null) { |
| Log.e(TAG, "BluetoothAdapter is Null"); |
| return; |
| } else { |
| if (!bluetoothAdapter.isEnabled()) { |
| Log.d(TAG, "BluetoothAdapter is NOT enabled, enable now"); |
| bluetoothAdapter.enable(); |
| if (!bluetoothAdapter.isEnabled()) { |
| Log.e(TAG, "Can't enable Bluetooth"); |
| return; |
| } |
| } |
| } |
| |
| // Prepare data for GATT service |
| mBluetoothManager = (BluetoothManager) context.getSystemService( |
| Service.BLUETOOTH_SERVICE); |
| |
| mGattServerCallBack = new MyGattServerCallback(); |
| |
| BluetoothGattCharacteristic characteristic = |
| new BluetoothGattCharacteristic(UUID.fromString(WRITABLE_CHAR_UUID), |
| BluetoothGattCharacteristic.PROPERTY_WRITE |
| | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, |
| BluetoothGattCharacteristic.PERMISSION_WRITE); |
| |
| BluetoothGattDescriptor descriptor = |
| new BluetoothGattDescriptor(UUID.fromString(READABLE_DESC_UUID), |
| BluetoothGattDescriptor.PERMISSION_READ |
| | BluetoothGattDescriptor.PERMISSION_WRITE); |
| |
| characteristic.addDescriptor(descriptor); |
| |
| mGattService = new BluetoothGattService(UUID.fromString(TEST_SERVICE_UUID), |
| BluetoothGattService.SERVICE_TYPE_PRIMARY); |
| mGattService.addCharacteristic(characteristic); |
| |
| // Create BLE Advertiser object |
| mBleAdvertiser = new MyBleAdvertiser(bluetoothAdapter); |
| Log.d(TAG, "Construstor finished"); |
| } |
| |
| /** |
| * Function to be called to start Gatt Server |
| */ |
| public void startGattServer() { |
| // Connect to Gatt Server |
| mGattServer = mBluetoothManager.openGattServer(mContext, mGattServerCallBack); |
| // Add GATT Service to Gatt Server |
| mGattServer.addService(mGattService); |
| // Start BLE Advertising here |
| mBleAdvertiser.startAdvertising(); |
| Log.d(TAG, "startGattServer finished"); |
| } |
| |
| /** |
| * Class to provide callback for GATT server to handle GATT requests |
| */ |
| class MyGattServerCallback extends BluetoothGattServerCallback { |
| |
| MyGattServerCallback() {} |
| |
| @Override |
| public void onServiceAdded(int status, BluetoothGattService service) { |
| Log.d(TAG, "onServiceAdded: " + status); |
| } |
| |
| @Override |
| public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, |
| BluetoothGattCharacteristic characteristic) { |
| Log.d(TAG, "onCharacteristicReadRequest: " + characteristic); |
| } |
| |
| @Override |
| public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, |
| BluetoothGattCharacteristic characteristic, boolean preparedWrite, |
| boolean responseNeeded, int offset, byte[] value) { |
| Log.d(TAG, "onCharacteristicWriteRequest requestId: " + requestId |
| + " preparedWrite: " + preparedWrite + " sendRespons back"); |
| |
| mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); |
| } |
| |
| @Override |
| public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, |
| BluetoothGattDescriptor descriptor) { |
| Log.d(TAG, "onDescriptorReadRequest requestId: " + requestId); |
| } |
| |
| @Override |
| public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, |
| BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, |
| int offset, byte[] value) { |
| Log.d(TAG, "onDescriptorWriteRequest requestId: " + requestId + " preparedWrite: " |
| + preparedWrite); |
| } |
| |
| @Override |
| public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { |
| Log.d(TAG, "onExecuteWrite requestId: " + requestId + " execute: " + execute); |
| mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null); |
| Log.d(TAG, "onExecuteWrite sendResponse back to GATT Client"); |
| } |
| |
| @Override |
| public void onNotificationSent(BluetoothDevice device, int status) { |
| Log.d(TAG, "onNotificationSent " + status); |
| } |
| |
| @Override |
| public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { |
| Log.d(TAG, "onConnectionStateChange status: " + status + " new state: " + newState); |
| if (newState == BluetoothProfile.STATE_CONNECTED) { |
| Log.d(TAG, "Connected to mac address " + device.getAddress() + " status " + status); |
| |
| } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { |
| Log.d(TAG, "Disconnected from mac address " + device.getAddress() + " status " |
| + status); |
| } |
| } |
| |
| @Override |
| public void onMtuChanged(BluetoothDevice device, int mtu) { |
| Log.d(TAG, "onMtuChanged: " + mtu); |
| } |
| } |
| |
| /** |
| * Class to provide BLE Advertising functionalities |
| */ |
| class MyBleAdvertiser { |
| |
| private BluetoothLeAdvertiser mAdvertiser; |
| private AdvertiseSettings mAdvertiseSettings; |
| private AdvertiseData mAdvertiseData; |
| private MyAdvertiseCallback mAdvertiseCallback; |
| |
| /** |
| * Constructor |
| * @param bluetoothAdapter - Default BluetoothAdapter |
| */ |
| MyBleAdvertiser(BluetoothAdapter bluetoothAdapter) { |
| // Prepare for BLE Advertisement |
| mAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); |
| mAdvertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true).build(); |
| mAdvertiseCallback = new MyAdvertiseCallback(); |
| mAdvertiseSettings = new AdvertiseSettings.Builder() |
| .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) |
| .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) |
| .setConnectable(true) |
| .setTimeout(0).build(); |
| } |
| |
| /** |
| * Wrapper function to start BLE Advertising |
| */ |
| public void startAdvertising() { |
| mAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData, |
| mAdvertiseCallback); |
| } |
| |
| /** |
| * Wrapper function to stop BLE Advertising |
| */ |
| public void stopAdvertising() { |
| mAdvertiser.stopAdvertising(mAdvertiseCallback); |
| } |
| |
| /** |
| * Class to provide callback to handle BLE Advertisement |
| */ |
| class MyAdvertiseCallback extends AdvertiseCallback { |
| private boolean mMaxReached; |
| // The lock object is used to synchronize mMaxReached |
| private final Object mLock = new Object(); |
| |
| MyAdvertiseCallback() { |
| mMaxReached = false; |
| } |
| |
| public void setMaxReached(boolean setMax) { |
| synchronized (mLock) { |
| mMaxReached = setMax; |
| } |
| } |
| public boolean getMaxReached() { |
| synchronized (mLock) { |
| return mMaxReached; |
| } |
| } |
| |
| @Override |
| public void onStartSuccess(AdvertiseSettings settingsInEffect) { |
| Log.d(TAG, "bluetooth_le_advertisement onSuccess "); |
| if (getMaxReached()) { |
| Log.d(TAG, "Stop Advertising"); |
| mBleAdvertiser.stopAdvertising(); |
| } else { |
| Log.d(TAG, "Start Advertising"); |
| mBleAdvertiser.startAdvertising(); |
| } |
| } |
| |
| @Override |
| public void onStartFailure(int errorCode) { |
| String errorString = "UNKNOWN_ERROR_CODE"; |
| if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) { |
| errorString = "ADVERTISE_FAILED_ALREADY_STARTED"; |
| } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE) { |
| errorString = "ADVERTISE_FAILED_DATA_TOO_LARGE"; |
| } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED) { |
| errorString = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED"; |
| } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR) { |
| errorString = "ADVERTISE_FAILED_INTERNAL_ERROR"; |
| } else if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { |
| errorString = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"; |
| setMaxReached(true); |
| } |
| Log.d(TAG, "bluetooth_le_advertisement onFailure: " + errorString); |
| } |
| } |
| } |
| } |