1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/display/display_prefs.h"
11 #include "ash/constants/ash_pref_names.h"
12 #include "ash/constants/ash_switches.h"
13 #include "ash/session/session_controller_impl.h"
14 #include "ash/shell.h"
15 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
16 #include "base/check_is_test.h"
17 #include "base/command_line.h"
18 #include "base/containers/contains.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/system/sys_info.h"
23 #include "base/values.h"
24 #include "components/prefs/pref_registry_simple.h"
25 #include "components/prefs/pref_service.h"
26 #include "components/prefs/scoped_user_pref_update.h"
27 #include "third_party/cros_system_api/dbus/service_constants.h"
28 #include "ui/display/display_features.h"
29 #include "ui/display/display_switches.h"
30 #include "ui/display/manager/display_layout_store.h"
31 #include "ui/display/manager/display_manager.h"
32 #include "ui/display/manager/json_converter.h"
33 #include "ui/display/manager/util/display_manager_util.h"
34 #include "ui/display/types/display_constants.h"
35 #include "ui/display/util/display_util.h"
36 #include "ui/gfx/geometry/insets.h"
37 #include "url/url_canon.h"
38 #include "url/url_util.h"
40 using chromeos::DisplayPowerState;
46 constexpr char kInsetsTopKey[] = "insets_top";
47 constexpr char kInsetsLeftKey[] = "insets_left";
48 constexpr char kInsetsBottomKey[] = "insets_bottom";
49 constexpr char kInsetsRightKey[] = "insets_right";
51 constexpr char kTouchCalibrationWidth[] = "touch_calibration_width";
52 constexpr char kTouchCalibrationHeight[] = "touch_calibration_height";
53 constexpr char kTouchCalibrationPointPairs[] = "touch_calibration_point_pairs";
55 constexpr char kTouchAssociationTimestamp[] = "touch_association_timestamp";
56 constexpr char kTouchAssociationCalibrationData[] =
57 "touch_association_calibration_data";
59 constexpr char kTouchDeviceIdentifier[] = "touch_device_identifer";
60 constexpr char kPortAssociationDisplayId[] = "port_association_display_id";
62 constexpr char kMirroringSourceId[] = "mirroring_source_id";
63 constexpr char kMirroringDestinationIds[] = "mirroring_destination_ids";
65 constexpr char kDisplayZoom[] = "display_zoom_factor";
67 constexpr char kDisplayPowerAllOn[] = "all_on";
68 constexpr char kDisplayPowerInternalOffExternalOn[] =
69 "internal_off_external_on";
70 constexpr char kDisplayPowerInternalOnExternalOff[] =
71 "internal_on_external_off";
73 constexpr char kVariableRefreshRateState[] = "vrr_state";
74 constexpr char kVsyncRateMin[] = "vsync_rate_min";
76 // This kind of boilerplates should be done by base::JSONValueConverter but it
77 // doesn't support classes like gfx::Insets for now.
78 // TODO(mukai): fix base::JSONValueConverter and use it here.
79 bool ValueToInsets(const base::Value::Dict& dict, gfx::Insets* insets) {
82 absl::optional<int> top = dict.FindInt(kInsetsTopKey);
83 absl::optional<int> left = dict.FindInt(kInsetsLeftKey);
84 absl::optional<int> bottom = dict.FindInt(kInsetsBottomKey);
85 absl::optional<int> right = dict.FindInt(kInsetsRightKey);
86 if (top && left && bottom && right) {
87 *insets = gfx::Insets::TLBR(*top, *left, *bottom, *right);
93 void InsetsToValue(const gfx::Insets& insets, base::Value::Dict& dict) {
94 dict.Set(kInsetsTopKey, insets.top());
95 dict.Set(kInsetsLeftKey, insets.left());
96 dict.Set(kInsetsBottomKey, insets.bottom());
97 dict.Set(kInsetsRightKey, insets.right());
100 // Unmarshalls the string containing CalibrationPointPairQuad and populates
101 // |point_pair_quad| with the unmarshalled data.
102 bool ParseTouchCalibrationStringValue(
103 const std::string& str,
104 display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) {
105 DCHECK(point_pair_quad);
107 std::vector<std::string> parts = base::SplitString(
108 str, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
109 size_t total = point_pair_quad->size();
110 gfx::Point display_point, touch_point;
111 for (std::size_t row = 0; row < total; row++) {
112 if (!base::StringToInt(parts[row * total], &x) ||
113 !base::StringToInt(parts[row * total + 1], &y)) {
116 display_point.SetPoint(x, y);
118 if (!base::StringToInt(parts[row * total + 2], &x) ||
119 !base::StringToInt(parts[row * total + 3], &y)) {
122 touch_point.SetPoint(x, y);
124 (*point_pair_quad)[row] = std::make_pair(display_point, touch_point);
129 // Retrieves touch calibration associated data from the dictionary and stores
130 // it in an instance of TouchCalibrationData struct.
131 bool ValueToTouchData(const base::Value::Dict& dict,
132 display::TouchCalibrationData* touch_calibration_data) {
133 display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad =
134 &(touch_calibration_data->point_pairs);
136 const std::string* str = dict.FindString(kTouchCalibrationPointPairs);
140 if (!ParseTouchCalibrationStringValue(*str, point_pair_quad))
143 absl::optional<int> width = dict.FindInt(kTouchCalibrationWidth);
144 absl::optional<int> height = dict.FindInt(kTouchCalibrationHeight);
145 if (!width || !height) {
148 touch_calibration_data->bounds = gfx::Size(*width, *height);
152 // Stores the touch calibration data into the dictionary.
153 void TouchDataToValue(
154 const display::TouchCalibrationData& touch_calibration_data,
155 base::Value::Dict& dict) {
157 for (std::size_t row = 0; row < touch_calibration_data.point_pairs.size();
159 str += base::NumberToString(
160 touch_calibration_data.point_pairs[row].first.x()) +
162 str += base::NumberToString(
163 touch_calibration_data.point_pairs[row].first.y()) +
165 str += base::NumberToString(
166 touch_calibration_data.point_pairs[row].second.x()) +
168 str += base::NumberToString(
169 touch_calibration_data.point_pairs[row].second.y());
170 if (row != touch_calibration_data.point_pairs.size() - 1)
173 dict.Set(kTouchCalibrationPointPairs, str);
174 dict.Set(kTouchCalibrationWidth, touch_calibration_data.bounds.width());
175 dict.Set(kTouchCalibrationHeight, touch_calibration_data.bounds.height());
178 display::DisplayManager* GetDisplayManager() {
179 return Shell::Get()->display_manager();
182 // Returns true if the current user can write display preferences to
184 bool UserCanSaveDisplayPreference() {
185 SessionControllerImpl* controller = Shell::Get()->session_controller();
186 auto user_type = controller->GetUserType();
190 return *user_type == user_manager::USER_TYPE_REGULAR ||
191 *user_type == user_manager::USER_TYPE_CHILD ||
192 *user_type == user_manager::USER_TYPE_KIOSK_APP ||
193 (*user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT &&
194 Shell::Get()->local_state()->GetBoolean(
195 prefs::kAllowMGSToStoreDisplayProperties));
198 void LoadDisplayLayouts(PrefService* local_state) {
199 display::DisplayLayoutStore* layout_store =
200 GetDisplayManager()->layout_store();
202 for (const auto it : local_state->GetDict(prefs::kSecondaryDisplays)) {
203 std::unique_ptr<display::DisplayLayout> layout(new display::DisplayLayout);
204 if (!display::JsonToDisplayLayout(it.second, layout.get())) {
205 LOG(WARNING) << "Invalid preference value for " << it.first;
209 if (base::Contains(it.first, ",")) {
210 std::vector<std::string> ids_str = base::SplitString(
211 it.first, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
212 std::vector<int64_t> ids;
213 for (std::string id_str : ids_str) {
215 if (!base::StringToInt64(id_str, &id))
219 display::DisplayIdList list = display::GenerateDisplayIdList(ids);
220 layout_store->RegisterLayoutForDisplayIdList(list, std::move(layout));
225 void LoadDisplayProperties(PrefService* local_state) {
226 for (const auto it : local_state->GetDict(prefs::kDisplayProperties)) {
227 const base::Value::Dict* dict_value = it.second.GetIfDict();
230 int64_t id = display::kInvalidDisplayId;
231 if (!base::StringToInt64(it.first, &id) ||
232 id == display::kInvalidDisplayId) {
235 const gfx::Insets* insets_to_set = nullptr;
237 display::Display::Rotation rotation = display::Display::ROTATE_0;
238 if (absl::optional<int> rotation_value = dict_value->FindInt("rotation")) {
239 rotation = static_cast<display::Display::Rotation>(*rotation_value);
242 int width = dict_value->FindInt("width").value_or(0);
243 int height = dict_value->FindInt("height").value_or(0);
244 gfx::Size resolution_in_pixels(width, height);
246 float device_scale_factor = 1.0;
247 if (absl::optional<int> dsf_value =
248 dict_value->FindInt("device-scale-factor")) {
249 device_scale_factor = static_cast<float>(*dsf_value) / 1000.0f;
252 // Default refresh rate is 60 Hz, until
253 // DisplayManager::OnNativeDisplaysChanged() updates us with the actual
255 double refresh_rate = 60.0;
256 bool is_interlaced = false;
257 if (display::features::IsListAllDisplayModesEnabled()) {
259 dict_value->FindDouble("refresh-rate").value_or(refresh_rate);
260 absl::optional<bool> is_interlaced_opt =
261 dict_value->FindBool("interlaced");
262 is_interlaced = is_interlaced_opt.value_or(false);
266 if (ValueToInsets(*dict_value, &insets))
267 insets_to_set = &insets;
269 double display_zoom = dict_value->FindDouble(kDisplayZoom).value_or(1.0);
271 display::VariableRefreshRateState variable_refresh_rate_state =
272 display::kVrrNotCapable;
273 if (absl::optional<int> vrr_state_value =
274 dict_value->FindInt(kVariableRefreshRateState)) {
275 variable_refresh_rate_state =
276 static_cast<display::VariableRefreshRateState>(*vrr_state_value);
278 absl::optional<float> vsync_rate_min =
279 dict_value->FindDouble(kVsyncRateMin);
281 GetDisplayManager()->RegisterDisplayProperty(
282 id, rotation, insets_to_set, resolution_in_pixels, device_scale_factor,
283 display_zoom, refresh_rate, is_interlaced, variable_refresh_rate_state,
288 void LoadDisplayRotationState(PrefService* local_state) {
289 const base::Value::Dict& properties =
290 local_state->GetDict(prefs::kDisplayRotationLock);
291 const absl::optional<bool> rotation_lock = properties.FindBool("lock");
295 const absl::optional<int> rotation = properties.FindInt("orientation");
299 GetDisplayManager()->RegisterDisplayRotationProperties(
300 *rotation_lock, static_cast<display::Display::Rotation>(*rotation));
303 void LoadDisplayTouchAssociations(PrefService* local_state) {
304 display::TouchDeviceManager::TouchAssociationMap touch_associations;
305 for (const auto item :
306 local_state->GetDict(prefs::kDisplayTouchAssociations)) {
307 uint32_t identifier_raw;
308 if (!base::StringToUint(item.first, &identifier_raw))
310 display::TouchDeviceIdentifier identifier(identifier_raw);
311 touch_associations.emplace(
312 identifier, display::TouchDeviceManager::AssociationInfoMap());
313 if (!item.second.is_dict())
315 for (const auto association_info_item : item.second.GetDict()) {
316 display::TouchDeviceManager::TouchAssociationInfo info;
318 if (!base::StringToInt64(association_info_item.first, &display_id))
320 absl::optional<double> value =
321 association_info_item.second.GetDict().FindDouble(
322 kTouchAssociationTimestamp);
325 info.timestamp = base::Time().FromSecondsSinceUnixEpoch(*value);
327 const base::Value::Dict* calibration_data_dict =
328 association_info_item.second.GetDict().FindDict(
329 kTouchAssociationCalibrationData);
330 if (!calibration_data_dict)
332 ValueToTouchData(*calibration_data_dict, &info.calibration_data);
333 touch_associations.at(identifier).emplace(display_id, info);
337 // Retrieve all the legacy format identifiers. This should be removed after
338 // a couple of milestones when everything is stable.
339 const display::TouchDeviceIdentifier& fallback_identifier =
340 display::TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier();
341 for (const auto it : local_state->GetDict(prefs::kDisplayProperties)) {
342 const base::Value::Dict* dict_value = it.second.GetIfDict();
345 int64_t id = display::kInvalidDisplayId;
346 if (!base::StringToInt64(it.first, &id) ||
347 id == display::kInvalidDisplayId) {
350 display::TouchCalibrationData calibration_data;
351 display::TouchCalibrationData* calibration_data_to_set = nullptr;
352 if (ValueToTouchData(*dict_value, &calibration_data))
353 calibration_data_to_set = &calibration_data;
355 if (calibration_data_to_set) {
356 if (!base::Contains(touch_associations, fallback_identifier)) {
357 touch_associations.emplace(
359 display::TouchDeviceManager::AssociationInfoMap());
361 display::TouchDeviceManager::TouchAssociationInfo info;
362 info.calibration_data = *calibration_data_to_set;
363 touch_associations.at(fallback_identifier).emplace(id, info);
367 // Retrieve port association information.
368 display::TouchDeviceManager::PortAssociationMap port_associations;
369 for (const auto item :
370 local_state->GetDict(prefs::kDisplayTouchPortAssociations)) {
371 // Retrieve the secondary id that identifies the port.
372 uint32_t secondary_id_raw;
373 if (!base::StringToUint(item.first, &secondary_id_raw))
376 if (!item.second.is_dict())
379 // Retrieve the touch device identifier that identifies the touch device.
380 const std::string* value =
381 item.second.GetDict().FindString(kTouchDeviceIdentifier);
384 uint32_t identifier_raw;
385 if (!base::StringToUint(*value, &identifier_raw))
388 // Retrieve the display that the touch device identified by |identifier_raw|
389 // was associated with.
390 value = item.second.GetDict().FindString(kPortAssociationDisplayId);
394 if (!base::StringToInt64(*value, &display_id))
397 port_associations.emplace(
398 std::piecewise_construct,
399 std::forward_as_tuple(identifier_raw, secondary_id_raw),
400 std::forward_as_tuple(display_id));
403 GetDisplayManager()->touch_device_manager()->RegisterTouchAssociations(
404 touch_associations, port_associations);
407 // Loads mirror info for each external display, the info will later be used to
408 // restore mirror mode.
409 void LoadExternalDisplayMirrorInfo(PrefService* local_state) {
410 const base::Value::List& pref_data =
411 local_state->GetList(prefs::kExternalDisplayMirrorInfo);
412 std::set<int64_t> external_display_mirror_info;
413 for (const auto& it : pref_data) {
414 const std::string* display_id_str = it.GetIfString();
419 if (!base::StringToInt64(*display_id_str, &display_id))
422 external_display_mirror_info.emplace(display_id);
424 GetDisplayManager()->set_external_display_mirror_info(
425 external_display_mirror_info);
428 // Loads mixed mirror mode parameters which will later be used to restore mixed
429 // mirror mode. Return false if the parameters fail to be loaded.
430 void LoadDisplayMixedMirrorModeParams(PrefService* local_state) {
431 const base::Value::Dict& pref_data =
432 local_state->GetDict(prefs::kDisplayMixedMirrorModeParams);
434 // This function is called once for system (re)start, so the parameters should
436 DCHECK(!GetDisplayManager()->mixed_mirror_mode_params());
438 auto* mirroring_source_id_string = pref_data.FindString(kMirroringSourceId);
439 if (!mirroring_source_id_string)
442 int64_t mirroring_source_id;
443 if (!base::StringToInt64(*mirroring_source_id_string, &mirroring_source_id)) {
447 auto* mirroring_destination_ids_list =
448 pref_data.FindList(kMirroringDestinationIds);
449 if (!mirroring_destination_ids_list)
452 display::DisplayIdList mirroring_destination_ids;
453 for (const auto& entry : *mirroring_destination_ids_list) {
455 if (!base::StringToInt64(entry.GetString(), &id))
457 mirroring_destination_ids.emplace_back(id);
460 GetDisplayManager()->set_mixed_mirror_mode_params(
461 absl::optional<display::MixedMirrorModeParams>(
462 absl::in_place, mirroring_source_id, mirroring_destination_ids));
465 void StoreDisplayLayoutPref(PrefService* pref_service,
466 const display::DisplayIdList& list,
467 const display::DisplayLayout& display_layout) {
468 DCHECK(display::DisplayLayout::Validate(list, display_layout));
469 std::string name = display::DisplayIdListToString(list);
471 ScopedDictPrefUpdate update(pref_service, prefs::kSecondaryDisplays);
472 base::Value::Dict* layout_dict = update->EnsureDict(name);
473 // This call modifies `layout_dict` in place.
474 display::DisplayLayoutToJson(display_layout, *layout_dict);
477 void StoreCurrentDisplayLayoutPrefs(PrefService* pref_service) {
478 display::DisplayManager* display_manager = GetDisplayManager();
479 if (!UserCanSaveDisplayPreference() ||
480 display_manager->num_connected_displays() < 2) {
484 display::DisplayIdList list = display_manager->GetConnectedDisplayIdList();
485 const display::DisplayLayout& display_layout =
486 display_manager->layout_store()->GetRegisteredDisplayLayout(list);
488 if (!display::DisplayLayout::Validate(list, display_layout)) {
489 // We should never apply an invalid layout, if we do, it persists and the
490 // user has no way of fixing it except by deleting the local state.
491 LOG(ERROR) << "Attempting to store an invalid display layout in the local"
492 << " state. Skipping.";
496 StoreDisplayLayoutPref(pref_service, list, display_layout);
499 void StoreCurrentDisplayProperties(PrefService* pref_service) {
500 display::DisplayManager* display_manager = GetDisplayManager();
502 ScopedDictPrefUpdate update(pref_service, prefs::kDisplayProperties);
503 base::Value::Dict& pref_data = update.Get();
505 // Pre-process data related to legacy touch calibration to opitmize lookup.
506 const display::TouchDeviceIdentifier& fallback_identifier =
507 display::TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier();
508 display::TouchDeviceManager::AssociationInfoMap legacy_data_map;
510 display_manager->touch_device_manager()->touch_associations(),
511 fallback_identifier)) {
513 display_manager->touch_device_manager()->touch_associations().at(
514 fallback_identifier);
517 size_t num = display_manager->GetNumDisplays();
518 for (size_t i = 0; i < num; ++i) {
519 const display::Display& display = display_manager->GetDisplayAt(i);
520 int64_t id = display.id();
521 display::ManagedDisplayInfo info = display_manager->GetDisplayInfo(id);
523 base::Value::Dict property_value;
524 // Don't save the display preference in unified mode because its
525 // size and modes can change depending on the combination of displays.
526 if (display_manager->IsInUnifiedMode())
528 // Don't save rotation when in tablet mode, so that if the device is
529 // rebooted into clamshell mode, it won't have an unexpected rotation.
530 // https://crbug.com/733092.
531 // But we should keep any original value so that it can be restored when
532 // exiting tablet mode.
533 if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
534 const base::Value::Dict* original_property =
535 pref_data.FindDict(base::NumberToString(id));
536 if (original_property) {
537 absl::optional<int> original_rotation =
538 original_property->FindInt("rotation");
539 if (original_rotation) {
540 property_value.Set("rotation", *original_rotation);
544 property_value.Set("rotation",
545 static_cast<int>(info.GetRotation(
546 display::Display::RotationSource::USER)));
549 display::ManagedDisplayMode mode;
550 if (!display.IsInternal() &&
551 display_manager->GetSelectedModeForDisplayId(id, &mode) &&
553 property_value.Set("width", mode.size().width());
554 property_value.Set("height", mode.size().height());
555 property_value.Set("device-scale-factor",
556 static_cast<int>(mode.device_scale_factor() * 1000));
558 if (display::features::IsListAllDisplayModesEnabled()) {
559 property_value.Set("interlaced", mode.is_interlaced());
560 property_value.Set("refresh-rate", mode.refresh_rate());
563 if (!info.overscan_insets_in_dip().IsEmpty())
564 InsetsToValue(info.overscan_insets_in_dip(), property_value);
566 // Store the legacy format touch calibration data. This can be removed after
567 // a couple of milestones when every device has migrated to the new format.
568 if (legacy_data_map.size() && base::Contains(legacy_data_map, id)) {
569 TouchDataToValue(legacy_data_map.at(id).calibration_data, property_value);
572 property_value.Set(kDisplayZoom, info.zoom_factor());
574 property_value.Set(kVariableRefreshRateState,
575 info.variable_refresh_rate_state());
576 if (const absl::optional<float>& vsync_rate_min = info.vsync_rate_min()) {
577 property_value.Set(kVsyncRateMin, vsync_rate_min.value());
580 pref_data.Set(base::NumberToString(id), std::move(property_value));
584 bool GetDisplayPowerStateFromString(const std::string& state_string,
585 chromeos::DisplayPowerState* power_state) {
586 if (state_string == kDisplayPowerAllOn) {
587 *power_state = chromeos::DISPLAY_POWER_ALL_ON;
588 } else if (state_string == kDisplayPowerInternalOffExternalOn) {
589 *power_state = chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON;
590 } else if (state_string == kDisplayPowerInternalOnExternalOff) {
591 *power_state = chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF;
593 // Don't restore ALL_OFF state. http://crbug.com/318456.
599 void StoreDisplayPowerState(PrefService* pref_service,
600 DisplayPowerState power_state) {
601 const char* state_string = nullptr;
602 switch (power_state) {
603 case chromeos::DISPLAY_POWER_ALL_ON:
604 state_string = kDisplayPowerAllOn;
606 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
607 state_string = kDisplayPowerInternalOffExternalOn;
609 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
610 state_string = kDisplayPowerInternalOnExternalOff;
612 case chromeos::DISPLAY_POWER_ALL_OFF:
613 // Don't store ALL_OFF state. http://crbug.com/318456.
617 pref_service->Set(prefs::kDisplayPowerState, base::Value(state_string));
620 void StoreCurrentDisplayPowerState(PrefService* pref_service) {
621 StoreDisplayPowerState(
623 Shell::Get()->display_configurator()->GetRequestedPowerState());
626 void StoreDisplayRotationPrefs(PrefService* pref_service,
627 display::Display::Rotation rotation,
628 bool rotation_lock) {
629 ScopedDictPrefUpdate update(pref_service, prefs::kDisplayRotationLock);
630 update->Set("lock", rotation_lock);
631 update->Set("orientation", static_cast<int>(rotation));
634 void StoreCurrentDisplayRotationLockPrefs(PrefService* pref_service) {
635 if (!display::HasInternalDisplay())
637 display::Display::Rotation rotation =
639 ->GetDisplayInfo(display::Display::InternalDisplayId())
640 .GetRotation(display::Display::RotationSource::ACCELEROMETER);
641 bool rotation_lock = Shell::Get()
643 ->registered_internal_display_rotation_lock();
644 StoreDisplayRotationPrefs(pref_service, rotation, rotation_lock);
647 void StoreDisplayTouchAssociations(PrefService* pref_service) {
648 display::TouchDeviceManager* touch_device_manager =
649 GetDisplayManager()->touch_device_manager();
651 ScopedDictPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations);
652 base::Value::Dict& pref_data = update.Get();
655 const display::TouchDeviceManager::TouchAssociationMap& touch_associations =
656 touch_device_manager->touch_associations();
658 for (const auto& association : touch_associations) {
659 base::Value::Dict association_info_map_value;
660 for (const auto& association_info : association.second) {
661 // Iteration for each pair of <Display ID, TouchAssociationInfo>.
662 base::Value::Dict association_info_value;
664 // Parsing each member of TouchAssociationInfo and storing them in
665 // |association_info_value|.
667 // Serialize timestamp.
668 association_info_value.Set(
669 kTouchAssociationTimestamp,
670 association_info.second.timestamp.InSecondsFSinceUnixEpoch());
672 // Serialize TouchCalibrationData.
673 base::Value::Dict calibration_data_value;
674 TouchDataToValue(association_info.second.calibration_data,
675 calibration_data_value);
676 association_info_value.Set(kTouchAssociationCalibrationData,
677 std::move(calibration_data_value));
679 // Move the searialzed TouchAssociationInfo stored in
680 // |association_info_value| to |association_info_map_value| against the
681 // display id as key. This is a 1 to 1 mapping of a single entry from
682 // AssociationInfoMap to its serialized form.
683 association_info_map_value.Set(
684 base::NumberToString(association_info.first),
685 std::move(association_info_value));
687 if (association_info_map_value.empty())
690 // Move the already serialized entry of AssociationInfoMap from
691 // |association_info_map_value| to |pref_data| against the
692 // TouchDeviceIdentifier as key. This is a 1 to 1 mapping of a single entry
693 // from TouchAssociationMap to its serialized form.
694 pref_data.Set(association.first.ToString(),
695 std::move(association_info_map_value));
698 // Store the port mappings. What display a touch device connected to a
699 // particular port is associated with.
700 ScopedDictPrefUpdate update_port(pref_service,
701 prefs::kDisplayTouchPortAssociations);
702 base::Value::Dict& port_pref_data = update_port.Get();
703 port_pref_data.clear();
705 const display::TouchDeviceManager::PortAssociationMap& port_associations =
706 touch_device_manager->port_associations();
708 // For each port identified by the secondary id of TouchDeviceIdentifier,
709 // we store the touch device and the display associated with it.
710 for (const auto& association : port_associations) {
711 base::Value::Dict association_info_value;
712 association_info_value.Set(kTouchDeviceIdentifier,
713 association.first.ToString());
714 association_info_value.Set(kPortAssociationDisplayId,
715 base::NumberToString(association.second));
717 port_pref_data.Set(association.first.SecondaryIdToString(),
718 std::move(association_info_value));
722 // Stores mirror info for each external display.
723 void StoreExternalDisplayMirrorInfo(PrefService* pref_service) {
724 ScopedListPrefUpdate update(pref_service, prefs::kExternalDisplayMirrorInfo);
725 base::Value::List& pref_data = update.Get();
727 const std::set<int64_t>& external_display_mirror_info =
728 GetDisplayManager()->external_display_mirror_info();
729 for (const auto& id : external_display_mirror_info)
730 pref_data.Append(base::NumberToString(id));
733 // Stores mixed mirror mode parameters. Clear the preferences if
734 // |mixed_mirror_mode_params| is null.
735 void StoreDisplayMixedMirrorModeParams(
736 PrefService* pref_service,
737 const absl::optional<display::MixedMirrorModeParams>& mixed_params) {
738 ScopedDictPrefUpdate update(pref_service,
739 prefs::kDisplayMixedMirrorModeParams);
740 base::Value::Dict& pref_data = update.Get();
746 pref_data.Set(kMirroringSourceId,
747 base::NumberToString(mixed_params->source_id));
749 base::Value::List mirroring_destination_ids_list;
750 for (const auto& id : mixed_params->destination_ids) {
751 mirroring_destination_ids_list.Append(base::NumberToString(id));
753 pref_data.Set(kMirroringDestinationIds,
754 std::move(mirroring_destination_ids_list));
757 void StoreCurrentDisplayMixedMirrorModeParams(PrefService* pref_service) {
758 StoreDisplayMixedMirrorModeParams(
759 pref_service, GetDisplayManager()->mixed_mirror_mode_params());
765 void DisplayPrefs::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
766 registry->RegisterDictionaryPref(prefs::kSecondaryDisplays);
767 registry->RegisterDictionaryPref(prefs::kDisplayProperties);
768 registry->RegisterStringPref(prefs::kDisplayPowerState, kDisplayPowerAllOn);
769 registry->RegisterDictionaryPref(prefs::kDisplayRotationLock);
770 registry->RegisterDictionaryPref(prefs::kDisplayTouchAssociations);
771 registry->RegisterDictionaryPref(prefs::kDisplayTouchPortAssociations);
772 registry->RegisterListPref(prefs::kExternalDisplayMirrorInfo);
773 registry->RegisterDictionaryPref(prefs::kDisplayMixedMirrorModeParams);
774 registry->RegisterBooleanPref(prefs::kAllowMGSToStoreDisplayProperties,
778 DisplayPrefs::DisplayPrefs(PrefService* local_state)
779 : local_state_(local_state) {
780 Shell::Get()->session_controller()->AddObserver(this);
782 // |local_state_| could be null in tests.
784 LoadDisplayPreferences();
787 DisplayPrefs::~DisplayPrefs() {
788 Shell::Get()->session_controller()->RemoveObserver(this);
791 void DisplayPrefs::OnFirstSessionStarted() {
792 if (store_requested_)
793 MaybeStoreDisplayPrefs();
796 void DisplayPrefs::MaybeStoreDisplayPrefs() {
797 DCHECK(local_state_);
799 // Stores the power state regardless of the login status, because the power
800 // state respects to the current status (close/open) of the lid which can be
801 // changed in any situation. See http://crbug.com/285360
802 StoreCurrentDisplayPowerState(local_state_);
803 StoreCurrentDisplayRotationLockPrefs(local_state_);
805 // We cannot really decide whether to store display prefs until there is an
806 // active user session. |OnFirstSessionStarted()| should eventually attempt to
807 // do a store in this case.
808 if (!Shell::Get()->session_controller()->GetUserType()) {
809 store_requested_ = true;
813 // There are multiple scenarios where we don't want to save display prefs.
814 // Some user types are not allowed, we don't want to change them while a
815 // display change confirmation dialog is still visible, etc.
816 if (!UserCanSaveDisplayPreference() ||
817 !Shell::Get()->ShouldSaveDisplaySettings()) {
821 store_requested_ = false;
822 // Don't save certain display properties when in tablet mode, so if
823 // the device is rebooted in clamshell mode, it won't have an unexpected
824 // mirroring layout. https://crbug.com/733092.
825 if (!Shell::Get()->tablet_mode_controller()->InTabletMode()) {
826 StoreCurrentDisplayLayoutPrefs(local_state_);
827 StoreExternalDisplayMirrorInfo(local_state_);
828 StoreCurrentDisplayMixedMirrorModeParams(local_state_);
830 StoreCurrentDisplayProperties(local_state_);
831 StoreDisplayTouchAssociations(local_state_);
832 // The display prefs need to be committed immediately to guarantee they're not
833 // lost, and are restored properly on reboot. https://crbug.com/936884.
834 // This sends a request via mojo to commit the prefs to disk.
835 local_state_->CommitPendingWrite();
838 void DisplayPrefs::LoadDisplayPreferences() {
839 LoadDisplayLayouts(local_state_);
840 LoadDisplayProperties(local_state_);
841 LoadExternalDisplayMirrorInfo(local_state_);
842 LoadDisplayMixedMirrorModeParams(local_state_);
843 LoadDisplayRotationState(local_state_);
844 LoadDisplayTouchAssociations(local_state_);
846 // Now that the display prefs have been loaded, request to reconfigure the
847 // displays, but signal the display manager to restore the mirror state of
848 // external displays from the loaded prefs (if any).
851 ->set_should_restore_mirror_mode_from_display_prefs(true);
852 Shell::Get()->display_configurator()->OnConfigurationChanged();
854 // Ensure that we have a reasonable initial display power state if
855 // powerd fails to send us one over D-Bus. Otherwise, we won't restore
856 // displays correctly after retaking control when changing virtual terminals.
857 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
858 switches::kFirstExecAfterBoot)) {
859 Shell::Get()->display_configurator()->InitializeDisplayPowerState();
863 // Restore DisplayPowerState:
864 const std::string value =
865 local_state_->GetValue(prefs::kDisplayPowerState).GetString();
866 chromeos::DisplayPowerState power_state;
867 if (GetDisplayPowerStateFromString(value, &power_state))
868 Shell::Get()->display_configurator()->SetInitialDisplayPower(power_state);
871 void DisplayPrefs::StoreDisplayRotationPrefsForTest(
872 display::Display::Rotation rotation,
873 bool rotation_lock) {
874 StoreDisplayRotationPrefs(local_state_, rotation, rotation_lock);
877 void DisplayPrefs::StoreDisplayLayoutPrefForTest(
878 const display::DisplayIdList& list,
879 const display::DisplayLayout& layout) {
880 StoreDisplayLayoutPref(local_state_, list, layout);
883 void DisplayPrefs::StoreDisplayPowerStateForTest(
884 DisplayPowerState power_state) {
885 StoreDisplayPowerState(local_state_, power_state);
888 void DisplayPrefs::LoadTouchAssociationPreferenceForTest() {
889 LoadDisplayTouchAssociations(local_state_);
892 void DisplayPrefs::LoadDisplayPrefsForTest() {
894 LoadDisplayPreferences();
897 void DisplayPrefs::StoreLegacyTouchDataForTest(
899 const display::TouchCalibrationData& data) {
900 ScopedDictPrefUpdate update(local_state_, prefs::kDisplayProperties);
901 base::Value::Dict property_value;
902 TouchDataToValue(data, property_value);
903 update->Set(base::NumberToString(display_id), std::move(property_value));
906 bool DisplayPrefs::ParseTouchCalibrationStringForTest(
907 const std::string& str,
908 display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) {
909 return ParseTouchCalibrationStringValue(str, point_pair_quad);
912 void DisplayPrefs::StoreDisplayMixedMirrorModeParamsForTest(
913 const absl::optional<display::MixedMirrorModeParams>& mixed_params) {
914 StoreDisplayMixedMirrorModeParams(local_state_, mixed_params);