• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 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 "content/browser/media/webrtc_internals.h"
6 
7 #include "base/path_service.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/media/webrtc_internals_ui_observer.h"
10 #include "content/browser/web_contents/web_contents_view.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/content_browser_client.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/power_save_blocker.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/web_contents.h"
18 
19 using base::ProcessId;
20 using std::string;
21 
22 namespace content {
23 
24 namespace {
25 
26 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
27     LAZY_INSTANCE_INITIALIZER;
28 
29 // Makes sure that |dict| has a ListValue under path "log".
EnsureLogList(base::DictionaryValue * dict)30 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
31   base::ListValue* log = NULL;
32   if (!dict->GetList("log", &log)) {
33     log = new base::ListValue();
34     if (log)
35       dict->Set("log", log);
36   }
37   return log;
38 }
39 
40 }  // namespace
41 
WebRTCInternals()42 WebRTCInternals::WebRTCInternals()
43     : aec_dump_enabled_(false) {
44   registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
45                  NotificationService::AllBrowserContextsAndSources());
46 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
47 // build if WebRTC is disabled?
48 #if defined(ENABLE_WEBRTC)
49   aec_dump_file_path_ =
50       GetContentClient()->browser()->GetDefaultDownloadDirectory();
51   if (aec_dump_file_path_.empty()) {
52     // In this case the default path (|aec_dump_file_path_|) will be empty and
53     // the platform default path will be used in the file dialog (with no
54     // default file name). See SelectFileDialog::SelectFile. On Android where
55     // there's no dialog we'll fail to open the file.
56     VLOG(1) << "Could not get the download directory.";
57   } else {
58     aec_dump_file_path_ =
59         aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
60   }
61 #endif  // defined(ENABLE_WEBRTC)
62 }
63 
~WebRTCInternals()64 WebRTCInternals::~WebRTCInternals() {
65 }
66 
GetInstance()67 WebRTCInternals* WebRTCInternals::GetInstance() {
68   return g_webrtc_internals.Pointer();
69 }
70 
OnAddPeerConnection(int render_process_id,ProcessId pid,int lid,const string & url,const string & rtc_configuration,const string & constraints)71 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
72                                           ProcessId pid,
73                                           int lid,
74                                           const string& url,
75                                           const string& rtc_configuration,
76                                           const string& constraints) {
77   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
78 
79   base::DictionaryValue* dict = new base::DictionaryValue();
80   if (!dict)
81     return;
82 
83   dict->SetInteger("rid", render_process_id);
84   dict->SetInteger("pid", static_cast<int>(pid));
85   dict->SetInteger("lid", lid);
86   dict->SetString("rtcConfiguration", rtc_configuration);
87   dict->SetString("constraints", constraints);
88   dict->SetString("url", url);
89   peer_connection_data_.Append(dict);
90   CreateOrReleasePowerSaveBlocker();
91 
92   if (observers_.might_have_observers())
93     SendUpdate("addPeerConnection", dict);
94 }
95 
OnRemovePeerConnection(ProcessId pid,int lid)96 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
97   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98   for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
99     base::DictionaryValue* dict = NULL;
100     peer_connection_data_.GetDictionary(i, &dict);
101 
102     int this_pid = 0;
103     int this_lid = 0;
104     dict->GetInteger("pid", &this_pid);
105     dict->GetInteger("lid", &this_lid);
106 
107     if (this_pid != static_cast<int>(pid) || this_lid != lid)
108       continue;
109 
110     peer_connection_data_.Remove(i, NULL);
111     CreateOrReleasePowerSaveBlocker();
112 
113     if (observers_.might_have_observers()) {
114       base::DictionaryValue id;
115       id.SetInteger("pid", static_cast<int>(pid));
116       id.SetInteger("lid", lid);
117       SendUpdate("removePeerConnection", &id);
118     }
119     break;
120   }
121 }
122 
OnUpdatePeerConnection(ProcessId pid,int lid,const string & type,const string & value)123 void WebRTCInternals::OnUpdatePeerConnection(
124     ProcessId pid, int lid, const string& type, const string& value) {
125   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
126 
127   for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
128     base::DictionaryValue* record = NULL;
129     peer_connection_data_.GetDictionary(i, &record);
130 
131     int this_pid = 0, this_lid = 0;
132     record->GetInteger("pid", &this_pid);
133     record->GetInteger("lid", &this_lid);
134 
135     if (this_pid != static_cast<int>(pid) || this_lid != lid)
136       continue;
137 
138     // Append the update to the end of the log.
139     base::ListValue* log = EnsureLogList(record);
140     if (!log)
141       return;
142 
143     base::DictionaryValue* log_entry = new base::DictionaryValue();
144     if (!log_entry)
145       return;
146 
147     double epoch_time = base::Time::Now().ToJsTime();
148     string time = base::DoubleToString(epoch_time);
149     log_entry->SetString("time", time);
150     log_entry->SetString("type", type);
151     log_entry->SetString("value", value);
152     log->Append(log_entry);
153 
154     if (observers_.might_have_observers()) {
155       base::DictionaryValue update;
156       update.SetInteger("pid", static_cast<int>(pid));
157       update.SetInteger("lid", lid);
158       update.MergeDictionary(log_entry);
159 
160       SendUpdate("updatePeerConnection", &update);
161     }
162     return;
163   }
164 }
165 
OnAddStats(base::ProcessId pid,int lid,const base::ListValue & value)166 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
167                                  const base::ListValue& value) {
168   if (!observers_.might_have_observers())
169     return;
170 
171   base::DictionaryValue dict;
172   dict.SetInteger("pid", static_cast<int>(pid));
173   dict.SetInteger("lid", lid);
174 
175   base::ListValue* list = value.DeepCopy();
176   if (!list)
177     return;
178 
179   dict.Set("reports", list);
180 
181   SendUpdate("addStats", &dict);
182 }
183 
OnGetUserMedia(int rid,base::ProcessId pid,const std::string & origin,bool audio,bool video,const std::string & audio_constraints,const std::string & video_constraints)184 void WebRTCInternals::OnGetUserMedia(int rid,
185                                      base::ProcessId pid,
186                                      const std::string& origin,
187                                      bool audio,
188                                      bool video,
189                                      const std::string& audio_constraints,
190                                      const std::string& video_constraints) {
191   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
192 
193   base::DictionaryValue* dict = new base::DictionaryValue();
194   dict->SetInteger("rid", rid);
195   dict->SetInteger("pid", static_cast<int>(pid));
196   dict->SetString("origin", origin);
197   if (audio)
198     dict->SetString("audio", audio_constraints);
199   if (video)
200     dict->SetString("video", video_constraints);
201 
202   get_user_media_requests_.Append(dict);
203 
204   if (observers_.might_have_observers())
205     SendUpdate("addGetUserMedia", dict);
206 }
207 
AddObserver(WebRTCInternalsUIObserver * observer)208 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210   observers_.AddObserver(observer);
211 }
212 
RemoveObserver(WebRTCInternalsUIObserver * observer)213 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215   observers_.RemoveObserver(observer);
216 
217   // Disables the AEC recording if it is enabled and the last webrtc-internals
218   // page is going away.
219   if (aec_dump_enabled_ && !observers_.might_have_observers())
220     DisableAecDump();
221 }
222 
UpdateObserver(WebRTCInternalsUIObserver * observer)223 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225   if (peer_connection_data_.GetSize() > 0)
226     observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
227 
228   for (base::ListValue::iterator it = get_user_media_requests_.begin();
229        it != get_user_media_requests_.end();
230        ++it) {
231     observer->OnUpdate("addGetUserMedia", *it);
232   }
233 }
234 
EnableAecDump(content::WebContents * web_contents)235 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
236 #if defined(ENABLE_WEBRTC)
237 #if defined(OS_ANDROID)
238   EnableAecDumpOnAllRenderProcessHosts();
239 #else
240   select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
241   select_file_dialog_->SelectFile(
242       ui::SelectFileDialog::SELECT_SAVEAS_FILE,
243       base::string16(),
244       aec_dump_file_path_,
245       NULL,
246       0,
247       FILE_PATH_LITERAL(""),
248       web_contents->GetTopLevelNativeWindow(),
249       NULL);
250 #endif
251 #endif
252 }
253 
DisableAecDump()254 void WebRTCInternals::DisableAecDump() {
255 #if defined(ENABLE_WEBRTC)
256   aec_dump_enabled_ = false;
257 
258   // Tear down the dialog since the user has unchecked the AEC dump box.
259   select_file_dialog_ = NULL;
260 
261   for (RenderProcessHost::iterator i(
262            content::RenderProcessHost::AllHostsIterator());
263        !i.IsAtEnd(); i.Advance()) {
264     i.GetCurrentValue()->DisableAecDump();
265   }
266 #endif
267 }
268 
ResetForTesting()269 void WebRTCInternals::ResetForTesting() {
270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271   observers_.Clear();
272   peer_connection_data_.Clear();
273   CreateOrReleasePowerSaveBlocker();
274   get_user_media_requests_.Clear();
275   aec_dump_enabled_ = false;
276 }
277 
SendUpdate(const string & command,base::Value * value)278 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
279   DCHECK(observers_.might_have_observers());
280 
281   FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
282                     observers_,
283                     OnUpdate(command, value));
284 }
285 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)286 void WebRTCInternals::Observe(int type,
287                               const NotificationSource& source,
288                               const NotificationDetails& details) {
289   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290   DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
291   OnRendererExit(Source<RenderProcessHost>(source)->GetID());
292 }
293 
FileSelected(const base::FilePath & path,int,void *)294 void WebRTCInternals::FileSelected(const base::FilePath& path,
295                                    int /* unused_index */,
296                                    void* /*unused_params */) {
297 #if defined(ENABLE_WEBRTC)
298   aec_dump_file_path_ = path;
299   EnableAecDumpOnAllRenderProcessHosts();
300 #endif
301 }
302 
FileSelectionCanceled(void * params)303 void WebRTCInternals::FileSelectionCanceled(void* params) {
304 #if defined(ENABLE_WEBRTC)
305   SendUpdate("aecRecordingFileSelectionCancelled", NULL);
306 #endif
307 }
308 
OnRendererExit(int render_process_id)309 void WebRTCInternals::OnRendererExit(int render_process_id) {
310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 
312   // Iterates from the end of the list to remove the PeerConnections created
313   // by the exitting renderer.
314   for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
315     base::DictionaryValue* record = NULL;
316     peer_connection_data_.GetDictionary(i, &record);
317 
318     int this_rid = 0;
319     record->GetInteger("rid", &this_rid);
320 
321     if (this_rid == render_process_id) {
322       if (observers_.might_have_observers()) {
323         int lid = 0, pid = 0;
324         record->GetInteger("lid", &lid);
325         record->GetInteger("pid", &pid);
326 
327         base::DictionaryValue update;
328         update.SetInteger("lid", lid);
329         update.SetInteger("pid", pid);
330         SendUpdate("removePeerConnection", &update);
331       }
332       peer_connection_data_.Remove(i, NULL);
333     }
334   }
335   CreateOrReleasePowerSaveBlocker();
336 
337   bool found_any = false;
338   // Iterates from the end of the list to remove the getUserMedia requests
339   // created by the exiting renderer.
340   for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
341     base::DictionaryValue* record = NULL;
342     get_user_media_requests_.GetDictionary(i, &record);
343 
344     int this_rid = 0;
345     record->GetInteger("rid", &this_rid);
346 
347     if (this_rid == render_process_id) {
348       get_user_media_requests_.Remove(i, NULL);
349       found_any = true;
350     }
351   }
352 
353   if (found_any && observers_.might_have_observers()) {
354     base::DictionaryValue update;
355     update.SetInteger("rid", render_process_id);
356     SendUpdate("removeGetUserMediaForRenderer", &update);
357   }
358 }
359 
360 #if defined(ENABLE_WEBRTC)
EnableAecDumpOnAllRenderProcessHosts()361 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
362   aec_dump_enabled_ = true;
363   for (RenderProcessHost::iterator i(
364            content::RenderProcessHost::AllHostsIterator());
365        !i.IsAtEnd(); i.Advance()) {
366     i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
367   }
368 }
369 #endif
370 
CreateOrReleasePowerSaveBlocker()371 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 
374   if (peer_connection_data_.empty() && power_save_blocker_) {
375     DVLOG(1) << ("Releasing the block on application suspension since no "
376                  "PeerConnections are active anymore.");
377     power_save_blocker_.reset();
378   } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
379     DVLOG(1) << ("Preventing the application from being suspended while one or "
380                  "more PeerConnections are active.");
381     power_save_blocker_ = content::PowerSaveBlocker::Create(
382         content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
383         "WebRTC has active PeerConnections.").Pass();
384   }
385 }
386 
387 }  // namespace content
388