blob: e1e4eedc2c32fec6bff129cf486ca4f085cece8c [file] [log] [blame]
/*
* Copyright (C) 2016 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.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.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.Handler;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.util.Log;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
public class BleConnectionPriorityServerService extends Service {
public static final boolean DEBUG = true;
public static final String TAG = "BlePriorityServer";
private static final String RESET_COUNT_VALUE = "RESET";
private static final String START_VALUE = "START";
private static final String STOP_VALUE = "STOP";
public static final String CONNECTION_PRIORITY_HIGH = "PR_H";
public static final String CONNECTION_PRIORITY_BALANCED = "PR_B";
public static final String CONNECTION_PRIORITY_LOW_POWER = "PR_L";
public static final String ACTION_BLUETOOTH_DISABLED =
"com.android.cts.verifier.bluetooth.action.BLUETOOTH_DISABLED";
public static final String ACTION_CONNECTION_WRITE_REQUEST =
"com.android.cts.verifier.bluetooth.action.CONNECTION_WRITE_REQUEST";
public static final String EXTRA_REQUEST_COUNT =
"com.android.cts.verifier.bluetooth.intent.EXTRA_REQUEST_COUNT";
public static final String ACTION_FINICH_CONNECTION_PRIORITY_HIGHT =
"com.android.cts.verifier.bluetooth.action.ACTION_FINICH_CONNECTION_PRIORITY_HIGHT";
public static final String ACTION_FINICH_CONNECTION_PRIORITY_BALANCED =
"com.android.cts.verifier.bluetooth.action.ACTION_FINICH_CONNECTION_PRIORITY_BALANCED";
public static final String ACTION_FINICH_CONNECTION_PRIORITY_LOW =
"com.android.cts.verifier.bluetooth.action.ACTION_FINICH_CONNECTION_PRIORITY_LOW";
public static final String ACTION_START_CONNECTION_PRIORITY_TEST =
"com.android.cts.verifier.bluetooth.action.ACTION_START_CONNECTION_PRIORITY_TEST";
public static final String EXTRA_AVERAGE =
"com.android.cts.verifier.bluetooth.intent.EXTRA_AVERAGE";
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 START_CHARACTERISTIC_UUID =
UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
private static final UUID STOP_CHARACTERISTIC_UUID =
UUID.fromString("00009995-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_UUID =
UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
public static final UUID ADV_SERVICE_UUID=
UUID.fromString("00002222-0000-1000-8000-00805f9b34fb");
private BluetoothManager mBluetoothManager;
private BluetoothGattServer mGattServer;
private BluetoothGattService mService;
private BluetoothDevice mDevice;
private Handler mHandler;
private BluetoothLeAdvertiser mAdvertiser;
private long mReceiveWriteCount;
private Timer mTimeoutTimer;
private TimerTask mTimeoutTimerTask;
@Override
public void onCreate() {
super.onCreate();
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
mService = createService();
if ((mGattServer != null) && (mAdvertiser != null)) {
mGattServer.addService(mService);
}
mDevice = null;
mHandler = new Handler();
if (!mBluetoothManager.getAdapter().isEnabled()) {
notifyBluetoothDisabled();
} else if (mGattServer == null) {
notifyOpenFail();
} else if (mAdvertiser == null) {
notifyAdvertiseUnsupported();
} else {
startAdvertise();
}
}
@Override
public void onDestroy() {
super.onDestroy();
cancelTimeoutTimer(false);
if (mTimeoutTimer != null) {
mTimeoutTimer.cancel();
mTimeoutTimer = null;
}
mTimeoutTimerTask = null;
stopAdvertise();
if (mGattServer == null) {
return;
}
if (mDevice != null) {
mGattServer.cancelConnection(mDevice);
}
mGattServer.clearServices();
mGattServer.close();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
private void notifyBluetoothDisabled() {
if (DEBUG) {
Log.d(TAG, "notifyBluetoothDisabled");
}
Intent intent = new Intent(ACTION_BLUETOOTH_DISABLED);
sendBroadcast(intent);
}
private void notifyTestStart() {
Intent intent = new Intent(BleConnectionPriorityServerService.ACTION_START_CONNECTION_PRIORITY_TEST);
sendBroadcast(intent);
}
private void notifyOpenFail() {
if (DEBUG) {
Log.d(TAG, "notifyOpenFail");
}
Intent intent = new Intent(BleServerService.BLE_OPEN_FAIL);
sendBroadcast(intent);
}
private void notifyAdvertiseUnsupported() {
if (DEBUG) {
Log.d(TAG, "notifyAdvertiseUnsupported");
}
Intent intent = new Intent(BleServerService.BLE_ADVERTISE_UNSUPPORTED);
sendBroadcast(intent);
}
private void notifyConnected() {
if (DEBUG) {
Log.d(TAG, "notifyConnected");
}
}
private void notifyDisconnected() {
if (DEBUG) {
Log.d(TAG, "notifyDisconnected");
}
}
private void notifyServiceAdded() {
if (DEBUG) {
Log.d(TAG, "notifyServiceAdded");
}
}
private void notifyCharacteristicWriteRequest() {
if (DEBUG) {
Log.d(TAG, "notifyCharacteristicWriteRequest");
}
Intent intent = new Intent(ACTION_CONNECTION_WRITE_REQUEST);
intent.putExtra(EXTRA_REQUEST_COUNT, String.valueOf(mReceiveWriteCount));
sendBroadcast(intent);
}
private void showMessage(final String msg) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
private synchronized void cancelTimeoutTimer(boolean runTimeout) {
if (mTimeoutTimerTask != null) {
mTimeoutTimer.cancel();
if (runTimeout) {
mTimeoutTimerTask.run();
}
mTimeoutTimerTask = null;
mTimeoutTimer = null;
}
}
private BluetoothGattService createService() {
BluetoothGattService service =
new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
// add characteristic to service
// property: 0x0A (read, write)
// permission: 0x11 (read, write)
BluetoothGattCharacteristic characteristic =
new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
characteristic.addDescriptor(descriptor);
service.addCharacteristic(characteristic);
characteristic = new BluetoothGattCharacteristic(START_CHARACTERISTIC_UUID, 0x0A, 0x11);
characteristic.addDescriptor(descriptor);
service.addCharacteristic(characteristic);
characteristic = new BluetoothGattCharacteristic(STOP_CHARACTERISTIC_UUID, 0x0A, 0x11);
characteristic.addDescriptor(descriptor);
service.addCharacteristic(characteristic);
return service;
}
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;
notifyConnected();
} else if (status == BluetoothProfile.STATE_DISCONNECTED) {
cancelTimeoutTimer(true);
notifyDisconnected();
mDevice = null;
}
}
}
@Override
public void onServiceAdded(int status, BluetoothGattService service) {
if (DEBUG) {
Log.d(TAG, "onServiceAdded()");
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyServiceAdded();
}
}
String mPriority = null;
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic,
boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
if (mGattServer == null) {
if (DEBUG) {
Log.d(TAG, "GattServer is null, return");
}
return;
}
if (DEBUG) {
Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite);
}
if (characteristic.getUuid().equals(START_CHARACTERISTIC_UUID)) {
// time out if previous measurement is running
cancelTimeoutTimer(true);
mPriority = new String(value);
Log.d(TAG, "Start Count Up. Priority is " + mPriority);
if (BleConnectionPriorityServerService.CONNECTION_PRIORITY_HIGH.equals(mPriority)) {
notifyTestStart();
}
// start timeout timer
mTimeoutTimer = new Timer(getClass().getName() + "_TimeoutTimer");
mTimeoutTimerTask = new TimerTask() {
@Override
public void run() {
// measurement timed out
mTimeoutTimerTask = null;
mTimeoutTimer = null;
mReceiveWriteCount = 0;
notifyMeasurementFinished(mPriority, Long.MAX_VALUE);
}
};
mTimeoutTimer.schedule(mTimeoutTimerTask, (BleConnectionPriorityClientService.DEFAULT_PERIOD * 2));
mReceiveWriteCount = 0;
} else if (characteristic.getUuid().equals(STOP_CHARACTERISTIC_UUID)) {
boolean isRunning = (mTimeoutTimerTask != null);
cancelTimeoutTimer(false);
String valeStr = new String(value);
String priority = null;
int writeCount = -1;
int sep = valeStr.indexOf(",");
if (sep > 0) {
priority = valeStr.substring(0, sep);
writeCount = Integer.valueOf(valeStr.substring(sep + 1));
}
if ((mPriority != null) && isRunning) {
if (mPriority.equals(priority)) {
long averageTime = BleConnectionPriorityClientService.DEFAULT_PERIOD / mReceiveWriteCount;
notifyMeasurementFinished(mPriority, averageTime);
Log.d(TAG, "Received " + mReceiveWriteCount + " of " + writeCount + " messages");
} else {
Log.d(TAG, "Connection priority does not match");
showMessage("Connection priority does not match");
}
} else {
Log.d(TAG, "Not Start Count UP.");
}
mReceiveWriteCount = 0;
} else {
if (mTimeoutTimerTask != null) {
++mReceiveWriteCount;
}
if (!preparedWrite) {
characteristic.setValue(value);
}
}
if (responseNeeded) {
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
}
}
};
private void notifyMeasurementFinished(String priority, long averageTime) {
Intent intent = new Intent();
intent.putExtra(EXTRA_AVERAGE, averageTime);
switch (priority) {
case CONNECTION_PRIORITY_HIGH:
intent.setAction(ACTION_FINICH_CONNECTION_PRIORITY_HIGHT);
break;
case CONNECTION_PRIORITY_BALANCED:
intent.setAction(ACTION_FINICH_CONNECTION_PRIORITY_BALANCED);
break;
case CONNECTION_PRIORITY_LOW_POWER:
intent.setAction(ACTION_FINICH_CONNECTION_PRIORITY_LOW);
break;
}
sendBroadcast(intent);
}
private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
notifyAdvertiseUnsupported();
} else {
notifyOpenFail();
}
}
};
private void startAdvertise() {
if (DEBUG) {
Log.d(TAG, "startAdvertise");
}
AdvertiseData data = new AdvertiseData.Builder()
.addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1, 2, 3})
.addServiceUuid(new ParcelUuid(ADV_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);
}
}
}