// Copyright 2018 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 "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/values.h" #include "chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h" #include "chrome/browser/chromeos/android_sms/android_sms_urls.h" #include "chrome/browser/chromeos/login/quick_unlock/auth_token.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h" #include "chromeos/components/multidevice/logging/logging.h" #include "chromeos/components/proximity_auth/proximity_auth_pref_names.h" #include "chromeos/services/multidevice_setup/public/cpp/prefs.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_message_handler.h" namespace chromeos { namespace settings { namespace { const char kPageContentDataModeKey[] = "mode"; const char kPageContentDataHostDeviceNameKey[] = "hostDeviceName"; const char kPageContentDataBetterTogetherStateKey[] = "betterTogetherState"; const char kPageContentDataInstantTetheringStateKey[] = "instantTetheringState"; const char kPageContentDataMessagesStateKey[] = "messagesState"; const char kPageContentDataSmartLockStateKey[] = "smartLockState"; const char kIsAndroidSmsPairingComplete[] = "isAndroidSmsPairingComplete"; constexpr char kAndroidSmsInfoOriginKey[] = "origin"; constexpr char kAndroidSmsInfoEnabledKey[] = "enabled"; void OnRetrySetHostNowResult(bool success) { if (success) return; PA_LOG(WARNING) << "OnRetrySetHostNowResult(): Attempt to retry setting the " << "host device failed."; } } // namespace MultideviceHandler::MultideviceHandler( PrefService* prefs, multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client, multidevice_setup::AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker, android_sms::AndroidSmsAppManager* android_sms_app_manager) : prefs_(prefs), multidevice_setup_client_(multidevice_setup_client), android_sms_pairing_state_tracker_(android_sms_pairing_state_tracker), android_sms_app_manager_(android_sms_app_manager), multidevice_setup_observer_(this), android_sms_pairing_state_tracker_observer_(this), android_sms_app_manager_observer_(this) { pref_change_registrar_.Init(prefs_); } MultideviceHandler::~MultideviceHandler() {} void MultideviceHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( "showMultiDeviceSetupDialog", base::BindRepeating(&MultideviceHandler::HandleShowMultiDeviceSetupDialog, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getPageContentData", base::BindRepeating(&MultideviceHandler::HandleGetPageContent, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setFeatureEnabledState", base::BindRepeating(&MultideviceHandler::HandleSetFeatureEnabledState, base::Unretained(this))); web_ui()->RegisterMessageCallback( "removeHostDevice", base::BindRepeating(&MultideviceHandler::HandleRemoveHostDevice, base::Unretained(this))); web_ui()->RegisterMessageCallback( "retryPendingHostSetup", base::BindRepeating(&MultideviceHandler::HandleRetryPendingHostSetup, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setUpAndroidSms", base::BindRepeating(&MultideviceHandler::HandleSetUpAndroidSms, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getSmartLockSignInEnabled", base::BindRepeating(&MultideviceHandler::HandleGetSmartLockSignInEnabled, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setSmartLockSignInEnabled", base::BindRepeating(&MultideviceHandler::HandleSetSmartLockSignInEnabled, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getSmartLockSignInAllowed", base::BindRepeating(&MultideviceHandler::HandleGetSmartLockSignInAllowed, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getAndroidSmsInfo", base::BindRepeating(&MultideviceHandler::HandleGetAndroidSmsInfo, base::Unretained(this))); } void MultideviceHandler::OnJavascriptAllowed() { if (multidevice_setup_client_) multidevice_setup_observer_.Add(multidevice_setup_client_); if (android_sms_pairing_state_tracker_) { android_sms_pairing_state_tracker_observer_.Add( android_sms_pairing_state_tracker_); } if (android_sms_app_manager_) android_sms_app_manager_observer_.Add(android_sms_app_manager_); pref_change_registrar_.Add( proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, base::BindRepeating( &MultideviceHandler::NotifySmartLockSignInEnabledChanged, base::Unretained(this))); pref_change_registrar_.Add( multidevice_setup::kSmartLockSigninAllowedPrefName, base::BindRepeating( &MultideviceHandler::NotifySmartLockSignInAllowedChanged, base::Unretained(this))); } void MultideviceHandler::OnJavascriptDisallowed() { pref_change_registrar_.RemoveAll(); if (multidevice_setup_client_) multidevice_setup_observer_.Remove(multidevice_setup_client_); if (android_sms_pairing_state_tracker_) { android_sms_pairing_state_tracker_observer_.Remove( android_sms_pairing_state_tracker_); } if (android_sms_app_manager_) android_sms_app_manager_observer_.Remove(android_sms_app_manager_); // Ensure that pending callbacks do not complete and cause JS to be evaluated. callback_weak_ptr_factory_.InvalidateWeakPtrs(); } void MultideviceHandler::OnHostStatusChanged( const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice& host_status_with_device) { UpdatePageContent(); NotifyAndroidSmsInfoChange(); } void MultideviceHandler::OnFeatureStatesChanged( const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap& feature_states_map) { UpdatePageContent(); NotifyAndroidSmsInfoChange(); } void MultideviceHandler::OnPairingStateChanged() { UpdatePageContent(); NotifyAndroidSmsInfoChange(); } void MultideviceHandler::OnInstalledAppUrlChanged() { UpdatePageContent(); NotifyAndroidSmsInfoChange(); } void MultideviceHandler::UpdatePageContent() { std::unique_ptr page_content_dictionary = GeneratePageContentDataDictionary(); DCHECK(page_content_dictionary); PA_LOG(VERBOSE) << "Updating MultiDevice settings page content with: " << *page_content_dictionary << "."; FireWebUIListener("settings.updateMultidevicePageContentData", *page_content_dictionary); } void MultideviceHandler::NotifyAndroidSmsInfoChange() { auto android_sms_info = GenerateAndroidSmsInfo(); FireWebUIListener("settings.onAndroidSmsInfoChange", *android_sms_info); } void MultideviceHandler::HandleShowMultiDeviceSetupDialog( const base::ListValue* args) { DCHECK(args->empty()); multidevice_setup::MultiDeviceSetupDialog::Show(); } void MultideviceHandler::HandleGetPageContent(const base::ListValue* args) { // This callback is expected to be the first one executed when the page is // loaded, so it should be the one to allow JS calls. AllowJavascript(); std::string callback_id; bool result = args->GetString(0, &callback_id); DCHECK(result); std::unique_ptr page_content_dictionary = GeneratePageContentDataDictionary(); DCHECK(page_content_dictionary); PA_LOG(VERBOSE) << "Responding to getPageContentData() request with: " << *page_content_dictionary << "."; ResolveJavascriptCallback(base::Value(callback_id), *page_content_dictionary); } void MultideviceHandler::HandleSetFeatureEnabledState( const base::ListValue* args) { std::string callback_id; bool result = args->GetString(0, &callback_id); DCHECK(result); int feature_as_int; result = args->GetInteger(1, &feature_as_int); DCHECK(result); auto feature = static_cast(feature_as_int); DCHECK(multidevice_setup::mojom::IsKnownEnumValue(feature)); bool enabled; result = args->GetBoolean(2, &enabled); DCHECK(result); base::Optional auth_token; std::string possible_token_value; if (args->GetString(3, &possible_token_value)) auth_token = possible_token_value; multidevice_setup_client_->SetFeatureEnabledState( feature, enabled, auth_token, base::BindOnce(&MultideviceHandler::OnSetFeatureStateEnabledResult, callback_weak_ptr_factory_.GetWeakPtr(), callback_id)); } void MultideviceHandler::HandleRemoveHostDevice(const base::ListValue* args) { DCHECK(args->empty()); multidevice_setup_client_->RemoveHostDevice(); } void MultideviceHandler::HandleRetryPendingHostSetup( const base::ListValue* args) { DCHECK(args->empty()); multidevice_setup_client_->RetrySetHostNow( base::BindOnce(&OnRetrySetHostNowResult)); } void MultideviceHandler::HandleSetUpAndroidSms(const base::ListValue* args) { DCHECK(args->empty()); android_sms_app_manager_->SetUpAndLaunchAndroidSmsApp(); } void MultideviceHandler::HandleGetSmartLockSignInEnabled( const base::ListValue* args) { std::string callback_id; CHECK(args->GetString(0, &callback_id)); bool signInEnabled = prefs_->GetBoolean( proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled); ResolveJavascriptCallback(base::Value(callback_id), base::Value(signInEnabled)); } void MultideviceHandler::HandleSetSmartLockSignInEnabled( const base::ListValue* args) { bool enabled = false; CHECK(args->GetBoolean(0, &enabled)); std::string auth_token; bool auth_token_present = args->GetString(1, &auth_token); // Either the user is disabling sign-in, or they are enabling it and the auth // token must be present. CHECK(!enabled || auth_token_present); // Only check auth token if the user is attempting to enable sign-in. if (enabled && !IsAuthTokenValid(auth_token)) return; prefs_->SetBoolean( proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, enabled); } void MultideviceHandler::HandleGetSmartLockSignInAllowed( const base::ListValue* args) { std::string callback_id; CHECK(args->GetString(0, &callback_id)); bool sign_in_allowed = prefs_->GetBoolean(multidevice_setup::kSmartLockSigninAllowedPrefName); ResolveJavascriptCallback(base::Value(callback_id), base::Value(sign_in_allowed)); } std::unique_ptr MultideviceHandler::GenerateAndroidSmsInfo() { base::Optional app_url; if (android_sms_app_manager_) app_url = android_sms_app_manager_->GetCurrentAppUrl(); if (!app_url) app_url = android_sms::GetAndroidMessagesURL(); auto android_sms_info = std::make_unique(); android_sms_info->SetString( kAndroidSmsInfoOriginKey, ContentSettingsPattern::FromURLNoWildcard(*app_url).ToString()); chromeos::multidevice_setup::mojom::FeatureState messages_state = multidevice_setup_client_->GetFeatureState( chromeos::multidevice_setup::mojom::Feature::kMessages); bool enabled_state = messages_state == chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser || messages_state == chromeos::multidevice_setup::mojom::FeatureState:: kFurtherSetupRequired; android_sms_info->SetBoolean(kAndroidSmsInfoEnabledKey, enabled_state); return android_sms_info; } void MultideviceHandler::HandleGetAndroidSmsInfo(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(1U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); ResolveJavascriptCallback(*callback_id, *GenerateAndroidSmsInfo()); } void MultideviceHandler::OnSetFeatureStateEnabledResult( const std::string& js_callback_id, bool success) { ResolveJavascriptCallback(base::Value(js_callback_id), base::Value(success)); } std::unique_ptr MultideviceHandler::GeneratePageContentDataDictionary() { auto page_content_dictionary = std::make_unique(); multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice host_status_with_device = GetHostStatusWithDevice(); multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap feature_states = GetFeatureStatesMap(); page_content_dictionary->SetInteger( kPageContentDataModeKey, static_cast(host_status_with_device.first)); page_content_dictionary->SetInteger( kPageContentDataBetterTogetherStateKey, static_cast( feature_states [multidevice_setup::mojom::Feature::kBetterTogetherSuite])); page_content_dictionary->SetInteger( kPageContentDataInstantTetheringStateKey, static_cast( feature_states [multidevice_setup::mojom::Feature::kInstantTethering])); page_content_dictionary->SetInteger( kPageContentDataMessagesStateKey, static_cast( feature_states[multidevice_setup::mojom::Feature::kMessages])); page_content_dictionary->SetInteger( kPageContentDataSmartLockStateKey, static_cast( feature_states[multidevice_setup::mojom::Feature::kSmartLock])); if (host_status_with_device.second) { page_content_dictionary->SetString(kPageContentDataHostDeviceNameKey, host_status_with_device.second->name()); } page_content_dictionary->SetBoolean( kIsAndroidSmsPairingComplete, android_sms_pairing_state_tracker_ ? android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete() : false); return page_content_dictionary; } void MultideviceHandler::NotifySmartLockSignInEnabledChanged() { bool sign_in_enabled = prefs_->GetBoolean( proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled); FireWebUIListener("smart-lock-signin-enabled-changed", base::Value(sign_in_enabled)); } void MultideviceHandler::NotifySmartLockSignInAllowedChanged() { bool sign_in_allowed = prefs_->GetBoolean(multidevice_setup::kSmartLockSigninAllowedPrefName); FireWebUIListener("smart-lock-signin-allowed-changed", base::Value(sign_in_allowed)); } bool MultideviceHandler::IsAuthTokenValid(const std::string& auth_token) { Profile* profile = Profile::FromWebUI(web_ui()); quick_unlock::QuickUnlockStorage* quick_unlock_storage = chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(profile); return quick_unlock_storage->GetAuthToken() && auth_token == quick_unlock_storage->GetAuthToken()->Identifier(); } multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice MultideviceHandler::GetHostStatusWithDevice() { if (multidevice_setup_client_) return multidevice_setup_client_->GetHostStatus(); return multidevice_setup::MultiDeviceSetupClient:: GenerateDefaultHostStatusWithDevice(); } multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap MultideviceHandler::GetFeatureStatesMap() { if (multidevice_setup_client_) return multidevice_setup_client_->GetFeatureStates(); return multidevice_setup::MultiDeviceSetupClient:: GenerateDefaultFeatureStatesMap(); } } // namespace settings } // namespace chromeos