| // Copyright 2021 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 "hps/daemon/dbus_adaptor.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include <base/location.h> |
| #include <base/logging.h> |
| #include <brillo/errors/error.h> |
| #include <brillo/errors/error_codes.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <hps/daemon/filters/filter_factory.h> |
| |
| namespace hps { |
| |
| constexpr char kErrorPath[] = "org.chromium.Hps.GetFeatureResultError"; |
| |
| namespace { |
| |
| std::vector<uint8_t> HpsResultToSerializedBytes(HpsResult result) { |
| HpsResultProto result_proto; |
| result_proto.set_value(result); |
| |
| std::vector<uint8_t> serialized; |
| serialized.resize(result_proto.ByteSizeLong()); |
| result_proto.SerializeToArray(serialized.data(), |
| static_cast<int>(serialized.size())); |
| return serialized; |
| } |
| |
| } // namespace |
| |
| void DBusAdaptor::FeatureState::Enable(const FeatureConfig& config, |
| FeatureCallback callback) { |
| DCHECK(!enabled_); |
| DCHECK(!enabled_in_hps_); |
| DCHECK(!callback.is_null()); |
| enabled_ = true; |
| config_ = config; |
| callback_ = std::move(callback); |
| raw_result_ = {}; |
| } |
| |
| void DBusAdaptor::FeatureState::Disable() { |
| DCHECK(enabled_); |
| DCHECK(enabled_in_hps_); |
| enabled_ = false; |
| config_ = {}; |
| callback_ = FeatureCallback{}; |
| raw_result_ = {}; |
| } |
| |
| void DBusAdaptor::FeatureState::DidCommit() { |
| enabled_in_hps_ = enabled_; |
| if (enabled_) { |
| DCHECK(!callback_.is_null()); |
| auto callback = base::BindRepeating( |
| &DBusAdaptor::FeatureState::OnFilteredResult, base::Unretained(this)); |
| filter_ = CreateFilter(config_, std::move(callback)); |
| } else { |
| DCHECK(callback_.is_null()); |
| filter_.reset(); |
| } |
| } |
| |
| void DBusAdaptor::FeatureState::OnFilteredResult(HpsResult result) { |
| DCHECK(callback_); |
| HpsResultProto result_proto; |
| SerializeInternal(result_proto, result); |
| std::vector<uint8_t> serialized; |
| serialized.resize(result_proto.ByteSizeLong()); |
| result_proto.SerializeToArray(serialized.data(), |
| static_cast<int>(serialized.size())); |
| callback_.Run(std::move(serialized)); |
| } |
| |
| void DBusAdaptor::FeatureState::DidShutDown() { |
| enabled_in_hps_ = false; |
| filter_.reset(); |
| } |
| |
| HpsResult DBusAdaptor::FeatureState::ProcessResult(FeatureResult result) { |
| DCHECK(enabled_); |
| raw_result_ = result; |
| return filter_->ProcessResult(result.inference_result, result.valid); |
| } |
| |
| void DBusAdaptor::FeatureState::Serialize(HpsResultProto& result_proto) { |
| DCHECK(filter_); |
| SerializeInternal(result_proto, filter_->GetCurrentResult()); |
| } |
| |
| void DBusAdaptor::FeatureState::SerializeInternal(HpsResultProto& result_proto, |
| HpsResult value) { |
| DCHECK(enabled_); |
| result_proto.set_value(value); |
| if (config_.report_raw_results()) { |
| result_proto.set_inference_result(raw_result_.inference_result); |
| result_proto.set_inference_result_valid(raw_result_.valid); |
| } |
| } |
| |
| DBusAdaptor::DBusAdaptor(scoped_refptr<dbus::Bus> bus, |
| std::unique_ptr<HPS> hps, |
| uint32_t poll_time_ms) |
| : org::chromium::HpsAdaptor(this), |
| dbus_object_(nullptr, bus, dbus::ObjectPath(::hps::kHpsServicePath)), |
| hps_(std::move(hps)), |
| poll_time_ms_(poll_time_ms) { |
| ShutDown(); |
| } |
| |
| void DBusAdaptor::RegisterAsync( |
| const brillo::dbus_utils::AsyncEventSequencer::CompletionAction& cb) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| RegisterWithDBusObject(&dbus_object_); |
| dbus_object_.RegisterAsync(cb); |
| } |
| |
| void DBusAdaptor::PollTask() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Make sure the HPS module is powered on again if we are resuming from a |
| // system suspend state. |
| CommitState(); |
| |
| for (uint8_t i = 0; i < kFeatures; ++i) { |
| auto& feature = features_[i]; |
| if (feature.enabled()) { |
| FeatureResult result = this->hps_->Result(i); |
| DCHECK(feature.filter()); |
| DCHECK(!feature.needs_commit()); |
| const auto res = feature.ProcessResult(result); |
| VLOG(2) << "Poll: Feature: " << static_cast<int>(i) |
| << " Valid: " << result.valid |
| << " Result: " << static_cast<int>(result.inference_result) |
| << " Filter: " << static_cast<int>(res); |
| } |
| } |
| } |
| |
| void DBusAdaptor::BootIfNeeded() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (hps_booted_) { |
| return; |
| } |
| hps_->Boot(); |
| hps_booted_ = true; |
| } |
| |
| void DBusAdaptor::ShutDown() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| poll_timer_.Stop(); |
| if (!hps_->ShutDown()) { |
| LOG(FATAL) << "Failed to shutdown"; |
| } |
| hps_booted_ = false; |
| for (auto& feature : features_) { |
| feature.DidShutDown(); |
| } |
| } |
| |
| // Synchronizes the desired feature enable/disable state with the HPS module. If |
| // no features are enabled, HPS is also powered off. If HPS has been reset |
| // because of a system suspend, this method will also restore any enabled |
| // feature settings. |
| bool DBusAdaptor::CommitState() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| bool result = true; |
| if (hps_booted_ && !hps_->IsRunning()) { |
| // If the HPS module is not running even though we haven't shut it down, the |
| // system was probably suspended and resumed, resetting HPS as a side |
| // effect. Reboot the module and restore the enabled features so we can |
| // continue polling. |
| // |
| // Note that it's possible for a system suspend and resume to happen at any |
| // point, including while we're polling for features. That means we'll |
| // either report unknown feature results or in the worst case abort with a |
| // fault if HPS is in an unexpected state. This means DBUS clients need to |
| // handle hpsd restarting at arbitrary times. |
| LOG(INFO) << "HPS reset detected"; |
| ShutDown(); |
| |
| // Post condition: all features are disabled in HPS after shutting down. |
| for (const auto& feature : features_) { |
| DCHECK(!feature.enabled_in_hps()); |
| } |
| } |
| |
| for (uint8_t i = 0; i < kFeatures; i++) { |
| auto& feature = features_[i]; |
| if (!feature.needs_commit()) |
| continue; |
| |
| if (feature.enabled()) { |
| // If we want to enable any features, HPS needs to be running. |
| BootIfNeeded(); |
| LOG_IF(FATAL, !hps_->Enable(i)) << "Failed to enable feature " << i; |
| } else { |
| // If any features need to be disabled, HPS must be running. If HPS is not |
| // running, all features are already disabled and we won't end up in this |
| // branch. |
| DCHECK(hps_booted_); |
| LOG_IF(FATAL, !hps_->Disable(i)) << "Failed to disable feature " << i; |
| } |
| feature.DidCommit(); |
| } |
| |
| // Post condition: all feature states have been committed to HPS. |
| for (const auto& feature : features_) { |
| DCHECK(!feature.needs_commit()); |
| } |
| |
| size_t active_features = |
| std::count_if(features_.begin(), features_.end(), |
| [](const auto& f) { return f.enabled_in_hps(); }); |
| |
| if (!active_features && hps_booted_) { |
| ShutDown(); |
| } else if (active_features && !poll_timer_.IsRunning()) { |
| poll_timer_.Start( |
| FROM_HERE, base::Milliseconds(poll_time_ms_), |
| base::BindRepeating(&DBusAdaptor::PollTask, base::Unretained(this))); |
| } |
| return result; |
| } |
| |
| bool DBusAdaptor::EnableFeature(brillo::ErrorPtr* error, |
| const hps::FeatureConfig& config, |
| uint8_t feature, |
| FeatureCallback callback) { |
| CHECK_LT(feature, kFeatures); |
| if (features_[feature].enabled()) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| kErrorPath, "hpsd: Feature already enabled."); |
| |
| return false; |
| } |
| features_[feature].Enable(config, std::move(callback)); |
| CommitState(); |
| return true; |
| } |
| |
| bool DBusAdaptor::DisableFeature(brillo::ErrorPtr* error, uint8_t feature) { |
| CHECK_LT(feature, kFeatures); |
| if (!features_[feature].enabled()) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| kErrorPath, "hpsd: Feature not enabled."); |
| |
| return false; |
| } |
| features_[feature].Disable(); |
| CommitState(); |
| return true; |
| } |
| |
| bool DBusAdaptor::GetFeatureResult(brillo::ErrorPtr* error, |
| HpsResultProto* result, |
| uint8_t feature) { |
| CHECK_LT(feature, kFeatures); |
| if (!features_[feature].enabled()) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| kErrorPath, "hpsd: Feature not enabled."); |
| |
| return false; |
| } |
| features_[feature].Serialize(*result); |
| return true; |
| } |
| |
| bool DBusAdaptor::EnableHpsSense(brillo::ErrorPtr* error, |
| const hps::FeatureConfig& config) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| LOG(INFO) << "DBusAdaptor::EnableHpsSense with config type " |
| << static_cast<int>(config.filter_config_case()); |
| return EnableFeature( |
| error, config, 0, |
| base::BindRepeating(&DBusAdaptor::SendHpsSenseChangedSignal, |
| base::Unretained(this))); |
| } |
| |
| bool DBusAdaptor::DisableHpsSense(brillo::ErrorPtr* error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| LOG(INFO) << "DBusAdaptor::DisableHpsSense"; |
| if (DisableFeature(error, 0)) { |
| DBusAdaptor::SendHpsSenseChangedSignal( |
| HpsResultToSerializedBytes(HpsResult::UNKNOWN)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool DBusAdaptor::GetResultHpsSense(brillo::ErrorPtr* error, |
| HpsResultProto* result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return GetFeatureResult(error, result, 0); |
| } |
| |
| bool DBusAdaptor::EnableHpsNotify(brillo::ErrorPtr* error, |
| const hps::FeatureConfig& config) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| LOG(INFO) << "DBusAdaptor::EnableHpsNotify with config type " |
| << static_cast<int>(config.filter_config_case()); |
| return EnableFeature( |
| error, config, 1, |
| base::BindRepeating(&DBusAdaptor::SendHpsNotifyChangedSignal, |
| base::Unretained(this))); |
| } |
| |
| bool DBusAdaptor::DisableHpsNotify(brillo::ErrorPtr* error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| LOG(INFO) << "DBusAdaptor::DisableHpsNotify"; |
| if (DisableFeature(error, 1)) { |
| DBusAdaptor::SendHpsNotifyChangedSignal( |
| HpsResultToSerializedBytes(HpsResult::UNKNOWN)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool DBusAdaptor::GetResultHpsNotify(brillo::ErrorPtr* error, |
| HpsResultProto* result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return GetFeatureResult(error, result, 1); |
| } |
| |
| } // namespace hps |