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
onCreate()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 
notifyBluetoothDisabled()232     private void notifyBluetoothDisabled() {
233         Intent intent = new Intent(BLE_BLUETOOTH_DISABLED);
234         sendBroadcast(intent);
235     }
236 
notifyMismatchSecure()237     private void notifyMismatchSecure() {
238         Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE);
239         sendBroadcast(intent);
240     }
241 
242     @Override
onStartCommand(Intent intent, int flags, int startId)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 
startServerTest(boolean secure)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 
sendMessage(byte[] buf)271     private void sendMessage(byte[] buf) {
272         mChatService.write(buf);
273     }
274 
sendData8bytes()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 
sendDataLargeBuf()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 
onTestFinish(String action)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                     break;
313                 case BLE_COC_SERVER_ACTION_DISCONNECT:
314                     if (mChatService != null) {
315                         mChatService.stop();
316                     }
317                     notifyDisconnected();
318                     break;
319                 default:
320                     Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction);
321             }
322         }
323     }
324 
325     @Override
onBind(Intent intent)326     public IBinder onBind(Intent intent) {
327         return null;
328     }
329 
330     @Override
onDestroy()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 
notifyOpenFail()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 
notifyAddServiceFail()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 
notifyAdvertiseUnsupported()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 
notifyConnected()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 
notifyDisconnected()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 
createService()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 
showMessage(final String msg)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 
cancelNotificationTaskOfSecureTestStartFailure()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 
leCheckConnectionType()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 
readData8bytes()539     private void readData8bytes() {
540         mNextReadExpectedLen = 8;
541         mTotalReadLen = 0;
542         mNextReadCompletionIntent = BLE_DATA_8BYTES_READ;
543         mNextReadByte = 1;
544     }
545 
readDataLargeBuf()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 
processChatStateChange(int newState)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 
checkReadBufContent(byte[] buf, int len)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 
handleMessageRead(Message msg)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             mTotalReadLen = 0;
613             if (mNextReadCompletionIntent.equals(BLE_DATA_8BYTES_READ)) {
614                 // Keeps the logic same as in the client code, to make sure that the expectation is
615                 // set before we receive the next bunch of data.
616                 readDataLargeBuf();
617             } else {
618                 mNextReadExpectedLen = -1;
619                 mNextReadCompletionIntent = null;
620             }
621         } else if (mNextReadExpectedLen > mTotalReadLen) {
622             if (!checkReadBufContent(buf, len)) {
623                 mNextReadExpectedLen = -1;
624                 return;
625             }
626         } else if (mNextReadExpectedLen < mTotalReadLen) {
627             Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len
628                   + ", expected len=" + mNextReadExpectedLen);
629         }
630     }
631 
handleMessageWrite(Message msg)632     private void handleMessageWrite(Message msg) {
633         byte[] buffer = (byte[]) msg.obj;
634         int len = buffer.length;
635         showMessage("LE CoC Server wrote " + len + " bytes" + ", mNextWriteExpectedLen="
636                     + mNextWriteExpectedLen);
637         if (len == mNextWriteExpectedLen) {
638             if (mNextWriteCompletionIntent != null) {
639                 Intent intent = new Intent(mNextWriteCompletionIntent);
640                 sendBroadcast(intent);
641             }
642         } else {
643             Log.d(TAG, "handleMessageWrite: unrecognized length=" + len);
644         }
645         mNextWriteCompletionIntent = null;
646         mNextWriteExpectedLen = -1;
647     }
648 
649     private class ChatHandler extends Handler {
650         @Override
handleMessage(Message msg)651         public void handleMessage(Message msg) {
652             super.handleMessage(msg);
653             if (DEBUG) {
654                 Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg);
655             }
656             switch (msg.what) {
657                 case BluetoothChatService.MESSAGE_STATE_CHANGE:
658                     processChatStateChange(msg.arg1);
659                     break;
660                 case BluetoothChatService.MESSAGE_READ:
661                     handleMessageRead(msg);
662                     break;
663                 case BluetoothChatService.MESSAGE_WRITE:
664                     handleMessageWrite(msg);
665                     break;
666             }
667         }
668     }
669 
670     /* Start the Chat Service to create the Bluetooth Server Socket for LE CoC */
createChatService()671     private void createChatService() {
672 
673         mChatService = new BluetoothChatService(this, new ChatHandler(), true);
674         mChatService.start(mSecure);
675         mPsm = mChatService.getPsm(mSecure);
676         if (DEBUG) {
677             Log.d(TAG, "createChatService: assigned PSM=" + mPsm);
678         }
679         if (mPsm > 0x00ff) {
680             Log.e(TAG, "createChatService: Invalid PSM=" + mPsm);
681         }
682         // Notify that the PSM is read
683         Intent intent = new Intent(BLE_PSM_READ);
684         sendBroadcast(intent);
685 
686         // Set the PSM value in the PSM characteristics in the GATT Server.
687         mLePsmCharacteristic.setValue(mPsm, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
688     }
689 
startAdvertise()690     private void startAdvertise() {
691         if (DEBUG) {
692             Log.d(TAG, "startAdvertise");
693         }
694         AdvertiseData data = new AdvertiseData.Builder()
695             .addServiceData(new ParcelUuid(ADV_COC_SERVICE_UUID), new byte[]{1,2,3})
696             .addServiceUuid(new ParcelUuid(ADV_COC_SERVICE_UUID))
697             .build();
698         AdvertiseSettings setting = new AdvertiseSettings.Builder()
699             .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
700             .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
701             .setConnectable(true)
702             .build();
703         mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
704     }
705 
stopAdvertise()706     private void stopAdvertise() {
707         if (DEBUG) {
708             Log.d(TAG, "stopAdvertise");
709         }
710         if (mAdvertiser != null) {
711             mAdvertiser.stopAdvertising(mAdvertiseCallback);
712         }
713     }
714 
715     private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){
716         @Override
717         public void onStartFailure(int errorCode) {
718             // Implementation for API Test.
719             super.onStartFailure(errorCode);
720             if (DEBUG) {
721                 Log.d(TAG, "onStartFailure");
722             }
723 
724             if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
725                 notifyAdvertiseUnsupported();
726             } else {
727                 notifyOpenFail();
728             }
729         }
730 
731         @Override
732         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
733             // Implementation for API Test.
734             super.onStartSuccess(settingsInEffect);
735             if (DEBUG) {
736                 Log.d(TAG, "onStartSuccess");
737             }
738         }
739     };
740 
dumpService(BluetoothGattService service, int level)741     /*protected*/ static void dumpService(BluetoothGattService service, int level) {
742         String indent = "";
743         for (int i = 0; i < level; ++i) {
744             indent += "  ";
745         }
746 
747         Log.d(TAG, indent + "[service]");
748         Log.d(TAG, indent + "UUID: " + service.getUuid());
749         Log.d(TAG, indent + "  [characteristics]");
750         for (BluetoothGattCharacteristic ch : service.getCharacteristics()) {
751             Log.d(TAG, indent + "    UUID: " + ch.getUuid());
752             Log.d(TAG, indent + "      properties: "
753                   + String.format("0x%02X", ch.getProperties()));
754             Log.d(TAG, indent + "      permissions: "
755                   + String.format("0x%02X", ch.getPermissions()));
756             Log.d(TAG, indent + "      [descriptors]");
757             for (BluetoothGattDescriptor d : ch.getDescriptors()) {
758                 Log.d(TAG, indent + "        UUID: " + d.getUuid());
759                 Log.d(TAG, indent + "          permissions: "
760                       + String.format("0x%02X", d.getPermissions()));
761             }
762         }
763 
764         if (service.getIncludedServices() != null) {
765             Log.d(TAG, indent + "  [included services]");
766             for (BluetoothGattService s : service.getIncludedServices()) {
767                 dumpService(s, level + 1);
768             }
769         }
770     }
771 }
772 
773