blob: f62f4f2842bf8a34fb907f19da885f408e9a4289 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "iioservice/daemon/sensor_service_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/containers/flat_map.h>
#include <base/stl_util.h>
#include <base/strings/string_util.h>
#include <libmems/iio_device.h>
#include <libmems/iio_channel.h>
#include <mojo/core/embedder/embedder.h>
#include <mojo/core/embedder/scoped_ipc_support.h>
#include "iioservice/daemon/sensor_device_fusion_gravity.h"
#include "iioservice/daemon/sensor_metrics.h"
#include "iioservice/include/common.h"
namespace iioservice {
// Add a namespace here to not leak |DeviceHasType|.
namespace {
// Assume there won't be more than 10000 iio devices.
constexpr int32_t kFusionDeviceIdDelta = 10000;
// Prefixes for each cros::mojom::DeviceType channel.
constexpr char kChnPrefixes[][12] = {
"", // NONE
"accel_", // ACCEL
"anglvel_", // ANGLVEL
"illuminance", // LIGHT
"count", // COUNT
"magn_", // MAGN
"angl", // ANGL
"pressure", // BARO
};
bool DeviceHasType(libmems::IioDevice* iio_device,
cros::mojom::DeviceType type) {
auto channels = iio_device->GetAllChannels();
int type_int = static_cast<int>(type);
switch (type) {
case cros::mojom::DeviceType::ACCEL:
case cros::mojom::DeviceType::ANGLVEL:
case cros::mojom::DeviceType::MAGN:
for (auto chn : channels) {
if (strncmp(chn->GetId(), kChnPrefixes[type_int],
strlen(kChnPrefixes[type_int])) == 0)
return true;
}
return false;
case cros::mojom::DeviceType::LIGHT:
case cros::mojom::DeviceType::COUNT:
case cros::mojom::DeviceType::ANGL:
case cros::mojom::DeviceType::BARO:
for (auto chn : channels) {
if (strcmp(chn->GetId(), kChnPrefixes[type_int]) == 0)
return true;
}
return false;
default:
// TODO(chenghaogyang): Support the uncalibrated devices.
return false;
}
}
Location GetLocation(libmems::IioDevice* device) {
auto location_opt = device->ReadStringAttribute(cros::mojom::kLocation);
if (location_opt.has_value()) {
std::string location_str = std::string(
base::TrimString(location_opt.value(), base::StringPiece("\0\n", 2),
base::TRIM_TRAILING));
if (location_str.compare(cros::mojom::kLocationBase) == 0)
return Location::kBase;
if (location_str.compare(cros::mojom::kLocationLid) == 0)
return Location::kLid;
if (location_str.compare(cros::mojom::kLocationCamera) == 0)
return Location::kCamera;
}
return Location::kNone;
}
std::string LocationToString(Location location) {
switch (location) {
case Location::kBase:
return cros::mojom::kLocationBase;
case Location::kLid:
return cros::mojom::kLocationLid;
case Location::kCamera:
return cros::mojom::kLocationCamera;
default:
return "";
}
}
} // namespace
// static
void SensorServiceImpl::SensorServiceImplDeleter(SensorServiceImpl* service) {
if (service == nullptr)
return;
if (!service->ipc_task_runner_->RunsTasksInCurrentSequence()) {
service->ipc_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SensorServiceImpl::SensorServiceImplDeleter, service));
return;
}
delete service;
}
// static
SensorServiceImpl::ScopedSensorServiceImpl SensorServiceImpl::Create(
scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
std::unique_ptr<libmems::IioContext> context) {
DCHECK(ipc_task_runner->RunsTasksInCurrentSequence());
auto sensor_device = SensorDeviceImpl::Create(ipc_task_runner, context.get());
if (!sensor_device) {
LOGF(ERROR) << "Failed to get SensorDevice";
return ScopedSensorServiceImpl(nullptr, SensorServiceImplDeleter);
}
return ScopedSensorServiceImpl(
new SensorServiceImpl(std::move(ipc_task_runner), std::move(context),
std::move(sensor_device)),
SensorServiceImplDeleter);
}
SensorServiceImpl::~SensorServiceImpl() {
ClearReceiversWithReason(
cros::mojom::SensorServiceDisconnectReason::IIOSERVICE_SHUTDOWN,
"iioservice is shutting down.");
}
void SensorServiceImpl::AddReceiver(
mojo::PendingReceiver<cros::mojom::SensorService> request) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
receiver_set_.Add(this, std::move(request), ipc_task_runner_);
SensorMetrics::GetInstance()->SendSensorClientConnected();
}
void SensorServiceImpl::ClearReceiversWithReason(
cros::mojom::SensorServiceDisconnectReason reason,
const std::string& description) {
const size_t n = receiver_set_.size();
receiver_set_.ClearWithReason(static_cast<uint32_t>(reason), description);
for (size_t i = 0; i < n; ++i)
SensorMetrics::GetInstance()->SendSensorClientDisconnected();
}
void SensorServiceImpl::OnDeviceAdded(int iio_device_id) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
if (device_types_map_.find(iio_device_id) != device_types_map_.end()) {
// Device is already added. Skipping.
return;
}
// Reload to check if there are new devices available.
context_->Reload();
if (!context_->IsValid()) {
LOGF(ERROR) << "No devices in the context";
return;
}
auto device = context_->GetDeviceById(iio_device_id);
if (!device) {
LOGF(ERROR) << "Cannot find device by id: " << iio_device_id;
return;
}
AddDevice(device);
}
void SensorServiceImpl::GetDeviceIds(cros::mojom::DeviceType type,
GetDeviceIdsCallback callback) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
std::vector<int32_t> ids;
for (auto device_types : device_types_map_) {
for (cros::mojom::DeviceType device_type : device_types.second) {
if (device_type == type) {
ids.push_back(device_types.first);
break;
}
}
}
std::move(callback).Run(std::move(ids));
}
void SensorServiceImpl::GetAllDeviceIds(GetAllDeviceIdsCallback callback) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
base::flat_map<int32_t, std::vector<cros::mojom::DeviceType>> ids(
device_types_map_.begin(), device_types_map_.end());
std::move(callback).Run(ids);
}
void SensorServiceImpl::GetDevice(
int32_t iio_device_id,
mojo::PendingReceiver<cros::mojom::SensorDevice> device_request) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
if (iio_device_id < kFusionDeviceIdDelta) { // IIO device
if (!sensor_device_) {
LOGF(ERROR) << "No available SensorDevice";
return;
}
auto it = device_types_map_.find(iio_device_id);
if (it == device_types_map_.end()) {
LOGF(ERROR) << "No available device with id: " << iio_device_id;
return;
}
const auto& types = it->second;
sensor_device_->AddReceiver(
iio_device_id, std::move(device_request),
std::set<cros::mojom::DeviceType>(types.begin(), types.end()));
} else { // Fusion device
auto it = sensor_device_fusions_.find(iio_device_id);
if (it == sensor_device_fusions_.end()) {
LOGF(ERROR) << "Invalid iio_device_id: " << iio_device_id;
return;
}
it->second->AddReceiver(std::move(device_request));
}
}
void SensorServiceImpl::RegisterNewDevicesObserver(
mojo::PendingRemote<cros::mojom::SensorServiceNewDevicesObserver>
observer) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
observers_.emplace_back(
mojo::Remote<cros::mojom::SensorServiceNewDevicesObserver>(
std::move(observer)));
}
SensorServiceImpl::SensorServiceImpl(
scoped_refptr<base::SequencedTaskRunner> ipc_task_runner,
std::unique_ptr<libmems::IioContext> context,
SensorDeviceImpl::ScopedSensorDeviceImpl sensor_device)
: ipc_task_runner_(ipc_task_runner),
context_(std::move(context)),
sensor_device_(std::move(sensor_device)) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
if (!sensor_device_)
LOGF(ERROR) << "Failed to get SensorDevice";
if (context_->IsValid()) {
for (auto device : context_->GetAllDevices())
AddDevice(device);
}
receiver_set_.set_disconnect_handler(
base::BindRepeating(&SensorServiceImpl::OnSensorServiceDisconnect,
weak_factory_.GetWeakPtr()));
}
void SensorServiceImpl::AddDevice(libmems::IioDevice* device) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
const int32_t id = device->GetId();
if (!device->DisableBuffer()) {
LOGF(ERROR) << "Permissions and ownerships hasn't been set for device: "
<< id;
return;
}
std::vector<cros::mojom::DeviceType> types;
for (int32_t i = static_cast<int32_t>(cros::mojom::DeviceType::ACCEL);
i <= static_cast<int32_t>(cros::mojom::DeviceType::kMaxValue); ++i) {
auto type = static_cast<cros::mojom::DeviceType>(i);
if (DeviceHasType(device, type))
types.push_back(type);
}
Location location = GetLocation(device);
AddDevice(id, types, location);
// Check fusion devices.
for (const auto& type : types) {
if (base::Contains(device_maps_[type], location)) {
LOGF(WARNING) << "Duplicated pair of type : " << type
<< ", and location: " << static_cast<int>(location);
continue;
}
device_maps_[type][location] = id;
CheckGravity(id, type, location);
}
}
void SensorServiceImpl::AddDevice(
int32_t id,
const std::vector<cros::mojom::DeviceType>& types,
Location location) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
device_types_map_.emplace(id, types);
SensorMetrics::GetInstance()->SetConfigForDevice(id, types,
LocationToString(location));
for (auto& observer : observers_)
observer->OnNewDeviceAdded(id, types);
}
void SensorServiceImpl::CheckGravity(int32_t id,
cros::mojom::DeviceType type,
Location location) {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
if (type == cros::mojom::DeviceType::ACCEL ||
type == cros::mojom::DeviceType::ANGLVEL) {
auto it_accel = device_maps_[cros::mojom::DeviceType::ACCEL].find(location);
auto it_gyro =
device_maps_[cros::mojom::DeviceType::ANGLVEL].find(location);
if (it_accel != device_maps_[cros::mojom::DeviceType::ACCEL].end() &&
it_gyro != device_maps_[cros::mojom::DeviceType::ANGLVEL].end()) {
// Create the gravity device on |location|.
DCHECK(!base::Contains(device_maps_[cros::mojom::DeviceType::GRAVITY],
location));
int32_t id = kFusionDeviceIdDelta + fusion_device_counter_++;
AddDevice(id, {cros::mojom::DeviceType::GRAVITY}, location);
device_maps_[cros::mojom::DeviceType::GRAVITY][location] = id;
if (!sensor_device_)
return;
auto* accel = context_->GetDeviceById(it_accel->second);
if (!accel)
return;
double min_freq, max_freq;
if (!accel->GetMinMaxFrequency(&min_freq, &max_freq))
max_freq = 100.0f;
sensor_device_fusions_.emplace(
id, SensorDeviceFusionGravity::Create(
id, location, ipc_task_runner_,
base::BindRepeating(&SensorDeviceImpl::AddReceiver,
sensor_device_->GetWeakPtr()),
max_freq, it_accel->second, it_gyro->second));
}
}
}
void SensorServiceImpl::OnSensorServiceDisconnect() {
DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
LOGF(INFO) << "SensorDevice disconnected. ReceiverId: "
<< receiver_set_.current_receiver();
SensorMetrics::GetInstance()->SendSensorClientDisconnected();
}
} // namespace iioservice