123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- // Copyright 2020 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 "services/device/generic_sensor/platform_sensor_provider_chromeos.h"
- #include <algorithm>
- #include <iterator>
- #include <utility>
- #include "base/bind.h"
- #include "base/containers/contains.h"
- #include "base/containers/cxx20_erase_map.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/ranges/algorithm.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "chromeos/components/sensors/sensor_util.h"
- #include "services/device/generic_sensor/platform_sensor_chromeos.h"
- using chromeos::sensors::mojom::SensorDeviceDisconnectReason;
- namespace device {
- namespace {
- constexpr base::TimeDelta kReconnectDelay = base::Milliseconds(1000);
- absl::optional<mojom::SensorType> ConvertSensorType(
- chromeos::sensors::mojom::DeviceType device_type) {
- switch (device_type) {
- case chromeos::sensors::mojom::DeviceType::ACCEL:
- return mojom::SensorType::ACCELEROMETER;
- case chromeos::sensors::mojom::DeviceType::ANGLVEL:
- return mojom::SensorType::GYROSCOPE;
- case chromeos::sensors::mojom::DeviceType::LIGHT:
- return mojom::SensorType::AMBIENT_LIGHT;
- case chromeos::sensors::mojom::DeviceType::MAGN:
- return mojom::SensorType::MAGNETOMETER;
- case chromeos::sensors::mojom::DeviceType::GRAVITY:
- return mojom::SensorType::GRAVITY;
- default:
- return absl::nullopt;
- }
- }
- bool DeviceNeedsLocationWithTypes(const std::vector<mojom::SensorType>& types) {
- for (auto type : types) {
- switch (type) {
- case mojom::SensorType::AMBIENT_LIGHT:
- case mojom::SensorType::ACCELEROMETER:
- case mojom::SensorType::GYROSCOPE:
- case mojom::SensorType::MAGNETOMETER:
- case mojom::SensorType::GRAVITY:
- return true;
- default:
- break;
- }
- }
- return false;
- }
- } // namespace
- PlatformSensorProviderChromeOS::PlatformSensorProviderChromeOS() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- RegisterSensorClient();
- }
- PlatformSensorProviderChromeOS::~PlatformSensorProviderChromeOS() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- }
- void PlatformSensorProviderChromeOS::SetUpChannel(
- mojo::PendingRemote<chromeos::sensors::mojom::SensorService>
- pending_remote) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (sensor_service_remote_.is_bound()) {
- LOG(ERROR) << "Ignoring the second Remote<SensorService>";
- return;
- }
- sensor_service_remote_.Bind(std::move(pending_remote));
- sensor_service_remote_.set_disconnect_handler(
- base::BindOnce(&PlatformSensorProviderChromeOS::OnSensorServiceDisconnect,
- weak_ptr_factory_.GetWeakPtr()));
- sensor_service_remote_->RegisterNewDevicesObserver(
- new_devices_observer_.BindNewPipeAndPassRemote());
- new_devices_observer_.set_disconnect_handler(base::BindOnce(
- &PlatformSensorProviderChromeOS::OnNewDevicesObserverDisconnect,
- weak_ptr_factory_.GetWeakPtr()));
- sensor_service_remote_->GetAllDeviceIds(
- base::BindOnce(&PlatformSensorProviderChromeOS::GetAllDeviceIdsCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void PlatformSensorProviderChromeOS::OnNewDeviceAdded(
- int32_t iio_device_id,
- const std::vector<chromeos::sensors::mojom::DeviceType>& types) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (base::Contains(sensors_, iio_device_id))
- return;
- RegisterDevice(iio_device_id, types);
- }
- void PlatformSensorProviderChromeOS::CreateSensorInternal(
- mojom::SensorType type,
- SensorReadingSharedBuffer* reading_buffer,
- CreateSensorCallback callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!sensor_service_remote_.is_bound() || !AreAllSensorsReady()) {
- // Wait until |sensor_service_remote_| is connected and all sensors are
- // ready to proceed.
- // If |sensor_service_remote_| is disconnected, wait until it re-connects to
- // proceed, as it needs a valid SensorDevice channel.
- return;
- }
- // As mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES needs to know
- // whether mojom::SensorType::GYROSCOPE exists or not to determine the
- // algorithm, wait until all sensors ready before processing the fusion
- // sensors as well.
- if (IsFusionSensorType(type)) {
- CreateFusionSensor(type, reading_buffer, std::move(callback));
- return;
- }
- auto id_opt = GetDeviceId(type);
- if (!id_opt.has_value()) {
- std::move(callback).Run(nullptr);
- return;
- }
- int32_t id = id_opt.value();
- DCHECK(base::Contains(sensors_, id));
- auto& sensor = sensors_[id];
- DCHECK(sensor.scale.has_value());
- auto sensor_device_remote = GetSensorDeviceRemote(id);
- std::move(callback).Run(base::MakeRefCounted<PlatformSensorChromeOS>(
- id, type, reading_buffer, this,
- base::BindOnce(&PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect,
- weak_ptr_factory_.GetWeakPtr(), id),
- sensor.scale.value(), std::move(sensor_device_remote)));
- }
- void PlatformSensorProviderChromeOS::FreeResources() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- }
- bool PlatformSensorProviderChromeOS::IsFusionSensorType(
- mojom::SensorType type) const {
- // Let iioservice provide the Gravity sensor.
- switch (type) {
- case mojom::SensorType::LINEAR_ACCELERATION:
- case mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES:
- case mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION:
- case mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES:
- case mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION:
- return true;
- default:
- return false;
- }
- }
- bool PlatformSensorProviderChromeOS::IsSensorTypeAvailable(
- mojom::SensorType type) const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return GetDeviceId(type).has_value();
- }
- PlatformSensorProviderChromeOS::SensorData::SensorData() = default;
- PlatformSensorProviderChromeOS::SensorData::~SensorData() = default;
- absl::optional<PlatformSensorProviderChromeOS::SensorLocation>
- PlatformSensorProviderChromeOS::ParseLocation(
- const absl::optional<std::string>& raw_location) {
- if (!raw_location.has_value()) {
- LOG(ERROR) << "No location attribute";
- return absl::nullopt;
- }
- // These locations must be listed in the same order as the SensorLocation
- // enum.
- const std::vector<std::string> location_strings = {
- chromeos::sensors::mojom::kLocationBase,
- chromeos::sensors::mojom::kLocationLid,
- chromeos::sensors::mojom::kLocationCamera};
- const auto it = base::ranges::find(location_strings, raw_location.value());
- if (it == std::end(location_strings))
- return absl::nullopt;
- return static_cast<SensorLocation>(
- std::distance(std::begin(location_strings), it));
- }
- absl::optional<int32_t> PlatformSensorProviderChromeOS::GetDeviceId(
- mojom::SensorType type) const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- const auto type_id = sensor_id_by_type_.find(type);
- if (type_id == sensor_id_by_type_.end())
- return absl::nullopt;
- return type_id->second;
- }
- void PlatformSensorProviderChromeOS::RegisterSensorClient() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!sensor_hal_client_.is_bound());
- if (!chromeos::sensors::BindSensorHalClient(
- sensor_hal_client_.BindNewPipeAndPassRemote())) {
- LOG(ERROR) << "Failed to bind SensorHalClient via Crosapi";
- return;
- }
- sensor_hal_client_.set_disconnect_handler(
- base::BindOnce(&PlatformSensorProviderChromeOS::OnSensorHalClientFailure,
- weak_ptr_factory_.GetWeakPtr(), kReconnectDelay));
- }
- void PlatformSensorProviderChromeOS::OnSensorHalClientFailure(
- base::TimeDelta reconnection_delay) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- LOG(ERROR) << "OnSensorHalClientFailure";
- ResetSensorService();
- sensor_hal_client_.reset();
- base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&PlatformSensorProviderChromeOS::RegisterSensorClient,
- weak_ptr_factory_.GetWeakPtr()),
- reconnection_delay);
- }
- void PlatformSensorProviderChromeOS::OnSensorServiceDisconnect() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- LOG(ERROR) << "OnSensorServiceDisconnect";
- ResetSensorService();
- }
- void PlatformSensorProviderChromeOS::OnNewDevicesObserverDisconnect() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- LOG(ERROR) << "OnNewDevicesObserverDisconnect";
- // Assumes IIO Service has crashed and waits for its relaunch.
- ResetSensorService();
- }
- void PlatformSensorProviderChromeOS::ResetSensorService() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- for (auto& sensor : sensors_) {
- // Reset only the remote while keeping the attributes information to speed
- // up recovery, as the attributes won't need to be queried again.
- sensor.second.remote.reset();
- }
- // Reset the existing PlatformSensors as well.
- for (const auto& type_id : sensor_id_by_type_)
- ReplaceAndRemoveSensor(type_id.first);
- new_devices_observer_.reset();
- sensor_service_remote_.reset();
- }
- void PlatformSensorProviderChromeOS::GetAllDeviceIdsCallback(
- const SensorIdTypesMap& ids_types) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(sensor_service_remote_.is_bound());
- sensor_ids_received_ = true;
- for (const auto& id_types : ids_types)
- RegisterDevice(id_types.first, id_types.second);
- ProcessSensorsIfPossible();
- }
- void PlatformSensorProviderChromeOS::RegisterDevice(
- int32_t id,
- const std::vector<chromeos::sensors::mojom::DeviceType>& types) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- SensorData& sensor = sensors_[id];
- if (sensor.ignored)
- return;
- for (const auto& device_type : types) {
- auto type_opt = ConvertSensorType(device_type);
- if (!type_opt.has_value())
- continue;
- sensor.types.push_back(type_opt.value());
- }
- if (sensor.types.empty()) {
- sensor.ignored = true;
- return;
- }
- sensor.remote.reset();
- std::vector<std::string> attr_names;
- if (!sensor.scale.has_value())
- attr_names.push_back(chromeos::sensors::mojom::kScale);
- if (DeviceNeedsLocationWithTypes(sensor.types) &&
- !sensor.location.has_value()) {
- attr_names.push_back(chromeos::sensors::mojom::kLocation);
- }
- if (attr_names.empty())
- return;
- sensor.remote = GetSensorDeviceRemote(id);
- // Add a temporary disconnect handler to catch failures during sensor
- // enumeration. PlatformSensorChromeOS will handle disconnection during
- // normal operation.
- sensor.remote.set_disconnect_with_reason_handler(
- base::BindOnce(&PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect,
- weak_ptr_factory_.GetWeakPtr(), id));
- sensor.remote->GetAttributes(
- std::move(attr_names),
- base::BindOnce(&PlatformSensorProviderChromeOS::GetAttributesCallback,
- weak_ptr_factory_.GetWeakPtr(), id));
- }
- void PlatformSensorProviderChromeOS::GetAttributesCallback(
- int32_t id,
- const std::vector<absl::optional<std::string>>& values) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto it = sensors_.find(id);
- DCHECK(it != sensors_.end());
- auto& sensor = it->second;
- DCHECK(sensor.remote.is_bound());
- size_t index = 0;
- if (!sensor.scale.has_value()) {
- if (index >= values.size()) {
- LOG(ERROR) << "values doesn't contain scale attribute.";
- IgnoreSensor(sensor);
- return;
- }
- double scale = 0.0;
- if (!values[index].has_value() ||
- !base::StringToDouble(values[index].value(), &scale)) {
- LOG(ERROR) << "Invalid scale: " << values[index].value_or("")
- << ", for accel with id: " << id;
- IgnoreSensor(sensor);
- return;
- }
- sensor.scale = scale;
- ++index;
- }
- if (DeviceNeedsLocationWithTypes(sensor.types) &&
- !sensor.location.has_value()) {
- if (index >= values.size()) {
- LOG(ERROR) << "values doesn't contain location attribute.";
- IgnoreSensor(sensor);
- return;
- }
- sensor.location = ParseLocation(values[index]);
- if (!sensor.location.has_value()) {
- LOG(ERROR) << "Failed to parse location: " << values[index].value_or("")
- << ", with sensor id: " << id;
- IgnoreSensor(sensor);
- return;
- }
- ++index;
- }
- ProcessSensorsIfPossible();
- }
- void PlatformSensorProviderChromeOS::IgnoreSensor(SensorData& sensor) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- sensor.ignored = true;
- sensor.remote.reset();
- ProcessSensorsIfPossible();
- }
- bool PlatformSensorProviderChromeOS::AreAllSensorsReady() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!sensor_ids_received_)
- return false;
- return base::ranges::all_of(sensors_, [](const auto& sensor) {
- return sensor.second.ignored ||
- (sensor.second.scale.has_value() &&
- (!DeviceNeedsLocationWithTypes(sensor.second.types) ||
- sensor.second.location.has_value()));
- });
- }
- void PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect(
- int32_t id,
- uint32_t custom_reason_code,
- const std::string& description) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto reason =
- static_cast<chromeos::sensors::mojom::SensorDeviceDisconnectReason>(
- custom_reason_code);
- LOG(ERROR) << "OnSensorDeviceDisconnect: " << id << ", reason: " << reason
- << ", description: " << description;
- switch (reason) {
- case SensorDeviceDisconnectReason::IIOSERVICE_CRASHED:
- ResetSensorService();
- break;
- case SensorDeviceDisconnectReason::DEVICE_REMOVED:
- // Hot-pluggable sensors should be HID-stack sensors and shouldn't have
- // the location attribute.
- if (sensors_[id].location.has_value()) {
- LOG(WARNING) << "Device being removed has location: "
- << static_cast<int>(sensors_[id].location.value());
- }
- base::EraseIf(sensor_id_by_type_, [this, &id](const auto& entry) {
- if (entry.second == id) {
- ReplaceAndRemoveSensor(entry.first);
- return true;
- }
- return false;
- });
- sensors_.erase(id);
- ProcessSensorsIfPossible();
- break;
- }
- }
- void PlatformSensorProviderChromeOS::ReplaceAndRemoveSensor(
- mojom::SensorType type) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto* platform_sensor = GetSensor(type).get();
- if (platform_sensor)
- platform_sensor->SensorReplaced();
- RemoveSensor(type, platform_sensor);
- }
- void PlatformSensorProviderChromeOS::ProcessSensorsIfPossible() {
- if (!AreAllSensorsReady())
- return;
- DetermineMotionSensors();
- DetermineLightSensor();
- RemoveUnusedSensorDeviceRemotes();
- ProcessStoredRequests();
- }
- // Follow Android's and W3C's requirements of motion sensors:
- // Android: https://source.android.com/devices/sensors/sensor-types
- // W3C: https://w3c.github.io/sensors/#local-coordinate-system
- // To implement fusion/composite sensors, accelerometer, gyroscope (and
- // magnetometer) must be in the same plane, so when it comes to choosing an
- // accelerometer in a convertible device, we choose the one which is in the
- // same place as the gyroscope. If we still have a choice, we use the one in
- // the lid.
- void PlatformSensorProviderChromeOS::DetermineMotionSensors() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- struct MotionSensorInfo {
- size_t count;
- std::vector<std::pair<int32_t, mojom::SensorType>> sensor_info_pairs;
- };
- std::map<SensorLocation, MotionSensorInfo> motion_sensor_location_info;
- for (const auto& sensor : sensors_) {
- if (sensor.second.ignored || !sensor.second.location)
- continue;
- SensorLocation location = sensor.second.location.value();
- DCHECK_LT(location, SensorLocation::kMax);
- for (auto type : sensor.second.types) {
- switch (type) {
- case mojom::SensorType::ACCELEROMETER:
- case mojom::SensorType::GYROSCOPE:
- case mojom::SensorType::MAGNETOMETER: {
- auto& motion_sensor_info = motion_sensor_location_info[location];
- motion_sensor_info.count++;
- motion_sensor_info.sensor_info_pairs.push_back(
- std::make_pair(sensor.first, type));
- break;
- }
- case mojom::SensorType::GRAVITY: {
- auto& motion_sensor_info = motion_sensor_location_info[location];
- // Don't need to increase |motion_sensor_info.count|, as gravity
- // sensors shouldn't influence the decision.
- motion_sensor_info.sensor_info_pairs.push_back(
- std::make_pair(sensor.first, type));
- break;
- }
- default:
- break;
- }
- }
- }
- const auto preferred_location =
- motion_sensor_location_info[SensorLocation::kLid].count >=
- motion_sensor_location_info[SensorLocation::kBase].count
- ? SensorLocation::kLid
- : SensorLocation::kBase;
- const auto& sensor_info_pairs =
- motion_sensor_location_info[preferred_location].sensor_info_pairs;
- for (const auto& pair : sensor_info_pairs)
- UpdateSensorIdMapping(pair.second, pair.first);
- }
- // Prefer the light sensor on the lid, as it's more meaningful to web API users.
- void PlatformSensorProviderChromeOS::DetermineLightSensor() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- absl::optional<int32_t> id = absl::nullopt;
- for (const auto& sensor : sensors_) {
- if (sensor.second.ignored ||
- !base::Contains(sensor.second.types, mojom::SensorType::AMBIENT_LIGHT))
- continue;
- if (!id.has_value() || sensor.second.location == SensorLocation::kLid)
- id = sensor.first;
- }
- if (id.has_value())
- UpdateSensorIdMapping(mojom::SensorType::AMBIENT_LIGHT, id.value());
- }
- void PlatformSensorProviderChromeOS::UpdateSensorIdMapping(
- const mojom::SensorType& type,
- int32_t id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto it = sensor_id_by_type_.find(type);
- if (it != sensor_id_by_type_.end() && it->second != id)
- ReplaceAndRemoveSensor(type);
- sensor_id_by_type_[type] = id;
- }
- void PlatformSensorProviderChromeOS::RemoveUnusedSensorDeviceRemotes() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- std::set<int32_t> used_ids;
- for (const auto& type_id : sensor_id_by_type_)
- used_ids.emplace(type_id.second);
- for (auto& sensor : sensors_) {
- if (!base::Contains(used_ids, sensor.first))
- sensor.second.remote.reset();
- }
- }
- void PlatformSensorProviderChromeOS::ProcessStoredRequests() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- std::vector<mojom::SensorType> request_types = GetPendingRequestTypes();
- for (const auto& type : request_types) {
- SensorReadingSharedBuffer* reading_buffer =
- GetSensorReadingSharedBufferForType(type);
- if (!reading_buffer)
- continue;
- if (IsFusionSensorType(type)) {
- CreateFusionSensor(
- type, reading_buffer,
- base::BindOnce(&PlatformSensorProviderChromeOS::NotifySensorCreated,
- weak_ptr_factory_.GetWeakPtr(), type));
- continue;
- }
- auto id_opt = GetDeviceId(type);
- if (!id_opt.has_value()) {
- NotifySensorCreated(type, nullptr);
- continue;
- }
- int32_t id = id_opt.value();
- DCHECK(base::Contains(sensors_, id));
- auto& sensor = sensors_[id];
- DCHECK(sensor.scale.has_value());
- auto sensor_device_remote = GetSensorDeviceRemote(id);
- NotifySensorCreated(
- type, base::MakeRefCounted<PlatformSensorChromeOS>(
- id, type, reading_buffer, this,
- base::BindOnce(
- &PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect,
- weak_ptr_factory_.GetWeakPtr(), id),
- sensor.scale.value(), std::move(sensor_device_remote)));
- }
- }
- mojo::Remote<chromeos::sensors::mojom::SensorDevice>
- PlatformSensorProviderChromeOS::GetSensorDeviceRemote(int32_t id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(sensor_service_remote_.is_bound());
- mojo::Remote<chromeos::sensors::mojom::SensorDevice> sensor_device_remote;
- auto& sensor = sensors_[id];
- if (sensor.remote.is_bound()) {
- // Reuse the previous remote.
- sensor_device_remote = std::move(sensor.remote);
- } else {
- sensor_service_remote_->GetDevice(
- id, sensor_device_remote.BindNewPipeAndPassReceiver());
- }
- return sensor_device_remote;
- }
- } // namespace device
|