multidevice_feature_access_manager_impl.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright 2020 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. #include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
  5. #include "ash/components/multidevice/logging/logging.h"
  6. #include "ash/components/phonehub/connection_scheduler.h"
  7. #include "ash/components/phonehub/message_sender.h"
  8. #include "ash/components/phonehub/pref_names.h"
  9. #include "ash/components/phonehub/util/histogram_util.h"
  10. #include "ash/constants/ash_features.h"
  11. #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
  12. #include "ash/webui/eche_app_ui/pref_names.h"
  13. #include "components/prefs/pref_change_registrar.h"
  14. #include "components/prefs/pref_registry_simple.h"
  15. #include "components/prefs/pref_service.h"
  16. #include "pref_names.h"
  17. namespace ash {
  18. namespace phonehub {
  19. namespace {
  20. using multidevice_setup::mojom::Feature;
  21. using multidevice_setup::mojom::FeatureState;
  22. } // namespace
  23. // static
  24. void MultideviceFeatureAccessManagerImpl::RegisterPrefs(
  25. PrefRegistrySimple* registry) {
  26. registry->RegisterIntegerPref(
  27. prefs::kCameraRollAccessStatus,
  28. static_cast<int>(AccessStatus::kAvailableButNotGranted));
  29. registry->RegisterIntegerPref(
  30. prefs::kNotificationAccessStatus,
  31. static_cast<int>(AccessStatus::kAvailableButNotGranted));
  32. registry->RegisterIntegerPref(
  33. prefs::kNotificationAccessProhibitedReason,
  34. static_cast<int>(AccessProhibitedReason::kUnknown));
  35. registry->RegisterBooleanPref(prefs::kHasDismissedSetupRequiredUi, false);
  36. registry->RegisterBooleanPref(prefs::kNeedsOneTimeNotificationAccessUpdate,
  37. true);
  38. registry->RegisterBooleanPref(prefs::kFeatureSetupRequestSupported, false);
  39. }
  40. MultideviceFeatureAccessManagerImpl::MultideviceFeatureAccessManagerImpl(
  41. PrefService* pref_service,
  42. multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
  43. FeatureStatusProvider* feature_status_provider,
  44. MessageSender* message_sender,
  45. ConnectionScheduler* connection_scheduler)
  46. : pref_service_(pref_service),
  47. multidevice_setup_client_(multidevice_setup_client),
  48. feature_status_provider_(feature_status_provider),
  49. message_sender_(message_sender),
  50. connection_scheduler_(connection_scheduler) {
  51. DCHECK(feature_status_provider_);
  52. DCHECK(message_sender_);
  53. DCHECK(multidevice_setup_client_);
  54. current_feature_status_ = feature_status_provider_->GetStatus();
  55. PA_LOG(VERBOSE) << __func__
  56. << ": current feature status = " << current_feature_status_;
  57. feature_status_provider_->AddObserver(this);
  58. pref_change_registrar_.Init(pref_service_);
  59. pref_change_registrar_.Add(
  60. eche_app::prefs::kAppsAccessStatus,
  61. base::BindRepeating(
  62. &MultideviceFeatureAccessManagerImpl::NotifyAppsAccessChanged,
  63. base::Unretained(this)));
  64. }
  65. MultideviceFeatureAccessManagerImpl::~MultideviceFeatureAccessManagerImpl() {
  66. feature_status_provider_->RemoveObserver(this);
  67. pref_change_registrar_.RemoveAll();
  68. }
  69. bool MultideviceFeatureAccessManagerImpl::
  70. HasMultideviceFeatureSetupUiBeenDismissed() const {
  71. return pref_service_->GetBoolean(prefs::kHasDismissedSetupRequiredUi);
  72. }
  73. void MultideviceFeatureAccessManagerImpl::DismissSetupRequiredUi() {
  74. pref_service_->SetBoolean(prefs::kHasDismissedSetupRequiredUi, true);
  75. }
  76. bool MultideviceFeatureAccessManagerImpl::IsAccessRequestAllowed(
  77. Feature feature) {
  78. const FeatureState feature_state =
  79. multidevice_setup_client_->GetFeatureState(feature);
  80. bool result = feature_state == FeatureState::kDisabledByUser ||
  81. feature_state == FeatureState::kEnabledByUser;
  82. return result;
  83. }
  84. MultideviceFeatureAccessManagerImpl::AccessStatus
  85. MultideviceFeatureAccessManagerImpl::GetNotificationAccessStatus() const {
  86. int status = pref_service_->GetInteger(prefs::kNotificationAccessStatus);
  87. return static_cast<AccessStatus>(status);
  88. }
  89. MultideviceFeatureAccessManagerImpl::AccessStatus
  90. MultideviceFeatureAccessManagerImpl::GetCameraRollAccessStatus() const {
  91. int status = pref_service_->GetInteger(prefs::kCameraRollAccessStatus);
  92. return static_cast<AccessStatus>(status);
  93. }
  94. MultideviceFeatureAccessManager::AccessStatus
  95. MultideviceFeatureAccessManagerImpl::GetAppsAccessStatus() const {
  96. // TODO(samchiu): The AppsAccessStatus will be updated by eche_app_ui
  97. // component only. We should listen to pref change and update it to
  98. // MultiDeviceFeatureOptInView.
  99. int status = pref_service_->GetInteger(eche_app::prefs::kAppsAccessStatus);
  100. return static_cast<AccessStatus>(status);
  101. }
  102. bool MultideviceFeatureAccessManagerImpl::GetFeatureSetupRequestSupported()
  103. const {
  104. return pref_service_->GetBoolean(prefs::kFeatureSetupRequestSupported);
  105. }
  106. MultideviceFeatureAccessManagerImpl::AccessProhibitedReason
  107. MultideviceFeatureAccessManagerImpl::GetNotificationAccessProhibitedReason()
  108. const {
  109. int reason =
  110. pref_service_->GetInteger(prefs::kNotificationAccessProhibitedReason);
  111. return static_cast<AccessProhibitedReason>(reason);
  112. }
  113. void MultideviceFeatureAccessManagerImpl::SetNotificationAccessStatusInternal(
  114. AccessStatus access_status,
  115. AccessProhibitedReason reason) {
  116. // TODO(http://crbug.com/1215559): Deprecate when there are no more active
  117. // Phone Hub notification users on M89. Some users had notifications
  118. // automatically disabled when updating from M89 to M90+ because the
  119. // notification feature state went from enabled-by-default to
  120. // disabled-by-default. To re-enable those users, we once and only once notify
  121. // observers if access has been granted by the phone. Notably, the
  122. // MultideviceSetupStateUpdate will decide whether or not the notification
  123. // feature should be enabled. See MultideviceSetupStateUpdater's method
  124. // IsWaitingForAccessToInitiallyEnableNotifications() for more details.
  125. bool needs_one_time_notifications_access_update =
  126. pref_service_->GetBoolean(prefs::kNeedsOneTimeNotificationAccessUpdate) &&
  127. access_status == AccessStatus::kAccessGranted;
  128. if (!needs_one_time_notifications_access_update &&
  129. !HasAccessStatusChanged(access_status, reason)) {
  130. return;
  131. }
  132. pref_service_->SetBoolean(prefs::kNeedsOneTimeNotificationAccessUpdate,
  133. false);
  134. PA_LOG(INFO) << "Notification access: "
  135. << std::make_pair(GetNotificationAccessStatus(),
  136. GetNotificationAccessProhibitedReason())
  137. << " => " << std::make_pair(access_status, reason);
  138. pref_service_->SetInteger(prefs::kNotificationAccessStatus,
  139. static_cast<int>(access_status));
  140. pref_service_->SetInteger(prefs::kNotificationAccessProhibitedReason,
  141. static_cast<int>(reason));
  142. NotifyNotificationAccessChanged();
  143. if (IsNotificationSetupOperationInProgress()) {
  144. switch (access_status) {
  145. case AccessStatus::kProhibited:
  146. SetNotificationSetupOperationStatus(
  147. NotificationAccessSetupOperation::Status::
  148. kProhibitedFromProvidingAccess);
  149. break;
  150. case AccessStatus::kAccessGranted:
  151. SetNotificationSetupOperationStatus(
  152. NotificationAccessSetupOperation::Status::kCompletedSuccessfully);
  153. break;
  154. case AccessStatus::kAvailableButNotGranted:
  155. // Intentionally blank; the operation status should not change.
  156. break;
  157. }
  158. } else if (IsCombinedSetupOperationInProgress()) {
  159. switch (access_status) {
  160. case AccessStatus::kProhibited:
  161. SetCombinedSetupOperationStatus(CombinedAccessSetupOperation::Status::
  162. kProhibitedFromProvidingAccess);
  163. break;
  164. case AccessStatus::kAccessGranted:
  165. combined_setup_notifications_pending_ = false;
  166. break;
  167. case AccessStatus::kAvailableButNotGranted:
  168. // Intentionally blank; the operation status should not change.
  169. break;
  170. }
  171. if (!combined_setup_notifications_pending_ &&
  172. !combined_setup_camera_roll_pending_) {
  173. SetCombinedSetupOperationStatus(
  174. CombinedAccessSetupOperation::Status::kCompletedSuccessfully);
  175. }
  176. }
  177. }
  178. void MultideviceFeatureAccessManagerImpl::SetCameraRollAccessStatusInternal(
  179. AccessStatus access_status) {
  180. PA_LOG(INFO) << "Camera Roll access: " << GetCameraRollAccessStatus()
  181. << " => " << access_status;
  182. pref_service_->SetInteger(prefs::kCameraRollAccessStatus,
  183. static_cast<int>(access_status));
  184. NotifyCameraRollAccessChanged();
  185. if (!IsCombinedSetupOperationInProgress()) {
  186. return;
  187. }
  188. switch (access_status) {
  189. case AccessStatus::kProhibited:
  190. SetCombinedSetupOperationStatus(
  191. CombinedAccessSetupOperation::Status::kProhibitedFromProvidingAccess);
  192. break;
  193. case AccessStatus::kAccessGranted:
  194. combined_setup_camera_roll_pending_ = false;
  195. break;
  196. case AccessStatus::kAvailableButNotGranted:
  197. // Intentionally blank; the operation status should not change.
  198. break;
  199. }
  200. if (!combined_setup_notifications_pending_ &&
  201. !combined_setup_camera_roll_pending_) {
  202. SetCombinedSetupOperationStatus(
  203. CombinedAccessSetupOperation::Status::kCompletedSuccessfully);
  204. }
  205. }
  206. void MultideviceFeatureAccessManagerImpl::
  207. SetFeatureSetupRequestSupportedInternal(bool supported) {
  208. pref_service_->SetBoolean(prefs::kFeatureSetupRequestSupported, supported);
  209. NotifyFeatureSetupRequestSupportedChanged();
  210. }
  211. void MultideviceFeatureAccessManagerImpl::OnNotificationSetupRequested() {
  212. PA_LOG(INFO) << "Notification access setup flow started.";
  213. switch (feature_status_provider_->GetStatus()) {
  214. // We're already connected, so request that the UI be shown on the phone.
  215. case FeatureStatus::kEnabledAndConnected:
  216. SendShowNotificationAccessSetupRequest();
  217. break;
  218. // We're already connecting, so wait until a connection succeeds before
  219. // trying to send a message
  220. case FeatureStatus::kEnabledAndConnecting:
  221. SetNotificationSetupOperationStatus(
  222. NotificationAccessSetupOperation::Status::kConnecting);
  223. break;
  224. // We are not connected, so schedule a connection; once the
  225. // connection succeeds, we'll send the message in OnFeatureStatusChanged().
  226. case FeatureStatus::kEnabledButDisconnected:
  227. SetNotificationSetupOperationStatus(
  228. NotificationAccessSetupOperation::Status::kConnecting);
  229. connection_scheduler_->ScheduleConnectionNow();
  230. break;
  231. default:
  232. NOTREACHED();
  233. break;
  234. }
  235. }
  236. void MultideviceFeatureAccessManagerImpl::OnCombinedSetupRequested(
  237. bool camera_roll,
  238. bool notifications) {
  239. combined_setup_camera_roll_pending_ = camera_roll;
  240. combined_setup_notifications_pending_ = notifications;
  241. PA_LOG(INFO) << "Combined access setup flow started.";
  242. switch (feature_status_provider_->GetStatus()) {
  243. // We're already connected, so request that the UI be shown on the phone.
  244. case FeatureStatus::kEnabledAndConnected:
  245. SendShowCombinedAccessSetupRequest();
  246. break;
  247. // We're already connecting, so wait until a connection succeeds before
  248. // trying to send a message
  249. case FeatureStatus::kEnabledAndConnecting:
  250. SetCombinedSetupOperationStatus(
  251. CombinedAccessSetupOperation::Status::kConnecting);
  252. break;
  253. // We are not connected, so schedule a connection; once the
  254. // connection succeeds, we'll send the message in OnFeatureStatusChanged().
  255. case FeatureStatus::kEnabledButDisconnected:
  256. SetCombinedSetupOperationStatus(
  257. CombinedAccessSetupOperation::Status::kConnecting);
  258. connection_scheduler_->ScheduleConnectionNow();
  259. break;
  260. default:
  261. NOTREACHED();
  262. break;
  263. }
  264. }
  265. void MultideviceFeatureAccessManagerImpl::OnFeatureStatusChanged() {
  266. if (IsNotificationSetupOperationInProgress()) {
  267. FeatureStatusChangedNotificationAccessSetup();
  268. } else if (IsCombinedSetupOperationInProgress()) {
  269. FeatureStatusChangedCombinedAccessSetup();
  270. }
  271. }
  272. void MultideviceFeatureAccessManagerImpl::
  273. FeatureStatusChangedNotificationAccessSetup() {
  274. const FeatureStatus previous_feature_status = current_feature_status_;
  275. current_feature_status_ = feature_status_provider_->GetStatus();
  276. PA_LOG(VERBOSE) << __func__
  277. << ": previous feature status = " << previous_feature_status
  278. << ", current feature status = " << current_feature_status_;
  279. if (previous_feature_status == current_feature_status_)
  280. return;
  281. // If we were previously connecting and could not establish a connection,
  282. // send a timeout state.
  283. if (previous_feature_status == FeatureStatus::kEnabledAndConnecting &&
  284. current_feature_status_ != FeatureStatus::kEnabledAndConnected) {
  285. SetNotificationSetupOperationStatus(
  286. NotificationAccessSetupOperation::Status::kTimedOutConnecting);
  287. return;
  288. }
  289. // If we were previously connected and are now no longer connected, send a
  290. // connection disconnected state.
  291. if (previous_feature_status == FeatureStatus::kEnabledAndConnected &&
  292. current_feature_status_ != FeatureStatus::kEnabledAndConnected) {
  293. SetNotificationSetupOperationStatus(
  294. NotificationAccessSetupOperation::Status::kConnectionDisconnected);
  295. return;
  296. }
  297. if (current_feature_status_ == FeatureStatus::kEnabledAndConnected) {
  298. SendShowNotificationAccessSetupRequest();
  299. return;
  300. }
  301. }
  302. void MultideviceFeatureAccessManagerImpl::
  303. FeatureStatusChangedCombinedAccessSetup() {
  304. const FeatureStatus previous_feature_status = current_feature_status_;
  305. current_feature_status_ = feature_status_provider_->GetStatus();
  306. PA_LOG(VERBOSE) << __func__
  307. << ": previous feature status = " << previous_feature_status
  308. << ", current feature status = " << current_feature_status_;
  309. if (previous_feature_status == current_feature_status_)
  310. return;
  311. // If we were previously connecting and could not establish a connection,
  312. // send a timeout state.
  313. if (previous_feature_status == FeatureStatus::kEnabledAndConnecting &&
  314. current_feature_status_ != FeatureStatus::kEnabledAndConnected) {
  315. SetCombinedSetupOperationStatus(
  316. CombinedAccessSetupOperation::Status::kTimedOutConnecting);
  317. return;
  318. }
  319. // If we were previously connected and are now no longer connected, send a
  320. // connection disconnected state.
  321. if (previous_feature_status == FeatureStatus::kEnabledAndConnected &&
  322. current_feature_status_ != FeatureStatus::kEnabledAndConnected) {
  323. SetCombinedSetupOperationStatus(
  324. CombinedAccessSetupOperation::Status::kConnectionDisconnected);
  325. return;
  326. }
  327. if (current_feature_status_ == FeatureStatus::kEnabledAndConnected) {
  328. SendShowCombinedAccessSetupRequest();
  329. return;
  330. }
  331. }
  332. void MultideviceFeatureAccessManagerImpl::
  333. SendShowNotificationAccessSetupRequest() {
  334. message_sender_->SendShowNotificationAccessSetupRequest();
  335. SetNotificationSetupOperationStatus(
  336. NotificationAccessSetupOperation::Status::
  337. kSentMessageToPhoneAndWaitingForResponse);
  338. }
  339. void MultideviceFeatureAccessManagerImpl::SendShowCombinedAccessSetupRequest() {
  340. message_sender_->SendFeatureSetupRequest(
  341. combined_setup_camera_roll_pending_,
  342. combined_setup_notifications_pending_);
  343. SetCombinedSetupOperationStatus(CombinedAccessSetupOperation::Status::
  344. kSentMessageToPhoneAndWaitingForResponse);
  345. }
  346. bool MultideviceFeatureAccessManagerImpl::HasAccessStatusChanged(
  347. AccessStatus access_status,
  348. AccessProhibitedReason reason) {
  349. if (access_status != GetNotificationAccessStatus())
  350. return true;
  351. if (access_status == AccessStatus::kProhibited &&
  352. reason != GetNotificationAccessProhibitedReason()) {
  353. return true;
  354. }
  355. return false;
  356. }
  357. } // namespace phonehub
  358. } // namespace ash