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 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothGatt; 24 import android.bluetooth.BluetoothGattCallback; 25 import android.bluetooth.BluetoothGattCharacteristic; 26 import android.bluetooth.BluetoothGattDescriptor; 27 import android.bluetooth.BluetoothGattService; 28 import android.bluetooth.BluetoothProfile; 29 import android.bluetooth.le.BluetoothLeScanner; 30 import android.bluetooth.le.ScanCallback; 31 import android.bluetooth.le.ScanFilter; 32 import android.bluetooth.le.ScanResult; 33 import android.bluetooth.le.ScanSettings; 34 import android.content.BroadcastReceiver; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.os.SystemClock; 38 import android.util.Log; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.UUID; 43 44 /** 45 * Class to provide Receiver for AlarmManager to start Gatt Client alarms 46 */ 47 public class GattClientListener extends BroadcastReceiver { 48 49 public static final String TAG = "GATTC"; 50 public static final String GATTCLIENT_ALARM = 51 "com.android.pmc.GATTClient.ALARM"; 52 private static final int MILLSEC = 1000; 53 private static final int INIT_VALUE = 0; 54 private Context mContext; 55 private final AlarmManager mAlarmManager; 56 57 private BluetoothAdapter mBluetoothAdapter; 58 59 private BluetoothGatt mBluetoothGatt; 60 private GattCallback mGattCallback; 61 62 private MyBleScanner mMyBleScanner; 63 private String mMacAddress; 64 private BluetoothDevice mDevice; 65 private int mWriteTime; 66 private int mIdleTime; 67 private int mCycles; 68 69 /** 70 * Constructor 71 * @param context - system will provide a context to this function 72 * @param alarmManager - system will provide a AlarmManager to this function 73 */ GattClientListener(Context context, AlarmManager alarmManager)74 public GattClientListener(Context context, AlarmManager alarmManager) { 75 Log.d(TAG, "Start GattClientListener()"); 76 mContext = context; 77 mAlarmManager = alarmManager; 78 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 79 80 if (mBluetoothAdapter == null) { 81 Log.e(TAG, "BluetoothAdapter is Null"); 82 return; 83 } else { 84 if (!mBluetoothAdapter.isEnabled()) { 85 Log.d(TAG, "BluetoothAdapter is NOT enabled, enable now"); 86 mBluetoothAdapter.enable(); 87 if (!mBluetoothAdapter.isEnabled()) { 88 Log.e(TAG, "Can't enable Bluetooth"); 89 return; 90 } 91 } 92 } 93 94 mMyBleScanner = new MyBleScanner(mBluetoothAdapter); 95 mGattCallback = new GattCallback(); 96 mBluetoothGatt = null; 97 mMacAddress = null; 98 mDevice = null; 99 Log.d(TAG, "End GattClientListener"); 100 } 101 102 /** 103 * Function to be called to start alarm by PMC 104 * 105 * @param startTime - time (sec) when next GATT writing needs to be started 106 * @param writeTime - how long (sec) to write GATT characteristic 107 * @param idleTime - how long (sec) it doesn't need to wait 108 * @param numCycles - how many of cycles of writing with idle time 109 */ startAlarm(int startTime, int writeTime, int idleTime, int numCycles, Intent intent)110 public void startAlarm(int startTime, int writeTime, int idleTime, int numCycles, 111 Intent intent) { 112 113 int currentAlarm = 0; 114 115 if (intent == null) { 116 // Start Scan here when this func is called for the first time 117 mMyBleScanner.startScan(); 118 mWriteTime = writeTime; 119 mIdleTime = idleTime; 120 mCycles = numCycles; 121 } else { 122 // Get alarm number inside the intent 123 currentAlarm = intent.getIntExtra("com.android.pmc.GATTClient.CurrentAlarm", 0); 124 } 125 Log.d(TAG, "Current Cycle Num: " + currentAlarm); 126 if (currentAlarm >= mCycles) { 127 Log.d(TAG, "All alarms are done"); 128 return; 129 } 130 131 Intent alarmIntent = new Intent(GattClientListener.GATTCLIENT_ALARM); 132 alarmIntent.putExtra("com.android.pmc.GATTClient.CurrentAlarm", ++currentAlarm); 133 134 long triggerTime = SystemClock.elapsedRealtime() + startTime * MILLSEC; 135 mAlarmManager.setExactAndAllowWhileIdle( 136 AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, 137 PendingIntent.getBroadcast(mContext, 0, 138 alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 139 } 140 141 /** 142 * Receive function will be called for AlarmManager to connect GATT 143 * and then to write characteristic 144 * 145 * @param context - system will provide a context to this function 146 * @param intent - system will provide an intent to this function 147 */ 148 @Override onReceive(Context context, Intent intent)149 public void onReceive(Context context, Intent intent) { 150 Log.d(TAG, "onReceiver: " + intent.getAction()); 151 if (!intent.getAction().equals(GATTCLIENT_ALARM)) { 152 return; 153 } 154 155 if (mMacAddress == null) mMacAddress = mMyBleScanner.getAdvMacAddress(); 156 if (mMacAddress == null || mMacAddress.isEmpty()) { 157 Log.e(TAG, "Remote device Mac Address is not set"); 158 return; 159 } 160 if (mDevice == null) mDevice = mBluetoothAdapter.getRemoteDevice(mMacAddress); 161 162 if (mBluetoothGatt == null) { 163 mBluetoothGatt = mDevice.connectGatt(mContext, 164 false, mGattCallback, BluetoothDevice.TRANSPORT_LE); 165 } else { 166 mBluetoothGatt.discoverServices(); 167 } 168 // Start next alarm to connect again then to write 169 startAlarm((mWriteTime + mIdleTime), mWriteTime, mIdleTime, mCycles, intent); 170 } 171 172 /** 173 * Callback for GATT Writing 174 */ 175 class GattCallback extends BluetoothGattCallback { 176 177 public static final int MAX_MTU = 511; 178 public static final int MAX_BYTES = 508; 179 private long mStartWriteTime; 180 GattCallback()181 GattCallback() {} 182 183 @Override onConnectionStateChange(BluetoothGatt gatt, int status, int newState)184 public void onConnectionStateChange(BluetoothGatt gatt, int status, 185 int newState) { 186 Log.d(TAG, "onConnectionStateChange " + status); 187 if (newState == BluetoothProfile.STATE_CONNECTED) { 188 Log.d(TAG, "State Connected to mac address " 189 + gatt.getDevice().getAddress() + " status " + status); 190 // Discover services in advertiser, callback will be called 191 mBluetoothGatt.discoverServices(); 192 193 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 194 Log.d(TAG, "State Disconnected from mac address " 195 + gatt.getDevice().getAddress() + " status " + status); 196 try { 197 mBluetoothGatt.close(); 198 } catch (Exception e) { 199 Log.e(TAG, "Close Gatt: " + e); 200 } 201 mBluetoothGatt = null; 202 203 } else if (newState == BluetoothProfile.STATE_CONNECTING) { 204 Log.d(TAG, "State Connecting to mac address " 205 + gatt.getDevice().getAddress() + " status " + status); 206 } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { 207 Log.d(TAG, "State Disconnecting from mac address " 208 + gatt.getDevice().getAddress() + " status " + status); 209 } 210 } 211 212 @Override onServicesDiscovered(BluetoothGatt gatt, int status)213 public void onServicesDiscovered(BluetoothGatt gatt, int status) { 214 Log.d(TAG, "onServicesDiscovered Status " + status); 215 mBluetoothGatt.requestMtu(MAX_MTU); 216 } 217 218 @Override onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)219 public void onCharacteristicRead(BluetoothGatt gatt, 220 BluetoothGattCharacteristic characteristic, int status) { 221 Log.d(TAG, "onCharacteristicRead: " + status); 222 } 223 224 @Override onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)225 public void onCharacteristicWrite(BluetoothGatt gatt, 226 BluetoothGattCharacteristic characteristic, int status) { 227 Log.d(TAG, "onCharacteristicWrite: " + status); 228 long timeElapse = SystemClock.elapsedRealtime() - mStartWriteTime; 229 if (timeElapse < (mWriteTime * MILLSEC)) { 230 writeCharacteristic(gatt, (int) (timeElapse / MILLSEC)); 231 } 232 } 233 234 @Override onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)235 public void onCharacteristicChanged(BluetoothGatt gatt, 236 BluetoothGattCharacteristic characteristic) { 237 Log.d(TAG, "onCharacteristicChanged"); 238 } 239 240 @Override onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)241 public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, 242 int status) { 243 Log.d(TAG, "onServicesDiscovered: " + status); 244 } 245 246 @Override onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)247 public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, 248 int status) { 249 Log.d(TAG, "onDescriptorWrite: " + status); 250 } 251 252 @Override onReliableWriteCompleted(BluetoothGatt gatt, int status)253 public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { 254 Log.d(TAG, "onReliableWriteCompleted: " + status); 255 } 256 257 @Override onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)258 public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { 259 Log.d(TAG, "onReadRemoteRssi: " + rssi + " status: " + status); 260 } 261 262 @Override onMtuChanged(BluetoothGatt gatt, int mtu, int status)263 public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { 264 // Every time it disconnects/reconnects it needs to re-set MTU 265 Log.d(TAG, "onMtuChanged " + mtu + " status: " + status); 266 // First time to write a characteristic to GATT server 267 mStartWriteTime = SystemClock.elapsedRealtime(); 268 writeCharacteristic(gatt, INIT_VALUE); 269 } 270 271 /** 272 * Function to be called to write a new GATT characteristic 273 * 274 * @param gatt - BluetoothGatt object to write 275 * @param value - value to be set inside GATT characteristic 276 */ writeCharacteristic(BluetoothGatt gatt, int value)277 private void writeCharacteristic(BluetoothGatt gatt, int value) { 278 UUID sUuid = UUID.fromString(GattServer.TEST_SERVICE_UUID); 279 BluetoothGattService service = gatt.getService(sUuid); 280 if (service == null) { 281 Log.e(TAG, "service not found!"); 282 return; 283 } 284 UUID cUuid = UUID.fromString(GattServer.WRITABLE_CHAR_UUID); 285 BluetoothGattCharacteristic characteristic = service.getCharacteristic(cUuid); 286 287 if (characteristic == null) { 288 Log.e(TAG, "Characteristic not found!"); 289 return; 290 } 291 292 byte[] byteValue = new byte[MAX_BYTES]; 293 294 for (int i = 0; i < MAX_BYTES; i++) { 295 byteValue[i] = (byte) value; 296 } 297 298 characteristic.setValue(byteValue); 299 characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); 300 gatt.writeCharacteristic(characteristic); 301 } 302 } 303 304 /** 305 * Class to provide Ble Scanner functionalities 306 */ 307 class MyBleScanner { 308 309 private BluetoothLeScanner mBLEScanner; 310 private ScanSettings mScanSettings; 311 private List<ScanFilter> mScanFilterList; 312 private MyScanCallback mScanCallback; 313 private String mAdvMacAddress = null; 314 315 /** 316 * Constructor 317 * @param context - system will provide a context to this function 318 */ MyBleScanner(BluetoothAdapter bluetoothAdapter)319 MyBleScanner(BluetoothAdapter bluetoothAdapter) { 320 321 mBLEScanner = bluetoothAdapter.getBluetoothLeScanner(); 322 mScanFilterList = new ArrayList<ScanFilter>(); 323 mScanSettings = new ScanSettings.Builder().setScanMode( 324 ScanSettings.SCAN_MODE_LOW_LATENCY).build(); 325 mScanCallback = new MyScanCallback(); 326 } 327 328 /** 329 * Wrapper function to start BLE Scanning 330 */ startScan()331 public void startScan() { 332 // Start Scan here when this func is called for the first time 333 if (mBLEScanner != null) { 334 mBLEScanner.startScan(mScanFilterList, mScanSettings, mScanCallback); 335 } else { 336 Log.e(TAG, "BLEScanner is null"); 337 } 338 339 } 340 341 /** 342 * Wrapper function to stop BLE Scanning 343 */ stopScan()344 public void stopScan() { 345 if (mBLEScanner != null) { 346 mBLEScanner.stopScan(mScanCallback); 347 } else { 348 Log.e(TAG, "BLEScanner is null"); 349 } 350 } 351 352 /** 353 * function to get Mac Address for BLE Advertiser device 354 */ getAdvMacAddress()355 public String getAdvMacAddress() { 356 // Return Mac address for Advertiser device 357 return mAdvMacAddress; 358 } 359 360 /** 361 * Class to provide callback for BLE Scanning 362 */ 363 class MyScanCallback extends ScanCallback { 364 365 @Override onScanResult(int callbackType, ScanResult result)366 public void onScanResult(int callbackType, ScanResult result) { 367 Log.d(TAG, "Bluetooth scan result: " + result.toString()); 368 BluetoothDevice device = result.getDevice(); 369 if (mAdvMacAddress == null) { 370 mAdvMacAddress = device.getAddress(); 371 Log.d(TAG, "Bluetooth Address: " + mAdvMacAddress); 372 } 373 mBLEScanner.stopScan(mScanCallback); 374 Log.d(TAG, "Bluetooth scan result: end "); 375 } 376 377 @Override onScanFailed(int errorCode)378 public void onScanFailed(int errorCode) { 379 Log.e(TAG, "Scan Failed: " + errorCode); 380 } 381 } 382 } 383 } 384 385