1// Copyright 2016 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16// Implementation of audio_volume_handler.h
17
18#include "audio_volume_handler.h"
19
20#include <base/files/file.h>
21#include <base/files/file_util.h>
22#include <base/logging.h>
23#include <brillo/map_utils.h>
24#include <brillo/message_loops/message_loop.h>
25#include <brillo/strings/string_utils.h>
26
27#include "audio_device_handler.h"
28
29namespace brillo {
30
31static const char kVolumeStateFilePath[] =
32    "/data/misc/brilloaudioservice/volume.dat";
33
34AudioVolumeHandler::AudioVolumeHandler() {
35  for (auto stream : kSupportedStreams_) {
36    step_sizes_.emplace(stream, kDefaultStepSize_);
37  }
38  selected_stream_ = AUDIO_STREAM_DEFAULT;
39  volume_state_file_ = base::FilePath(kVolumeStateFilePath);
40}
41
42AudioVolumeHandler::~AudioVolumeHandler() {}
43
44void AudioVolumeHandler::APSDisconnect() { aps_.clear(); }
45
46void AudioVolumeHandler::APSConnect(
47    android::sp<android::IAudioPolicyService> aps) {
48  aps_ = aps;
49  InitAPSAllStreams();
50}
51
52void AudioVolumeHandler::RegisterCallback(
53    base::Callback<void(audio_stream_type_t, int, int)>& callback) {
54  callback_ = callback;
55}
56
57int AudioVolumeHandler::ConvertToUserDefinedIndex(audio_stream_type_t stream,
58                                                  int index) {
59  return index / step_sizes_[stream];
60}
61
62int AudioVolumeHandler::ConvertToInternalIndex(audio_stream_type_t stream,
63                                               int index) {
64  return index * step_sizes_[stream];
65}
66
67void AudioVolumeHandler::TriggerCallback(audio_stream_type_t stream,
68                                         int previous_index,
69                                         int current_index) {
70  int user_defined_previous_index =
71      ConvertToUserDefinedIndex(stream, previous_index);
72  int user_defined_current_index =
73      ConvertToUserDefinedIndex(stream, current_index);
74  MessageLoop::current()->PostTask(base::Bind(callback_,
75                                              stream,
76                                              user_defined_previous_index,
77                                              user_defined_current_index));
78}
79
80void AudioVolumeHandler::GenerateVolumeFile() {
81  for (auto stream : kSupportedStreams_) {
82    for (auto device : AudioDeviceHandler::kSupportedOutputDevices_) {
83      PersistVolumeConfiguration(stream, device, kDefaultCurrentIndex_);
84    }
85  }
86  if (!kv_store_->Save(volume_state_file_)) {
87    LOG(ERROR) << "Could not save volume data file!";
88  }
89}
90
91int AudioVolumeHandler::GetVolumeMaxSteps(audio_stream_type_t stream) {
92  return ConvertToUserDefinedIndex(stream, kMaxIndex_);
93}
94
95int AudioVolumeHandler::SetVolumeMaxSteps(audio_stream_type_t stream,
96                                          int max_steps) {
97  if (max_steps <= kMinIndex_ || max_steps > kMaxIndex_)
98    return EINVAL;
99  step_sizes_[stream] = kMaxIndex_ / max_steps;
100  return 0;
101}
102
103int AudioVolumeHandler::GetVolumeCurrentIndex(audio_stream_type_t stream,
104                                              audio_devices_t device) {
105  auto key = kCurrentIndexKey_ + "." + string_utils::ToString(stream) + "." +
106             string_utils::ToString(device);
107  std::string value;
108  kv_store_->GetString(key, &value);
109  return std::stoi(value);
110}
111
112int AudioVolumeHandler::GetVolumeIndex(audio_stream_type_t stream,
113                                       audio_devices_t device) {
114  return ConvertToUserDefinedIndex(stream,
115                                   GetVolumeCurrentIndex(stream, device));
116}
117
118int AudioVolumeHandler::SetVolumeIndex(audio_stream_type_t stream,
119                                       audio_devices_t device,
120                                       int index) {
121  if (index < kMinIndex_ ||
122      index > ConvertToUserDefinedIndex(stream, kMaxIndex_))
123    return EINVAL;
124  int previous_index = GetVolumeCurrentIndex(stream, device);
125  int current_absolute_index = ConvertToInternalIndex(stream, index);
126  PersistVolumeConfiguration(stream, device, current_absolute_index);
127  TriggerCallback(stream, previous_index, current_absolute_index);
128  return 0;
129}
130
131void AudioVolumeHandler::PersistVolumeConfiguration(audio_stream_type_t stream,
132                                                    audio_devices_t device,
133                                                    int index) {
134  auto key = kCurrentIndexKey_ + "." + string_utils::ToString(stream) + "." +
135             string_utils::ToString(device);
136  kv_store_->SetString(key, string_utils::ToString(index));
137  kv_store_->Save(volume_state_file_);
138}
139
140void AudioVolumeHandler::InitAPSAllStreams() {
141  for (auto stream : kSupportedStreams_) {
142    aps_->initStreamVolume(stream, kMinIndex_, kMaxIndex_);
143    for (auto device : AudioDeviceHandler::kSupportedOutputDevices_) {
144      int current_index = GetVolumeCurrentIndex(stream, device);
145      aps_->setStreamVolumeIndex(stream, current_index, device);
146    }
147  }
148}
149
150void AudioVolumeHandler::SetVolumeFilePathForTesting(
151    const base::FilePath& path) {
152  volume_state_file_ = path;
153}
154
155void AudioVolumeHandler::Init(android::sp<android::IAudioPolicyService> aps) {
156  aps_ = aps;
157  kv_store_ = std::unique_ptr<KeyValueStore>(new KeyValueStore());
158  if (!base::PathExists(volume_state_file_)) {
159    // Generate key-value store and save it to a file.
160    GenerateVolumeFile();
161  } else {
162    // Load the file. If loading fails, generate the file.
163    if (!kv_store_->Load(volume_state_file_)) {
164      LOG(ERROR) << "Could not load volume data file!";
165      GenerateVolumeFile();
166    }
167  }
168  // Inform APS.
169  InitAPSAllStreams();
170}
171
172audio_stream_type_t AudioVolumeHandler::GetVolumeControlStream() {
173  return selected_stream_;
174}
175
176void AudioVolumeHandler::SetVolumeControlStream(audio_stream_type_t stream) {
177  selected_stream_ = stream;
178}
179
180int AudioVolumeHandler::GetNewVolumeIndex(int previous_index, int direction,
181                                          audio_stream_type_t stream) {
182  int current_index =
183      previous_index + ConvertToInternalIndex(stream, direction);
184  if (current_index < kMinIndex_) {
185    return kMinIndex_;
186  } else if (current_index > kMaxIndex_) {
187    return kMaxIndex_;
188  } else
189    return current_index;
190}
191
192void AudioVolumeHandler::AdjustStreamVolume(audio_stream_type_t stream,
193                                            int direction) {
194  VLOG(1) << "Adjusting volume of stream " << selected_stream_
195          << " in direction " << direction;
196  auto device = aps_->getDevicesForStream(stream);
197  int previous_index = GetVolumeCurrentIndex(stream, device);
198  int current_index = GetNewVolumeIndex(previous_index, direction, stream);
199  VLOG(1) << "Current index is " << current_index << " for stream " << stream
200          << " and device " << device;
201  aps_->setStreamVolumeIndex(stream, current_index, device);
202  PersistVolumeConfiguration(selected_stream_, device, current_index);
203  TriggerCallback(stream, previous_index, current_index);
204}
205
206void AudioVolumeHandler::AdjustVolumeActiveStreams(int direction) {
207  if (selected_stream_ != AUDIO_STREAM_DEFAULT) {
208    AdjustStreamVolume(selected_stream_, direction);
209    return;
210  }
211  for (auto stream : kSupportedStreams_) {
212    if (aps_->isStreamActive(stream)) {
213      AdjustStreamVolume(stream, direction);
214      return;
215    }
216  }
217}
218
219void AudioVolumeHandler::ProcessEvent(const struct input_event& event) {
220  VLOG(1) << event.type << " " << event.code << " " << event.value;
221  if (event.type == EV_KEY) {
222    switch (event.code) {
223      case KEY_VOLUMEDOWN:
224        AdjustVolumeActiveStreams(-1);
225        break;
226      case KEY_VOLUMEUP:
227        AdjustVolumeActiveStreams(1);
228        break;
229      default:
230        // This event code is not supported by this handler.
231        break;
232    }
233  }
234}
235
236}  // namespace brillo
237