blob: 768767920d33030789a977c2578cfe0e4389e015 [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/serial/serial_device_enumerator_android.h"
#include <fcntl.h>
#include <cstdint>
#include "base/android/jni_string.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "components/device_event_log/device_event_log.h"
#include "device/base/features.h"
#include "services/device/serial/serial_io_handler_android.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "services/device/serial/jni_headers/ChromeSerialManager_jni.h"
namespace device {
using ::jni_zero::AttachCurrentThread;
SerialDeviceEnumeratorAndroid::SerialDeviceEnumeratorAndroid()
: task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
void SerialDeviceEnumeratorAndroid::Initialize() {
if (!base::FeatureList::IsEnabled(features::kWebSerialWiredDevicesAndroid)) {
return;
}
JNIEnv* env = AttachCurrentThread();
j_serial_manager_.Reset(
Java_ChromeSerialManager_create(env, reinterpret_cast<int64_t>(this)));
if (j_serial_manager_.is_null()) {
SERIAL_LOG(ERROR) << "Could not find Android Serial Service";
return;
}
}
SerialDeviceEnumeratorAndroid::~SerialDeviceEnumeratorAndroid() {
if (j_serial_manager_.is_null()) {
return;
}
JNIEnv* env = AttachCurrentThread();
Java_ChromeSerialManager_close(env, j_serial_manager_);
}
scoped_refptr<SerialIoHandler> SerialDeviceEnumeratorAndroid::CreateIoHandler(
const base::FilePath& path,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
return new SerialIoHandlerAndroid(path, std::move(ui_task_runner),
weak_factory_.GetWeakPtr());
}
void SerialDeviceEnumeratorAndroid::OpenPath(const base::FilePath& path,
OpenPathCallback callback,
ErrorCallback error_callback) {
if (j_serial_manager_.is_null()) {
std::move(error_callback)
.Run("Android Serial Service is not available", path.value());
return;
}
JNIEnv* env = AttachCurrentThread();
const base::FilePath& file_path = path.BaseName();
const std::string& name = file_path.value();
if (callbacks_.find(name) != callbacks_.end()) {
std::move(error_callback).Run("Already opening port", path.value());
return;
}
callbacks_.emplace(name, std::make_unique<Callbacks>(
std::move(callback), std::move(error_callback)));
const std::string& error =
Java_ChromeSerialManager_openPort(env, j_serial_manager_, name);
if (!error.empty()) {
auto node = callbacks_.extract(name);
if (node.empty()) {
return;
}
std::move(node.mapped())->Error("Error opening port", error);
}
}
void SerialDeviceEnumeratorAndroid::OpenPathCallbackViaJni(
JNIEnv* env,
const std::string& port_name,
int32_t fd) {
auto node = callbacks_.extract(port_name);
if (node.empty()) {
return;
}
std::move(node.mapped())->Success(base::ScopedFD(fd));
}
void SerialDeviceEnumeratorAndroid::ErrorCallbackViaJni(
JNIEnv* env,
const std::string& port_name,
const std::string& error_name,
const std::string& message) {
auto node = callbacks_.extract(port_name);
if (node.empty()) {
return;
}
std::move(node.mapped())->Error(error_name, message);
}
void SerialDeviceEnumeratorAndroid::AddPortViaJni(JNIEnv* env,
const std::string& name,
int32_t vendor_id,
int32_t product_id,
bool initial_enumeration) {
if (initial_enumeration) {
// The class constructor invokes create() via JNI, which invokes this method
// from Java, in this case we are already on the valid sequence.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddPortName(name, vendor_id, product_id);
} else {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SerialDeviceEnumeratorAndroid::AddPortName,
base::Unretained(this), name, vendor_id, product_id));
}
}
void SerialDeviceEnumeratorAndroid::RemovePortViaJni(JNIEnv* env,
const std::string& name) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SerialDeviceEnumeratorAndroid::RemovePortName,
base::Unretained(this), name));
}
void SerialDeviceEnumeratorAndroid::AddPortName(const std::string& name,
int vendor_id,
int product_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mojom::SerialPortInfoPtr port = mojom::SerialPortInfo::New();
port->path = base::FilePath("serial://").Append(name);
port->vendor_id = vendor_id;
port->has_vendor_id = (vendor_id >= 0);
port->product_id = product_id;
port->has_product_id = (product_id >= 0);
base::UnguessableToken token = base::UnguessableToken::Create();
tokens_.try_emplace(name, token);
port->token = token;
AddPort(std::move(port));
}
void SerialDeviceEnumeratorAndroid::RemovePortName(const std::string& name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto node = tokens_.extract(name);
if (node.empty()) {
return;
}
RemovePort(node.mapped());
}
void SerialDeviceEnumeratorAndroid::SetSerialManagerForTesting(
jni_zero::ScopedJavaLocalRef<jobject> j_serial_manager) {
j_serial_manager_.Reset(std::move(j_serial_manager));
}
SerialDeviceEnumeratorAndroid::Callbacks::Callbacks(
OpenPathCallback success_callback,
ErrorCallback error_callback)
: success_callback_(std::move(success_callback)),
error_callback_(std::move(error_callback)) {}
SerialDeviceEnumeratorAndroid::Callbacks::~Callbacks() = default;
void SerialDeviceEnumeratorAndroid::Callbacks::Success(base::ScopedFD fd) {
std::move(success_callback_).Run(std::move(fd));
}
void SerialDeviceEnumeratorAndroid::Callbacks::Error(
const std::string& error_name,
const std::string& message) {
std::move(error_callback_).Run(error_name, message);
}
} // namespace device
DEFINE_JNI(ChromeSerialManager)