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