Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/safe_browsing/incident_reporting_service.h" | 5 #include "chrome/browser/safe_browsing/incident_reporting_service.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 13 #include "base/prefs/pref_service.h" | 13 #include "base/prefs/pref_service.h" |
| 14 #include "base/prefs/scoped_user_pref_update.h" | 14 #include "base/prefs/scoped_user_pref_update.h" |
| 15 #include "base/process/process_info.h" | 15 #include "base/process/process_info.h" |
| 16 #include "base/single_thread_task_runner.h" | |
| 16 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/thread_task_runner_handle.h" | |
| 18 #include "base/threading/sequenced_worker_pool.h" | 20 #include "base/threading/sequenced_worker_pool.h" |
| 19 #include "base/values.h" | 21 #include "base/values.h" |
| 20 #include "chrome/browser/browser_process.h" | 22 #include "chrome/browser/browser_process.h" |
| 21 #include "chrome/browser/chrome_notification_types.h" | 23 #include "chrome/browser/chrome_notification_types.h" |
| 22 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" | 24 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" |
| 23 #include "chrome/browser/profiles/profile.h" | 25 #include "chrome/browser/profiles/profile.h" |
| 24 #include "chrome/browser/safe_browsing/database_manager.h" | 26 #include "chrome/browser/safe_browsing/database_manager.h" |
| 25 #include "chrome/browser/safe_browsing/environment_data_collection.h" | 27 #include "chrome/browser/safe_browsing/environment_data_collection.h" |
| 26 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h" | 28 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h" |
| 27 #include "chrome/browser/safe_browsing/preference_validation_delegate.h" | 29 #include "chrome/browser/safe_browsing/preference_validation_delegate.h" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 // The key for a specific instance of an incident. | 65 // The key for a specific instance of an incident. |
| 64 std::string key; | 66 std::string key; |
| 65 | 67 |
| 66 // A hash digest representing a specific instance of an incident. | 68 // A hash digest representing a specific instance of an incident. |
| 67 uint32_t digest; | 69 uint32_t digest; |
| 68 }; | 70 }; |
| 69 | 71 |
| 70 // The amount of time the service will wait to collate incidents. | 72 // The amount of time the service will wait to collate incidents. |
| 71 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute | 73 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute |
| 72 | 74 |
| 75 // The amount of time between running delayed analysis callbacks. | |
| 76 const int64 kDefaultCallbackMs = 1000 * 20; | |
|
robertshield
2014/08/05 21:26:28
how about kDefaultCallbackIntervalMs ?
grt (UTC plus 2)
2014/08/06 02:37:31
Done.
| |
| 77 | |
| 73 // Returns the number of incidents contained in |incident|. The result is | 78 // Returns the number of incidents contained in |incident|. The result is |
| 74 // expected to be 1. Used in DCHECKs. | 79 // expected to be 1. Used in DCHECKs. |
| 75 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { | 80 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { |
| 76 size_t result = 0; | 81 size_t result = 0; |
| 77 if (incident.has_tracked_preference()) | 82 if (incident.has_tracked_preference()) |
| 78 ++result; | 83 ++result; |
| 79 // Add detection for new incident types here. | 84 // Add detection for new incident types here. |
| 80 return result; | 85 return result; |
| 81 } | 86 } |
| 82 | 87 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string, | 155 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string, |
| 151 &type_dict)) { | 156 &type_dict)) { |
| 152 type_dict = new base::DictionaryValue(); | 157 type_dict = new base::DictionaryValue(); |
| 153 incidents_sent->SetWithoutPathExpansion(type_string, type_dict); | 158 incidents_sent->SetWithoutPathExpansion(type_string, type_dict); |
| 154 } | 159 } |
| 155 type_dict->SetStringWithoutPathExpansion(data.key, | 160 type_dict->SetStringWithoutPathExpansion(data.key, |
| 156 base::UintToString(data.digest)); | 161 base::UintToString(data.digest)); |
| 157 } | 162 } |
| 158 } | 163 } |
| 159 | 164 |
| 165 // Runs |callback| on the thread to which |thread_runner| belongs. The callback | |
| 166 // is run immediately if this function is called on |thread_runner|'s thread. | |
| 167 void AddIncidentOnOriginThread( | |
| 168 const AddIncidentCallback& callback, | |
| 169 scoped_refptr<base::SingleThreadTaskRunner> thread_runner, | |
| 170 scoped_ptr<ClientIncidentReport_IncidentData> incident) { | |
| 171 if (thread_runner->BelongsToCurrentThread()) | |
| 172 callback.Run(incident.Pass()); | |
| 173 else | |
| 174 thread_runner->PostTask(FROM_HERE, | |
| 175 base::Bind(callback, base::Passed(&incident))); | |
| 176 } | |
| 177 | |
| 160 } // namespace | 178 } // namespace |
| 161 | 179 |
| 162 struct IncidentReportingService::ProfileContext { | 180 struct IncidentReportingService::ProfileContext { |
| 163 ProfileContext(); | 181 ProfileContext(); |
| 164 ~ProfileContext(); | 182 ~ProfileContext(); |
| 165 | 183 |
| 166 // The incidents collected for this profile pending creation and/or upload. | 184 // The incidents collected for this profile pending creation and/or upload. |
| 167 ScopedVector<ClientIncidentReport_IncidentData> incidents; | 185 ScopedVector<ClientIncidentReport_IncidentData> incidents; |
| 168 | 186 |
| 169 // False until PROFILE_ADDED notification is received. | 187 // False until PROFILE_ADDED notification is received. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) | 231 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) |
| 214 : database_manager_(safe_browsing_service ? | 232 : database_manager_(safe_browsing_service ? |
| 215 safe_browsing_service->database_manager() : NULL), | 233 safe_browsing_service->database_manager() : NULL), |
| 216 url_request_context_getter_(request_context_getter), | 234 url_request_context_getter_(request_context_getter), |
| 217 collect_environment_data_fn_(&CollectEnvironmentData), | 235 collect_environment_data_fn_(&CollectEnvironmentData), |
| 218 environment_collection_task_runner_( | 236 environment_collection_task_runner_( |
| 219 content::BrowserThread::GetBlockingPool() | 237 content::BrowserThread::GetBlockingPool() |
| 220 ->GetTaskRunnerWithShutdownBehavior( | 238 ->GetTaskRunnerWithShutdownBehavior( |
| 221 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), | 239 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
| 222 environment_collection_pending_(), | 240 environment_collection_pending_(), |
| 223 collection_timeout_pending_(), | 241 collation_timeout_pending_(), |
| 224 upload_timer_(FROM_HERE, | 242 collation_timer_(FROM_HERE, |
| 225 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), | 243 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), |
| 226 this, | 244 this, |
| 227 &IncidentReportingService::OnCollectionTimeout), | 245 &IncidentReportingService::OnCollationTimeout), |
| 246 delayed_analysis_callbacks_( | |
| 247 base::TimeDelta::FromMilliseconds(kDefaultCallbackMs), | |
| 248 content::BrowserThread::GetBlockingPool()), | |
| 228 receiver_weak_ptr_factory_(this), | 249 receiver_weak_ptr_factory_(this), |
| 229 weak_ptr_factory_(this) { | 250 weak_ptr_factory_(this) { |
| 230 notification_registrar_.Add(this, | 251 notification_registrar_.Add(this, |
| 231 chrome::NOTIFICATION_PROFILE_ADDED, | 252 chrome::NOTIFICATION_PROFILE_ADDED, |
| 232 content::NotificationService::AllSources()); | 253 content::NotificationService::AllSources()); |
| 233 notification_registrar_.Add(this, | 254 notification_registrar_.Add(this, |
| 234 chrome::NOTIFICATION_PROFILE_DESTROYED, | 255 chrome::NOTIFICATION_PROFILE_DESTROYED, |
| 235 content::NotificationService::AllSources()); | 256 content::NotificationService::AllSources()); |
| 236 } | 257 } |
| 237 | 258 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 263 scoped_ptr<TrackedPreferenceValidationDelegate> | 284 scoped_ptr<TrackedPreferenceValidationDelegate> |
| 264 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { | 285 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { |
| 265 DCHECK(thread_checker_.CalledOnValidThread()); | 286 DCHECK(thread_checker_.CalledOnValidThread()); |
| 266 | 287 |
| 267 if (profile->IsOffTheRecord()) | 288 if (profile->IsOffTheRecord()) |
| 268 return scoped_ptr<TrackedPreferenceValidationDelegate>(); | 289 return scoped_ptr<TrackedPreferenceValidationDelegate>(); |
| 269 return scoped_ptr<TrackedPreferenceValidationDelegate>( | 290 return scoped_ptr<TrackedPreferenceValidationDelegate>( |
| 270 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); | 291 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); |
| 271 } | 292 } |
| 272 | 293 |
| 294 void IncidentReportingService::RegisterDelayedAnalysisCallback( | |
| 295 const DelayedAnalysisCallback& callback) { | |
| 296 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 297 | |
| 298 // |callback| will be run on the blocking pool, so it will likely run the | |
| 299 // AddIncidentCallback there as well. Bounce the run of that callback back to | |
| 300 // the main thread via AddIncidentOnOriginThread. | |
|
robertshield
2014/08/05 21:26:28
s/main/current ?
grt (UTC plus 2)
2014/08/06 02:37:31
Done.
| |
| 301 delayed_analysis_callbacks_.RegisterCallback( | |
| 302 base::Bind(callback, | |
| 303 base::Bind(&AddIncidentOnOriginThread, | |
| 304 GetAddIncidentCallback(NULL), | |
| 305 base::ThreadTaskRunnerHandle::Get()))); | |
| 306 | |
| 307 // Start running the callbacks if any profiles are participating in safe | |
| 308 // browsing. If none are now, running will commence if/when a participaing | |
| 309 // profile is added. | |
| 310 if (FindEligibleProfile()) | |
| 311 delayed_analysis_callbacks_.Start(); | |
| 312 } | |
| 313 | |
| 273 void IncidentReportingService::SetCollectEnvironmentHook( | 314 void IncidentReportingService::SetCollectEnvironmentHook( |
| 274 CollectEnvironmentDataFn collect_environment_data_hook, | 315 CollectEnvironmentDataFn collect_environment_data_hook, |
| 275 const scoped_refptr<base::TaskRunner>& task_runner) { | 316 const scoped_refptr<base::TaskRunner>& task_runner) { |
| 276 if (collect_environment_data_hook) { | 317 if (collect_environment_data_hook) { |
| 277 collect_environment_data_fn_ = collect_environment_data_hook; | 318 collect_environment_data_fn_ = collect_environment_data_hook; |
| 278 environment_collection_task_runner_ = task_runner; | 319 environment_collection_task_runner_ = task_runner; |
| 279 } else { | 320 } else { |
| 280 collect_environment_data_fn_ = &CollectEnvironmentData; | 321 collect_environment_data_fn_ = &CollectEnvironmentData; |
| 281 environment_collection_task_runner_ = | 322 environment_collection_task_runner_ = |
| 282 content::BrowserThread::GetBlockingPool() | 323 content::BrowserThread::GetBlockingPool() |
| 283 ->GetTaskRunnerWithShutdownBehavior( | 324 ->GetTaskRunnerWithShutdownBehavior( |
| 284 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | 325 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
| 285 } | 326 } |
| 286 } | 327 } |
| 287 | 328 |
| 288 void IncidentReportingService::OnProfileAdded(Profile* profile) { | 329 void IncidentReportingService::OnProfileAdded(Profile* profile) { |
| 289 DCHECK(thread_checker_.CalledOnValidThread()); | 330 DCHECK(thread_checker_.CalledOnValidThread()); |
| 290 | 331 |
| 291 // Track the addition of all profiles even when no report is being assembled | 332 // Track the addition of all profiles even when no report is being assembled |
| 292 // so that the service can determine whether or not it can evaluate a | 333 // so that the service can determine whether or not it can evaluate a |
| 293 // profile's preferences at the time of incident addition. | 334 // profile's preferences at the time of incident addition. |
| 294 ProfileContext* context = GetOrCreateProfileContext(profile); | 335 ProfileContext* context = GetOrCreateProfileContext(profile); |
| 295 context->added = true; | 336 context->added = true; |
| 296 | 337 |
| 338 const bool safe_browsing_enabled = | |
| 339 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled); | |
| 340 | |
| 341 // Start processing delayed analysis callbacks if this new profile | |
| 342 // participates in safe browsing. | |
|
robertshield
2014/08/05 21:26:28
Re-mention here how Start() is idempotent for repe
grt (UTC plus 2)
2014/08/06 02:37:31
Done.
| |
| 343 if (safe_browsing_enabled) | |
| 344 delayed_analysis_callbacks_.Start(); | |
| 345 | |
| 346 // Start a new report if this profile participates in safe browsing and there | |
| 347 // are process-wide incidents. | |
| 348 if (safe_browsing_enabled && GetProfileContext(NULL)) | |
| 349 BeginReportProcessing(); | |
| 350 | |
| 351 // TODO(grt): register for pref change notifications to start delayed analysis | |
| 352 // and/or report processing if sb is currently disabled but subsequently | |
| 353 // enabled. | |
| 354 | |
| 297 // Nothing else to do if a report is not being assembled. | 355 // Nothing else to do if a report is not being assembled. |
| 298 if (!report_) | 356 if (!report_) |
| 299 return; | 357 return; |
| 300 | 358 |
| 301 // Drop all incidents received prior to creation if the profile is not | 359 // Drop all incidents associated with this profile that were received prior to |
| 302 // participating in safe browsing. | 360 // its addition if the profile is not participating in safe browsing. |
| 303 if (!context->incidents.empty() && | 361 if (!context->incidents.empty() && !safe_browsing_enabled) { |
| 304 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 362 for (size_t i = 0; i < context->incidents.size(); ++i) |
| 305 for (size_t i = 0; i < context->incidents.size(); ++i) { | |
| 306 LogIncidentDataType(DROPPED, *context->incidents[i]); | 363 LogIncidentDataType(DROPPED, *context->incidents[i]); |
| 307 } | |
| 308 context->incidents.clear(); | 364 context->incidents.clear(); |
| 309 } | 365 } |
| 310 | 366 |
| 311 // Take another stab at finding the most recent download if a report is being | 367 // Take another stab at finding the most recent download if a report is being |
| 312 // assembled and one hasn't been found yet (the LastDownloadFinder operates | 368 // assembled and one hasn't been found yet (the LastDownloadFinder operates |
| 313 // only on profiles that have been added to the ProfileManager). | 369 // only on profiles that have been added to the ProfileManager). |
| 314 BeginDownloadCollection(); | 370 BeginDownloadCollection(); |
| 315 } | 371 } |
| 316 | 372 |
| 317 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( | 373 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 // Forget about this profile. Incidents not yet sent for upload are lost. | 411 // Forget about this profile. Incidents not yet sent for upload are lost. |
| 356 // No new incidents will be accepted for it. | 412 // No new incidents will be accepted for it. |
| 357 delete it->second; | 413 delete it->second; |
| 358 profiles_.erase(it); | 414 profiles_.erase(it); |
| 359 | 415 |
| 360 // Remove the association with this profile from all pending uploads. | 416 // Remove the association with this profile from all pending uploads. |
| 361 for (size_t i = 0; i < uploads_.size(); ++i) | 417 for (size_t i = 0; i < uploads_.size(); ++i) |
| 362 uploads_[i]->profiles_to_state.erase(profile); | 418 uploads_[i]->profiles_to_state.erase(profile); |
| 363 } | 419 } |
| 364 | 420 |
| 421 Profile* IncidentReportingService::FindEligibleProfile() const { | |
| 422 Profile* candidate = NULL; | |
| 423 for (ProfileContextCollection::const_iterator scan = profiles_.begin(); | |
| 424 scan != profiles_.end(); | |
| 425 ++scan) { | |
| 426 // Skip over profiles that have yet to be added to the profile manager. | |
| 427 // This will also skip over the NULL-profile context used to hold | |
| 428 // process-wide incidents. | |
| 429 if (!scan->second->added) | |
| 430 continue; | |
| 431 PrefService* prefs = scan->first->GetPrefs(); | |
| 432 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { | |
|
mattm
2014/08/06 02:10:17
Need to check prefs::kSafeBrowsingEnabled too (ext
grt (UTC plus 2)
2014/08/06 02:37:31
Done.
| |
| 433 candidate = scan->first; | |
| 434 break; | |
| 435 } | |
| 436 if (!candidate && prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) | |
| 437 candidate = scan->first; | |
| 438 } | |
| 439 return candidate; | |
| 440 } | |
| 441 | |
| 365 void IncidentReportingService::AddIncident( | 442 void IncidentReportingService::AddIncident( |
| 366 Profile* profile, | 443 Profile* profile, |
| 367 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { | 444 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { |
| 368 DCHECK(thread_checker_.CalledOnValidThread()); | 445 DCHECK(thread_checker_.CalledOnValidThread()); |
| 369 // Incidents outside the context of a profile are not supported at the moment. | |
| 370 DCHECK(profile); | |
| 371 DCHECK_EQ(1U, CountIncidents(*incident_data)); | 446 DCHECK_EQ(1U, CountIncidents(*incident_data)); |
| 372 | 447 |
| 373 ProfileContext* context = GetProfileContext(profile); | 448 ProfileContext* context = GetProfileContext(profile); |
| 374 // It is forbidden to call this function with a destroyed profile. | 449 // It is forbidden to call this function with a destroyed profile. |
| 375 DCHECK(context); | 450 DCHECK(context); |
| 451 // If this is a process-wide incident, the context must not indicate that the | |
| 452 // profile (which is NULL) has been added to the profile manager. | |
| 453 DCHECK(profile || !context->added); | |
| 376 | 454 |
| 377 // Drop the incident immediately if profile creation has completed and the | 455 // Drop the incident immediately if the profile has already been added to the |
| 378 // profile is not participating in safe browsing. Preference evaluation is | 456 // manager and is not participating in safe browsing. Preference evaluation is |
| 379 // deferred until OnProfileAdded() if profile creation has not yet | 457 // deferred until OnProfileAdded() otherwise. |
| 380 // completed. | |
| 381 if (context->added && | 458 if (context->added && |
| 382 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 459 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
| 383 LogIncidentDataType(DROPPED, *incident_data); | 460 LogIncidentDataType(DROPPED, *incident_data); |
| 384 return; | 461 return; |
| 385 } | 462 } |
| 386 | 463 |
| 387 // Start assembling a new report if this is the first incident ever or the | |
| 388 // first since the last upload. | |
| 389 if (!report_) { | |
| 390 report_.reset(new ClientIncidentReport()); | |
| 391 first_incident_time_ = base::Time::Now(); | |
| 392 } | |
| 393 | |
| 394 // Provide time to the new incident if the caller didn't do so. | 464 // Provide time to the new incident if the caller didn't do so. |
| 395 if (!incident_data->has_incident_time_msec()) | 465 if (!incident_data->has_incident_time_msec()) |
| 396 incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime()); | 466 incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime()); |
| 397 | 467 |
| 398 // Take ownership of the incident. | 468 // Take ownership of the incident. |
| 399 context->incidents.push_back(incident_data.release()); | 469 context->incidents.push_back(incident_data.release()); |
| 400 | 470 |
| 471 // Remember when the first incident for this report arrived. | |
| 472 if (first_incident_time_.is_null()) | |
| 473 first_incident_time_ = base::Time::Now(); | |
| 474 // Log the time between the previous incident and this one. | |
| 401 if (!last_incident_time_.is_null()) { | 475 if (!last_incident_time_.is_null()) { |
| 402 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime", | 476 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime", |
| 403 base::TimeTicks::Now() - last_incident_time_); | 477 base::TimeTicks::Now() - last_incident_time_); |
| 404 } | 478 } |
| 405 last_incident_time_ = base::TimeTicks::Now(); | 479 last_incident_time_ = base::TimeTicks::Now(); |
| 406 | 480 |
| 407 // Persist the incident data. | 481 // Persist the incident data. |
| 408 | 482 |
| 409 // Restart the delay timer to send the report upon expiration. | 483 // Start assembling a new report if this is the first incident ever or the |
| 410 collection_timeout_pending_ = true; | 484 // first since the last upload. |
| 411 upload_timer_.Reset(); | 485 BeginReportProcessing(); |
| 486 } | |
| 412 | 487 |
| 488 void IncidentReportingService::BeginReportProcessing() { | |
| 489 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 490 | |
| 491 // Createa a new report if needed. | |
|
mattm
2014/08/06 02:10:17
Createa
grt (UTC plus 2)
2014/08/06 02:37:31
Done.
| |
| 492 if (!report_) | |
| 493 report_.reset(new ClientIncidentReport()); | |
| 494 | |
| 495 // Ensure that collection tasks are running (calls are idempotent). | |
| 496 BeginIncidentCollation(); | |
| 413 BeginEnvironmentCollection(); | 497 BeginEnvironmentCollection(); |
| 414 BeginDownloadCollection(); | 498 BeginDownloadCollection(); |
| 415 } | 499 } |
| 416 | 500 |
| 501 void IncidentReportingService::BeginIncidentCollation() { | |
| 502 // Restart the delay timer to send the report upon expiration. | |
| 503 collation_timeout_pending_ = true; | |
| 504 collation_timer_.Reset(); | |
| 505 } | |
| 506 | |
| 417 void IncidentReportingService::BeginEnvironmentCollection() { | 507 void IncidentReportingService::BeginEnvironmentCollection() { |
| 418 DCHECK(thread_checker_.CalledOnValidThread()); | 508 DCHECK(thread_checker_.CalledOnValidThread()); |
| 419 DCHECK(report_); | 509 DCHECK(report_); |
| 420 // Nothing to do if environment collection is pending or has already | 510 // Nothing to do if environment collection is pending or has already |
| 421 // completed. | 511 // completed. |
| 422 if (environment_collection_pending_ || report_->has_environment()) | 512 if (environment_collection_pending_ || report_->has_environment()) |
| 423 return; | 513 return; |
| 424 | 514 |
| 425 environment_collection_begin_ = base::TimeTicks::Now(); | 515 environment_collection_begin_ = base::TimeTicks::Now(); |
| 426 ClientIncidentReport_EnvironmentData* environment_data = | 516 ClientIncidentReport_EnvironmentData* environment_data = |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 455 DCHECK(environment_collection_pending_); | 545 DCHECK(environment_collection_pending_); |
| 456 DCHECK(report_ && !report_->has_environment()); | 546 DCHECK(report_ && !report_->has_environment()); |
| 457 environment_collection_pending_ = false; | 547 environment_collection_pending_ = false; |
| 458 | 548 |
| 459 // CurrentProcessInfo::CreationTime() is missing on some platforms. | 549 // CurrentProcessInfo::CreationTime() is missing on some platforms. |
| 460 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) | 550 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) |
| 461 base::TimeDelta uptime = | 551 base::TimeDelta uptime = |
| 462 first_incident_time_ - base::CurrentProcessInfo::CreationTime(); | 552 first_incident_time_ - base::CurrentProcessInfo::CreationTime(); |
| 463 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds()); | 553 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds()); |
| 464 #endif | 554 #endif |
| 465 first_incident_time_ = base::Time(); | |
| 466 | 555 |
| 467 report_->set_allocated_environment(environment_data.release()); | 556 report_->set_allocated_environment(environment_data.release()); |
| 468 | 557 |
| 469 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime", | 558 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime", |
| 470 base::TimeTicks::Now() - environment_collection_begin_); | 559 base::TimeTicks::Now() - environment_collection_begin_); |
| 471 environment_collection_begin_ = base::TimeTicks(); | 560 environment_collection_begin_ = base::TimeTicks(); |
| 472 | 561 |
| 473 UploadIfCollectionComplete(); | 562 UploadIfCollectionComplete(); |
| 474 } | 563 } |
| 475 | 564 |
| 476 bool IncidentReportingService::WaitingToCollateIncidents() { | 565 bool IncidentReportingService::WaitingToCollateIncidents() { |
| 477 return collection_timeout_pending_; | 566 return collation_timeout_pending_; |
| 478 } | 567 } |
| 479 | 568 |
| 480 void IncidentReportingService::CancelIncidentCollection() { | 569 void IncidentReportingService::CancelIncidentCollection() { |
| 481 collection_timeout_pending_ = false; | 570 collation_timeout_pending_ = false; |
| 482 last_incident_time_ = base::TimeTicks(); | 571 last_incident_time_ = base::TimeTicks(); |
| 483 report_.reset(); | 572 report_.reset(); |
| 484 } | 573 } |
| 485 | 574 |
| 486 void IncidentReportingService::OnCollectionTimeout() { | 575 void IncidentReportingService::OnCollationTimeout() { |
| 487 DCHECK(thread_checker_.CalledOnValidThread()); | 576 DCHECK(thread_checker_.CalledOnValidThread()); |
| 488 | 577 |
| 489 // Exit early if collection was cancelled. | 578 // Exit early if collection was cancelled. |
| 490 if (!collection_timeout_pending_) | 579 if (!collation_timeout_pending_) |
| 491 return; | 580 return; |
| 492 | 581 |
| 493 // Wait another round if incidents have come in from a profile that has yet to | 582 // Wait another round if profile-bound incidents have come in from a profile |
| 494 // complete creation. | 583 // that has yet to complete creation. |
| 495 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 584 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
| 496 scan != profiles_.end(); | 585 scan != profiles_.end(); |
| 497 ++scan) { | 586 ++scan) { |
| 498 if (!scan->second->added && !scan->second->incidents.empty()) { | 587 if (scan->first && !scan->second->added && |
| 499 upload_timer_.Reset(); | 588 !scan->second->incidents.empty()) { |
| 589 collation_timer_.Reset(); | |
| 500 return; | 590 return; |
| 501 } | 591 } |
| 502 } | 592 } |
| 503 | 593 |
| 504 collection_timeout_pending_ = false; | 594 collation_timeout_pending_ = false; |
| 505 | 595 |
| 506 UploadIfCollectionComplete(); | 596 UploadIfCollectionComplete(); |
| 507 } | 597 } |
| 508 | 598 |
| 509 void IncidentReportingService::BeginDownloadCollection() { | 599 void IncidentReportingService::BeginDownloadCollection() { |
| 510 DCHECK(thread_checker_.CalledOnValidThread()); | 600 DCHECK(thread_checker_.CalledOnValidThread()); |
| 511 DCHECK(report_); | 601 DCHECK(report_); |
| 512 // Nothing to do if a search for the most recent download is already pending | 602 // Nothing to do if a search for the most recent download is already pending |
| 513 // or if one has already been found. | 603 // or if one has already been found. |
| 514 if (last_download_finder_ || report_->has_download()) | 604 if (last_download_finder_ || report_->has_download()) |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 574 // Bail out if there are still outstanding collection tasks. Completion of any | 664 // Bail out if there are still outstanding collection tasks. Completion of any |
| 575 // of these will start another upload attempt. | 665 // of these will start another upload attempt. |
| 576 if (WaitingForEnvironmentCollection() || | 666 if (WaitingForEnvironmentCollection() || |
| 577 WaitingToCollateIncidents() || | 667 WaitingToCollateIncidents() || |
| 578 WaitingForMostRecentDownload()) { | 668 WaitingForMostRecentDownload()) { |
| 579 return; | 669 return; |
| 580 } | 670 } |
| 581 | 671 |
| 582 // Take ownership of the report and clear things for future reports. | 672 // Take ownership of the report and clear things for future reports. |
| 583 scoped_ptr<ClientIncidentReport> report(report_.Pass()); | 673 scoped_ptr<ClientIncidentReport> report(report_.Pass()); |
| 674 first_incident_time_ = base::Time(); | |
| 584 last_incident_time_ = base::TimeTicks(); | 675 last_incident_time_ = base::TimeTicks(); |
| 585 | 676 |
| 586 // Drop the report if no executable download was found. | 677 // Drop the report if no executable download was found. |
| 587 if (!report->has_download()) { | 678 if (!report->has_download()) { |
| 588 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", | 679 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", |
| 589 IncidentReportUploader::UPLOAD_NO_DOWNLOAD, | 680 IncidentReportUploader::UPLOAD_NO_DOWNLOAD, |
| 590 IncidentReportUploader::NUM_UPLOAD_RESULTS); | 681 IncidentReportUploader::NUM_UPLOAD_RESULTS); |
| 591 return; | 682 return; |
| 592 } | 683 } |
| 593 | 684 |
| 594 ClientIncidentReport_EnvironmentData_Process* process = | 685 ClientIncidentReport_EnvironmentData_Process* process = |
| 595 report->mutable_environment()->mutable_process(); | 686 report->mutable_environment()->mutable_process(); |
| 596 | 687 |
| 597 // Not all platforms have a metrics reporting preference. | 688 // Not all platforms have a metrics reporting preference. |
| 598 if (g_browser_process->local_state()->FindPreference( | 689 if (g_browser_process->local_state()->FindPreference( |
| 599 prefs::kMetricsReportingEnabled)) { | 690 prefs::kMetricsReportingEnabled)) { |
| 600 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( | 691 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( |
| 601 prefs::kMetricsReportingEnabled)); | 692 prefs::kMetricsReportingEnabled)); |
| 602 } | 693 } |
| 603 | 694 |
| 604 // Check for extended consent in any profile while collecting incidents. | 695 // Find a profile suitable for tracking process-wide incidents. |
| 605 process->set_extended_consent(false); | 696 Profile* analyses_profile = FindEligibleProfile(); |
|
mattm
2014/08/06 02:10:17
is "analyses" intentional here? (vs analysis)
grt (UTC plus 2)
2014/08/06 02:37:31
Yeah, it's the profile used for the plural analyse
| |
| 697 process->set_extended_consent( | |
| 698 analyses_profile ? analyses_profile->GetPrefs()->GetBoolean( | |
| 699 prefs::kSafeBrowsingExtendedReportingEnabled) : | |
| 700 false); | |
| 701 | |
| 702 // Associate process-wide incidents with the analyses profile. | |
| 703 ProfileContext* null_context = GetProfileContext(NULL); | |
| 704 if (null_context && analyses_profile) { | |
| 705 DCHECK(!null_context->incidents.empty()); | |
| 706 ProfileContext* analyses_context = GetProfileContext(analyses_profile); | |
| 707 // Move the incidents to the target context. | |
| 708 analyses_context->incidents.insert(analyses_context->incidents.end(), | |
| 709 null_context->incidents.begin(), | |
| 710 null_context->incidents.end()); | |
| 711 null_context->incidents.weak_clear(); | |
| 712 // Delete the process-wide context. | |
| 713 delete null_context; | |
| 714 profiles_.erase(NULL); | |
|
mattm
2014/08/06 02:10:17
Does this mean no more process-wide incidents can
grt (UTC plus 2)
2014/08/06 02:37:31
Nice catch. Any added after this should go into a
| |
| 715 } | |
| 716 | |
| 606 // Collect incidents across all profiles participating in safe browsing. Drop | 717 // Collect incidents across all profiles participating in safe browsing. Drop |
| 607 // incidents if the profile stopped participating before collection completed. | 718 // incidents if the profile stopped participating before collection completed. |
| 608 // Prune previously submitted incidents. | 719 // Prune previously submitted incidents. |
| 609 // Associate the profiles and their incident data with the upload. | 720 // Associate the profiles and their incident data with the upload. |
| 610 size_t prune_count = 0; | 721 size_t prune_count = 0; |
| 611 UploadContext::PersistentIncidentStateCollection profiles_to_state; | 722 UploadContext::PersistentIncidentStateCollection profiles_to_state; |
| 612 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 723 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
| 613 scan != profiles_.end(); | 724 scan != profiles_.end(); |
| 614 ++scan) { | 725 ++scan) { |
| 726 // Bypass process-wide incidents that have not yet been associated with a | |
| 727 // profile. | |
| 728 if (!scan->first) | |
| 729 continue; | |
| 615 PrefService* prefs = scan->first->GetPrefs(); | 730 PrefService* prefs = scan->first->GetPrefs(); |
| 616 if (process && | |
| 617 prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { | |
| 618 process->set_extended_consent(true); | |
| 619 process = NULL; // Don't check any more once one is found. | |
| 620 } | |
| 621 ProfileContext* context = scan->second; | 731 ProfileContext* context = scan->second; |
| 622 if (context->incidents.empty()) | 732 if (context->incidents.empty()) |
| 623 continue; | 733 continue; |
| 624 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 734 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
| 625 for (size_t i = 0; i < context->incidents.size(); ++i) { | 735 for (size_t i = 0; i < context->incidents.size(); ++i) { |
| 626 LogIncidentDataType(DROPPED, *context->incidents[i]); | 736 LogIncidentDataType(DROPPED, *context->incidents[i]); |
| 627 } | 737 } |
| 628 context->incidents.clear(); | 738 context->incidents.clear(); |
| 629 continue; | 739 continue; |
| 630 } | 740 } |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 793 if (!profile->IsOffTheRecord()) | 903 if (!profile->IsOffTheRecord()) |
| 794 OnProfileDestroyed(profile); | 904 OnProfileDestroyed(profile); |
| 795 break; | 905 break; |
| 796 } | 906 } |
| 797 default: | 907 default: |
| 798 break; | 908 break; |
| 799 } | 909 } |
| 800 } | 910 } |
| 801 | 911 |
| 802 } // namespace safe_browsing | 912 } // namespace safe_browsing |
| OLD | NEW |