blob: 958f3655b8929e04066dc0f31bc87b8555f8abbb [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/bluetooth_adapter_android.h"
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "device/bluetooth/android/wrappers.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_device_android.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
#include "jni/ChromeBluetoothAdapter_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::AppendJavaStringArrayToStringVector;
using base::android::JavaParamRef;
namespace {
// The poll interval in ms when there is no active discovery. This
// matches the max allowed advertisting interval for connectable
// devices.
enum { kPassivePollInterval = 11000 };
// The poll interval in ms when there is an active discovery.
enum { kActivePollInterval = 1000 };
// The delay in ms to wait before purging devices when a scan starts.
enum { kPurgeDelay = 500 };
}
namespace device {
// static
base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
const InitCallback& init_callback) {
return BluetoothAdapterAndroid::Create(
BluetoothAdapterWrapper_CreateWithDefaultAdapter().obj());
}
// static
base::WeakPtr<BluetoothAdapterAndroid> BluetoothAdapterAndroid::Create(
jobject bluetooth_adapter_wrapper) { // Java Type: bluetoothAdapterWrapper
BluetoothAdapterAndroid* adapter = new BluetoothAdapterAndroid();
adapter->j_adapter_.Reset(Java_ChromeBluetoothAdapter_create(
AttachCurrentThread(), reinterpret_cast<intptr_t>(adapter),
bluetooth_adapter_wrapper));
adapter->ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
return adapter->weak_ptr_factory_.GetWeakPtr();
}
// static
bool BluetoothAdapterAndroid::RegisterJNI(JNIEnv* env) {
return RegisterNativesImpl(env); // Generated in BluetoothAdapter_jni.h
}
std::string BluetoothAdapterAndroid::GetAddress() const {
return ConvertJavaStringToUTF8(Java_ChromeBluetoothAdapter_getAddress(
AttachCurrentThread(), j_adapter_));
}
std::string BluetoothAdapterAndroid::GetName() const {
return ConvertJavaStringToUTF8(
Java_ChromeBluetoothAdapter_getName(AttachCurrentThread(), j_adapter_));
}
void BluetoothAdapterAndroid::SetName(const std::string& name,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterAndroid::IsInitialized() const {
return true;
}
bool BluetoothAdapterAndroid::IsPresent() const {
return Java_ChromeBluetoothAdapter_isPresent(AttachCurrentThread(),
j_adapter_);
}
bool BluetoothAdapterAndroid::IsPowered() const {
return Java_ChromeBluetoothAdapter_isPowered(AttachCurrentThread(),
j_adapter_);
}
void BluetoothAdapterAndroid::SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
if (Java_ChromeBluetoothAdapter_setPowered(AttachCurrentThread(), j_adapter_,
powered)) {
callback.Run();
} else {
error_callback.Run();
}
}
bool BluetoothAdapterAndroid::IsDiscoverable() const {
return Java_ChromeBluetoothAdapter_isDiscoverable(AttachCurrentThread(),
j_adapter_);
}
void BluetoothAdapterAndroid::SetDiscoverable(
bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterAndroid::IsDiscovering() const {
return Java_ChromeBluetoothAdapter_isDiscovering(AttachCurrentThread(),
j_adapter_);
}
BluetoothAdapter::UUIDList BluetoothAdapterAndroid::GetUUIDs() const {
NOTIMPLEMENTED();
return UUIDList();
}
void BluetoothAdapterAndroid::CreateRfcommService(
const BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
error_callback.Run("Not Implemented");
}
void BluetoothAdapterAndroid::CreateL2capService(
const BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
error_callback.Run("Not Implemented");
}
void BluetoothAdapterAndroid::RegisterAudioSink(
const BluetoothAudioSink::Options& options,
const AcquiredCallback& callback,
const BluetoothAudioSink::ErrorCallback& error_callback) {
error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
}
void BluetoothAdapterAndroid::RegisterAdvertisement(
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const CreateAdvertisementErrorCallback& error_callback) {
error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
}
BluetoothLocalGattService* BluetoothAdapterAndroid::GetGattService(
const std::string& identifier) const {
return nullptr;
}
void BluetoothAdapterAndroid::OnAdapterStateChanged(
JNIEnv* env,
const JavaParamRef<jobject>& caller,
const bool powered) {
NotifyAdapterPoweredChanged(powered);
}
void BluetoothAdapterAndroid::OnScanFailed(
JNIEnv* env,
const JavaParamRef<jobject>& caller) {
num_discovery_sessions_ = 0;
MarkDiscoverySessionsAsInactive();
}
void BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan(
JNIEnv* env,
const JavaParamRef<jobject>& caller,
const JavaParamRef<jstring>& address,
const JavaParamRef<jobject>&
bluetooth_device_wrapper, // Java Type: bluetoothDeviceWrapper
int32_t rssi,
const JavaParamRef<jobjectArray>& advertised_uuids, // Java Type: String[]
int32_t tx_power) {
std::string device_address = ConvertJavaStringToUTF8(env, address);
DevicesMap::const_iterator iter = devices_.find(device_address);
bool is_new_device = false;
std::unique_ptr<BluetoothDeviceAndroid> device_android_owner;
BluetoothDeviceAndroid* device_android;
if (iter == devices_.end()) {
// New device.
is_new_device = true;
device_android_owner.reset(
BluetoothDeviceAndroid::Create(this, bluetooth_device_wrapper));
device_android = device_android_owner.get();
} else {
// Existing device.
device_android = static_cast<BluetoothDeviceAndroid*>(iter->second);
}
DCHECK(device_android);
std::vector<std::string> advertised_uuids_strings;
AppendJavaStringArrayToStringVector(env, advertised_uuids,
&advertised_uuids_strings);
BluetoothDevice::UUIDList advertised_bluetooth_uuids;
for (std::string& uuid : advertised_uuids_strings) {
advertised_bluetooth_uuids.push_back(BluetoothUUID(std::move(uuid)));
}
int8_t clamped_tx_power = BluetoothDevice::ClampPower(tx_power);
device_android->UpdateAdvertisementData(
BluetoothDevice::ClampPower(rssi), std::move(advertised_bluetooth_uuids),
{} /* service_data */,
// Android uses INT32_MIN to indicate no Advertised Tx Power.
// https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
tx_power == INT32_MIN ? nullptr : &clamped_tx_power);
if (is_new_device) {
devices_.add(device_address, std::move(device_android_owner));
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
DeviceAdded(this, device_android));
} else {
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
DeviceChanged(this, device_android));
}
}
BluetoothAdapterAndroid::BluetoothAdapterAndroid() : weak_ptr_factory_(this) {
}
BluetoothAdapterAndroid::~BluetoothAdapterAndroid() {
Java_ChromeBluetoothAdapter_onBluetoothAdapterAndroidDestruction(
AttachCurrentThread(), j_adapter_);
}
void BluetoothAdapterAndroid::PurgeTimedOutDevices() {
RemoveTimedOutDevices();
if (IsDiscovering()) {
ui_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&BluetoothAdapterAndroid::PurgeTimedOutDevices,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kActivePollInterval));
} else {
ui_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&BluetoothAdapterAndroid::RemoveTimedOutDevices,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kPassivePollInterval));
}
}
void BluetoothAdapterAndroid::AddDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
// TODO(scheib): Support filters crbug.com/490401
bool session_added = false;
if (IsPowered()) {
if (num_discovery_sessions_ > 0) {
session_added = true;
} else if (Java_ChromeBluetoothAdapter_startScan(AttachCurrentThread(),
j_adapter_)) {
session_added = true;
// Using a delayed task in order to give the adapter some time
// to settle before purging devices.
ui_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&BluetoothAdapterAndroid::PurgeTimedOutDevices,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kPurgeDelay));
}
} else {
VLOG(1) << "AddDiscoverySession: Fails: !isPowered";
}
if (session_added) {
num_discovery_sessions_++;
VLOG(1) << "AddDiscoverySession: Now " << unsigned(num_discovery_sessions_)
<< " sessions.";
callback.Run();
} else {
// TODO(scheib): Eventually wire the SCAN_FAILED result through to here.
error_callback.Run(UMABluetoothDiscoverySessionOutcome::UNKNOWN);
}
}
void BluetoothAdapterAndroid::RemoveDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
bool session_removed = false;
if (num_discovery_sessions_ == 0) {
VLOG(1) << "RemoveDiscoverySession: No scan in progress.";
NOTREACHED();
} else {
--num_discovery_sessions_;
session_removed = true;
if (num_discovery_sessions_ == 0) {
VLOG(1) << "RemoveDiscoverySession: Now 0 sessions. Stopping scan.";
session_removed = Java_ChromeBluetoothAdapter_stopScan(
AttachCurrentThread(), j_adapter_);
for (const auto& device_id_object_pair : devices_) {
device_id_object_pair.second->ClearAdvertisementData();
}
} else {
VLOG(1) << "RemoveDiscoverySession: Now "
<< unsigned(num_discovery_sessions_) << " sessions.";
}
}
if (session_removed) {
callback.Run();
} else {
// TODO(scheib): Eventually wire the SCAN_FAILED result through to here.
error_callback.Run(UMABluetoothDiscoverySessionOutcome::UNKNOWN);
}
}
void BluetoothAdapterAndroid::SetDiscoveryFilter(
std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
// TODO(scheib): Support filters crbug.com/490401
NOTIMPLEMENTED();
error_callback.Run(UMABluetoothDiscoverySessionOutcome::NOT_IMPLEMENTED);
}
void BluetoothAdapterAndroid::RemovePairingDelegateInternal(
device::BluetoothDevice::PairingDelegate* pairing_delegate) {
}
} // namespace device