blob: bbbe7a25edaa0d77d5ded426b4e86c497a241182 [file] [log] [blame]
Stephane Zermatten40481b802019-01-09 18:50:521// Copyright 2018 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/autofill_assistant/client_android.h"
6
Stephane Zermatten33b24102019-02-08 15:53:037#include <utility>
8#include <vector>
9
Stephane Zermatten40481b802019-01-09 18:50:5210#include "base/android/jni_android.h"
11#include "base/android/jni_array.h"
12#include "base/android/jni_string.h"
13#include "base/android/locale_utils.h"
Sebastien Marchandf1349f52019-01-25 03:16:4114#include "base/bind.h"
Stephane Zermatten40481b802019-01-09 18:50:5215#include "base/command_line.h"
16#include "base/metrics/field_trial_params.h"
Stephane Zermatten3471b362019-02-08 15:53:0317#include "base/no_destructor.h"
Stephane Zermatten40481b802019-01-09 18:50:5218#include "base/task/post_task.h"
Stephane Zermatten5d2e3b42019-03-05 10:33:3019#include "base/time/default_tick_clock.h"
Andrew Grieve4a42c22e2019-06-24 16:14:2920#include "chrome/android/features/autofill_assistant/jni_headers/AutofillAssistantClient_jni.h"
Stephane Zermatten40481b802019-01-09 18:50:5221#include "chrome/browser/android/chrome_feature_list.h"
22#include "chrome/browser/autofill/android/personal_data_manager_android.h"
23#include "chrome/browser/autofill/personal_data_manager_factory.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/profiles/profile_manager.h"
26#include "chrome/browser/signin/identity_manager_factory.h"
27#include "chrome/common/channel_info.h"
28#include "components/autofill_assistant/browser/access_token_fetcher.h"
29#include "components/autofill_assistant/browser/controller.h"
Hui(Andy) Wufbebf91d22019-02-01 01:23:5430#include "components/autofill_assistant/browser/features.h"
Stephane Zermatten40481b802019-01-09 18:50:5231#include "components/signin/core/browser/account_info.h"
Henrique Ferreiro94eb46f2019-07-03 14:38:5632#include "components/signin/public/identity_manager/identity_manager.h"
Stephane Zermatten40481b802019-01-09 18:50:5233#include "components/version_info/channel.h"
34#include "content/public/browser/browser_task_traits.h"
35#include "content/public/browser/browser_thread.h"
36#include "content/public/browser/web_contents.h"
37#include "google_apis/google_api_keys.h"
Stephane Zermatten40481b802019-01-09 18:50:5238#include "url/gurl.h"
39
40using base::android::AttachCurrentThread;
41using base::android::JavaParamRef;
42using base::android::JavaRef;
43
44namespace autofill_assistant {
45namespace switches {
46const char* const kAutofillAssistantServerKey = "autofill-assistant-key";
Clemens Arbesserd01d02e2019-03-01 09:18:1347const char* const kAutofillAssistantUrl = "autofill-assistant-url";
Stephane Zermatten40481b802019-01-09 18:50:5248} // namespace switches
49
50namespace {
51
Clemens Arbesserd01d02e2019-03-01 09:18:1352const char* const kDefaultAutofillAssistantServerUrl =
53 "https://automate-pa.googleapis.com";
Stephane Zermatten40481b802019-01-09 18:50:5254
Stephane Zermatten40481b802019-01-09 18:50:5255// Fills a map from two Java arrays of strings of the same length.
56void FillParametersFromJava(JNIEnv* env,
57 const JavaRef<jobjectArray>& names,
58 const JavaRef<jobjectArray>& values,
59 std::map<std::string, std::string>* parameters) {
60 std::vector<std::string> names_vector;
61 base::android::AppendJavaStringArrayToStringVector(env, names, &names_vector);
62 std::vector<std::string> values_vector;
63 base::android::AppendJavaStringArrayToStringVector(env, values,
64 &values_vector);
65 DCHECK_EQ(names_vector.size(), values_vector.size());
66 for (size_t i = 0; i < names_vector.size(); ++i) {
67 parameters->insert(std::make_pair(names_vector[i], values_vector[i]));
68 }
69}
70
71} // namespace
72
73static base::android::ScopedJavaLocalRef<jobject>
74JNI_AutofillAssistantClient_FromWebContents(
75 JNIEnv* env,
76 const base::android::JavaParamRef<jobject>& jweb_contents) {
77 auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
78 ClientAndroid::CreateForWebContents(web_contents);
79 return ClientAndroid::FromWebContents(web_contents)->GetJavaObject();
80}
81
82ClientAndroid::ClientAndroid(content::WebContents* web_contents)
83 : web_contents_(web_contents),
Stephane Zermatten40481b802019-01-09 18:50:5284 java_object_(Java_AutofillAssistantClient_create(
85 AttachCurrentThread(),
86 reinterpret_cast<intptr_t>(this))),
Clemens Arbesserd01d02e2019-03-01 09:18:1387 weak_ptr_factory_(this) {
88 server_url_ = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
89 switches::kAutofillAssistantUrl);
90 if (server_url_.empty()) {
91 server_url_ = kDefaultAutofillAssistantServerUrl;
92 }
93}
Stephane Zermatten40481b802019-01-09 18:50:5294
95ClientAndroid::~ClientAndroid() {
Mathias Carlen217b6272019-01-28 15:04:1896 if (controller_ != nullptr) {
97 // In the case of an unexpected closing of the activity or tab, controller_
98 // will not yet have been cleaned up (since that happens when a web
99 // contents object gets destroyed).
100 Metrics::RecordDropOut(Metrics::CONTENT_DESTROYED);
101 }
Stephane Zermatten40481b802019-01-09 18:50:52102 Java_AutofillAssistantClient_clearNativePtr(AttachCurrentThread(),
103 java_object_);
104}
105
Stephane Zermatten40481b802019-01-09 18:50:52106base::android::ScopedJavaLocalRef<jobject> ClientAndroid::GetJavaObject() {
107 return base::android::ScopedJavaLocalRef<jobject>(java_object_);
108}
109
Jordan Demeulenaere825b6ba2019-01-17 11:24:34110void ClientAndroid::Start(JNIEnv* env,
111 const JavaParamRef<jobject>& jcaller,
112 const JavaParamRef<jstring>& jinitial_url,
Mathias Carlencbc1f2d2019-04-08 10:31:31113 const JavaParamRef<jstring>& jexperiment_ids,
Jordan Demeulenaere825b6ba2019-01-17 11:24:34114 const JavaParamRef<jobjectArray>& parameterNames,
Jordan Demeulenaeredf068bfa2019-06-20 08:16:03115 const JavaParamRef<jobjectArray>& parameterValues,
116 const JavaParamRef<jobject>& joverlay_coordinator) {
Stephane Zermatten40481b802019-01-09 18:50:52117 CreateController();
Jordan Demeulenaeredf068bfa2019-06-20 08:16:03118
119 // If an overlay is already shown, then show the rest of the UI.
120 if (joverlay_coordinator) {
121 CreateUI(joverlay_coordinator);
122 }
123
Stephane Zermatten40481b802019-01-09 18:50:52124 GURL initial_url(base::android::ConvertJavaStringToUTF8(env, jinitial_url));
125 std::map<std::string, std::string> parameters;
126 FillParametersFromJava(env, parameterNames, parameterValues, &parameters);
Stephane Zermatten16cb7c8c2019-07-08 13:09:28127 controller_->Start(initial_url, TriggerContext::Create(
Mathias Carlencbc1f2d2019-04-08 10:31:31128 std::move(parameters),
129 base::android::ConvertJavaStringToUTF8(
130 env, jexperiment_ids)));
Stephane Zermatten40481b802019-01-09 18:50:52131}
132
Stephane Zermattend1442d942019-02-22 13:54:47133void ClientAndroid::DestroyUI(
134 JNIEnv* env,
135 const base::android::JavaParamRef<jobject>& jcaller) {
136 DestroyUI();
137}
138
139void ClientAndroid::TransferUITo(
140 JNIEnv* env,
141 const base::android::JavaParamRef<jobject>& jcaller,
142 const base::android::JavaParamRef<jobject>& jother_web_contents) {
143 if (!ui_controller_android_)
144 return;
145
146 auto ui_ptr = std::move(ui_controller_android_);
147 // From this point on, the UIController, in ui_ptr, is either transferred or
148 // deleted.
149
150 if (!jother_web_contents)
151 return;
152
153 auto* other_web_contents =
154 content::WebContents::FromJavaWebContents(jother_web_contents);
155 DCHECK_NE(other_web_contents, web_contents_);
156
157 ClientAndroid* other_client =
158 ClientAndroid::FromWebContents(other_web_contents);
159 if (!other_client || !other_client->NeedsUI())
160 return;
161
162 other_client->SetUI(std::move(ui_ptr));
163}
164
Stephane Zermatten40481b802019-01-09 18:50:52165base::android::ScopedJavaLocalRef<jstring> ClientAndroid::GetPrimaryAccountName(
166 JNIEnv* env,
167 const JavaParamRef<jobject>& jcaller) {
Sylvain Defresne6bd9b1c42019-02-13 16:56:01168 CoreAccountInfo account_info =
Stephane Zermatten40481b802019-01-09 18:50:52169 IdentityManagerFactory::GetForProfile(
170 Profile::FromBrowserContext(web_contents_->GetBrowserContext()))
171 ->GetPrimaryAccountInfo();
172 return base::android::ConvertUTF8ToJavaString(env, account_info.email);
173}
174
175void ClientAndroid::OnAccessToken(JNIEnv* env,
176 const JavaParamRef<jobject>& jcaller,
177 jboolean success,
178 const JavaParamRef<jstring>& access_token) {
179 if (fetch_access_token_callback_) {
180 std::move(fetch_access_token_callback_)
181 .Run(success, base::android::ConvertJavaStringToUTF8(access_token));
182 }
183}
184
Stephane Zermatten3471b362019-02-08 15:53:03185void ClientAndroid::ShowUI() {
186 if (!controller_) {
187 CreateController();
188 // TODO(crbug.com/806868): allow delaying controller creation, for
189 // onboarding.
190 }
191 if (!ui_controller_android_) {
Jordan Demeulenaeredf068bfa2019-06-20 08:16:03192 CreateUI(nullptr);
Stephane Zermatten3471b362019-02-08 15:53:03193 }
194}
195
Jordan Demeulenaeredf068bfa2019-06-20 08:16:03196void ClientAndroid::CreateUI(
197 const JavaParamRef<jobject>& joverlay_coordinator) {
198 std::unique_ptr<UiControllerAndroid> ui_ptr =
199 UiControllerAndroid::CreateFromWebContents(web_contents_,
200 joverlay_coordinator);
201 if (ui_ptr)
202 SetUI(std::move(ui_ptr));
203}
204
Stephane Zermatten3471b362019-02-08 15:53:03205void ClientAndroid::DestroyUI() {
Stephane Zermatten3471b362019-02-08 15:53:03206 ui_controller_android_.reset();
207}
208
Stephane Zermatten40481b802019-01-09 18:50:52209std::string ClientAndroid::GetApiKey() {
210 std::string api_key;
211 if (google_apis::IsGoogleChromeAPIKeyUsed()) {
212 api_key = chrome::GetChannel() == version_info::Channel::STABLE
213 ? google_apis::GetAPIKey()
214 : google_apis::GetNonStableAPIKey();
215 }
216 const auto* command_line = base::CommandLine::ForCurrentProcess();
217 if (command_line->HasSwitch(switches::kAutofillAssistantServerKey)) {
218 api_key = command_line->GetSwitchValueASCII(
219 switches::kAutofillAssistantServerKey);
220 }
221 return api_key;
222}
223
224std::string ClientAndroid::GetAccountEmailAddress() {
225 JNIEnv* env = AttachCurrentThread();
226 return base::android::ConvertJavaStringToUTF8(
227 Java_AutofillAssistantClient_getAccountEmailAddress(env, java_object_));
228}
229
230AccessTokenFetcher* ClientAndroid::GetAccessTokenFetcher() {
231 return this;
232}
233
234autofill::PersonalDataManager* ClientAndroid::GetPersonalDataManager() {
235 return autofill::PersonalDataManagerFactory::GetForProfile(
236 ProfileManager::GetLastUsedProfile());
237}
238
239std::string ClientAndroid::GetServerUrl() {
Clemens Arbesserd01d02e2019-03-01 09:18:13240 return server_url_;
Stephane Zermatten40481b802019-01-09 18:50:52241}
242
Stephane Zermatten3471b362019-02-08 15:53:03243UiController* ClientAndroid::GetUiController() {
244 if (ui_controller_android_)
245 return ui_controller_android_.get();
Jordan Demeulenaere825b6ba2019-01-17 11:24:34246
Stephane Zermatten3471b362019-02-08 15:53:03247 static base::NoDestructor<UiController> noop_controller_;
248 return noop_controller_.get();
Stephane Zermatten40481b802019-01-09 18:50:52249}
250
251std::string ClientAndroid::GetLocale() {
252 return base::android::GetDefaultLocaleString();
253}
254
255std::string ClientAndroid::GetCountryCode() {
256 return base::android::ConvertJavaStringToUTF8(
257 Java_AutofillAssistantClient_getCountryCode(AttachCurrentThread(),
258 java_object_));
259}
260
Stephane Zermatten33b24102019-02-08 15:53:03261void ClientAndroid::Shutdown(Metrics::DropOutReason reason) {
Stephane Zermatten40481b802019-01-09 18:50:52262 if (!controller_)
263 return;
264
Stephane Zermatten154c24a2019-04-17 16:08:00265 // Lets the controller and the ui controller know shutdown is about to happen.
266 // TODO(b/128300038): Replace Controller::WillShutdown with a Detach call on
267 // ui_controller_android_.
268 controller_->WillShutdown(reason);
Stephane Zermatten33b24102019-02-08 15:53:03269
270 Metrics::RecordDropOut(reason);
Stephane Zermatten14482fc2019-02-26 16:50:15271
272 // Delete the controller in a separate task. This avoids tricky ordering
273 // issues when Shutdown is called from the controller.
274 base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
275 base::BindOnce(&ClientAndroid::DestroyController,
276 weak_ptr_factory_.GetWeakPtr()));
Stephane Zermatten40481b802019-01-09 18:50:52277}
278
279void ClientAndroid::FetchAccessToken(
280 base::OnceCallback<void(bool, const std::string&)> callback) {
281 DCHECK(!fetch_access_token_callback_);
282
283 fetch_access_token_callback_ = std::move(callback);
284 JNIEnv* env = AttachCurrentThread();
285 Java_AutofillAssistantClient_fetchAccessToken(env, java_object_);
286}
287
288void ClientAndroid::InvalidateAccessToken(const std::string& access_token) {
289 JNIEnv* env = AttachCurrentThread();
290 Java_AutofillAssistantClient_invalidateAccessToken(
291 env, java_object_,
292 base::android::ConvertUTF8ToJavaString(env, access_token));
293}
294
295void ClientAndroid::CreateController() {
296 if (controller_) {
297 return;
298 }
Stephane Zermatten5d2e3b42019-03-05 10:33:30299 controller_ = std::make_unique<Controller>(
300 web_contents_, /* client= */ this, base::DefaultTickClock::GetInstance());
Stephane Zermatten40481b802019-01-09 18:50:52301}
302
Stephane Zermatten14482fc2019-02-26 16:50:15303void ClientAndroid::DestroyController() {
304 controller_.reset();
305}
306
Stephane Zermattend1442d942019-02-22 13:54:47307bool ClientAndroid::NeedsUI() {
308 return !ui_controller_android_ && controller_ && controller_->NeedsUI();
309}
310
311void ClientAndroid::SetUI(
312 std::unique_ptr<UiControllerAndroid> ui_controller_android) {
313 ui_controller_android_ = std::move(ui_controller_android);
314 ui_controller_android_->Attach(web_contents_, this, controller_.get());
315}
316
Nico Weber6fcfd572019-02-23 03:28:03317WEB_CONTENTS_USER_DATA_KEY_IMPL(ClientAndroid)
Stephane Zermatten40481b802019-01-09 18:50:52318
319} // namespace autofill_assistant.