detachable_base_handler.cc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // 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. #include "ash/detachable_base/detachable_base_handler.h"
  5. #include "ash/constants/ash_pref_names.h"
  6. #include "ash/detachable_base/detachable_base_observer.h"
  7. #include "ash/public/cpp/session/user_info.h"
  8. #include "ash/shell.h"
  9. #include "base/bind.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/values.h"
  12. #include "components/account_id/account_id.h"
  13. #include "components/prefs/pref_registry_simple.h"
  14. #include "components/prefs/pref_service.h"
  15. #include "components/prefs/scoped_user_pref_update.h"
  16. namespace ash {
  17. namespace {
  18. // Keys in local state used to persist detachable base pairing state.
  19. // Example detachable base pref value:
  20. // {
  21. // ash.detachable_base.devices: {
  22. // user_key_1: {
  23. // last_used: hex encoded base device ID
  24. // },
  25. // user_key_2: {
  26. // // no detachable_base device info, e.g. the user has not ever used it
  27. // }
  28. // }
  29. // }
  30. constexpr char kLastUsedByUserPrefKey[] = "last_used";
  31. std::string GetKeyForPrefs(const AccountId& account_id) {
  32. if (account_id.HasAccountIdKey())
  33. return account_id.GetAccountIdKey();
  34. return account_id.GetUserEmail();
  35. }
  36. } // namespace
  37. // static
  38. void DetachableBaseHandler::RegisterPrefs(PrefRegistrySimple* registry) {
  39. registry->RegisterDictionaryPref(prefs::kDetachableBaseDevices);
  40. }
  41. DetachableBaseHandler::DetachableBaseHandler(PrefService* local_state)
  42. : local_state_(local_state),
  43. hammerd_observation_(this),
  44. power_manager_observation_(this) {
  45. if (HammerdClient::Get()) // May be null in tests
  46. hammerd_observation_.Observe(HammerdClient::Get());
  47. chromeos::PowerManagerClient* power_manager_client =
  48. chromeos::PowerManagerClient::Get();
  49. power_manager_observation_.Observe(power_manager_client);
  50. power_manager_client->GetSwitchStates(
  51. base::BindOnce(&DetachableBaseHandler::OnGotPowerManagerSwitchStates,
  52. weak_ptr_factory_.GetWeakPtr()));
  53. if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
  54. NotifyPairingStatusChanged();
  55. }
  56. DetachableBaseHandler::~DetachableBaseHandler() = default;
  57. void DetachableBaseHandler::AddObserver(DetachableBaseObserver* observer) {
  58. observers_.AddObserver(observer);
  59. }
  60. void DetachableBaseHandler::RemoveObserver(DetachableBaseObserver* observer) {
  61. observers_.RemoveObserver(observer);
  62. }
  63. void DetachableBaseHandler::RemoveUserData(const UserInfo& user) {
  64. last_used_devices_.erase(user.account_id);
  65. if (local_state_) {
  66. DictionaryPrefUpdate update(local_state_, prefs::kDetachableBaseDevices);
  67. update->RemoveKey(GetKeyForPrefs(user.account_id));
  68. }
  69. }
  70. DetachableBasePairingStatus DetachableBaseHandler::GetPairingStatus() const {
  71. if (!tablet_mode_.has_value() ||
  72. *tablet_mode_ == chromeos::PowerManagerClient::TabletMode::ON) {
  73. return DetachableBasePairingStatus::kNone;
  74. }
  75. return pairing_status_;
  76. }
  77. bool DetachableBaseHandler::PairedBaseMatchesLastUsedByUser(
  78. const UserInfo& user) const {
  79. if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
  80. return false;
  81. DCHECK(!authenticated_base_id_.empty());
  82. // If local state is not set for non-ephemeral users, the last used detachable
  83. // base cannot be determined at this point.
  84. // Assume no device has previously been used - the state will be evaluated
  85. // again when the local state prefs get initialzized.
  86. if (!local_state_ && !user.is_ephemeral)
  87. return true;
  88. DetachableBaseId last_used_base = GetLastUsedDeviceForUser(user);
  89. return last_used_base.empty() || authenticated_base_id_ == last_used_base;
  90. }
  91. bool DetachableBaseHandler::SetPairedBaseAsLastUsedByUser(
  92. const UserInfo& user) {
  93. if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
  94. return false;
  95. // Do not update the last paired device for non-ephemeral users if local state
  96. // is not set, as the value would not be correctly preserved. Note that the
  97. // observers will be notified about the pairing status change when the local
  98. // state gets initialized - they should attempt to set this value at that
  99. // point.
  100. if (!local_state_ && !user.is_ephemeral)
  101. return false;
  102. last_used_devices_[user.account_id] = authenticated_base_id_;
  103. if (!user.is_ephemeral) {
  104. DictionaryPrefUpdate update(local_state_, prefs::kDetachableBaseDevices);
  105. update->SetPath({GetKeyForPrefs(user.account_id), kLastUsedByUserPrefKey},
  106. base::Value(authenticated_base_id_));
  107. }
  108. return true;
  109. }
  110. void DetachableBaseHandler::BaseFirmwareUpdateNeeded() {
  111. NotifyBaseRequiresFirmwareUpdate(true /*requires_update*/);
  112. }
  113. void DetachableBaseHandler::BaseFirmwareUpdateStarted() {}
  114. void DetachableBaseHandler::BaseFirmwareUpdateSucceeded() {
  115. NotifyBaseRequiresFirmwareUpdate(false /*requires_update*/);
  116. }
  117. void DetachableBaseHandler::BaseFirmwareUpdateFailed() {}
  118. void DetachableBaseHandler::PairChallengeSucceeded(
  119. const std::vector<uint8_t>& base_id) {
  120. authenticated_base_id_ = base::HexEncode(base_id.data(), base_id.size());
  121. pairing_status_ = DetachableBasePairingStatus::kAuthenticated;
  122. if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
  123. NotifyPairingStatusChanged();
  124. }
  125. void DetachableBaseHandler::PairChallengeFailed() {
  126. authenticated_base_id_.clear();
  127. pairing_status_ = DetachableBasePairingStatus::kNotAuthenticated;
  128. if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
  129. NotifyPairingStatusChanged();
  130. }
  131. void DetachableBaseHandler::InvalidBaseConnected() {
  132. authenticated_base_id_.clear();
  133. pairing_status_ = DetachableBasePairingStatus::kInvalidDevice;
  134. if (GetPairingStatus() != DetachableBasePairingStatus::kNone)
  135. NotifyPairingStatusChanged();
  136. }
  137. void DetachableBaseHandler::TabletModeEventReceived(
  138. chromeos::PowerManagerClient::TabletMode mode,
  139. base::TimeTicks timestamp) {
  140. UpdateTabletMode(mode);
  141. }
  142. void DetachableBaseHandler::OnGotPowerManagerSwitchStates(
  143. absl::optional<chromeos::PowerManagerClient::SwitchStates> switch_states) {
  144. if (!switch_states.has_value() || tablet_mode_.has_value())
  145. return;
  146. UpdateTabletMode(switch_states->tablet_mode);
  147. }
  148. void DetachableBaseHandler::UpdateTabletMode(
  149. chromeos::PowerManagerClient::TabletMode mode) {
  150. DetachableBasePairingStatus old_pairing_status = GetPairingStatus();
  151. tablet_mode_ = mode;
  152. if (*tablet_mode_ == chromeos::PowerManagerClient::TabletMode::ON) {
  153. authenticated_base_id_.clear();
  154. pairing_status_ = DetachableBasePairingStatus::kNone;
  155. NotifyBaseRequiresFirmwareUpdate(false /*requires_update*/);
  156. }
  157. if (GetPairingStatus() != old_pairing_status)
  158. NotifyPairingStatusChanged();
  159. }
  160. DetachableBaseHandler::DetachableBaseId
  161. DetachableBaseHandler::GetLastUsedDeviceForUser(const UserInfo& user) const {
  162. const auto it = last_used_devices_.find(user.account_id);
  163. // If the last used device was set within this session, bypass local state.
  164. if (it != last_used_devices_.end())
  165. return it->second;
  166. // For ephemeral lookup do not attempt getting the value from local state;
  167. // return empty string instead.
  168. if (user.is_ephemeral)
  169. return "";
  170. const base::Value::Dict& detachable_base_info =
  171. local_state_->GetValueDict(prefs::kDetachableBaseDevices);
  172. const base::Value::Dict* account_info =
  173. detachable_base_info.FindDictByDottedPath(
  174. GetKeyForPrefs(user.account_id));
  175. if (!account_info)
  176. return "";
  177. const std::string* last_used =
  178. account_info->FindString(kLastUsedByUserPrefKey);
  179. return last_used ? *last_used : "";
  180. }
  181. void DetachableBaseHandler::NotifyPairingStatusChanged() {
  182. for (auto& observer : observers_)
  183. observer.OnDetachableBasePairingStatusChanged(GetPairingStatus());
  184. }
  185. void DetachableBaseHandler::NotifyBaseRequiresFirmwareUpdate(
  186. bool requires_update) {
  187. for (auto& observer : observers_)
  188. observer.OnDetachableBaseRequiresUpdateChanged(requires_update);
  189. }
  190. } // namespace ash