gt:             if (supportedServices.contains(service.getUuid())) {
context: /*  Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
      Gobbetti, JohnnySun
  
      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.btle;
  
  import android.bluetooth.BluetoothGatt;
  import android.bluetooth.BluetoothGattCharacteristic;
  import android.bluetooth.BluetoothGattDescriptor;
  import android.bluetooth.BluetoothGattService;
  
  import org.slf4j.Logger;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Set;
  import java.util.UUID;
  
  import nodomain.freeyourgadget.gadgetbridge.Logging;
  import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
  import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
  import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
  import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
  
  /**
   * Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka
   * Bluetooth Smart.
   * <p/>
   * The connection to the device and all communication is made with a generic {@link BtLEQueue}.
   * Messages to the device are encoded as {@link BtLEAction actions} that are grouped with a
   * {@link Transaction} and sent via {@link BtLEQueue}.
   *
   * @see TransactionBuilder
   * @see BtLEQueue
   */
  public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback {
      private BtLEQueue mQueue;
      private Map<UUID, BluetoothGattCharacteristic> mAvailableCharacteristics;
      private final Set<UUID> mSupportedServices = new HashSet<>(4);
      private Logger logger;
  
      private final List<AbstractBleProfile<?>> mSupportedProfiles = new ArrayList<>();
      public static final String BASE_UUID = "0000%s-0000-1000-8000-00805f9b34fb"; //this is common for all BTLE devices. see http://stackoverflow.com/questions/18699251/finding-out-android-bluetooth-le-gatt-profiles
      private final Object characteristicsMonitor = new Object();
  
      public AbstractBTLEDeviceSupport(Logger logger) {
          this.logger = logger;
          if (logger == null) {
              throw new IllegalArgumentException("logger must not be null");
          }
      }
  
      @Override
      public boolean connect() {
          if (mQueue == null) {
              mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, getContext());
              mQueue.setAutoReconnect(getAutoReconnect());
          }
          return mQueue.connect();
      }
  
      @Override
      public void setAutoReconnect(boolean enable) {
          super.setAutoReconnect(enable);
          if (mQueue != null) {
              mQueue.setAutoReconnect(enable);
          }
      }
  
      /**
       * Subclasses should populate the given builder to initialize the device (if necessary).
       *
       * @param builder
       * @return the same builder as passed as the argument
       */
      protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
          return builder;
      }
  
      @Override
      public void dispose() {
          if (mQueue != null) {
              mQueue.dispose();
              mQueue = null;
          }
      }
  
      public TransactionBuilder createTransactionBuilder(String taskName) {
          return new TransactionBuilder(taskName);
      }
  
      /**
       * Send commands like this to the device:
       * <p>
       * <code>perform("sms notification").write(someCharacteristic, someByteArray).queue(getQueue());</code>
       * </p>
       * TODO: support orchestration of multiple reads and writes depending on returned values
       *
       * @see #performConnected(Transaction)
       * @see #initializeDevice(TransactionBuilder)
       */
      public TransactionBuilder performInitialized(String taskName) throws IOException {
          if (!isConnected()) {
              if (!connect()) {
                  throw new IOException("1: Unable to connect to device: " + getDevice());
              }
          }
          if (!isInitialized()) {
              // first, add a transaction that performs device initialization
              TransactionBuilder builder = createTransactionBuilder("Initialize device");
              builder.add(new CheckInitializedAction(gbDevice));
              initializeDevice(builder).queue(getQueue());
          }
          return createTransactionBuilder(taskName);
      }
  
      /**
       * @param transaction
       * @throws IOException
       * @see {@link #performInitialized(String)}
       */
      public void performConnected(Transaction transaction) throws IOException {
          if (!isConnected()) {
              if (!connect()) {
                  throw new IOException("2: Unable to connect to device: " + getDevice());
              }
          }
          getQueue().add(transaction);
      }
  
      /**
       * Performs the actions of the given transaction as soon as possible,
       * that is, before any other queued transactions, but after the actions
       * of the currently executing transaction.
       * @param builder
       */
      public void performImmediately(TransactionBuilder builder) throws IOException {
          if (!isConnected()) {
              throw new IOException("Not connected to device: " + getDevice());
          }
          getQueue().insert(builder.getTransaction());
      }
  
      public BtLEQueue getQueue() {
          return mQueue;
      }
  
      /**
       * Subclasses should call this method to add services they support.
       * Only supported services will be queried for characteristics.
       *
       * @param aSupportedService
       * @see #getCharacteristic(UUID)
       */
      protected void addSupportedService(UUID aSupportedService) {
          mSupportedServices.add(aSupportedService);
      }
  
      protected void addSupportedProfile(AbstractBleProfile<?> profile) {
          mSupportedProfiles.add(profile);
      }
  
      /**
       * Returns the characteristic matching the given UUID. Only characteristics
       * are returned whose service is marked as supported.
       *
       * @param uuid
       * @return the characteristic for the given UUID or <code>null</code>
       * @see #addSupportedService(UUID)
       */
      public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
          synchronized (characteristicsMonitor) {
              if (mAvailableCharacteristics == null) {
                  return null;
              }
              return mAvailableCharacteristics.get(uuid);
          }
      }
  
      private void gattServicesDiscovered(List<BluetoothGattService> discoveredGattServices) {
          if (discoveredGattServices == null) {
              logger.warn("No gatt services discovered: null!");
              return;
          }
          Set<UUID> supportedServices = getSupportedServices();
          Map<UUID, BluetoothGattCharacteristic> newCharacteristics = new HashMap<>();
          for (BluetoothGattService service : discoveredGattServices) {
  