fixup! Fix for Geolocation webTCT failures
[platform/framework/web/chromium-efl.git] / ash / display / display_prefs.cc
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.
4
5 #include "ash/display/display_prefs.h"
6
7 #include <stddef.h>
8
9 #include <string>
10
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"
39
40 using chromeos::DisplayPowerState;
41
42 namespace ash {
43
44 namespace {
45
46 constexpr char kInsetsTopKey[] = "insets_top";
47 constexpr char kInsetsLeftKey[] = "insets_left";
48 constexpr char kInsetsBottomKey[] = "insets_bottom";
49 constexpr char kInsetsRightKey[] = "insets_right";
50
51 constexpr char kTouchCalibrationWidth[] = "touch_calibration_width";
52 constexpr char kTouchCalibrationHeight[] = "touch_calibration_height";
53 constexpr char kTouchCalibrationPointPairs[] = "touch_calibration_point_pairs";
54
55 constexpr char kTouchAssociationTimestamp[] = "touch_association_timestamp";
56 constexpr char kTouchAssociationCalibrationData[] =
57     "touch_association_calibration_data";
58
59 constexpr char kTouchDeviceIdentifier[] = "touch_device_identifer";
60 constexpr char kPortAssociationDisplayId[] = "port_association_display_id";
61
62 constexpr char kMirroringSourceId[] = "mirroring_source_id";
63 constexpr char kMirroringDestinationIds[] = "mirroring_destination_ids";
64
65 constexpr char kDisplayZoom[] = "display_zoom_factor";
66
67 constexpr char kDisplayPowerAllOn[] = "all_on";
68 constexpr char kDisplayPowerInternalOffExternalOn[] =
69     "internal_off_external_on";
70 constexpr char kDisplayPowerInternalOnExternalOff[] =
71     "internal_on_external_off";
72
73 constexpr char kVariableRefreshRateState[] = "vrr_state";
74 constexpr char kVsyncRateMin[] = "vsync_rate_min";
75
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) {
80   DCHECK(insets);
81
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);
88     return true;
89   }
90   return false;
91 }
92
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());
98 }
99
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);
106   int x = 0, y = 0;
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)) {
114       return false;
115     }
116     display_point.SetPoint(x, y);
117
118     if (!base::StringToInt(parts[row * total + 2], &x) ||
119         !base::StringToInt(parts[row * total + 3], &y)) {
120       return false;
121     }
122     touch_point.SetPoint(x, y);
123
124     (*point_pair_quad)[row] = std::make_pair(display_point, touch_point);
125   }
126   return true;
127 }
128
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);
135
136   const std::string* str = dict.FindString(kTouchCalibrationPointPairs);
137   if (!str)
138     return false;
139
140   if (!ParseTouchCalibrationStringValue(*str, point_pair_quad))
141     return false;
142
143   absl::optional<int> width = dict.FindInt(kTouchCalibrationWidth);
144   absl::optional<int> height = dict.FindInt(kTouchCalibrationHeight);
145   if (!width || !height) {
146     return false;
147   }
148   touch_calibration_data->bounds = gfx::Size(*width, *height);
149   return true;
150 }
151
152 // Stores the touch calibration data into the dictionary.
153 void TouchDataToValue(
154     const display::TouchCalibrationData& touch_calibration_data,
155     base::Value::Dict& dict) {
156   std::string str;
157   for (std::size_t row = 0; row < touch_calibration_data.point_pairs.size();
158        row++) {
159     str += base::NumberToString(
160                touch_calibration_data.point_pairs[row].first.x()) +
161            " ";
162     str += base::NumberToString(
163                touch_calibration_data.point_pairs[row].first.y()) +
164            " ";
165     str += base::NumberToString(
166                touch_calibration_data.point_pairs[row].second.x()) +
167            " ";
168     str += base::NumberToString(
169         touch_calibration_data.point_pairs[row].second.y());
170     if (row != touch_calibration_data.point_pairs.size() - 1)
171       str += " ";
172   }
173   dict.Set(kTouchCalibrationPointPairs, str);
174   dict.Set(kTouchCalibrationWidth, touch_calibration_data.bounds.width());
175   dict.Set(kTouchCalibrationHeight, touch_calibration_data.bounds.height());
176 }
177
178 display::DisplayManager* GetDisplayManager() {
179   return Shell::Get()->display_manager();
180 }
181
182 // Returns true if the current user can write display preferences to
183 // Local State.
184 bool UserCanSaveDisplayPreference() {
185   SessionControllerImpl* controller = Shell::Get()->session_controller();
186   auto user_type = controller->GetUserType();
187   if (!user_type)
188     return false;
189
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));
196 }
197
198 void LoadDisplayLayouts(PrefService* local_state) {
199   display::DisplayLayoutStore* layout_store =
200       GetDisplayManager()->layout_store();
201
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;
206       continue;
207     }
208
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) {
214         int64_t id;
215         if (!base::StringToInt64(id_str, &id))
216           continue;
217         ids.push_back(id);
218       }
219       display::DisplayIdList list = display::GenerateDisplayIdList(ids);
220       layout_store->RegisterLayoutForDisplayIdList(list, std::move(layout));
221     }
222   }
223 }
224
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();
228     if (!dict_value)
229       continue;
230     int64_t id = display::kInvalidDisplayId;
231     if (!base::StringToInt64(it.first, &id) ||
232         id == display::kInvalidDisplayId) {
233       continue;
234     }
235     const gfx::Insets* insets_to_set = nullptr;
236
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);
240     }
241
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);
245
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;
250     }
251
252     // Default refresh rate is 60 Hz, until
253     // DisplayManager::OnNativeDisplaysChanged() updates us with the actual
254     // display info.
255     double refresh_rate = 60.0;
256     bool is_interlaced = false;
257     if (display::features::IsListAllDisplayModesEnabled()) {
258       refresh_rate =
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);
263     }
264
265     gfx::Insets insets;
266     if (ValueToInsets(*dict_value, &insets))
267       insets_to_set = &insets;
268
269     double display_zoom = dict_value->FindDouble(kDisplayZoom).value_or(1.0);
270
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);
277     }
278     absl::optional<float> vsync_rate_min =
279         dict_value->FindDouble(kVsyncRateMin);
280
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,
284         vsync_rate_min);
285   }
286 }
287
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");
292   if (!rotation_lock)
293     return;
294
295   const absl::optional<int> rotation = properties.FindInt("orientation");
296   if (!rotation)
297     return;
298
299   GetDisplayManager()->RegisterDisplayRotationProperties(
300       *rotation_lock, static_cast<display::Display::Rotation>(*rotation));
301 }
302
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))
309       continue;
310     display::TouchDeviceIdentifier identifier(identifier_raw);
311     touch_associations.emplace(
312         identifier, display::TouchDeviceManager::AssociationInfoMap());
313     if (!item.second.is_dict())
314       continue;
315     for (const auto association_info_item : item.second.GetDict()) {
316       display::TouchDeviceManager::TouchAssociationInfo info;
317       int64_t display_id;
318       if (!base::StringToInt64(association_info_item.first, &display_id))
319         continue;
320       absl::optional<double> value =
321           association_info_item.second.GetDict().FindDouble(
322               kTouchAssociationTimestamp);
323       if (!value)
324         continue;
325       info.timestamp = base::Time().FromSecondsSinceUnixEpoch(*value);
326
327       const base::Value::Dict* calibration_data_dict =
328           association_info_item.second.GetDict().FindDict(
329               kTouchAssociationCalibrationData);
330       if (!calibration_data_dict)
331         continue;
332       ValueToTouchData(*calibration_data_dict, &info.calibration_data);
333       touch_associations.at(identifier).emplace(display_id, info);
334     }
335   }
336
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();
343     if (!dict_value)
344       continue;
345     int64_t id = display::kInvalidDisplayId;
346     if (!base::StringToInt64(it.first, &id) ||
347         id == display::kInvalidDisplayId) {
348       continue;
349     }
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;
354
355     if (calibration_data_to_set) {
356       if (!base::Contains(touch_associations, fallback_identifier)) {
357         touch_associations.emplace(
358             fallback_identifier,
359             display::TouchDeviceManager::AssociationInfoMap());
360       }
361       display::TouchDeviceManager::TouchAssociationInfo info;
362       info.calibration_data = *calibration_data_to_set;
363       touch_associations.at(fallback_identifier).emplace(id, info);
364     }
365   }
366
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))
374       continue;
375
376     if (!item.second.is_dict())
377       continue;
378
379     // Retrieve the touch device identifier that identifies the touch device.
380     const std::string* value =
381         item.second.GetDict().FindString(kTouchDeviceIdentifier);
382     if (!value)
383       continue;
384     uint32_t identifier_raw;
385     if (!base::StringToUint(*value, &identifier_raw))
386       continue;
387
388     // Retrieve the display that the touch device identified by |identifier_raw|
389     // was associated with.
390     value = item.second.GetDict().FindString(kPortAssociationDisplayId);
391     if (!value)
392       continue;
393     int64_t display_id;
394     if (!base::StringToInt64(*value, &display_id))
395       continue;
396
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));
401   }
402
403   GetDisplayManager()->touch_device_manager()->RegisterTouchAssociations(
404       touch_associations, port_associations);
405 }
406
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();
415     if (!display_id_str)
416       continue;
417
418     int64_t display_id;
419     if (!base::StringToInt64(*display_id_str, &display_id))
420       continue;
421
422     external_display_mirror_info.emplace(display_id);
423   }
424   GetDisplayManager()->set_external_display_mirror_info(
425       external_display_mirror_info);
426 }
427
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);
433
434   // This function is called once for system (re)start, so the parameters should
435   // be empty.
436   DCHECK(!GetDisplayManager()->mixed_mirror_mode_params());
437
438   auto* mirroring_source_id_string = pref_data.FindString(kMirroringSourceId);
439   if (!mirroring_source_id_string)
440     return;
441
442   int64_t mirroring_source_id;
443   if (!base::StringToInt64(*mirroring_source_id_string, &mirroring_source_id)) {
444     return;
445   }
446
447   auto* mirroring_destination_ids_list =
448       pref_data.FindList(kMirroringDestinationIds);
449   if (!mirroring_destination_ids_list)
450     return;
451
452   display::DisplayIdList mirroring_destination_ids;
453   for (const auto& entry : *mirroring_destination_ids_list) {
454     int64_t id;
455     if (!base::StringToInt64(entry.GetString(), &id))
456       return;
457     mirroring_destination_ids.emplace_back(id);
458   }
459
460   GetDisplayManager()->set_mixed_mirror_mode_params(
461       absl::optional<display::MixedMirrorModeParams>(
462           absl::in_place, mirroring_source_id, mirroring_destination_ids));
463 }
464
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);
470
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);
475 }
476
477 void StoreCurrentDisplayLayoutPrefs(PrefService* pref_service) {
478   display::DisplayManager* display_manager = GetDisplayManager();
479   if (!UserCanSaveDisplayPreference() ||
480       display_manager->num_connected_displays() < 2) {
481     return;
482   }
483
484   display::DisplayIdList list = display_manager->GetConnectedDisplayIdList();
485   const display::DisplayLayout& display_layout =
486       display_manager->layout_store()->GetRegisteredDisplayLayout(list);
487
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.";
493     return;
494   }
495
496   StoreDisplayLayoutPref(pref_service, list, display_layout);
497 }
498
499 void StoreCurrentDisplayProperties(PrefService* pref_service) {
500   display::DisplayManager* display_manager = GetDisplayManager();
501
502   ScopedDictPrefUpdate update(pref_service, prefs::kDisplayProperties);
503   base::Value::Dict& pref_data = update.Get();
504
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;
509   if (base::Contains(
510           display_manager->touch_device_manager()->touch_associations(),
511           fallback_identifier)) {
512     legacy_data_map =
513         display_manager->touch_device_manager()->touch_associations().at(
514             fallback_identifier);
515   }
516
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);
522
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())
527       continue;
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);
541         }
542       }
543     } else {
544       property_value.Set("rotation",
545                          static_cast<int>(info.GetRotation(
546                              display::Display::RotationSource::USER)));
547     }
548
549     display::ManagedDisplayMode mode;
550     if (!display.IsInternal() &&
551         display_manager->GetSelectedModeForDisplayId(id, &mode) &&
552         !mode.native()) {
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));
557
558       if (display::features::IsListAllDisplayModesEnabled()) {
559         property_value.Set("interlaced", mode.is_interlaced());
560         property_value.Set("refresh-rate", mode.refresh_rate());
561       }
562     }
563     if (!info.overscan_insets_in_dip().IsEmpty())
564       InsetsToValue(info.overscan_insets_in_dip(), property_value);
565
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);
570     }
571
572     property_value.Set(kDisplayZoom, info.zoom_factor());
573
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());
578     }
579
580     pref_data.Set(base::NumberToString(id), std::move(property_value));
581   }
582 }
583
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;
592   } else {
593     // Don't restore ALL_OFF state. http://crbug.com/318456.
594     return false;
595   }
596   return true;
597 }
598
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;
605       break;
606     case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
607       state_string = kDisplayPowerInternalOffExternalOn;
608       break;
609     case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
610       state_string = kDisplayPowerInternalOnExternalOff;
611       break;
612     case chromeos::DISPLAY_POWER_ALL_OFF:
613       // Don't store ALL_OFF state. http://crbug.com/318456.
614       break;
615   }
616   if (state_string)
617     pref_service->Set(prefs::kDisplayPowerState, base::Value(state_string));
618 }
619
620 void StoreCurrentDisplayPowerState(PrefService* pref_service) {
621   StoreDisplayPowerState(
622       pref_service,
623       Shell::Get()->display_configurator()->GetRequestedPowerState());
624 }
625
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));
632 }
633
634 void StoreCurrentDisplayRotationLockPrefs(PrefService* pref_service) {
635   if (!display::HasInternalDisplay())
636     return;
637   display::Display::Rotation rotation =
638       GetDisplayManager()
639           ->GetDisplayInfo(display::Display::InternalDisplayId())
640           .GetRotation(display::Display::RotationSource::ACCELEROMETER);
641   bool rotation_lock = Shell::Get()
642                            ->display_manager()
643                            ->registered_internal_display_rotation_lock();
644   StoreDisplayRotationPrefs(pref_service, rotation, rotation_lock);
645 }
646
647 void StoreDisplayTouchAssociations(PrefService* pref_service) {
648   display::TouchDeviceManager* touch_device_manager =
649       GetDisplayManager()->touch_device_manager();
650
651   ScopedDictPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations);
652   base::Value::Dict& pref_data = update.Get();
653   pref_data.clear();
654
655   const display::TouchDeviceManager::TouchAssociationMap& touch_associations =
656       touch_device_manager->touch_associations();
657
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;
663
664       // Parsing each member of TouchAssociationInfo and storing them in
665       // |association_info_value|.
666
667       // Serialize timestamp.
668       association_info_value.Set(
669           kTouchAssociationTimestamp,
670           association_info.second.timestamp.InSecondsFSinceUnixEpoch());
671
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));
678
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));
686     }
687     if (association_info_map_value.empty())
688       continue;
689
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));
696   }
697
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();
704
705   const display::TouchDeviceManager::PortAssociationMap& port_associations =
706       touch_device_manager->port_associations();
707
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));
716
717     port_pref_data.Set(association.first.SecondaryIdToString(),
718                        std::move(association_info_value));
719   }
720 }
721
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();
726   pref_data.clear();
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));
731 }
732
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();
741   pref_data.clear();
742
743   if (!mixed_params)
744     return;
745
746   pref_data.Set(kMirroringSourceId,
747                 base::NumberToString(mixed_params->source_id));
748
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));
752   }
753   pref_data.Set(kMirroringDestinationIds,
754                 std::move(mirroring_destination_ids_list));
755 }
756
757 void StoreCurrentDisplayMixedMirrorModeParams(PrefService* pref_service) {
758   StoreDisplayMixedMirrorModeParams(
759       pref_service, GetDisplayManager()->mixed_mirror_mode_params());
760 }
761
762 }  // namespace
763
764 // static
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,
775                                 false);
776 }
777
778 DisplayPrefs::DisplayPrefs(PrefService* local_state)
779     : local_state_(local_state) {
780   Shell::Get()->session_controller()->AddObserver(this);
781
782   // |local_state_| could be null in tests.
783   if (local_state_)
784     LoadDisplayPreferences();
785 }
786
787 DisplayPrefs::~DisplayPrefs() {
788   Shell::Get()->session_controller()->RemoveObserver(this);
789 }
790
791 void DisplayPrefs::OnFirstSessionStarted() {
792   if (store_requested_)
793     MaybeStoreDisplayPrefs();
794 }
795
796 void DisplayPrefs::MaybeStoreDisplayPrefs() {
797   DCHECK(local_state_);
798
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_);
804
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;
810     return;
811   }
812
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()) {
818     return;
819   }
820
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_);
829   }
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();
836 }
837
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_);
845
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).
849   Shell::Get()
850       ->display_manager()
851       ->set_should_restore_mirror_mode_from_display_prefs(true);
852   Shell::Get()->display_configurator()->OnConfigurationChanged();
853
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();
860     return;
861   }
862
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);
869 }
870
871 void DisplayPrefs::StoreDisplayRotationPrefsForTest(
872     display::Display::Rotation rotation,
873     bool rotation_lock) {
874   StoreDisplayRotationPrefs(local_state_, rotation, rotation_lock);
875 }
876
877 void DisplayPrefs::StoreDisplayLayoutPrefForTest(
878     const display::DisplayIdList& list,
879     const display::DisplayLayout& layout) {
880   StoreDisplayLayoutPref(local_state_, list, layout);
881 }
882
883 void DisplayPrefs::StoreDisplayPowerStateForTest(
884     DisplayPowerState power_state) {
885   StoreDisplayPowerState(local_state_, power_state);
886 }
887
888 void DisplayPrefs::LoadTouchAssociationPreferenceForTest() {
889   LoadDisplayTouchAssociations(local_state_);
890 }
891
892 void DisplayPrefs::LoadDisplayPrefsForTest() {
893   CHECK_IS_TEST();
894   LoadDisplayPreferences();
895 }
896
897 void DisplayPrefs::StoreLegacyTouchDataForTest(
898     int64_t display_id,
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));
904 }
905
906 bool DisplayPrefs::ParseTouchCalibrationStringForTest(
907     const std::string& str,
908     display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) {
909   return ParseTouchCalibrationStringValue(str, point_pair_quad);
910 }
911
912 void DisplayPrefs::StoreDisplayMixedMirrorModeParamsForTest(
913     const absl::optional<display::MixedMirrorModeParams>& mixed_params) {
914   StoreDisplayMixedMirrorModeParams(local_state_, mixed_params);
915 }
916
917 }  // namespace ash