1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-09 15:11:34 +02:00

Add handler thread to poll TX power level, properly use performInitialized(), correctly terminate threads

This commit is contained in:
Andreas Böhler 2019-01-04 18:48:16 +01:00 committed by Carsten Pfeiffer
parent 0009badd71
commit cf870bff8a
5 changed files with 287 additions and 34 deletions

View File

@ -64,6 +64,17 @@ public final class CasioGB6900Constants {
public static final UUID FUNCTION_SWITCH_CHARACTERISTIC = UUID.fromString("26eb001e-b012-49a8-b1f8-394fb2032b0f");
public static final String MUSIC_MESSAGE = "Music";
// Link Loss
public static final UUID LINK_LOSS_SERVICE = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
// TxPower
public static final UUID TX_POWER_SERVICE_UUID = UUID.fromString("00001804-0000-1000-8000-00805f9b34fb");
public static final UUID TX_POWER_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb");
// Settings
public static final UUID CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID = UUID.fromString("26eb000f-b012-49a8-b1f8-394fb2032b0f");
// Notification Types
public static final byte CALL_NOTIFICATION_ID = 3;

View File

@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
@ -25,12 +26,20 @@ import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
class CasioGATTServer extends BluetoothGattServerCallback {
private static final Logger LOG = LoggerFactory.getLogger(CasioGATTServer.class);
@ -142,6 +151,18 @@ class CasioGATTServer extends BluetoothGattServerCallback {
if (newState == BluetoothGattServer.STATE_DISCONNECTED) {
}
if (newState == BluetoothGattServer.STATE_CONNECTED) {
GBDevice.State devState = mDeviceSupport.getDevice().getState();
Intent deviceCommunicationServiceIntent = new Intent(mContext, DeviceCommunicationService.class);
if (devState.equals(GBDevice.State.WAITING_FOR_RECONNECT) || devState.equals(GBDevice.State.NOT_CONNECTED)) {
LOG.info("Forcing re-connect because GATT server has been reconnected.");
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(deviceCommunicationServiceIntent);
//PendingIntent reconnectPendingIntent = PendingIntent.getService(mContext, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
}
}
@Override

View File

@ -25,6 +25,7 @@ public class CasioGATTThread extends Thread {
CasioGATTServer mServer = null;
private static final Logger LOG = LoggerFactory.getLogger(CasioGATTThread.class);
private boolean mStopFlag = false;
private final Object waitObject = new Object();
public CasioGATTThread(Context context, CasioGB6900DeviceSupport deviceSupport)
{
@ -36,18 +37,25 @@ public class CasioGATTThread extends Thread {
}
@Override
public void run()
{
if(!mServer.initialize()) {
public void run() {
if (!mServer.initialize()) {
LOG.error("Error initializing CasioGATTServer. Has the context been set?");
return;
}
while(!mStopFlag) {
try {
wait(100);
} catch(Exception e)
{
long waitTime = 60 * 1000;
while (!mStopFlag) {
synchronized (waitObject) {
try {
waitObject.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mStopFlag) {
break;
}
}
mServer.close();
@ -55,6 +63,9 @@ public class CasioGATTThread extends Thread {
public void quit() {
mStopFlag = true;
synchronized (waitObject) {
waitObject.notify();
}
}

View File

@ -20,6 +20,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.net.Uri;
@ -45,23 +46,24 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceSupport.class);
public BluetoothGattCharacteristic mCasioCharact1 = null;
public BluetoothGattCharacteristic mCasioCharact2 = null;
public BluetoothGattCharacteristic mCasioCharact3 = null;
public BluetoothGattCharacteristic mCasioCharact4 = null;
public BluetoothGattCharacteristic mCasioCharact5 = null;
private CasioGATTThread mThread = null;
private ArrayList<BluetoothGattCharacteristic> mCasioCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
private CasioGATTThread mThread;
private CasioHandlerThread mHandlerThread = null;
private MusicSpec mBufferMusicSpec = null;
private MusicStateSpec mBufferMusicStateSpec = null;
private BluetoothGatt mBtGatt = null;
public CasioGB6900DeviceSupport() {
super(LOG);
addSupportedService(GattService.UUID_SERVICE_IMMEDIATE_ALERT);
addSupportedService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE);
addSupportedService(CasioGB6900Constants.ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_IMMEDIATE_ALERT_SERVICE_UUID);
@ -70,6 +72,8 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
addSupportedService(CasioGB6900Constants.WATCH_FEATURES_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_PHONE_ALERT_STATUS_SERVICE);
addSupportedService(CasioGB6900Constants.MORE_ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.TX_POWER_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.LINK_LOSS_SERVICE);
mThread = new CasioGATTThread(getContext(), this);
}
@ -80,6 +84,34 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
mThread.start();
}
@Override
public void dispose() {
LOG.info("Dispose");
close();
super.dispose();
}
private void close() {
if (mHandlerThread != null) {
mHandlerThread.quit();
mHandlerThread.interrupt();
mHandlerThread = null;
}
if(mThread != null) {
mThread.quit();
mThread.interrupt();
mThread = null;
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt) {
mBtGatt = gatt;
super.onServicesDiscovered(gatt);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
@ -87,24 +119,57 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
mCasioCharact1 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT);
mCasioCharact2 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT);
mCasioCharact3 = getCharacteristic(CasioGB6900Constants.FUNCTION_SWITCH_CHARACTERISTIC);
mCasioCharact4 = getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID);
mCasioCharact5 = getCharacteristic(CasioGB6900Constants.RINGER_CONTROL_POINT);
addCharacteristics();
builder.setGattCallback(this);
builder.notify(mCasioCharact1, true);
builder.notify(mCasioCharact2, true);
builder.notify(mCasioCharact3, true);
builder.notify(mCasioCharact4, true);
builder.notify(mCasioCharact5, true);
enableNotifications(builder, true);
configureWatch(builder);
LOG.info("Initialization Done");
return builder;
}
// FIXME: Replace hardcoded values by configuration
private void configureWatch(TransactionBuilder builder) {
if (mBtGatt == null)
return;
byte value[] = new byte[]{GattCharacteristic.MILD_ALERT};
BluetoothGattService llService = mBtGatt.getService(CasioGB6900Constants.LINK_LOSS_SERVICE);
BluetoothGattCharacteristic charact = llService.getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID);
builder.write(charact, value);
}
private void addCharacteristics() {
mCasioCharacteristics.clear();
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.FUNCTION_SWITCH_CHARACTERISTIC));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.RINGER_CONTROL_POINT));
}
public boolean enableNotifications(TransactionBuilder builder, boolean enable) {
for(BluetoothGattCharacteristic charact : mCasioCharacteristics) {
builder.notify(charact, enable);
}
return true;
}
public void readTxPowerLevel() {
try {
TransactionBuilder builder = performInitialized("readTxPowerLevel");
builder.read(getCharacteristic(CasioGB6900Constants.TX_POWER_LEVEL_CHARACTERISTIC_UUID));
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("readTxPowerLevel failed: " + e.getMessage());
}
}
private void writeCasioCurrentTime(TransactionBuilder builder) {
byte[] arr = new byte[10];
Calendar cal = Calendar.getInstance();
@ -172,6 +237,10 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
{
case (byte) 1:
LOG.info("Initialization done, setting state to INITIALIZED");
if(mHandlerThread == null) {
mHandlerThread = new CasioHandlerThread(getDevice(), getContext(), this);
mHandlerThread.start();
}
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
handled = true;
@ -195,7 +264,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
{
TransactionBuilder builder = createTransactionBuilder("writeCasioCurrentTime");
writeCasioCurrentTime(builder);
performImmediately(builder);
performConnected(builder.getTransaction());
handled = true;
} catch (IOException e) {
LOG.warn("handleTimeRequests::writeCasioCurrentTime failed: " + e.getMessage());
@ -206,7 +275,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
{
TransactionBuilder builder = createTransactionBuilder("writeCasioLocalTimeInformation");
writeCasioLocalTimeInformation(builder);
performImmediately(builder);
performConnected(builder.getTransaction());
handled = true;
} catch (IOException e) {
LOG.warn("handleTimeRequests::writeCasioLocalTimeInformation failed: " + e.getMessage());
@ -221,7 +290,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
{
TransactionBuilder builder = createTransactionBuilder("writeCasioVirtualServerFeature");
writeCasioVirtualServerFeature(builder);
performImmediately(builder);
performConnected(builder.getTransaction());
} catch (IOException e) {
LOG.warn("handleServerFeatureRequests failed: " + e.getMessage());
}
@ -251,15 +320,35 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
return handled;
}
@Override
public boolean onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if(data.length == 0)
return true;
if(characteristicUUID.equals(CasioGB6900Constants.TX_POWER_LEVEL_CHARACTERISTIC_UUID)) {
String str = "onCharacteristicRead: Received power level: ";
for(int i=0; i<data.length; i++) {
str += String.format("0x%1x ", data[i]);
}
LOG.info(str);
}
else {
return super.onCharacteristicRead(gatt, characteristic, status);
}
return true;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
boolean handled = false;
if (super.onCharacteristicChanged(gatt, characteristic)) {
return true;
}
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
@ -295,6 +384,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
if(!handled) {
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
return super.onCharacteristicChanged(gatt, characteristic);
}
return true;
}
@ -316,7 +406,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
builder.write(getCharacteristic(CasioGB6900Constants.ALERT_CHARACTERISTIC_UUID), msg);
LOG.info("Showing notification, title: " + title + " message (not sent): " + message);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("showNotification failed: " + e.getMessage());
}
@ -364,7 +454,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("SetTime");
writeCasioLocalTimeInformation(builder);
writeCasioCurrentTime(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(IOException e) {
LOG.warn("onSetTime failed: " + e.getMessage());
}
@ -421,7 +511,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
arr[i+3] = bInfo[i];
}
builder.write(getCharacteristic(CasioGB6900Constants.MORE_ALERT_FOR_LONG_UUID), arr);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("sendMusicInfo failed: " + e.getMessage());
}

View File

@ -0,0 +1,120 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
public class CasioHandlerThread extends GBDeviceIoThread {
private static final Logger LOG = LoggerFactory.getLogger(CasioHandlerThread.class);
private boolean mQuit = false;
private CasioGB6900DeviceSupport mDeviceSupport;
private final Object waitObject = new Object();
//private CasioGATTServer mServer = null;
private int TX_PERIOD = 60;
private Calendar mTxTime = GregorianCalendar.getInstance();
public CasioHandlerThread(GBDevice gbDevice, Context context, CasioGB6900DeviceSupport deviceSupport) {
super(gbDevice, context);
LOG.info("Initializing Casio Handler Thread");
mQuit = false;
//mServer = new CasioGATTServer(context, deviceSupport);
mDeviceSupport = deviceSupport;
}
@Override
public void run() {
mQuit = false;
/*
if(!mServer.initialize()) {
LOG.error("Error initializing CasioGATTServer. Has the context been set?");
return;
}
*/
long waitTime = TX_PERIOD * 1000;
while (!mQuit) {
if (waitTime > 0) {
synchronized (waitObject) {
try {
waitObject.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (mQuit) {
break;
}
if (gbDevice.getState() == GBDevice.State.NOT_CONNECTED) {
quit();
}
Calendar now = GregorianCalendar.getInstance();
if (now.compareTo(mTxTime) > 0) {
requestTxPowerLevel();
}
now = GregorianCalendar.getInstance();
waitTime = mTxTime.getTimeInMillis() - now.getTimeInMillis();
}
}
public void requestTxPowerLevel() {
try {
mDeviceSupport.readTxPowerLevel();
} catch(Exception e) {
}
mTxTime = GregorianCalendar.getInstance();
mTxTime.add(Calendar.SECOND, TX_PERIOD);
synchronized (waitObject) {
waitObject.notify();
}
}
@Override
public void quit() {
LOG.info("CasioHandlerThread: Quit Handler Thread");
mQuit = true;
synchronized (waitObject) {
waitObject.notify();
}
}
}