All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sputnikdev.bluetooth.manager.impl.DeviceGovernorImpl Maven / Gradle / Ivy

package org.sputnikdev.bluetooth.manager.impl;

/*-
 * #%L
 * org.sputnikdev:bluetooth-manager
 * %%
 * Copyright (C) 2017 Sputnik Dev
 * %%
 * 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.
 * #L%
 */

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sputnikdev.bluetooth.URL;
import org.sputnikdev.bluetooth.manager.AdapterGovernor;
import org.sputnikdev.bluetooth.manager.BluetoothGovernor;
import org.sputnikdev.bluetooth.manager.BluetoothObjectType;
import org.sputnikdev.bluetooth.manager.BluetoothObjectVisitor;
import org.sputnikdev.bluetooth.manager.BluetoothSmartDeviceListener;
import org.sputnikdev.bluetooth.manager.CharacteristicGovernor;
import org.sputnikdev.bluetooth.manager.DeviceGovernor;
import org.sputnikdev.bluetooth.manager.GattCharacteristic;
import org.sputnikdev.bluetooth.manager.GattService;
import org.sputnikdev.bluetooth.manager.GenericBluetoothDeviceListener;
import org.sputnikdev.bluetooth.manager.NotReadyException;

/**
 *
 * @author Vlad Kolotov
 */
class DeviceGovernorImpl extends BluetoothObjectGovernor implements DeviceGovernor {

    public static final int BLE_BLUETOOTH_CLASS = 0;

    private Logger logger = LoggerFactory.getLogger(DeviceGovernorImpl.class);

    private final List genericBluetoothDeviceListeners = new ArrayList<>();
    private final List bluetoothSmartDeviceListeners = new ArrayList<>();
    private ConnectionNotification connectionNotification;
    private BlockedNotification blockedNotification;
    private ServicesResolvedNotification servicesResolvedNotification;
    private RSSINotification rssiNotification;
    private boolean connectionControl;
    private boolean blockedControl;
    private boolean online = false;
    private int onlineTimeout = 20;

    DeviceGovernorImpl(BluetoothManagerImpl bluetoothManager, URL url) {
        super(bluetoothManager, url);
    }

    @Override
    void init(Device device) {
        enableRSSINotifications(device);
        enableConnectionNotifications(device);
        enableServicesResolvedNotifications(device);
        enableBlockedNotifications(device);
    }

    @Override
    void update(Device device) {
        AdapterGovernor adapterGovernor = bluetoothManager.getAdapterGovernor(getURL());
        if (adapterGovernor != null && adapterGovernor.isReady() && adapterGovernor.isPowered()) {
            updateBlocked(device);
            if (!blockedControl && isBleEnabled()) {
                boolean connected = updateConnected(device);
                if (connected) {
                    updateOnline(true);
                    updateCharacteristics();
                    return;
                }
            }
        }
        updateOnline(isOnline());
    }

    @Override
    void reset(Device device) {
        logger.info("Resetting device governor: " + getURL());
        updateOnline(false);
        logger.info("Disable device notifications: " + getURL());
        device.disableConnectedNotifications();
        device.disableServicesResolvedNotifications();
        device.disableRSSINotifications();
        device.disableBlockedNotifications();
        logger.info("Disconnecting device: " + getURL());
        if (device.isConnected()) {
            device.disconnect();
            notifyConnected(false);
            resetCharacteristics();
        }
        connectionNotification = null;
        servicesResolvedNotification = null;
        rssiNotification = null;
        blockedNotification = null;
        logger.info("Resetting device governor completed: " + getURL());
    }

    @Override
    Device findBluetoothObject() {
        return BluetoothObjectFactory.getDefault().getDevice(getURL());
    }

    @Override
    public int getBluetoothClass() throws NotReadyException {
        return getBluetoothObject().getBluetoothClass();
    }

    @Override
    public boolean isBleEnabled() throws NotReadyException {
        return getBluetoothClass() == BLE_BLUETOOTH_CLASS;
    }

    @Override
    public String getName() throws NotReadyException {
        return getBluetoothObject().getName();
    }

    @Override
    public String getAlias() throws NotReadyException {
        return getBluetoothObject().getAlias();
    }

    @Override
    public void setAlias(String alias) throws NotReadyException {
        getBluetoothObject().setAlias(alias);
    }

    @Override
    public String getDisplayName() throws NotReadyException {
        String alias = getAlias();
        return alias != null ? alias : getName();
    }

    @Override
    public boolean getConnectionControl() {
        return connectionControl;
    }

    public void setConnectionControl(boolean connectionControl) {
        this.connectionControl = connectionControl;
    }

    @Override
    public boolean getBlockedControl() {
        return this.blockedControl;
    }

