chromium/device/bluetooth/test/bluetooth_test_android.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/bluetooth/test/bluetooth_test_android.h"

#include <iterator>
#include <sstream>

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "device/bluetooth/android/wrappers.h"
#include "device/bluetooth/bluetooth_adapter_android.h"
#include "device/bluetooth/bluetooth_device_android.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_android.h"
#include "device/bluetooth/bluetooth_remote_gatt_descriptor_android.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_android.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "device/bluetooth_test_jni_headers/Fakes_jni.h"

using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;

namespace device {

BluetoothTestAndroid::BluetoothTestAndroid() {
}

BluetoothTestAndroid::~BluetoothTestAndroid() {
}

void BluetoothTestAndroid::SetUp() {
  // Set the permission to true so that we can use the API.
  Java_Fakes_setLocationServicesState(AttachCurrentThread(),
                                      true /* isEnabled */);
  Java_Fakes_initFakeThreadUtilsWrapper(AttachCurrentThread(),
                                        reinterpret_cast<intptr_t>(this));
}

void BluetoothTestAndroid::TearDown() {
  // Unit tests are able to reset the adapter themselves (e.g.
  // BluetoothTest::TogglePowerFakeAdapter_DestroyWithPending), so this check is
  // necessary.
  if (adapter_) {
    BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
    for (auto* device : devices) {
      DeleteDevice(device);
    }
  }
  EXPECT_EQ(0, gatt_open_connections_);

  BluetoothTestBase::TearDown();
}

static void RunJavaRunnable(
    const base::android::ScopedJavaGlobalRef<jobject>& runnable_ref) {
  Java_Fakes_runRunnable(AttachCurrentThread(), runnable_ref);
}

void BluetoothTestAndroid::PostTaskFromJava(
    JNIEnv* env,
    const JavaParamRef<jobject>& runnable) {
  base::android::ScopedJavaGlobalRef<jobject> runnable_ref;
  // ScopedJavaGlobalRef does not hold onto the env reference, so it is safe to
  // use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before
  // running the Runnable.
  runnable_ref.Reset(env, runnable);
  task_environment_.GetMainThreadTaskRunner()->PostTask(
      FROM_HERE, base::BindOnce(&RunJavaRunnable, runnable_ref));
}

bool BluetoothTestAndroid::PlatformSupportsLowEnergy() {
  return true;
}

void BluetoothTestAndroid::InitWithDefaultAdapter() {
  adapter_ = BluetoothAdapterAndroid::Create(
      BluetoothAdapterWrapper_CreateWithDefaultAdapter());
}

void BluetoothTestAndroid::InitWithoutDefaultAdapter() {
  adapter_ = BluetoothAdapterAndroid::Create(nullptr);
}

void BluetoothTestAndroid::InitWithFakeAdapter() {
  j_fake_bluetooth_adapter_.Reset(Java_FakeBluetoothAdapter_create(
      AttachCurrentThread(), reinterpret_cast<intptr_t>(this)));

  adapter_ = BluetoothAdapterAndroid::Create(j_fake_bluetooth_adapter_).get();
}

bool BluetoothTestAndroid::DenyPermission() {
  Java_FakeBluetoothAdapter_setFakeContextLocationPermission(
      AttachCurrentThread(), j_fake_bluetooth_adapter_, false);
  return true;
}

BluetoothDevice* BluetoothTestAndroid::SimulateLowEnergyDevice(
    int device_ordinal) {
  TestBluetoothAdapterObserver observer(adapter_);
  Java_FakeBluetoothAdapter_simulateLowEnergyDevice(
      AttachCurrentThread(), j_fake_bluetooth_adapter_, device_ordinal);
  return observer.last_device();
}

void BluetoothTestAndroid::RememberDeviceForSubsequentAction(
    BluetoothDevice* device) {
  BluetoothDeviceAndroid* device_android =
      static_cast<BluetoothDeviceAndroid*>(device);

  Java_FakeBluetoothDevice_rememberDeviceForSubsequentAction(
      base::android::AttachCurrentThread(), device_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateLocationServicesOff() {
  Java_Fakes_setLocationServicesState(AttachCurrentThread(),
                                      false /* isEnabled */);
}

void BluetoothTestAndroid::ForceIllegalStateException() {
  Java_FakeBluetoothAdapter_forceIllegalStateException(
      AttachCurrentThread(), j_fake_bluetooth_adapter_);
}

void BluetoothTestAndroid::SimulateGattConnection(BluetoothDevice* device) {
  BluetoothDeviceAndroid* device_android =
      static_cast<BluetoothDeviceAndroid*>(device);

  Java_FakeBluetoothDevice_connectionStateChange(
      AttachCurrentThread(), device_android->GetJavaObject(),
      0,      // android.bluetooth.BluetoothGatt.GATT_SUCCESS
      true);  // connected
}

void BluetoothTestAndroid::SimulateGattConnectionError(
    BluetoothDevice* device,
    BluetoothDevice::ConnectErrorCode) {
  BluetoothDeviceAndroid* device_android =
      static_cast<BluetoothDeviceAndroid*>(device);

  Java_FakeBluetoothDevice_connectionStateChange(
      AttachCurrentThread(), device_android->GetJavaObject(),
      // TODO(ortuno): Add all types of errors Android can produce. For now we
      // just return a timeout error.
      // http://crbug.com/578191
      0x08,    // Connection Timeout from Bluetooth Spec.
      false);  // connected
}

void BluetoothTestAndroid::SimulateGattDisconnection(BluetoothDevice* device) {
  BluetoothDeviceAndroid* device_android =
      static_cast<BluetoothDeviceAndroid*>(device);

  Java_FakeBluetoothDevice_connectionStateChange(
      AttachCurrentThread(), device_android->GetJavaObject(),
      0x13,    // Connection terminate by peer user from Bluetooth Spec.
      false);  // disconnected
}

void BluetoothTestAndroid::SimulateGattServicesDiscovered(
    BluetoothDevice* device,
    const std::vector<std::string>& uuids,
    const std::vector<std::string>& blocked_uuids) {
  DCHECK(blocked_uuids.empty()) << "Setting blocked_uuids unsupported.";
  BluetoothDeviceAndroid* device_android = nullptr;
  if (device) {
    device_android = static_cast<BluetoothDeviceAndroid*>(device);
  }

  // Join UUID strings into a single string.
  std::ostringstream uuids_space_delimited;
  base::ranges::copy(
      uuids, std::ostream_iterator<std::string>(uuids_space_delimited, " "));

  JNIEnv* env = base::android::AttachCurrentThread();
  Java_FakeBluetoothDevice_servicesDiscovered(
      env, device_android ? device_android->GetJavaObject() : nullptr,
      0,  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
      base::android::ConvertUTF8ToJavaString(env, uuids_space_delimited.str()));
}

void BluetoothTestAndroid::SimulateGattServicesDiscoveryError(
    BluetoothDevice* device) {
  BluetoothDeviceAndroid* device_android = nullptr;
  if (device) {
    device_android = static_cast<BluetoothDeviceAndroid*>(device);
  }

  Java_FakeBluetoothDevice_servicesDiscovered(
      AttachCurrentThread(),
      device_android ? device_android->GetJavaObject() : nullptr,
      0x00000101,  // android.bluetooth.BluetoothGatt.GATT_FAILURE
      nullptr);
}

void BluetoothTestAndroid::SimulateGattCharacteristic(
    BluetoothRemoteGattService* service,
    const std::string& uuid,
    int properties) {
  BluetoothRemoteGattServiceAndroid* service_android =
      static_cast<BluetoothRemoteGattServiceAndroid*>(service);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattService_addCharacteristic(
      env, service_android->GetJavaObject(),
      base::android::ConvertUTF8ToJavaString(env, uuid), properties);
}

void BluetoothTestAndroid::RememberCharacteristicForSubsequentAction(
    BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);

  Java_FakeBluetoothGattCharacteristic_rememberCharacteristicForSubsequentAction(
      base::android::AttachCurrentThread(),
      characteristic_android->GetJavaObject());
}

void BluetoothTestAndroid::RememberCCCDescriptorForSubsequentAction(
    BluetoothRemoteGattCharacteristic* characteristic) {
  remembered_ccc_descriptor_ =
      characteristic
          ->GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
                                     ClientCharacteristicConfigurationUuid())
          .at(0);
  DCHECK(remembered_ccc_descriptor_);
  RememberDescriptorForSubsequentAction(remembered_ccc_descriptor_);
}

void BluetoothTestAndroid::SimulateGattNotifySessionStarted(
    BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android = nullptr;
  if (characteristic) {
    descriptor_android = static_cast<BluetoothRemoteGattDescriptorAndroid*>(
        characteristic
            ->GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
                                       ClientCharacteristicConfigurationUuid())
            .at(0));
  }
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(),
      descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      0);  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
}

