// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/sensors/ash/sensor_hal_dispatcher.h"
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "chromeos/ash/components/mojo_service_manager/connection.h"
#include "third_party/cros_system_api/mojo/service_constants.h"
using chromeos::mojo_service_manager::mojom::ErrorOrServiceState;
using chromeos::mojo_service_manager::mojom::ServiceState;
namespace chromeos {
namespace sensors {
namespace {
SensorHalDispatcher* g_sensor_hal_dispatcher = nullptr;
constexpr base::TimeDelta kRequestSensorServiceTimeout = base::Seconds(1);
}
// static
void SensorHalDispatcher::Initialize() {
if (g_sensor_hal_dispatcher) {
LOG(WARNING) << "SensorHalDispatcher was already initialized";
return;
}
g_sensor_hal_dispatcher = new SensorHalDispatcher();
}
// static
void SensorHalDispatcher::Shutdown() {
if (!g_sensor_hal_dispatcher) {
LOG(WARNING)
<< "SensorHalDispatcher::Shutdown() called with null dispatcher";
return;
}
delete g_sensor_hal_dispatcher;
g_sensor_hal_dispatcher = nullptr;
}
// static
SensorHalDispatcher* SensorHalDispatcher::GetInstance() {
return g_sensor_hal_dispatcher;
}
void SensorHalDispatcher::RegisterServer(
mojo::PendingRemote<mojom::SensorHalServer> remote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (iio_sensor_available_) {
LOG(ERROR)
<< "SensorHalServer shouldn't be used with Mojo Service Manager: "
<< mojo_services::kIioSensor << " enabled";
return;
}
sensor_hal_server_.Bind(std::move(remote));
sensor_hal_server_.set_disconnect_handler(
base::BindOnce(&SensorHalDispatcher::OnSensorHalServerDisconnect,
base::Unretained(this)));
// Set up the Mojo channels for clients which registered before the server
// registers.
for (auto& client : sensor_hal_clients_)
EstablishMojoChannel(client);
}
void SensorHalDispatcher::RegisterClient(
mojo::PendingRemote<mojom::SensorHalClient> remote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto client = mojo::Remote<mojom::SensorHalClient>(std::move(remote));
if (iio_sensor_available_ || sensor_hal_server_)
EstablishMojoChannel(client);
sensor_hal_clients_.Add(std::move(client));
}
SensorHalDispatcher::SensorHalDispatcher() : receiver_(this) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sensor_hal_clients_.set_disconnect_handler(
base::BindRepeating(&SensorHalDispatcher::OnSensorHalClientDisconnect,
base::Unretained(this)));
}
void SensorHalDispatcher::TryToEstablishMojoChannelByServiceManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(ash::mojo_service_manager::IsServiceManagerBound());
DCHECK(!receiver_.is_bound());
auto* proxy = ash::mojo_service_manager::GetServiceManagerProxy();
proxy->AddServiceObserver(receiver_.BindNewPipeAndPassRemote());
proxy->Query(mojo_services::kIioSensor,
base::BindOnce(&SensorHalDispatcher::QueryCallback,
base::Unretained(this)));
}
void SensorHalDispatcher::OnServiceEvent(
mojo_service_manager::mojom::ServiceEventPtr event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (event->service_name != mojo_services::kIioSensor)
return;
switch (event->type) {
case mojo_service_manager::mojom::ServiceEvent::Type::kRegistered:
OnIioSensorServiceRegistered();
break;
case mojo_service_manager::mojom::ServiceEvent::Type::kUnRegistered:
iio_sensor_available_ = false;
break;
case mojo_service_manager::mojom::ServiceEvent::Type::kDefaultValue:
LOG(WARNING) << "Unsupported kDefaultValue";
break;
}
}
SensorHalDispatcher::~SensorHalDispatcher() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SensorHalDispatcher::QueryCallback(
mojo_service_manager::mojom::ErrorOrServiceStatePtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (result->which()) {
case ErrorOrServiceState::Tag::kState:
switch (result->get_state()->which()) {
case ServiceState::Tag::kRegisteredState:
// IioSensor service is already registered by iioservice.
OnIioSensorServiceRegistered();
break;
case ServiceState::Tag::kUnregisteredState:
// IioSensor service hasn't been registered by iioservice, but the
// policy file exists. Although we can establish mojo channels as
// usual, and let Mojo Service Manager take care of waiting for the
// service to be registered, we still wait for iioservice's registry,
// to make sure sensor clients' are notified when iioservice is truly
// available.
//
// After we deprecate SensorHalDispatcher, each sensor client can
// decide the logic to request IioSensor service itself.
iio_sensor_available_ = false;
break;
case ServiceState::Tag::kDefaultType:
LOG(ERROR) << "Unsupported ServiceState::Tag::kDefaultType";
break;
}
break;
case ErrorOrServiceState::Tag::kError:
LOG(ERROR) << "Error code: " << result->get_error()->code
<< ", message: " << result->get_error()->message;
break;
case ErrorOrServiceState::Tag::kDefaultType:
LOG(ERROR) << "Unknown type: " << result->get_default_type();
break;
}
}
void SensorHalDispatcher::OnIioSensorServiceRegistered() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (iio_sensor_available_)
return;
DCHECK(!sensor_hal_server_);
iio_sensor_available_ = true;
for (auto& client : sensor_hal_clients_)
EstablishMojoChannel(client);
}
void SensorHalDispatcher::EstablishMojoChannel(
const mojo::Remote<mojom::SensorHalClient>& client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(iio_sensor_available_ || sensor_hal_server_);
mojo::PendingRemote<mojom::SensorService> service_remote;
if (iio_sensor_available_) {
DCHECK(ash::mojo_service_manager::IsServiceManagerBound());
ash::mojo_service_manager::GetServiceManagerProxy()->Request(
mojo_services::kIioSensor, kRequestSensorServiceTimeout,
service_remote.InitWithNewPipeAndPassReceiver().PassPipe());
} else {
sensor_hal_server_->CreateChannel(
service_remote.InitWithNewPipeAndPassReceiver());
}
client->SetUpChannel(std::move(service_remote));
}
void SensorHalDispatcher::OnSensorHalServerDisconnect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "Sensor HAL Server connection lost";
sensor_hal_server_.reset();
}
void SensorHalDispatcher::OnSensorHalClientDisconnect(
mojo::RemoteSetElementId id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "Sensor HAL Client connection lost: " << id;
}
} // namespace sensors
} // namespace chromeos