1 // Copyright (c) 2013 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
5 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "chromeos/audio/audio_device.h"
16 #include "chromeos/constants/chromeos_pref_names.h"
17 #include "components/prefs/pref_registry_simple.h"
18 #include "components/prefs/pref_service.h"
19 #include "components/prefs/scoped_user_pref_update.h"
20
21 namespace {
22
23 // Values used for muted preference.
24 const int kPrefMuteOff = 0;
25 const int kPrefMuteOn = 1;
26
27 // Prefs keys.
28 const char kActiveKey[] = "active";
29 const char kActivateByUserKey[] = "activate_by_user";
30
31 // Gets the device id string for storing audio preference. The format of
32 // device string is a string consisting of 3 parts:
33 // |version of stable device ID| :
34 // |integer from lower 32 bit of device id| :
35 // |0(output device) or 1(input device)|
36 // If an audio device has both integrated input and output devices, the first 2
37 // parts of the string could be identical, only the last part will differentiate
38 // them.
39 // Note that |version of stable device ID| is present only for devices with
40 // stable device ID version >= 2. For devices with version 1, the device id
41 // string contains only latter 2 parts - in order to preserve backward
42 // compatibility with existing ID from before v2 stable device ID was
43 // introduced.
GetVersionedDeviceIdString(const chromeos::AudioDevice & device,int version)44 std::string GetVersionedDeviceIdString(const chromeos::AudioDevice& device,
45 int version) {
46 CHECK(device.stable_device_id_version >= version);
47 DCHECK_GE(device.stable_device_id_version, 1);
48 DCHECK_LE(device.stable_device_id_version, 2);
49
50 bool use_deprecated_id = version == 1 && device.stable_device_id_version == 2;
51 uint64_t stable_device_id = use_deprecated_id
52 ? device.deprecated_stable_device_id
53 : device.stable_device_id;
54 std::string version_prefix = version == 2 ? "2 : " : "";
55 std::string device_id_string =
56 version_prefix +
57 base::NumberToString(stable_device_id &
58 static_cast<uint64_t>(0xffffffff)) +
59 " : " + (device.is_input ? "1" : "0");
60 // Replace any periods from the device id string with a space, since setting
61 // names cannot contain periods.
62 std::replace(device_id_string.begin(), device_id_string.end(), '.', ' ');
63 return device_id_string;
64 }
65
GetDeviceIdString(const chromeos::AudioDevice & device)66 std::string GetDeviceIdString(const chromeos::AudioDevice& device) {
67 return GetVersionedDeviceIdString(device, device.stable_device_id_version);
68 }
69
70 // Migrates an entry associated with |device|'s v1 stable device ID in
71 // |settings| to the key derived from |device|'s v2 stable device ID
72 // (which is expected to be equal to |intended_key|), if the entry can
73 // be found.
74 // Returns whether the migration occurred.
MigrateDeviceIdInSettings(base::DictionaryValue * settings,const std::string & intended_key,const chromeos::AudioDevice & device)75 bool MigrateDeviceIdInSettings(base::DictionaryValue* settings,
76 const std::string& intended_key,
77 const chromeos::AudioDevice& device) {
78 if (device.stable_device_id_version == 1)
79 return false;
80
81 DCHECK_EQ(2, device.stable_device_id_version);
82
83 std::string old_device_id = GetVersionedDeviceIdString(device, 1);
84 std::unique_ptr<base::Value> value;
85 if (!settings->Remove(old_device_id, &value))
86 return false;
87
88 DCHECK_EQ(intended_key, GetDeviceIdString(device));
89 settings->Set(intended_key, std::move(value));
90 return true;
91 }
92
93 } // namespace
94
95 namespace chromeos {
96
GetOutputVolumeValue(const AudioDevice * device)97 double AudioDevicesPrefHandlerImpl::GetOutputVolumeValue(
98 const AudioDevice* device) {
99 if (!device)
100 return kDefaultOutputVolumePercent;
101 else
102 return GetOutputVolumePrefValue(*device);
103 }
104
GetInputGainValue(const AudioDevice * device)105 double AudioDevicesPrefHandlerImpl::GetInputGainValue(
106 const AudioDevice* device) {
107 DCHECK(device);
108 return GetInputGainPrefValue(*device);
109 }
110
SetVolumeGainValue(const AudioDevice & device,double value)111 void AudioDevicesPrefHandlerImpl::SetVolumeGainValue(
112 const AudioDevice& device, double value) {
113 // TODO(baileyberro): Refactor public interface to use two explicit methods.
114 device.is_input ? SetInputGainPrefValue(device, value)
115 : SetOutputVolumePrefValue(device, value);
116 }
117
SetOutputVolumePrefValue(const AudioDevice & device,double value)118 void AudioDevicesPrefHandlerImpl::SetOutputVolumePrefValue(
119 const AudioDevice& device,
120 double value) {
121 DCHECK(!device.is_input);
122 // Use this opportunity to remove device record under deprecated device ID,
123 // if one exists.
124 if (device.stable_device_id_version == 2) {
125 std::string old_device_id = GetVersionedDeviceIdString(device, 1);
126 device_volume_settings_->Remove(old_device_id, nullptr);
127 }
128 device_volume_settings_->SetDouble(GetDeviceIdString(device), value);
129
130 SaveDevicesVolumePref();
131 }
132
SetInputGainPrefValue(const AudioDevice & device,double value)133 void AudioDevicesPrefHandlerImpl::SetInputGainPrefValue(
134 const AudioDevice& device,
135 double value) {
136 DCHECK(device.is_input);
137
138 const std::string device_id = GetDeviceIdString(device);
139
140 // Use this opportunity to remove input device record from
141 // |device_volume_settings_|.
142 // TODO(baileyberro): Remove this check in M94.
143 if (device_volume_settings_->HasKey(device_id)) {
144 device_volume_settings_->Remove(device_id, nullptr);
145 SaveDevicesVolumePref();
146 }
147
148 device_gain_settings_->SetDouble(device_id, value);
149 SaveDevicesGainPref();
150 }
151
GetMuteValue(const AudioDevice & device)152 bool AudioDevicesPrefHandlerImpl::GetMuteValue(const AudioDevice& device) {
153 std::string device_id_str = GetDeviceIdString(device);
154 if (!device_mute_settings_->HasKey(device_id_str))
155 MigrateDeviceMuteSettings(device_id_str, device);
156
157 int mute = kPrefMuteOff;
158 device_mute_settings_->GetInteger(device_id_str, &mute);
159
160 return (mute == kPrefMuteOn);
161 }
162
SetMuteValue(const AudioDevice & device,bool mute)163 void AudioDevicesPrefHandlerImpl::SetMuteValue(const AudioDevice& device,
164 bool mute) {
165 // Use this opportunity to remove device record under deprecated device ID,
166 // if one exists.
167 if (device.stable_device_id_version == 2) {
168 std::string old_device_id = GetVersionedDeviceIdString(device, 1);
169 device_mute_settings_->Remove(old_device_id, nullptr);
170 }
171 device_mute_settings_->SetInteger(GetDeviceIdString(device),
172 mute ? kPrefMuteOn : kPrefMuteOff);
173 SaveDevicesMutePref();
174 }
175
SetDeviceActive(const AudioDevice & device,bool active,bool activate_by_user)176 void AudioDevicesPrefHandlerImpl::SetDeviceActive(const AudioDevice& device,
177 bool active,
178 bool activate_by_user) {
179 auto dict = std::make_unique<base::DictionaryValue>();
180 dict->SetBoolean(kActiveKey, active);
181 if (active)
182 dict->SetBoolean(kActivateByUserKey, activate_by_user);
183
184 // Use this opportunity to remove device record under deprecated device ID,
185 // if one exists.
186 if (device.stable_device_id_version == 2) {
187 std::string old_device_id = GetVersionedDeviceIdString(device, 1);
188 device_state_settings_->Remove(old_device_id, nullptr);
189 }
190 device_state_settings_->Set(GetDeviceIdString(device), std::move(dict));
191 SaveDevicesStatePref();
192 }
193
GetDeviceActive(const AudioDevice & device,bool * active,bool * activate_by_user)194 bool AudioDevicesPrefHandlerImpl::GetDeviceActive(const AudioDevice& device,
195 bool* active,
196 bool* activate_by_user) {
197 const std::string device_id_str = GetDeviceIdString(device);
198 if (!device_state_settings_->HasKey(device_id_str) &&
199 !MigrateDevicesStatePref(device_id_str, device)) {
200 return false;
201 }
202
203 base::DictionaryValue* dict = NULL;
204 if (!device_state_settings_->GetDictionary(device_id_str, &dict)) {
205 LOG(ERROR) << "Could not get device state for device:" << device.ToString();
206 return false;
207 }
208 if (!dict->GetBoolean(kActiveKey, active)) {
209 LOG(ERROR) << "Could not get active value for device:" << device.ToString();
210 return false;
211 }
212
213 if (*active && !dict->GetBoolean(kActivateByUserKey, activate_by_user)) {
214 LOG(ERROR) << "Could not get activate_by_user value for previously "
215 "active device:"
216 << device.ToString();
217 return false;
218 }
219
220 return true;
221 }
222
GetAudioOutputAllowedValue()223 bool AudioDevicesPrefHandlerImpl::GetAudioOutputAllowedValue() {
224 return local_state_->GetBoolean(prefs::kAudioOutputAllowed);
225 }
226
AddAudioPrefObserver(AudioPrefObserver * observer)227 void AudioDevicesPrefHandlerImpl::AddAudioPrefObserver(
228 AudioPrefObserver* observer) {
229 observers_.AddObserver(observer);
230 }
231
RemoveAudioPrefObserver(AudioPrefObserver * observer)232 void AudioDevicesPrefHandlerImpl::RemoveAudioPrefObserver(
233 AudioPrefObserver* observer) {
234 observers_.RemoveObserver(observer);
235 }
236
GetOutputVolumePrefValue(const AudioDevice & device)237 double AudioDevicesPrefHandlerImpl::GetOutputVolumePrefValue(
238 const AudioDevice& device) {
239 DCHECK(!device.is_input);
240 std::string device_id_str = GetDeviceIdString(device);
241 if (!device_volume_settings_->HasKey(device_id_str))
242 MigrateDeviceVolumeGainSettings(device_id_str, device);
243
244 double value;
245 device_volume_settings_->GetDouble(device_id_str, &value);
246
247 return value;
248 }
249
GetInputGainPrefValue(const AudioDevice & device)250 double AudioDevicesPrefHandlerImpl::GetInputGainPrefValue(
251 const AudioDevice& device) {
252 DCHECK(device.is_input);
253 std::string device_id_str = GetDeviceIdString(device);
254 if (!device_gain_settings_->HasKey(device_id_str))
255 SetInputGainPrefValue(device, kDefaultInputGainPercent);
256
257 double value;
258 device_gain_settings_->GetDouble(device_id_str, &value);
259
260 return value;
261 }
262
GetDeviceDefaultOutputVolume(const AudioDevice & device)263 double AudioDevicesPrefHandlerImpl::GetDeviceDefaultOutputVolume(
264 const AudioDevice& device) {
265 if (device.type == AUDIO_TYPE_HDMI)
266 return kDefaultHdmiOutputVolumePercent;
267 else
268 return kDefaultOutputVolumePercent;
269 }
270
AudioDevicesPrefHandlerImpl(PrefService * local_state)271 AudioDevicesPrefHandlerImpl::AudioDevicesPrefHandlerImpl(
272 PrefService* local_state)
273 : device_mute_settings_(std::make_unique<base::DictionaryValue>()),
274 device_volume_settings_(std::make_unique<base::DictionaryValue>()),
275 device_gain_settings_(std::make_unique<base::DictionaryValue>()),
276 device_state_settings_(std::make_unique<base::DictionaryValue>()),
277 local_state_(local_state) {
278 InitializePrefObservers();
279
280 LoadDevicesMutePref();
281 LoadDevicesVolumePref();
282 LoadDevicesGainPref();
283 LoadDevicesStatePref();
284 }
285
286 AudioDevicesPrefHandlerImpl::~AudioDevicesPrefHandlerImpl() = default;
287
InitializePrefObservers()288 void AudioDevicesPrefHandlerImpl::InitializePrefObservers() {
289 pref_change_registrar_.Init(local_state_);
290 base::RepeatingClosure callback =
291 base::BindRepeating(&AudioDevicesPrefHandlerImpl::NotifyAudioPolicyChange,
292 base::Unretained(this));
293 pref_change_registrar_.Add(prefs::kAudioOutputAllowed, callback);
294 }
295
LoadDevicesMutePref()296 void AudioDevicesPrefHandlerImpl::LoadDevicesMutePref() {
297 const base::DictionaryValue* mute_prefs =
298 local_state_->GetDictionary(prefs::kAudioDevicesMute);
299 if (mute_prefs)
300 device_mute_settings_.reset(mute_prefs->DeepCopy());
301 }
302
SaveDevicesMutePref()303 void AudioDevicesPrefHandlerImpl::SaveDevicesMutePref() {
304 DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesMute);
305 dict_update->Clear();
306 dict_update->MergeDictionary(device_mute_settings_.get());
307 }
308
LoadDevicesVolumePref()309 void AudioDevicesPrefHandlerImpl::LoadDevicesVolumePref() {
310 const base::DictionaryValue* volume_prefs =
311 local_state_->GetDictionary(prefs::kAudioDevicesVolumePercent);
312 if (volume_prefs)
313 device_volume_settings_.reset(volume_prefs->DeepCopy());
314 }
315
SaveDevicesVolumePref()316 void AudioDevicesPrefHandlerImpl::SaveDevicesVolumePref() {
317 DictionaryPrefUpdate dict_update(local_state_,
318 prefs::kAudioDevicesVolumePercent);
319 dict_update->Clear();
320 dict_update->MergeDictionary(device_volume_settings_.get());
321 }
322
LoadDevicesGainPref()323 void AudioDevicesPrefHandlerImpl::LoadDevicesGainPref() {
324 const base::DictionaryValue* gain_prefs =
325 local_state_->GetDictionary(prefs::kAudioDevicesGainPercent);
326 if (gain_prefs)
327 device_gain_settings_.reset(gain_prefs->DeepCopy());
328 }
329
SaveDevicesGainPref()330 void AudioDevicesPrefHandlerImpl::SaveDevicesGainPref() {
331 DictionaryPrefUpdate dict_update(local_state_,
332 prefs::kAudioDevicesGainPercent);
333 dict_update->Clear();
334 dict_update->MergeDictionary(device_gain_settings_.get());
335 }
336
LoadDevicesStatePref()337 void AudioDevicesPrefHandlerImpl::LoadDevicesStatePref() {
338 const base::DictionaryValue* state_prefs =
339 local_state_->GetDictionary(prefs::kAudioDevicesState);
340 if (state_prefs)
341 device_state_settings_.reset(state_prefs->DeepCopy());
342 }
343
SaveDevicesStatePref()344 void AudioDevicesPrefHandlerImpl::SaveDevicesStatePref() {
345 DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesState);
346 dict_update->Clear();
347 dict_update->MergeDictionary(device_state_settings_.get());
348 }
349
MigrateDevicesStatePref(const std::string & device_key,const AudioDevice & device)350 bool AudioDevicesPrefHandlerImpl::MigrateDevicesStatePref(
351 const std::string& device_key,
352 const AudioDevice& device) {
353 if (!MigrateDeviceIdInSettings(device_state_settings_.get(), device_key,
354 device)) {
355 return false;
356 }
357
358 SaveDevicesStatePref();
359 return true;
360 }
361
MigrateDeviceMuteSettings(const std::string & device_key,const AudioDevice & device)362 void AudioDevicesPrefHandlerImpl::MigrateDeviceMuteSettings(
363 const std::string& device_key,
364 const AudioDevice& device) {
365 if (!MigrateDeviceIdInSettings(device_mute_settings_.get(), device_key,
366 device)) {
367 // If there was no recorded value for deprecated device ID, use value from
368 // global mute pref.
369 int old_mute = local_state_->GetInteger(prefs::kAudioMute);
370 device_mute_settings_->SetInteger(device_key, old_mute);
371 }
372 SaveDevicesMutePref();
373 }
374
MigrateDeviceVolumeGainSettings(const std::string & device_key,const AudioDevice & device)375 void AudioDevicesPrefHandlerImpl::MigrateDeviceVolumeGainSettings(
376 const std::string& device_key,
377 const AudioDevice& device) {
378 DCHECK(!device.is_input);
379 if (!MigrateDeviceIdInSettings(device_volume_settings_.get(), device_key,
380 device)) {
381 // If there was no recorded value for deprecated device ID, use value from
382 // global vloume pref.
383 double old_volume = local_state_->GetDouble(prefs::kAudioVolumePercent);
384 device_volume_settings_->SetDouble(device_key, old_volume);
385 }
386 SaveDevicesVolumePref();
387 }
388
NotifyAudioPolicyChange()389 void AudioDevicesPrefHandlerImpl::NotifyAudioPolicyChange() {
390 for (auto& observer : observers_)
391 observer.OnAudioPolicyPrefChanged();
392 }
393
394 // static
RegisterPrefs(PrefRegistrySimple * registry)395 void AudioDevicesPrefHandlerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
396 registry->RegisterDictionaryPref(prefs::kAudioDevicesVolumePercent);
397 registry->RegisterDictionaryPref(prefs::kAudioDevicesGainPercent);
398 registry->RegisterDictionaryPref(prefs::kAudioDevicesMute);
399 registry->RegisterDictionaryPref(prefs::kAudioDevicesState);
400
401 // Register the prefs backing the audio muting policies.
402 // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
403 // media system.
404 registry->RegisterBooleanPref(prefs::kAudioOutputAllowed, true);
405
406 // Register the legacy audio prefs for migration.
407 registry->RegisterDoublePref(prefs::kAudioVolumePercent,
408 kDefaultOutputVolumePercent);
409 registry->RegisterIntegerPref(prefs::kAudioMute, kPrefMuteOff);
410 }
411
412 } // namespace chromeos
413