1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/media/webrtc_logging_handler_host.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/cpu.h"
12#include "base/logging.h"
13#include "base/prefs/pref_service.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/sys_info.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/chromeos/settings/cros_settings.h"
18#include "chrome/browser/media/webrtc_log_upload_list.h"
19#include "chrome/browser/media/webrtc_log_uploader.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/common/chrome_switches.h"
22#include "chrome/common/media/webrtc_logging_messages.h"
23#include "chrome/common/partial_circular_buffer.h"
24#include "chrome/common/pref_names.h"
25#include "chromeos/settings/cros_settings_names.h"
26#include "content/public/browser/browser_thread.h"
27#include "content/public/browser/content_browser_client.h"
28#include "content/public/browser/gpu_data_manager.h"
29#include "content/public/browser/render_process_host.h"
30#include "gpu/config/gpu_info.h"
31#include "net/base/address_family.h"
32#include "net/base/net_util.h"
33#include "net/url_request/url_request_context_getter.h"
34
35#if defined(OS_LINUX)
36#include "base/linux_util.h"
37#endif
38
39#if defined(OS_MACOSX)
40#include "base/mac/mac_util.h"
41#endif
42
43using base::IntToString;
44using content::BrowserThread;
45
46
47#if defined(OS_ANDROID)
48const size_t kWebRtcLogSize = 1 * 1024 * 1024;  // 1 MB
49#else
50const size_t kWebRtcLogSize = 6 * 1024 * 1024;  // 6 MB
51#endif
52
53namespace {
54
55const char kLogNotStoppedOrNoLogOpen[] =
56    "Logging not stopped or no log open.";
57
58// For privacy reasons when logging IP addresses. The returned "sensitive
59// string" is for release builds a string with the end stripped away. Last
60// octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
61// "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
62// not stripped.
63std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) {
64#if defined(NDEBUG)
65  std::string sensitive_address;
66  switch (net::GetAddressFamily(address)) {
67    case net::ADDRESS_FAMILY_IPV4: {
68      sensitive_address = net::IPAddressToString(address);
69      size_t find_pos = sensitive_address.rfind('.');
70      if (find_pos == std::string::npos)
71        return std::string();
72      sensitive_address.resize(find_pos);
73      sensitive_address += ".x";
74      break;
75    }
76    case net::ADDRESS_FAMILY_IPV6: {
77      // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
78      // that the end has been stripped out.
79      net::IPAddressNumber sensitive_address_number = address;
80      sensitive_address_number.resize(net::kIPv6AddressSize - 10);
81      sensitive_address_number.resize(net::kIPv6AddressSize, 0);
82      sensitive_address = net::IPAddressToString(sensitive_address_number);
83      break;
84    }
85    case net::ADDRESS_FAMILY_UNSPECIFIED: {
86      break;
87    }
88  }
89  return sensitive_address;
90#else
91  return net::IPAddressToString(address);
92#endif
93}
94
95}  // namespace
96
97WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
98    : profile_(profile),
99      logging_state_(CLOSED),
100      upload_log_on_render_close_(false) {
101  DCHECK(profile_);
102}
103
104WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
105
106void WebRtcLoggingHandlerHost::SetMetaData(
107    const std::map<std::string, std::string>& meta_data,
108    const GenericDoneCallback& callback) {
109  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
110  DCHECK(!callback.is_null());
111
112  bool success = false;
113  std::string error_message;
114  if (logging_state_ == CLOSED) {
115    meta_data_ = meta_data;
116    success = true;
117  } else {
118    error_message = "Meta data must be set before starting";
119  }
120  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
121                                   base::Bind(callback, success,
122                                              error_message));
123}
124
125void WebRtcLoggingHandlerHost::StartLogging(
126    const GenericDoneCallback& callback) {
127  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
128  DCHECK(!callback.is_null());
129
130  start_callback_ = callback;
131  if (logging_state_ != CLOSED) {
132    FireGenericDoneCallback(&start_callback_, false, "A log is already open");
133    return;
134  }
135  logging_state_ = STARTING;
136  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
137      &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
138}
139
140void WebRtcLoggingHandlerHost::StopLogging(
141    const GenericDoneCallback& callback) {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143  DCHECK(!callback.is_null());
144
145  stop_callback_ = callback;
146  if (logging_state_ != STARTED) {
147    FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
148    return;
149  }
150  logging_state_ = STOPPING;
151  Send(new WebRtcLoggingMsg_StopLogging());
152}
153
154void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) {
155  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
156  DCHECK(!callback.is_null());
157
158  if (logging_state_ != STOPPED) {
159    if (!callback.is_null()) {
160      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
161          base::Bind(callback, false, "", kLogNotStoppedOrNoLogOpen));
162    }
163    return;
164  }
165  upload_callback_ = callback;
166  TriggerUploadLog();
167}
168
169void WebRtcLoggingHandlerHost::UploadLogDone() {
170  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
171  logging_state_ = CLOSED;
172}
173
174void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) {
175  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
176  DCHECK(!callback.is_null());
177
178  GenericDoneCallback discard_callback = callback;
179  if (logging_state_ != STOPPED) {
180    FireGenericDoneCallback(&discard_callback, false,
181                            kLogNotStoppedOrNoLogOpen);
182    return;
183  }
184  g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
185  circular_buffer_.reset();
186  log_buffer_.reset();
187  logging_state_ = CLOSED;
188  FireGenericDoneCallback(&discard_callback, true, "");
189}
190
191void WebRtcLoggingHandlerHost::OnChannelClosing() {
192  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
193  if (logging_state_ == STARTED || logging_state_ == STOPPED) {
194    if (upload_log_on_render_close_) {
195      logging_state_ = STOPPED;
196      TriggerUploadLog();
197    } else {
198      g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
199    }
200  }
201  content::BrowserMessageFilter::OnChannelClosing();
202}
203
204void WebRtcLoggingHandlerHost::OnDestruct() const {
205  BrowserThread::DeleteOnIOThread::Destruct(this);
206}
207
208bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message,
209                                                 bool* message_was_ok) {
210  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211  bool handled = true;
212  IPC_BEGIN_MESSAGE_MAP_EX(WebRtcLoggingHandlerHost, message, *message_was_ok)
213    IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessage, OnAddLogMessage)
214    IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped,
215                        OnLoggingStoppedInRenderer)
216    IPC_MESSAGE_UNHANDLED(handled = false)
217  IPC_END_MESSAGE_MAP_EX()
218
219  return handled;
220}
221
222void WebRtcLoggingHandlerHost::OnAddLogMessage(const std::string& message) {
223  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224  if (logging_state_ == STARTED || logging_state_ == STOPPING) {
225    DCHECK(circular_buffer_.get());
226    circular_buffer_->Write(message.c_str(), message.length());
227    const char eol = '\n';
228    circular_buffer_->Write(&eol, 1);
229  }
230}
231
232void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
233  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
234  logging_state_ = STOPPED;
235  FireGenericDoneCallback(&stop_callback_, true, "");
236}
237
238void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
239  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
240  if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) {
241    logging_state_ = CLOSED;
242      FireGenericDoneCallback(
243          &start_callback_, false, "Cannot start, maybe the maximum number of "
244          "simultaneuos logs has been reached.");
245    return;
246  }
247  system_request_context_ = g_browser_process->system_request_context();
248  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
249      &WebRtcLoggingHandlerHost::DoStartLogging, this));
250}
251
252void WebRtcLoggingHandlerHost::DoStartLogging() {
253  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
254
255  log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
256  circular_buffer_.reset(
257    new PartialCircularBuffer(log_buffer_.get(),
258                              kWebRtcLogSize,
259                              kWebRtcLogSize / 2,
260                              false));
261
262  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
263      &WebRtcLoggingHandlerHost::LogMachineInfo, this));
264}
265
266void WebRtcLoggingHandlerHost::LogMachineInfo() {
267  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
268
269  // Meta data
270  std::string info;
271  std::map<std::string, std::string>::iterator it = meta_data_.begin();
272  for (; it != meta_data_.end(); ++it) {
273    info = it->first + ": " + it->second + '\n';
274    circular_buffer_->Write(info.c_str(), info.length());
275  }
276
277  // OS
278  info = base::SysInfo::OperatingSystemName() + " " +
279         base::SysInfo::OperatingSystemVersion() + " " +
280         base::SysInfo::OperatingSystemArchitecture() + '\n';
281  circular_buffer_->Write(info.c_str(), info.length());
282#if defined(OS_LINUX)
283  info = "Linux distribution: " + base::GetLinuxDistro() + '\n';
284  circular_buffer_->Write(info.c_str(), info.length());
285#endif
286
287  // CPU
288  base::CPU cpu;
289  info = "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) +
290         "." + IntToString(cpu.stepping()) +
291         ", x" + IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
292         IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB" + '\n';
293  circular_buffer_->Write(info.c_str(), info.length());
294  std::string cpu_brand = cpu.cpu_brand();
295  // Workaround for crbug.com/249713.
296  // TODO(grunell): Remove workaround when bug is fixed.
297  size_t null_pos = cpu_brand.find('\0');
298  if (null_pos != std::string::npos)
299    cpu_brand.erase(null_pos);
300  info = "Cpu brand: " + cpu_brand + '\n';
301  circular_buffer_->Write(info.c_str(), info.length());
302
303  // Computer model
304#if defined(OS_MACOSX)
305  info = "Computer model: " + base::mac::GetModelIdentifier() + '\n';
306#else
307  info = "Computer model: Not available\n";
308#endif
309  circular_buffer_->Write(info.c_str(), info.length());
310
311  // GPU
312  gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
313  info = "Gpu: machine-model='" + gpu_info.machine_model +
314         "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) +
315         ", device-id=" + IntToString(gpu_info.gpu.device_id) +
316         ", driver-vendor='" + gpu_info.driver_vendor +
317         "', driver-version=" + gpu_info.driver_version + '\n';
318  circular_buffer_->Write(info.c_str(), info.length());
319
320  // Network interfaces
321  net::NetworkInterfaceList network_list;
322  net::GetNetworkList(&network_list);
323  info  = "Discovered " + IntToString(network_list.size()) +
324          " network interfaces:" + '\n';
325  circular_buffer_->Write(info.c_str(), info.length());
326  for (net::NetworkInterfaceList::iterator it = network_list.begin();
327       it != network_list.end(); ++it) {
328    info = "Name: " + it->name +
329           ", Address: " + IPAddressToSensitiveString(it->address) + '\n';
330    circular_buffer_->Write(info.c_str(), info.length());
331  }
332
333  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
334      &WebRtcLoggingHandlerHost::NotifyLoggingStarted, this));
335}
336
337void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
338  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
339  Send(new WebRtcLoggingMsg_StartLogging());
340  logging_state_ = STARTED;
341  FireGenericDoneCallback(&start_callback_, true, "");
342}
343
344void WebRtcLoggingHandlerHost::TriggerUploadLog() {
345  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
346  DCHECK(logging_state_ == STOPPED);
347
348  logging_state_ = UPLOADING;
349  WebRtcLogUploadDoneData upload_done_data;
350  upload_done_data.upload_list_path =
351      WebRtcLogUploadList::GetFilePathForProfile(profile_);
352  upload_done_data.callback = upload_callback_;
353  upload_done_data.host = this;
354  upload_callback_.Reset();
355
356  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
357      &WebRtcLogUploader::LoggingStoppedDoUpload,
358      base::Unretained(g_browser_process->webrtc_log_uploader()),
359      system_request_context_,
360      Passed(&log_buffer_),
361      kWebRtcLogSize,
362      meta_data_,
363      upload_done_data));
364
365  meta_data_.clear();
366  circular_buffer_.reset();
367}
368
369void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
370    GenericDoneCallback* callback, bool success,
371    const std::string& error_message) {
372  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
373  DCHECK(!(*callback).is_null());
374  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
375                                   base::Bind(*callback, success,
376                                              error_message));
377  (*callback).Reset();
378}
379