| // Copyright 2018 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. |
| |
| #ifndef CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_ |
| #define CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_ |
| |
| #include <map> |
| #include <set> |
| |
| #include "base/optional.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h" |
| #include "chrome/browser/media/webrtc/webrtc_event_log_uploader.h" |
| #include "services/network/public/cpp/network_connection_tracker.h" |
| |
| // TODO(crbug.com/775415): Avoid uploading logs when Chrome shutdown imminent. |
| |
| namespace net { |
| class URLRequestContextGetter; |
| } // namespace net |
| |
| class WebRtcRemoteEventLogManager final |
| : public network::NetworkConnectionTracker::NetworkConnectionObserver { |
| using BrowserContextId = WebRtcEventLogPeerConnectionKey::BrowserContextId; |
| using LogFilesMap = |
| std::map<WebRtcEventLogPeerConnectionKey, std::unique_ptr<LogFileWriter>>; |
| using PeerConnectionKey = WebRtcEventLogPeerConnectionKey; |
| |
| public: |
| WebRtcRemoteEventLogManager( |
| WebRtcRemoteEventLogsObserver* observer, |
| scoped_refptr<base::SequencedTaskRunner> task_runner); |
| ~WebRtcRemoteEventLogManager() override; |
| |
| // Sets a network::NetworkConnectionTracker which will be used to track |
| // network connectivity. |
| // Must not be called more than once. |
| // Must be called before any call to EnableForBrowserContext(). |
| void SetNetworkConnectionTracker( |
| network::NetworkConnectionTracker* network_connection_tracker); |
| |
| // Sets a net::URLRequestContextGetter which will be used for uploads. |
| // Must not be called more than once. |
| // Must be called before any call to EnableForBrowserContext(). |
| void SetUrlRequestContextGetter(net::URLRequestContextGetter* context_getter); |
| |
| // Sets a LogFileWriter factory. |
| // Must not be called more than once. |
| // Must be called before any call to EnableForBrowserContext(). |
| void SetLogFileWriterFactory( |
| std::unique_ptr<LogFileWriter::Factory> log_file_writer_factory); |
| |
| // Enables remote-bound logging for a given BrowserContext. Logs stored during |
| // previous sessions become eligible for upload, and recording of new logs for |
| // peer connections associated with this BrowserContext, in the |
| // BrowserContext's user-data directory, becomes possible. |
| // This method would typically be called when a BrowserContext is initialized. |
| void EnableForBrowserContext(BrowserContextId browser_context_id, |
| const base::FilePath& browser_context_dir); |
| |
| // Enables remote-bound logging for a given BrowserContext. Pending logs from |
| // earlier (while it was enabled) may still be uploaded, but no additional |
| // logs will be created. |
| void DisableForBrowserContext(BrowserContextId browser_context_id); |
| |
| // Called to inform |this| of peer connections being added/removed. |
| // This information is used to: |
| // 1. Make decisions about when to upload previously finished logs. |
| // 2. When a peer connection is removed, if it was being logged, its log |
| // changes from ACTIVE to PENDING. |
| // The return value of both methods indicates only the consistency of the |
| // information with previously received information (e.g. can't remove a |
| // peer connection that was never added, etc.). |
| bool PeerConnectionAdded(const PeerConnectionKey& key, |
| const std::string& peer_connection_id); |
| bool PeerConnectionRemoved(const PeerConnectionKey& key); |
| |
| // Attempt to start logging the WebRTC events of an active peer connection. |
| // Logging is subject to several restrictions: |
| // 1. May not log more than kMaxNumberActiveRemoteWebRtcEventLogFiles logs |
| // at the same time. |
| // 2. Each browser context may have only kMaxPendingLogFilesPerBrowserContext |
| // pending logs. Since active logs later become pending logs, it is also |
| // forbidden to start a remote-bound log that would, once completed, become |
| // a pending log that would exceed that limit. |
| // 3. The maximum file size must be sensible. |
| // |
| // If all of the restrictions were observed, and if a file was successfully |
| // created, true will be return. |
| // |
| // If the call succeeds, the log's identifier will be written to |log_id|. |
| // The log identifier is exactly 32 uppercase ASCII characters from the |
| // ranges 0-9 and A-F. |
| // |
| // If the call fails, an error message is written to |error_message|. |
| // The error message will be specific to the failure (as opposed to a generic |
| // one) is produced only if that error message is useful for the caller: |
| // * Bad parameters. |
| // * Function called at a time when the caller could know it would fail, |
| // such as for a peer connection that was already logged. |
| // We intentionally avoid giving specific errors in some cases, so as |
| // to avoid leaking information such as having too many active and/or |
| // pending logs. |
| bool StartRemoteLogging(int render_process_id, |
| BrowserContextId browser_context_id, |
| const std::string& peer_connection_id, |
| const base::FilePath& browser_context_dir, |
| size_t max_file_size_bytes, |
| std::string* log_id, |
| std::string* error_message); |
| |
| // If an active remote-bound log exists for the given peer connection, this |
| // will append |message| to that log. |
| // If writing |message| to the log would exceed the log's maximum allowed |
| // size, the write is disallowed and the file is closed instead (and changes |
| // from ACTIVE to PENDING). |
| // If the log file's capacity is exhausted as a result of this function call, |
| // or if a write error occurs, the file is closed, and the remote-bound log |
| // changes from ACTIVE to PENDING. |
| // True is returned if and only if |message| was written in its entirety to |
| // an active log. |
| bool EventLogWrite(const PeerConnectionKey& key, const std::string& message); |
| |
| // Clear PENDING WebRTC event logs associated with a given browser context, |
| // in a given time range, then post |reply| back to the thread from which |
| // the method was originally invoked (which can be any thread). |
| // Log files currently being written are *not* interrupted. |
| // Active uploads *are* interrupted. |
| void ClearCacheForBrowserContext(BrowserContextId browser_context_id, |
| const base::Time& delete_begin, |
| const base::Time& delete_end); |
| |
| // An implicit PeerConnectionRemoved() on all of the peer connections that |
| // were associated with the renderer process. |
| void RenderProcessHostExitedDestroyed(int render_process_id); |
| |
| // network::NetworkConnectionTracker::NetworkConnectionObserver implementation |
| void OnConnectionChanged(network::mojom::ConnectionType type) override; |
| |
| // Unit tests may use this to inject null uploaders, or ones which are |
| // directly controlled by the unit test (succeed or fail according to the |
| // test's needs). |
| // Note that for simplicity's sake, this may be called from outside the |
| // task queue on which this object lives (WebRtcEventLogManager::task_queue_). |
| // Therefore, if a test calls this, it should call it before it initializes |
| // any BrowserContext with pending log files in its directory. |
| void SetWebRtcEventLogUploaderFactoryForTesting( |
| std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory); |
| |
| // Exposes UploadConditionsHold() to unit tests. See WebRtcEventLogManager's |
| // documentation for the rationale. |
| void UploadConditionsHoldForTesting(base::OnceCallback<void(bool)> callback); |
| |
| private: |
| // Validates log parameters (at the moment, only max file size). |
| // If valid, returns true. Otherwise, false, and |error_message| gets |
| // a relevant error. |
| bool AreLogParametersValid(size_t max_file_size_bytes, |
| std::string* error_message) const; |
| |
| // Checks whether a browser context has already been enabled via a call to |
| // EnableForBrowserContext(), and not yet disabled using a call to |
| // DisableForBrowserContext(). |
| bool BrowserContextEnabled(BrowserContextId browser_context_id) const; |
| |
| // Closes an active log file. |
| // If |make_pending| is true, closing the file changes its state from ACTIVE |
| // to PENDING. If |make_pending| is false, or if the file couldn't be closed |
| // correctly, the file will be deleted. |
| // Returns an iterator to the next ACTIVE file. |
| LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it, |
| bool make_pending); |
| |
| // Attempts to create the directory where we'll write the logs, if it does |
| // not already exist. Returns true if the directory exists (either it already |
| // existed, or it was successfully created). |
| bool MaybeCreateLogsDirectory(const base::FilePath& remote_bound_logs_dir); |
| |
| // Scans the user data directory associated with this BrowserContext for |
| // remote-bound logs that were created during previous Chrome sessions. |
| // This function does *not* protect against manipulation by the user, |
| // who might seed the directory with more files than were permissible. |
| void AddPendingLogs(BrowserContextId browser_context_id, |
| const base::FilePath& remote_bound_logs_dir); |
| |
| // Attempts the creation of a locally stored file into which a remote-bound |
| // log may be written. The log-identifier is returned if successful, the empty |
| // string otherwise. |
| bool StartWritingLog(const PeerConnectionKey& key, |
| const base::FilePath& browser_context_dir, |
| size_t max_file_size_bytes, |
| std::string* log_id, |
| std::string* error_message); |
| |
| // Checks if the referenced peer connection has an associated active |
| // remote-bound log. If it does, the log is changed from ACTIVE to PENDING. |
| void MaybeStopRemoteLogging(const PeerConnectionKey& key); |
| |
| // Get rid of pending logs whose age exceeds our retention policy. |
| // On the one hand, we want to remove expired files as soon as possible, but |
| // on the other hand, we don't want to waste CPU by checking this too often. |
| // Therefore, we prune pending files: |
| // 1. When a new BrowserContext is initalized, thereby also pruning the |
| // pending logs contributed by that BrowserContext. |
| // 2. Before initiating a new upload, thereby avoiding uploading a file that |
| // has just now expired. |
| // 3. On infrequent events - peer connection addition/removal, but NOT |
| // on something that could potentially be frequent, such as EventLogWrite. |
| // Note that the last modification date of a file, which is the value measured |
| // against for retention, is only read from disk once per file, meaning |
| // this check is not too expensive. |
| void PrunePendingLogs(); |
| |
| // PrunePendingLogs() and schedule the next proactive prune. |
| void RecurringPendingLogsPrune(); |
| |
| // Removes pending logs files which match the given filter criteria, as |
| // described by LogFileMatchesFilter's documentation. |
| void MaybeRemovePendingLogs( |
| const base::Time& delete_begin, |
| const base::Time& delete_end, |
| base::Optional<BrowserContextId> browser_context_id = |
| base::Optional<BrowserContextId>()); |
| |
| // Cancels and deletes active logs which match the given filter criteria, as |
| // described by LogFileMatchesFilter's documentation. |
| void MaybeCancelActiveLogs(const base::Time& delete_begin, |
| const base::Time& delete_end, |
| BrowserContextId browser_context_id); |
| |
| // If the currently uploaded file matches the given filter criteria, as |
| // described by LogFileMatchesFilter's documentation, the upload will be |
| // cancelled, and the log file deleted. If this happens, the next pending log |
| // file will be considered for upload. |
| // This method is used to ensure that clearing of browsing data by the user |
| // does not leave the currently-uploaded file on disk, even for the duration |
| // of the upload. |
| void MaybeCancelUpload(const base::Time& delete_begin, |
| const base::Time& delete_end, |
| base::Optional<BrowserContextId> browser_context_id = |
| base::Optional<BrowserContextId>()); |
| |
| // Checks whether a log file matches a range and (potentially) BrowserContext: |
| // * A file matches if its last modification date was at or later than |
| // |filter_range_begin|, and earlier than |filter_range_end|. |
| // * If a null time-point is given as either |filter_range_begin| or |
| // |filter_range_end|, it is treated as "beginning-of-time" or |
| // "end-of-time", respectively. |
| // * If |filter_browser_context_id| is set, only log files associated with it |
| // can match the filter. |
| bool LogFileMatchesFilter( |
| BrowserContextId log_browser_context_id, |
| const base::Time& log_last_modification, |
| base::Optional<BrowserContextId> filter_browser_context_id, |
| const base::Time& filter_range_begin, |
| const base::Time& filter_range_end) const; |
| |
| // Return |true| if and only if we can start another active log (with respect |
| // to limitations on the numbers active and pending logs). |
| bool AdditionalActiveLogAllowed(BrowserContextId browser_context_id) const; |
| |
| // Uploading suppressed while active peer connections exist (unless |
| // suppression) is turned off from the command line. |
| bool UploadSuppressed() const; |
| |
| // Check whether all the conditions necessary for uploading log files are |
| // currently satisfied. |
| // 1. There may be no active peer connections which might be adversely |
| // affected by the bandwidth consumption of the upload. |
| // 2. Chrome has a network connection, and that conneciton is either a wired |
| // one, or WiFi. (That is, not 3G, etc.) |
| // 3. Naturally, a file pending upload must exist. |
| bool UploadConditionsHold() const; |
| |
| // When the conditions necessary for uploading first hold, schedule a delayed |
| // task to upload (MaybeStartUploading). If they ever stop holding, void it. |
| void ManageUploadSchedule(); |
| |
| // Posted as a delayed task by ManageUploadSchedule. If not voided until |
| // executed, will initiate an upload of the next log file. |
| void MaybeStartUploading(); |
| |
| // Callback for the success/failure of an upload. |
| // When an upload is complete, it might be time to upload the next file. |
| // Note: |log_file| and |upload_successful| are ignored in production; they |
| // are used in unit tests, so we keep them here to make things simpler, so |
| // that this method would match WebRtcEventLogUploader::UploadResultCallback |
| // without adaptation. |
| void OnWebRtcEventLogUploadComplete(const base::FilePath& log_file, |
| bool upload_successful); |
| |
| // Given a renderer process ID and peer connection ID (a string naming the |
| // peer connection), find the peer connection to which they refer. |
| bool FindPeerConnection(int render_process_id, |
| const std::string& peer_connection_id, |
| PeerConnectionKey* key) const; |
| |
| // Find the next peer connection in a map to which the renderer process ID |
| // and peer connection ID refer. |
| // This helper allows FindPeerConnection() to DCHECK on uniqueness of the ID |
| // without descending down a recursive rabbit hole. |
| std::map<PeerConnectionKey, const std::string>::const_iterator |
| FindNextPeerConnection( |
| std::map<PeerConnectionKey, const std::string>::const_iterator begin, |
| int render_process_id, |
| const std::string& peer_connection_id) const; |
| |
| // Normally, uploading is suppressed while there are active peer connections. |
| // This may be disabled from the command line. |
| const bool upload_suppression_disabled_; |
| |
| // Proactive pruning will be done only if this is non-zero, in which case, |
| // every |proactive_prune_scheduling_delta_|, pending logs will be pruned. |
| // This avoids them staying around on disk for longer than their expiration |
| // if no event occurs which triggers reactive pruning. |
| const base::TimeDelta proactive_prune_scheduling_delta_; |
| |
| // The conditions for upload must hold for this much time, uninterrupted, |
| // before an upload may be initiated. |
| const base::TimeDelta upload_delay_; |
| |
| // Proactive pruning, if enabled, starts with the first enabled browser |
| // context. To avoid unnecessary complexity, if that browser context is |
| // disabled, proactive pruning is not disabled. |
| bool proactive_prune_scheduling_started_; |
| |
| // This is used to inform WebRtcEventLogManager when remote-bound logging |
| // of a peer connection starts/stops, which allows WebRtcEventLogManager to |
| // decide when to ask WebRTC to start/stop sending event logs. |
| WebRtcRemoteEventLogsObserver* const observer_; |
| |
| // The IDs of the BrowserContexts for which logging is enabled. |
| std::set<BrowserContextId> enabled_browser_contexts_; |
| |
| // Currently active peer connections, mapped to their ID (as per the |
| // RTCPeerConnection.id origin trial). PeerConnections which have been closed |
| // are not considered active, regardless of whether they have been torn down. |
| std::map<PeerConnectionKey, const std::string> active_peer_connections_; |
| |
| // Creates LogFileWriter instances (compressed/uncompressed, etc.). |
| std::unique_ptr<LogFileWriter::Factory> log_file_writer_factory_; |
| |
| // Remote-bound logs which we're currently in the process of writing to disk. |
| LogFilesMap active_logs_; |
| |
| // Remote-bound logs which have been written to disk before (either during |
| // this Chrome session or during an earlier one), and which are no waiting to |
| // be uploaded. |
| std::set<WebRtcLogFileInfo> pending_logs_; |
| |
| // Null if no ongoing upload, or an uploader which owns a file, and is |
| // currently busy uploading it to a remote server. |
| std::unique_ptr<WebRtcEventLogUploader> uploader_; |
| |
| // Provides notifications of network changes. |
| network::NetworkConnectionTracker* network_connection_tracker_; |
| |
| // Whether the network we are currently connected to, if any, is one over |
| // which we may upload. |
| bool uploading_supported_for_connection_type_; |
| |
| // If the conditions for initiating an upload do not hold, this will be |
| // set to an empty base::TimeTicks. |
| // If the conditions were found to hold, this will record the time when they |
| // started holding. (It will be set back to 0 if they ever cease holding.) |
| base::TimeTicks time_when_upload_conditions_met_; |
| |
| // This is a vehicle for DCHECKs to ensure code sanity. It counts the number |
| // of scheduled tasks of MaybeStartUploading(), and proves that we never |
| // end up with a scheduled upload that never occurs. |
| size_t scheduled_upload_tasks_; |
| |
| // Producer of uploader objects. (In unit tests, this would create |
| // null-implementation uploaders, or uploaders whose behavior is controlled |
| // by the unit test.) |
| std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory_; |
| |
| // |this| is created and destroyed on the UI thread, but operates on the |
| // following IO-capable sequenced task runner. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebRtcRemoteEventLogManager); |
| }; |
| |
| #endif // CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_REMOTE_H_ |