| 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 #include "chrome/browser/android/datausage/external_data_use_observer.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/android/jni_string.h" | |
| 10 #include "base/message_loop/message_loop.h" | |
| 11 #include "components/data_usage/core/data_use.h" | |
| 12 #include "content/public/browser/browser_thread.h" | |
| 13 #include "jni/ExternalDataUseObserver_jni.h" | |
| 14 #include "third_party/re2/re2/re2.h" | |
| 15 #include "url/gurl.h" | |
| 16 | |
| 17 using base::android::ConvertUTF8ToJavaString; | |
| 18 using base::android::ToJavaArrayOfStrings; | |
| 19 | |
| 20 namespace chrome { | |
| 21 | |
| 22 namespace android { | |
| 23 | |
| 24 ExternalDataUseObserver::ExternalDataUseObserver( | |
| 25 data_usage::DataUseAggregator* data_use_aggregator, | |
| 26 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, | |
| 27 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | |
| 28 : data_use_aggregator_(data_use_aggregator), | |
| 29 matching_rules_fetch_pending_(false), | |
| 30 submit_data_report_pending_(false), | |
| 31 registered_as_observer_(false), | |
| 32 io_task_runner_(io_task_runner), | |
| 33 ui_task_runner_(ui_task_runner), | |
| 34 previous_report_time_(base::Time::Now()), | |
| 35 io_weak_factory_(this), | |
| 36 ui_weak_factory_(this) { | |
| 37 DCHECK(data_use_aggregator_); | |
| 38 DCHECK(io_task_runner_); | |
| 39 DCHECK(ui_task_runner_); | |
| 40 ui_task_runner_->PostTask( | |
| 41 FROM_HERE, | |
| 42 base::Bind(&ExternalDataUseObserver::CreateJavaObjectOnUIThread, | |
| 43 GetUIWeakPtr())); | |
| 44 | |
| 45 ui_task_runner_->PostTask( | |
| 46 FROM_HERE, | |
| 47 base::Bind(&ExternalDataUseObserver::FetchMatchingRulesOnUIThread, | |
| 48 GetUIWeakPtr())); | |
| 49 | |
| 50 matching_rules_fetch_pending_ = true; | |
| 51 data_use_aggregator_->AddObserver(this); | |
| 52 registered_as_observer_ = true; | |
| 53 } | |
| 54 | |
| 55 void ExternalDataUseObserver::CreateJavaObjectOnUIThread() { | |
| 56 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 57 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 58 j_external_data_use_observer_.Reset(Java_ExternalDataUseObserver_create( | |
| 59 env, base::android::GetApplicationContext(), | |
| 60 reinterpret_cast<intptr_t>(this))); | |
| 61 DCHECK(!j_external_data_use_observer_.is_null()); | |
| 62 } | |
| 63 | |
| 64 ExternalDataUseObserver::~ExternalDataUseObserver() { | |
| 65 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 66 | |
| 67 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 68 if (!j_external_data_use_observer_.is_null()) { | |
| 69 Java_ExternalDataUseObserver_onDestroy(env, | |
| 70 j_external_data_use_observer_.obj()); | |
| 71 } | |
| 72 if (registered_as_observer_) | |
| 73 data_use_aggregator_->RemoveObserver(this); | |
| 74 } | |
| 75 | |
| 76 void ExternalDataUseObserver::FetchMatchingRulesOnUIThread() const { | |
| 77 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 78 DCHECK(!j_external_data_use_observer_.is_null()); | |
| 79 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 80 Java_ExternalDataUseObserver_fetchMatchingRules( | |
| 81 env, j_external_data_use_observer_.obj()); | |
| 82 } | |
| 83 | |
| 84 void ExternalDataUseObserver::FetchMatchingRulesCallback( | |
| 85 JNIEnv* env, | |
| 86 jobject obj, | |
| 87 const base::android::JavaParamRef<jobjectArray>& app_package_name, | |
| 88 const base::android::JavaParamRef<jobjectArray>& domain_path_regex, | |
| 89 const base::android::JavaParamRef<jobjectArray>& label) { | |
| 90 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 91 // Convert to native objects. | |
| 92 std::vector<std::string> app_package_name_native; | |
| 93 std::vector<std::string> domain_path_regex_native; | |
| 94 std::vector<std::string> label_native; | |
| 95 | |
| 96 if (app_package_name && domain_path_regex && label) { | |
| 97 base::android::AppendJavaStringArrayToStringVector( | |
| 98 env, app_package_name, &app_package_name_native); | |
| 99 base::android::AppendJavaStringArrayToStringVector( | |
| 100 env, domain_path_regex, &domain_path_regex_native); | |
| 101 base::android::AppendJavaStringArrayToStringVector(env, label, | |
| 102 &label_native); | |
| 103 } | |
| 104 | |
| 105 io_task_runner_->PostTask( | |
| 106 FROM_HERE, | |
| 107 base::Bind(&ExternalDataUseObserver::FetchMatchingRulesCallbackOnIOThread, | |
| 108 GetIOWeakPtr(), app_package_name_native, | |
| 109 domain_path_regex_native, label_native)); | |
| 110 } | |
| 111 | |
| 112 void ExternalDataUseObserver::FetchMatchingRulesCallbackOnIOThread( | |
| 113 const std::vector<std::string>& app_package_name, | |
| 114 const std::vector<std::string>& domain_path_regex, | |
| 115 const std::vector<std::string>& label) { | |
| 116 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 117 | |
| 118 RegisterURLRegexes(app_package_name, domain_path_regex, label); | |
| 119 matching_rules_fetch_pending_ = false; | |
| 120 // Process buffered reports. | |
| 121 } | |
| 122 | |
| 123 void ExternalDataUseObserver::OnReportDataUseDone(JNIEnv* env, | |
| 124 jobject obj, | |
| 125 bool success) { | |
| 126 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 127 io_task_runner_->PostTask( | |
| 128 FROM_HERE, | |
| 129 base::Bind(&ExternalDataUseObserver::OnReportDataUseDoneOnIOThread, | |
| 130 GetIOWeakPtr(), success)); | |
| 131 } | |
| 132 | |
| 133 base::WeakPtr<ExternalDataUseObserver> ExternalDataUseObserver::GetIOWeakPtr() { | |
| 134 return io_weak_factory_.GetWeakPtr(); | |
| 135 } | |
| 136 | |
| 137 base::WeakPtr<ExternalDataUseObserver> ExternalDataUseObserver::GetUIWeakPtr() { | |
| 138 return ui_weak_factory_.GetWeakPtr(); | |
| 139 } | |
| 140 | |
| 141 void ExternalDataUseObserver::OnReportDataUseDoneOnIOThread(bool success) { | |
| 142 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 143 DCHECK(!buffered_data_reports_.empty()); | |
| 144 DCHECK(submit_data_report_pending_); | |
| 145 | |
| 146 // TODO(tbansal): If not successful, record UMA. | |
| 147 | |
| 148 submit_data_report_pending_ = false; | |
| 149 | |
| 150 SubmitBufferedDataUseReport(); | |
| 151 } | |
| 152 | |
| 153 void ExternalDataUseObserver::OnDataUse( | |
| 154 const std::vector<const data_usage::DataUse*>& data_use_sequence) { | |
| 155 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 156 | |
| 157 if (matching_rules_fetch_pending_) { | |
| 158 // TODO(tbansal): Buffer reports. | |
| 159 } | |
| 160 | |
| 161 std::string label; | |
| 162 | |
| 163 for (const data_usage::DataUse* data_use : data_use_sequence) { | |
| 164 if (!Matches(data_use->url, &label)) | |
| 165 continue; | |
| 166 | |
| 167 BufferDataUseReport(data_use, label, previous_report_time_, | |
| 168 base::Time::Now()); | |
| 169 } | |
| 170 previous_report_time_ = base::Time::Now(); | |
| 171 | |
| 172 // TODO(tbansal): Post SubmitBufferedDataUseReport on IO thread once the | |
| 173 // task runners are plumbed in. | |
| 174 SubmitBufferedDataUseReport(); | |
| 175 } | |
| 176 | |
| 177 void ExternalDataUseObserver::BufferDataUseReport( | |
| 178 const data_usage::DataUse* data_use, | |
| 179 const std::string& label, | |
| 180 const base::Time& start_time, | |
| 181 const base::Time& end_time) { | |
| 182 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 183 DCHECK(!label.empty()); | |
| 184 DCHECK_LE(0, data_use->rx_bytes); | |
| 185 DCHECK_LE(0, data_use->tx_bytes); | |
| 186 if (data_use->rx_bytes < 0 || data_use->tx_bytes < 0) | |
| 187 return; | |
| 188 | |
| 189 DataUseReportKey data_use_report_key = | |
| 190 DataUseReportKey(label, data_use->connection_type, data_use->mcc_mnc); | |
| 191 | |
| 192 DataUseReport report = DataUseReport(start_time, end_time, data_use->rx_bytes, | |
| 193 data_use->tx_bytes); | |
| 194 | |
| 195 // Check if the |data_use_report_key| is already in the buffered reports. | |
| 196 DataUseReports::iterator it = | |
| 197 buffered_data_reports_.find(data_use_report_key); | |
| 198 if (it == buffered_data_reports_.end()) { | |
| 199 // Limit the buffer size. | |
| 200 if (buffered_data_reports_.size() == kMaxBufferSize) { | |
| 201 // TODO(tbansal): Add UMA to track impact of lost reports. | |
| 202 // Remove the first entry. | |
| 203 buffered_data_reports_.erase(buffered_data_reports_.begin()); | |
| 204 } | |
| 205 buffered_data_reports_.insert(std::make_pair(data_use_report_key, report)); | |
| 206 } else { | |
| 207 DataUseReport existing_report = DataUseReport(it->second); | |
| 208 DataUseReport merged_report = DataUseReport( | |
| 209 std::min(existing_report.start_time, report.start_time), | |
| 210 std::max(existing_report.end_time, report.end_time), | |
| 211 existing_report.bytes_downloaded + report.bytes_downloaded, | |
| 212 existing_report.bytes_uploaded + report.bytes_uploaded); | |
| 213 buffered_data_reports_.erase(it); | |
| 214 buffered_data_reports_.insert( | |
| 215 std::make_pair(data_use_report_key, merged_report)); | |
| 216 } | |
| 217 | |
| 218 DCHECK_LE(buffered_data_reports_.size(), | |
| 219 static_cast<size_t>(kMaxBufferSize)); | |
| 220 } | |
| 221 | |
| 222 void ExternalDataUseObserver::SubmitBufferedDataUseReport() { | |
| 223 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 224 | |
| 225 if (submit_data_report_pending_ || buffered_data_reports_.empty()) | |
| 226 return; | |
| 227 | |
| 228 // TODO(tbansal): Keep buffering until enough data has been received. | |
| 229 | |
| 230 // Send one data use report. | |
| 231 DataUseReports::iterator it = buffered_data_reports_.begin(); | |
| 232 DataUseReportKey key = it->first; | |
| 233 DataUseReport report = it->second; | |
| 234 | |
| 235 // Remove the entry from the map. | |
| 236 buffered_data_reports_.erase(it); | |
| 237 | |
| 238 submit_data_report_pending_ = true; | |
| 239 | |
| 240 ui_task_runner_->PostTask( | |
| 241 FROM_HERE, base::Bind(&ExternalDataUseObserver::ReportDataUseOnUIThread, | |
| 242 GetIOWeakPtr(), key, report)); | |
| 243 } | |
| 244 | |
| 245 void ExternalDataUseObserver::ReportDataUseOnUIThread( | |
| 246 const DataUseReportKey& key, | |
| 247 const DataUseReport& report) const { | |
| 248 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 249 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 250 DCHECK(!j_external_data_use_observer_.is_null()); | |
| 251 | |
| 252 // End time should be greater than start time. | |
| 253 int64_t start_time_milliseconds = report.start_time.ToJavaTime(); | |
| 254 int64_t end_time_milliseconds = report.end_time.ToJavaTime(); | |
| 255 if (start_time_milliseconds >= end_time_milliseconds) | |
| 256 start_time_milliseconds = end_time_milliseconds - 1; | |
| 257 | |
| 258 Java_ExternalDataUseObserver_reportDataUse( | |
| 259 env, j_external_data_use_observer_.obj(), | |
| 260 ConvertUTF8ToJavaString(env, key.label).obj(), key.connection_type, | |
| 261 ConvertUTF8ToJavaString(env, key.mcc_mnc).obj(), start_time_milliseconds, | |
| 262 end_time_milliseconds, report.bytes_downloaded, report.bytes_uploaded); | |
| 263 } | |
| 264 | |
| 265 void ExternalDataUseObserver::RegisterURLRegexes( | |
| 266 const std::vector<std::string>& app_package_name, | |
| 267 const std::vector<std::string>& domain_path_regex, | |
| 268 const std::vector<std::string>& label) { | |
| 269 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 270 DCHECK_EQ(app_package_name.size(), domain_path_regex.size()); | |
| 271 DCHECK_EQ(app_package_name.size(), label.size()); | |
| 272 | |
| 273 matching_rules_.clear(); | |
| 274 re2::RE2::Options options(re2::RE2::DefaultOptions); | |
| 275 options.set_case_sensitive(false); | |
| 276 | |
| 277 for (size_t i = 0; i < domain_path_regex.size(); ++i) { | |
| 278 const std::string& url_regex = domain_path_regex[i]; | |
| 279 if (url_regex.empty()) | |
| 280 continue; | |
| 281 scoped_ptr<re2::RE2> pattern(new re2::RE2(url_regex, options)); | |
| 282 if (!pattern->ok()) | |
| 283 continue; | |
| 284 DCHECK(!label[i].empty()); | |
| 285 matching_rules_.push_back( | |
| 286 new MatchingRule(app_package_name[i], pattern.Pass(), label[i])); | |
| 287 } | |
| 288 | |
| 289 if (matching_rules_.size() == 0 && registered_as_observer_) { | |
| 290 // Unregister as an observer if no regular expressions were received. | |
| 291 data_use_aggregator_->RemoveObserver(this); | |
| 292 registered_as_observer_ = false; | |
| 293 } else if (matching_rules_.size() > 0 && !registered_as_observer_) { | |
| 294 // Register as an observer if regular expressions were received. | |
| 295 data_use_aggregator_->AddObserver(this); | |
| 296 registered_as_observer_ = true; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 bool ExternalDataUseObserver::Matches(const GURL& gurl, | |
| 301 std::string* label) const { | |
| 302 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 303 *label = ""; | |
| 304 | |
| 305 if (!gurl.is_valid() || gurl.is_empty()) | |
| 306 return false; | |
| 307 | |
| 308 for (size_t i = 0; i < matching_rules_.size(); ++i) { | |
| 309 const re2::RE2* pattern = matching_rules_[i]->pattern(); | |
| 310 if (re2::RE2::FullMatch(gurl.spec(), *pattern)) { | |
| 311 *label = matching_rules_[i]->label(); | |
| 312 return true; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 return false; | |
| 317 } | |
| 318 | |
| 319 ExternalDataUseObserver::MatchingRule::MatchingRule( | |
| 320 const std::string& app_package_name, | |
| 321 scoped_ptr<re2::RE2> pattern, | |
| 322 const std::string& label) | |
| 323 : app_package_name_(app_package_name), | |
| 324 pattern_(pattern.Pass()), | |
| 325 label_(label) {} | |
| 326 | |
| 327 ExternalDataUseObserver::MatchingRule::~MatchingRule() {} | |
| 328 | |
| 329 const re2::RE2* ExternalDataUseObserver::MatchingRule::pattern() const { | |
| 330 return pattern_.get(); | |
| 331 } | |
| 332 | |
| 333 const std::string& ExternalDataUseObserver::MatchingRule::label() const { | |
| 334 return label_; | |
| 335 } | |
| 336 | |
| 337 bool RegisterExternalDataUseObserver(JNIEnv* env) { | |
| 338 return RegisterNativesImpl(env); | |
| 339 } | |
| 340 | |
| 341 } // namespace android | |
| 342 | |
| 343 } // namespace chrome | |
| OLD | NEW |