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