1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "AudioControl.h"
18
19 #include <stdio.h>
20
21 #include <android-base/logging.h>
22 #include <android-base/parseint.h>
23 #include <android-base/strings.h>
24
25 #include <hidl/HidlTransportSupport.h>
26 #include <hwbinder/IPCThreadState.h>
27 #include <private/android_filesystem_config.h>
28
29 #include "CloseHandle.h"
30
31 namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
32
33 using ::android::base::EqualsIgnoreCase;
34 using ::android::hardware::hidl_handle;
35 using ::android::hardware::hidl_string;
36
37 static const float kLowerBound = -1.0f;
38 static const float kUpperBound = 1.0f;
39
AudioControl()40 AudioControl::AudioControl() {}
41
registerFocusListener(const sp<IFocusListener> & listener)42 Return<sp<ICloseHandle>> AudioControl::registerFocusListener(const sp<IFocusListener>& listener) {
43 LOG(DEBUG) << "registering focus listener";
44 sp<ICloseHandle> closeHandle(nullptr);
45
46 if (listener) {
47 mFocusListener = listener;
48
49 closeHandle = new CloseHandle([this, listener]() {
50 if (mFocusListener == listener) {
51 mFocusListener = nullptr;
52 }
53 });
54 } else {
55 LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op.";
56 }
57
58 return closeHandle;
59 }
60
setBalanceTowardRight(float value)61 Return<void> AudioControl::setBalanceTowardRight(float value) {
62 if (isValidValue(value)) {
63 // Just log in this default mock implementation
64 LOG(INFO) << "Balance set to " << value;
65 } else {
66 LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
67 }
68 return Void();
69 }
70
setFadeTowardFront(float value)71 Return<void> AudioControl::setFadeTowardFront(float value) {
72 if (isValidValue(value)) {
73 // Just log in this default mock implementation
74 LOG(INFO) << "Fader set to " << value;
75 } else {
76 LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
77 }
78 return Void();
79 }
80
isValidValue(float value)81 bool AudioControl::isValidValue(float value) {
82 return (value > kLowerBound) && (value < kUpperBound);
83 }
84
onAudioFocusChange(hidl_bitfield<AudioUsage> usage,int zoneId,hidl_bitfield<AudioFocusChange> focusChange)85 Return<void> AudioControl::onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId,
86 hidl_bitfield<AudioFocusChange> focusChange) {
87 LOG(INFO) << "Focus changed: " << static_cast<int>(focusChange) << " for usage "
88 << static_cast<int>(usage) << " in zone " << zoneId;
89 return Void();
90 }
91
debug(const hidl_handle & fd,const hidl_vec<hidl_string> & options)92 Return<void> AudioControl::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
93 if (fd.getNativeHandle() == nullptr || fd->numFds == 0) {
94 LOG(ERROR) << "Invalid parameters passed to debug()";
95 return Void();
96 }
97
98 cmdDump(fd->data[0], options);
99 return Void();
100 }
101
cmdDump(int fd,const hidl_vec<hidl_string> & options)102 void AudioControl::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
103 if (options.size() == 0) {
104 dump(fd);
105 return;
106 }
107
108 std::string option = options[0];
109 if (EqualsIgnoreCase(option, "--help")) {
110 cmdHelp(fd);
111 } else if (EqualsIgnoreCase(option, "--request")) {
112 cmdRequestFocus(fd, options);
113 } else if (EqualsIgnoreCase(option, "--abandon")) {
114 cmdAbandonFocus(fd, options);
115 } else {
116 dprintf(fd, "Invalid option: %s\n", option.c_str());
117 }
118 }
119
dump(int fd)120 void AudioControl::dump(int fd) {
121 if (mFocusListener == nullptr) {
122 dprintf(fd, "No focus listener registered\n");
123 } else {
124 dprintf(fd, "Focus listener registered\n");
125 }
126 }
127
cmdHelp(int fd) const128 void AudioControl::cmdHelp(int fd) const {
129 dprintf(fd, "Usage: \n\n");
130 dprintf(fd, "[no args]: dumps focus listener status\n");
131 dprintf(fd, "--help: shows this help\n");
132 dprintf(fd,
133 "--request <USAGE> <ZONE_ID> <FOCUS_GAIN>: requests audio focus for specified "
134 "usage (int), audio zone ID (int), and focus gain type (int)\n");
135 dprintf(fd,
136 "--abandon <USAGE> <ZONE_ID>: abandons audio focus for specified usage (int) and "
137 "audio zone ID (int)\n");
138 }
139
cmdRequestFocus(int fd,const hidl_vec<hidl_string> & options)140 void AudioControl::cmdRequestFocus(int fd, const hidl_vec<hidl_string>& options) {
141 if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;
142
143 hidl_bitfield<AudioUsage> usage;
144 if (!safelyParseInt(options[1], &usage)) {
145 dprintf(fd, "Non-integer usage provided with request: %s\n", options[1].c_str());
146 return;
147 }
148 int zoneId;
149 if (!safelyParseInt(options[2], &zoneId)) {
150 dprintf(fd, "Non-integer zoneId provided with request: %s\n", options[2].c_str());
151 return;
152 }
153 hidl_bitfield<AudioFocusChange> focusGain;
154 if (!safelyParseInt(options[3], &focusGain)) {
155 dprintf(fd, "Non-integer focusGain provided with request: %s\n", options[3].c_str());
156 return;
157 }
158
159 if (mFocusListener == nullptr) {
160 dprintf(fd, "Unable to request focus - no focus listener registered\n");
161 return;
162 }
163
164 mFocusListener->requestAudioFocus(usage, zoneId, focusGain);
165 dprintf(fd, "Requested focus for usage %d, zoneId %d, and focusGain %d\n", usage, zoneId,
166 focusGain);
167 }
168
cmdAbandonFocus(int fd,const hidl_vec<hidl_string> & options)169 void AudioControl::cmdAbandonFocus(int fd, const hidl_vec<hidl_string>& options) {
170 if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 2)) return;
171
172 hidl_bitfield<AudioUsage> usage;
173 if (!safelyParseInt(options[1], &usage)) {
174 dprintf(fd, "Non-integer usage provided with abandon: %s\n", options[1].c_str());
175 return;
176 }
177 int zoneId;
178 if (!safelyParseInt(options[2], &zoneId)) {
179 dprintf(fd, "Non-integer zoneId provided with abandon: %s\n", options[2].c_str());
180 return;
181 }
182
183 if (mFocusListener == nullptr) {
184 dprintf(fd, "Unable to abandon focus - no focus listener registered\n");
185 return;
186 }
187
188 mFocusListener->abandonAudioFocus(usage, zoneId);
189 dprintf(fd, "Abandoned focus for usage %d and zoneId %d\n", usage, zoneId);
190 }
191
checkCallerHasWritePermissions(int fd)192 bool AudioControl::checkCallerHasWritePermissions(int fd) {
193 // Double check that's only called by root - it should be be blocked at the HIDL debug() level,
194 // but it doesn't hurt to make sure...
195 if (hardware::IPCThreadState::self()->getCallingUid() != AID_ROOT) {
196 dprintf(fd, "Must be root\n");
197 return false;
198 }
199 return true;
200 }
201
checkArgumentsSize(int fd,const hidl_vec<hidl_string> & options,size_t expectedSize)202 bool AudioControl::checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options,
203 size_t expectedSize) {
204 // options includes the command, so reducing size by one
205 size_t size = options.size() - 1;
206 if (size == expectedSize) {
207 return true;
208 }
209 dprintf(fd, "Invalid number of arguments: required %zu, got %zu\n", expectedSize, size);
210 return false;
211 }
212
safelyParseInt(std::string s,int * out)213 bool AudioControl::safelyParseInt(std::string s, int* out) {
214 if (!android::base::ParseInt(s, out)) {
215 return false;
216 }
217 return true;
218 }
219
220 } // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
221