Commit e4effb80 authored by Xiaohui Chen's avatar Xiaohui Chen Committed by Commit Bot

[assistant] Disable entry points when policy disables ARC++

Refactored assistant availability check to a central location.
Also added a notification when policy is disabled.

BUG=b:69167025
TEST=run with test domain account, see entry points disabled

Change-Id: I18a3b47c4ffa8ce8cb3d18eb30315cbfec83f16d
Reviewed-on: https://chromium-review.googlesource.com/776117
Commit-Queue: Xiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517556}
parent 12e2eb98
......@@ -4,6 +4,7 @@
#include "ash/accelerators/accelerator_controller.h"
#include <string>
#include <utility>
#include "ash/accelerators/accelerator_commands.h"
......@@ -42,6 +43,7 @@
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/web_notification/web_notification_tray.h"
#include "ash/utility/screenshot_controller.h"
#include "ash/voice_interaction/voice_interaction_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/window_selector_controller.h"
#include "ash/wm/screen_pinning_controller.h"
......@@ -92,6 +94,7 @@ using message_center::SystemNotificationWarningLevel;
// Toast id and duration for voice interaction shortcuts
const char kSecondaryUserToastId[] = "voice_interaction_secondary_user";
const char kUnsupportedLocaleToastId[] = "voice_interaction_locale_unsupported";
const char kPolicyDisabledToastId[] = "voice_interaction_policy_disabled";
const int kToastDurationMs = 2500;
// The notification delegate that will be used to open the keyboard shortcut
......@@ -174,6 +177,11 @@ void ShowDeprecatedAcceleratorNotification(const char* const notification_id,
std::move(notification));
}
void ShowToast(std::string id, const base::string16& text) {
ToastData toast(id, text, kToastDurationMs, base::nullopt);
Shell::Get()->toast_manager()->Show(toast);
}
ui::Accelerator CreateAccelerator(ui::KeyboardCode keycode,
int modifiers,
bool trigger_on_press) {
......@@ -675,27 +683,36 @@ void HandleToggleVoiceInteraction(const ui::Accelerator& accelerator) {
base::UserMetricsAction("VoiceInteraction.Started.Assistant"));
}
// Show a toast if the active user is not primary.
if (Shell::Get()->session_controller()->GetPrimaryUserSession() !=
Shell::Get()->session_controller()->GetUserSession(0)) {
ash::ToastData toast(
kSecondaryUserToastId,
l10n_util::GetStringUTF16(
IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE),
kToastDurationMs, base::Optional<base::string16>());
ash::Shell::Get()->toast_manager()->Show(toast);
return;
}
// Show a toast if voice interaction is disabled due to unsupported locales.
if (!chromeos::switches::IsVoiceInteractionLocalesSupported()) {
ash::ToastData toast(
kUnsupportedLocaleToastId,
l10n_util::GetStringUTF16(
IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE),
kToastDurationMs, base::Optional<base::string16>());
ash::Shell::Get()->toast_manager()->Show(toast);
return;
switch (Shell::Get()->voice_interaction_controller()->allowed_state()) {
case mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER:
// Show a toast if the active user is not primary.
ShowToast(kSecondaryUserToastId,
l10n_util::GetStringUTF16(
IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE));
return;
case mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE:
// Show a toast if voice interaction is disabled due to unsupported
// locales.
ShowToast(
kUnsupportedLocaleToastId,
l10n_util::GetStringUTF16(
IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE));
return;
case mojom::AssistantAllowedState::DISALLOWED_BY_ARC_POLICY:
// Show a toast if voice interaction is disabled due to enterprise policy.
ShowToast(kPolicyDisabledToastId,
l10n_util::GetStringUTF16(
IDS_ASH_VOICE_INTERACTION_DISABLED_BY_POLICY_MESSAGE));
return;
case mojom::AssistantAllowedState::DISALLOWED_BY_ARC_DISALLOWED:
case mojom::AssistantAllowedState::DISALLOWED_BY_FLAG:
case mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER:
case mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO:
// TODO(xiaohuic): show a specific toast.
return;
case mojom::AssistantAllowedState::ALLOWED:
// Nothing need to do if allowed.
break;
}
Shell::Get()->app_list()->ToggleVoiceInteractionSession();
......
......@@ -476,7 +476,10 @@ This file contains the strings for ash.
The Google Assistant doesn’t speak this language.
</message>
<message name="IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the active user profile is not the primary user profile.">
Assistant is only available for primary profile.
The Google Assistant is only available for primary profile.
</message>
<message name="IDS_ASH_VOICE_INTERACTION_DISABLED_BY_POLICY_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the feature is disabled by enterprise policy.">
The Google Assistant is disabled by your administrator.
</message>
<message name="IDS_ASH_TOAST_DISMISS_BUTTON" desc="The text button shown in toasts to close the toast immediately without waiting timeout.">
......
......@@ -22,6 +22,27 @@ enum VoiceInteractionState {
RUNNING
};
enum AssistantAllowedState {
// Assistant feature is allowed.
ALLOWED = 0,
// Disallowed because ARC++ is disallowed. There could be many specific
// reasones why ARC++ is disallowed. This enum is a catch all. Some enums
// below will show specific reasons.
DISALLOWED_BY_ARC_DISALLOWED,
// Disallowed because ARC++ is disabled by policy.
DISALLOWED_BY_ARC_POLICY,
// Disallowed because user's locale is not compatible.
DISALLOWED_BY_LOCALE,
// Disallowed because the feature flag is off.
DISALLOWED_BY_FLAG,
// Disallowed because current user is not primary user.
DISALLOWED_BY_NONPRIMARY_USER,
// Disallowed because current user is supervised user.
DISALLOWED_BY_SUPERVISED_USER,
// Disallowed because incognito mode.
DISALLOWED_BY_INCOGNITO
};
// Interface for ash client (Chrome) to connect to the voice interaction
// controller, which notifies changes of voice interaction related flags.
interface VoiceInteractionController {
......@@ -38,4 +59,8 @@ interface VoiceInteractionController {
// Called when the voice interaction setup complete status is changed.
NotifySetupCompleted(bool completed);
// Notify if voice interaction feature is allowed or not. e.g. not allowed
// if disabled by policy.
NotifyFeatureAllowed(AssistantAllowedState state);
};
......@@ -4,6 +4,7 @@
#include "ash/shelf/app_list_button.h"
#include <algorithm>
#include <memory>
#include <utility>
......@@ -597,9 +598,9 @@ bool AppListButton::UseVoiceInteractionStyle() {
Shell::Get()->voice_interaction_controller();
bool settings_enabled = controller->settings_enabled();
bool setup_completed = controller->setup_completed();
if (voice_interaction_overlay_ &&
chromeos::switches::IsVoiceInteractionEnabled() &&
Shell::Get()->session_controller()->IsUserPrimary() &&
bool is_feature_allowed =
controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED;
if (voice_interaction_overlay_ && is_feature_allowed &&
(settings_enabled || !setup_completed)) {
return true;
}
......
......@@ -5,6 +5,7 @@
#include "ash/shelf/app_list_button.h"
#include <memory>
#include <string>
#include "ash/public/cpp/config.h"
#include "ash/root_window_controller.h"
......@@ -162,9 +163,9 @@ TEST_F(VoiceInteractionAppListButtonTest,
}
TEST_F(VoiceInteractionAppListButtonTest, LongPressGestureWithSecondaryUser) {
// Simulate two user with secondary user as active.
SimulateUserLogin("user1@test.com");
SimulateUserLogin("user2@test.com");
// Disallowed by secondary user.
Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed(
mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER);
// Enable voice interaction in system settings.
Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
......
......@@ -4,6 +4,8 @@
#include "ash/voice_interaction/voice_interaction_controller.h"
#include <utility>
namespace ash {
VoiceInteractionController::VoiceInteractionController() : binding_(this) {}
......@@ -49,4 +51,11 @@ void VoiceInteractionController::NotifySetupCompleted(bool completed) {
observer.OnVoiceInteractionSetupCompleted(completed);
}
void VoiceInteractionController::NotifyFeatureAllowed(
mojom::AssistantAllowedState state) {
allowed_state_ = state;
for (auto& observer : observers_)
observer.OnAssistantFeatureAllowedChanged(state);
}
} // namespace ash
......@@ -30,6 +30,7 @@ class ASH_EXPORT VoiceInteractionController
void NotifySettingsEnabled(bool enabled) override;
void NotifyContextEnabled(bool enabled) override;
void NotifySetupCompleted(bool completed) override;
void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override;
mojom::VoiceInteractionState voice_interaction_state() const {
return voice_interaction_state_;
......@@ -39,6 +40,8 @@ class ASH_EXPORT VoiceInteractionController
bool setup_completed() const { return setup_completed_; }
mojom::AssistantAllowedState allowed_state() const { return allowed_state_; }
private:
// Voice interaction state. The initial value should be set to STOPPED to make
// sure the app list button burst animation could be correctly shown.
......@@ -51,6 +54,10 @@ class ASH_EXPORT VoiceInteractionController
// Whether voice intearction setup flow has completed.
bool setup_completed_ = false;
// Whether voice intearction feature is allowed or disallowed for what reason.
mojom::AssistantAllowedState allowed_state_ =
mojom::AssistantAllowedState::ALLOWED;
base::ObserverList<VoiceInteractionObserver> observers_;
mojo::Binding<mojom::VoiceInteractionController> binding_;
......
......@@ -9,6 +9,7 @@ namespace ash {
namespace mojom {
enum class AssistantAllowedState;
enum class VoiceInteractionState;
} // namespace mojom
......@@ -29,6 +30,10 @@ class VoiceInteractionObserver {
// Called when voice interaction setup flow completed.
virtual void OnVoiceInteractionSetupCompleted(bool completed) {}
// Called when assistant feature allowed state has changed.
virtual void OnAssistantFeatureAllowedChanged(
mojom::AssistantAllowedState state) {}
protected:
virtual ~VoiceInteractionObserver() = default;
};
......
......@@ -426,4 +426,33 @@ void UpdateArcFileSystemCompatibilityPrefIfNeeded(
base::Bind(&StoreCompatibilityCheckResult, account_id, callback));
}
ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
const Profile* profile) {
if (!chromeos::switches::IsVoiceInteractionFlagsEnabled())
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_FLAG;
if (!chromeos::switches::IsVoiceInteractionLocalesSupported())
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE;
if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
if (profile->IsOffTheRecord())
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
if (profile->IsLegacySupervised())
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER;
const PrefService* prefs = profile->GetPrefs();
if (prefs->IsManagedPreference(prefs::kArcEnabled) &&
!prefs->GetBoolean(prefs::kArcEnabled)) {
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_ARC_POLICY;
}
if (!IsArcAllowedForProfile(profile))
return ash::mojom::AssistantAllowedState::DISALLOWED_BY_ARC_DISALLOWED;
return ash::mojom::AssistantAllowedState::ALLOWED;
}
} // namespace arc
......@@ -7,6 +7,7 @@
#include <stdint.h>
#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
#include "base/callback_forward.h"
// Most utility should be put in components/arc/arc_util.{h,cc}, rather than
......@@ -126,6 +127,10 @@ void UpdateArcFileSystemCompatibilityPrefIfNeeded(
const base::FilePath& profile_path,
const base::Closure& callback);
// Returns whether Google Assistant feature is allowed for given |profile|.
ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
const Profile* profile);
} // namespace arc
#endif // CHROME_BROWSER_CHROMEOS_ARC_ARC_UTIL_H_
......@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/sys_info.h"
#include "base/test/icu_test_util.h"
#include "base/test/scoped_command_line.h"
#include "base/values.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
......@@ -469,6 +470,85 @@ TEST_F(ChromeArcUtilTest, ArcPlayStoreEnabledForProfile_Managed) {
EXPECT_FALSE(IsArcPlayStoreEnabledPreferenceManagedForProfile(profile()));
}
TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_Flag) {
base::CommandLine::ForCurrentProcess()->InitFromArgv({
"", "--arc-availability=officially-supported",
});
ScopedLogIn login(GetFakeUserManager(),
AccountId::FromUserEmailGaiaId(
profile()->GetProfileUserName(), kTestGaiaId));
EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_FLAG,
IsAssistantAllowedForProfile(profile()));
}
TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_SecondaryUser) {
base::CommandLine::ForCurrentProcess()->InitFromArgv(
{"", "--arc-availability=officially-supported",
"--enable-voice-interaction"});
ScopedLogIn login2(
GetFakeUserManager(),
AccountId::FromUserEmailGaiaId("user2@gmail.com", "0123456789"));
ScopedLogIn login(GetFakeUserManager(),
AccountId::FromUserEmailGaiaId(
profile()->GetProfileUserName(), kTestGaiaId));
EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER,
IsAssistantAllowedForProfile(profile()));
}
TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_SupervisedUser) {
base::CommandLine::ForCurrentProcess()->InitFromArgv(
{"", "--arc-availability=officially-supported",
"--enable-voice-interaction"});
ScopedLogIn login(GetFakeUserManager(),
AccountId::FromUserEmailGaiaId(
profile()->GetProfileUserName(), kTestGaiaId));
profile()->SetSupervisedUserId("foo");
EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER,
IsAssistantAllowedForProfile(profile()));
}
TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_Locale) {
base::CommandLine::ForCurrentProcess()->InitFromArgv(
{"", "--arc-availability=officially-supported",
"--enable-voice-interaction"});
base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
ScopedLogIn login(GetFakeUserManager(),
AccountId::FromUserEmailGaiaId(
profile()->GetProfileUserName(), kTestGaiaId));
EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE,
IsAssistantAllowedForProfile(profile()));
}
TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_Managed) {
base::CommandLine::ForCurrentProcess()->InitFromArgv(
{"", "--arc-availability=officially-supported",
"--enable-voice-interaction"});
ScopedLogIn login(GetFakeUserManager(),
AccountId::FromUserEmailGaiaId(
profile()->GetProfileUserName(), kTestGaiaId));
ASSERT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
IsAssistantAllowedForProfile(profile()));
profile()->GetTestingPrefService()->SetManagedPref(
prefs::kArcEnabled, std::make_unique<base::Value>(false));
EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_ARC_POLICY,
IsAssistantAllowedForProfile(profile()));
profile()->GetTestingPrefService()->SetManagedPref(
prefs::kArcEnabled, std::make_unique<base::Value>(true));
EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
IsAssistantAllowedForProfile(profile()));
profile()->GetTestingPrefService()->RemoveManagedPref(prefs::kArcEnabled);
EXPECT_EQ(ash::mojom::AssistantAllowedState::ALLOWED,
IsAssistantAllowedForProfile(profile()));
}
// Test the AreArcAllOptInPreferencesIgnorableForProfile() function.
TEST_F(ChromeArcUtilTest, AreArcAllOptInPreferencesIgnorableForProfile) {
base::CommandLine::ForCurrentProcess()->InitFromArgv(
......
......@@ -21,6 +21,7 @@
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
#include "chrome/browser/chromeos/arc/voice_interaction/highlighter_controller_client.h"
#include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
......@@ -374,6 +375,10 @@ void ArcVoiceInteractionFrameworkService::HideMetalayer() {
void ArcVoiceInteractionFrameworkService::OnArcPlayStoreEnabledChanged(
bool enabled) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
voice_interaction_controller_client_->NotifyFeatureAllowed(
IsAssistantAllowedForProfile(Profile::FromBrowserContext(context_)));
if (enabled)
return;
......@@ -390,7 +395,8 @@ void ArcVoiceInteractionFrameworkService::OnSessionStateChanged() {
if (session_state != session_manager::SessionState::ACTIVE)
return;
PrefService* prefs = Profile::FromBrowserContext(context_)->GetPrefs();
Profile* profile = Profile::FromBrowserContext(context_);
PrefService* prefs = profile->GetPrefs();
bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionEnabled);
voice_interaction_controller_client_->NotifySettingsEnabled(enabled);
......@@ -401,6 +407,9 @@ void ArcVoiceInteractionFrameworkService::OnSessionStateChanged() {
prefs->GetBoolean(prefs::kArcVoiceInteractionValuePropAccepted);
voice_interaction_controller_client_->NotifySetupCompleted(setup_completed);
voice_interaction_controller_client_->NotifyFeatureAllowed(
IsAssistantAllowedForProfile(profile));
// We only want notify the status change on first user signed in.
session_manager::SessionManager::Get()->RemoveObserver(this);
}
......
......@@ -116,6 +116,9 @@ class TestVoiceInteractionController
void NotifySetupCompleted(bool completed) override {
voice_interaction_setup_completed_ = completed;
}
void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override {
assistant_allowed_state_ = state;
}
ash::mojom::VoiceInteractionState voice_interaction_state() const {
return voice_interaction_state_;
......@@ -129,6 +132,9 @@ class TestVoiceInteractionController
bool voice_interaction_setup_completed() const {
return voice_interaction_setup_completed_;
}
ash::mojom::AssistantAllowedState assistant_allowed_state() const {
return assistant_allowed_state_;
}
private:
ash::mojom::VoiceInteractionState voice_interaction_state_ =
......@@ -136,6 +142,8 @@ class TestVoiceInteractionController
bool voice_interaction_settings_enabled_ = false;
bool voice_interaction_context_enabled_ = false;
bool voice_interaction_setup_completed_ = false;
ash::mojom::AssistantAllowedState assistant_allowed_state_ =
ash::mojom::AssistantAllowedState::ALLOWED;
mojo::Binding<ash::mojom::VoiceInteractionController> binding_;
......@@ -433,6 +441,13 @@ TEST_F(ArcVoiceInteractionFrameworkServiceTest,
FlushVoiceInteractionControllerMojo();
EXPECT_EQ(controller->voice_interaction_state(),
ash::mojom::VoiceInteractionState::RUNNING);
// Send the signal to set the assistant allowed state.
controller_client->NotifyFeatureAllowed(
ash::mojom::AssistantAllowedState::DISALLOWED_BY_ARC_POLICY);
FlushVoiceInteractionControllerMojo();
EXPECT_EQ(controller->assistant_allowed_state(),
ash::mojom::AssistantAllowedState::DISALLOWED_BY_ARC_POLICY);
}
} // namespace arc
......@@ -4,6 +4,8 @@
#include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
#include <utility>
#include "ash/public/interfaces/constants.mojom.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
......@@ -37,6 +39,12 @@ void VoiceInteractionControllerClient::NotifySetupCompleted(bool completed) {
voice_interaction_controller_->NotifySetupCompleted(completed);
}
void VoiceInteractionControllerClient::NotifyFeatureAllowed(
ash::mojom::AssistantAllowedState state) {
DCHECK(voice_interaction_controller_);
voice_interaction_controller_->NotifyFeatureAllowed(state);
}
void VoiceInteractionControllerClient::SetControllerForTesting(
ash::mojom::VoiceInteractionControllerPtr controller) {
voice_interaction_controller_ = std::move(controller);
......
......@@ -23,6 +23,7 @@ class VoiceInteractionControllerClient {
void NotifySettingsEnabled(bool enabled);
void NotifyContextEnabled(bool enabled);
void NotifySetupCompleted(bool completed);
void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state);
// Testing methods.
void SetControllerForTesting(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment