1 // Copyright 2018 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 #ifndef CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_
6 #define CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_
7 
8 #include <map>
9 #include <set>
10 #include <string>
11 #include <vector>
12 
13 #include "base/memory/weak_ptr.h"
14 #include "base/optional.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/media/webrtc/webrtc_event_log_history.h"
18 #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h"
19 #include "chrome/browser/media/webrtc/webrtc_event_log_uploader.h"
20 #include "components/upload_list/upload_list.h"
21 #include "services/network/public/cpp/network_connection_tracker.h"
22 
23 namespace webrtc_event_logging {
24 
25 class WebRtcRemoteEventLogManager final
26     : public network::NetworkConnectionTracker::NetworkConnectionObserver {
27   using BrowserContextId = WebRtcEventLogPeerConnectionKey::BrowserContextId;
28   using LogFilesMap =
29       std::map<WebRtcEventLogPeerConnectionKey, std::unique_ptr<LogFileWriter>>;
30   using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
31 
32  public:
33   WebRtcRemoteEventLogManager(
34       WebRtcRemoteEventLogsObserver* observer,
35       scoped_refptr<base::SequencedTaskRunner> task_runner);
36   ~WebRtcRemoteEventLogManager() override;
37 
38   // Sets a network::NetworkConnectionTracker which will be used to track
39   // network connectivity.
40   // Must not be called more than once.
41   // Must be called before any call to EnableForBrowserContext().
42   void SetNetworkConnectionTracker(
43       network::NetworkConnectionTracker* network_connection_tracker);
44 
45   // Sets a LogFileWriter factory.
46   // Must not be called more than once.
47   // Must be called before any call to EnableForBrowserContext().
48   void SetLogFileWriterFactory(
49       std::unique_ptr<LogFileWriter::Factory> log_file_writer_factory);
50 
51   // Enables remote-bound logging for a given BrowserContext. Logs stored during
52   // previous sessions become eligible for upload, and recording of new logs for
53   // peer connections associated with this BrowserContext, in the
54   // BrowserContext's user-data directory, becomes possible.
55   // This method would typically be called when a BrowserContext is initialized.
56   // Enabling for the same BrowserContext twice in a row, without disabling
57   // in between, is an error.
58   void EnableForBrowserContext(BrowserContextId browser_context_id,
59                                const base::FilePath& browser_context_dir);
60 
61   // Disables remote-bound logging for a given BrowserContext. Pending logs from
62   // earlier (while it was enabled) may no longer be uploaded, additional
63   // logs will not be created, and any active uploads associated with the
64   // BrowserContext will be cancelled.
65   // Disabling for a BrowserContext which was not enabled is not an error,
66   // because the caller is not required to know whether a previous call
67   // to EnableForBrowserContext() was successful.
68   void DisableForBrowserContext(BrowserContextId browser_context_id);
69 
70   // Called to inform |this| of peer connections being added/removed.
71   // This information is used to:
72   // 1. Make decisions about when to upload previously finished logs.
73   // 2. When a peer connection is removed, if it was being logged, its log
74   //    changes from ACTIVE to PENDING.
75   // The return value of both methods indicates only the consistency of the
76   // information with previously received information (e.g. can't remove a
77   // peer connection that was never added, etc.).
78   bool PeerConnectionAdded(const PeerConnectionKey& key);
79   bool PeerConnectionRemoved(const PeerConnectionKey& key);
80 
81   // Called to inform |this| that a peer connection has been associated
82   // with |session_id|. After this, it is possible to refer to  that peer
83   // connection using StartRemoteLogging() by providing |session_id|.
84   bool PeerConnectionSessionIdSet(const PeerConnectionKey& key,
85                                   const std::string& session_id);
86 
87   // Attempt to start logging the WebRTC events of an active peer connection.
88   // Logging is subject to several restrictions:
89   // 1. May not log more than kMaxNumberActiveRemoteWebRtcEventLogFiles logs
90   //    at the same time.
91   // 2. Each browser context may have only kMaxPendingLogFilesPerBrowserContext
92   //    pending logs. Since active logs later become pending logs, it is also
93   //    forbidden to start a remote-bound log that would, once completed, become
94   //    a pending log that would exceed that limit.
95   // 3. The maximum file size must be sensible.
96   //
97   // If all of the restrictions were observed, and if a file was successfully
98   // created, true will be returned.
99   //
100   // If the call succeeds, the log's identifier will be written to |log_id|.
101   // The log identifier is exactly 32 uppercase ASCII characters from the
102   // ranges 0-9 and A-F.
103   //
104   // The log's filename will also incorporate |web_app_id|.
105   // |web_app_id| must be between 1 and 99 (inclusive); error otherwise.
106   //
107   // If the call fails, an error message is written to |error_message|.
108   // The error message will be specific to the failure (as opposed to a generic
109   // one) is produced only if that error message is useful for the caller:
110   // * Bad parameters.
111   // * Function called at a time when the caller could know it would fail,
112   //   such as for a peer connection that was already logged.
113   // We intentionally avoid giving specific errors in some cases, so as
114   // to avoid leaking information such as having too many active and/or
115   // pending logs.
116   bool StartRemoteLogging(int render_process_id,
117                           BrowserContextId browser_context_id,
118                           const std::string& session_id,
119                           const base::FilePath& browser_context_dir,
120                           size_t max_file_size_bytes,
121                           int output_period_ms,
122                           size_t web_app_id,
123                           std::string* log_id,
124                           std::string* error_message);
125 
126   // If an active remote-bound log exists for the given peer connection, this
127   // will append |message| to that log.
128   // If writing |message| to the log would exceed the log's maximum allowed
129   // size, the write is disallowed and the file is closed instead (and changes
130   // from ACTIVE to PENDING).
131   // If the log file's capacity is exhausted as a result of this function call,
132   // or if a write error occurs, the file is closed, and the remote-bound log
133   // changes from ACTIVE to PENDING.
134   // True is returned if and only if |message| was written in its entirety to
135   // an active log.
136   bool EventLogWrite(const PeerConnectionKey& key, const std::string& message);
137 
138   // Clear PENDING WebRTC event logs associated with a given browser context,
139   // in a given time range, then post |reply| back to the thread from which
140   // the method was originally invoked (which can be any thread).
141   // Log files currently being written are *not* interrupted.
142   // Active uploads *are* interrupted.
143   void ClearCacheForBrowserContext(BrowserContextId browser_context_id,
144                                    const base::Time& delete_begin,
145                                    const base::Time& delete_end);
146 
147   // See documentation of same method in WebRtcEventLogManager for details.
148   void GetHistory(
149       BrowserContextId browser_context_id,
150       base::OnceCallback<void(const std::vector<UploadList::UploadInfo>&)>
151           reply);
152 
153   // Works on not-enabled BrowserContext-s, which means the logs are never made
154   // eligible for upload. Useful when a BrowserContext is loaded which in
155   // the past had remote-logging enabled, but no longer does.
156   void RemovePendingLogsForNotEnabledBrowserContext(
157       BrowserContextId browser_context_id,
158       const base::FilePath& browser_context_dir);
159 
160   // An implicit PeerConnectionRemoved() on all of the peer connections that
161   // were associated with the renderer process.
162   void RenderProcessHostExitedDestroyed(int render_process_id);
163 
164   // network::NetworkConnectionTracker::NetworkConnectionObserver implementation
165   void OnConnectionChanged(network::mojom::ConnectionType type) override;
166 
167   // Unit tests may use this to inject null uploaders, or ones which are
168   // directly controlled by the unit test (succeed or fail according to the
169   // test's needs).
170   // Note that for simplicity's sake, this may be called from outside the
171   // task queue on which this object lives (WebRtcEventLogManager::task_queue_).
172   // Therefore, if a test calls this, it should call it before it initializes
173   // any BrowserContext with pending log files in its directory.
174   void SetWebRtcEventLogUploaderFactoryForTesting(
175       std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory);
176 
177   // Exposes UploadConditionsHold() to unit tests. See WebRtcEventLogManager's
178   // documentation for the rationale.
179   void UploadConditionsHoldForTesting(base::OnceCallback<void(bool)> callback);
180 
181   // In production code, |task_runner_| stops running tasks as part of Chrome's
182   // shut-down process, before |this| is torn down. In unit tests, this is
183   // not the case.
184   void ShutDownForTesting(base::OnceClosure reply);
185 
186  private:
187   using PeerConnectionMap = std::map<PeerConnectionKey, std::string>;
188 
189   // Validates log parameters.
190   // If valid, returns true. Otherwise, false, and |error_message| gets
191   // a relevant error.
192   bool AreLogParametersValid(size_t max_file_size_bytes,
193                              int output_period_ms,
194                              size_t web_app_id,
195                              std::string* error_message) const;
196 
197   // Checks whether a browser context has already been enabled via a call to
198   // EnableForBrowserContext(), and not yet disabled using a call to
199   // DisableForBrowserContext().
200   bool BrowserContextEnabled(BrowserContextId browser_context_id) const;
201 
202   // Closes an active log file.
203   // If |make_pending| is true, closing the file changes its state from ACTIVE
204   // to PENDING. If |make_pending| is false, or if the file couldn't be closed
205   // correctly, the file will be deleted.
206   // Returns an iterator to the next ACTIVE file.
207   LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it,
208                                      bool make_pending);
209 
210   // Attempts to create the directory where we'll write the logs, if it does
211   // not already exist. Returns true if the directory exists (either it already
212   // existed, or it was successfully created).
213   bool MaybeCreateLogsDirectory(const base::FilePath& remote_bound_logs_dir);
214 
215   // Scans the user data directory associated with the BrowserContext
216   // associated with the given BrowserContextId remote-bound logs that were
217   // created during previous Chrome sessions and for history files,
218   // then process them (discard expired files, etc.)
219   void LoadLogsDirectory(BrowserContextId browser_context_id,
220                          const base::FilePath& remote_bound_logs_dir);
221 
222   // Loads the pending log file whose path is |path|, into the BrowserContext
223   // indicated by |browser_context_id|. Note that the contents of the file are
224   // note read by this method.
225   // Returns true if the file was loaded correctly, and should be kept on disk;
226   // false if the file was not loaded (e.g. incomplete or expired), and needs
227   // to be deleted.
228   bool LoadPendingLogInfo(BrowserContextId browser_context_id,
229                           const base::FilePath& path,
230                           base::Time last_modified);
231 
232   // Loads a history file. Returns a WebRtcEventLogHistoryFileReader if the
233   // file was loaded correctly, and should be kept on disk; nullptr otherwise,
234   // signaling that the file should be deleted.
235   // |prune_begin| and |prune_end| define a time range where, if the log falls
236   // within the range, it will not be loaded.
237   std::unique_ptr<WebRtcEventLogHistoryFileReader> LoadHistoryFile(
238       BrowserContextId browser_context_id,
239       const base::FilePath& path,
240       const base::Time& prune_begin,
241       const base::Time& prune_end);
242 
243   // Deletes any history logs associated with |browser_context_id| captured or
244   // uploaded between |prune_begin| and |prune_end|, inclusive, then returns a
245   // set of readers for the remaining (meaning not-pruned) history files.
246   std::set<WebRtcEventLogHistoryFileReader>
247   PruneAndLoadHistoryFilesForBrowserContext(
248       const base::Time& prune_begin,
249       const base::Time& prune_end,
250       BrowserContextId browser_context_id);
251 
252   // Attempts the creation of a locally stored file into which a remote-bound
253   // log may be written. The log-identifier is returned if successful, the empty
254   // string otherwise.
255   bool StartWritingLog(const PeerConnectionKey& key,
256                        const base::FilePath& browser_context_dir,
257                        size_t max_file_size_bytes,
258                        int output_period_ms,
259                        size_t web_app_id,
260                        std::string* log_id_out,
261                        std::string* error_message_out);
262 
263   // Checks if the referenced peer connection has an associated active
264   // remote-bound log. If it does, the log is changed from ACTIVE to PENDING.
265   void MaybeStopRemoteLogging(const PeerConnectionKey& key);
266 
267   // Get rid of pending logs whose age exceeds our retention policy.
268   // On the one hand, we want to remove expired files as soon as possible, but
269   // on the other hand, we don't want to waste CPU by checking this too often.
270   // Therefore, we prune pending files:
271   // 1. When a new BrowserContext is initalized, thereby also pruning the
272   //    pending logs contributed by that BrowserContext.
273   // 2. Before initiating a new upload, thereby avoiding uploading a file that
274   //    has just now expired.
275   // 3. On infrequent events - peer connection addition/removal, but NOT
276   //    on something that could potentially be frequent, such as EventLogWrite.
277   // Note that the last modification date of a file, which is the value measured
278   // against for retention, is only read from disk once per file, meaning
279   // this check is not too expensive.
280   // If a |browser_context_id| is provided, logs are only pruned for it.
281   void PrunePendingLogs(
282       base::Optional<BrowserContextId> browser_context_id = base::nullopt);
283 
284   // PrunePendingLogs() and schedule the next proactive pending logs prune.
285   void RecurringlyPrunePendingLogs();
286 
287   // Removes expired history files.
288   // Since these are small, and since looking for them is not as cheap as
289   // looking for pending logs, we do not make an effort to remove them as
290   // soon as possible.
291   void PruneHistoryFiles();
292 
293   // PruneHistoryFiles() and schedule the next proactive history files prune.
294   void RecurringlyPruneHistoryFiles();
295 
296   // Cancels and deletes active logs which match the given filter criteria, as
297   // described by MatchesFilter's documentation.
298   // This method not trigger any pending logs to be uploaded, allowing it to
299   // be safely used in a context that clears browsing data.
300   void MaybeCancelActiveLogs(const base::Time& delete_begin,
301                              const base::Time& delete_end,
302                              BrowserContextId browser_context_id);
303 
304   // Removes pending logs files which match the given filter criteria, as
305   // described by MatchesFilter's documentation.
306   // This method not trigger any pending logs to be uploaded, allowing it to
307   // be safely used in a context that clears browsing data.
308   void MaybeRemovePendingLogs(
309       const base::Time& delete_begin,
310       const base::Time& delete_end,
311       base::Optional<BrowserContextId> browser_context_id,
312       bool is_cache_clear);
313 
314   // Remove all history files associated with |browser_context_id| which were
315   // either captured or uploaded between |delete_begin| and |delete_end|.
316   // This method not trigger any pending logs to be uploaded, allowing it to
317   // be safely used in a context that clears browsing data.
318   void MaybeRemoveHistoryFiles(const base::Time& delete_begin,
319                                const base::Time& delete_end,
320                                BrowserContextId browser_context_id);
321 
322   // If the currently uploaded file matches the given filter criteria, as
323   // described by MatchesFilter's documentation, the upload will be
324   // cancelled, and the log file deleted. If this happens, the next pending log
325   // file will be considered for upload.
326   // This method is used to ensure that clearing of browsing data by the user
327   // does not leave the currently-uploaded file on disk, even for the duration
328   // of the upload.
329   // This method not trigger any pending logs to be uploaded, allowing it to
330   // be safely used in a context that clears browsing data.
331   void MaybeCancelUpload(const base::Time& delete_begin,
332                          const base::Time& delete_end,
333                          BrowserContextId browser_context_id);
334 
335   // Checks whether a log file matches a range and (potentially) BrowserContext:
336   // * A file matches if its last modification date was at or later than
337   //   |filter_range_begin|, and earlier than |filter_range_end|.
338   // * If a null time-point is given as either |filter_range_begin| or
339   //   |filter_range_end|, it is treated as "beginning-of-time" or
340   //   "end-of-time", respectively.
341   // * If |filter_browser_context_id| is set, only log files associated with it
342   //   can match the filter.
343   bool MatchesFilter(BrowserContextId log_browser_context_id,
344                      const base::Time& log_last_modification,
345                      base::Optional<BrowserContextId> filter_browser_context_id,
346                      const base::Time& filter_range_begin,
347                      const base::Time& filter_range_end) const;
348 
349   // Return |true| if and only if we can start another active log (with respect
350   // to limitations on the numbers active and pending logs).
351   bool AdditionalActiveLogAllowed(BrowserContextId browser_context_id) const;
352 
353   // Uploading suppressed while active peer connections exist (unless
354   // suppression) is turned off from the command line.
355   bool UploadSuppressed() const;
356 
357   // Check whether all the conditions necessary for uploading log files are
358   // currently satisfied.
359   // 1. There may be no active peer connections which might be adversely
360   //    affected by the bandwidth consumption of the upload.
361   // 2. Chrome has a network connection, and that conneciton is either a wired
362   //    one, or WiFi. (That is, not 3G, etc.)
363   // 3. Naturally, a file pending upload must exist.
364   bool UploadConditionsHold() const;
365 
366   // When the conditions necessary for uploading first hold, schedule a delayed
367   // task to upload (MaybeStartUploading). If they ever stop holding, void it.
368   void ManageUploadSchedule();
369 
370   // Posted as a delayed task by ManageUploadSchedule. If not voided until
371   // executed, will initiate an upload of the next log file.
372   void MaybeStartUploading();
373 
374   // Callback for the success/failure of an upload.
375   // When an upload is complete, it might be time to upload the next file.
376   // Note: |log_file| and |upload_successful| are ignored in production; they
377   // are used in unit tests, so we keep them here to make things simpler, so
378   // that this method would match WebRtcEventLogUploader::UploadResultCallback
379   // without adaptation.
380   void OnWebRtcEventLogUploadComplete(const base::FilePath& log_file,
381                                       bool upload_successful);
382 
383   // Given a renderer process ID and peer connection's session ID, find the
384   // peer connection to which they refer.
385   bool FindPeerConnection(int render_process_id,
386                           const std::string& session_id,
387                           PeerConnectionKey* key) const;
388 
389   // Find the next peer connection in a map to which the renderer process ID
390   // and session ID refer.
391   // This helper allows FindPeerConnection() to DCHECK on uniqueness of the ID
392   // without descending down a recursive rabbit hole.
393   PeerConnectionMap::const_iterator FindNextPeerConnection(
394       PeerConnectionMap::const_iterator begin,
395       int render_process_id,
396       const std::string& session_id) const;
397 
398   // Normally, uploading is suppressed while there are active peer connections.
399   // This may be disabled from the command line.
400   const bool upload_suppression_disabled_;
401 
402   // The conditions for upload must hold for this much time, uninterrupted,
403   // before an upload may be initiated.
404   const base::TimeDelta upload_delay_;
405 
406   // If non-zero, every |proactive_pending_logs_prune_delta_|, pending logs
407   // will be pruned. This avoids them staying around on disk for longer than
408   // their expiration if no event occurs which triggers reactive pruning.
409   const base::TimeDelta proactive_pending_logs_prune_delta_;
410 
411   // Proactive pruning, if enabled, starts with the first enabled browser
412   // context. To avoid unnecessary complexity, if that browser context is
413   // disabled, proactive pruning is not disabled.
414   bool proactive_prune_scheduling_started_;
415 
416   // This is used to inform WebRtcEventLogManager when remote-bound logging
417   // of a peer connection starts/stops, which allows WebRtcEventLogManager to
418   // decide when to ask WebRTC to start/stop sending event logs.
419   WebRtcRemoteEventLogsObserver* const observer_;
420 
421   // The IDs of the BrowserContexts for which logging is enabled, mapped to
422   // the directory where each BrowserContext's remote-bound logs are stored.
423   std::map<BrowserContextId, base::FilePath> enabled_browser_contexts_;
424 
425   // Currently active peer connections, mapped to their session IDs (once the
426   // session ID is set).
427   // PeerConnections which have been closed are not considered active,
428   // regardless of whether they have been torn down.
429   PeerConnectionMap active_peer_connections_;
430 
431   // Creates LogFileWriter instances (compressed/uncompressed, etc.).
432   std::unique_ptr<LogFileWriter::Factory> log_file_writer_factory_;
433 
434   // Remote-bound logs which we're currently in the process of writing to disk.
435   LogFilesMap active_logs_;
436 
437   // Remote-bound logs which have been written to disk before (either during
438   // this Chrome session or during an earlier one), and which are no waiting to
439   // be uploaded.
440   std::set<WebRtcLogFileInfo> pending_logs_;
441 
442   // Null if no ongoing upload, or an uploader which owns a file, and is
443   // currently busy uploading it to a remote server.
444   std::unique_ptr<WebRtcEventLogUploader> uploader_;
445 
446   // The path to the file which is currently being uploaded.
447   // Used to ensure a callback from the uploader refers to the current
448   // file, rather than a second callback from the previously uploaded file,
449   // e.g. when when Cancel() is called right after the upload finishes.
450   base::FilePath currently_uploaded_file_;
451 
452   // Provides notifications of network changes.
453   network::NetworkConnectionTracker* network_connection_tracker_;
454 
455   // Whether the network we are currently connected to, if any, is one over
456   // which we may upload.
457   bool uploading_supported_for_connection_type_;
458 
459   // If the conditions for initiating an upload do not hold, this will be
460   // set to an empty base::TimeTicks.
461   // If the conditions were found to hold, this will record the time when they
462   // started holding. (It will be set back to 0 if they ever cease holding.)
463   base::TimeTicks time_when_upload_conditions_met_;
464 
465   // This is a vehicle for DCHECKs to ensure code sanity. It counts the number
466   // of scheduled tasks of MaybeStartUploading(), and proves that we never
467   // end up with a scheduled upload that never occurs.
468   size_t scheduled_upload_tasks_;
469 
470   // Producer of uploader objects. (In unit tests, this would create
471   // null-implementation uploaders, or uploaders whose behavior is controlled
472   // by the unit test.)
473   std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory_;
474 
475   // |this| is created and destroyed on the UI thread, but operates on the
476   // following IO-capable sequenced task runner.
477   scoped_refptr<base::SequencedTaskRunner> task_runner_;
478 
479   // Weak pointer factory. Only expected to be useful for unit tests, because
480   // in production, |task_runner_| is stopped during shut-down, so tasks will
481   // either find the pointer to be valid, or not run because the runner has
482   // already been stopped.
483   // Note that the unique_ptr is used just to make it clearer that ownership is
484   // here. In reality, this is never auto-destroyed; see destructor for details.
485   std::unique_ptr<base::WeakPtrFactory<WebRtcRemoteEventLogManager>>
486       weak_ptr_factory_;
487 
488   DISALLOW_COPY_AND_ASSIGN(WebRtcRemoteEventLogManager);
489 };
490 
491 }  // namespace webrtc_event_logging
492 
493 #endif  // CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_
494