OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 #import <UIKit/UIKit.h> |
| 6 |
| 7 #import "ios/chrome/today_extension/today_metrics_logger.h" |
| 8 |
| 9 #include "base/base64.h" |
| 10 #include "base/cpu.h" |
| 11 #include "base/mac/bind_objc_block.h" |
| 12 #import "base/mac/scoped_nsobject.h" |
| 13 #include "base/metrics/histogram_base.h" |
| 14 #include "base/metrics/statistics_recorder.h" |
| 15 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/strings/utf_string_conversions.h" |
| 17 #include "base/sys_info.h" |
| 18 #include "components/metrics/metrics_log.h" |
| 19 #include "components/metrics/metrics_log_uploader.h" |
| 20 #include "components/metrics/metrics_pref_names.h" |
| 21 #include "components/metrics/metrics_service_client.h" |
| 22 #include "components/metrics/net/version_utils.h" |
| 23 #include "components/prefs/json_pref_store.h" |
| 24 #include "components/prefs/pref_registry_simple.h" |
| 25 #include "components/prefs/pref_service.h" |
| 26 #include "components/prefs/pref_service_factory.h" |
| 27 #include "components/prefs/value_map_pref_store.h" |
| 28 #include "components/variations/active_field_trials.h" |
| 29 #include "components/version_info/version_info.h" |
| 30 #import "ios/chrome/common/app_group/app_group_constants.h" |
| 31 #import "ios/chrome/common/app_group/app_group_metrics.h" |
| 32 #import "ios/chrome/common/app_group/app_group_metrics_client.h" |
| 33 #include "ios/chrome/common/channel_info.h" |
| 34 |
| 35 namespace { |
| 36 |
| 37 // User default key to keep track of the current log session ID. Increased every |
| 38 // time a log is created. This ID must be offset using |
| 39 // app_group::AppGroupSessionID. |
| 40 NSString* const kTodayExtensionMetricsSessionID = @"MetricsSessionID"; |
| 41 |
| 42 // User default key to the current log serialized. In case of an extension |
| 43 // restart, this log can be written to disk for upload. |
| 44 NSString* const kTodayExtensionMetricsCurrentLog = @"MetricsCurrentLog"; |
| 45 |
| 46 // Maximum number of event in a log. |
| 47 const int kMaxEventsPerLog = 1000; |
| 48 |
| 49 // Maximum age of a log. |
| 50 const int kMaxLogLifeTimeInSeconds = 86400; |
| 51 |
| 52 // A simple implementation of metrics::MetricsServiceClient. |
| 53 // As logs are uploaded by Chrome application, not all methods are needed. |
| 54 // Only the method needed to initialize the metrics logs are implementsd. |
| 55 class TodayMetricsServiceClient : public metrics::MetricsServiceClient { |
| 56 public: |
| 57 TodayMetricsServiceClient() {} |
| 58 metrics::MetricsService* GetMetricsService() override; |
| 59 void SetMetricsClientId(const std::string& client_id) override; |
| 60 int32_t GetProduct() override; |
| 61 std::string GetApplicationLocale() override; |
| 62 bool GetBrand(std::string* brand_code) override; |
| 63 metrics::SystemProfileProto::Channel GetChannel() override; |
| 64 std::string GetVersionString() override; |
| 65 void OnLogUploadComplete() override; |
| 66 void InitializeSystemProfileMetrics( |
| 67 const base::Closure& done_callback) override; |
| 68 void CollectFinalMetricsForLog(const base::Closure& done_callback) override; |
| 69 std::unique_ptr<metrics::MetricsLogUploader> CreateUploader( |
| 70 const base::Callback<void(int)>& on_upload_complete) override; |
| 71 base::TimeDelta GetStandardUploadInterval() override; |
| 72 |
| 73 private: |
| 74 DISALLOW_COPY_AND_ASSIGN(TodayMetricsServiceClient); |
| 75 }; |
| 76 |
| 77 class TodayMetricsLog : public metrics::MetricsLog { |
| 78 public: |
| 79 // Creates a new today metrics log of the specified type. |
| 80 // TodayMetricsLog is similar to metrics::MetricsLog but allow serialization |
| 81 // of open logs. |
| 82 TodayMetricsLog(const std::string& client_id, |
| 83 int session_id, |
| 84 LogType log_type, |
| 85 TodayMetricsServiceClient* client, |
| 86 PrefService* local_state); |
| 87 |
| 88 // Fills |encoded_log| with the serialized protobuf representation of the |
| 89 // record. Can be called even on open log. |
| 90 void GetOpenEncodedLog(std::string* encoded_log) const; |
| 91 |
| 92 private: |
| 93 DISALLOW_COPY_AND_ASSIGN(TodayMetricsLog); |
| 94 }; |
| 95 |
| 96 metrics::MetricsService* TodayMetricsServiceClient::GetMetricsService() { |
| 97 NOTREACHED(); |
| 98 return nullptr; |
| 99 } |
| 100 |
| 101 void TodayMetricsServiceClient::SetMetricsClientId( |
| 102 const std::string& client_id) { |
| 103 NOTREACHED(); |
| 104 } |
| 105 |
| 106 int32_t TodayMetricsServiceClient::GetProduct() { |
| 107 return metrics::ChromeUserMetricsExtension::CHROME; |
| 108 } |
| 109 |
| 110 bool TodayMetricsServiceClient::GetBrand(std::string* brand_code) { |
| 111 base::scoped_nsobject<NSUserDefaults> shared_defaults( |
| 112 [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]); |
| 113 |
| 114 NSString* ns_brand_code = [shared_defaults |
| 115 stringForKey:base::SysUTF8ToNSString(app_group::kBrandCode)]; |
| 116 if (!ns_brand_code) |
| 117 return false; |
| 118 |
| 119 *brand_code = base::SysNSStringToUTF8(ns_brand_code); |
| 120 return true; |
| 121 } |
| 122 |
| 123 metrics::SystemProfileProto::Channel TodayMetricsServiceClient::GetChannel() { |
| 124 return metrics::AsProtobufChannel(::GetChannel()); |
| 125 } |
| 126 |
| 127 std::string TodayMetricsServiceClient::GetApplicationLocale() { |
| 128 return base::SysNSStringToUTF8( |
| 129 [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]); |
| 130 } |
| 131 |
| 132 std::string TodayMetricsServiceClient::GetVersionString() { |
| 133 return metrics::GetVersionString(); |
| 134 } |
| 135 |
| 136 void TodayMetricsServiceClient::OnLogUploadComplete() { |
| 137 NOTREACHED(); |
| 138 } |
| 139 |
| 140 void TodayMetricsServiceClient::InitializeSystemProfileMetrics( |
| 141 const base::Closure& done_callback) { |
| 142 NOTREACHED(); |
| 143 } |
| 144 |
| 145 void TodayMetricsServiceClient::CollectFinalMetricsForLog( |
| 146 const base::Closure& done_callback) { |
| 147 NOTREACHED(); |
| 148 } |
| 149 |
| 150 std::unique_ptr<metrics::MetricsLogUploader> |
| 151 TodayMetricsServiceClient::CreateUploader( |
| 152 const base::Callback<void(int)>& on_upload_complete) { |
| 153 NOTREACHED(); |
| 154 return nullptr; |
| 155 } |
| 156 |
| 157 base::TimeDelta TodayMetricsServiceClient::GetStandardUploadInterval() { |
| 158 NOTREACHED(); |
| 159 return base::TimeDelta::FromSeconds(0); |
| 160 } |
| 161 |
| 162 TodayMetricsLog::TodayMetricsLog(const std::string& client_id, |
| 163 int session_id, |
| 164 LogType log_type, |
| 165 TodayMetricsServiceClient* client, |
| 166 PrefService* local_state) |
| 167 : metrics::MetricsLog(client_id, |
| 168 session_id, |
| 169 log_type, |
| 170 client, |
| 171 local_state) {} |
| 172 |
| 173 void TodayMetricsLog::GetOpenEncodedLog(std::string* encoded_log) const { |
| 174 uma_proto()->SerializeToString(encoded_log); |
| 175 } |
| 176 |
| 177 } // namespace |
| 178 |
| 179 TodayMetricsLogger* TodayMetricsLogger::GetInstance() { |
| 180 // |logger| is a singleton that should live as long as the application. |
| 181 // We do not delete it and it will be deleted when the application dies. |
| 182 static TodayMetricsLogger* logger = new TodayMetricsLogger(); |
| 183 return logger; |
| 184 } |
| 185 |
| 186 void TodayMetricsLogger::RecordUserAction(base::UserMetricsAction action) { |
| 187 if (!log_ && !CreateNewLog()) { |
| 188 return; |
| 189 } |
| 190 log_->RecordUserAction(action.str_); |
| 191 PersistLogs(); |
| 192 } |
| 193 |
| 194 void TodayMetricsLogger::PersistLogs() { |
| 195 histogram_snapshot_manager_.PrepareDeltas( |
| 196 base::StatisticsRecorder::begin(false), base::StatisticsRecorder::end(), |
| 197 base::Histogram::kNoFlags, base::Histogram::kNoFlags); |
| 198 std::string encoded_log; |
| 199 log_->GetOpenEncodedLog(&encoded_log); |
| 200 NSData* ns_encoded_log = |
| 201 [NSData dataWithBytes:encoded_log.c_str() length:encoded_log.length()]; |
| 202 [[NSUserDefaults standardUserDefaults] |
| 203 setObject:ns_encoded_log |
| 204 forKey:kTodayExtensionMetricsCurrentLog]; |
| 205 if (log_->num_events() >= kMaxEventsPerLog || |
| 206 (base::TimeTicks::Now() - log_->creation_time()).InSeconds() >= |
| 207 kMaxLogLifeTimeInSeconds) { |
| 208 CreateNewLog(); |
| 209 } |
| 210 } |
| 211 |
| 212 bool TodayMetricsLogger::CreateNewLog() { |
| 213 id previous_log = [[NSUserDefaults standardUserDefaults] |
| 214 dataForKey:kTodayExtensionMetricsCurrentLog]; |
| 215 if (previous_log) { |
| 216 app_group::client_app::AddPendingLog(previous_log, |
| 217 app_group::APP_GROUP_TODAY_EXTENSION); |
| 218 thread_pool_->PostTask( |
| 219 FROM_HERE, base::Bind(&app_group::client_app::CleanOldPendingLogs)); |
| 220 } |
| 221 |
| 222 base::scoped_nsobject<NSUserDefaults> shared_defaults( |
| 223 [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]); |
| 224 |
| 225 NSString* client_id = [shared_defaults |
| 226 stringForKey:base::SysUTF8ToNSString(app_group::kChromeAppClientID)]; |
| 227 NSString* enabled_date = [shared_defaults |
| 228 stringForKey:base::SysUTF8ToNSString(app_group::kUserMetricsEnabledDate)]; |
| 229 |
| 230 NSString* install_date = [shared_defaults |
| 231 stringForKey:base::SysUTF8ToNSString(app_group::kInstallDate)]; |
| 232 |
| 233 if (!client_id || !enabled_date || !install_date) { |
| 234 return false; |
| 235 } |
| 236 |
| 237 int session_id = [[NSUserDefaults standardUserDefaults] |
| 238 integerForKey:kTodayExtensionMetricsSessionID]; |
| 239 [[NSUserDefaults standardUserDefaults] |
| 240 setInteger:session_id + 1 |
| 241 forKey:kTodayExtensionMetricsSessionID]; |
| 242 session_id = app_group::AppGroupSessionID( |
| 243 session_id, app_group::APP_GROUP_TODAY_EXTENSION); |
| 244 log_.reset(new TodayMetricsLog(base::SysNSStringToUTF8(client_id), session_id, |
| 245 metrics::MetricsLog::ONGOING_LOG, |
| 246 metrics_service_client_.get(), |
| 247 pref_service_.get())); |
| 248 |
| 249 log_->RecordEnvironment(std::vector<metrics::MetricsProvider*>(), |
| 250 std::vector<variations::ActiveGroupId>(), |
| 251 [install_date longLongValue], |
| 252 [enabled_date longLongValue]); |
| 253 |
| 254 return true; |
| 255 } |
| 256 |
| 257 TodayMetricsLogger::TodayMetricsLogger() |
| 258 : pref_registry_(new PrefRegistrySimple()), |
| 259 thread_pool_( |
| 260 new base::SequencedWorkerPool(2, |
| 261 "LoggerPool", |
| 262 base::TaskPriority::BACKGROUND)), |
| 263 metrics_service_client_(new TodayMetricsServiceClient()), |
| 264 histogram_snapshot_manager_(this) { |
| 265 metrics::MetricsLog::RegisterPrefs(pref_registry_.get()); |
| 266 |
| 267 NSString* url = [[NSSearchPathForDirectoriesInDomains( |
| 268 NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] |
| 269 stringByAppendingPathComponent:@"Application Support/localstate"]; |
| 270 base::FilePath path(base::SysNSStringToUTF8(url)); |
| 271 sequenced_task_runner_ = |
| 272 JsonPrefStore::GetTaskRunnerForFile(path, thread_pool_.get()); |
| 273 PrefServiceFactory factory; |
| 274 factory.set_extension_prefs(value_map_prefs_.get()); |
| 275 factory.SetUserPrefsFile(path, sequenced_task_runner_.get()); |
| 276 pref_service_ = factory.Create(pref_registry_.get()); |
| 277 base::StatisticsRecorder::Initialize(); |
| 278 } |
| 279 |
| 280 TodayMetricsLogger::~TodayMetricsLogger() {} |
| 281 |
| 282 void TodayMetricsLogger::RecordDelta(const base::HistogramBase& histogram, |
| 283 const base::HistogramSamples& snapshot) { |
| 284 log_->RecordHistogramDelta(histogram.histogram_name(), snapshot); |
| 285 } |
| 286 |
| 287 void TodayMetricsLogger::InconsistencyDetected( |
| 288 base::HistogramBase::Inconsistency problem) { |
| 289 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowser", problem, |
| 290 base::HistogramBase::NEVER_EXCEEDED_VALUE); |
| 291 } |
| 292 |
| 293 void TodayMetricsLogger::UniqueInconsistencyDetected( |
| 294 base::HistogramBase::Inconsistency problem) { |
| 295 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowserUnique", problem, |
| 296 base::HistogramBase::NEVER_EXCEEDED_VALUE); |
| 297 } |
| 298 |
| 299 void TodayMetricsLogger::InconsistencyDetectedInLoggedCount(int amount) { |
| 300 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentSnapshotBrowser", |
| 301 std::abs(amount)); |
| 302 } |
OLD | NEW |