Commit 50c4372d authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Chromium LUCI CQ

[CrOS PhoneHub] Add support for prohibited notification access

Notification access is not allowed for users using a Work Profile. This
CL updates NotificationAccessManager to support this case, and it
updates PhoneStatusProcessor to mark access as prohibited if a Work
Profile is active.

This CL also propagates this value to settings so that the notifications
settings UI can be disabled in this case.

Bug: 1155151, 1106937
Change-Id: I76cc99e17eca333f695c4786b387e5e419a7350a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2573681
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarJimmy Gong <jimmyxgong@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834085}
parent 0e7b21d0
......@@ -156,8 +156,13 @@ void NotificationOptInView::InitLayout() {
void NotificationOptInView::UpdateVisibility() {
DCHECK(notification_access_manager_);
// Can only request access if it is available but has not yet been granted.
bool can_request_access = notification_access_manager_->GetAccessStatus() ==
chromeos::phonehub::NotificationAccessManager::
AccessStatus::kAvailableButNotGranted;
const bool should_show =
!notification_access_manager_->HasAccessBeenGranted() &&
can_request_access &&
!notification_access_manager_->HasNotificationSetupUiBeenDismissed();
SetVisible(should_show);
}
......
......@@ -205,7 +205,9 @@ TEST_F(PhoneHubTrayTest, FocusBubbleWhenOpenedByKeyboard) {
}
TEST_F(PhoneHubTrayTest, ShowNotificationOptInViewWhenAccessNotGranted) {
GetNotificationAccessManager()->SetHasAccessBeenGrantedInternal(false);
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted);
ClickTrayButton();
......@@ -223,7 +225,19 @@ TEST_F(PhoneHubTrayTest, ShowNotificationOptInViewWhenAccessNotGranted) {
}
TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessHasBeenGranted) {
GetNotificationAccessManager()->SetHasAccessBeenGrantedInternal(true);
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::
kAccessGranted);
ClickTrayButton();
EXPECT_TRUE(notification_opt_in_view());
EXPECT_FALSE(notification_opt_in_view()->GetVisible());
}
TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessIsProhibited) {
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::kProhibited);
ClickTrayButton();
......@@ -232,7 +246,9 @@ TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessHasBeenGranted) {
}
TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) {
GetNotificationAccessManager()->SetHasAccessBeenGrantedInternal(false);
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted);
ClickTrayButton();
EXPECT_TRUE(notification_opt_in_view());
......@@ -251,13 +267,17 @@ TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) {
ClickOnAndWait(notification_opt_in_set_up_button());
// Simulate that notification access has been granted.
GetNotificationAccessManager()->SetHasAccessBeenGrantedInternal(true);
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::
kAccessGranted);
// This view should be dismissed.
EXPECT_FALSE(notification_opt_in_view()->GetVisible());
// Simulate that notification access has been revoked by the phone.
GetNotificationAccessManager()->SetHasAccessBeenGrantedInternal(false);
GetNotificationAccessManager()->SetAccessStatusInternal(
chromeos::phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted);
// This view should show up again.
EXPECT_TRUE(notification_opt_in_view()->GetVisible());
......
......@@ -379,9 +379,12 @@ void MultideviceHandler::HandleAttemptNotificationSetup(
DCHECK(features::IsPhoneHubEnabled());
DCHECK(!notification_access_operation_);
if (notification_access_manager_->HasAccessBeenGranted()) {
PA_LOG(WARNING) << "Phonehub notification access has already been granted, "
"returning early.";
phonehub::NotificationAccessManager::AccessStatus access_status =
notification_access_manager_->GetAccessStatus();
if (access_status != phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted) {
PA_LOG(WARNING) << "Cannot request notification access setup flow; current "
<< "status: " << access_status;
return;
}
......@@ -473,16 +476,12 @@ MultideviceHandler::GeneratePageContentDataDictionary() {
? android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete()
: false);
// TODO(khorimoto): Send prohibited value if notification access is
// prohibited.
static const int kAccessNotGranted = 1;
static const int kAccessGranted = 2;
int access_value = kAccessNotGranted;
if (notification_access_manager_ &&
notification_access_manager_->HasAccessBeenGranted()) {
access_value = kAccessGranted;
}
page_content_dictionary->SetInteger(kNotificationAccessStatus, access_value);
phonehub::NotificationAccessManager::AccessStatus access_status = phonehub::
NotificationAccessManager::AccessStatus::kAvailableButNotGranted;
if (notification_access_manager_)
access_status = notification_access_manager_->GetAccessStatus();
page_content_dictionary->SetInteger(kNotificationAccessStatus,
static_cast<int32_t>(access_status));
return page_content_dictionary;
}
......
......@@ -158,7 +158,8 @@ class MultideviceHandlerTest : public testing::Test {
std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
fake_notification_access_manager_ =
std::make_unique<phonehub::FakeNotificationAccessManager>(
/*has_access_been_granted=*/false);
phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted);
fake_android_sms_pairing_state_tracker_ = std::make_unique<
multidevice_setup::FakeAndroidSmsPairingStateTracker>();
fake_android_sms_app_manager_ =
......@@ -227,8 +228,11 @@ class MultideviceHandlerTest : public testing::Test {
}
void CallAttemptNotificationSetup(bool has_access_been_granted) {
fake_notification_access_manager()->SetHasAccessBeenGrantedInternal(
has_access_been_granted);
fake_notification_access_manager()->SetAccessStatusInternal(
has_access_been_granted
? phonehub::NotificationAccessManager::AccessStatus::kAccessGranted
: phonehub::NotificationAccessManager::AccessStatus::
kAvailableButNotGranted);
base::ListValue empty_args;
test_web_ui()->HandleReceivedMessage("attemptNotificationSetup",
&empty_args);
......
......@@ -8,22 +8,23 @@ namespace chromeos {
namespace phonehub {
FakeNotificationAccessManager::FakeNotificationAccessManager(
bool has_access_been_granted)
: has_access_been_granted_(has_access_been_granted) {}
AccessStatus access_status)
: access_status_(access_status) {}
FakeNotificationAccessManager::~FakeNotificationAccessManager() = default;
void FakeNotificationAccessManager::SetHasAccessBeenGrantedInternal(
bool has_access_been_granted) {
if (has_access_been_granted_ == has_access_been_granted)
void FakeNotificationAccessManager::SetAccessStatusInternal(
AccessStatus access_status) {
if (access_status_ == access_status)
return;
has_access_been_granted_ = has_access_been_granted;
access_status_ = access_status;
NotifyNotificationAccessChanged();
}
bool FakeNotificationAccessManager::HasAccessBeenGranted() const {
return has_access_been_granted_;
NotificationAccessManager::AccessStatus
FakeNotificationAccessManager::GetAccessStatus() const {
return access_status_;
}
bool FakeNotificationAccessManager::HasNotificationSetupUiBeenDismissed()
......@@ -41,9 +42,17 @@ void FakeNotificationAccessManager::ResetHasNotificationSetupUiBeenDismissed() {
void FakeNotificationAccessManager::SetNotificationSetupOperationStatus(
NotificationAccessSetupOperation::Status new_status) {
if (new_status ==
NotificationAccessSetupOperation::Status::kCompletedSuccessfully) {
SetHasAccessBeenGrantedInternal(true);
switch (new_status) {
case NotificationAccessSetupOperation::Status::kCompletedSuccessfully:
SetAccessStatusInternal(AccessStatus::kAccessGranted);
break;
case NotificationAccessSetupOperation::Status::
kProhibitedFromProvidingAccess:
SetAccessStatusInternal(AccessStatus::kProhibited);
break;
default:
// Do not update access status based on other operation status values.
break;
}
NotificationAccessManager::SetNotificationSetupOperationStatus(new_status);
......
......@@ -12,24 +12,25 @@ namespace phonehub {
class FakeNotificationAccessManager : public NotificationAccessManager {
public:
explicit FakeNotificationAccessManager(bool has_access_been_granted = false);
explicit FakeNotificationAccessManager(
AccessStatus access_status = AccessStatus::kAvailableButNotGranted);
~FakeNotificationAccessManager() override;
using NotificationAccessManager::IsSetupOperationInProgress;
void SetHasAccessBeenGrantedInternal(bool has_access_been_granted) override;
void SetAccessStatusInternal(AccessStatus access_status) override;
void SetNotificationSetupOperationStatus(
NotificationAccessSetupOperation::Status new_status);
// NotificationAccessManager:
bool HasAccessBeenGranted() const override;
AccessStatus GetAccessStatus() const override;
bool HasNotificationSetupUiBeenDismissed() const override;
void DismissSetupRequiredUi() override;
void ResetHasNotificationSetupUiBeenDismissed();
private:
bool has_access_been_granted_;
AccessStatus access_status_;
bool has_notification_setup_ui_been_dismissed_ = false;
};
......
......@@ -43,8 +43,10 @@ MultideviceSetupStateUpdater::~MultideviceSetupStateUpdater() {
}
void MultideviceSetupStateUpdater::OnNotificationAccessChanged() {
if (notification_access_manager_->HasAccessBeenGranted())
if (notification_access_manager_->GetAccessStatus() ==
NotificationAccessManager::AccessStatus::kAccessGranted) {
return;
}
PA_LOG(INFO) << "Disabling PhoneHubNotifications feature.";
......
......@@ -36,7 +36,10 @@ class MultideviceSetupStateUpdaterTest : public testing::Test {
}
void SetNotififcationAccess(bool enabled) {
fake_notification_access_manager_.SetHasAccessBeenGrantedInternal(enabled);
fake_notification_access_manager_.SetAccessStatusInternal(
enabled
? NotificationAccessManager::AccessStatus::kAccessGranted
: NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
}
void SetFeatureState(Feature feature, FeatureState feature_state) {
......
......@@ -18,7 +18,9 @@ NotificationAccessManager::~NotificationAccessManager() = default;
std::unique_ptr<NotificationAccessSetupOperation>
NotificationAccessManager::AttemptNotificationSetup(
NotificationAccessSetupOperation::Delegate* delegate) {
if (HasAccessBeenGranted())
// Should only be able to start the setup process if notification access is
// available but not yet granted.
if (GetAccessStatus() != AccessStatus::kAvailableButNotGranted)
return nullptr;
int operation_id = next_operation_id_;
......@@ -75,5 +77,21 @@ void NotificationAccessManager::OnSetupOperationDeleted(int operation_id) {
PA_LOG(INFO) << "Notification access setup operation has ended.";
}
std::ostream& operator<<(std::ostream& stream,
NotificationAccessManager::AccessStatus status) {
switch (status) {
case NotificationAccessManager::AccessStatus::kProhibited:
stream << "[Access prohibited]";
break;
case NotificationAccessManager::AccessStatus::kAvailableButNotGranted:
stream << "[Access available but not granted]";
break;
case NotificationAccessManager::AccessStatus::kAccessGranted:
stream << "[Access granted]";
break;
}
return stream;
}
} // namespace phonehub
} // namespace chromeos
......@@ -5,6 +5,8 @@
#ifndef CHROMEOS_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_H_
#define CHROMEOS_COMPONENTS_PHONEHUB_NOTIFICATION_ACCESS_MANAGER_H_
#include <ostream>
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
......@@ -25,11 +27,25 @@ namespace phonehub {
// access setup flow via AttemptNotificationSetup().
class NotificationAccessManager {
public:
// Status of notification access. Numerical values are stored in prefs and
// should not be changed or reused.
enum class AccessStatus {
// Access has not been granted and is prohibited from being granted (e.g.,
// if the phone is using a Work Profile).
kProhibited = 0,
// Access has not been granted, but the user is free to grant access.
kAvailableButNotGranted = 1,
// Access has been granted by the user.
kAccessGranted = 2
};
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
// Called when notification access has changed; use HasAccessBeenGranted()
// Called when notification access has changed; use GetAccessStatus()
// for the new status.
virtual void OnNotificationAccessChanged() = 0;
};
......@@ -39,7 +55,7 @@ class NotificationAccessManager {
delete;
virtual ~NotificationAccessManager();
virtual bool HasAccessBeenGranted() const = 0;
virtual AccessStatus GetAccessStatus() const = 0;
virtual bool HasNotificationSetupUiBeenDismissed() const = 0;
......@@ -77,11 +93,9 @@ class NotificationAccessManager {
friend class NotificationAccessManagerImplTest;
friend class PhoneStatusProcessor;
// This only sets the internal state of the whether notification access has
// mode been enabled and does not send a request to set the state of the
// remote phone device.
virtual void SetHasAccessBeenGrantedInternal(
bool has_access_been_granted) = 0;
// Sets the internal AccessStatus but does not send a request for a new
// status to the remote phone device.
virtual void SetAccessStatusInternal(AccessStatus access_status) = 0;
void OnSetupOperationDeleted(int operation_id);
......@@ -91,6 +105,9 @@ class NotificationAccessManager {
base::WeakPtrFactory<NotificationAccessManager> weak_ptr_factory_{this};
};
std::ostream& operator<<(std::ostream& stream,
NotificationAccessManager::AccessStatus status);
} // namespace phonehub
} // namespace chromeos
......
......@@ -17,7 +17,9 @@ namespace phonehub {
// static
void NotificationAccessManagerImpl::RegisterPrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kNotificationAccessGranted, false);
registry->RegisterIntegerPref(
prefs::kNotificationAccessStatus,
static_cast<int>(AccessStatus::kAvailableButNotGranted));
registry->RegisterBooleanPref(prefs::kHasDismissedSetupRequiredUi, false);
}
......@@ -50,25 +52,40 @@ void NotificationAccessManagerImpl::DismissSetupRequiredUi() {
pref_service_->SetBoolean(prefs::kHasDismissedSetupRequiredUi, true);
}
bool NotificationAccessManagerImpl::HasAccessBeenGranted() const {
return pref_service_->GetBoolean(prefs::kNotificationAccessGranted);
NotificationAccessManagerImpl::AccessStatus
NotificationAccessManagerImpl::GetAccessStatus() const {
int status = pref_service_->GetInteger(prefs::kNotificationAccessStatus);
return static_cast<AccessStatus>(status);
}
void NotificationAccessManagerImpl::SetHasAccessBeenGrantedInternal(
bool has_access_been_granted) {
if (has_access_been_granted == HasAccessBeenGranted())
void NotificationAccessManagerImpl::SetAccessStatusInternal(
AccessStatus access_status) {
if (access_status == GetAccessStatus())
return;
PA_LOG(INFO) << "Notification access state has been set to: "
<< has_access_been_granted;
PA_LOG(INFO) << "Notification access: " << GetAccessStatus() << " => "
<< access_status;
pref_service_->SetBoolean(prefs::kNotificationAccessGranted,
has_access_been_granted);
pref_service_->SetInteger(prefs::kNotificationAccessStatus,
static_cast<int>(access_status));
NotifyNotificationAccessChanged();
if (IsSetupOperationInProgress() && has_access_been_granted) {
if (!IsSetupOperationInProgress())
return;
switch (access_status) {
case AccessStatus::kProhibited:
SetNotificationSetupOperationStatus(
NotificationAccessSetupOperation::Status::
kProhibitedFromProvidingAccess);
break;
case AccessStatus::kAccessGranted:
SetNotificationSetupOperationStatus(
NotificationAccessSetupOperation::Status::kCompletedSuccessfully);
break;
case AccessStatus::kAvailableButNotGranted:
// Intentionally blank; the operation status should not change.
break;
}
}
......
......@@ -20,9 +20,6 @@ class ConnectionScheduler;
// Implements NotificationAccessManager by persisting the last-known
// notification access value to user prefs.
// TODO(khorimoto): Currently HasAccessBeenGranted() always returns false. Have
// it return true once the phone has sent a message indicating that it has
// granted access.
class NotificationAccessManagerImpl : public NotificationAccessManager,
public FeatureStatusProvider::Observer {
public:
......@@ -39,8 +36,8 @@ class NotificationAccessManagerImpl : public NotificationAccessManager,
friend class NotificationAccessManagerImplTest;
// NotificationAccessManager:
bool HasAccessBeenGranted() const override;
void SetHasAccessBeenGrantedInternal(bool has_access_been_granted) override;
AccessStatus GetAccessStatus() const override;
void SetAccessStatusInternal(AccessStatus access_status) override;
void OnSetupRequested() override;
bool HasNotificationSetupUiBeenDismissed() const override;
......
......@@ -73,9 +73,9 @@ class NotificationAccessManagerImplTest : public testing::Test {
void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
void Initialize(bool initial_has_access_been_granted) {
pref_service_.SetBoolean(prefs::kNotificationAccessGranted,
initial_has_access_been_granted);
void Initialize(NotificationAccessManager::AccessStatus expected_status) {
pref_service_.SetInteger(prefs::kNotificationAccessStatus,
static_cast<int>(expected_status));
manager_ = std::make_unique<NotificationAccessManagerImpl>(
&pref_service_, fake_feature_status_provider_.get(),
fake_message_sender_.get(), fake_connection_scheduler_.get());
......@@ -87,10 +87,11 @@ class NotificationAccessManagerImplTest : public testing::Test {
return fake_delegate_.status();
}
void VerifyNotificationAccessGrantedState(bool expected_value) {
EXPECT_EQ(expected_value,
pref_service_.GetBoolean(prefs::kNotificationAccessGranted));
EXPECT_EQ(expected_value, manager_->HasAccessBeenGranted());
void VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus expected_status) {
EXPECT_EQ(static_cast<int>(expected_status),
pref_service_.GetInteger(prefs::kNotificationAccessStatus));
EXPECT_EQ(expected_status, manager_->GetAccessStatus());
}
bool HasNotificationSetupUiBeenDismissed() {
......@@ -107,8 +108,8 @@ class NotificationAccessManagerImplTest : public testing::Test {
return manager_->IsSetupOperationInProgress();
}
void SetHasAccessBeenGrantedInternal(bool has_access_been_granted) {
manager_->SetHasAccessBeenGrantedInternal(has_access_been_granted);
void SetAccessStatusInternal(NotificationAccessManager::AccessStatus status) {
manager_->SetAccessStatusInternal(status);
}
void SetFeatureStatus(FeatureStatus status) {
......@@ -143,12 +144,12 @@ class NotificationAccessManagerImplTest : public testing::Test {
TEST_F(NotificationAccessManagerImplTest, ShouldShowSetupRequiredUi) {
// Notification setup is not dismissed initially even when access has been
// granted.
Initialize(/*initial_has_access_been_granted=*/true);
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
// Notification setup is not dismissed initially when access has not been
// granted.
Initialize(/*initial_has_access_been_granted=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
// Simlulate dismissal of UI.
......@@ -156,17 +157,18 @@ TEST_F(NotificationAccessManagerImplTest, ShouldShowSetupRequiredUi) {
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
// Dismissal value is persisted on initialization with access not granted.
Initialize(/*initial_has_access_been_granted=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
// Dismissal value is persisted on initialization with access granted.
Initialize(/*initial_has_access_been_granted=*/true);
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
}
TEST_F(NotificationAccessManagerImplTest, InitiallyGranted) {
Initialize(/*initial_has_access_been_granted=*/true);
VerifyNotificationAccessGrantedState(/*expected_value=*/true);
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Cannot start the notification access setup flow if access has already been
// granted.
......@@ -175,8 +177,9 @@ TEST_F(NotificationAccessManagerImplTest, InitiallyGranted) {
}
TEST_F(NotificationAccessManagerImplTest, OnFeatureStatusChanged) {
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
......@@ -210,8 +213,9 @@ TEST_F(NotificationAccessManagerImplTest, StartDisconnectedAndNoAccess) {
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled but disconnected status and access
// not granted.
......@@ -231,18 +235,56 @@ TEST_F(NotificationAccessManagerImplTest, StartDisconnectedAndNoAccess) {
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetHasAccessBeenGrantedInternal(/*has_access_been_granted=*/true);
VerifyNotificationAccessGrantedState(/*expected_value=*/true);
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest,
StartDisconnectedAndNoAccess_Prohibited) {
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled but disconnected status and access
// not granted.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
// Simulate changing states from connecting to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
// Verify that the request message has been sent and our operation status
// is updated.
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kProhibited);
EXPECT_EQ(
NotificationAccessSetupOperation::Status::kProhibitedFromProvidingAccess,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, StartConnectingAndNoAccess) {
// Set initial state to connecting.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled and connecting status and access
// not granted.
......@@ -260,8 +302,10 @@ TEST_F(NotificationAccessManagerImplTest, StartConnectingAndNoAccess) {
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetHasAccessBeenGrantedInternal(/*has_access_been_granted=*/true);
VerifyNotificationAccessGrantedState(/*expected_value=*/true);
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
......@@ -270,8 +314,9 @@ TEST_F(NotificationAccessManagerImplTest, StartConnectedAndNoAccess) {
// Set initial state to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled and connected status and access
// not granted.
......@@ -286,8 +331,10 @@ TEST_F(NotificationAccessManagerImplTest, StartConnectedAndNoAccess) {
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetHasAccessBeenGrantedInternal(/*has_access_been_granted=*/true);
VerifyNotificationAccessGrantedState(/*expected_value=*/true);
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
......@@ -296,8 +343,9 @@ TEST_F(NotificationAccessManagerImplTest, SimulateConnectingToDisconnected) {
// Set initial state to connecting.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
......@@ -312,8 +360,9 @@ TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisconnected) {
// Simulate connected state.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
......@@ -330,8 +379,9 @@ TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisabled) {
// Simulate connected state.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(/*initial_has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
......@@ -345,12 +395,27 @@ TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisabled) {
}
TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToNotGranted) {
Initialize(/*initial_has_access_been_granted=*/true);
VerifyNotificationAccessGrantedState(/*expected_value=*/true);
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Simulate flipping the access state to no granted.
SetHasAccessBeenGrantedInternal(/*has_access_been_granted=*/false);
VerifyNotificationAccessGrantedState(/*expected_value=*/false);
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
}
TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToProhibited) {
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Simulate flipping the access state to prohibited.
SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kProhibited);
}
} // namespace phonehub
} // namespace chromeos
......@@ -16,11 +16,13 @@ namespace {
// Status values which are considered "final" - i.e., once the status of an
// operation changes to one of these values, the operation has completed. These
// status values indicate either a success or a fatal error.
constexpr std::array<NotificationAccessSetupOperation::Status, 3>
constexpr std::array<NotificationAccessSetupOperation::Status, 4>
kOperationFinishedStatus{
NotificationAccessSetupOperation::Status::kTimedOutConnecting,
NotificationAccessSetupOperation::Status::kConnectionDisconnected,
NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
NotificationAccessSetupOperation::Status::
kProhibitedFromProvidingAccess,
};
} // namespace
......@@ -66,6 +68,10 @@ std::ostream& operator<<(std::ostream& stream,
case NotificationAccessSetupOperation::Status::kCompletedSuccessfully:
stream << "[Completed successfully]";
break;
case NotificationAccessSetupOperation::Status::
kProhibitedFromProvidingAccess:
stream << "[Prohibited from providing access]";
break;
}
return stream;
......
......@@ -48,6 +48,10 @@ class NotificationAccessSetupOperation {
// The user has completed the phone-side opt-in flow.
kCompletedSuccessfully = 5,
// The user's phone is prohibited from granting notification access (e.g.,
// the user could be using a Work Profile).
kProhibitedFromProvidingAccess = 6
};
// Returns true if the provided status is the final one for this operation,
......
......@@ -108,6 +108,21 @@ PhoneStatusModel::BatterySaverState GetBatterySaverStateFromProto(
}
}
NotificationAccessManager::AccessStatus ComputeNotificationAccessState(
const proto::PhoneProperties& phone_properties) {
// If the user has a Work Profile active, notification access is not allowed
// by Android. See https://crbug.com/1155151.
if (phone_properties.profile_type() == proto::ProfileType::WORK_PROFILE)
return NotificationAccessManager::AccessStatus::kProhibited;
if (phone_properties.notification_access_state() ==
proto::NotificationAccessState::ACCESS_GRANTED) {
return NotificationAccessManager::AccessStatus::kAccessGranted;
}
return NotificationAccessManager::AccessStatus::kAvailableButNotGranted;
}
base::Optional<Notification> ProcessNotificationProto(
const proto::Notification& proto) {
// Only process notifications that are messaging apps with inline-replies.
......@@ -219,9 +234,8 @@ void PhoneStatusProcessor::SetReceivedPhoneStatusModelStates(
proto::NotificationMode::DO_NOT_DISTURB_ON,
phone_properties.profile_type() != proto::ProfileType::WORK_PROFILE);
notification_access_manager_->SetHasAccessBeenGrantedInternal(
phone_properties.notification_access_state() ==
proto::NotificationAccessState::ACCESS_GRANTED);
notification_access_manager_->SetAccessStatusInternal(
ComputeNotificationAccessState(phone_properties));
find_my_device_controller_->SetIsPhoneRingingInternal(
phone_properties.ring_status() == proto::FindMyDeviceRingStatus::RINGING);
......
......@@ -111,7 +111,7 @@ TEST_F(PhoneStatusProcessorTest, PhoneStatusSnapshotUpdate) {
expected_phone_properties->set_profile_type(
proto::ProfileType::DEFAULT_PROFILE);
expected_phone_properties->set_notification_access_state(
proto::NotificationAccessState::ACCESS_GRANTED);
proto::NotificationAccessState::ACCESS_NOT_GRANTED);
expected_phone_properties->set_ring_status(
proto::FindMyDeviceRingStatus::RINGING);
expected_phone_properties->set_battery_percentage(24u);
......@@ -142,7 +142,8 @@ TEST_F(PhoneStatusProcessorTest, PhoneStatusSnapshotUpdate) {
EXPECT_TRUE(fake_do_not_disturb_controller_->CanRequestNewDndState());
EXPECT_EQ(FindMyDeviceController::Status::kRingingOn,
fake_find_my_device_controller_->GetPhoneRingingStatus());
EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted());
EXPECT_EQ(NotificationAccessManager::AccessStatus::kAvailableButNotGranted,
fake_notification_access_manager_->GetAccessStatus());
base::Optional<PhoneStatusModel> phone_status_model =
mutable_phone_model_->phone_status_model();
......@@ -206,7 +207,8 @@ TEST_F(PhoneStatusProcessorTest, PhoneStatusUpdate) {
EXPECT_FALSE(fake_do_not_disturb_controller_->CanRequestNewDndState());
EXPECT_EQ(FindMyDeviceController::Status::kRingingOn,
fake_find_my_device_controller_->GetPhoneRingingStatus());
EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted());
EXPECT_EQ(NotificationAccessManager::AccessStatus::kProhibited,
fake_notification_access_manager_->GetAccessStatus());
base::Optional<PhoneStatusModel> phone_status_model =
mutable_phone_model_->phone_status_model();
......@@ -220,18 +222,21 @@ TEST_F(PhoneStatusProcessorTest, PhoneStatusUpdate) {
EXPECT_EQ(PhoneStatusModel::MobileStatus::kSimWithReception,
phone_status_model->mobile_status());
// Update with one removed notification.
// Update with one removed notification and a default profile.
expected_update.add_removed_notification_ids(0u);
expected_update.mutable_properties()->set_profile_type(
proto::ProfileType::DEFAULT_PROFILE);
fake_message_receiver_->NotifyPhoneStatusUpdateReceived(expected_update);
EXPECT_EQ(0u, fake_notification_manager_->num_notifications());
EXPECT_EQ(base::UTF8ToUTF16(test_remote_device_.name()),
*mutable_phone_model_->phone_name());
EXPECT_TRUE(fake_do_not_disturb_controller_->IsDndEnabled());
EXPECT_FALSE(fake_do_not_disturb_controller_->CanRequestNewDndState());
EXPECT_TRUE(fake_do_not_disturb_controller_->CanRequestNewDndState());
EXPECT_EQ(FindMyDeviceController::Status::kRingingOn,
fake_find_my_device_controller_->GetPhoneRingingStatus());
EXPECT_TRUE(fake_notification_access_manager_->HasAccessBeenGranted());
EXPECT_EQ(NotificationAccessManager::AccessStatus::kAccessGranted,
fake_notification_access_manager_->GetAccessStatus());
phone_status_model = mutable_phone_model_->phone_status_model();
EXPECT_EQ(PhoneStatusModel::ChargingState::kChargingAc,
......
......@@ -8,9 +8,11 @@ namespace chromeos {
namespace phonehub {
namespace prefs {
// Whether notification access had been granted by the user on their phone.
const char kNotificationAccessGranted[] =
"cros.phonehub.notification_access_granted";
// The last provided notification access status provided by the phone. This pref
// stores the numerical value associated with the
// NotificationAccessManager::AccessStatus enum.
const char kNotificationAccessStatus[] =
"cros.phonehub.notification_access_status";
// Whether user has completed onboarding and dismissed the UI before or if
// the user has already gone through the onboarding process and has enabled the
......
......@@ -9,7 +9,7 @@ namespace chromeos {
namespace phonehub {
namespace prefs {
extern const char kNotificationAccessGranted[];
extern const char kNotificationAccessStatus[];
extern const char kHideOnboardingUi[];
extern const char kIsAwaitingVerifiedHost[];
extern const char kHasDismissedSetupRequiredUi[];
......
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