summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc')
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc633
1 files changed, 633 insertions, 0 deletions
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc b/chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc
new file mode 100644
index 00000000000..1b386f4a803
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/webrtc_log_uploader.cc
@@ -0,0 +1,633 @@
+// Copyright 2013 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 "chrome/browser/media/webrtc/webrtc_log_uploader.h"
+
+#include <stddef.h>
+#include <cstdlib>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/pickle.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "components/version_info/version_info.h"
+#include "components/webrtc_logging/browser/log_cleanup.h"
+#include "components/webrtc_logging/browser/text_log_list.h"
+#include "components/webrtc_logging/common/partial_circular_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/zlib/zlib.h"
+
+namespace {
+
+const int kLogCountLimit = 5;
+const uint32_t kIntermediateCompressionBufferBytes = 256 * 1024; // 256 KB
+const int kLogListLimitLines = 50;
+
+const char kWebrtcLogUploadContentType[] = "multipart/form-data";
+const char kWebrtcLogMultipartBoundary[] =
+ "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
+
+// Adds the header section for a gzip file to the multipart |post_data|.
+void AddMultipartFileContentHeader(std::string* post_data,
+ const std::string& content_name) {
+ post_data->append("--");
+ post_data->append(kWebrtcLogMultipartBoundary);
+ post_data->append("\r\nContent-Disposition: form-data; name=\"");
+ post_data->append(content_name);
+ post_data->append("\"; filename=\"");
+ post_data->append(content_name + ".gz");
+ post_data->append("\"\r\nContent-Type: application/gzip\r\n\r\n");
+}
+
+// Adds |compressed_log| to |post_data|.
+void AddLogData(std::string* post_data, const std::string& compressed_log) {
+ AddMultipartFileContentHeader(post_data, "webrtc_log");
+ post_data->append(compressed_log);
+ post_data->append("\r\n");
+}
+
+// Adds the RTP dump data to |post_data|.
+void AddRtpDumpData(std::string* post_data,
+ const std::string& name,
+ const std::string& dump_data) {
+ AddMultipartFileContentHeader(post_data, name);
+ post_data->append(dump_data.data(), dump_data.size());
+ post_data->append("\r\n");
+}
+
+// Helper for WebRtcLogUploader::CompressLog().
+void ResizeForNextOutput(std::string* compressed_log, z_stream* stream) {
+ size_t old_size = compressed_log->size() - stream->avail_out;
+ compressed_log->resize(old_size + kIntermediateCompressionBufferBytes);
+ stream->next_out =
+ reinterpret_cast<unsigned char*>(&(*compressed_log)[old_size]);
+ stream->avail_out = kIntermediateCompressionBufferBytes;
+}
+
+} // namespace
+
+WebRtcLogUploader::UploadDoneData::UploadDoneData() = default;
+WebRtcLogUploader::UploadDoneData::UploadDoneData(
+ const WebRtcLogUploader::UploadDoneData& other) = default;
+WebRtcLogUploader::UploadDoneData::~UploadDoneData() = default;
+
+WebRtcLogUploader::WebRtcLogUploader()
+ : main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
+ background_task_runner_(
+ base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock(),
+ base::TaskPriority::BEST_EFFORT})) {}
+
+WebRtcLogUploader::~WebRtcLogUploader() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+ DCHECK(pending_uploads_.empty());
+ DCHECK(shutdown_);
+}
+
+bool WebRtcLogUploader::ApplyForStartLogging() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+ if (log_count_ < kLogCountLimit && !shutdown_) {
+ ++log_count_;
+ return true;
+ }
+ return false;
+}
+
+void WebRtcLogUploader::LoggingStoppedDontUpload() {
+ DecreaseLogCount();
+}
+
+void WebRtcLogUploader::LoggingStoppedDoUpload(
+ std::unique_ptr<WebRtcLogBuffer> log_buffer,
+ std::unique_ptr<WebRtcLogMetaDataMap> meta_data,
+ const WebRtcLogUploader::UploadDoneData& upload_done_data) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(log_buffer.get());
+ DCHECK(meta_data.get());
+ DCHECK(!upload_done_data.paths.directory.empty());
+
+ std::string compressed_log = CompressLog(log_buffer.get());
+
+ std::string local_log_id;
+
+ if (base::PathExists(upload_done_data.paths.directory)) {
+ webrtc_logging::DeleteOldWebRtcLogFiles(upload_done_data.paths.directory);
+
+ local_log_id = base::NumberToString(base::Time::Now().ToDoubleT());
+ base::FilePath log_file_path =
+ upload_done_data.paths.directory.AppendASCII(local_log_id)
+ .AddExtension(FILE_PATH_LITERAL(".gz"));
+ WriteCompressedLogToFile(compressed_log, log_file_path);
+
+ base::FilePath log_list_path =
+ webrtc_logging::TextLogList::GetWebRtcLogListFileForDirectory(
+ upload_done_data.paths.directory);
+ AddLocallyStoredLogInfoToUploadListFile(log_list_path, local_log_id);
+ }
+
+ UploadDoneData upload_done_data_with_log_id = upload_done_data;
+ upload_done_data_with_log_id.local_log_id = local_log_id;
+ PrepareMultipartPostData(compressed_log, std::move(meta_data),
+ upload_done_data_with_log_id);
+}
+
+void WebRtcLogUploader::PrepareMultipartPostData(
+ const std::string& compressed_log,
+ std::unique_ptr<WebRtcLogMetaDataMap> meta_data,
+ const WebRtcLogUploader::UploadDoneData& upload_done_data) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!compressed_log.empty());
+ DCHECK(meta_data.get());
+
+ std::unique_ptr<std::string> post_data(new std::string());
+ SetupMultipart(post_data.get(), compressed_log,
+ upload_done_data.paths.incoming_rtp_dump,
+ upload_done_data.paths.outgoing_rtp_dump, *meta_data.get());
+
+ // If a test has set the test string pointer, write to it and skip uploading.
+ // Still fire the upload callback so that we can run an extension API test
+ // using the test framework for that without hanging.
+ // TODO(grunell): Remove this when the api test for this feature is fully
+ // implemented according to the test plan. http://crbug.com/257329.
+ if (post_data_) {
+ *post_data_ = *post_data;
+ NotifyUploadDoneAndLogStats(net::HTTP_OK, net::OK, "", upload_done_data);
+ return;
+ }
+
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&WebRtcLogUploader::UploadCompressedLog,
+ base::Unretained(this), upload_done_data,
+ std::move(post_data)));
+}
+
+void WebRtcLogUploader::UploadStoredLog(
+ const WebRtcLogUploader::UploadDoneData& upload_data) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!upload_data.local_log_id.empty());
+ DCHECK(!upload_data.paths.directory.empty());
+
+ base::FilePath native_log_path =
+ upload_data.paths.directory.AppendASCII(upload_data.local_log_id)
+ .AddExtension(FILE_PATH_LITERAL(".gz"));
+
+ std::string compressed_log;
+ if (!base::ReadFileToString(native_log_path, &compressed_log)) {
+ DPLOG(WARNING) << "Could not read WebRTC log file.";
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailed",
+ upload_data.web_app_id);
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailureReason",
+ WebRtcLogUploadFailureReason::kStoredLogNotFound);
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(upload_data.callback, false, "", "Log doesn't exist."));
+ return;
+ }
+
+ UploadDoneData upload_data_with_rtp = upload_data;
+
+ // Optimistically set the rtp paths to what they should be if they exist.
+ upload_data_with_rtp.paths.incoming_rtp_dump =
+ upload_data.paths.directory.AppendASCII(upload_data.local_log_id)
+ .AddExtension(FILE_PATH_LITERAL(".rtp_in"));
+
+ upload_data_with_rtp.paths.outgoing_rtp_dump =
+ upload_data.paths.directory.AppendASCII(upload_data.local_log_id)
+ .AddExtension(FILE_PATH_LITERAL(".rtp_out"));
+
+ std::unique_ptr<WebRtcLogMetaDataMap> meta_data(new WebRtcLogMetaDataMap());
+ {
+ std::string meta_data_contents;
+ base::FilePath meta_path =
+ upload_data.paths.directory.AppendASCII(upload_data.local_log_id)
+ .AddExtension(FILE_PATH_LITERAL(".meta"));
+ if (base::ReadFileToString(meta_path, &meta_data_contents) &&
+ !meta_data_contents.empty()) {
+ base::Pickle pickle(&meta_data_contents[0], meta_data_contents.size());
+ base::PickleIterator it(pickle);
+ std::string key, value;
+ while (it.ReadString(&key) && it.ReadString(&value))
+ (*meta_data.get())[key] = value;
+ }
+ }
+
+ PrepareMultipartPostData(compressed_log, std::move(meta_data),
+ upload_data_with_rtp);
+}
+
+void WebRtcLogUploader::LoggingStoppedDoStore(
+ const WebRtcLogPaths& log_paths,
+ const std::string& log_id,
+ std::unique_ptr<WebRtcLogBuffer> log_buffer,
+ std::unique_ptr<WebRtcLogMetaDataMap> meta_data,
+ const GenericDoneCallback& done_callback) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!log_id.empty());
+ DCHECK(log_buffer.get());
+ DCHECK(!log_paths.directory.empty());
+
+ webrtc_logging::DeleteOldWebRtcLogFiles(log_paths.directory);
+
+ base::FilePath log_list_path =
+ webrtc_logging::TextLogList::GetWebRtcLogListFileForDirectory(
+ log_paths.directory);
+
+ // Store the native log with a ".gz" extension.
+ std::string compressed_log = CompressLog(log_buffer.get());
+ base::FilePath native_log_path =
+ log_paths.directory.AppendASCII(log_id).AddExtension(
+ FILE_PATH_LITERAL(".gz"));
+ WriteCompressedLogToFile(compressed_log, native_log_path);
+ AddLocallyStoredLogInfoToUploadListFile(log_list_path, log_id);
+
+ // Move the rtp dump files to the log directory with a name of
+ // <log id>.rtp_[in|out].
+ if (!log_paths.incoming_rtp_dump.empty()) {
+ base::FilePath rtp_path =
+ log_paths.directory.AppendASCII(log_id).AddExtension(
+ FILE_PATH_LITERAL(".rtp_in"));
+ base::Move(log_paths.incoming_rtp_dump, rtp_path);
+ }
+
+ if (!log_paths.outgoing_rtp_dump.empty()) {
+ base::FilePath rtp_path =
+ log_paths.directory.AppendASCII(log_id).AddExtension(
+ FILE_PATH_LITERAL(".rtp_out"));
+ base::Move(log_paths.outgoing_rtp_dump, rtp_path);
+ }
+
+ if (meta_data.get() && !meta_data->empty()) {
+ base::Pickle pickle;
+ for (const auto& it : *meta_data.get()) {
+ pickle.WriteString(it.first);
+ pickle.WriteString(it.second);
+ }
+ base::FilePath meta_path =
+ log_paths.directory.AppendASCII(log_id).AddExtension(
+ FILE_PATH_LITERAL(".meta"));
+ base::WriteFile(meta_path, static_cast<const char*>(pickle.data()),
+ pickle.size());
+ }
+
+ main_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(done_callback, true, ""));
+
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&WebRtcLogUploader::DecreaseLogCount,
+ base::Unretained(this)));
+}
+
+void WebRtcLogUploader::Shutdown() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+ DCHECK(!shutdown_);
+
+ // Clear the pending uploads list, which will reset all URL loaders.
+ pending_uploads_.clear();
+ shutdown_ = true;
+}
+
+void WebRtcLogUploader::OnSimpleLoaderComplete(
+ SimpleURLLoaderList::iterator it,
+ const WebRtcLogUploader::UploadDoneData& upload_done_data,
+ std::unique_ptr<std::string> response_body) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+ DCHECK(!shutdown_);
+ network::SimpleURLLoader* loader = it->get();
+ base::Optional<int> response_code;
+ if (loader->ResponseInfo() && loader->ResponseInfo()->headers) {
+ response_code = loader->ResponseInfo()->headers->response_code();
+ }
+ const int network_error_code = loader->NetError();
+ pending_uploads_.erase(it);
+ std::string report_id;
+ if (response_body)
+ report_id = std::move(*response_body);
+ // The log path can be empty here if we failed getting it before. We still
+ // upload the log if that's the case.
+ if (!upload_done_data.paths.directory.empty()) {
+ // TODO(jiayl): Add the RTP dump records to chrome://webrtc-logs.
+ base::FilePath log_list_path =
+ webrtc_logging::TextLogList::GetWebRtcLogListFileForDirectory(
+ upload_done_data.paths.directory);
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WebRtcLogUploader::AddUploadedLogInfoToUploadListFile,
+ log_list_path, upload_done_data.local_log_id,
+ report_id));
+ }
+ NotifyUploadDoneAndLogStats(response_code, network_error_code, report_id,
+ upload_done_data);
+}
+
+void WebRtcLogUploader::SetupMultipart(
+ std::string* post_data,
+ const std::string& compressed_log,
+ const base::FilePath& incoming_rtp_dump,
+ const base::FilePath& outgoing_rtp_dump,
+ const std::map<std::string, std::string>& meta_data) {
+#if defined(OS_WIN)
+ const char product[] = "Chrome";
+#elif defined(OS_MACOSX)
+ const char product[] = "Chrome_Mac";
+#elif defined(OS_LINUX)
+#if !defined(ADDRESS_SANITIZER)
+ const char product[] = "Chrome_Linux";
+#else
+ const char product[] = "Chrome_Linux_ASan";
+#endif
+#elif defined(OS_ANDROID)
+ const char product[] = "Chrome_Android";
+#elif defined(OS_CHROMEOS)
+ const char product[] = "Chrome_ChromeOS";
+#else
+#error Platform not supported.
+#endif
+ net::AddMultipartValueForUpload("prod", product, kWebrtcLogMultipartBoundary,
+ "", post_data);
+ net::AddMultipartValueForUpload("ver",
+ version_info::GetVersionNumber() + "-webrtc",
+ kWebrtcLogMultipartBoundary, "", post_data);
+ net::AddMultipartValueForUpload("guid", "0", kWebrtcLogMultipartBoundary, "",
+ post_data);
+ net::AddMultipartValueForUpload("type", "webrtc_log",
+ kWebrtcLogMultipartBoundary, "", post_data);
+
+ // Add custom meta data.
+ for (const auto& it : meta_data) {
+ net::AddMultipartValueForUpload(it.first, it.second,
+ kWebrtcLogMultipartBoundary, "", post_data);
+ }
+
+ AddLogData(post_data, compressed_log);
+
+ // Add the rtp dumps if they exist.
+ base::FilePath rtp_dumps[2] = {incoming_rtp_dump, outgoing_rtp_dump};
+ static const char* const kRtpDumpNames[2] = {"rtpdump_recv", "rtpdump_send"};
+
+ for (size_t i = 0; i < 2; ++i) {
+ if (!rtp_dumps[i].empty() && base::PathExists(rtp_dumps[i])) {
+ std::string dump_data;
+ if (base::ReadFileToString(rtp_dumps[i], &dump_data))
+ AddRtpDumpData(post_data, kRtpDumpNames[i], dump_data);
+ }
+ }
+
+ net::AddMultipartFinalDelimiterForUpload(kWebrtcLogMultipartBoundary,
+ post_data);
+}
+
+std::string WebRtcLogUploader::CompressLog(WebRtcLogBuffer* buffer) {
+ z_stream stream = {0};
+ int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ // windowBits = 15 is default, 16 is added to
+ // produce a gzip header + trailer.
+ 15 + 16,
+ 8, // memLevel = 8 is default.
+ Z_DEFAULT_STRATEGY);
+ DCHECK_EQ(Z_OK, result);
+
+ std::string compressed_log;
+ ResizeForNextOutput(&compressed_log, &stream);
+
+ uint8_t intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
+ webrtc_logging::PartialCircularBuffer read_buffer(buffer->Read());
+ do {
+ if (stream.avail_in == 0) {
+ uint32_t read = read_buffer.Read(&intermediate_buffer[0],
+ sizeof(intermediate_buffer));
+ stream.next_in = &intermediate_buffer[0];
+ stream.avail_in = read;
+ if (read != kIntermediateCompressionBufferBytes)
+ break;
+ }
+ result = deflate(&stream, Z_SYNC_FLUSH);
+ DCHECK_EQ(Z_OK, result);
+ if (stream.avail_out == 0)
+ ResizeForNextOutput(&compressed_log, &stream);
+ } while (true);
+
+ // Ensure we have enough room in the output buffer. Easier to always just do a
+ // resize than looping around and resize if needed.
+ if (stream.avail_out < kIntermediateCompressionBufferBytes)
+ ResizeForNextOutput(&compressed_log, &stream);
+
+ result = deflate(&stream, Z_FINISH);
+ DCHECK_EQ(Z_STREAM_END, result);
+ result = deflateEnd(&stream);
+ DCHECK_EQ(Z_OK, result);
+
+ compressed_log.resize(compressed_log.size() - stream.avail_out);
+ return compressed_log;
+}
+
+void WebRtcLogUploader::UploadCompressedLog(
+ const WebRtcLogUploader::UploadDoneData& upload_done_data,
+ std::unique_ptr<std::string> post_data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+
+ DecreaseLogCount();
+
+ // We don't log upload failure to UMA in case of shutting down for
+ // consistency, since there are other cases during shutdown were we don't get
+ // a chance to log.
+ if (shutdown_)
+ return;
+
+ std::string content_type = kWebrtcLogUploadContentType;
+ content_type.append("; boundary=");
+ content_type.append(kWebrtcLogMultipartBoundary);
+
+ // Create traffic annotation tag.
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("webrtc_log_upload", R"(
+ semantics {
+ sender: "Webrtc Log Uploader"
+ description: "Uploads WebRTC debug logs for Hangouts."
+ trigger:
+ "When a Hangouts extension or Hangouts services extension signals "
+ "to upload via the private WebRTC logging extension API."
+ data:
+ "WebRTC specific log entries, additional system information, and "
+ "RTP packet headers for incoming and outgoing WebRTC streams. "
+ "Audio or video data is never sent."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "This feature can be disabled by unchecking 'Report additional "
+ "diagnostics to help improve Hangouts.' in Hangouts settings."
+ policy_exception_justification:
+ "Not implemented, it would be good to do so."
+ })");
+
+ constexpr char kUploadURL[] = "https://clients2.google.com/cr/report";
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = !upload_url_for_testing_.is_empty()
+ ? upload_url_for_testing_
+ : GURL(kUploadURL);
+ resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+ resource_request->method = "POST";
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ simple_url_loader->AttachStringForUpload(*post_data, content_type);
+ auto it = pending_uploads_.insert(pending_uploads_.begin(),
+ std::move(simple_url_loader));
+ network::SimpleURLLoader* raw_loader = it->get();
+ raw_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ g_browser_process->shared_url_loader_factory().get(),
+ base::BindOnce(&WebRtcLogUploader::OnSimpleLoaderComplete,
+ base::Unretained(this), std::move(it), upload_done_data));
+}
+
+void WebRtcLogUploader::DecreaseLogCount() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+ --log_count_;
+}
+
+void WebRtcLogUploader::WriteCompressedLogToFile(
+ const std::string& compressed_log,
+ const base::FilePath& log_file_path) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!compressed_log.empty());
+ base::WriteFile(log_file_path, &compressed_log[0], compressed_log.size());
+}
+
+void WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile(
+ const base::FilePath& upload_list_path,
+ const std::string& local_log_id) {
+ DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!upload_list_path.empty());
+ DCHECK(!local_log_id.empty());
+
+ std::string contents;
+
+ if (base::PathExists(upload_list_path)) {
+ if (!base::ReadFileToString(upload_list_path, &contents)) {
+ DPLOG(WARNING) << "Could not read WebRTC log list file.";
+ return;
+ }
+
+ // Limit the number of log entries to |kLogListLimitLines| - 1, to make room
+ // for the new entry. Each line including the last ends with a '\n', so hit
+ // n will be before line n-1 (from the back).
+ int lf_count = 0;
+ int i = contents.size() - 1;
+ for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
+ if (contents[i] == '\n')
+ ++lf_count;
+ }
+ if (lf_count >= kLogListLimitLines) {
+ // + 1 to compensate for the for loop decrease before the conditional
+ // check and + 1 to get the length.
+ contents.erase(0, i + 2);
+ }
+ }
+
+ // Write the log ID and capture time to the log list file. Leave the upload
+ // time and report ID empty.
+ contents += ",," + local_log_id + "," +
+ base::NumberToString(base::Time::Now().ToDoubleT()) + '\n';
+
+ int written =
+ base::WriteFile(upload_list_path, &contents[0], contents.size());
+ if (written != static_cast<int>(contents.size())) {
+ DPLOG(WARNING) << "Could not write all data to WebRTC log list file: "
+ << written;
+ }
+}
+
+// static
+void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
+ const base::FilePath& upload_list_path,
+ const std::string& local_log_id,
+ const std::string& report_id) {
+ DCHECK(!upload_list_path.empty());
+ DCHECK(!local_log_id.empty());
+ DCHECK(!report_id.empty());
+
+ std::string contents;
+
+ if (base::PathExists(upload_list_path)) {
+ if (!base::ReadFileToString(upload_list_path, &contents)) {
+ DPLOG(WARNING) << "Could not read WebRTC log list file.";
+ return;
+ }
+ }
+
+ // Write the Unix time and report ID to the log list file. We should be able
+ // to find the local log ID, in that case insert the data into the existing
+ // line. Otherwise add it in the end.
+ base::Time time_now = base::Time::Now();
+ std::string time_now_str = base::NumberToString(time_now.ToDoubleT());
+ size_t pos = contents.find(",," + local_log_id);
+ if (pos != std::string::npos) {
+ contents.insert(pos, time_now_str);
+ contents.insert(pos + time_now_str.length() + 1, report_id);
+ } else {
+ contents += time_now_str + "," + report_id + ",," + time_now_str + "\n";
+ }
+
+ int written =
+ base::WriteFile(upload_list_path, &contents[0], contents.size());
+ if (written != static_cast<int>(contents.size())) {
+ DPLOG(WARNING) << "Could not write all data to WebRTC log list file: "
+ << written;
+ }
+}
+
+void WebRtcLogUploader::NotifyUploadDoneAndLogStats(
+ base::Optional<int> response_code,
+ int network_error_code,
+ const std::string& report_id,
+ const WebRtcLogUploader::UploadDoneData& upload_done_data) {
+ if (upload_done_data.callback.is_null())
+ return;
+
+ const bool success = response_code == net::HTTP_OK;
+ std::string error_message;
+ if (success) {
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadSuccessful",
+ upload_done_data.web_app_id);
+ } else {
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailed",
+ upload_done_data.web_app_id);
+ if (response_code.has_value()) {
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailureReason",
+ response_code.value());
+ } else {
+ DCHECK_NE(network_error_code, net::OK);
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailureReason",
+ WebRtcLogUploadFailureReason::kNetworkError);
+ base::UmaHistogramSparse("WebRtcTextLogging.UploadFailureNetErrorCode",
+ std::abs(network_error_code));
+ }
+ error_message = base::StrCat(
+ {"Uploading failed, response code: ",
+ response_code.has_value() ? base::NumberToString(response_code.value())
+ : "<no value>"});
+ }
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(upload_done_data.callback, success, report_id,
+ error_message));
+}