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 { BleEncryptedServerService()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 onCreate()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 onDestroy()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 onBind(Intent intent)147 public IBinder onBind(Intent intent) { 148 return null; 149 } 150 151 @Override onStartCommand(Intent intent, int flags, int startId)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 */ resetValues()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 notifyBluetoothDisabled()193 private void notifyBluetoothDisabled() { 194 Intent intent = new Intent(INTENT_BLUETOOTH_DISABLED); 195 sendBroadcast(intent); 196 } 197 notifyOpenFail()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 notifyAdvertiseUnsupported()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 notifyConnected()214 private void notifyConnected() { 215 if (DEBUG) { 216 Log.d(TAG, "notifyConnected"); 217 } 218 resetValues(); 219 } 220 notifyDisconnected()221 private void notifyDisconnected() { 222 if (DEBUG) { 223 Log.d(TAG, "notifyDisconnected"); 224 } 225 } 226 notifyServiceAdded()227 private void notifyServiceAdded() { 228 if (DEBUG) { 229 Log.d(TAG, "notifyServiceAdded"); 230 } 231 } 232 notifyCharacteristicWriteRequest()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 notifyCharacteristicReadRequest()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 notifyDescriptorWriteRequest()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 notifyDescriptorReadRequest()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 createService()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 startAdvertise()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 stopAdvertise()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