void BluetoothTestAndroid::SimulateGattNotifySessionStartError(
    BluetoothRemoteGattCharacteristic* characteristic,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android = nullptr;
  if (characteristic) {
    descriptor_android = static_cast<BluetoothRemoteGattDescriptorAndroid*>(
        characteristic
            ->GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
                                       ClientCharacteristicConfigurationUuid())
            .at(0));
  }
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(),
      descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code));
}

void BluetoothTestAndroid::SimulateGattNotifySessionStopped(
    BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android = nullptr;
  if (characteristic) {
    descriptor_android = static_cast<BluetoothRemoteGattDescriptorAndroid*>(
        characteristic
            ->GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
                                       ClientCharacteristicConfigurationUuid())
            .at(0));
  }
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(),
      descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      0);  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
}

void BluetoothTestAndroid::SimulateGattNotifySessionStopError(
    BluetoothRemoteGattCharacteristic* characteristic,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android = nullptr;
  if (characteristic) {
    descriptor_android = static_cast<BluetoothRemoteGattDescriptorAndroid*>(
        characteristic
            ->GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
                                       ClientCharacteristicConfigurationUuid())
            .at(0));
  }
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(),
      descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code));
}

void BluetoothTestAndroid::
    SimulateGattCharacteristicSetNotifyWillFailSynchronouslyOnce(
        BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattCharacteristic_setCharacteristicNotificationWillFailSynchronouslyOnce(
      env, characteristic_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateGattCharacteristicChanged(
    BluetoothRemoteGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattCharacteristic_valueChanged(
      env, characteristic_android ? characteristic_android->GetJavaObject()
                                  : nullptr,
      base::android::ToJavaByteArray(env, value));
}

void BluetoothTestAndroid::SimulateGattCharacteristicRead(
    BluetoothRemoteGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattCharacteristic_valueRead(
      env, characteristic_android ? characteristic_android->GetJavaObject()
                                  : nullptr,
      0,  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
      base::android::ToJavaByteArray(env, value));
}

void BluetoothTestAndroid::SimulateGattCharacteristicReadError(
    BluetoothRemoteGattCharacteristic* characteristic,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();
  std::vector<uint8_t> empty_value;

  Java_FakeBluetoothGattCharacteristic_valueRead(
      env, characteristic_android->GetJavaObject(),
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code),
      base::android::ToJavaByteArray(env, empty_value));
}

void BluetoothTestAndroid::
    SimulateGattCharacteristicReadWillFailSynchronouslyOnce(
        BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattCharacteristic_setReadCharacteristicWillFailSynchronouslyOnce(
      env, characteristic_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateGattCharacteristicWrite(
    BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  Java_FakeBluetoothGattCharacteristic_valueWrite(
      base::android::AttachCurrentThread(),
      characteristic_android ? characteristic_android->GetJavaObject()
                             : nullptr,
      0);  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
}

void BluetoothTestAndroid::SimulateGattCharacteristicWriteError(
    BluetoothRemoteGattCharacteristic* characteristic,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  Java_FakeBluetoothGattCharacteristic_valueWrite(
      base::android::AttachCurrentThread(),
      characteristic_android->GetJavaObject(),
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code));
}

void BluetoothTestAndroid::
    SimulateGattCharacteristicWriteWillFailSynchronouslyOnce(
        BluetoothRemoteGattCharacteristic* characteristic) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  Java_FakeBluetoothGattCharacteristic_setWriteCharacteristicWillFailSynchronouslyOnce(
      base::android::AttachCurrentThread(),
      characteristic_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateGattDescriptor(
    BluetoothRemoteGattCharacteristic* characteristic,
    const std::string& uuid) {
  BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
      static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattCharacteristic_addDescriptor(
      env, characteristic_android->GetJavaObject(),
      base::android::ConvertUTF8ToJavaString(env, uuid));
}

void BluetoothTestAndroid::RememberDescriptorForSubsequentAction(
    BluetoothRemoteGattDescriptor* descriptor) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);

  Java_FakeBluetoothGattDescriptor_rememberDescriptorForSubsequentAction(
      base::android::AttachCurrentThread(),
      descriptor_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateGattDescriptorRead(
    BluetoothRemoteGattDescriptor* descriptor,
    const std::vector<uint8_t>& value) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattDescriptor_valueRead(
      env, descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      0,  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
      base::android::ToJavaByteArray(env, value));
}

void BluetoothTestAndroid::SimulateGattDescriptorReadError(
    BluetoothRemoteGattDescriptor* descriptor,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  JNIEnv* env = base::android::AttachCurrentThread();
  std::vector<uint8_t> empty_value;

  Java_FakeBluetoothGattDescriptor_valueRead(
      env, descriptor_android->GetJavaObject(),
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code),
      base::android::ToJavaByteArray(env, empty_value));
}

void BluetoothTestAndroid::SimulateGattDescriptorReadWillFailSynchronouslyOnce(
    BluetoothRemoteGattDescriptor* descriptor) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  JNIEnv* env = base::android::AttachCurrentThread();

  Java_FakeBluetoothGattDescriptor_setReadDescriptorWillFailSynchronouslyOnce(
      env, descriptor_android->GetJavaObject());
}