    @Override
    public void setBlockedControl(boolean blockedControl) {
        this.blockedControl = blockedControl;
    }

    @Override
    public boolean isConnected() throws NotReadyException {
        return getBluetoothObject().isConnected();
    }

    @Override
    public boolean isBlocked() throws NotReadyException {
        return getBluetoothObject().isBlocked();
    }

    @Override
    public boolean isOnline() {
        return Instant.now().minusSeconds(this.onlineTimeout).isBefore(this.getLastActivity().toInstant());
    }

    @Override
    public int getOnlineTimeout() {
        return this.onlineTimeout;
    }

    @Override
    public void setOnlineTimeout(int onlineTimeout) {
        this.onlineTimeout = onlineTimeout;
    }

    @Override
    public short getRSSI() throws NotReadyException {
        return getBluetoothObject().getRSSI();
    }

    @Override
    public void addBluetoothSmartDeviceListener(BluetoothSmartDeviceListener bluetoothSmartDeviceListener) {
        synchronized (this.bluetoothSmartDeviceListeners) {
            this.bluetoothSmartDeviceListeners.add(bluetoothSmartDeviceListener);
        }
    }

    @Override
    public void removeBluetoothSmartDeviceListener(BluetoothSmartDeviceListener bluetoothSmartDeviceListener) {
        synchronized (this.bluetoothSmartDeviceListeners) {
            this.bluetoothSmartDeviceListeners.remove(bluetoothSmartDeviceListener);
        }
    }

    @Override
    public void addGenericBluetoothDeviceListener(GenericBluetoothDeviceListener genericBluetoothDeviceListener) {
        synchronized (this.genericBluetoothDeviceListeners) {
            this.genericBluetoothDeviceListeners.add(genericBluetoothDeviceListener);
        }
    }

    @Override
    public void removeGenericBluetoothDeviceListener(GenericBluetoothDeviceListener listener) {
        synchronized (this.genericBluetoothDeviceListeners) {
            this.genericBluetoothDeviceListeners.remove(listener);
        }
    }

    @Override
    public Map> getServicesToCharacteristicsMap() throws NotReadyException {
        Map> services = new HashMap<>();
        for (Service service : getBluetoothObject().getServices()) {
            URL serviceURL = service.getURL();
            services.put(serviceURL, (List) bluetoothManager.getGovernors(service.getCharacteristics()));
        }
        return services;
    }

    @Override
    public List getCharacteristics() throws NotReadyException {
        return BluetoothManagerUtils.getURLs(getAllCharacteristics());
    }

    @Override
    public List getCharacteristicGovernors() throws NotReadyException {
        return (List) bluetoothManager.getGovernors(getAllCharacteristics());
    }

    @Override
    public String toString() {
        String result = "[Device] " + getURL();
        if (isReady()) {
            result += " [" + getDisplayName() + "]";
        }
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        BluetoothGovernor that = (BluetoothGovernor) o;
        return url.equals(that.getURL());
    }

    @Override
    public int hashCode() {
        int result = url.hashCode();
        result = 31 * result + url.hashCode();
        return result;
    }

    @Override
    public BluetoothObjectType getType() {
        return BluetoothObjectType.DEVICE;
    }

    @Override
    public void accept(BluetoothObjectVisitor visitor) throws Exception {
        visitor.visit(this);
    }

    void notifyConnected(boolean connected) {
        synchronized (this.bluetoothSmartDeviceListeners) {
            for (BluetoothSmartDeviceListener listener : this.bluetoothSmartDeviceListeners) {
                try {
                    if (connected) {
                        listener.connected();
                    } else {
                        listener.disconnected();
                    }
                } catch (Exception ex) {
                    logger.error("Execution error of a connection listener", ex);
                }
            }
        }
    }

    void notifyBlocked(boolean blocked) {
        synchronized (this.genericBluetoothDeviceListeners) {
            for (GenericBluetoothDeviceListener listener : this.genericBluetoothDeviceListeners) {
                try {
                    listener.blocked(blocked);
                } catch (Exception ex) {
                    logger.error("Execution error of a Blocked listener", ex);
                }
            }
        }
    }

    void notifyServicesResolved(boolean resolved) {
        synchronized (this.bluetoothSmartDeviceListeners) {
            for (BluetoothSmartDeviceListener listener : this.bluetoothSmartDeviceListeners) {
                try {
                    if (resolved) {
                        listener.servicesResolved(getResolvedServices());
                    } else {
                        listener.servicesUnresolved();
                    }
                } catch (Exception ex) {
                    logger.error("Execution error of a service resolved listener", ex);
                }
            }
        }
    }

    void notifyRSSIChanged(Short rssi) {
        synchronized (this.genericBluetoothDeviceListeners) {
            for (GenericBluetoothDeviceListener listener : this.genericBluetoothDeviceListeners) {
                try {
                    listener.rssiChanged(rssi != null ? rssi : 0);
                } catch (Exception ex) {
                    logger.error("Execution error of a RSSI listener", ex);
                }
            }
        }
    }

    void notifyOnline(boolean online) {
        synchronized (this.genericBluetoothDeviceListeners) {
            for (GenericBluetoothDeviceListener listener : this.genericBluetoothDeviceListeners) {
                try {
                    if (online) {
                        listener.online();
                    } else {
                        listener.offline();
                    }
                } catch (Exception ex) {
                    logger.error("Execution error of an online listener", ex);
                }
            }
        }
    }

    private List getAllCharacteristics() throws NotReadyException {
        List characteristics = new ArrayList<>();
        for (Service service : getBluetoothObject().getServices()) {
            characteristics.addAll(service.getCharacteristics());
        }
        return characteristics;
    }

    private void enableConnectionNotifications(Device bluetoothDevice) {
        if (this.connectionNotification == null) {
            logger.info("Enabling connection notification: {} ", getURL());
            this.connectionNotification = new ConnectionNotification();
            bluetoothDevice.enableConnectedNotifications(connectionNotification);
        }
    }

    private void enableBlockedNotifications(Device bluetoothDevice) {
        if (this.blockedNotification == null) {
            logger.info("Enabling blocked notification: {} ", getURL());
            this.blockedNotification = new BlockedNotification();
            bluetoothDevice.enableBlockedNotifications(blockedNotification);
        }
    }

    private void enableServicesResolvedNotifications(Device bluetoothDevice) {
        if (this.servicesResolvedNotification == null) {
            logger.info("Enabling services resolved notification: {} ", getURL());
            this.servicesResolvedNotification = new ServicesResolvedNotification();
            bluetoothDevice.enableServicesResolvedNotifications(this.servicesResolvedNotification);
        }
    }

    private void enableRSSINotifications(Device bluetoothDevice) {
        if (rssiNotification == null) {
            logger.info("Enabling RSSI notification: {} ", getURL());
            this.rssiNotification = new RSSINotification();
            bluetoothDevice.enableRSSINotifications(rssiNotification);
        }
    }

    private List getResolvedServices() {
        logger.info("Services resolved: " + getURL());
        List services = new ArrayList<>();
        Device device = getBluetoothObject();
        for (Service service : device.getServices()) {
            List characteristics = new ArrayList<>();
            for (Characteristic characteristic : service.getCharacteristics()) {
                characteristics.add(convert(characteristic));
            }
            services.add(new GattService(service.getURL(), characteristics));
        }
        return services;
    }

    private void updateCharacteristics() {
        bluetoothManager.updateDescendants(url);
    }

    private void resetCharacteristics() {
        bluetoothManager.resetDescendants(url);
    }

    private GattCharacteristic convert(Characteristic characteristic) {
        return new GattCharacteristic(characteristic.getURL(), characteristic.getFlags());
    }

    private void updateOnline(boolean online) {
        if (online != this.online) {
            notifyOnline(online);
        }
        this.online = online;
    }


    private void updateBlocked(Device device) {
        if (blockedControl != device.isBlocked()) {
            device.setBlocked(blockedControl);
        }
    }

    private boolean updateConnected(Device device) {
        boolean connected = device.isConnected();
        if (connectionControl && !connected) {
            connected = device.connect();
        } else if (!connectionControl && connected) {
            device.disconnect();
            resetCharacteristics();
            connected = false;
        }
        return connected;
    }

    private class ConnectionNotification implements Notification {
        @Override
        public void notify(Boolean connected) {
            logger.info("Connected (notification): " + getURL() + " " + connected);
            notifyConnected(connected);
            updateLastChanged();
        }
    }

    private class BlockedNotification implements Notification {
        @Override
        public void notify(Boolean blocked) {
            logger.info("Blocked (notification): " + getURL() + " " + blocked);
            notifyBlocked(blocked);
            updateLastChanged();
        }
    }

    private class ServicesResolvedNotification implements Notification {
        @Override
        public void notify(Boolean serviceResolved) {
            logger.info("Services resolved (notification): " + serviceResolved);

            if (serviceResolved) {
                updateCharacteristics();
            } else {
                logger.info("Resetting characteristic governors due to services unresolved event");
                resetCharacteristics();
            }

            notifyServicesResolved(serviceResolved);
            updateLastChanged();
        }
    }

    private class RSSINotification implements Notification {
        @Override
        public void notify(Short rssi) {
            notifyRSSIChanged(rssi);
            updateLastChanged();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy