|  | // Copyright 2019 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. | 
|  |  | 
|  | #include "base/android/jni_android.h" | 
|  | #include "base/android/jni_string.h" | 
|  | #include "base/base_jni_headers/NativeUmaRecorder_jni.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/metrics/histogram.h" | 
|  | #include "base/metrics/histogram_base.h" | 
|  | #include "base/metrics/sparse_histogram.h" | 
|  | #include "base/metrics/user_metrics.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "base/time/time.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace android { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Simple thread-safe wrapper for caching histograms. This avoids | 
|  | // relatively expensive JNI string translation for each recording. | 
|  | class HistogramCache { | 
|  | public: | 
|  | HistogramCache() {} | 
|  |  | 
|  | std::string HistogramConstructionParamsToString(HistogramBase* histogram) { | 
|  | std::string params_str = histogram->histogram_name(); | 
|  | switch (histogram->GetHistogramType()) { | 
|  | case HISTOGRAM: | 
|  | case LINEAR_HISTOGRAM: | 
|  | case BOOLEAN_HISTOGRAM: | 
|  | case CUSTOM_HISTOGRAM: { | 
|  | Histogram* hist = static_cast<Histogram*>(histogram); | 
|  | params_str += StringPrintf("/%d/%d/%d", hist->declared_min(), | 
|  | hist->declared_max(), hist->bucket_count()); | 
|  | break; | 
|  | } | 
|  | case SPARSE_HISTOGRAM: | 
|  | case DUMMY_HISTOGRAM: | 
|  | break; | 
|  | } | 
|  | return params_str; | 
|  | } | 
|  |  | 
|  | void CheckHistogramArgs(JNIEnv* env, | 
|  | jstring j_histogram_name, | 
|  | int32_t expected_min, | 
|  | int32_t expected_max, | 
|  | uint32_t expected_bucket_count, | 
|  | HistogramBase* histogram) { | 
|  | std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
|  | bool valid_arguments = Histogram::InspectConstructionArguments( | 
|  | histogram_name, &expected_min, &expected_max, &expected_bucket_count); | 
|  | DCHECK(valid_arguments); | 
|  | DCHECK(histogram->HasConstructionArguments(expected_min, expected_max, | 
|  | expected_bucket_count)) | 
|  | << histogram_name << "/" << expected_min << "/" << expected_max << "/" | 
|  | << expected_bucket_count << " vs. " | 
|  | << HistogramConstructionParamsToString(histogram); | 
|  | } | 
|  |  | 
|  | HistogramBase* BooleanHistogram(JNIEnv* env, | 
|  | jstring j_histogram_name, | 
|  | jlong j_histogram_hint) { | 
|  | DCHECK(j_histogram_name); | 
|  | HistogramBase* histogram = HistogramFromHint(j_histogram_hint); | 
|  | if (histogram) | 
|  | return histogram; | 
|  |  | 
|  | std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
|  | histogram = BooleanHistogram::FactoryGet( | 
|  | histogram_name, HistogramBase::kUmaTargetedHistogramFlag); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | HistogramBase* ExponentialHistogram(JNIEnv* env, | 
|  | jstring j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jint j_min, | 
|  | jint j_max, | 
|  | jint j_num_buckets) { | 
|  | DCHECK(j_histogram_name); | 
|  | int32_t min = static_cast<int32_t>(j_min); | 
|  | int32_t max = static_cast<int32_t>(j_max); | 
|  | int32_t num_buckets = static_cast<int32_t>(j_num_buckets); | 
|  | HistogramBase* histogram = HistogramFromHint(j_histogram_hint); | 
|  | if (histogram) { | 
|  | CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, | 
|  | histogram); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | DCHECK_GE(min, 1) << "The min expected sample must be >= 1"; | 
|  |  | 
|  | std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
|  | histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets, | 
|  | HistogramBase::kUmaTargetedHistogramFlag); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | HistogramBase* LinearHistogram(JNIEnv* env, | 
|  | jstring j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jint j_min, | 
|  | jint j_max, | 
|  | jint j_num_buckets) { | 
|  | DCHECK(j_histogram_name); | 
|  | int32_t min = static_cast<int32_t>(j_min); | 
|  | int32_t max = static_cast<int32_t>(j_max); | 
|  | int32_t num_buckets = static_cast<int32_t>(j_num_buckets); | 
|  | HistogramBase* histogram = HistogramFromHint(j_histogram_hint); | 
|  | if (histogram) { | 
|  | CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets, | 
|  | histogram); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
|  | histogram = | 
|  | LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets, | 
|  | HistogramBase::kUmaTargetedHistogramFlag); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | HistogramBase* SparseHistogram(JNIEnv* env, | 
|  | jstring j_histogram_name, | 
|  | jlong j_histogram_hint) { | 
|  | DCHECK(j_histogram_name); | 
|  | HistogramBase* histogram = HistogramFromHint(j_histogram_hint); | 
|  | if (histogram) | 
|  | return histogram; | 
|  |  | 
|  | std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); | 
|  | histogram = SparseHistogram::FactoryGet( | 
|  | histogram_name, HistogramBase::kUmaTargetedHistogramFlag); | 
|  | return histogram; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Convert a jlong |histogram_hint| from Java to a HistogramBase* via a cast. | 
|  | // The Java side caches these in a map (see NativeUmaRecorder.java), which is | 
|  | // safe to do since C++ Histogram objects are never freed. | 
|  | static HistogramBase* HistogramFromHint(jlong j_histogram_hint) { | 
|  | return reinterpret_cast<HistogramBase*>(j_histogram_hint); | 
|  | } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(HistogramCache); | 
|  | }; | 
|  |  | 
|  | LazyInstance<HistogramCache>::Leaky g_histograms; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | jlong JNI_NativeUmaRecorder_RecordBooleanHistogram( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jstring>& j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jboolean j_sample) { | 
|  | bool sample = static_cast<bool>(j_sample); | 
|  | HistogramBase* histogram = g_histograms.Get().BooleanHistogram( | 
|  | env, j_histogram_name, j_histogram_hint); | 
|  | histogram->AddBoolean(sample); | 
|  | return reinterpret_cast<jlong>(histogram); | 
|  | } | 
|  |  | 
|  | jlong JNI_NativeUmaRecorder_RecordExponentialHistogram( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jstring>& j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jint j_sample, | 
|  | jint j_min, | 
|  | jint j_max, | 
|  | jint j_num_buckets) { | 
|  | int sample = static_cast<int>(j_sample); | 
|  | HistogramBase* histogram = g_histograms.Get().ExponentialHistogram( | 
|  | env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets); | 
|  | histogram->Add(sample); | 
|  | return reinterpret_cast<jlong>(histogram); | 
|  | } | 
|  |  | 
|  | jlong JNI_NativeUmaRecorder_RecordLinearHistogram( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jstring>& j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jint j_sample, | 
|  | jint j_min, | 
|  | jint j_max, | 
|  | jint j_num_buckets) { | 
|  | int sample = static_cast<int>(j_sample); | 
|  | HistogramBase* histogram = g_histograms.Get().LinearHistogram( | 
|  | env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets); | 
|  | histogram->Add(sample); | 
|  | return reinterpret_cast<jlong>(histogram); | 
|  | } | 
|  |  | 
|  | jlong JNI_NativeUmaRecorder_RecordSparseHistogram( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jstring>& j_histogram_name, | 
|  | jlong j_histogram_hint, | 
|  | jint j_sample) { | 
|  | int sample = static_cast<int>(j_sample); | 
|  | HistogramBase* histogram = g_histograms.Get().SparseHistogram( | 
|  | env, j_histogram_name, j_histogram_hint); | 
|  | histogram->Add(sample); | 
|  | return reinterpret_cast<jlong>(histogram); | 
|  | } | 
|  |  | 
|  | void JNI_NativeUmaRecorder_RecordUserAction( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jstring>& j_user_action_name, | 
|  | jlong j_millis_since_event) { | 
|  | // Time values coming from Java need to be synchronized with TimeTick clock. | 
|  | RecordComputedActionSince(ConvertJavaStringToUTF8(env, j_user_action_name), | 
|  | TimeDelta::FromMilliseconds(j_millis_since_event)); | 
|  | } | 
|  |  | 
|  | }  // namespace android | 
|  | }  // namespace base |