Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright 2018 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.BluetoothAdapter;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.bluetooth.BluetoothGatt;
     23 import android.bluetooth.BluetoothGattCharacteristic;
     24 import android.bluetooth.BluetoothGattDescriptor;
     25 import android.bluetooth.BluetoothGattServer;
     26 import android.bluetooth.BluetoothGattServerCallback;
     27 import android.bluetooth.BluetoothGattService;
     28 import android.bluetooth.BluetoothManager;
     29 import android.bluetooth.BluetoothProfile;
     30 import android.bluetooth.BluetoothServerSocket;
     31 import android.bluetooth.BluetoothSocket;
     32 import android.bluetooth.le.AdvertiseCallback;
     33 import android.bluetooth.le.AdvertiseData;
     34 import android.bluetooth.le.AdvertiseSettings;
     35 import android.bluetooth.le.BluetoothLeAdvertiser;
     36 import android.content.Context;
     37 import android.content.Intent;
     38 import android.os.Build;
     39 import android.os.Handler;
     40 import android.os.IBinder;
     41 import android.os.Message;
     42 import android.os.ParcelUuid;
     43 import android.util.Log;
     44 import android.widget.Toast;
     45 
     46 import com.android.cts.verifier.R;
     47 
     48 import java.io.IOException;
     49 
     50 import java.util.ArrayList;
     51 import java.util.Arrays;
     52 import java.util.HashMap;
     53 import java.util.LinkedHashMap;
     54 import java.util.List;
     55 import java.util.Set;
     56 import java.util.Timer;
     57 import java.util.UUID;
     58 
     59 public class BleCocServerService extends Service {
     60 
     61     public static final boolean DEBUG = true;
     62     public static final String TAG = "BleCocServerService";
     63 
     64     public static final int COMMAND_ADD_SERVICE = 0;
     65     public static final int COMMAND_WRITE_CHARACTERISTIC = 1;
     66     public static final int COMMAND_WRITE_DESCRIPTOR = 2;
     67 
     68     public static final int TEST_DATA_EXCHANGE_BUFSIZE = 8 * 1024;
     69 
     70     public static final String BLE_BLUETOOTH_MISMATCH_SECURE =
     71             "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE";
     72     public static final String BLE_BLUETOOTH_MISMATCH_INSECURE =
     73             "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE";
     74     public static final String BLE_BLUETOOTH_DISABLED =
     75             "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED";
     76 
     77     public static final String BLE_ACTION_COC_SERVER_INSECURE =
     78             "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_INSECURE";
     79     public static final String BLE_ACTION_COC_SERVER_SECURE =
     80             "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_SECURE";
     81 
     82     public static final String BLE_ACTION_SERVER_SECURE =
     83             "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_SECURE";
     84     public static final String BLE_ACTION_SERVER_NON_SECURE =
     85             "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_NON_SECURE";
     86 
     87     public static final String BLE_LE_CONNECTED =
     88             "com.android.cts.verifier.bluetooth.BLE_LE_CONNECTED";
     89     public static final String BLE_COC_LISTENER_CREATED =
     90             "com.android.cts.verifier.bluetooth.BLE_COC_LISTENER_CREATED";
     91     public static final String BLE_PSM_READ =
     92             "com.android.cts.verifier.bluetooth.BLE_PSM_READ";
     93     public static final String BLE_COC_CONNECTED =
     94             "com.android.cts.verifier.bluetooth.BLE_COC_CONNECTED";
     95     public static final String BLE_CONNECTION_TYPE_CHECKED =
     96             "com.android.cts.verifier.bluetooth.BLE_CONNECTION_TYPE_CHECKED";
     97     public static final String BLE_DATA_8BYTES_READ =
     98             "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_READ";
     99     public static final String BLE_DATA_LARGEBUF_READ =
    100             "com.android.cts.verifier.bluetooth.BLE_DATA_LARGEBUF_READ";
    101     public static final String BLE_DATA_8BYTES_SENT =
    102             "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_SENT";
    103     public static final String BLE_LE_DISCONNECTED =
    104             "com.android.cts.verifier.bluetooth.BLE_LE_DISCONNECTED";
    105     public static final String BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES =
    106             "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES";
    107     public static final String BLE_COC_SERVER_ACTION_EXCHANGE_DATA =
    108             "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_EXCHANGE_DATA";
    109     public static final String BLE_COC_SERVER_ACTION_DISCONNECT =
    110             "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_DISCONNECT";
    111 
    112     public static final String BLE_SERVER_DISCONNECTED =
    113             "com.android.cts.verifier.bluetooth.BLE_SERVER_DISCONNECTED";
    114     public static final String BLE_OPEN_FAIL =
    115             "com.android.cts.verifier.bluetooth.BLE_OPEN_FAIL";
    116     public static final String BLE_ADVERTISE_UNSUPPORTED =
    117             "com.android.cts.verifier.bluetooth.BLE_ADVERTISE_UNSUPPORTED";
    118     public static final String BLE_ADD_SERVICE_FAIL =
    119             "com.android.cts.verifier.bluetooth.BLE_ADD_SERVICE_FAIL";
    120 
    121     private static final UUID SERVICE_UUID =
    122             UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
    123     private static final UUID CHARACTERISTIC_UUID =
    124             UUID.fromString("00009998-0000-1000-8000-00805f9b34fb");
    125     private static final UUID CHARACTERISTIC_RESULT_UUID =
    126             UUID.fromString("00009974-0000-1000-8000-00805f9b34fb");
    127     private static final UUID UPDATE_CHARACTERISTIC_UUID =
    128             UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
    129     private static final UUID DESCRIPTOR_UUID =
    130             UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
    131     public static final UUID ADV_COC_SERVICE_UUID=
    132             UUID.fromString("00003334-0000-1000-8000-00805f9b34fb");
    133 
    134     private static final UUID SERVICE_UUID_ADDITIONAL =
    135             UUID.fromString("00009995-0000-1000-8000-00805f9b34fb");
    136     private static final UUID SERVICE_UUID_INCLUDED =
    137             UUID.fromString("00009994-0000-1000-8000-00805f9b34fb");
    138 
    139     // Variable for registration permission of Descriptor
    140     private static final UUID DESCRIPTOR_NO_READ_UUID =
    141             UUID.fromString("00009973-0000-1000-8000-00805f9b34fb");
    142     private static final UUID DESCRIPTOR_NO_WRITE_UUID =
    143             UUID.fromString("00009972-0000-1000-8000-00805f9b34fb");
    144     private static final UUID DESCRIPTOR_NEED_ENCRYPTED_READ_UUID =
    145             UUID.fromString("00009969-0000-1000-8000-00805f9b34fb");
    146     private static final UUID DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID =
    147             UUID.fromString("00009968-0000-1000-8000-00805f9b34fb");
    148 
    149     private static final int CONN_INTERVAL = 150;   // connection interval 150ms
    150 
    151     private static final int EXECUTION_DELAY = 1500;
    152 
    153     // Delay of notification when secure test failed to start.
    154     private static final long NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE = 5 * 1000;
    155 
    156     public static final String WRITE_VALUE = "SERVER_TEST";
    157     private static final String NOTIFY_VALUE = "NOTIFY_TEST";
    158     private static final String INDICATE_VALUE = "INDICATE_TEST";
    159     public static final String READ_NO_PERMISSION = "READ_NO_CHAR";
    160     public static final String WRITE_NO_PERMISSION = "WRITE_NO_CHAR";
    161     public static final String DESCRIPTOR_READ_NO_PERMISSION = "READ_NO_DESC";
    162     public static final String DESCRIPTOR_WRITE_NO_PERMISSION = "WRITE_NO_DESC";
    163 
    164     private BluetoothManager mBluetoothManager;
    165     private BluetoothGattServer mGattServer;
    166     private BluetoothGattService mService;
    167     private BluetoothDevice mDevice;
    168     private Handler mHandler;
    169     private BluetoothLeAdvertiser mAdvertiser;
    170     private boolean mSecure;
    171     private int mMtuSize = -1;
    172 
    173     private BluetoothServerSocket mServerSocket;
    174     private int mPsm = -1;
    175     private BluetoothGattCharacteristic mLePsmCharacteristic;
    176     BluetoothChatService mChatService;
    177 
    178     private int mNextReadExpectedLen = -1;
    179     private String mNextReadCompletionIntent;
    180     private int mTotalReadLen = 0;
    181     private byte mNextReadByte;
    182     private int mNextWriteExpectedLen = -1;
    183     private String mNextWriteCompletionIntent = null;
    184 
    185     // Handler for communicating task with peer.
    186     private TestTaskQueue mTaskQueue;
    187 
    188     // current test category
    189     private String mCurrentAction;
    190 
    191     // Task to notify failure of starting secure test.
    192     //   Secure test calls BluetoothDevice#createBond() when devices were not paired.
    193     //   createBond() causes onConnectionStateChange() twice, and it works as strange sequence.
    194     //   At the first onConnectionStateChange(), target device is not paired (bond state is
    195     //   BluetoothDevice.BOND_NONE).
    196     //   At the second onConnectionStateChange(), target devices is paired (bond state is
    197     //   BluetoothDevice.BOND_BONDED).
    198     //   CTS Verifier will perform lazy check of bond state. Verifier checks bond state
    199     //   after NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE from the first onConnectionStateChange().
    200     private Runnable mNotificationTaskOfSecureTestStartFailure;
    201 
    202     @Override
    203     public void onCreate() {
    204         super.onCreate();
    205 
    206         mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread");
    207 
    208         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    209         mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
    210         mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
    211 
    212         mService = createService();
    213 
    214         mDevice = null;
    215 
    216         mHandler = new Handler();
    217         if (!mBluetoothManager.getAdapter().isEnabled()) {
    218             notifyBluetoothDisabled();
    219         } else if (mGattServer == null) {
    220             notifyOpenFail();
    221         } else if (mAdvertiser == null) {
    222             notifyAdvertiseUnsupported();
    223         } else {
    224             // start adding services
    225             mSecure = false;
    226             if (!mGattServer.addService(mService)) {
    227                 notifyAddServiceFail();
    228             }
    229         }
    230     }
    231 
    232     private void notifyBluetoothDisabled() {
    233         Intent intent = new Intent(BLE_BLUETOOTH_DISABLED);
    234         sendBroadcast(intent);
    235     }
    236 
    237     private void notifyMismatchSecure() {
    238         Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE);
    239         sendBroadcast(intent);
    240     }
    241 
    242     @Override
    243     public int onStartCommand(Intent intent, int flags, int startId) {
    244         String action = intent.getAction();
    245         if (action != null) {
    246             if (DEBUG) {
    247                 Log.d(TAG, "onStartCommand: action=" + action);
    248             }
    249             mTaskQueue.addTask(new Runnable() {
    250                 @Override
    251                 public void run() {
    252                     onTestFinish(intent.getAction());
    253                 }
    254             }, EXECUTION_DELAY);
    255         }
    256         return START_NOT_STICKY;
    257     }
    258 
    259     private void startServerTest(boolean secure) {
    260         mSecure = secure;
    261 
    262         if (mBluetoothManager.getAdapter().isEnabled() && (mChatService == null)) {
    263             createChatService();
    264         }
    265 
    266         if (mBluetoothManager.getAdapter().isEnabled() && (mAdvertiser != null)) {
    267             startAdvertise();
    268         }
    269     }
    270 
    271     private void sendMessage(byte[] buf) {
    272         mChatService.write(buf);
    273     }
    274 
    275     private void sendData8bytes() {
    276         if (DEBUG) Log.d(TAG, "sendData8bytes");
    277 
    278         final byte[] buf = new byte[]{1,2,3,4,5,6,7,8};
    279         mNextWriteExpectedLen = 8;
    280         mNextWriteCompletionIntent = BLE_DATA_8BYTES_SENT;
    281         sendMessage(buf);
    282     }
    283 
    284     private void sendDataLargeBuf() {
    285         final int len = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
    286         if (DEBUG) Log.d(TAG, "sendDataLargeBuf of size=" + len);
    287 
    288         byte[] buf = new byte[len];
    289         for (int i = 0; i < len; i++) {
    290             buf[i] = (byte)(i + 1);
    291         }
    292         mNextWriteExpectedLen = len;
    293         mNextWriteCompletionIntent = null;
    294         sendMessage(buf);
    295     }
    296 
    297     private void onTestFinish(String action) {
    298         mCurrentAction = action;
    299         if (mCurrentAction != null) {
    300             switch (mCurrentAction) {
    301                 case BLE_ACTION_COC_SERVER_INSECURE:
    302                     startServerTest(false);
    303                     break;
    304                 case BLE_ACTION_COC_SERVER_SECURE:
    305                     startServerTest(true);
    306                     break;
    307                 case BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES:
    308                     sendData8bytes();
    309                     break;
    310                 case BLE_COC_SERVER_ACTION_EXCHANGE_DATA:
    311                     sendDataLargeBuf();
    312                     readDataLargeBuf();
    313                     break;
    314                 case BLE_COC_SERVER_ACTION_DISCONNECT:
    315                     if (mChatService != null) {
    316                         mChatService.stop();
    317                     }
    318                     break;
    319                 default:
    320                     Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction);
    321             }
    322         }
    323     }
    324 
    325     @Override
    326     public IBinder onBind(Intent intent) {
    327         return null;
    328     }
    329 
    330     @Override
    331     public void onDestroy() {
    332         super.onDestroy();
    333 
    334         if (mChatService != null) {
    335             mChatService.stop();
    336         }
    337 
    338         cancelNotificationTaskOfSecureTestStartFailure();
    339         stopAdvertise();
    340 
    341         mTaskQueue.quit();
    342 
    343         if (mGattServer == null) {
    344            return;
    345         }
    346         if (mDevice != null) {
    347             mGattServer.cancelConnection(mDevice);
    348         }
    349         mGattServer.clearServices();
    350         mGattServer.close();
    351     }
    352 
    353     private void notifyOpenFail() {
    354         if (DEBUG) {
    355             Log.d(TAG, "notifyOpenFail");
    356         }
    357         Intent intent = new Intent(BLE_OPEN_FAIL);
    358         sendBroadcast(intent);
    359     }
    360 
    361     private void notifyAddServiceFail() {
    362         if (DEBUG) {
    363             Log.d(TAG, "notifyAddServiceFail");
    364         }
    365         Intent intent = new Intent(BLE_ADD_SERVICE_FAIL);
    366         sendBroadcast(intent);
    367     }
    368 
    369     private void notifyAdvertiseUnsupported() {
    370         if (DEBUG) {
    371             Log.d(TAG, "notifyAdvertiseUnsupported");
    372         }
    373         Intent intent = new Intent(BLE_ADVERTISE_UNSUPPORTED);
    374         sendBroadcast(intent);
    375     }
    376 
    377     private void notifyConnected() {
    378         if (DEBUG) {
    379             Log.d(TAG, "notifyConnected");
    380         }
    381         Intent intent = new Intent(BLE_LE_CONNECTED);
    382         sendBroadcast(intent);
    383     }
    384 
    385     private void notifyDisconnected() {
    386         if (DEBUG) {
    387             Log.d(TAG, "notifyDisconnected");
    388         }
    389         Intent intent = new Intent(BLE_SERVER_DISCONNECTED);
    390         sendBroadcast(intent);
    391     }
    392 
    393     private BluetoothGattService createService() {
    394         BluetoothGattService service =
    395                 new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
    396         BluetoothGattCharacteristic characteristic =
    397                 new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
    398         characteristic.setValue(WRITE_VALUE.getBytes());
    399 
    400         BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
    401         descriptor.setValue(WRITE_VALUE.getBytes());
    402         characteristic.addDescriptor(descriptor);
    403 
    404         BluetoothGattDescriptor descriptor_permission =
    405             new BluetoothGattDescriptor(DESCRIPTOR_NO_READ_UUID, 0x10);
    406         characteristic.addDescriptor(descriptor_permission);
    407 
    408         descriptor_permission = new BluetoothGattDescriptor(DESCRIPTOR_NO_WRITE_UUID, 0x01);
    409         characteristic.addDescriptor(descriptor_permission);
    410 
    411         service.addCharacteristic(characteristic);
    412 
    413         // Registered the characteristic of PSM Value
    414         mLePsmCharacteristic =
    415                 new BluetoothGattCharacteristic(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID,
    416                                                 BluetoothGattCharacteristic.PROPERTY_READ,
    417                                                 BluetoothGattCharacteristic.PERMISSION_READ);
    418         service.addCharacteristic(mLePsmCharacteristic);
    419 
    420         return service;
    421     }
    422 
    423     private void showMessage(final String msg) {
    424         mHandler.post(new Runnable() {
    425             public void run() {
    426                 Toast.makeText(BleCocServerService.this, msg, Toast.LENGTH_SHORT).show();
    427             }
    428         });
    429     }
    430 
    431     private synchronized void cancelNotificationTaskOfSecureTestStartFailure() {
    432         if (mNotificationTaskOfSecureTestStartFailure != null) {
    433             mHandler.removeCallbacks(mNotificationTaskOfSecureTestStartFailure);
    434             mNotificationTaskOfSecureTestStartFailure = null;
    435         }
    436     }
    437 
    438     private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
    439         @Override
    440         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
    441             if (DEBUG) {
    442                 Log.d(TAG, "onConnectionStateChange: newState=" + newState);
    443             }
    444 
    445             if (status == BluetoothGatt.GATT_SUCCESS) {
    446                 if (newState == BluetoothProfile.STATE_CONNECTED) {
    447                     mDevice = device;
    448                     boolean bonded = false;
    449                     Set<BluetoothDevice> pairedDevices =
    450                         mBluetoothManager.getAdapter().getBondedDevices();
    451                     if (pairedDevices.size() > 0) {
    452                         for (BluetoothDevice target : pairedDevices) {
    453                             if (target.getAddress().equals(device.getAddress())) {
    454                                 bonded = true;
    455                                 break;
    456                             }
    457                         }
    458                     }
    459 
    460                     if (mSecure && ((device.getBondState() == BluetoothDevice.BOND_NONE) ||
    461                                     !bonded)) {
    462                         // not pairing and execute Secure Test
    463                         Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: "
    464                               + "Not paired but execute secure test");
    465                         cancelNotificationTaskOfSecureTestStartFailure();
    466                     } else if (!mSecure && ((device.getBondState() != BluetoothDevice.BOND_NONE)
    467                                             || bonded)) {
    468                         // already pairing and execute Insecure Test
    469                         Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: "
    470                               + "Paired but execute insecure test");
    471                     } else {
    472                         cancelNotificationTaskOfSecureTestStartFailure();
    473                     }
    474                     notifyConnected();
    475                 } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
    476                     notifyDisconnected();
    477                     mDevice = null;
    478                 }
    479             }
    480         }
    481 
    482         @Override
    483         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
    484                 BluetoothGattCharacteristic characteristic) {
    485             if (mGattServer == null) {
    486                 if (DEBUG) {
    487                     Log.d(TAG, "GattServer is null, return");
    488                 }
    489                 return;
    490             }
    491             if (DEBUG) {
    492                 Log.d(TAG, "onCharacteristicReadRequest()");
    493             }
    494 
    495             boolean finished = false;
    496             byte[] value = null;
    497             if (mMtuSize > 0) {
    498                 byte[] buf = characteristic.getValue();
    499                 if (buf != null) {
    500                     int len = Math.min((buf.length - offset), mMtuSize);
    501                     if (len > 0) {
    502                         value = Arrays.copyOfRange(buf, offset, (offset + len));
    503                     }
    504                     finished = ((offset + len) >= buf.length);
    505                     if (finished) {
    506                         Log.d(TAG, "sent whole data: " + (new String(characteristic.getValue())));
    507                     }
    508                 }
    509             } else {
    510                 value = characteristic.getValue();
    511                 finished = true;
    512             }
    513 
    514             mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
    515 
    516             UUID uid = characteristic.getUuid();
    517             if (uid.equals(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID)) {
    518                 Log.d(TAG, "onCharacteristicReadRequest: reading PSM");
    519             }
    520         }
    521 
    522     };
    523 
    524     private void leCheckConnectionType() {
    525         if (mChatService == null) {
    526             Log.e(TAG, "leCheckConnectionType: no LE Coc connection");
    527             return;
    528         }
    529         int type = mChatService.getSocketConnectionType();
    530         if (type != BluetoothSocket.TYPE_L2CAP) {
    531             Log.e(TAG, "leCheckConnectionType: invalid connection type=" + type);
    532             return;
    533         }
    534         showMessage("LE CoC Connection Type Checked");
    535         Intent intent = new Intent(BLE_CONNECTION_TYPE_CHECKED);
    536         sendBroadcast(intent);
    537     }
    538 
    539     private void readData8bytes() {
    540         mNextReadExpectedLen = 8;
    541         mTotalReadLen = 0;
    542         mNextReadCompletionIntent = BLE_DATA_8BYTES_READ;
    543         mNextReadByte = 1;
    544     }
    545 
    546     private void readDataLargeBuf() {
    547         mNextReadExpectedLen = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE;
    548         mTotalReadLen = 0;
    549         mNextReadCompletionIntent = BLE_DATA_LARGEBUF_READ;
    550         mNextReadByte = 1;
    551     }
    552 
    553     private void processChatStateChange(int newState) {
    554         Intent intent;
    555         if (DEBUG) {
    556             Log.d(TAG, "processChatStateChange: newState=" + newState);
    557         }
    558         switch (newState) {
    559         case BluetoothChatService.STATE_LISTEN:
    560             intent = new Intent(BLE_COC_LISTENER_CREATED);
    561             sendBroadcast(intent);
    562             break;
    563         case BluetoothChatService.STATE_CONNECTED:
    564             intent = new Intent(BLE_COC_CONNECTED);
    565             sendBroadcast(intent);
    566 
    567             // Check the connection type
    568             leCheckConnectionType();
    569 
    570             // Prepare the next data read
    571             readData8bytes();
    572             break;
    573         }
    574     }
    575 
    576     private boolean checkReadBufContent(byte[] buf, int len) {
    577         // Check that the content is correct
    578         for (int i = 0; i < len; i++) {
    579             if (buf[i] != mNextReadByte) {
    580                 Log.e(TAG, "handleMessageRead: Error: wrong byte content. buf["
    581                       + i + "]=" + buf[i] + " not equal to " + mNextReadByte);
    582                 return false;
    583             }
    584             mNextReadByte++;
    585         }
    586         return true;
    587     }
    588 
    589     private void handleMessageRead(Message msg) {
    590         byte[] buf = (byte[])msg.obj;
    591         int len = msg.arg1;
    592         if (len <= 0) {
    593             return;
    594         }
    595         mTotalReadLen += len;
    596         if (DEBUG) {
    597             Log.d(TAG, "handleMessageRead: receive buffer of length=" + len + ", mTotalReadLen="
    598                   + mTotalReadLen + ", mNextReadExpectedLen=" + mNextReadExpectedLen);
    599         }
    600 
    601         if (mNextReadExpectedLen == mTotalReadLen) {
    602             if (!checkReadBufContent(buf, len)) {
    603                 mNextReadExpectedLen = -1;
    604                 return;
    605             }
    606             showMessage("Read " + len + " bytes");
    607             if (DEBUG) {
    608                 Log.d(TAG, "handleMessageRead: broadcast intent " + mNextReadCompletionIntent);
    609             }
    610             Intent intent = new Intent(mNextReadCompletionIntent);
    611             sendBroadcast(intent);
    612             mNextReadExpectedLen = -1;
    613             mNextReadCompletionIntent = null;
    614             mTotalReadLen = 0;
    615         } else if (mNextReadExpectedLen > mTotalReadLen) {
    616             if (!checkReadBufContent(buf, len)) {
    617                 mNextReadExpectedLen = -1;
    618                 return;
    619             }
    620         } else if (mNextReadExpectedLen < mTotalReadLen) {
    621             Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len
    622                   + ", expected len=" + mNextReadExpectedLen);
    623         }
    624     }
    625 
    626     private void handleMessageWrite(Message msg) {
    627         byte[] buffer = (byte[]) msg.obj;
    628         int len = buffer.length;
    629         showMessage("LE CoC Server wrote " + len + " bytes" + ", mNextWriteExpectedLen="
    630                     + mNextWriteExpectedLen);
    631         if (len == mNextWriteExpectedLen) {
    632             if (mNextWriteCompletionIntent != null) {
    633                 Intent intent = new Intent(mNextWriteCompletionIntent);
    634                 sendBroadcast(intent);
    635             }
    636         } else {
    637             Log.d(TAG, "handleMessageWrite: unrecognized length=" + len);
    638         }
    639         mNextWriteCompletionIntent = null;
    640         mNextWriteExpectedLen = -1;
    641     }
    642 
    643     private class ChatHandler extends Handler {
    644         @Override
    645         public void handleMessage(Message msg) {
    646             super.handleMessage(msg);
    647             if (DEBUG) {
    648                 Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg);
    649             }
    650             switch (msg.what) {
    651                 case BluetoothChatService.MESSAGE_STATE_CHANGE:
    652                     processChatStateChange(msg.arg1);
    653                     break;
    654                 case BluetoothChatService.MESSAGE_READ:
    655                     handleMessageRead(msg);
    656                     break;
    657                 case BluetoothChatService.MESSAGE_WRITE:
    658                     handleMessageWrite(msg);
    659                     break;
    660             }
    661         }
    662     }
    663 
    664     /* Start the Chat Service to create the Bluetooth Server Socket for LE CoC */
    665     private void createChatService() {
    666 
    667         mChatService = new BluetoothChatService(this, new ChatHandler(), true);
    668         mChatService.start(mSecure);
    669         mPsm = mChatService.getPsm(mSecure);
    670         if (DEBUG) {
    671             Log.d(TAG, "createChatService: assigned PSM=" + mPsm);
    672         }
    673         if (mPsm > 0x00ff) {
    674             Log.e(TAG, "createChatService: Invalid PSM=" + mPsm);
    675         }
    676         // Notify that the PSM is read
    677         Intent intent = new Intent(BLE_PSM_READ);
    678         sendBroadcast(intent);
    679 
    680         // Set the PSM value in the PSM characteristics in the GATT Server.
    681         mLePsmCharacteristic.setValue(mPsm, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
    682     }
    683 
    684     private void startAdvertise() {
    685         if (DEBUG) {
    686             Log.d(TAG, "startAdvertise");
    687         }
    688         AdvertiseData data = new AdvertiseData.Builder()
    689             .addServiceData(new ParcelUuid(ADV_COC_SERVICE_UUID), new byte[]{1,2,3})
    690             .addServiceUuid(new ParcelUuid(ADV_COC_SERVICE_UUID))
    691             .build();
    692         AdvertiseSettings setting = new AdvertiseSettings.Builder()
    693             .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
    694             .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
    695             .setConnectable(true)
    696             .build();
    697         mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
    698     }
    699 
    700     private void stopAdvertise() {
    701         if (DEBUG) {
    702             Log.d(TAG, "stopAdvertise");
    703         }
    704         if (mAdvertiser != null) {
    705             mAdvertiser.stopAdvertising(mAdvertiseCallback);
    706         }
    707     }
    708 
    709     private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){
    710         @Override
    711         public void onStartFailure(int errorCode) {
    712             // Implementation for API Test.
    713             super.onStartFailure(errorCode);
    714             if (DEBUG) {
    715                 Log.d(TAG, "onStartFailure");
    716             }
    717 
    718             if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
    719                 notifyAdvertiseUnsupported();
    720             } else {
    721                 notifyOpenFail();
    722             }
    723         }
    724 
    725         @Override
    726         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
    727             // Implementation for API Test.
    728             super.onStartSuccess(settingsInEffect);
    729             if (DEBUG) {
    730                 Log.d(TAG, "onStartSuccess");
    731             }
    732         }
    733     };
    734 
    735     /*protected*/ static void dumpService(BluetoothGattService service, int level) {
    736         String indent = "";
    737         for (int i = 0; i < level; ++i) {
    738             indent += "  ";
    739         }
    740 
    741         Log.d(TAG, indent + "[service]");
    742         Log.d(TAG, indent + "UUID: " + service.getUuid());
    743         Log.d(TAG, indent + "  [characteristics]");
    744         for (BluetoothGattCharacteristic ch : service.getCharacteristics()) {
    745             Log.d(TAG, indent + "    UUID: " + ch.getUuid());
    746             Log.d(TAG, indent + "      properties: "
    747                   + String.format("0x%02X", ch.getProperties()));
    748             Log.d(TAG, indent + "      permissions: "
    749                   + String.format("0x%02X", ch.getPermissions()));
    750             Log.d(TAG, indent + "      [descriptors]");
    751             for (BluetoothGattDescriptor d : ch.getDescriptors()) {
    752                 Log.d(TAG, indent + "        UUID: " + d.getUuid());
    753                 Log.d(TAG, indent + "          permissions: "
    754                       + String.format("0x%02X", d.getPermissions()));
    755             }
    756         }
    757 
    758         if (service.getIncludedServices() != null) {
    759             Log.d(TAG, indent + "  [included services]");
    760             for (BluetoothGattService s : service.getIncludedServices()) {
    761                 dumpService(s, level + 1);
    762             }
    763         }
    764     }
    765 }
    766 
    767