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

org.robolectric.shadows.ShadowBluetoothManager Maven / Gradle / Ivy

package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.R;
import static android.os.Build.VERSION_CODES.S;
import static com.google.common.base.Preconditions.checkArgument;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothGatt;
import android.content.Context;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.ImmutableIntArray;
import java.util.ArrayList;
import java.util.List;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;

/** Shadow of {@link BluetoothManager} that makes the testing possible. */
@Implements(value = BluetoothManager.class, minSdk = JELLY_BEAN_MR2)
public class ShadowBluetoothManager {
  private static final ImmutableIntArray VALID_STATES =
      ImmutableIntArray.of(
          BluetoothProfile.STATE_CONNECTED,
          BluetoothProfile.STATE_CONNECTING,
          BluetoothProfile.STATE_DISCONNECTED,
          BluetoothProfile.STATE_DISCONNECTING);

  private final ArrayList bleDevices = new ArrayList<>();

  /** Used for storing registered {@link BluetoothDevice} with the specified profile and state. */
  @AutoValue
  abstract static class BleDevice {
    /** {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}. */
    abstract int profile();
    /**
     * State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED}, {@link
     * BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED} and {@link
     * BluetoothProfile#STATE_DISCONNECTING}.
     */
    abstract int state();
    /** The remote bluetooth device. */
    abstract BluetoothDevice device();

    static Builder builder() {
      return new AutoValue_ShadowBluetoothManager_BleDevice.Builder();
    }

    @AutoValue.Builder
    abstract static class Builder {
      abstract Builder setProfile(int profile);

      abstract Builder setState(int state);

      abstract Builder setDevice(BluetoothDevice device);

      abstract BleDevice build();
    }
  }

  /**
   * Get the {@link BluetoothAdapter} for this device.
   *
   * @return BluetoothAdapter instance
   */
  @Implementation
  protected BluetoothAdapter getAdapter() {
    return BluetoothAdapter.getDefaultAdapter();
  }

  @Implementation
  protected List getDevicesMatchingConnectionStates(int profile, int[] states) {
    checkArgument(isProfileValid(profile), "Profile not supported: %s", profile);

    if (states == null) {
      return ImmutableList.of();
    }

    ImmutableList.Builder result = ImmutableList.builder();
    ImmutableIntArray stateArray = ImmutableIntArray.copyOf(states);
    for (BleDevice ble : bleDevices) {
      if (ble.profile() == profile && stateArray.contains(ble.state())) {
        result.add(ble.device());
      }
    }
    return result.build();
  }

  /**
   * Add a remote bluetooth device that will be served by {@link
   * BluetoothManager#getDevicesMatchingConnectionStates} for the specified profile and states of
   * the profile connection.
   *
   * @param profile {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
   * @param state State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED},
   *     {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED} and
   *     {@link BluetoothProfile#STATE_DISCONNECTING}.
   * @param device The remote bluetooth device.
   */
  public void addDevice(int profile, int state, BluetoothDevice device) {
    if (isProfileValid(profile) && VALID_STATES.contains(state)) {
      bleDevices.add(
          BleDevice.builder().setProfile(profile).setState(state).setDevice(device).build());
    }
  }

  private boolean isProfileValid(int profile) {
    return profile == BluetoothProfile.GATT || profile == BluetoothProfile.GATT_SERVER;
  }

  @Implementation(minSdk = O, maxSdk = R)
  protected BluetoothGattServer openGattServer(
      Context context, BluetoothGattServerCallback callback, int transport) {
    return createGattServer(context, callback, transport);
  }

  /**
   * Overrides behavior of {@link openGattServer} and returns {@link ShadowBluetoothGattServer}
   * after creating and using a nullProxy for {@link IBluetoothGatt}.
   */
  @Implementation(minSdk = S)
  protected BluetoothGattServer openGattServer(
      Context context, BluetoothGattServerCallback callback, int transport, boolean eattSupport) {
    return createGattServer(context, callback, transport);
  }

  private BluetoothGattServer createGattServer(
      Context unusedContext, BluetoothGattServerCallback callback, int transport) {
    IBluetoothGatt iGatt = ReflectionHelpers.createNullProxy(IBluetoothGatt.class);
    BluetoothGattServer gattServer;

    if (RuntimeEnvironment.getApiLevel() <= R) {
      gattServer =
          ReflectionHelpers.callConstructor(
              BluetoothGattServer.class,
              ClassParameter.from(IBluetoothGatt.class, iGatt),
              ClassParameter.from(int.class, transport));
    } else {
      gattServer =
          ReflectionHelpers.callConstructor(
              BluetoothGattServer.class,
              ClassParameter.from(IBluetoothGatt.class, iGatt),
              ClassParameter.from(int.class, transport),
              ClassParameter.from(BluetoothAdapter.class, this.getAdapter()));
    }
    PerfStatsCollector.getInstance().incrementCount("constructShadowBluetoothGattServer");
    ShadowBluetoothGattServer shadowBluetoothGattServer = Shadow.extract(gattServer);
    shadowBluetoothGattServer.setGattServerCallback(callback);
    return gattServer;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy