/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AudioControl.h" #include #include #include #include #include #include #include #include "CloseHandle.h" namespace android::hardware::automotive::audiocontrol::V2_0::implementation { using ::android::base::EqualsIgnoreCase; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; static const float kLowerBound = -1.0f; static const float kUpperBound = 1.0f; AudioControl::AudioControl() {} Return> AudioControl::registerFocusListener(const sp& listener) { LOG(DEBUG) << "registering focus listener"; sp closeHandle(nullptr); if (listener) { mFocusListener = listener; closeHandle = new CloseHandle([this, listener]() { if (mFocusListener == listener) { mFocusListener = nullptr; } }); } else { LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op."; } return closeHandle; } Return AudioControl::setBalanceTowardRight(float value) { if (isValidValue(value)) { // Just log in this default mock implementation LOG(INFO) << "Balance set to " << value; } else { LOG(ERROR) << "Balance value out of range -1 to 1 at " << value; } return Void(); } Return AudioControl::setFadeTowardFront(float value) { if (isValidValue(value)) { // Just log in this default mock implementation LOG(INFO) << "Fader set to " << value; } else { LOG(ERROR) << "Fader value out of range -1 to 1 at " << value; } return Void(); } bool AudioControl::isValidValue(float value) { return (value > kLowerBound) && (value < kUpperBound); } Return AudioControl::onAudioFocusChange(hidl_bitfield usage, int zoneId, hidl_bitfield focusChange) { LOG(INFO) << "Focus changed: " << static_cast(focusChange) << " for usage " << static_cast(usage) << " in zone " << zoneId; return Void(); } Return AudioControl::debug(const hidl_handle& fd, const hidl_vec& options) { if (fd.getNativeHandle() == nullptr || fd->numFds == 0) { LOG(ERROR) << "Invalid parameters passed to debug()"; return Void(); } cmdDump(fd->data[0], options); return Void(); } void AudioControl::cmdDump(int fd, const hidl_vec& options) { if (options.size() == 0) { dump(fd); return; } std::string option = options[0]; if (EqualsIgnoreCase(option, "--help")) { cmdHelp(fd); } else if (EqualsIgnoreCase(option, "--request")) { cmdRequestFocus(fd, options); } else if (EqualsIgnoreCase(option, "--abandon")) { cmdAbandonFocus(fd, options); } else { dprintf(fd, "Invalid option: %s\n", option.c_str()); } } void AudioControl::dump(int fd) { if (mFocusListener == nullptr) { dprintf(fd, "No focus listener registered\n"); } else { dprintf(fd, "Focus listener registered\n"); } } void AudioControl::cmdHelp(int fd) const { dprintf(fd, "Usage: \n\n"); dprintf(fd, "[no args]: dumps focus listener status\n"); dprintf(fd, "--help: shows this help\n"); dprintf(fd, "--request : requests audio focus for specified " "usage (int), audio zone ID (int), and focus gain type (int)\n"); dprintf(fd, "--abandon : abandons audio focus for specified usage (int) and " "audio zone ID (int)\n"); } void AudioControl::cmdRequestFocus(int fd, const hidl_vec& options) { if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return; hidl_bitfield usage; if (!safelyParseInt(options[1], &usage)) { dprintf(fd, "Non-integer usage provided with request: %s\n", options[1].c_str()); return; } int zoneId; if (!safelyParseInt(options[2], &zoneId)) { dprintf(fd, "Non-integer zoneId provided with request: %s\n", options[2].c_str()); return; } hidl_bitfield focusGain; if (!safelyParseInt(options[3], &focusGain)) { dprintf(fd, "Non-integer focusGain provided with request: %s\n", options[3].c_str()); return; } if (mFocusListener == nullptr) { dprintf(fd, "Unable to request focus - no focus listener registered\n"); return; } mFocusListener->requestAudioFocus(usage, zoneId, focusGain); dprintf(fd, "Requested focus for usage %d, zoneId %d, and focusGain %d\n", usage, zoneId, focusGain); } void AudioControl::cmdAbandonFocus(int fd, const hidl_vec& options) { if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 2)) return; hidl_bitfield usage; if (!safelyParseInt(options[1], &usage)) { dprintf(fd, "Non-integer usage provided with abandon: %s\n", options[1].c_str()); return; } int zoneId; if (!safelyParseInt(options[2], &zoneId)) { dprintf(fd, "Non-integer zoneId provided with abandon: %s\n", options[2].c_str()); return; } if (mFocusListener == nullptr) { dprintf(fd, "Unable to abandon focus - no focus listener registered\n"); return; } mFocusListener->abandonAudioFocus(usage, zoneId); dprintf(fd, "Abandoned focus for usage %d and zoneId %d\n", usage, zoneId); } bool AudioControl::checkCallerHasWritePermissions(int fd) { // Double check that's only called by root - it should be be blocked at the HIDL debug() level, // but it doesn't hurt to make sure... if (hardware::IPCThreadState::self()->getCallingUid() != AID_ROOT) { dprintf(fd, "Must be root\n"); return false; } return true; } bool AudioControl::checkArgumentsSize(int fd, const hidl_vec& options, size_t expectedSize) { // options includes the command, so reducing size by one size_t size = options.size() - 1; if (size == expectedSize) { return true; } dprintf(fd, "Invalid number of arguments: required %zu, got %zu\n", expectedSize, size); return false; } bool AudioControl::safelyParseInt(std::string s, int* out) { if (!android::base::ParseInt(s, out)) { return false; } return true; } } // namespace android::hardware::automotive::audiocontrol::V2_0::implementation