/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_
#define METRICS_UPLOADER_UPLOAD_SERVICE_H_
#include <memory>
#include <string>
#include <base/metrics/histogram_base.h>
#include <base/metrics/histogram_flattener.h>
#include <base/metrics/histogram_snapshot_manager.h>
#include <brillo/daemons/daemon.h>
#include "persistent_integer.h"
#include "uploader/crash_counters.h"
#include "uploader/metrics_log.h"
#include "uploader/metricsd_service_runner.h"
#include "uploader/proto/chrome_user_metrics_extension.pb.h"
#include "uploader/sender.h"
#include "uploader/system_profile_cache.h"
class SystemProfileSetter;
// Service responsible for backing up the currently aggregated metrics to disk
// and uploading them periodically to the server.
//
// A given metrics sample can be in one of three locations.
// * in-memory metrics: in memory aggregated metrics, waiting to be staged for
// upload.
// * saved log: protobuf message, written to disk periodically and on shutdown
// to make a backup of metrics data for uploading later.
// * staged log: protobuf message waiting to be uploaded.
//
// The service works as follows:
// On startup, we create the in-memory metrics from the saved log if it exists.
//
// Periodically (every |disk_persistence_interval_| seconds), we take a snapshot
// of the in-memory metrics and save them to disk.
//
// Periodically (every |upload_interval| seconds), we:
// * take a snapshot of the in-memory metrics and create the staged log
// * save the staged log to disk to avoid losing it if metricsd or the system
// crashes between two uploads.
// * delete the last saved log: all the metrics contained in it are also in the
// newly created staged log.
//
// On shutdown (SIGINT or SIGTERM), we save the in-memory metrics to disk.
//
// Note: the in-memory metrics can be stored in |current_log_| or
// base::StatisticsRecorder.
class UploadService : public base::HistogramFlattener, public brillo::Daemon {
public:
UploadService(const std::string& server,
const base::TimeDelta& upload_interval,
const base::TimeDelta& disk_persistence_interval,
const base::FilePath& private_metrics_directory,
const base::FilePath& shared_metrics_directory);
// Initializes the upload service.
int OnInit() override;
// Cleans up the internal state before exiting.
void OnShutdown(int* exit_code) override;
// Starts a new log. The log needs to be regenerated after each successful
// launch as it is destroyed when staging the log.
void StartNewLog();
// Saves the current metrics to a file.
void PersistToDisk();
// Triggers an upload event.
void UploadEvent();
// Sends the staged log.
void SendStagedLog();
// Implements inconsistency detection to match HistogramFlattener's
// interface.
void InconsistencyDetected(
base::HistogramBase::Inconsistency problem) override {}
void UniqueInconsistencyDetected(
base::HistogramBase::Inconsistency problem) override {}
void InconsistencyDetectedInLoggedCount(int amount) override {}
private:
friend class UploadServiceTest;
FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
FRIEND_TEST(UploadServiceTest, CorruptedSavedLog);
FRIEND_TEST(UploadServiceTest, CurrentLogSavedAndResumed);
FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
FRIEND_TEST(UploadServiceTest, LogFromTheMetricsLibrary);
FRIEND_TEST(UploadServiceTest, LogKernelCrash);
FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
FRIEND_TEST(UploadServiceTest, LogUserCrash);
FRIEND_TEST(UploadServiceTest, PersistEmptyLog);
FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
// Initializes the upload service for testing.
void InitForTest(SystemProfileSetter* setter);
// If a staged log fails to upload more than kMaxFailedUpload times, it
// will be discarded.
static const int kMaxFailedUpload;
// Loads the log saved to disk if it exists.
void LoadSavedLog();
// Resets the internal state.
void Reset();
// Returns true iff metrics reporting is enabled.
bool AreMetricsEnabled();
// Event callback for handling Upload events.
void UploadEventCallback();
// Event callback for handling Persist events.
void PersistEventCallback();
// Aggregates all histogram available in memory and store them in the current
// log.
void GatherHistograms();
// Callback for HistogramSnapshotManager to store the histograms.
void RecordDelta(const base::HistogramBase& histogram,
const base::HistogramSamples& snapshot) override;
// Compiles all the samples received into a single protobuf and adds all
// system information.
void StageCurrentLog();
// Returns true iff a log is staged.
bool HasStagedLog();
// Remove the staged log iff the upload failed more than |kMaxFailedUpload|.
void RemoveFailedLog();
// Returns the current log. If there is no current log, creates it first.
MetricsLog* GetOrCreateCurrentLog();
std::unique_ptr<SystemProfileSetter> system_profile_setter_;
base::HistogramSnapshotManager histogram_snapshot_manager_;
std::unique_ptr<Sender> sender_;
chromeos_metrics::PersistentInteger failed_upload_count_;
std::unique_ptr<MetricsLog> current_log_;
std::shared_ptr<CrashCounters> counters_;
base::TimeDelta upload_interval_;
base::TimeDelta disk_persistence_interval_;
MetricsdServiceRunner metricsd_service_runner_;
base::FilePath consent_file_;
base::FilePath staged_log_path_;
base::FilePath saved_log_path_;
bool testing_;
};
#endif // METRICS_UPLOADER_UPLOAD_SERVICE_H_