| // Copyright 2017 The Fuchsia 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 "src/cobalt/bin/app/cobalt_app.h" |
| |
| #include <memory> |
| |
| #include "lib/sys/cpp/component_context.h" |
| #include "src/cobalt/bin/app/activity_listener_impl.h" |
| #include "src/cobalt/bin/app/metric_event_logger_factory_impl.h" |
| #include "src/cobalt/bin/app/utils.h" |
| #include "src/cobalt/bin/utils/fuchsia_http_client.h" |
| #include "third_party/cobalt/src/lib/util/posix_file_system.h" |
| #include "third_party/cobalt/src/public/cobalt_config.h" |
| #include "third_party/cobalt/src/public/cobalt_service.h" |
| |
| namespace cobalt { |
| |
| using encoder::ClientSecret; |
| using logger::ProjectContextFactory; |
| using util::PosixFileSystem; |
| using utils::FuchsiaHTTPClient; |
| |
| const size_t kMaxBytesPerEnvelope = 512 * 1024; // 0.5 MiB. |
| |
| constexpr char kMetricsRegistryPath[] = "/config/data/global_metrics_registry.pb"; |
| |
| constexpr char kObservationStorePath[] = "/data/observation_store"; |
| constexpr char kLocalAggregateProtoStorePath[] = "/data/local_aggregate_store"; |
| constexpr char kLocalAggregationPath[] = "/data/local_aggregate_storage"; |
| constexpr char kObsHistoryProtoStorePath[] = "/data/obs_history_store"; |
| constexpr char kSystemDataCachePrefix[] = "/data/system_data_"; |
| constexpr char kLocalLogFilePath[] = "/data/cobalt_observations.pb"; |
| |
| const size_t kClearcutMaxRetries = 5; |
| |
| std::unique_ptr<CobaltRegistry> ReadRegistry(const std::string& global_metrics_registry_path) { |
| std::ifstream registry_file_stream; |
| registry_file_stream.open(global_metrics_registry_path); |
| FX_CHECK(registry_file_stream && registry_file_stream.good()) |
| << "Could not open the Cobalt global metrics registry: " << global_metrics_registry_path; |
| std::string global_metrics_registry_bytes; |
| global_metrics_registry_bytes.assign((std::istreambuf_iterator<char>(registry_file_stream)), |
| std::istreambuf_iterator<char>()); |
| FX_CHECK(!global_metrics_registry_bytes.empty()) |
| << "Could not read the Cobalt global metrics registry: " << global_metrics_registry_path; |
| |
| auto cobalt_registry = std::make_unique<CobaltRegistry>(); |
| FX_CHECK(cobalt_registry->ParseFromString(global_metrics_registry_bytes)) |
| << "Unable to parse global metrics registry at: " << global_metrics_registry_path; |
| |
| return cobalt_registry; |
| } |
| |
| CobaltConfig CobaltApp::CreateCobaltConfig( |
| async_dispatcher_t* dispatcher, const std::string& global_metrics_registry_path, |
| const FuchsiaConfigurationData& configuration_data, FuchsiaSystemClockInterface* system_clock, |
| FuchsiaHTTPClient::LoaderFactory http_loader_factory, UploadScheduleConfig upload_schedule_cfg, |
| size_t event_aggregator_backfill_days, bool use_memory_observation_store, |
| size_t max_bytes_per_observation_store, const std::string& product_name, |
| const std::string& board_name, const std::string& version, |
| std::unique_ptr<ActivityListenerImpl> listener) { |
| // |target_pipeline| is the pipeline used for sending data to cobalt. In particular, it is the |
| // source of the encryption keys, as well as determining the destination for generated |
| // observations (either clearcut, or the local filesystem). |
| std::unique_ptr<TargetPipelineInterface> target_pipeline; |
| const auto& backend_environment = configuration_data.GetBackendEnvironment(); |
| if (backend_environment == system_data::Environment::LOCAL) { |
| target_pipeline = std::make_unique<LocalPipeline>(); |
| } else { |
| target_pipeline = std::make_unique<TargetPipeline>( |
| backend_environment, ReadPublicKeyPem(configuration_data.ShufflerPublicKeyPath()), |
| ReadPublicKeyPem(configuration_data.AnalyzerPublicKeyPath()), |
| std::make_unique<FuchsiaHTTPClient>(std::move(http_loader_factory)), kClearcutMaxRetries); |
| } |
| |
| CobaltConfig cfg = { |
| .product_name = product_name, |
| .board_name_suggestion = board_name, |
| .version = version, |
| .build_type = configuration_data.GetBuildType(), |
| .release_stage = configuration_data.GetReleaseStage(), |
| |
| .file_system = std::make_unique<PosixFileSystem>(), |
| .use_memory_observation_store = use_memory_observation_store, |
| .max_bytes_per_event = fuchsia::cobalt::MAX_BYTES_PER_EVENT, |
| .max_bytes_per_envelope = kMaxBytesPerEnvelope, |
| .max_bytes_total = max_bytes_per_observation_store, |
| .observation_store_directory = kObservationStorePath, |
| |
| .local_aggregate_proto_store_path = kLocalAggregateProtoStorePath, |
| .obs_history_proto_store_path = kObsHistoryProtoStorePath, |
| .local_aggregate_store_dir = kLocalAggregationPath, |
| .local_aggregate_store_strategy = StorageStrategy::Delayed, |
| |
| .upload_schedule_cfg = upload_schedule_cfg, |
| |
| .target_pipeline = std::move(target_pipeline), |
| |
| .local_shipping_manager_path = kLocalLogFilePath, |
| |
| .api_key = configuration_data.GetApiKey(), |
| .client_secret = CobaltApp::getClientSecret(), |
| .global_registry = ReadRegistry(global_metrics_registry_path), |
| |
| .local_aggregation_backfill_days = event_aggregator_backfill_days, |
| |
| .validated_clock = system_clock, |
| |
| .activity_listener = std::move(listener), |
| |
| .enable_replacement_metrics = configuration_data.GetEnableReplacementMetrics(), |
| }; |
| return cfg; |
| } |
| |
| CobaltApp CobaltApp::CreateCobaltApp( |
| std::unique_ptr<sys::ComponentContext> context, async_dispatcher_t* dispatcher, |
| UploadScheduleConfig upload_schedule_cfg, size_t event_aggregator_backfill_days, |
| bool start_event_aggregator_worker, bool use_memory_observation_store, |
| size_t max_bytes_per_observation_store, const std::string& product_name, |
| const std::string& board_name, const std::string& version) { |
| // Create the configuration data from the data in the filesystem. |
| FuchsiaConfigurationData configuration_data; |
| |
| sys::ComponentContext* context_ptr = context.get(); |
| |
| auto validated_clock = std::make_unique<FuchsiaSystemClock>(dispatcher); |
| |
| auto cobalt_service = std::make_unique<CobaltService>(CreateCobaltConfig( |
| dispatcher, kMetricsRegistryPath, configuration_data, validated_clock.get(), |
| [context_ptr]() { |
| fuchsia::net::http::LoaderSyncPtr loader_sync; |
| context_ptr->svc()->Connect(loader_sync.NewRequest()); |
| return loader_sync; |
| }, |
| upload_schedule_cfg, event_aggregator_backfill_days, use_memory_observation_store, |
| max_bytes_per_observation_store, product_name, board_name, version, |
| std::make_unique<ActivityListenerImpl>(dispatcher, context->svc()))); |
| |
| cobalt_service->SetDataCollectionPolicy(configuration_data.GetDataCollectionPolicy()); |
| |
| return CobaltApp(std::move(context), dispatcher, std::move(cobalt_service), |
| std::move(validated_clock), start_event_aggregator_worker, |
| configuration_data.GetWatchForUserConsent()); |
| } |
| |
| CobaltApp::CobaltApp(std::unique_ptr<sys::ComponentContext> context, async_dispatcher_t* dispatcher, |
| std::unique_ptr<CobaltServiceInterface> cobalt_service, |
| std::unique_ptr<FuchsiaSystemClockInterface> validated_clock, |
| bool start_event_aggregator_worker, bool watch_for_user_consent) |
| : context_(std::move(context)), |
| cobalt_service_(std::move(cobalt_service)), |
| validated_clock_(std::move(validated_clock)), |
| timer_manager_(dispatcher) { |
| auto current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| FX_LOGS(INFO) << "Waiting for the system clock to become accurate at: " |
| << std::put_time(std::localtime(¤t_time), "%F %T %z"); |
| validated_clock_->AwaitExternalSource([this, start_event_aggregator_worker]() { |
| auto current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| FX_LOGS(INFO) << "The system clock has become accurate, now at: " |
| << std::put_time(std::localtime(¤t_time), "%F %T %z"); |
| |
| // Now that the clock is accurate, notify CobaltService. |
| cobalt_service_->SystemClockIsAccurate(std::make_unique<util::SystemClock>(), |
| start_event_aggregator_worker); |
| controller_impl_->OnSystemClockBecomesAccurate(); |
| }); |
| |
| // Create LoggerFactory protocol implementation and start serving it. |
| logger_factory_impl_.reset(new LoggerFactoryImpl(&timer_manager_, cobalt_service_.get())); |
| context_->outgoing()->AddPublicService( |
| logger_factory_bindings_.GetHandler(logger_factory_impl_.get())); |
| |
| // Create MetricEventLoggerFactory protocol implementation and start serving it. |
| metric_event_logger_factory_impl_ = |
| std::make_unique<MetricEventLoggerFactoryImpl>(cobalt_service_.get()); |
| context_->outgoing()->AddPublicService( |
| metric_event_logger_factory_bindings_.GetHandler(metric_event_logger_factory_impl_.get())); |
| |
| // Create process.lifecycle protocol implementation and start serving it. |
| process_lifecycle_impl_ = std::make_unique<ProcessLifecycle>( |
| cobalt_service_.get(), logger_factory_impl_.get(), metric_event_logger_factory_impl_.get(), |
| &process_lifecycle_bindings_); |
| context_->outgoing()->AddPublicService( |
| process_lifecycle_bindings_.GetHandler(process_lifecycle_impl_.get()), |
| "fuchsia.process.lifecycle.Lifecycle"); |
| |
| // Create SystemDataUpdater protocol implementation and start serving it. |
| system_data_updater_impl_.reset( |
| new SystemDataUpdaterImpl(cobalt_service_->system_data(), kSystemDataCachePrefix)); |
| context_->outgoing()->AddPublicService( |
| system_data_updater_bindings_.GetHandler(system_data_updater_impl_.get())); |
| |
| if (watch_for_user_consent) { |
| user_consent_watcher_ = std::make_unique<UserConsentWatcher>( |
| dispatcher, context_->svc(), [this](const CobaltService::DataCollectionPolicy& new_policy) { |
| cobalt_service_->SetDataCollectionPolicy(new_policy); |
| }); |
| |
| user_consent_watcher_->StartWatching(); |
| } |
| |
| // Create Controller protocol implementation and start serving it. |
| controller_impl_ = std::make_unique<CobaltControllerImpl>(dispatcher, cobalt_service_.get()); |
| context_->outgoing()->AddPublicService(controller_bindings_.GetHandler(controller_impl_.get())); |
| } |
| |
| ClientSecret CobaltApp::getClientSecret() { |
| // TODO(rudominer): Generate a client secret only once, store it |
| // persistently and reuse it in future instances. |
| return ClientSecret::GenerateNewSecret(); |
| } |
| |
| } // namespace cobalt |