1 // Copyright 2017 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/contextualsearch/contextual_search_ranker_logger_impl.h>
6
7 #include "base/android/jni_string.h"
8 #include "base/android/scoped_java_ref.h"
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "chrome/android/chrome_jni_headers/ContextualSearchRankerLoggerImpl_jni.h"
12 #include "chrome/browser/assist_ranker/assist_ranker_service_factory.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/flags/android/chrome_feature_list.h"
15 #include "components/assist_ranker/assist_ranker_service_impl.h"
16 #include "components/assist_ranker/binary_classifier_predictor.h"
17 #include "components/assist_ranker/predictor_config_definitions.h"
18 #include "components/assist_ranker/proto/ranker_example.pb.h"
19 #include "components/keyed_service/core/keyed_service.h"
20 #include "components/ukm/content/source_url_recorder.h"
21 #include "content/public/browser/web_contents.h"
22
23 namespace content {
24 class BrowserContext;
25 }
26
27 namespace {
28
29 const char kContextualSearchRankerDidPredict[] = "OutcomeRankerDidPredict";
30 const char kContextualSearchRankerPrediction[] = "OutcomeRankerPrediction";
31 const char kContextualSearchImportantFeature[] = "DidOptIn";
32 const char kContextualSearchImportantOutcome[] = "OutcomeWasPanelOpened";
33 const char kContextualSearchRankerPredictionScore[] =
34 "OutcomeRankerPredictionScore";
35
36 } // namespace
37
ContextualSearchRankerLoggerImpl(JNIEnv * env,jobject obj)38 ContextualSearchRankerLoggerImpl::ContextualSearchRankerLoggerImpl(JNIEnv* env,
39 jobject obj)
40 : java_object_(env, obj) {}
41
~ContextualSearchRankerLoggerImpl()42 ContextualSearchRankerLoggerImpl::~ContextualSearchRankerLoggerImpl() {}
43
SetupLoggingAndRanker(JNIEnv * env,jobject obj,const base::android::JavaParamRef<jobject> & java_web_contents)44 void ContextualSearchRankerLoggerImpl::SetupLoggingAndRanker(
45 JNIEnv* env,
46 jobject obj,
47 const base::android::JavaParamRef<jobject>& java_web_contents) {
48 content::WebContents* web_contents =
49 content::WebContents::FromJavaWebContents(java_web_contents);
50 if (!web_contents)
51 return;
52
53 source_id_ = ukm::GetSourceIdForWebContentsDocument(web_contents);
54 SetupRankerPredictor(*web_contents);
55 // Start building example data based on features to be gathered and logged.
56 ranker_example_ = std::make_unique<assist_ranker::RankerExample>();
57 }
58
SetupRankerPredictor(content::WebContents & web_contents)59 void ContextualSearchRankerLoggerImpl::SetupRankerPredictor(
60 content::WebContents& web_contents) {
61 // Create one predictor for the current BrowserContext.
62 if (browser_context_) {
63 DCHECK(browser_context_ == web_contents.GetBrowserContext());
64 return;
65 }
66 browser_context_ = web_contents.GetBrowserContext();
67
68 assist_ranker::AssistRankerService* assist_ranker_service =
69 assist_ranker::AssistRankerServiceFactory::GetForBrowserContext(
70 browser_context_);
71 if (assist_ranker_service) {
72 predictor_ = assist_ranker_service->FetchBinaryClassifierPredictor(
73 assist_ranker::GetContextualSearchPredictorConfig());
74 }
75 }
76
LogFeature(const std::string & feature_name,int value)77 void ContextualSearchRankerLoggerImpl::LogFeature(
78 const std::string& feature_name,
79 int value) {
80 auto& features = *ranker_example_->mutable_features();
81 features[feature_name].set_int32_value(value);
82 }
83
LogInt32(JNIEnv * env,jobject obj,const base::android::JavaParamRef<jstring> & j_feature,jint j_int)84 void ContextualSearchRankerLoggerImpl::LogInt32(
85 JNIEnv* env,
86 jobject obj,
87 const base::android::JavaParamRef<jstring>& j_feature,
88 jint j_int) {
89 std::string feature = base::android::ConvertJavaStringToUTF8(env, j_feature);
90 LogFeature(feature, j_int);
91 }
92
RunInference(JNIEnv * env,jobject obj)93 AssistRankerPrediction ContextualSearchRankerLoggerImpl::RunInference(
94 JNIEnv* env,
95 jobject obj) {
96 has_predicted_decision_ = true;
97 bool prediction = false;
98 bool was_able_to_predict = false;
99 if (IsQueryEnabledInternal()) {
100 was_able_to_predict = predictor_->Predict(*ranker_example_, &prediction);
101 // Log to UMA whether we were able to predict or not.
102 base::UmaHistogramBoolean("Search.ContextualSearch.Ranker.WasAbleToPredict",
103 was_able_to_predict);
104 // TODO(chrome-ranker-team): this should be logged internally by Ranker.
105 LogFeature(kContextualSearchRankerDidPredict,
106 static_cast<int>(was_able_to_predict));
107 if (was_able_to_predict) {
108 LogFeature(kContextualSearchRankerPrediction,
109 static_cast<int>(prediction));
110 // For offline validation also log the prediction score.
111 // TODO(donnd): remove when https://crbug.com/914179 is resolved.
112 float score;
113 bool was_able_to_predict_score =
114 predictor_->PredictScore(*ranker_example_, &score);
115 if (was_able_to_predict_score)
116 LogFeature(kContextualSearchRankerPredictionScore, score);
117 }
118 }
119 AssistRankerPrediction prediction_enum;
120 if (was_able_to_predict) {
121 if (prediction) {
122 prediction_enum = ASSIST_RANKER_PREDICTION_SHOW;
123 } else {
124 prediction_enum = ASSIST_RANKER_PREDICTION_SUPPRESS;
125 }
126 } else {
127 prediction_enum = ASSIST_RANKER_PREDICTION_UNAVAILABLE;
128 }
129 // TODO(donnd): remove when behind-the-flag bug fixed (crbug.com/786589).
130 DVLOG(0) << "prediction: " << prediction_enum;
131 return prediction_enum;
132 }
133
WriteLogAndReset(JNIEnv * env,jobject obj)134 void ContextualSearchRankerLoggerImpl::WriteLogAndReset(JNIEnv* env,
135 jobject obj) {
136 if (predictor_ && ranker_example_ && source_id_ != ukm::kInvalidSourceId) {
137 predictor_->LogExampleToUkm(*ranker_example_.get(), source_id_);
138 source_id_ = ukm::kInvalidSourceId;
139 logImportantFeaturePresent(kContextualSearchImportantFeature, false);
140 logImportantFeaturePresent(kContextualSearchImportantOutcome, true);
141 }
142 has_predicted_decision_ = false;
143 ranker_example_ = std::make_unique<assist_ranker::RankerExample>();
144 }
145
logImportantFeaturePresent(const std::string & feature,bool is_outcome) const146 void ContextualSearchRankerLoggerImpl::logImportantFeaturePresent(
147 const std::string& feature,
148 bool is_outcome) const {
149 if (ranker_example_->features().count(feature) > 0) {
150 DCHECK(ranker_example_->features().count(feature) < 2);
151 base::UmaHistogramBoolean("Search.ContextualSearch.Ranker.RecordedNative",
152 is_outcome);
153 }
154 }
155
IsQueryEnabled(JNIEnv * env,jobject obj)156 bool ContextualSearchRankerLoggerImpl::IsQueryEnabled(JNIEnv* env,
157 jobject obj) {
158 return IsQueryEnabledInternal();
159 }
160
IsQueryEnabledInternal()161 bool ContextualSearchRankerLoggerImpl::IsQueryEnabledInternal() {
162 return predictor_ && predictor_->is_query_enabled();
163 }
164
165 // Java wrapper boilerplate
166
Destroy(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj)167 void ContextualSearchRankerLoggerImpl::Destroy(
168 JNIEnv* env,
169 const base::android::JavaParamRef<jobject>& obj) {
170 delete this;
171 }
172
JNI_ContextualSearchRankerLoggerImpl_Init(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj)173 jlong JNI_ContextualSearchRankerLoggerImpl_Init(
174 JNIEnv* env,
175 const base::android::JavaParamRef<jobject>& obj) {
176 ContextualSearchRankerLoggerImpl* ranker_logger_impl =
177 new ContextualSearchRankerLoggerImpl(env, obj);
178 return reinterpret_cast<intptr_t>(ranker_logger_impl);
179 }
180