| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.verifier.bluetooth; |
| |
| import android.app.Service; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.BluetoothDevice; |
| import android.bluetooth.BluetoothGatt; |
| import android.bluetooth.BluetoothGattCharacteristic; |
| import android.bluetooth.BluetoothGattDescriptor; |
| import android.bluetooth.BluetoothGattServer; |
| import android.bluetooth.BluetoothGattServerCallback; |
| import android.bluetooth.BluetoothGattService; |
| import android.bluetooth.BluetoothManager; |
| import android.bluetooth.BluetoothProfile; |
| import android.bluetooth.BluetoothServerSocket; |
| import android.bluetooth.BluetoothSocket; |
| import android.bluetooth.le.AdvertiseCallback; |
| import android.bluetooth.le.AdvertiseData; |
| import android.bluetooth.le.AdvertiseSettings; |
| import android.bluetooth.le.BluetoothLeAdvertiser; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.os.ParcelUuid; |
| import android.util.Log; |
| import android.widget.Toast; |
| |
| import com.android.cts.verifier.R; |
| |
| import java.io.IOException; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.Timer; |
| import java.util.UUID; |
| |
| public class BleCocServerService extends Service { |
| |
| public static final boolean DEBUG = true; |
| public static final String TAG = "BleCocServerService"; |
| |
| public static final int COMMAND_ADD_SERVICE = 0; |
| public static final int COMMAND_WRITE_CHARACTERISTIC = 1; |
| public static final int COMMAND_WRITE_DESCRIPTOR = 2; |
| |
| public static final int TEST_DATA_EXCHANGE_BUFSIZE = 8 * 1024; |
| |
| public static final String BLE_BLUETOOTH_MISMATCH_SECURE = |
| "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_SECURE"; |
| public static final String BLE_BLUETOOTH_MISMATCH_INSECURE = |
| "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_MISMATCH_INSECURE"; |
| public static final String BLE_BLUETOOTH_DISABLED = |
| "com.android.cts.verifier.bluetooth.BLE_BLUETOOTH_DISABLED"; |
| |
| public static final String BLE_ACTION_COC_SERVER_INSECURE = |
| "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_INSECURE"; |
| public static final String BLE_ACTION_COC_SERVER_SECURE = |
| "com.android.cts.verifier.bluetooth.BLE_ACTION_COC_SERVER_SECURE"; |
| |
| public static final String BLE_ACTION_SERVER_SECURE = |
| "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_SECURE"; |
| public static final String BLE_ACTION_SERVER_NON_SECURE = |
| "com.android.cts.verifier.bluetooth.BLE_ACTION_SERVER_NON_SECURE"; |
| |
| public static final String BLE_LE_CONNECTED = |
| "com.android.cts.verifier.bluetooth.BLE_LE_CONNECTED"; |
| public static final String BLE_COC_LISTENER_CREATED = |
| "com.android.cts.verifier.bluetooth.BLE_COC_LISTENER_CREATED"; |
| public static final String BLE_PSM_READ = |
| "com.android.cts.verifier.bluetooth.BLE_PSM_READ"; |
| public static final String BLE_COC_CONNECTED = |
| "com.android.cts.verifier.bluetooth.BLE_COC_CONNECTED"; |
| public static final String BLE_CONNECTION_TYPE_CHECKED = |
| "com.android.cts.verifier.bluetooth.BLE_CONNECTION_TYPE_CHECKED"; |
| public static final String BLE_DATA_8BYTES_READ = |
| "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_READ"; |
| public static final String BLE_DATA_LARGEBUF_READ = |
| "com.android.cts.verifier.bluetooth.BLE_DATA_LARGEBUF_READ"; |
| public static final String BLE_DATA_8BYTES_SENT = |
| "com.android.cts.verifier.bluetooth.BLE_DATA_8BYTES_SENT"; |
| public static final String BLE_LE_DISCONNECTED = |
| "com.android.cts.verifier.bluetooth.BLE_LE_DISCONNECTED"; |
| public static final String BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES = |
| "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES"; |
| public static final String BLE_COC_SERVER_ACTION_EXCHANGE_DATA = |
| "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_EXCHANGE_DATA"; |
| public static final String BLE_COC_SERVER_ACTION_DISCONNECT = |
| "com.android.cts.verifier.bluetooth.BLE_COC_SERVER_ACTION_DISCONNECT"; |
| |
| public static final String BLE_SERVER_DISCONNECTED = |
| "com.android.cts.verifier.bluetooth.BLE_SERVER_DISCONNECTED"; |
| public static final String BLE_OPEN_FAIL = |
| "com.android.cts.verifier.bluetooth.BLE_OPEN_FAIL"; |
| public static final String BLE_ADVERTISE_UNSUPPORTED = |
| "com.android.cts.verifier.bluetooth.BLE_ADVERTISE_UNSUPPORTED"; |
| public static final String BLE_ADD_SERVICE_FAIL = |
| "com.android.cts.verifier.bluetooth.BLE_ADD_SERVICE_FAIL"; |
| |
| private static final UUID SERVICE_UUID = |
| UUID.fromString("00009999-0000-1000-8000-00805f9b34fb"); |
| private static final UUID CHARACTERISTIC_UUID = |
| UUID.fromString("00009998-0000-1000-8000-00805f9b34fb"); |
| private static final UUID CHARACTERISTIC_RESULT_UUID = |
| UUID.fromString("00009974-0000-1000-8000-00805f9b34fb"); |
| private static final UUID UPDATE_CHARACTERISTIC_UUID = |
| UUID.fromString("00009997-0000-1000-8000-00805f9b34fb"); |
| private static final UUID DESCRIPTOR_UUID = |
| UUID.fromString("00009996-0000-1000-8000-00805f9b34fb"); |
| public static final UUID ADV_COC_SERVICE_UUID= |
| UUID.fromString("00003334-0000-1000-8000-00805f9b34fb"); |
| |
| private static final UUID SERVICE_UUID_ADDITIONAL = |
| UUID.fromString("00009995-0000-1000-8000-00805f9b34fb"); |
| private static final UUID SERVICE_UUID_INCLUDED = |
| UUID.fromString("00009994-0000-1000-8000-00805f9b34fb"); |
| |
| // Variable for registration permission of Descriptor |
| private static final UUID DESCRIPTOR_NO_READ_UUID = |
| UUID.fromString("00009973-0000-1000-8000-00805f9b34fb"); |
| private static final UUID DESCRIPTOR_NO_WRITE_UUID = |
| UUID.fromString("00009972-0000-1000-8000-00805f9b34fb"); |
| private static final UUID DESCRIPTOR_NEED_ENCRYPTED_READ_UUID = |
| UUID.fromString("00009969-0000-1000-8000-00805f9b34fb"); |
| private static final UUID DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID = |
| UUID.fromString("00009968-0000-1000-8000-00805f9b34fb"); |
| |
| private static final int CONN_INTERVAL = 150; // connection interval 150ms |
| |
| private static final int EXECUTION_DELAY = 1500; |
| |
| // Delay of notification when secure test failed to start. |
| private static final long NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE = 5 * 1000; |
| |
| public static final String WRITE_VALUE = "SERVER_TEST"; |
| private static final String NOTIFY_VALUE = "NOTIFY_TEST"; |
| private static final String INDICATE_VALUE = "INDICATE_TEST"; |
| public static final String READ_NO_PERMISSION = "READ_NO_CHAR"; |
| public static final String WRITE_NO_PERMISSION = "WRITE_NO_CHAR"; |
| public static final String DESCRIPTOR_READ_NO_PERMISSION = "READ_NO_DESC"; |
| public static final String DESCRIPTOR_WRITE_NO_PERMISSION = "WRITE_NO_DESC"; |
| |
| private BluetoothManager mBluetoothManager; |
| private BluetoothGattServer mGattServer; |
| private BluetoothGattService mService; |
| private BluetoothDevice mDevice; |
| private Handler mHandler; |
| private BluetoothLeAdvertiser mAdvertiser; |
| private boolean mSecure; |
| private int mMtuSize = -1; |
| |
| private BluetoothServerSocket mServerSocket; |
| private int mPsm = -1; |
| private BluetoothGattCharacteristic mLePsmCharacteristic; |
| BluetoothChatService mChatService; |
| |
| private int mNextReadExpectedLen = -1; |
| private String mNextReadCompletionIntent; |
| private int mTotalReadLen = 0; |
| private byte mNextReadByte; |
| private int mNextWriteExpectedLen = -1; |
| private String mNextWriteCompletionIntent = null; |
| |
| // Handler for communicating task with peer. |
| private TestTaskQueue mTaskQueue; |
| |
| // current test category |
| private String mCurrentAction; |
| |
| // Task to notify failure of starting secure test. |
| // Secure test calls BluetoothDevice#createBond() when devices were not paired. |
| // createBond() causes onConnectionStateChange() twice, and it works as strange sequence. |
| // At the first onConnectionStateChange(), target device is not paired (bond state is |
| // BluetoothDevice.BOND_NONE). |
| // At the second onConnectionStateChange(), target devices is paired (bond state is |
| // BluetoothDevice.BOND_BONDED). |
| // CTS Verifier will perform lazy check of bond state. Verifier checks bond state |
| // after NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE from the first onConnectionStateChange(). |
| private Runnable mNotificationTaskOfSecureTestStartFailure; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| |
| mTaskQueue = new TestTaskQueue(getClass().getName() + "_taskHandlerThread"); |
| |
| mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); |
| mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser(); |
| mGattServer = mBluetoothManager.openGattServer(this, mCallbacks); |
| |
| mService = createService(); |
| |
| mDevice = null; |
| |
| mHandler = new Handler(); |
| if (!mBluetoothManager.getAdapter().isEnabled()) { |
| notifyBluetoothDisabled(); |
| } else if (mGattServer == null) { |
| notifyOpenFail(); |
| } else if (mAdvertiser == null) { |
| notifyAdvertiseUnsupported(); |
| } else { |
| // start adding services |
| mSecure = false; |
| if (!mGattServer.addService(mService)) { |
| notifyAddServiceFail(); |
| } |
| } |
| } |
| |
| private void notifyBluetoothDisabled() { |
| Intent intent = new Intent(BLE_BLUETOOTH_DISABLED); |
| sendBroadcast(intent); |
| } |
| |
| private void notifyMismatchSecure() { |
| Intent intent = new Intent(BLE_BLUETOOTH_MISMATCH_SECURE); |
| sendBroadcast(intent); |
| } |
| |
| @Override |
| public int onStartCommand(Intent intent, int flags, int startId) { |
| String action = intent.getAction(); |
| if (action != null) { |
| if (DEBUG) { |
| Log.d(TAG, "onStartCommand: action=" + action); |
| } |
| mTaskQueue.addTask(new Runnable() { |
| @Override |
| public void run() { |
| onTestFinish(intent.getAction()); |
| } |
| }, EXECUTION_DELAY); |
| } |
| return START_NOT_STICKY; |
| } |
| |
| private void startServerTest(boolean secure) { |
| mSecure = secure; |
| |
| if (mBluetoothManager.getAdapter().isEnabled() && (mChatService == null)) { |
| createChatService(); |
| } |
| |
| if (mBluetoothManager.getAdapter().isEnabled() && (mAdvertiser != null)) { |
| startAdvertise(); |
| } |
| } |
| |
| private void sendMessage(byte[] buf) { |
| mChatService.write(buf); |
| } |
| |
| private void sendData8bytes() { |
| if (DEBUG) Log.d(TAG, "sendData8bytes"); |
| |
| final byte[] buf = new byte[]{1,2,3,4,5,6,7,8}; |
| mNextWriteExpectedLen = 8; |
| mNextWriteCompletionIntent = BLE_DATA_8BYTES_SENT; |
| sendMessage(buf); |
| } |
| |
| private void sendDataLargeBuf() { |
| final int len = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE; |
| if (DEBUG) Log.d(TAG, "sendDataLargeBuf of size=" + len); |
| |
| byte[] buf = new byte[len]; |
| for (int i = 0; i < len; i++) { |
| buf[i] = (byte)(i + 1); |
| } |
| mNextWriteExpectedLen = len; |
| mNextWriteCompletionIntent = null; |
| sendMessage(buf); |
| } |
| |
| private void onTestFinish(String action) { |
| mCurrentAction = action; |
| if (mCurrentAction != null) { |
| switch (mCurrentAction) { |
| case BLE_ACTION_COC_SERVER_INSECURE: |
| startServerTest(false); |
| break; |
| case BLE_ACTION_COC_SERVER_SECURE: |
| startServerTest(true); |
| break; |
| case BLE_COC_SERVER_ACTION_SEND_DATA_8BYTES: |
| sendData8bytes(); |
| break; |
| case BLE_COC_SERVER_ACTION_EXCHANGE_DATA: |
| sendDataLargeBuf(); |
| readDataLargeBuf(); |
| break; |
| case BLE_COC_SERVER_ACTION_DISCONNECT: |
| if (mChatService != null) { |
| mChatService.stop(); |
| } |
| break; |
| default: |
| Log.e(TAG, "Error: Unhandled or invalid action=" + mCurrentAction); |
| } |
| } |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return null; |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| |
| if (mChatService != null) { |
| mChatService.stop(); |
| } |
| |
| cancelNotificationTaskOfSecureTestStartFailure(); |
| stopAdvertise(); |
| |
| mTaskQueue.quit(); |
| |
| if (mGattServer == null) { |
| return; |
| } |
| if (mDevice != null) { |
| mGattServer.cancelConnection(mDevice); |
| } |
| mGattServer.clearServices(); |
| mGattServer.close(); |
| } |
| |
| private void notifyOpenFail() { |
| if (DEBUG) { |
| Log.d(TAG, "notifyOpenFail"); |
| } |
| Intent intent = new Intent(BLE_OPEN_FAIL); |
| sendBroadcast(intent); |
| } |
| |
| private void notifyAddServiceFail() { |
| if (DEBUG) { |
| Log.d(TAG, "notifyAddServiceFail"); |
| } |
| Intent intent = new Intent(BLE_ADD_SERVICE_FAIL); |
| sendBroadcast(intent); |
| } |
| |
| private void notifyAdvertiseUnsupported() { |
| if (DEBUG) { |
| Log.d(TAG, "notifyAdvertiseUnsupported"); |
| } |
| Intent intent = new Intent(BLE_ADVERTISE_UNSUPPORTED); |
| sendBroadcast(intent); |
| } |
| |
| private void notifyConnected() { |
| if (DEBUG) { |
| Log.d(TAG, "notifyConnected"); |
| } |
| Intent intent = new Intent(BLE_LE_CONNECTED); |
| sendBroadcast(intent); |
| } |
| |
| private void notifyDisconnected() { |
| if (DEBUG) { |
| Log.d(TAG, "notifyDisconnected"); |
| } |
| Intent intent = new Intent(BLE_SERVER_DISCONNECTED); |
| sendBroadcast(intent); |
| } |
| |
| private BluetoothGattService createService() { |
| BluetoothGattService service = |
| new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY); |
| BluetoothGattCharacteristic characteristic = |
| new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11); |
| characteristic.setValue(WRITE_VALUE.getBytes()); |
| |
| BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11); |
| descriptor.setValue(WRITE_VALUE.getBytes()); |
| characteristic.addDescriptor(descriptor); |
| |
| BluetoothGattDescriptor descriptor_permission = |
| new BluetoothGattDescriptor(DESCRIPTOR_NO_READ_UUID, 0x10); |
| characteristic.addDescriptor(descriptor_permission); |
| |
| descriptor_permission = new BluetoothGattDescriptor(DESCRIPTOR_NO_WRITE_UUID, 0x01); |
| characteristic.addDescriptor(descriptor_permission); |
| |
| service.addCharacteristic(characteristic); |
| |
| // Registered the characteristic of PSM Value |
| mLePsmCharacteristic = |
| new BluetoothGattCharacteristic(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID, |
| BluetoothGattCharacteristic.PROPERTY_READ, |
| BluetoothGattCharacteristic.PERMISSION_READ); |
| service.addCharacteristic(mLePsmCharacteristic); |
| |
| return service; |
| } |
| |
| private void showMessage(final String msg) { |
| mHandler.post(new Runnable() { |
| public void run() { |
| Toast.makeText(BleCocServerService.this, msg, Toast.LENGTH_SHORT).show(); |
| } |
| }); |
| } |
| |
| private synchronized void cancelNotificationTaskOfSecureTestStartFailure() { |
| if (mNotificationTaskOfSecureTestStartFailure != null) { |
| mHandler.removeCallbacks(mNotificationTaskOfSecureTestStartFailure); |
| mNotificationTaskOfSecureTestStartFailure = null; |
| } |
| } |
| |
| private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() { |
| @Override |
| public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { |
| if (DEBUG) { |
| Log.d(TAG, "onConnectionStateChange: newState=" + newState); |
| } |
| |
| if (status == BluetoothGatt.GATT_SUCCESS) { |
| if (newState == BluetoothProfile.STATE_CONNECTED) { |
| mDevice = device; |
| boolean bonded = false; |
| Set<BluetoothDevice> pairedDevices = |
| mBluetoothManager.getAdapter().getBondedDevices(); |
| if (pairedDevices.size() > 0) { |
| for (BluetoothDevice target : pairedDevices) { |
| if (target.getAddress().equals(device.getAddress())) { |
| bonded = true; |
| break; |
| } |
| } |
| } |
| |
| if (mSecure && ((device.getBondState() == BluetoothDevice.BOND_NONE) || |
| !bonded)) { |
| // not pairing and execute Secure Test |
| Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: " |
| + "Not paired but execute secure test"); |
| cancelNotificationTaskOfSecureTestStartFailure(); |
| } else if (!mSecure && ((device.getBondState() != BluetoothDevice.BOND_NONE) |
| || bonded)) { |
| // already pairing and execute Insecure Test |
| Log.e(TAG, "BluetoothGattServerCallback.onConnectionStateChange: " |
| + "Paired but execute insecure test"); |
| } else { |
| cancelNotificationTaskOfSecureTestStartFailure(); |
| } |
| notifyConnected(); |
| } else if (status == BluetoothProfile.STATE_DISCONNECTED) { |
| notifyDisconnected(); |
| mDevice = null; |
| } |
| } |
| } |
| |
| @Override |
| public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, |
| BluetoothGattCharacteristic characteristic) { |
| if (mGattServer == null) { |
| if (DEBUG) { |
| Log.d(TAG, "GattServer is null, return"); |
| } |
| return; |
| } |
| if (DEBUG) { |
| Log.d(TAG, "onCharacteristicReadRequest()"); |
| } |
| |
| boolean finished = false; |
| byte[] value = null; |
| if (mMtuSize > 0) { |
| byte[] buf = characteristic.getValue(); |
| if (buf != null) { |
| int len = Math.min((buf.length - offset), mMtuSize); |
| if (len > 0) { |
| value = Arrays.copyOfRange(buf, offset, (offset + len)); |
| } |
| finished = ((offset + len) >= buf.length); |
| if (finished) { |
| Log.d(TAG, "sent whole data: " + (new String(characteristic.getValue()))); |
| } |
| } |
| } else { |
| value = characteristic.getValue(); |
| finished = true; |
| } |
| |
| mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); |
| |
| UUID uid = characteristic.getUuid(); |
| if (uid.equals(BleCocClientService.LE_PSM_CHARACTERISTIC_UUID)) { |
| Log.d(TAG, "onCharacteristicReadRequest: reading PSM"); |
| } |
| } |
| |
| }; |
| |
| private void leCheckConnectionType() { |
| if (mChatService == null) { |
| Log.e(TAG, "leCheckConnectionType: no LE Coc connection"); |
| return; |
| } |
| int type = mChatService.getSocketConnectionType(); |
| if (type != BluetoothSocket.TYPE_L2CAP) { |
| Log.e(TAG, "leCheckConnectionType: invalid connection type=" + type); |
| return; |
| } |
| showMessage("LE CoC Connection Type Checked"); |
| Intent intent = new Intent(BLE_CONNECTION_TYPE_CHECKED); |
| sendBroadcast(intent); |
| } |
| |
| private void readData8bytes() { |
| mNextReadExpectedLen = 8; |
| mTotalReadLen = 0; |
| mNextReadCompletionIntent = BLE_DATA_8BYTES_READ; |
| mNextReadByte = 1; |
| } |
| |
| private void readDataLargeBuf() { |
| mNextReadExpectedLen = BleCocServerService.TEST_DATA_EXCHANGE_BUFSIZE; |
| mTotalReadLen = 0; |
| mNextReadCompletionIntent = BLE_DATA_LARGEBUF_READ; |
| mNextReadByte = 1; |
| } |
| |
| private void processChatStateChange(int newState) { |
| Intent intent; |
| if (DEBUG) { |
| Log.d(TAG, "processChatStateChange: newState=" + newState); |
| } |
| switch (newState) { |
| case BluetoothChatService.STATE_LISTEN: |
| intent = new Intent(BLE_COC_LISTENER_CREATED); |
| sendBroadcast(intent); |
| break; |
| case BluetoothChatService.STATE_CONNECTED: |
| intent = new Intent(BLE_COC_CONNECTED); |
| sendBroadcast(intent); |
| |
| // Check the connection type |
| leCheckConnectionType(); |
| |
| // Prepare the next data read |
| readData8bytes(); |
| break; |
| } |
| } |
| |
| private boolean checkReadBufContent(byte[] buf, int len) { |
| // Check that the content is correct |
| for (int i = 0; i < len; i++) { |
| if (buf[i] != mNextReadByte) { |
| Log.e(TAG, "handleMessageRead: Error: wrong byte content. buf[" |
| + i + "]=" + buf[i] + " not equal to " + mNextReadByte); |
| return false; |
| } |
| mNextReadByte++; |
| } |
| return true; |
| } |
| |
| private void handleMessageRead(Message msg) { |
| byte[] buf = (byte[])msg.obj; |
| int len = msg.arg1; |
| if (len <= 0) { |
| return; |
| } |
| mTotalReadLen += len; |
| if (DEBUG) { |
| Log.d(TAG, "handleMessageRead: receive buffer of length=" + len + ", mTotalReadLen=" |
| + mTotalReadLen + ", mNextReadExpectedLen=" + mNextReadExpectedLen); |
| } |
| |
| if (mNextReadExpectedLen == mTotalReadLen) { |
| if (!checkReadBufContent(buf, len)) { |
| mNextReadExpectedLen = -1; |
| return; |
| } |
| showMessage("Read " + len + " bytes"); |
| if (DEBUG) { |
| Log.d(TAG, "handleMessageRead: broadcast intent " + mNextReadCompletionIntent); |
| } |
| Intent intent = new Intent(mNextReadCompletionIntent); |
| sendBroadcast(intent); |
| mNextReadExpectedLen = -1; |
| mNextReadCompletionIntent = null; |
| mTotalReadLen = 0; |
| } else if (mNextReadExpectedLen > mTotalReadLen) { |
| if (!checkReadBufContent(buf, len)) { |
| mNextReadExpectedLen = -1; |
| return; |
| } |
| } else if (mNextReadExpectedLen < mTotalReadLen) { |
| Log.e(TAG, "handleMessageRead: Unexpected receive buffer of length=" + len |
| + ", expected len=" + mNextReadExpectedLen); |
| } |
| } |
| |
| private void handleMessageWrite(Message msg) { |
| byte[] buffer = (byte[]) msg.obj; |
| int len = buffer.length; |
| showMessage("LE CoC Server wrote " + len + " bytes" + ", mNextWriteExpectedLen=" |
| + mNextWriteExpectedLen); |
| if (len == mNextWriteExpectedLen) { |
| if (mNextWriteCompletionIntent != null) { |
| Intent intent = new Intent(mNextWriteCompletionIntent); |
| sendBroadcast(intent); |
| } |
| } else { |
| Log.d(TAG, "handleMessageWrite: unrecognized length=" + len); |
| } |
| mNextWriteCompletionIntent = null; |
| mNextWriteExpectedLen = -1; |
| } |
| |
| private class ChatHandler extends Handler { |
| @Override |
| public void handleMessage(Message msg) { |
| super.handleMessage(msg); |
| if (DEBUG) { |
| Log.d(TAG, "ChatHandler.handleMessage: msg=" + msg); |
| } |
| switch (msg.what) { |
| case BluetoothChatService.MESSAGE_STATE_CHANGE: |
| processChatStateChange(msg.arg1); |
| break; |
| case BluetoothChatService.MESSAGE_READ: |
| handleMessageRead(msg); |
| break; |
| case BluetoothChatService.MESSAGE_WRITE: |
| handleMessageWrite(msg); |
| break; |
| } |
| } |
| } |
| |
| /* Start the Chat Service to create the Bluetooth Server Socket for LE CoC */ |
| private void createChatService() { |
| |
| mChatService = new BluetoothChatService(this, new ChatHandler(), true); |
| mChatService.start(mSecure); |
| mPsm = mChatService.getPsm(mSecure); |
| if (DEBUG) { |
| Log.d(TAG, "createChatService: assigned PSM=" + mPsm); |
| } |
| if (mPsm > 0x00ff) { |
| Log.e(TAG, "createChatService: Invalid PSM=" + mPsm); |
| } |
| // Notify that the PSM is read |
| Intent intent = new Intent(BLE_PSM_READ); |
| sendBroadcast(intent); |
| |
| // Set the PSM value in the PSM characteristics in the GATT Server. |
| mLePsmCharacteristic.setValue(mPsm, BluetoothGattCharacteristic.FORMAT_UINT8, 0); |
| } |
| |
| private void startAdvertise() { |
| if (DEBUG) { |
| Log.d(TAG, "startAdvertise"); |
| } |
| AdvertiseData data = new AdvertiseData.Builder() |
| .addServiceData(new ParcelUuid(ADV_COC_SERVICE_UUID), new byte[]{1,2,3}) |
| .addServiceUuid(new ParcelUuid(ADV_COC_SERVICE_UUID)) |
| .build(); |
| AdvertiseSettings setting = new AdvertiseSettings.Builder() |
| .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) |
| .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) |
| .setConnectable(true) |
| .build(); |
| mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback); |
| } |
| |
| private void stopAdvertise() { |
| if (DEBUG) { |
| Log.d(TAG, "stopAdvertise"); |
| } |
| if (mAdvertiser != null) { |
| mAdvertiser.stopAdvertising(mAdvertiseCallback); |
| } |
| } |
| |
| private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){ |
| @Override |
| public void onStartFailure(int errorCode) { |
| // Implementation for API Test. |
| super.onStartFailure(errorCode); |
| if (DEBUG) { |
| Log.d(TAG, "onStartFailure"); |
| } |
| |
| if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) { |
| notifyAdvertiseUnsupported(); |
| } else { |
| notifyOpenFail(); |
| } |
| } |
| |
| @Override |
| public void onStartSuccess(AdvertiseSettings settingsInEffect) { |
| // Implementation for API Test. |
| super.onStartSuccess(settingsInEffect); |
| if (DEBUG) { |
| Log.d(TAG, "onStartSuccess"); |
| } |
| } |
| }; |
| |
| /*protected*/ static void dumpService(BluetoothGattService service, int level) { |
| String indent = ""; |
| for (int i = 0; i < level; ++i) { |
| indent += " "; |
| } |
| |
| Log.d(TAG, indent + "[service]"); |
| Log.d(TAG, indent + "UUID: " + service.getUuid()); |
| Log.d(TAG, indent + " [characteristics]"); |
| for (BluetoothGattCharacteristic ch : service.getCharacteristics()) { |
| Log.d(TAG, indent + " UUID: " + ch.getUuid()); |
| Log.d(TAG, indent + " properties: " |
| + String.format("0x%02X", ch.getProperties())); |
| Log.d(TAG, indent + " permissions: " |
| + String.format("0x%02X", ch.getPermissions())); |
| Log.d(TAG, indent + " [descriptors]"); |
| for (BluetoothGattDescriptor d : ch.getDescriptors()) { |
| Log.d(TAG, indent + " UUID: " + d.getUuid()); |
| Log.d(TAG, indent + " permissions: " |
| + String.format("0x%02X", d.getPermissions())); |
| } |
| } |
| |
| if (service.getIncludedServices() != null) { |
| Log.d(TAG, indent + " [included services]"); |
| for (BluetoothGattService s : service.getIncludedServices()) { |
| dumpService(s, level + 1); |
| } |
| } |
| } |
| } |
| |