blob: ccffcdb9d250d4930fbeee18e2004b57973669cc [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";
public static final String ACTION_BLUETOOTH_DISABLED =
"com.android.cts.verifier.bluetooth.action.BLUETOOTH_DISABLED";
public static final String ACTION_CONNECTION_PRIORITY_FINISH =
"com.android.cts.verifier.bluetooth.action.ACTION_CONNECTION_PRIORITY_FINISH";
public static final String ACTION_START_CONNECTION_PRIORITY_TEST =
"com.android.cts.verifier.bluetooth.action.ACTION_START_CONNECTION_PRIORITY_TEST";
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 int interval_low = 0;
private int interval_balanced = 0;
private int interval_high = 0;
@Override
public void onCreate() {
super.onCreate();
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
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();
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 showMessage(final String msg) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
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) {
notifyDisconnected();
mDevice = null;
}
}
//onConnectionUpdated is hidden callback, can't be marked as @Override.
// We must have a call to it, otherwise compiler will delete it during optimization.
if (status == 0xFFEFFEE) {
// This should never execute, but will make compiler not remove onConnectionUpdated
onConnectionUpdated(null, 0, 0, 0, 0);
throw new IllegalStateException("This should never happen!");
}
}
// @Override uncomment once this becomes public API
public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, int status) {
Log.i(TAG, "onConnectionUpdated() status=" + status + ", interval=" + interval);
if (status != 0) {
interval_low = interval_balanced = interval_high = 0;
return;
}
// since we don't know when the test started, wait for three descending interval values.
// Even though conneciton is updated by service discovery, it never happen three times
// descending in any scenario.
// shift all values
interval_low = interval_balanced;
interval_balanced = interval_high;
interval_high = interval;
// If we end up with three descending values, test is passed.
if (interval_low > interval_balanced && interval_balanced > interval_high) {
showMessage("intervals: " + interval_low +" > " + interval_balanced + " > " + interval_high);
notifyMeasurementFinished();
}
}
};
private void notifyMeasurementFinished() {
Intent intent = new Intent();
intent.setAction(ACTION_CONNECTION_PRIORITY_FINISH);
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);
}
}
}