void BluetoothTestAndroid::SimulateGattDescriptorWrite(
    BluetoothRemoteGattDescriptor* descriptor) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(),
      descriptor_android ? descriptor_android->GetJavaObject() : nullptr,
      0);  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
}

void BluetoothTestAndroid::SimulateGattDescriptorWriteError(
    BluetoothRemoteGattDescriptor* descriptor,
    BluetoothGattService::GattErrorCode error_code) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  Java_FakeBluetoothGattDescriptor_valueWrite(
      base::android::AttachCurrentThread(), descriptor_android->GetJavaObject(),
      BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code));
}

void BluetoothTestAndroid::SimulateGattDescriptorWriteWillFailSynchronouslyOnce(
    BluetoothRemoteGattDescriptor* descriptor) {
  BluetoothRemoteGattDescriptorAndroid* descriptor_android =
      static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor);
  Java_FakeBluetoothGattDescriptor_setWriteDescriptorWillFailSynchronouslyOnce(
      base::android::AttachCurrentThread(),
      descriptor_android->GetJavaObject());
}

void BluetoothTestAndroid::OnFakeBluetoothDeviceConnectGattCalled(JNIEnv* env) {
  gatt_open_connections_++;
  gatt_connection_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattDisconnect(JNIEnv* env) {
  gatt_disconnection_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattClose(JNIEnv* env) {
  gatt_open_connections_--;

  // close implies disconnect
  gatt_disconnection_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattDiscoverServices(JNIEnv* env) {
  gatt_discovery_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattSetCharacteristicNotification(
    JNIEnv* env) {
  gatt_notify_characteristic_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattReadCharacteristic(JNIEnv* env) {
  gatt_read_characteristic_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattWriteCharacteristic(
    JNIEnv* env,
    const JavaParamRef<jbyteArray>& value) {
  gatt_write_characteristic_attempts_++;
  base::android::JavaByteArrayToByteVector(env, value, &last_write_value_);
}

void BluetoothTestAndroid::OnFakeBluetoothGattReadDescriptor(JNIEnv* env) {
  gatt_read_descriptor_attempts_++;
}

void BluetoothTestAndroid::OnFakeBluetoothGattWriteDescriptor(
    JNIEnv* env,
    const JavaParamRef<jbyteArray>& value) {
  gatt_write_descriptor_attempts_++;
  base::android::JavaByteArrayToByteVector(env, value, &last_write_value_);
}

void BluetoothTestAndroid::OnFakeAdapterStateChanged(
    JNIEnv* env,
    const bool powered) {
  // Delegate to the real implementation if the adapter is still alive.
  if (adapter_) {
    static_cast<BluetoothAdapterAndroid*>(adapter_.get())
        ->OnAdapterStateChanged(
            env, base::android::JavaParamRef<jobject>(nullptr), powered);
  }
}

}  // namespace device