Project import
diff --git a/libhardware_legacy/Android.mk b/libhardware_legacy/Android.mk new file mode 100644 index 0000000..b0254f5 --- /dev/null +++ b/libhardware_legacy/Android.mk
@@ -0,0 +1,54 @@ +# Copyright 2006 The Android Open Source Project + +# Setting LOCAL_PATH will mess up all-subdir-makefiles, so do it beforehand. +legacy_modules := power uevent wifi + +SAVE_MAKEFILES := $(call all-named-subdir-makefiles,$(legacy_modules)) +LEGACY_AUDIO_MAKEFILES := $(call all-named-subdir-makefiles,audio) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := libcutils liblog libmedia + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +LOCAL_CFLAGS += -DQEMU_HARDWARE -Wno-unused-parameter -Wno-gnu-designator +QEMU_HARDWARE := true + +LOCAL_SHARED_LIBRARIES += libdl + +include $(SAVE_MAKEFILES) + +LOCAL_MODULE:= libhardware_legacy + +include $(BUILD_SHARED_LIBRARY) + +# static library for librpc +include $(CLEAR_VARS) + +LOCAL_MODULE:= libpower + +LOCAL_SRC_FILES += power/power.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +include $(BUILD_STATIC_LIBRARY) + +# shared library for various HALs +include $(CLEAR_VARS) + +LOCAL_MODULE := libpower + +LOCAL_SRC_FILES := power/power.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +LOCAL_SHARED_LIBRARIES := libcutils + +include $(BUILD_SHARED_LIBRARY) + +# legacy_audio builds it's own set of libraries that aren't linked into +# hardware_legacy +include $(LEGACY_AUDIO_MAKEFILES)
diff --git a/libhardware_legacy/CleanSpec.mk b/libhardware_legacy/CleanSpec.mk new file mode 100644 index 0000000..56193a9 --- /dev/null +++ b/libhardware_legacy/CleanSpec.mk
@@ -0,0 +1,51 @@ +# Copyright (C) 2007 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/) \ No newline at end of file
diff --git a/libhardware_legacy/MODULE_LICENSE_APACHE2 b/libhardware_legacy/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libhardware_legacy/MODULE_LICENSE_APACHE2
diff --git a/libhardware_legacy/NOTICE b/libhardware_legacy/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libhardware_legacy/NOTICE
@@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +
diff --git a/libhardware_legacy/audio/A2dpAudioInterface.cpp b/libhardware_legacy/audio/A2dpAudioInterface.cpp new file mode 100644 index 0000000..50708ba --- /dev/null +++ b/libhardware_legacy/audio/A2dpAudioInterface.cpp
@@ -0,0 +1,500 @@ +/* + * Copyright (C) 2008 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 <math.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/Timers.h> + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" +#include <hardware_legacy/power.h> + + +namespace android_audio_legacy { + +static const char *sA2dpWakeLock = "A2dpOutputStream"; +#define MAX_WRITE_RETRIES 5 + +// ---------------------------------------------------------------------------- + +//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface() +//{ +// AudioHardwareInterface* hw = 0; +// +// hw = AudioHardwareInterface::create(); +// ALOGD("new A2dpAudioInterface(hw: %p)", hw); +// hw = new A2dpAudioInterface(hw); +// return hw; +//} + +A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) : + mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ + closeOutputStream((AudioStreamOut *)mOutput); + delete mHardwareInterface; +} + +status_t A2dpAudioInterface::initCheck() +{ + if (mHardwareInterface == 0) return NO_INIT; + return mHardwareInterface->initCheck(); +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + if (!audio_is_a2dp_out_device(devices)) { + ALOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); + return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); + } + + status_t err = 0; + + // only one output stream allowed + if (mOutput) { + if (status) + *status = -1; + return NULL; + } + + // create new output stream + A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); + if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { + mOutput = out; + mOutput->setBluetoothEnabled(mBluetoothEnabled); + mOutput->setSuspended(mSuspended); + } else { + delete out; + } + + if (status) + *status = err; + return mOutput; +} + +void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) { + if (mOutput == 0 || mOutput != out) { + mHardwareInterface->closeOutputStream(out); + } + else { + delete mOutput; + mOutput = 0; + } +} + + +AudioStreamIn* A2dpAudioInterface::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); +} + +void A2dpAudioInterface::closeInputStream(AudioStreamIn* in) +{ + return mHardwareInterface->closeInputStream(in); +} + +status_t A2dpAudioInterface::setMode(int mode) +{ + return mHardwareInterface->setMode(mode); +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ + return mHardwareInterface->setMicMute(state); +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ + return mHardwareInterface->getMicMute(state); +} + +status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key; + status_t status = NO_ERROR; + + ALOGV("setParameters() %s", keyValuePairs.string()); + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + mBluetoothEnabled = (value == "true"); + if (mOutput) { + mOutput->setBluetoothEnabled(mBluetoothEnabled); + } + param.remove(key); + } + key = String8("A2dpSuspended"); + if (param.get(key, value) == NO_ERROR) { + mSuspended = (value == "true"); + if (mOutput) { + mOutput->setSuspended(mSuspended); + } + param.remove(key); + } + + if (param.size()) { + status_t hwStatus = mHardwareInterface->setParameters(param.toString()); + if (status == NO_ERROR) { + status = hwStatus; + } + } + + return status; +} + +String8 A2dpAudioInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + AudioParameter a2dpParam = AudioParameter(); + String8 value; + String8 key; + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + value = mBluetoothEnabled ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); + } + key = "A2dpSuspended"; + if (param.get(key, value) == NO_ERROR) { + value = mSuspended ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); + } + + String8 keyValuePairs = a2dpParam.toString(); + + if (param.size()) { + if (keyValuePairs != "") { + keyValuePairs += ";"; + } + keyValuePairs += mHardwareInterface->getParameters(param.toString()); + } + + ALOGV("getParameters() %s", keyValuePairs.string()); + return keyValuePairs; +} + +size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ + return mHardwareInterface->setVoiceVolume(v); +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ + return mHardwareInterface->setMasterVolume(v); +} + +status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) +{ + return mHardwareInterface->dumpState(fd, args); +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : + mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL), + // assume BT enabled to start, this is safe because its only the + // enabled->disabled transition we are worried about + mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false) +{ + // use any address by default + strcpy(mA2dpAddress, "00:00:00:00:00:00"); + init(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( + uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + ALOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate); + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())){ + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mDevice = device; + mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000; + return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ + ALOGV("A2dpAudioStreamOut destructor"); + close(); + ALOGV("A2dpAudioStreamOut destructor returning from close()"); +} + +ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) +{ + status_t status = -1; + { + Mutex::Autolock lock(mLock); + + size_t remaining = bytes; + + if (!mBluetoothEnabled || mClosing || mSuspended) { + ALOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \ + mBluetoothEnabled %d, mClosing %d, mSuspended %d", + mBluetoothEnabled, mClosing, mSuspended); + goto Error; + } + + if (mStandby) { + acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock); + mStandby = false; + mLastWriteTime = systemTime(); + } + + status = init(); + if (status < 0) + goto Error; + + int retries = MAX_WRITE_RETRIES; + while (remaining > 0 && retries) { + status = a2dp_write(mData, buffer, remaining); + if (status < 0) { + ALOGE("a2dp_write failed err: %d\n", status); + goto Error; + } + if (status == 0) { + retries--; + } + remaining -= status; + buffer = (char *)buffer + status; + } + + // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread + // does no spin and starve other threads. + // NOTE: It is likely that the A2DP headset is being disconnected + nsecs_t now = systemTime(); + if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) { + ALOGV("A2DP sink runs too fast"); + usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime)); + } + mLastWriteTime = now; + return bytes; + + } +Error: + + standby(); + + // Simulate audio output timing in case of error + usleep(mBufferDurationUs); + + return status; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::init() +{ + if (!mData) { + status_t status = a2dp_init(44100, 2, &mData); + if (status < 0) { + ALOGE("a2dp_init failed err: %d\n", status); + mData = NULL; + return status; + } + a2dp_set_sink(mData, mA2dpAddress); + } + + return 0; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() +{ + Mutex::Autolock lock(mLock); + return standby_l(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby_l() +{ + int result = NO_ERROR; + + if (!mStandby) { + ALOGV_IF(mClosing || !mBluetoothEnabled, "Standby skip stop: closing %d enabled %d", + mClosing, mBluetoothEnabled); + if (!mClosing && mBluetoothEnabled) { + result = a2dp_stop(mData); + } + release_wake_lock(sA2dpWakeLock); + mStandby = true; + } + + return result; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key = String8("a2dp_sink_address"); + status_t status = NO_ERROR; + int device; + ALOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string()); + + if (param.get(key, value) == NO_ERROR) { + if (value.length() != strlen("00:00:00:00:00:00")) { + status = BAD_VALUE; + } else { + setAddress(value.string()); + } + param.remove(key); + } + key = String8("closing"); + if (param.get(key, value) == NO_ERROR) { + mClosing = (value == "true"); + if (mClosing) { + standby(); + } + param.remove(key); + } + key = AudioParameter::keyRouting; + if (param.getInt(key, device) == NO_ERROR) { + if (audio_is_a2dp_out_device(device)) { + mDevice = device; + status = NO_ERROR; + } else { + status = BAD_VALUE; + } + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8("a2dp_sink_address"); + + if (param.get(key, value) == NO_ERROR) { + value = mA2dpAddress; + param.add(key, value); + } + key = AudioParameter::keyRouting; + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + ALOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string()); + return param.toString(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) +{ + Mutex::Autolock lock(mLock); + + if (strlen(address) != strlen("00:00:00:00:00:00")) + return -EINVAL; + + strcpy(mA2dpAddress, address); + if (mData) + a2dp_set_sink(mData, mA2dpAddress); + + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled) +{ + ALOGD("setBluetoothEnabled %d", enabled); + + Mutex::Autolock lock(mLock); + + mBluetoothEnabled = enabled; + if (!enabled) { + return close_l(); + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff) +{ + ALOGV("setSuspended %d", onOff); + mSuspended = onOff; + standby(); + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ + Mutex::Autolock lock(mLock); + ALOGV("A2dpAudioStreamOut::close() calling close_l()"); + return close_l(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l() +{ + standby_l(); + if (mData) { + ALOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)"); + a2dp_cleanup(mData); + mData = NULL; + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args) +{ + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames) +{ + //TODO: enable when supported by driver + return INVALID_OPERATION; +} + +}; // namespace android
diff --git a/libhardware_legacy/audio/A2dpAudioInterface.h b/libhardware_legacy/audio/A2dpAudioInterface.h new file mode 100644 index 0000000..8fe9745 --- /dev/null +++ b/libhardware_legacy/audio/A2dpAudioInterface.h
@@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef A2DP_AUDIO_HARDWARE_H +#define A2DP_AUDIO_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioHardwareBase.h> + + +namespace android_audio_legacy { + using android::Mutex; + +class A2dpAudioInterface : public AudioHardwareBase +{ + class A2dpAudioStreamOut; + +public: + A2dpAudioInterface(AudioHardwareInterface* hw); + virtual ~A2dpAudioInterface(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + virtual status_t setMode(int mode); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); +// static AudioHardwareInterface* createA2dpInterface(); + +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + class A2dpAudioStreamOut : public AudioStreamOut { + public: + A2dpAudioStreamOut(); + virtual ~A2dpAudioStreamOut(); + status_t set(uint32_t device, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + virtual uint32_t sampleRate() const { return 44100; } + // SBC codec wants a multiple of 512 + virtual size_t bufferSize() const { return 512 * 20; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); + + private: + friend class A2dpAudioInterface; + status_t init(); + status_t close(); + status_t close_l(); + status_t setAddress(const char* address); + status_t setBluetoothEnabled(bool enabled); + status_t setSuspended(bool onOff); + status_t standby_l(); + + private: + int mFd; + bool mStandby; + int mStartCount; + int mRetryCount; + char mA2dpAddress[20]; + void* mData; + Mutex mLock; + bool mBluetoothEnabled; + uint32_t mDevice; + bool mClosing; + bool mSuspended; + nsecs_t mLastWriteTime; + uint32_t mBufferDurationUs; + }; + + friend class A2dpAudioStreamOut; + + A2dpAudioStreamOut* mOutput; + AudioHardwareInterface *mHardwareInterface; + char mA2dpAddress[20]; + bool mBluetoothEnabled; + bool mSuspended; +}; + + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // A2DP_AUDIO_HARDWARE_H
diff --git a/libhardware_legacy/audio/Android.mk b/libhardware_legacy/audio/Android.mk new file mode 100644 index 0000000..d9d2d6a --- /dev/null +++ b/libhardware_legacy/audio/Android.mk
@@ -0,0 +1,89 @@ +# Copyright 2011 The Android Open Source Project + +#AUDIO_POLICY_TEST := true +#ENABLE_AUDIO_DUMP := true + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + AudioHardwareInterface.cpp \ + audio_hw_hal.cpp + +LOCAL_MODULE := libaudiohw_legacy +LOCAL_SHARED_LIBRARIES := libmedia +LOCAL_STATIC_LIBRARIES := libmedia_helper +LOCAL_CFLAGS := -Wno-unused-parameter -Wno-gnu-designator +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + AudioPolicyManagerBase.cpp \ + AudioPolicyCompatClient.cpp \ + audio_policy_hal.cpp + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +LOCAL_SHARED_LIBRARIES := libmedia +LOCAL_STATIC_LIBRARIES := libmedia_helper +LOCAL_MODULE := libaudiopolicy_legacy +LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include + +include $(BUILD_STATIC_LIBRARY) + +# The default audio policy, for now still implemented on top of legacy +# policy code +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + AudioPolicyManagerDefault.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libmedia \ + libutils \ + liblog + +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + libaudiopolicy_legacy + +LOCAL_MODULE := audio_policy.default +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include + +include $(BUILD_SHARED_LIBRARY) + +#ifeq ($(ENABLE_AUDIO_DUMP),true) +# LOCAL_SRC_FILES += AudioDumpInterface.cpp +# LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP +#endif +# +#ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) +# LOCAL_CFLAGS += -D GENERIC_AUDIO +#endif + +#ifeq ($(BOARD_HAVE_BLUETOOTH),true) +# LOCAL_SRC_FILES += A2dpAudioInterface.cpp +# LOCAL_SHARED_LIBRARIES += liba2dp +# LOCAL_C_INCLUDES += $(call include-path-for, bluez) +# +# LOCAL_CFLAGS += \ +# -DWITH_BLUETOOTH \ +#endif +# +#include $(BUILD_SHARED_LIBRARY) + +# AudioHardwareGeneric.cpp \ +# AudioHardwareStub.cpp \
diff --git a/libhardware_legacy/audio/AudioDumpInterface.cpp b/libhardware_legacy/audio/AudioDumpInterface.cpp new file mode 100644 index 0000000..62fdbd6 --- /dev/null +++ b/libhardware_legacy/audio/AudioDumpInterface.cpp
@@ -0,0 +1,573 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.cpp +** +** Copyright 2008, 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. +*/ + +#define LOG_TAG "AudioFlingerDump" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "AudioDumpInterface.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) + : mPolicyCommands(String8("")), mFileName(String8("")) +{ + if(hw == 0) { + ALOGE("Dump construct hw = 0"); + } + mFinalInterface = hw; + ALOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface); +} + + +AudioDumpInterface::~AudioDumpInterface() +{ + for (size_t i = 0; i < mOutputs.size(); i++) { + closeOutputStream((AudioStreamOut *)mOutputs[i]); + } + + for (size_t i = 0; i < mInputs.size(); i++) { + closeInputStream((AudioStreamIn *)mInputs[i]); + } + + if(mFinalInterface) delete mFinalInterface; +} + + +AudioStreamOut* AudioDumpInterface::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AudioStreamOut* outFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO; + uint32_t lRate = 44100; + + + outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); + if (outFinal != 0) { + lFormat = outFinal->format(); + lChannels = outFinal->channels(); + lRate = outFinal->sampleRate(); + } else { + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } + } + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } + } + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } + } + if (status) *status = NO_ERROR; + } + ALOGV("openOutputStream(), outFinal %p", outFinal); + + AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal, + devices, lFormat, lChannels, lRate); + mOutputs.add(dumOutput); + + return dumOutput; +} + +void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) +{ + AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out; + + if (mOutputs.indexOf(dumpOut) < 0) { + ALOGW("Attempt to close invalid output stream"); + return; + } + + ALOGV("closeOutputStream() output %p", out); + + dumpOut->standby(); + if (dumpOut->finalStream() != NULL) { + mFinalInterface->closeOutputStream(dumpOut->finalStream()); + } + + mOutputs.remove(dumpOut); + delete dumpOut; +} + +AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + AudioStreamIn* inFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; + uint32_t lRate = 8000; + + inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); + if (inFinal != 0) { + lFormat = inFinal->format(); + lChannels = inFinal->channels(); + lRate = inFinal->sampleRate(); + } else { + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } + } + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } + } + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } + } + if (status) *status = NO_ERROR; + } + ALOGV("openInputStream(), inFinal %p", inFinal); + + AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal, + devices, lFormat, lChannels, lRate); + mInputs.add(dumInput); + + return dumInput; +} +void AudioDumpInterface::closeInputStream(AudioStreamIn* in) +{ + AudioStreamInDump *dumpIn = (AudioStreamInDump *)in; + + if (mInputs.indexOf(dumpIn) < 0) { + ALOGW("Attempt to close invalid input stream"); + return; + } + dumpIn->standby(); + if (dumpIn->finalStream() != NULL) { + mFinalInterface->closeInputStream(dumpIn->finalStream()); + } + + mInputs.remove(dumpIn); + delete dumpIn; +} + + +status_t AudioDumpInterface::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + int valueInt; + ALOGV("setParameters %s", keyValuePairs.string()); + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + mFileName = value; + param.remove(String8("test_cmd_file_name")); + } + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); + param.remove(String8("test_cmd_policy")); + mPolicyCommands = param.toString(); + ALOGV("test_cmd_policy command %s written", mPolicyCommands.string()); + return NO_ERROR; + } + + if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioDumpInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + AudioParameter response; + String8 value; + +// ALOGV("getParameters %s", keys.string()); + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); + if (mPolicyCommands.length() != 0) { + response = AudioParameter(mPolicyCommands); + response.addInt(String8("test_cmd_policy"), 1); + } else { + response.addInt(String8("test_cmd_policy"), 0); + } + param.remove(String8("test_cmd_policy")); +// ALOGV("test_cmd_policy command %s read", mPolicyCommands.string()); + } + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + response.add(String8("test_cmd_file_name"), mFileName); + param.remove(String8("test_cmd_file_name")); + } + + String8 keyValuePairs = response.toString(); + + if (param.size() && mFinalInterface != 0 ) { + keyValuePairs += ";"; + keyValuePairs += mFinalInterface->getParameters(param.toString()); + } + + return keyValuePairs; +} + +status_t AudioDumpInterface::setMode(int mode) +{ + return mFinalInterface->setMode(mode); +} + +size_t AudioDumpInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mFinalInterface->getInputBufferSize(sampleRate, format, channelCount); +} + +// ---------------------------------------------------------------------------- + +AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate) + : mInterface(interface), mId(id), + mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices), + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) +{ + ALOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); +} + + +AudioStreamOutDump::~AudioStreamOutDump() +{ + ALOGV("AudioStreamOutDump destructor"); + Close(); +} + +ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) +{ + ssize_t ret; + + if (mFinalStream) { + ret = mFinalStream->write(buffer, bytes); + } else { + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); + ret = bytes; + } + if(!mFile) { + if (mInterface->fileName() != "") { + char name[255]; + sprintf(name, "%s_out_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + ALOGV("Opening dump file %s, fh %p", name, mFile); + } + } + if (mFile) { + fwrite(buffer, bytes, 1, mFile); + } + return ret; +} + +status_t AudioStreamOutDump::standby() +{ + ALOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); + + Close(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +uint32_t AudioStreamOutDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamOutDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamOutDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} +int AudioStreamOutDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} +uint32_t AudioStreamOutDump::latency() const +{ + if (mFinalStream != 0 ) return mFinalStream->latency(); + return 0; +} +status_t AudioStreamOutDump::setVolume(float left, float right) +{ + if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right); + return NO_ERROR; +} +status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) +{ + ALOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string()); + + if (mFinalStream != 0 ) { + return mFinalStream->setParameters(keyValuePairs); + } + + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + int valueInt; + status_t status = NO_ERROR; + + if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) { + mId = valueInt; + } + + if (param.getInt(String8("format"), valueInt) == NO_ERROR) { + if (mFile == 0) { + mFormat = valueInt; + } else { + status = INVALID_OPERATION; + } + } + if (param.getInt(String8("channels"), valueInt) == NO_ERROR) { + if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) { + mChannels = valueInt; + } else { + status = BAD_VALUE; + } + } + if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) { + if (valueInt > 0 && valueInt <= 48000) { + if (mFile == 0) { + mSampleRate = valueInt; + } else { + status = INVALID_OPERATION; + } + } else { + status = BAD_VALUE; + } + } + return status; +} + +String8 AudioStreamOutDump::getParameters(const String8& keys) +{ + if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); + + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} + +void AudioStreamOutDump::Close() +{ + if(mFile) { + fclose(mFile); + mFile = 0; + } +} + +status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames) +{ + if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames); + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate) + : mInterface(interface), mId(id), + mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) +{ + ALOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); +} + + +AudioStreamInDump::~AudioStreamInDump() +{ + Close(); +} + +ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) +{ + ssize_t ret; + + if (mFinalStream) { + ret = mFinalStream->read(buffer, bytes); + if(!mFile) { + if (mInterface->fileName() != "") { + char name[255]; + sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + ALOGV("Opening input dump file %s, fh %p", name, mFile); + } + } + if (mFile) { + fwrite(buffer, bytes, 1, mFile); + } + } else { + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); + ret = bytes; + if(!mFile) { + char name[255]; + strcpy(name, "/sdcard/music/sine440"); + if (channels() == AudioSystem::CHANNEL_IN_MONO) { + strcat(name, "_mo"); + } else { + strcat(name, "_st"); + } + if (format() == AudioSystem::PCM_16_BIT) { + strcat(name, "_16b"); + } else { + strcat(name, "_8b"); + } + if (sampleRate() < 16000) { + strcat(name, "_8k"); + } else if (sampleRate() < 32000) { + strcat(name, "_22k"); + } else if (sampleRate() < 48000) { + strcat(name, "_44k"); + } else { + strcat(name, "_48k"); + } + strcat(name, ".wav"); + mFile = fopen(name, "rb"); + ALOGV("Opening input read file %s, fh %p", name, mFile); + if (mFile) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } + } + if (mFile) { + ssize_t bytesRead = fread(buffer, bytes, 1, mFile); + if (bytesRead >=0 && bytesRead < bytes) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile); + } + } + } + + return ret; +} + +status_t AudioStreamInDump::standby() +{ + ALOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); + + Close(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +status_t AudioStreamInDump::setGain(float gain) +{ + if (mFinalStream != 0 ) return mFinalStream->setGain(gain); + return NO_ERROR; +} + +uint32_t AudioStreamInDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamInDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamInDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} + +int AudioStreamInDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} + +status_t AudioStreamInDump::setParameters(const String8& keyValuePairs) +{ + ALOGV("AudioStreamInDump::setParameters()"); + if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioStreamInDump::getParameters(const String8& keys) +{ + if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); + + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +unsigned int AudioStreamInDump::getInputFramesLost() const +{ + if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost(); + return 0; +} + +status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} + +void AudioStreamInDump::Close() +{ + if(mFile) { + fclose(mFile); + mFile = 0; + } +} +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioDumpInterface.h b/libhardware_legacy/audio/AudioDumpInterface.h new file mode 100644 index 0000000..814ce5f --- /dev/null +++ b/libhardware_legacy/audio/AudioDumpInterface.h
@@ -0,0 +1,170 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.h +** +** Copyright 2008, 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. +*/ + +#ifndef ANDROID_AUDIO_DUMP_INTERFACE_H +#define ANDROID_AUDIO_DUMP_INTERFACE_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/String8.h> +#include <utils/SortedVector.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +#define AUDIO_DUMP_WAVE_HDR_SIZE 44 + +class AudioDumpInterface; + +class AudioStreamOutDump : public AudioStreamOut { +public: + AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); + ~AudioStreamOutDump(); + + virtual ssize_t write(const void* buffer, size_t bytes); + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + virtual uint32_t latency() const; + virtual status_t setVolume(float left, float right); + virtual status_t standby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t dump(int fd, const Vector<String16>& args); + void Close(void); + AudioStreamOut* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } + int getId() { return mId; } + virtual status_t getRenderPosition(uint32_t *dspFrames); + +private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mLatency; // + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; + AudioStreamOut *mFinalStream; + FILE *mFile; // output file + int mFileCount; +}; + +class AudioStreamInDump : public AudioStreamIn { +public: + AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); + ~AudioStreamInDump(); + + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + + virtual status_t setGain(float gain); + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t standby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const; + virtual status_t dump(int fd, const Vector<String16>& args); + void Close(void); + AudioStreamIn* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } + +private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; + AudioStreamIn *mFinalStream; + FILE *mFile; // output file + int mFileCount; +}; + +class AudioDumpInterface : public AudioHardwareBase +{ + +public: + AudioDumpInterface(AudioHardwareInterface* hw); + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual ~AudioDumpInterface(); + + virtual status_t initCheck() + {return mFinalInterface->initCheck();} + virtual status_t setVoiceVolume(float volume) + {return mFinalInterface->setVoiceVolume(volume);} + virtual status_t setMasterVolume(float volume) + {return mFinalInterface->setMasterVolume(volume);} + + virtual status_t setMode(int mode); + + // mic mute + virtual status_t setMicMute(bool state) + {return mFinalInterface->setMicMute(state);} + virtual status_t getMicMute(bool* state) + {return mFinalInterface->getMicMute(state);} + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + + virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } + + String8 fileName() const { return mFileName; } +protected: + + AudioHardwareInterface *mFinalInterface; + SortedVector<AudioStreamOutDump *> mOutputs; + SortedVector<AudioStreamInDump *> mInputs; + Mutex mLock; + String8 mPolicyCommands; + String8 mFileName; +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_DUMP_INTERFACE_H
diff --git a/libhardware_legacy/audio/AudioHardwareGeneric.cpp b/libhardware_legacy/audio/AudioHardwareGeneric.cpp new file mode 100644 index 0000000..a2b00f8 --- /dev/null +++ b/libhardware_legacy/audio/AudioHardwareGeneric.cpp
@@ -0,0 +1,413 @@ +/* +** +** Copyright 2007, 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 <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sched.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define LOG_TAG "AudioHardware" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareGeneric.h" +#include <media/AudioRecord.h> + +#include <hardware_legacy/AudioSystemLegacy.h> + +namespace android_audio_legacy { + +// ---------------------------------------------------------------------------- + +static char const * const kAudioDeviceName = "/dev/eac"; + +// ---------------------------------------------------------------------------- + +AudioHardwareGeneric::AudioHardwareGeneric() + : mOutput(0), mInput(0), mFd(-1), mMicMute(false) +{ + mFd = ::open(kAudioDeviceName, O_RDWR); +} + +AudioHardwareGeneric::~AudioHardwareGeneric() +{ + if (mFd >= 0) ::close(mFd); + closeOutputStream((AudioStreamOut *)mOutput); + closeInputStream((AudioStreamIn *)mInput); +} + +status_t AudioHardwareGeneric::initCheck() +{ + if (mFd >= 0) { + if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR) + return NO_ERROR; + } + return NO_INIT; +} + +AudioStreamOut* AudioHardwareGeneric::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AutoMutex lock(mLock); + + // only one output stream allowed + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); + status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + return mOutput; +} + +void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) { + if (mOutput && out == mOutput) { + delete mOutput; + mOutput = 0; + } +} + +AudioStreamIn* AudioHardwareGeneric::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + return 0; + } + + AutoMutex lock(mLock); + + // only one input stream allowed + if (mInput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamInGeneric* in = new AudioStreamInGeneric(); + status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mInput = in; + } else { + delete in; + } + return mInput; +} + +void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) { + if (mInput && in == mInput) { + delete mInput; + mInput = 0; + } +} + +status_t AudioHardwareGeneric::setVoiceVolume(float v) +{ + // Implement: set voice volume + return NO_ERROR; +} + +status_t AudioHardwareGeneric::setMasterVolume(float v) +{ + // Implement: set master volume + // return error - software mixer will handle it + return INVALID_OPERATION; +} + +status_t AudioHardwareGeneric::setMicMute(bool state) +{ + mMicMute = state; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareGeneric::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + if (mInput) { + mInput->dump(fd, args); + } + if (mOutput) { + mOutput->dump(fd, args); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutGeneric::set( + AudioHardwareGeneric *hw, + int fd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())) { + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mAudioHardware = hw; + mFd = fd; + mDevice = devices; + return NO_ERROR; +} + +AudioStreamOutGeneric::~AudioStreamOutGeneric() +{ +} + +ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) +{ + Mutex::Autolock _l(mLock); + return ssize_t(::write(mFd, buffer, bytes)); +} + +status_t AudioStreamOutGeneric::standby() +{ + // Implement: audio hardware to standby mode + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + ALOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamOutGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + ALOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + +status_t AudioStreamOutGeneric::getRenderPosition(uint32_t *dspFrames) +{ + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +// record functions +status_t AudioStreamInGeneric::set( + AudioHardwareGeneric *hw, + int fd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics) +{ + if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE; + ALOGV("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate); + // check values + if ((*pFormat != format()) || + (*pChannels != channels()) || + (*pRate != sampleRate())) { + ALOGE("Error opening input channel"); + *pFormat = format(); + *pChannels = channels(); + *pRate = sampleRate(); + return BAD_VALUE; + } + + mAudioHardware = hw; + mFd = fd; + mDevice = devices; + return NO_ERROR; +} + +AudioStreamInGeneric::~AudioStreamInGeneric() +{ +} + +ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) +{ + AutoMutex lock(mLock); + if (mFd < 0) { + ALOGE("Attempt to read from unopened device"); + return NO_INIT; + } + return ::read(mFd, buffer, bytes); +} + +status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + ALOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamInGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + ALOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioHardwareGeneric.h b/libhardware_legacy/audio/AudioHardwareGeneric.h new file mode 100644 index 0000000..55498dc --- /dev/null +++ b/libhardware_legacy/audio/AudioHardwareGeneric.h
@@ -0,0 +1,156 @@ +/* +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_GENERIC_H +#define ANDROID_AUDIO_HARDWARE_GENERIC_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioSystemLegacy.h> +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android_audio_legacy { + using android::Mutex; + using android::AutoMutex; + +// ---------------------------------------------------------------------------- + +class AudioHardwareGeneric; + +class AudioStreamOutGeneric : public AudioStreamOut { +public: + AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamOutGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 20; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; + uint32_t mDevice; +}; + +class AudioStreamInGeneric : public AudioStreamIn { +public: + AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamInGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics); + + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return INVALID_OPERATION; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const { return 0; } + virtual status_t addAudioEffect(effect_handle_t effect) { return NO_ERROR; } + virtual status_t removeAudioEffect(effect_handle_t effect) { return NO_ERROR; } + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; + uint32_t mDevice; +}; + + +class AudioHardwareGeneric : public AudioHardwareBase +{ +public: + AudioHardwareGeneric(); + virtual ~AudioHardwareGeneric(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + + void closeOutputStream(AudioStreamOutGeneric* out); + void closeInputStream(AudioStreamInGeneric* in); +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + status_t dumpInternals(int fd, const Vector<String16>& args); + + Mutex mLock; + AudioStreamOutGeneric *mOutput; + AudioStreamInGeneric *mInput; + int mFd; + bool mMicMute; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H
diff --git a/libhardware_legacy/audio/AudioHardwareInterface.cpp b/libhardware_legacy/audio/AudioHardwareInterface.cpp new file mode 100644 index 0000000..dbf6f33 --- /dev/null +++ b/libhardware_legacy/audio/AudioHardwareInterface.cpp
@@ -0,0 +1,164 @@ +/* +** +** Copyright 2007, 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 <cutils/properties.h> +#include <string.h> +#include <unistd.h> +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioHardwareInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" +#include "AudioHardwareGeneric.h" + +#ifdef ENABLE_AUDIO_DUMP +#include "AudioDumpInterface.h" +#endif + + +// change to 1 to log routing calls +#define LOG_ROUTING_CALLS 1 + +namespace android_audio_legacy { + +#if LOG_ROUTING_CALLS +static const char* routingModeStrings[] = +{ + "OUT OF RANGE", + "INVALID", + "CURRENT", + "NORMAL", + "RINGTONE", + "IN_CALL", + "IN_COMMUNICATION" +}; + +static const char* routeNone = "NONE"; + +static const char* displayMode(int mode) +{ + if ((mode < AudioSystem::MODE_INVALID) || (mode >= AudioSystem::NUM_MODES)) + return routingModeStrings[0]; + return routingModeStrings[mode+3]; +} +#endif + +// ---------------------------------------------------------------------------- + +AudioHardwareInterface* AudioHardwareInterface::create() +{ + return NULL; +} + +AudioStreamOut::~AudioStreamOut() +{ +} + +// default implementation is unsupported +status_t AudioStreamOut::getNextWriteTimestamp(int64_t *timestamp) +{ + return INVALID_OPERATION; +} + +AudioStreamIn::~AudioStreamIn() {} + +AudioHardwareBase::AudioHardwareBase() +{ + mMode = 0; +} + +status_t AudioHardwareBase::setMode(int mode) +{ +#if LOG_ROUTING_CALLS + ALOGD("setMode(%s)", displayMode(mode)); +#endif + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + if (mMode == mode) + return ALREADY_EXISTS; + mMode = mode; + return NO_ERROR; +} + +// default implementation +status_t AudioHardwareBase::setParameters(const String8& keyValuePairs) +{ + return NO_ERROR; +} + +// default implementation +String8 AudioHardwareBase::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +// default implementation +size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + if (sampleRate != 8000) { + ALOGW("getInputBufferSize bad sampling rate: %d", sampleRate); + return 0; + } + if (format != AudioSystem::PCM_16_BIT) { + ALOGW("getInputBufferSize bad format: %d", format); + return 0; + } + if (channelCount != 1) { + ALOGW("getInputBufferSize bad channel count: %d", channelCount); + return 0; + } + + return 320; +} + +// default implementation is unsupported +status_t AudioHardwareBase::getMasterVolume(float *volume) +{ + return INVALID_OPERATION; +} + +status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); + result.append(buffer); + ::write(fd, result.string(), result.size()); + dump(fd, args); // Dump the state of the concrete child. + return NO_ERROR; +} + +// default implementation calls its "without flags" counterpart +AudioStreamOut* AudioHardwareInterface::openOutputStreamWithFlags(uint32_t devices, + audio_output_flags_t flags, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status) +{ + return openOutputStream(devices, format, channels, sampleRate, status); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioHardwareStub.cpp b/libhardware_legacy/audio/AudioHardwareStub.cpp new file mode 100644 index 0000000..fd647d5 --- /dev/null +++ b/libhardware_legacy/audio/AudioHardwareStub.cpp
@@ -0,0 +1,215 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.cpp +** +** Copyright 2007, 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 <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <unistd.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" +#include <media/AudioRecord.h> + +namespace android_audio_legacy { + +// ---------------------------------------------------------------------------- + +AudioHardwareStub::AudioHardwareStub() : mMicMute(false) +{ +} + +AudioHardwareStub::~AudioHardwareStub() +{ +} + +status_t AudioHardwareStub::initCheck() +{ + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareStub::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AudioStreamOutStub* out = new AudioStreamOutStub(); + status_t lStatus = out->set(format, channels, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return out; + delete out; + return 0; +} + +void AudioHardwareStub::closeOutputStream(AudioStreamOut* out) +{ + delete out; +} + +AudioStreamIn* AudioHardwareStub::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + return 0; + } + + AudioStreamInStub* in = new AudioStreamInStub(); + status_t lStatus = in->set(format, channels, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return in; + delete in; + return 0; +} + +void AudioHardwareStub::closeInputStream(AudioStreamIn* in) +{ + delete in; +} + +status_t AudioHardwareStub::setVoiceVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::setMasterVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareStub::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + + return NO_ERROR; +} + +ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) +{ + // fake timing for audio output + usleep(bytes * 1000000 / sizeof(int16_t) / + audio_channel_count_from_out_mask(channels()) / sampleRate()); + return bytes; +} + +status_t AudioStreamOutStub::standby() +{ + return NO_ERROR; +} + +status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +String8 AudioStreamOutStub::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames) +{ + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics) +{ + return NO_ERROR; +} + +ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) +{ + // fake timing for audio input + usleep(bytes * 1000000 / sizeof(int16_t) / + audio_channel_count_from_in_mask(channels()) / sampleRate()); + memset(buffer, 0, bytes); + return bytes; +} + +status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInStub::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +String8 AudioStreamInStub::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +AudioHardwareInterface* createAudioHardware(void) { + return new AudioHardwareStub(); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioHardwareStub.h b/libhardware_legacy/audio/AudioHardwareStub.h new file mode 100644 index 0000000..c5f7a80 --- /dev/null +++ b/libhardware_legacy/audio/AudioHardwareStub.h
@@ -0,0 +1,108 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_STUB_H +#define ANDROID_AUDIO_HARDWARE_STUB_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android_audio_legacy { + +// ---------------------------------------------------------------------------- + +class AudioStreamOutStub : public AudioStreamOut { +public: + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate); + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } + virtual status_t setVolume(float left, float right) { return NO_ERROR; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); +}; + +class AudioStreamInStub : public AudioStreamIn { +public: + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics); + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return NO_ERROR; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const { return 0; } + virtual status_t addAudioEffect(effect_handle_t effect) { return NO_ERROR; } + virtual status_t removeAudioEffect(effect_handle_t effect) { return NO_ERROR; } +}; + +class AudioHardwareStub : public AudioHardwareBase +{ +public: + AudioHardwareStub(); + virtual ~AudioHardwareStub(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } + virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + + bool mMicMute; +private: + status_t dumpInternals(int fd, const Vector<String16>& args); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_STUB_H
diff --git a/libhardware_legacy/audio/AudioPolicyCompatClient.cpp b/libhardware_legacy/audio/AudioPolicyCompatClient.cpp new file mode 100644 index 0000000..9d02d98 --- /dev/null +++ b/libhardware_legacy/audio/AudioPolicyCompatClient.cpp
@@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "AudioPolicyCompatClient" +//#define LOG_NDEBUG 0 + +#include <stdint.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +#include <hardware_legacy/AudioSystemLegacy.h> + +#include "AudioPolicyCompatClient.h" + +namespace android_audio_legacy { + +audio_module_handle_t AudioPolicyCompatClient::loadHwModule(const char *moduleName) +{ + return mServiceOps->load_hw_module(mService, moduleName); +} + +audio_io_handle_t AudioPolicyCompatClient::openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + return mServiceOps->open_output_on_module(mService, module, pDevices, pSamplingRate, + pFormat, pChannelMask, pLatencyMs, + flags, offloadInfo); +} + +audio_io_handle_t AudioPolicyCompatClient::openDuplicateOutput(audio_io_handle_t output1, + audio_io_handle_t output2) +{ + return mServiceOps->open_duplicate_output(mService, output1, output2); +} + +status_t AudioPolicyCompatClient::closeOutput(audio_io_handle_t output) +{ + return mServiceOps->close_output(mService, output); +} + +status_t AudioPolicyCompatClient::suspendOutput(audio_io_handle_t output) +{ + return mServiceOps->suspend_output(mService, output); +} + +status_t AudioPolicyCompatClient::restoreOutput(audio_io_handle_t output) +{ + return mServiceOps->restore_output(mService, output); +} + +audio_io_handle_t AudioPolicyCompatClient::openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + return mServiceOps->open_input_on_module(mService, module, pDevices, + pSamplingRate, pFormat, pChannelMask); +} + +status_t AudioPolicyCompatClient::closeInput(audio_io_handle_t input) +{ + return mServiceOps->close_input(mService, input); +} + +status_t AudioPolicyCompatClient::invalidateStream(AudioSystem::stream_type stream) +{ + return mServiceOps->invalidate_stream(mService, (audio_stream_type_t)stream); +} + +status_t AudioPolicyCompatClient::moveEffects(int session, audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput) +{ + return mServiceOps->move_effects(mService, session, srcOutput, dstOutput); +} + +String8 AudioPolicyCompatClient::getParameters(audio_io_handle_t ioHandle, const String8& keys) +{ + char *str; + String8 out_str8; + + str = mServiceOps->get_parameters(mService, ioHandle, keys.string()); + out_str8 = String8(str); + free(str); + + return out_str8; +} + +void AudioPolicyCompatClient::setParameters(audio_io_handle_t ioHandle, + const String8& keyValuePairs, + int delayMs) +{ + mServiceOps->set_parameters(mService, ioHandle, keyValuePairs.string(), + delayMs); +} + +status_t AudioPolicyCompatClient::setStreamVolume( + AudioSystem::stream_type stream, + float volume, + audio_io_handle_t output, + int delayMs) +{ + return mServiceOps->set_stream_volume(mService, (audio_stream_type_t)stream, + volume, output, delayMs); +} + +status_t AudioPolicyCompatClient::startTone(ToneGenerator::tone_type tone, + AudioSystem::stream_type stream) +{ + return mServiceOps->start_tone(mService, + AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION, + (audio_stream_type_t)stream); +} + +status_t AudioPolicyCompatClient::stopTone() +{ + return mServiceOps->stop_tone(mService); +} + +status_t AudioPolicyCompatClient::setVoiceVolume(float volume, int delayMs) +{ + return mServiceOps->set_voice_volume(mService, volume, delayMs); +} + +}; // namespace android_audio_legacy
diff --git a/libhardware_legacy/audio/AudioPolicyCompatClient.h b/libhardware_legacy/audio/AudioPolicyCompatClient.h new file mode 100644 index 0000000..19f76e1 --- /dev/null +++ b/libhardware_legacy/audio/AudioPolicyCompatClient.h
@@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef ANDROID_AUDIOPOLICYCLIENTLEGACY_H +#define ANDROID_AUDIOPOLICYCLIENTLEGACY_H + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +#include <hardware_legacy/AudioSystemLegacy.h> +#include <hardware_legacy/AudioPolicyInterface.h> + +/************************************/ +/* FOR BACKWARDS COMPATIBILITY ONLY */ +/************************************/ +namespace android_audio_legacy { + +class AudioPolicyCompatClient : public AudioPolicyClientInterface { +public: + AudioPolicyCompatClient(struct audio_policy_service_ops *serviceOps, + void *service) : + mServiceOps(serviceOps) , mService(service) {} + + virtual audio_module_handle_t loadHwModule(const char *moduleName); + + virtual audio_io_handle_t openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, + audio_io_handle_t output2); + virtual status_t closeOutput(audio_io_handle_t output); + virtual status_t suspendOutput(audio_io_handle_t output); + virtual status_t restoreOutput(audio_io_handle_t output); + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask); + virtual status_t closeInput(audio_io_handle_t input); + virtual status_t invalidateStream(AudioSystem::stream_type stream); + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput); + + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + virtual void setParameters(audio_io_handle_t ioHandle, + const String8& keyValuePairs, + int delayMs = 0); + virtual status_t setStreamVolume(AudioSystem::stream_type stream, + float volume, + audio_io_handle_t output, + int delayMs = 0); + virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream); + virtual status_t stopTone(); + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + +private: + struct audio_policy_service_ops* mServiceOps; + void* mService; +}; + +}; // namespace android_audio_legacy + +#endif // ANDROID_AUDIOPOLICYCLIENTLEGACY_H
diff --git a/libhardware_legacy/audio/AudioPolicyManagerBase.cpp b/libhardware_legacy/audio/AudioPolicyManagerBase.cpp new file mode 100644 index 0000000..60790b7 --- /dev/null +++ b/libhardware_legacy/audio/AudioPolicyManagerBase.cpp
@@ -0,0 +1,4368 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyManagerBase" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +// A device mask for all audio input devices that are considered "virtual" when evaluating +// active inputs in getActiveInput() +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL AUDIO_DEVICE_IN_REMOTE_SUBMIX +// A device mask for all audio output devices that are considered "remote" when evaluating +// active output devices in isStreamActiveRemotely() +#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX + +#include <inttypes.h> +#include <math.h> + +#include <cutils/properties.h> +#include <utils/Log.h> +#include <utils/Timers.h> + +#include <hardware/audio.h> +#include <hardware/audio_effect.h> +#include <hardware_legacy/audio_policy_conf.h> +#include <hardware_legacy/AudioPolicyManagerBase.h> + +namespace android_audio_legacy { + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManagerBase::setDeviceConnectionState(audio_devices_t device, + AudioSystem::device_connection_state state, + const char *device_address) +{ + // device_address can be NULL and should be handled as an empty string in this case, + // and it is not checked by AudioPolicyInterfaceImpl.cpp + if (device_address == NULL) { + device_address = ""; + } + ALOGV("setDeviceConnectionState() device: 0x%X, state %d, address %s", device, state, device_address); + + // connect/disconnect only 1 device at a time + if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + + if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) { + ALOGE("setDeviceConnectionState() invalid address: %s", device_address); + return BAD_VALUE; + } + + // handle output devices + if (audio_is_output_device(device)) { + SortedVector <audio_io_handle_t> outputs; + + if (!mHasA2dp && audio_is_a2dp_out_device(device)) { + ALOGE("setDeviceConnectionState() invalid A2DP device: %x", device); + return BAD_VALUE; + } + if (!mHasUsb && audio_is_usb_out_device(device)) { + ALOGE("setDeviceConnectionState() invalid USB audio device: %x", device); + return BAD_VALUE; + } + if (!mHasRemoteSubmix && audio_is_remote_submix_device((audio_devices_t)device)) { + ALOGE("setDeviceConnectionState() invalid remote submix audio device: %x", device); + return BAD_VALUE; + } + + // save a copy of the opened output descriptors before any output is opened or closed + // by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies() + mPreviousOutputs = mOutputs; + String8 paramStr; + switch (state) + { + // handle output device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: + if (mAvailableOutputDevices & device) { + ALOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() connecting device %x", device); + + if (mHasA2dp && audio_is_a2dp_out_device(device)) { + // handle A2DP device connection + AudioParameter param; + param.add(String8(AUDIO_PARAMETER_A2DP_SINK_ADDRESS), String8(device_address)); + paramStr = param.toString(); + } else if (mHasUsb && audio_is_usb_out_device(device)) { + // handle USB device connection + paramStr = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + } + + if (checkOutputsForDevice(device, state, outputs, paramStr) != NO_ERROR) { + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs", + outputs.size()); + // register new device as available + mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices | device); + + if (mHasA2dp && audio_is_a2dp_out_device(device)) { + // handle A2DP device connection + mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + mA2dpSuspended = false; + } else if (audio_is_bluetooth_sco_device(device)) { + // handle SCO device connection + mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + } else if (mHasUsb && audio_is_usb_out_device(device)) { + // handle USB device connection + mUsbOutCardAndDevice = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + } + + break; + // handle output device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: { + if (!(mAvailableOutputDevices & device)) { + ALOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting device %x", device); + // remove device from available output devices + mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices & ~device); + checkOutputsForDevice(device, state, outputs, paramStr); + + if (mHasA2dp && audio_is_a2dp_out_device(device)) { + // handle A2DP device disconnection + mA2dpDeviceAddress = ""; + mA2dpSuspended = false; + } else if (audio_is_bluetooth_sco_device(device)) { + // handle SCO device disconnection + mScoDeviceAddress = ""; + } else if (mHasUsb && audio_is_usb_out_device(device)) { + // handle USB device disconnection + mUsbOutCardAndDevice = ""; + } + // not currently handling multiple simultaneous submixes: ignoring remote submix + // case and address + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + checkA2dpSuspend(); + checkOutputForAllStrategies(); + // outputs must be closed after checkOutputForAllStrategies() is executed + if (!outputs.isEmpty()) { + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + // close unused outputs after device disconnection or direct outputs that have been + // opened by checkOutputsForDevice() to query dynamic parameters + if ((state == AudioSystem::DEVICE_STATE_UNAVAILABLE) || + (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && + (desc->mDirectOpenCount == 0))) { + closeOutput(outputs[i]); + } + } + } + + updateDevicesAndOutputs(); + for (size_t i = 0; i < mOutputs.size(); i++) { + // do not force device change on duplicated output because if device is 0, it will + // also force a device 0 for the two outputs it is duplicated to which may override + // a valid device selection on those outputs. + setOutputDevice(mOutputs.keyAt(i), + getNewDevice(mOutputs.keyAt(i), true /*fromCache*/), + !mOutputs.valueAt(i)->isDuplicated(), + 0); + } + + return NO_ERROR; + } // end if is output device + + // handle input devices + if (audio_is_input_device(device)) { + SortedVector <audio_io_handle_t> inputs; + + String8 paramStr; + switch (state) + { + // handle input device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: { + if (mAvailableInputDevices & device) { + ALOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + + if (mHasUsb && audio_is_usb_in_device(device)) { + // handle USB device connection + paramStr = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + } else if (mHasA2dp && audio_is_a2dp_in_device(device)) { + // handle A2DP device connection + AudioParameter param; + param.add(String8(AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS), String8(device_address)); + paramStr = param.toString(); + } + + if (checkInputsForDevice(device, state, inputs, paramStr) != NO_ERROR) { + return INVALID_OPERATION; + } + mAvailableInputDevices = mAvailableInputDevices | (device & ~AUDIO_DEVICE_BIT_IN); + } + break; + + // handle input device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: { + if (!(mAvailableInputDevices & device)) { + ALOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + checkInputsForDevice(device, state, inputs, paramStr); + mAvailableInputDevices = (audio_devices_t) (mAvailableInputDevices & ~device); + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + closeAllInputs(); + + return NO_ERROR; + } // end if is input device + + ALOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(audio_devices_t device, + const char *device_address) +{ + // similar to setDeviceConnectionState + if (device_address == NULL) { + device_address = ""; + } + AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE; + String8 address = String8(device_address); + if (audio_is_output_device(device)) { + if (device & mAvailableOutputDevices) { + if (audio_is_a2dp_out_device(device) && + (!mHasA2dp || (address != "" && mA2dpDeviceAddress != address))) { + return state; + } + if (audio_is_bluetooth_sco_device(device) && + address != "" && mScoDeviceAddress != address) { + return state; + } + if (audio_is_usb_out_device(device) && + (!mHasUsb || (address != "" && mUsbOutCardAndDevice != address))) { + ALOGE("getDeviceConnectionState() invalid device: %x", device); + return state; + } + if (audio_is_remote_submix_device((audio_devices_t)device) && !mHasRemoteSubmix) { + return state; + } + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } else if (audio_is_input_device(device)) { + if (device & mAvailableInputDevices) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } + + return state; +} + +void AudioPolicyManagerBase::setPhoneState(int state) +{ + ALOGV("setPhoneState() state %d", state); + audio_devices_t newDevice = AUDIO_DEVICE_NONE; + if (state < 0 || state >= AudioSystem::NUM_MODES) { + ALOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return; + } + + // if leaving call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isInCall()) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, false, true); + } + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + bool force = false; + + // are we entering or starting a call + if (!isStateInCall(oldState) && isStateInCall(state)) { + ALOGV(" Entering call in setPhoneState()"); + // force routing command to audio hardware when starting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; + } + } else if (isStateInCall(oldState) && !isStateInCall(state)) { + ALOGV(" Exiting call in setPhoneState()"); + // force routing command to audio hardware when exiting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_DTMF][j]; + } + } else if (isStateInCall(state) && (state != oldState)) { + ALOGV(" Switching between telephony and VoIP in setPhoneState()"); + // force routing command to audio hardware when switching between telephony and VoIP + // even if no device change is needed + force = true; + } + + // check for device and output changes triggered by new phone state + newDevice = getNewDevice(mPrimaryOutput, false /*fromCache*/); + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + + // force routing command to audio hardware when ending call + // even if no device change is needed + if (isStateInCall(oldState) && newDevice == AUDIO_DEVICE_NONE) { + newDevice = hwOutputDesc->device(); + } + + int delayMs = 0; + if (isStateInCall(state)) { + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + // mute media and sonification strategies and delay device switch by the largest + // latency of any output where either strategy is active. + // This avoid sending the ring tone or music tail into the earpiece or headset. + if ((desc->isStrategyActive(STRATEGY_MEDIA, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime) || + desc->isStrategyActive(STRATEGY_SONIFICATION, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && + (delayMs < (int)desc->mLatency*2)) { + delayMs = desc->mLatency*2; + } + setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); + setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/)); + } + } + + // change routing is necessary + setOutputDevice(mPrimaryOutput, newDevice, force, delayMs); + + // if entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isStateInCall(state)) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, true, true); + } + } + + // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE + if (state == AudioSystem::MODE_RINGTONE && + isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { + mLimitRingtoneVolume = true; + } else { + mLimitRingtoneVolume = false; + } +} + +void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + + bool forceVolumeReeval = false; + switch(usage) { + case AudioSystem::FOR_COMMUNICATION: + if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO && + config != AudioSystem::FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return; + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AudioSystem::FOR_MEDIA: + if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP && + config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_ANALOG_DOCK && + config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE && + config != AudioSystem::FORCE_NO_BT_A2DP) { + ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return; + } + mForceUse[usage] = config; + break; + case AudioSystem::FOR_RECORD: + if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return; + } + mForceUse[usage] = config; + break; + case AudioSystem::FOR_DOCK: + if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK && + config != AudioSystem::FORCE_BT_DESK_DOCK && + config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_ANALOG_DOCK && + config != AudioSystem::FORCE_DIGITAL_DOCK) { + ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AudioSystem::FOR_SYSTEM: + if (config != AudioSystem::FORCE_NONE && + config != AudioSystem::FORCE_SYSTEM_ENFORCED) { + ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + default: + ALOGW("setForceUse() invalid usage %d", usage); + break; + } + + // check for device and output changes triggered by new force usage + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/); + setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); + if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { + applyStreamVolumes(output, newDevice, 0, true); + } + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); + audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) { + ALOGV("setForceUse() changing device from %x to %x for input %d", + inputDesc->mDevice, newDevice, activeInput); + inputDesc->mDevice = newDevice; + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); + mpClientInterface->setParameters(activeInput, param.toString()); + } + } + +} + +AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value) +{ + ALOGV("setSystemProperty() property %s, value %s", property, value); +} + +// Find a direct output profile compatible with the parameters passed, even if the input flags do +// not explicitly request a direct output +AudioPolicyManagerBase::IOProfile *AudioPolicyManagerBase::getProfileForDirectOutput( + audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) +{ + for (size_t i = 0; i < mHwModules.size(); i++) { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { + IOProfile *profile = mHwModules[i]->mOutputProfiles[j]; + if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) { + if (mAvailableOutputDevices & profile->mSupportedDevices) { + return mHwModules[i]->mOutputProfiles[j]; + } + } + } else { + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, + AUDIO_OUTPUT_FLAG_DIRECT)) { + if (mAvailableOutputDevices & profile->mSupportedDevices) { + return mHwModules[i]->mOutputProfiles[j]; + } + } + } + } + } + return 0; +} + +audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::output_flags flags, + const audio_offload_info_t *offloadInfo) +{ + audio_io_handle_t output = 0; + uint32_t latency = 0; + routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + ALOGV("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x", + device, stream, samplingRate, format, channelMask, flags); + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + ALOGV("getOutput() opening test output"); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = mTestDevice; + outputDesc->mSamplingRate = mTestSamplingRate; + outputDesc->mFormat = mTestFormat; + outputDesc->mChannelMask = mTestChannels; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = (audio_output_flags_t)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + mTestOutputs[mCurOutput] = mpClientInterface->openOutput(0, &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags, + offloadInfo); + if (mTestOutputs[mCurOutput]) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"),mCurOutput); + mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); + addOutput(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + + // open a direct output if required by specified parameters + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flags = (AudioSystem::output_flags)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + IOProfile *profile = NULL; + if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || + !isNonOffloadableEffectEnabled()) { + profile = getProfileForDirectOutput(device, + samplingRate, + format, + channelMask, + (audio_output_flags_t)flags); + } + + if (profile != NULL) { + AudioOutputDescriptor *outputDesc = NULL; + + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (profile == desc->mProfile)) { + outputDesc = desc; + // reuse direct output if currently open and configured with same parameters + if ((samplingRate == outputDesc->mSamplingRate) && + (format == outputDesc->mFormat) && + (channelMask == outputDesc->mChannelMask)) { + outputDesc->mDirectOpenCount++; + ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i)); + return mOutputs.keyAt(i); + } + } + } + // close direct output if currently open and configured with different parameters + if (outputDesc != NULL) { + closeOutput(outputDesc->mId); + } + outputDesc = new AudioOutputDescriptor(profile); + outputDesc->mDevice = device; + outputDesc->mSamplingRate = samplingRate; + outputDesc->mFormat = format; + outputDesc->mChannelMask = channelMask; + outputDesc->mLatency = 0; + outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags); + outputDesc->mRefCount[stream] = 0; + outputDesc->mStopTime[stream] = 0; + outputDesc->mDirectOpenCount = 1; + output = mpClientInterface->openOutput(profile->mModule->mHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags, + offloadInfo); + + // only accept an output with the requested parameters + if (output == 0 || + (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) || + (format != AUDIO_FORMAT_DEFAULT && format != outputDesc->mFormat) || + (channelMask != 0 && channelMask != outputDesc->mChannelMask)) { + ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d," + "format %d %d, channelMask %04x %04x", output, samplingRate, + outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask, + outputDesc->mChannelMask); + if (output != 0) { + mpClientInterface->closeOutput(output); + } + delete outputDesc; + return 0; + } + audio_io_handle_t srcOutput = getOutputForEffect(); + addOutput(output, outputDesc); + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput == output) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput); + } + mPreviousOutputs = mOutputs; + ALOGV("getOutput() returns new direct output %d", output); + return output; + } + + // ignoring channel mask due to downmix capability in mixer + + // open a non direct output + + // for non direct outputs, only PCM is supported + if (audio_is_linear_pcm(format)) { + // get which output is suitable for the specified stream. The actual + // routing change will happen when startOutput() will be called + SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs); + + output = selectOutput(outputs, flags); + } + ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d," + "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags); + + ALOGV("getOutput() returns output %d", output); + + return output; +} + +audio_io_handle_t AudioPolicyManagerBase::selectOutput(const SortedVector<audio_io_handle_t>& outputs, + AudioSystem::output_flags flags) +{ + // select one output among several that provide a path to a particular device or set of + // devices (the list was previously build by getOutputsForDevice()). + // The priority is as follows: + // 1: the output with the highest number of requested policy flags + // 2: the primary output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + if (outputs.size() == 1) { + return outputs[0]; + } + + int maxCommonFlags = 0; + audio_io_handle_t outputFlags = 0; + audio_io_handle_t outputPrimary = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]); + if (!outputDesc->isDuplicated()) { + int commonFlags = (int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags); + if (commonFlags > maxCommonFlags) { + outputFlags = outputs[i]; + maxCommonFlags = commonFlags; + ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags); + } + if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + outputPrimary = outputs[i]; + } + } + } + + if (outputFlags != 0) { + return outputFlags; + } + if (outputPrimary != 0) { + return outputPrimary; + } + + return outputs[0]; +} + +status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session) +{ + ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("startOutput() unknown output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // increment usage count for this stream on the requested output: + // NOTE that the usage count is the same for duplicated output and hardware output which is + // necessary for a correct control of hardware output routing by startOutput() and stopOutput() + outputDesc->changeRefCount(stream, 1); + + if (outputDesc->mRefCount[stream] == 1) { + audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/); + routing_strategy strategy = getStrategy(stream); + bool shouldWait = (strategy == STRATEGY_SONIFICATION) || + (strategy == STRATEGY_SONIFICATION_RESPECTFUL); + uint32_t waitMs = 0; + bool force = false; + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (desc != outputDesc) { + // force a device change if any other output is managed by the same hw + // module and has a current device selection that differs from selected device. + // In this case, the audio HAL must receive the new device selection so that it can + // change the device currently selected by the other active output. + if (outputDesc->sharesHwModuleWith(desc) && + desc->device() != newDevice) { + force = true; + } + // wait for audio on other active outputs to be presented when starting + // a notification so that audio focus effect can propagate. + uint32_t latency = desc->latency(); + if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) { + waitMs = latency; + } + } + } + uint32_t muteWaitMs = setOutputDevice(output, newDevice, force); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, true, false); + } + + // apply volume rules for current stream and device if necessary + checkAndSetVolume(stream, + mStreams[stream].getVolumeIndex(newDevice), + output, + newDevice); + + // update the outputs if starting an output with a stream that can affect notification + // routing + handleNotificationRoutingForStream(stream); + if (waitMs > muteWaitMs) { + usleep((waitMs - muteWaitMs) * 2 * 1000); + } + } + return NO_ERROR; +} + + +status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session) +{ + ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("stopOutput() unknown output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, false, false); + } + + if (outputDesc->mRefCount[stream] > 0) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + // store time at which the stream was stopped - see isStreamActive() + if (outputDesc->mRefCount[stream] == 0) { + outputDesc->mStopTime[stream] = systemTime(); + audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/); + // delay the device switch by twice the latency because stopOutput() is executed when + // the track stop() command is received and at that time the audio track buffer can + // still contain data that needs to be drained. The latency only covers the audio HAL + // and kernel buffers. Also the latency does not always include additional delay in the + // audio path (audio DSP, CODEC ...) + setOutputDevice(output, newDevice, false, outputDesc->mLatency*2); + + // force restoring the device selection on other active outputs if it differs from the + // one being selected for this output + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t curOutput = mOutputs.keyAt(i); + AudioOutputDescriptor *desc = mOutputs.valueAt(i); + if (curOutput != output && + desc->isActive() && + outputDesc->sharesHwModuleWith(desc) && + (newDevice != desc->device())) { + setOutputDevice(curOutput, + getNewDevice(curOutput, false /*fromCache*/), + true, + outputDesc->mLatency*2); + } + } + // update the outputs if stopping one with a stream that can affect notification routing + handleNotificationRoutingForStream(stream); + } + return NO_ERROR; + } else { + ALOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output) +{ + ALOGV("releaseOutput() %d", output); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("releaseOutput() releasing unknown output %d", output); + return; + } + +#ifdef AUDIO_POLICY_TEST + int testIndex = testOutputIndex(output); + if (testIndex != 0) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + if (outputDesc->isActive()) { + mpClientInterface->closeOutput(output); + delete mOutputs.valueAt(index); + mOutputs.removeItem(output); + mTestOutputs[testIndex] = 0; + } + return; + } +#endif //AUDIO_POLICY_TEST + + AudioOutputDescriptor *desc = mOutputs.valueAt(index); + if (desc->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) { + if (desc->mDirectOpenCount <= 0) { + ALOGW("releaseOutput() invalid open count %d for output %d", + desc->mDirectOpenCount, output); + return; + } + if (--desc->mDirectOpenCount == 0) { + closeOutput(output); + // If effects where present on the output, audioflinger moved them to the primary + // output by default: move them back to the appropriate output. + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput != mPrimaryOutput) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput); + } + } + } +} + + +audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::audio_in_acoustics acoustics) +{ + audio_io_handle_t input = 0; + audio_devices_t device = getDeviceForInputSource(inputSource); + + ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, acoustics %x", + inputSource, samplingRate, format, channelMask, acoustics); + + if (device == AUDIO_DEVICE_NONE) { + ALOGW("getInput() could not find device for inputSource %d", inputSource); + return 0; + } + + // adapt channel selection to input source + switch(inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + default: + break; + } + + IOProfile *profile = getInputProfile(device, + samplingRate, + format, + channelMask); + if (profile == NULL) { + ALOGW("getInput() could not find profile for device 0x%X, samplingRate %d, format %d, " + "channelMask 0x%X", + device, samplingRate, format, channelMask); + return 0; + } + + if (profile->mModule->mHandle == 0) { + ALOGE("getInput(): HW module %s not opened", profile->mModule->mName); + return 0; + } + + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(profile); + + inputDesc->mInputSource = inputSource; + inputDesc->mDevice = device; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannelMask = channelMask; + inputDesc->mRefCount = 0; + + input = mpClientInterface->openInput(profile->mModule->mHandle, + &inputDesc->mDevice, + &inputDesc->mSamplingRate, + &inputDesc->mFormat, + &inputDesc->mChannelMask); + + // only accept input with the exact requested set of parameters + if (input == 0 || + (samplingRate != inputDesc->mSamplingRate) || + (format != inputDesc->mFormat) || + (channelMask != inputDesc->mChannelMask)) { + ALOGI("getInput() failed opening input: samplingRate %d, format %d, channelMask 0x%X", + samplingRate, format, channelMask); + if (input != 0) { + mpClientInterface->closeInput(input); + } + delete inputDesc; + return 0; + } + addInput(input, inputDesc); + + return input; +} + +status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input) +{ + ALOGV("startInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("startInput() unknown input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +#ifdef AUDIO_POLICY_TEST + if (mTestInput == 0) +#endif //AUDIO_POLICY_TEST + { + // refuse 2 active AudioRecord clients at the same time except if the active input + // uses AUDIO_SOURCE_HOTWORD in which case it is closed. + audio_io_handle_t activeInput = getActiveInput(); + if (!isVirtualInputDevice(inputDesc->mDevice) && activeInput != 0) { + AudioInputDescriptor *activeDesc = mInputs.valueFor(activeInput); + if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) { + ALOGW("startInput() preempting already started low-priority input %d", activeInput); + stopInput(activeInput); + releaseInput(activeInput); + } else { + ALOGW("startInput() input %d failed: other input already started", input); + return INVALID_OPERATION; + } + } + } + + audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) { + inputDesc->mDevice = newDevice; + } + + // automatically enable the remote submix output when input is started + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AudioSystem::DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice); + + int aliasSource = (inputDesc->mInputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputDesc->mInputSource; + + param.addInt(String8(AudioParameter::keyInputSource), aliasSource); + ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource); + + mpClientInterface->setParameters(input, param.toString()); + + inputDesc->mRefCount = 1; + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input) +{ + ALOGV("stopInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("stopInput() unknown input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + + if (inputDesc->mRefCount == 0) { + ALOGW("stopInput() input %d already stopped", input); + return INVALID_OPERATION; + } else { + // automatically disable the remote submix output when input is stopped + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AudioSystem::DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), 0); + mpClientInterface->setParameters(input, param.toString()); + inputDesc->mRefCount = 0; + return NO_ERROR; + } +} + +void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input) +{ + ALOGV("releaseInput() %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("releaseInput() releasing unknown input %d", input); + return; + } + mpClientInterface->closeInput(input); + delete mInputs.valueAt(index); + mInputs.removeItem(input); + + ALOGV("releaseInput() exit"); +} + +void AudioPolicyManagerBase::closeAllInputs() { + for(size_t input_index = 0; input_index < mInputs.size(); input_index++) { + mpClientInterface->closeInput(mInputs.keyAt(input_index)); + } + mInputs.clear(); +} + +void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMin >= indexMax) { + ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); + return; + } + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, + int index, + audio_devices_t device) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + + ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d", + stream, device, index); + + // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and + // clear all device specific values + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + mStreams[stream].mIndexCur.clear(); + } + mStreams[stream].mIndexCur.add(device, index); + + // compute and apply stream volume on all outputs according to connected device + status_t status = NO_ERROR; + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_devices_t curDevice = + getDeviceForVolume(mOutputs.valueAt(i)->device()); + if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) { + status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + } + return status; +} + +status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, + int *index, + audio_devices_t device) +{ + if (index == NULL) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + // if device is AUDIO_DEVICE_OUT_DEFAULT, return volume for device corresponding to + // the strategy the stream belongs to. + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); + } + device = getDeviceForVolume(device); + + *index = mStreams[stream].getVolumeIndex(device); + ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); + return NO_ERROR; +} + +audio_io_handle_t AudioPolicyManagerBase::selectOutputForEffects( + const SortedVector<audio_io_handle_t>& outputs) +{ + // select one output among several suitable for global effects. + // The priority is as follows: + // 1: An offloaded output. If the effect ends up not being offloadable, + // AudioFlinger will invalidate the track and the offloaded output + // will be closed causing the effect to be moved to a PCM output. + // 2: A deep buffer output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + + audio_io_handle_t outputOffloaded = 0; + audio_io_handle_t outputDeepBuffer = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]); + ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags); + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + outputOffloaded = outputs[i]; + } + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) { + outputDeepBuffer = outputs[i]; + } + } + + ALOGV("selectOutputForEffects outputOffloaded %d outputDeepBuffer %d", + outputOffloaded, outputDeepBuffer); + if (outputOffloaded != 0) { + return outputOffloaded; + } + if (outputDeepBuffer != 0) { + return outputDeepBuffer; + } + + return outputs[0]; +} + +audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(const effect_descriptor_t *desc) +{ + // apply simple rule where global effects are attached to the same output as MUSIC streams + + routing_strategy strategy = getStrategy(AudioSystem::MUSIC); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(device, mOutputs); + + audio_io_handle_t output = selectOutputForEffects(dstOutputs); + ALOGV("getOutputForEffect() got output %d for fx %s flags %x", + output, (desc == NULL) ? "unspecified" : desc->name, (desc == NULL) ? 0 : desc->flags); + + return output; +} + +status_t AudioPolicyManagerBase::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + ssize_t index = mOutputs.indexOfKey(io); + if (index < 0) { + index = mInputs.indexOfKey(io); + if (index < 0) { + ALOGW("registerEffect() unknown io %d", io); + return INVALID_OPERATION; + } + } + + if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { + ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", + desc->name, desc->memoryUsage); + return INVALID_OPERATION; + } + mTotalEffectsMemory += desc->memoryUsage; + ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", + desc->name, io, strategy, session, id); + ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); + + EffectDescriptor *pDesc = new EffectDescriptor(); + memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t)); + pDesc->mIo = io; + pDesc->mStrategy = (routing_strategy)strategy; + pDesc->mSession = session; + pDesc->mEnabled = false; + + mEffects.add(id, pDesc); + + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::unregisterEffect(int id) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + EffectDescriptor *pDesc = mEffects.valueAt(index); + + setEffectEnabled(pDesc, false); + + if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) { + ALOGW("unregisterEffect() memory %d too big for total %d", + pDesc->mDesc.memoryUsage, mTotalEffectsMemory); + pDesc->mDesc.memoryUsage = mTotalEffectsMemory; + } + mTotalEffectsMemory -= pDesc->mDesc.memoryUsage; + ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", + pDesc->mDesc.name, id, pDesc->mDesc.memoryUsage, mTotalEffectsMemory); + + mEffects.removeItem(id); + delete pDesc; + + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::setEffectEnabled(int id, bool enabled) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + return setEffectEnabled(mEffects.valueAt(index), enabled); +} + +status_t AudioPolicyManagerBase::setEffectEnabled(EffectDescriptor *pDesc, bool enabled) +{ + if (enabled == pDesc->mEnabled) { + ALOGV("setEffectEnabled(%s) effect already %s", + enabled?"true":"false", enabled?"enabled":"disabled"); + return INVALID_OPERATION; + } + + if (enabled) { + if (mTotalEffectsCpuLoad + pDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { + ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", + pDesc->mDesc.name, (float)pDesc->mDesc.cpuLoad/10); + return INVALID_OPERATION; + } + mTotalEffectsCpuLoad += pDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); + } else { + if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) { + ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", + pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); + pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; + } + mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); + } + pDesc->mEnabled = enabled; + return NO_ERROR; +} + +bool AudioPolicyManagerBase::isNonOffloadableEffectEnabled() +{ + for (size_t i = 0; i < mEffects.size(); i++) { + const EffectDescriptor * const pDesc = mEffects.valueAt(i); + if (pDesc->mEnabled && (pDesc->mStrategy == STRATEGY_MEDIA) && + ((pDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { + ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", + pDesc->mDesc.name, pDesc->mSession); + return true; + } + } + return false; +} + +bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (outputDesc->isStreamActive((AudioSystem::stream_type)stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManagerBase::isStreamActiveRemotely(int stream, uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && + outputDesc->isStreamActive((AudioSystem::stream_type)stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManagerBase::isSourceActive(audio_source_t source) const +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const AudioInputDescriptor * inputDescriptor = mInputs.valueAt(i); + if ((inputDescriptor->mInputSource == (int)source || + (source == (audio_source_t)AUDIO_SOURCE_VOICE_RECOGNITION && + inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) + && (inputDescriptor->mRefCount > 0)) { + return true; + } + } + return false; +} + + +status_t AudioPolicyManagerBase::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); + result.append(buffer); + + snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput); + result.append(buffer); + snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string()); + result.append(buffer); + snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string()); + result.append(buffer); + snprintf(buffer, SIZE, " USB audio ALSA %s\n", mUsbOutCardAndDevice.string()); + result.append(buffer); + snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices); + result.append(buffer); + snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices); + result.append(buffer); + snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for system %d\n", mForceUse[AudioSystem::FOR_SYSTEM]); + result.append(buffer); + write(fd, result.string(), result.size()); + + + snprintf(buffer, SIZE, "\nHW Modules dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mHwModules.size(); i++) { + snprintf(buffer, SIZE, "- HW Module %zu:\n", i + 1); + write(fd, buffer, strlen(buffer)); + mHwModules[i]->dump(fd); + } + + snprintf(buffer, SIZE, "\nOutputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mOutputs.size(); i++) { + snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mOutputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nInputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mInputs.size(); i++) { + snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mInputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nStreams dump:\n"); + write(fd, buffer, strlen(buffer)); + snprintf(buffer, SIZE, + " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + snprintf(buffer, SIZE, " %02zu ", i); + write(fd, buffer, strlen(buffer)); + mStreams[i].dump(fd); + } + + snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", + (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); + write(fd, buffer, strlen(buffer)); + + snprintf(buffer, SIZE, "Registered effects:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mEffects.size(); i++) { + snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mEffects.valueAt(i)->dump(fd); + } + + + return NO_ERROR; +} + +// This function checks for the parameters which can be offloaded. +// This can be enhanced depending on the capability of the DSP and policy +// of the system. +bool AudioPolicyManagerBase::isOffloadSupported(const audio_offload_info_t& offloadInfo) +{ + ALOGV("isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d," + " BitRate=%u, duration=%" PRId64 " us, has_video=%d", + offloadInfo.sample_rate, offloadInfo.channel_mask, + offloadInfo.format, + offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, + offloadInfo.has_video); + + // Check if offload has been disabled + char propValue[PROPERTY_VALUE_MAX]; + if (property_get("audio.offload.disable", propValue, "0")) { + if (atoi(propValue) != 0) { + ALOGV("offload disabled by audio.offload.disable=%s", propValue ); + return false; + } + } + + // Check if stream type is music, then only allow offload as of now. + if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC) + { + ALOGV("isOffloadSupported: stream_type != MUSIC, returning false"); + return false; + } + + //TODO: enable audio offloading with video when ready + if (offloadInfo.has_video) + { + ALOGV("isOffloadSupported: has_video == true, returning false"); + return false; + } + + //If duration is less than minimum value defined in property, return false + if (property_get("audio.offload.min.duration.secs", propValue, NULL)) { + if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) { + ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue); + return false; + } + } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) { + ALOGV("Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS); + return false; + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + if (isNonOffloadableEffectEnabled()) { + return false; + } + + // See if there is a profile to support this. + // AUDIO_DEVICE_NONE + IOProfile *profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */, + offloadInfo.sample_rate, + offloadInfo.format, + offloadInfo.channel_mask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + ALOGV("isOffloadSupported() profile %sfound", profile != NULL ? "" : "NOT "); + return (profile != NULL); +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerBase +// ---------------------------------------------------------------------------- + +AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface) + : +#ifdef AUDIO_POLICY_TEST + Thread(false), +#endif //AUDIO_POLICY_TEST + mPrimaryOutput((audio_io_handle_t)0), + mAvailableOutputDevices(AUDIO_DEVICE_NONE), + mPhoneState(AudioSystem::MODE_NORMAL), + mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), + mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), + mA2dpSuspended(false), mHasA2dp(false), mHasUsb(false), mHasRemoteSubmix(false), + mSpeakerDrcEnabled(false) +{ + mpClientInterface = clientInterface; + + for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) { + mForceUse[i] = AudioSystem::FORCE_NONE; + } + + mA2dpDeviceAddress = String8(""); + mScoDeviceAddress = String8(""); + mUsbOutCardAndDevice = String8(""); + + if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) { + if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) { + ALOGE("could not load audio policy configuration file, setting defaults"); + defaultAudioPolicyConfig(); + } + } + + // must be done after reading the policy + initializeVolumeCurves(); + + // open all output streams needed to access attached devices + for (size_t i = 0; i < mHwModules.size(); i++) { + mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName); + if (mHwModules[i]->mHandle == 0) { + ALOGW("could not open HW module %s", mHwModules[i]->mName); + continue; + } + // open all output streams needed to access attached devices + // except for direct output streams that are only opened when they are actually + // required by an app. + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + const IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j]; + + if ((outProfile->mSupportedDevices & mAttachedOutputDevices) && + ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile); + outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice & + outProfile->mSupportedDevices); + audio_io_handle_t output = mpClientInterface->openOutput( + outProfile->mModule->mHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags); + if (output == 0) { + delete outputDesc; + } else { + mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices | + (outProfile->mSupportedDevices & mAttachedOutputDevices)); + if (mPrimaryOutput == 0 && + outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + mPrimaryOutput = output; + } + addOutput(output, outputDesc); + setOutputDevice(output, + (audio_devices_t)(mDefaultOutputDevice & + outProfile->mSupportedDevices), + true); + } + } + } + } + + ALOGE_IF((mAttachedOutputDevices & ~mAvailableOutputDevices), + "Not output found for attached devices %08x", + (mAttachedOutputDevices & ~mAvailableOutputDevices)); + + ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output"); + + updateDevicesAndOutputs(); + +#ifdef AUDIO_POLICY_TEST + if (mPrimaryOutput != 0) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + + mTestDevice = AUDIO_DEVICE_OUT_SPEAKER; + mTestSamplingRate = 44100; + mTestFormat = AudioSystem::PCM_16_BIT; + mTestChannels = AudioSystem::CHANNEL_OUT_STEREO; + mTestLatencyMs = 0; + mCurOutput = 0; + mDirectOutput = false; + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + mTestOutputs[i] = 0; + } + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "AudioPolicyManagerTest"); + run(buffer, ANDROID_PRIORITY_AUDIO); + } +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManagerBase::~AudioPolicyManagerBase() +{ +#ifdef AUDIO_POLICY_TEST + exit(); +#endif //AUDIO_POLICY_TEST + for (size_t i = 0; i < mOutputs.size(); i++) { + mpClientInterface->closeOutput(mOutputs.keyAt(i)); + delete mOutputs.valueAt(i); + } + for (size_t i = 0; i < mInputs.size(); i++) { + mpClientInterface->closeInput(mInputs.keyAt(i)); + delete mInputs.valueAt(i); + } + for (size_t i = 0; i < mHwModules.size(); i++) { + delete mHwModules[i]; + } +} + +status_t AudioPolicyManagerBase::initCheck() +{ + return (mPrimaryOutput == 0) ? NO_INIT : NO_ERROR; +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManagerBase::threadLoop() +{ + ALOGV("entering threadLoop()"); + while (!exitPending()) + { + String8 command; + int valueInt; + String8 value; + + Mutex::Autolock _l(mLock); + mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + + command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); + AudioParameter param = AudioParameter(command); + + if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR && + valueInt != 0) { + ALOGV("Test command %s received", command.string()); + String8 target; + if (param.get(String8("target"), target) != NO_ERROR) { + target = "Manager"; + } + if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_output")); + mCurOutput = valueInt; + } + if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_direct")); + if (value == "false") { + mDirectOutput = false; + } else if (value == "true") { + mDirectOutput = true; + } + } + if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_input")); + mTestInput = valueInt; + } + + if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_format")); + int format = AudioSystem::INVALID_FORMAT; + if (value == "PCM 16 bits") { + format = AudioSystem::PCM_16_BIT; + } else if (value == "PCM 8 bits") { + format = AudioSystem::PCM_8_BIT; + } else if (value == "Compressed MP3") { + format = AudioSystem::MP3; + } + if (format != AudioSystem::INVALID_FORMAT) { + if (target == "Manager") { + mTestFormat = format; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("format"), format); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_channels")); + int channels = 0; + + if (value == "Channels Stereo") { + channels = AudioSystem::CHANNEL_OUT_STEREO; + } else if (value == "Channels Mono") { + channels = AudioSystem::CHANNEL_OUT_MONO; + } + if (channels != 0) { + if (target == "Manager") { + mTestChannels = channels; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("channels"), channels); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_sampleRate")); + if (valueInt >= 0 && valueInt <= 96000) { + int samplingRate = valueInt; + if (target == "Manager") { + mTestSamplingRate = samplingRate; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("sampling_rate"), samplingRate); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + + if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_reopen")); + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + mpClientInterface->closeOutput(mPrimaryOutput); + + audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle; + + delete mOutputs.valueFor(mPrimaryOutput); + mOutputs.removeItem(mPrimaryOutput); + + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER; + mPrimaryOutput = mpClientInterface->openOutput(moduleHandle, + &outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannelMask, + &outputDesc->mLatency, + outputDesc->mFlags); + if (mPrimaryOutput == 0) { + ALOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannelMask); + } else { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + addOutput(mPrimaryOutput, outputDesc); + } + } + + + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManagerBase::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output) +{ + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + if (output == mTestOutputs[i]) return i; + } + return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc) +{ + outputDesc->mId = id; + mOutputs.add(id, outputDesc); +} + +void AudioPolicyManagerBase::addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc) +{ + inputDesc->mId = id; + mInputs.add(id, inputDesc); +} + +status_t AudioPolicyManagerBase::checkOutputsForDevice(audio_devices_t device, + AudioSystem::device_connection_state state, + SortedVector<audio_io_handle_t>& outputs, + const String8 paramStr) +{ + AudioOutputDescriptor *desc; + + if (state == AudioSystem::DEVICE_STATE_AVAILABLE) { + // first list already open outputs that can be routed to this device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices & device)) { + ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } + } + // then look for output profiles that can be routed to this device + SortedVector<IOProfile *> profiles; + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices & device) { + ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i); + profiles.add(mHwModules[i]->mOutputProfiles[j]); + } + } + } + + if (profiles.isEmpty() && outputs.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + + // open outputs for matching profiles if needed. Direct outputs are also opened to + // query for dynamic parameters and will be closed later by setDeviceConnectionState() + for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) { + IOProfile *profile = profiles[profile_index]; + + // nothing to do if one output is already opened for this profile + size_t j; + for (j = 0; j < mOutputs.size(); j++) { + desc = mOutputs.valueAt(j); + if (!desc->isDuplicated() && desc->mProfile == profile) { + break; + } + } + if (j != mOutputs.size()) { + continue; + } + + ALOGV("opening output for device %08x with params %s", device, paramStr.string()); + desc = new AudioOutputDescriptor(profile); + desc->mDevice = device; + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.sample_rate = desc->mSamplingRate; + offloadInfo.format = desc->mFormat; + offloadInfo.channel_mask = desc->mChannelMask; + + audio_io_handle_t output = mpClientInterface->openOutput(profile->mModule->mHandle, + &desc->mDevice, + &desc->mSamplingRate, + &desc->mFormat, + &desc->mChannelMask, + &desc->mLatency, + desc->mFlags, + &offloadInfo); + if (output != 0) { + if (!paramStr.isEmpty()) { + // Here is where the out_set_parameters() for card & device gets called + mpClientInterface->setParameters(output, paramStr); + } + + // Here is where we step through and resolve any "dynamic" fields + String8 reply; + char *value; + if (profile->mSamplingRates[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)); + ALOGV("checkOutputsForDevice() direct output sup sampling rates %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadSamplingRates(value + 1, profile); + } + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS)); + ALOGV("checkOutputsForDevice() direct output sup formats %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadFormats(value + 1, profile); + } + } + if (profile->mChannelMasks[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS)); + ALOGV("checkOutputsForDevice() direct output sup channel masks %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadOutChannels(value + 1, profile); + } + } + if (((profile->mSamplingRates[0] == 0) && + (profile->mSamplingRates.size() < 2)) || + ((profile->mFormats[0] == 0) && + (profile->mFormats.size() < 2)) || + ((profile->mChannelMasks[0] == 0) && + (profile->mChannelMasks.size() < 2))) { + ALOGW("checkOutputsForDevice() direct output missing param"); + mpClientInterface->closeOutput(output); + output = 0; + } else if (profile->mSamplingRates[0] == 0) { + mpClientInterface->closeOutput(output); + desc->mSamplingRate = profile->mSamplingRates[1]; + offloadInfo.sample_rate = desc->mSamplingRate; + output = mpClientInterface->openOutput( + profile->mModule->mHandle, + &desc->mDevice, + &desc->mSamplingRate, + &desc->mFormat, + &desc->mChannelMask, + &desc->mLatency, + desc->mFlags, + &offloadInfo); + } + + if (output != 0) { + addOutput(output, desc); + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) { + audio_io_handle_t duplicatedOutput = 0; + + // set initial stream volume for device + applyStreamVolumes(output, device, 0, true); + + //TODO: configure audio effect output stage here + + // open a duplicating output thread for the new output and the primary output + duplicatedOutput = mpClientInterface->openDuplicateOutput(output, + mPrimaryOutput); + if (duplicatedOutput != 0) { + // add duplicated output descriptor + AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(NULL); + dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput); + dupOutputDesc->mOutput2 = mOutputs.valueFor(output); + dupOutputDesc->mSamplingRate = desc->mSamplingRate; + dupOutputDesc->mFormat = desc->mFormat; + dupOutputDesc->mChannelMask = desc->mChannelMask; + dupOutputDesc->mLatency = desc->mLatency; + addOutput(duplicatedOutput, dupOutputDesc); + applyStreamVolumes(duplicatedOutput, device, 0, true); + } else { + ALOGW("checkOutputsForDevice() could not open dup output for %d and %d", + mPrimaryOutput, output); + mpClientInterface->closeOutput(output); + mOutputs.removeItem(output); + output = 0; + } + } + } + } + if (output == 0) { + ALOGW("checkOutputsForDevice() could not open output for device %x", device); + delete desc; + profiles.removeAt(profile_index); + profile_index--; + } else { + outputs.add(output); + ALOGV("checkOutputsForDevice(): adding output %d", output); + } + } + + if (profiles.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + } else { // Disconnect + // check if one opened output is not needed any more after disconnecting one device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && + !(desc->mProfile->mSupportedDevices & mAvailableOutputDevices)) { + ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } + } + // Clear any profiles associated with the disconnected device. + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + IOProfile *profile = mHwModules[i]->mOutputProfiles[j]; + if (profile->mSupportedDevices & device) { + ALOGV("checkOutputsForDevice(): clearing direct output profile %zu on module %zu", + j, i); + if (profile->mSamplingRates[0] == 0) { + profile->mSamplingRates.clear(); + profile->mSamplingRates.add(0); + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + profile->mFormats.clear(); + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + } + if (profile->mChannelMasks[0] == 0) { + profile->mChannelMasks.clear(); + profile->mChannelMasks.add(0); + } + } + } + } + } + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::checkInputsForDevice(audio_devices_t device, + AudioSystem::device_connection_state state, + SortedVector<audio_io_handle_t>& inputs, + const String8 paramStr) +{ + AudioInputDescriptor *desc; + if (state == AudioSystem::DEVICE_STATE_AVAILABLE) { + // first list already open inputs that can be routed to this device + for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (desc->mProfile->mSupportedDevices & (device & ~AUDIO_DEVICE_BIT_IN)) { + ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index)); + inputs.add(mInputs.keyAt(input_index)); + } + } + + // then look for input profiles that can be routed to this device + SortedVector<IOProfile *> profiles; + for (size_t module_index = 0; module_index < mHwModules.size(); module_index++) + { + if (mHwModules[module_index]->mHandle == 0) { + continue; + } + for (size_t profile_index = 0; + profile_index < mHwModules[module_index]->mInputProfiles.size(); + profile_index++) + { + if (mHwModules[module_index]->mInputProfiles[profile_index]->mSupportedDevices + & (device & ~AUDIO_DEVICE_BIT_IN)) { + ALOGV("checkInputsForDevice(): adding profile %zu from module %zu", + profile_index, module_index); + profiles.add(mHwModules[module_index]->mInputProfiles[profile_index]); + } + } + } + + if (profiles.isEmpty() && inputs.isEmpty()) { + ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + return BAD_VALUE; + } + + // open inputs for matching profiles if needed. Direct inputs are also opened to + // query for dynamic parameters and will be closed later by setDeviceConnectionState() + for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) { + + IOProfile *profile = profiles[profile_index]; + // nothing to do if one input is already opened for this profile + size_t input_index; + for (input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (desc->mProfile == profile) { + break; + } + } + if (input_index != mInputs.size()) { + continue; + } + + ALOGV("opening input for device 0x%X with params %s", device, paramStr.string()); + desc = new AudioInputDescriptor(profile); + desc->mDevice = device; + + audio_io_handle_t input = mpClientInterface->openInput(profile->mModule->mHandle, + &desc->mDevice, + &desc->mSamplingRate, + &desc->mFormat, + &desc->mChannelMask); + + if (input != 0) { + if (!paramStr.isEmpty()) { + mpClientInterface->setParameters(input, paramStr); + } + + // Here is where we step through and resolve any "dynamic" fields + String8 reply; + char *value; + if (profile->mSamplingRates[0] == 0) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)); + ALOGV("checkInputsForDevice() direct input sup sampling rates %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadSamplingRates(value + 1, profile); + } + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS)); + ALOGV("checkInputsForDevice() direct input sup formats %s", reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadFormats(value + 1, profile); + } + } + if (profile->mChannelMasks[0] == 0) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS)); + ALOGV("checkInputsForDevice() direct input sup channel masks %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + loadInChannels(value + 1, profile); + } + } + if (((profile->mSamplingRates[0] == 0) && (profile->mSamplingRates.size() < 2)) || + ((profile->mFormats[0] == 0) && (profile->mFormats.size() < 2)) || + ((profile->mChannelMasks[0] == 0) && (profile->mChannelMasks.size() < 2))) { + ALOGW("checkInputsForDevice() direct input missing param"); + mpClientInterface->closeInput(input); + input = 0; + } + + if (input != 0) { + addInput(input, desc); + } + } // endif input != 0 + + if (input == 0) { + ALOGW("checkInputsForDevice() could not open input for device 0x%X", device); + delete desc; + profiles.removeAt(profile_index); + profile_index--; + } else { + inputs.add(input); + ALOGV("checkInputsForDevice(): adding input %d", input); + } + } // end scan profiles + + if (profiles.isEmpty()) { + ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + return BAD_VALUE; + } + } else { + // Disconnect + // check if one opened input is not needed any more after disconnecting one device + for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (!(desc->mProfile->mSupportedDevices & mAvailableInputDevices)) { + ALOGV("checkInputsForDevice(): disconnecting adding input %d", + mInputs.keyAt(input_index)); + inputs.add(mInputs.keyAt(input_index)); + } + } + // Clear any profiles associated with the disconnected device. + for (size_t module_index = 0; module_index < mHwModules.size(); module_index++) + { + if (mHwModules[module_index]->mHandle == 0) { + continue; + } + for (size_t profile_index = 0; + profile_index < mHwModules[module_index]->mInputProfiles.size(); + profile_index++) + { + IOProfile *profile = mHwModules[module_index]->mInputProfiles[profile_index]; + if (profile->mSupportedDevices & device) { + ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %zu", + profile_index, module_index); + if (profile->mSamplingRates[0] == 0) { + profile->mSamplingRates.clear(); + profile->mSamplingRates.add(0); + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + profile->mFormats.clear(); + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + } + if (profile->mChannelMasks[0] == 0) { + profile->mChannelMasks.clear(); + profile->mChannelMasks.add(0); + } + } + } + } + } // end disconnect + + return NO_ERROR; +} + +void AudioPolicyManagerBase::closeOutput(audio_io_handle_t output) +{ + ALOGV("closeOutput(%d)", output); + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + if (outputDesc == NULL) { + ALOGW("closeOutput() unknown output %d", output); + return; + } + + // look for duplicated outputs connected to the output being removed. + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *dupOutputDesc = mOutputs.valueAt(i); + if (dupOutputDesc->isDuplicated() && + (dupOutputDesc->mOutput1 == outputDesc || + dupOutputDesc->mOutput2 == outputDesc)) { + AudioOutputDescriptor *outputDesc2; + if (dupOutputDesc->mOutput1 == outputDesc) { + outputDesc2 = dupOutputDesc->mOutput2; + } else { + outputDesc2 = dupOutputDesc->mOutput1; + } + // As all active tracks on duplicated output will be deleted, + // and as they were also referenced on the other output, the reference + // count for their stream type must be adjusted accordingly on + // the other output. + for (int j = 0; j < (int)AudioSystem::NUM_STREAM_TYPES; j++) { + int refCount = dupOutputDesc->mRefCount[j]; + outputDesc2->changeRefCount((AudioSystem::stream_type)j,-refCount); + } + audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i); + ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); + + mpClientInterface->closeOutput(duplicatedOutput); + delete mOutputs.valueFor(duplicatedOutput); + mOutputs.removeItem(duplicatedOutput); + } + } + + AudioParameter param; + param.add(String8("closing"), String8("true")); + mpClientInterface->setParameters(output, param.toString()); + + mpClientInterface->closeOutput(output); + delete outputDesc; + mOutputs.removeItem(output); + mPreviousOutputs = mOutputs; +} + +SortedVector<audio_io_handle_t> AudioPolicyManagerBase::getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs) +{ + SortedVector<audio_io_handle_t> outputs; + + ALOGVV("getOutputsForDevice() device %04x", device); + for (size_t i = 0; i < openOutputs.size(); i++) { + ALOGVV("output %d isDuplicated=%d device=%04x", + i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices()); + if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) { + ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i)); + outputs.add(openOutputs.keyAt(i)); + } + } + return outputs; +} + +bool AudioPolicyManagerBase::vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2) +{ + if (outputs1.size() != outputs2.size()) { + return false; + } + for (size_t i = 0; i < outputs1.size(); i++) { + if (outputs1[i] != outputs2[i]) { + return false; + } + } + return true; +} + +void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy) +{ + audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); + audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs); + + if (!vectorsEqual(srcOutputs,dstOutputs)) { + ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d", + strategy, srcOutputs[0], dstOutputs[0]); + // mute strategy while moving tracks from one output to another + for (size_t i = 0; i < srcOutputs.size(); i++) { + AudioOutputDescriptor *desc = mOutputs.valueFor(srcOutputs[i]); + if (desc->isStrategyActive(strategy)) { + setStrategyMute(strategy, true, srcOutputs[i]); + setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice); + } + } + + // Move effects associated to this strategy from previous output to new output + if (strategy == STRATEGY_MEDIA) { + audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs); + SortedVector<audio_io_handle_t> moved; + for (size_t i = 0; i < mEffects.size(); i++) { + EffectDescriptor *desc = mEffects.valueAt(i); + if (desc->mSession == AUDIO_SESSION_OUTPUT_MIX && + desc->mIo != fxOutput) { + if (moved.indexOf(desc->mIo) < 0) { + ALOGV("checkOutputForStrategy() moving effect %d to output %d", + mEffects.keyAt(i), fxOutput); + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, desc->mIo, + fxOutput); + moved.add(desc->mIo); + } + desc->mIo = fxOutput; + } + } + } + // Move tracks associated to this strategy from previous output to new output + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + mpClientInterface->invalidateStream((AudioSystem::stream_type)i); + } + } + } +} + +void AudioPolicyManagerBase::checkOutputForAllStrategies() +{ + checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); + checkOutputForStrategy(STRATEGY_PHONE); + checkOutputForStrategy(STRATEGY_SONIFICATION); + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + checkOutputForStrategy(STRATEGY_MEDIA); + checkOutputForStrategy(STRATEGY_DTMF); +} + +audio_io_handle_t AudioPolicyManagerBase::getA2dpOutput() +{ + if (!mHasA2dp) { + return 0; + } + + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { + return mOutputs.keyAt(i); + } + } + + return 0; +} + +void AudioPolicyManagerBase::checkA2dpSuspend() +{ + if (!mHasA2dp) { + return; + } + audio_io_handle_t a2dpOutput = getA2dpOutput(); + if (a2dpOutput == 0) { + return; + } + + // suspend A2DP output if: + // (NOT already suspended) && + // ((SCO device is connected && + // (forced usage for communication || for record is SCO))) || + // (phone state is ringing || in call) + // + // restore A2DP output if: + // (Already suspended) && + // ((SCO device is NOT connected || + // (forced usage NOT for communication && NOT for record is SCO))) && + // (phone state is NOT ringing && NOT in call) + // + if (mA2dpSuspended) { + if (((mScoDeviceAddress == "") || + ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) && + (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) && + ((mPhoneState != AudioSystem::MODE_IN_CALL) && + (mPhoneState != AudioSystem::MODE_RINGTONE))) { + + mpClientInterface->restoreOutput(a2dpOutput); + mA2dpSuspended = false; + } + } else { + if (((mScoDeviceAddress != "") && + ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || + (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) || + ((mPhoneState == AudioSystem::MODE_IN_CALL) || + (mPhoneState == AudioSystem::MODE_RINGTONE))) { + + mpClientInterface->suspendOutput(a2dpOutput); + mA2dpSuspended = true; + } + } +} + +audio_devices_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache) +{ + audio_devices_t device = AUDIO_DEVICE_NONE; + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + // check the following by order of priority to request a routing change if necessary: + // 1: the strategy enforced audible is active on the output: + // use device for strategy enforced audible + // 2: we are in call or the strategy phone is active on the output: + // use device for strategy phone + // 3: the strategy sonification is active on the output: + // use device for strategy sonification + // 4: the strategy "respectful" sonification is active on the output: + // use device for strategy "respectful" sonification + // 5: the strategy media is active on the output: + // use device for strategy media + // 6: the strategy DTMF is active on the output: + // use device for strategy DTMF + if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) { + device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); + } else if (isInCall() || + outputDesc->isStrategyActive(STRATEGY_PHONE)) { + device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_MEDIA)) { + device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { + device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + } + + ALOGV("getNewDevice() selected device %x", device); + return device; +} + +uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) { + return (uint32_t)getStrategy(stream); +} + +audio_devices_t AudioPolicyManagerBase::getDevicesForStream(AudioSystem::stream_type stream) { + audio_devices_t devices; + // By checking the range of stream before calling getStrategy, we avoid + // getStrategy's behavior for invalid streams. getStrategy would do a ALOGE + // and then return STRATEGY_MEDIA, but we want to return the empty set. + if (stream < (AudioSystem::stream_type) 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + devices = AUDIO_DEVICE_NONE; + } else { + AudioPolicyManagerBase::routing_strategy strategy = getStrategy(stream); + devices = getDeviceForStrategy(strategy, true /*fromCache*/); + } + return devices; +} + +AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy( + AudioSystem::stream_type stream) { + // stream to strategy mapping + switch (stream) { + case AudioSystem::VOICE_CALL: + case AudioSystem::BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AudioSystem::RING: + case AudioSystem::ALARM: + return STRATEGY_SONIFICATION; + case AudioSystem::NOTIFICATION: + return STRATEGY_SONIFICATION_RESPECTFUL; + case AudioSystem::DTMF: + return STRATEGY_DTMF; + default: + ALOGE("unknown stream type"); + case AudioSystem::SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AudioSystem::TTS: + case AudioSystem::MUSIC: + return STRATEGY_MEDIA; + case AudioSystem::ENFORCED_AUDIBLE: + return STRATEGY_ENFORCED_AUDIBLE; + } +} + +void AudioPolicyManagerBase::handleNotificationRoutingForStream(AudioSystem::stream_type stream) { + switch(stream) { + case AudioSystem::MUSIC: + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + updateDevicesAndOutputs(); + break; + default: + break; + } +} + +audio_devices_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, + bool fromCache) +{ + uint32_t device = AUDIO_DEVICE_NONE; + + if (fromCache) { + ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", + strategy, mDeviceForStrategy[strategy]); + return mDeviceForStrategy[strategy]; + } + + switch (strategy) { + + case STRATEGY_SONIFICATION_RESPECTFUL: + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActiveRemotely(AudioSystem::MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing on a remote device, use the the sonification behavior. + // Note that we test this usecase before testing if media is playing because + // the isStreamActive() method only informs about the activity of a stream, not + // if it's for local playback. Note also that we use the same delay between both tests + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActive(AudioSystem::MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing (or has recently played), use the same device + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + } else { + // when media is not playing anymore, fall back on the sonification behavior + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } + + break; + + case STRATEGY_DTMF: + if (!isInCall()) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { + case AudioSystem::FORCE_BT_SCO: + if (!isInCall() || strategy != STRATEGY_DTMF) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + if (device) break; + // if SCO device is requested but no SCO device is available, fall back to default case + // FALL THROUGH + + default: // FORCE_NONE + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (mHasA2dp && !isInCall() && + (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; + if (device) break; + if (mPhoneState != AudioSystem::MODE_IN_CALL) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE; + if (device) break; + device = mDefaultOutputDevice; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); + } + break; + + case AudioSystem::FORCE_SPEAKER: + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (mHasA2dp && !isInCall() && + (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } + if (mPhoneState != AudioSystem::MODE_IN_CALL) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; + if (device) break; + device = mDefaultOutputDevice; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); + } + break; + } + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } + // FALL THROUGH + + case STRATEGY_ENFORCED_AUDIBLE: + // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + // except: + // - when in call where it doesn't default to STRATEGY_PHONE behavior + // - in countries where not enforced in which case it follows STRATEGY_MEDIA + + if ((strategy == STRATEGY_SONIFICATION) || + (mForceUse[AudioSystem::FOR_SYSTEM] == AudioSystem::FORCE_SYSTEM_ENFORCED)) { + device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); + } + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + case STRATEGY_MEDIA: { + uint32_t device2 = AUDIO_DEVICE_NONE; + if (strategy != STRATEGY_SONIFICATION) { + // no sonification on remote submix (e.g. WFD) + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + if ((device2 == AUDIO_DEVICE_NONE) && + mHasA2dp && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { + // no sonification on aux digital (e.g. HDMI) + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_ANALOG_DOCK)) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; + } + + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or + // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise + device |= device2; + if (device) break; + device = mDefaultOutputDevice; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); + } + } break; + + default: + ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + +void AudioPolicyManagerBase::updateDevicesAndOutputs() +{ + for (int i = 0; i < NUM_STRATEGIES; i++) { + mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + } + mPreviousOutputs = mOutputs; +} + +uint32_t AudioPolicyManagerBase::checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs) +{ + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + if (outputDesc->isDuplicated()) { + return 0; + } + + uint32_t muteWaitMs = 0; + audio_devices_t device = outputDesc->device(); + bool shouldMute = outputDesc->isActive() && (AudioSystem::popCount(device) >= 2); + // temporary mute output if device selection changes to avoid volume bursts due to + // different per device volumes + bool tempMute = outputDesc->isActive() && (device != prevDevice); + + for (size_t i = 0; i < NUM_STRATEGIES; i++) { + audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + bool mute = shouldMute && (curDevice & device) && (curDevice != device); + bool doMute = false; + + if (mute && !outputDesc->mStrategyMutedByDevice[i]) { + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = true; + } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){ + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = false; + } + if (doMute || tempMute) { + for (size_t j = 0; j < mOutputs.size(); j++) { + AudioOutputDescriptor *desc = mOutputs.valueAt(j); + // skip output if it does not share any device with current output + if ((desc->supportedDevices() & outputDesc->supportedDevices()) + == AUDIO_DEVICE_NONE) { + continue; + } + audio_io_handle_t curOutput = mOutputs.keyAt(j); + ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d", + mute ? "muting" : "unmuting", i, curDevice, curOutput); + setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs); + if (desc->isStrategyActive((routing_strategy)i)) { + // do tempMute only for current output + if (tempMute && (desc == outputDesc)) { + setStrategyMute((routing_strategy)i, true, curOutput); + setStrategyMute((routing_strategy)i, false, curOutput, + desc->latency() * 2, device); + } + if ((tempMute && (desc == outputDesc)) || mute) { + if (muteWaitMs < desc->latency()) { + muteWaitMs = desc->latency(); + } + } + } + } + } + } + + // FIXME: should not need to double latency if volume could be applied immediately by the + // audioflinger mixer. We must account for the delay between now and the next time + // the audioflinger thread for this output will process a buffer (which corresponds to + // one buffer size, usually 1/2 or 1/4 of the latency). + muteWaitMs *= 2; + // wait for the PCM output buffers to empty before proceeding with the rest of the command + if (muteWaitMs > delayMs) { + muteWaitMs -= delayMs; + usleep(muteWaitMs * 1000); + return muteWaitMs; + } + return 0; +} + +uint32_t AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force, + int delayMs) +{ + ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs); + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + AudioParameter param; + uint32_t muteWaitMs; + + if (outputDesc->isDuplicated()) { + muteWaitMs = setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs); + muteWaitMs += setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs); + return muteWaitMs; + } + // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current + // output profile + if ((device != AUDIO_DEVICE_NONE) && + ((device & outputDesc->mProfile->mSupportedDevices) == 0)) { + return 0; + } + + // filter devices according to output selected + device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices); + + audio_devices_t prevDevice = outputDesc->mDevice; + + ALOGV("setOutputDevice() prevDevice %04x", prevDevice); + + if (device != AUDIO_DEVICE_NONE) { + outputDesc->mDevice = device; + + // Force routing if previously asked for this output + if (outputDesc->mForceRouting) { + ALOGV("Force routing to current device as previous device was null for this output"); + force = true; + + // Request consumed. Reset mForceRouting to false + outputDesc->mForceRouting = false; + } + } + else { + // Device is null and does not reflect the routing. Save the necessity to force + // re-routing upon next attempt to select a non-null device for this output + outputDesc->mForceRouting = true; + } + + muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs); + + // Do not change the routing if: + // - the requested device is AUDIO_DEVICE_NONE + // - the requested device is the same as current device and force is not specified. + // Doing this check here allows the caller to call setOutputDevice() without conditions + if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force) { + ALOGV("setOutputDevice() setting same device %04x or null device for output %d", device, output); + return muteWaitMs; + } + + ALOGV("setOutputDevice() changing device"); + // do the routing + param.addInt(String8(AudioParameter::keyRouting), (int)device); + mpClientInterface->setParameters(output, param.toString(), delayMs); + + // update stream volumes according to new device + applyStreamVolumes(output, device, delayMs); + + return muteWaitMs; +} + +AudioPolicyManagerBase::IOProfile *AudioPolicyManagerBase::getInputProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask) +{ + // Choose an input profile based on the requested capture parameters: select the first available + // profile supporting all requested parameters. + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) + { + IOProfile *profile = mHwModules[i]->mInputProfiles[j]; + // profile->log(); + if (profile->isCompatibleProfile(device, samplingRate, format, + channelMask, AUDIO_OUTPUT_FLAG_NONE)) { + return profile; + } + } + } + return NULL; +} + +audio_devices_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource) +{ + uint32_t device = AUDIO_DEVICE_NONE; + + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + break; + } + // FALL THROUGH + + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + if (mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + break; + } + // FALL THROUGH + + case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_HOTWORD: + case AUDIO_SOURCE_VOICE_COMMUNICATION: + if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO && + mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (mAvailableInputDevices & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + } + break; + case AUDIO_SOURCE_REMOTE_SUBMIX: + if (mAvailableInputDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + default: + ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); + break; + } + ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +bool AudioPolicyManagerBase::isVirtualInputDevice(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + +audio_io_handle_t AudioPolicyManagerBase::getActiveInput(bool ignoreVirtualInputs) +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const AudioInputDescriptor * input_descriptor = mInputs.valueAt(i); + if ((input_descriptor->mRefCount > 0) + && (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) { + return mInputs.keyAt(i); + } + } + return 0; +} + + +audio_devices_t AudioPolicyManagerBase::getDeviceForVolume(audio_devices_t device) +{ + if (device == AUDIO_DEVICE_NONE) { + // this happens when forcing a route update and no track is active on an output. + // In this case the returned category is not important. + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (AudioSystem::popCount(device) > 1) { + // Multiple device selection is either: + // - speaker + one other device: give priority to speaker in this case. + // - one A2DP device + another device: happens with duplicated output. In this case + // retain the device on the A2DP output as the other must not correspond to an active + // selection if not the speaker. + if (device & AUDIO_DEVICE_OUT_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else { + device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); + } + } + + ALOGW_IF(AudioSystem::popCount(device) != 1, + "getDeviceForVolume() invalid device combination: %08x", + device); + + return device; +} + +AudioPolicyManagerBase::device_category AudioPolicyManagerBase::getDeviceCategory(audio_devices_t device) +{ + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return DEVICE_CATEGORY_EARPIECE; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + return DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: + default: + return DEVICE_CATEGORY_SPEAKER; + } +} + +float AudioPolicyManagerBase::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + device_category deviceCategory = getDeviceCategory(device); + const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; + + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + curve[VOLMAX].mIndex - + curve[VOLMIN].mIndex; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < curve[VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[VOLMAX].mIndex) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = curve[segment].mDBAttenuation + + ((float)(volIdx - curve[segment].mIndex)) * + ( (curve[segment+1].mDBAttenuation - + curve[segment].mDBAttenuation) / + ((float)(curve[segment+1].mIndex - + curve[segment].mIndex)) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + curve[segment].mIndex, volIdx, + curve[segment+1].mIndex, + curve[segment].mDBAttenuation, + decibels, + curve[segment+1].mDBAttenuation, + amplification); + + return amplification; +} + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sDefaultVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sDefaultMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sSpeakerMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sSpeakerSonificationVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sSpeakerSonificationVolumeCurveDrc[AudioPolicyManagerBase::VOLCNT] = { + {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} +}; + +// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks +// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. +// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). +// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sDefaultSystemVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sDefaultSystemVolumeCurveDrc[AudioPolicyManagerBase::VOLCNT] = { + {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sHeadsetSystemVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sDefaultVoiceVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + AudioPolicyManagerBase::sSpeakerVoiceVolumeCurve[AudioPolicyManagerBase::VOLCNT] = { + {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} +}; + +const AudioPolicyManagerBase::VolumeCurvePoint + *AudioPolicyManagerBase::sVolumeProfiles[AudioSystem::NUM_STREAM_TYPES] + [AudioPolicyManagerBase::DEVICE_CATEGORY_CNT] = { + { // AUDIO_STREAM_VOICE_CALL + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_SYSTEM + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_RING + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_MUSIC + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_ALARM + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_NOTIFICATION + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_BLUETOOTH_SCO + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_ENFORCED_AUDIBLE + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_DTMF + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, + { // AUDIO_STREAM_TTS + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EARPIECE + }, +}; + +void AudioPolicyManagerBase::initializeVolumeCurves() +{ + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[i].mVolumeCurve[j] = + sVolumeProfiles[i][j]; + } + } + + // Check availability of DRC on speaker path: if available, override some of the speaker curves + if (mSpeakerDrcEnabled) { + mStreams[AUDIO_STREAM_SYSTEM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sDefaultSystemVolumeCurveDrc; + mStreams[AUDIO_STREAM_RING].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_ALARM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + } +} + +float AudioPolicyManagerBase::computeVolume(int stream, + int index, + audio_io_handle_t output, + audio_devices_t device) +{ + float volume = 1.0; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + StreamDescriptor &streamDesc = mStreams[stream]; + + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + // if volume is not 0 (not muted), force media volume to max on digital output + if (stream == AudioSystem::MUSIC && + index != mStreams[stream].mIndexMin && + (device == AUDIO_DEVICE_OUT_AUX_DIGITAL || + device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) { + return 1.0; + } + + volume = volIndexToAmpl(device, streamDesc, index); + + // if a headset is connected, apply the following rules to ring tones and notifications + // to avoid sound level bursts in user's ears: + // - always attenuate ring tones and notifications volume by 6dB + // - if music is playing, always limit the volume to current music volume, + // with a minimum threshold at -36dB so that notification is always perceived. + const routing_strategy stream_strategy = getStrategy((AudioSystem::stream_type)stream); + if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) && + ((stream_strategy == STRATEGY_SONIFICATION) + || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) + || (stream == AudioSystem::SYSTEM) + || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && + (mForceUse[AudioSystem::FOR_SYSTEM] == AudioSystem::FORCE_NONE))) && + streamDesc.mCanBeMuted) { + volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; + // when the phone is ringing we must consider that music could have been paused just before + // by the music application and behave as if music was active if the last music track was + // just stopped + if (isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || + mLimitRingtoneVolume) { + audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); + float musicVol = computeVolume(AudioSystem::MUSIC, + mStreams[AudioSystem::MUSIC].getVolumeIndex(musicDevice), + output, + musicDevice); + float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? + musicVol : SONIFICATION_HEADSET_VOLUME_MIN; + if (volume > minVol) { + volume = minVol; + ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); + } + } + } + + return volume; +} + +status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, + int index, + audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + + // do not change actual stream volume if the stream is muted + if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { + ALOGVV("checkAndSetVolume() stream %d muted count %d", + stream, mOutputs.valueFor(output)->mMuteCount[stream]); + return NO_ERROR; + } + + // do not change in call volume if bluetooth is connected and vice versa + if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || + (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) { + ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + float volume = computeVolume(stream, index, output, device); + // We actually change the volume if: + // - the float value returned by computeVolume() changed + // - the force flag is set + if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || + force) { + mOutputs.valueFor(output)->mCurVolume[stream] = volume; + ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); + // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is + // enabled + if (stream == AudioSystem::BLUETOOTH_SCO) { + mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs); + } + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs); + } + + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::BLUETOOTH_SCO) { + float voiceVolume; + // Force voice volume to max for bluetooth SCO as volume is managed by the headset + if (stream == AudioSystem::VOICE_CALL) { + voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + } else { + voiceVolume = 1.0; + } + + if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) { + mpClientInterface->setVoiceVolume(voiceVolume, delayMs); + mLastVoiceVolume = voiceVolume; + } + } + + return NO_ERROR; +} + +void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + ALOGVV("applyStreamVolumes() for output %d and device %x", output, device); + + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + checkAndSetVolume(stream, + mStreams[stream].getVolumeIndex(device), + output, + device, + delayMs, + force); + } +} + +void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + if (getStrategy((AudioSystem::stream_type)stream) == strategy) { + setStreamMute(stream, on, output, delayMs, device); + } + } +} + +void AudioPolicyManagerBase::setStreamMute(int stream, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + StreamDescriptor &streamDesc = mStreams[stream]; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x", + stream, on, output, outputDesc->mMuteCount[stream], device); + + if (on) { + if (outputDesc->mMuteCount[stream] == 0) { + if (streamDesc.mCanBeMuted && + ((stream != AudioSystem::ENFORCED_AUDIBLE) || + (mForceUse[AudioSystem::FOR_SYSTEM] == AudioSystem::FORCE_NONE))) { + checkAndSetVolume(stream, 0, output, device, delayMs); + } + } + // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored + outputDesc->mMuteCount[stream]++; + } else { + if (outputDesc->mMuteCount[stream] == 0) { + ALOGV("setStreamMute() unmuting non muted stream!"); + return; + } + if (--outputDesc->mMuteCount[stream] == 0) { + checkAndSetVolume(stream, + streamDesc.getVolumeIndex(device), + output, + device, + delayMs); + } + } +} + +void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange) +{ + // if the stream pertains to sonification strategy and we are in call we must + // mute the stream if it is low visibility. If it is high visibility, we must play a tone + // in the device used for phone strategy and play the tone if the selected device does not + // interfere with the device used for phone strategy + // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as + // many times as there are active tracks on the output + const routing_strategy stream_strategy = getStrategy((AudioSystem::stream_type)stream); + if ((stream_strategy == STRATEGY_SONIFICATION) || + ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput); + ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", + stream, starting, outputDesc->mDevice, stateChange); + if (outputDesc->mRefCount[stream]) { + int muteCount = 1; + if (stateChange) { + muteCount = outputDesc->mRefCount[stream]; + } + if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { + ALOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } else { + ALOGV("handleIncallSonification() high visibility"); + if (outputDesc->device() & + getDeviceForStrategy(STRATEGY_PHONE, true /*fromCache*/)) { + ALOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } + if (starting) { + mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + +bool AudioPolicyManagerBase::isInCall() +{ + return isStateInCall(mPhoneState); +} + +bool AudioPolicyManagerBase::isStateInCall(int state) { + return ((state == AudioSystem::MODE_IN_CALL) || + (state == AudioSystem::MODE_IN_COMMUNICATION)); +} + +uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad() +{ + return MAX_EFFECTS_CPU_LOAD; +} + +uint32_t AudioPolicyManagerBase::getMaxEffectsMemory() +{ + return MAX_EFFECTS_MEMORY; +} + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor( + const IOProfile *profile) + : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), + mChannelMask(0), mLatency(0), + mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), + mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0), + mForceRouting(false) +{ + // clear usage count for all stream types + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + mRefCount[i] = 0; + mCurVolume[i] = -1.0; + mMuteCount[i] = 0; + mStopTime[i] = 0; + } + for (int i = 0; i < NUM_STRATEGIES; i++) { + mStrategyMutedByDevice[i] = false; + } + if (profile != NULL) { + mSamplingRate = profile->mSamplingRates[0]; + mFormat = profile->mFormats[0]; + mChannelMask = profile->mChannelMasks[0]; + mFlags = profile->mFlags; + } +} + +audio_devices_t AudioPolicyManagerBase::AudioOutputDescriptor::device() const +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); + } else { + return mDevice; + } +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::latency() +{ + if (isDuplicated()) { + return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; + } else { + return mLatency; + } +} + +bool AudioPolicyManagerBase::AudioOutputDescriptor::sharesHwModuleWith( + const AudioOutputDescriptor *outputDesc) +{ + if (isDuplicated()) { + return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); + } else if (outputDesc->isDuplicated()){ + return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2); + } else { + return (mProfile->mModule == outputDesc->mProfile->mModule); + } +} + +void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta) +{ + // forward usage count change to attached outputs + if (isDuplicated()) { + mOutput1->changeRefCount(stream, delta); + mOutput2->changeRefCount(stream, delta); + } + if ((delta + (int)mRefCount[stream]) < 0) { + ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +audio_devices_t AudioPolicyManagerBase::AudioOutputDescriptor::supportedDevices() +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); + } else { + return mProfile->mSupportedDevices ; + } +} + +bool AudioPolicyManagerBase::AudioOutputDescriptor::isActive(uint32_t inPastMs) const +{ + return isStrategyActive(NUM_STRATEGIES, inPastMs); +} + +bool AudioPolicyManagerBase::AudioOutputDescriptor::isStrategyActive(routing_strategy strategy, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if ((sysTime == 0) && (inPastMs != 0)) { + sysTime = systemTime(); + } + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + if (((getStrategy((AudioSystem::stream_type)i) == strategy) || + (NUM_STRATEGIES == strategy)) && + isStreamActive((AudioSystem::stream_type)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManagerBase::AudioOutputDescriptor::isStreamActive(AudioSystem::stream_type stream, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if (mRefCount[stream] != 0) { + return true; + } + if (inPastMs == 0) { + return false; + } + if (sysTime == 0) { + sysTime = systemTime(); + } + if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { + return true; + } + return false; +} + + +status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %08x\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Latency: %d\n", mLatency); + result.append(buffer); + snprintf(buffer, SIZE, " Flags %08x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", device()); + result.append(buffer); + snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); + result.append(buffer); + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]); + result.append(buffer); + } + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor(const IOProfile *profile) + : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0), + mDevice(AUDIO_DEVICE_NONE), mRefCount(0), + mInputSource(0), mProfile(profile) +{ + if (profile != NULL) { + mSamplingRate = profile->mSamplingRates[0]; + mFormat = profile->mFormats[0]; + mChannelMask = profile->mChannelMasks[0]; + } +} + +status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %d\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- StreamDescriptor class implementation + +AudioPolicyManagerBase::StreamDescriptor::StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) +{ + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); +} + +int AudioPolicyManagerBase::StreamDescriptor::getVolumeIndex(audio_devices_t device) +{ + device = AudioPolicyManagerBase::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT + if (mIndexCur.indexOfKey(device) < 0) { + device = AUDIO_DEVICE_OUT_DEFAULT; + } + return mIndexCur.valueFor(device); +} + +void AudioPolicyManagerBase::StreamDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%s %02d %02d ", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + result.append(buffer); + for (size_t i = 0; i < mIndexCur.size(); i++) { + snprintf(buffer, SIZE, "%04x : %02d, ", + mIndexCur.keyAt(i), + mIndexCur.valueAt(i)); + result.append(buffer); + } + result.append("\n"); + + write(fd, result.string(), result.size()); +} + +// --- EffectDescriptor class implementation + +status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " I/O: %d\n", mIo); + result.append(buffer); + snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); + result.append(buffer); + snprintf(buffer, SIZE, " Session: %d\n", mSession); + result.append(buffer); + snprintf(buffer, SIZE, " Name: %s\n", mDesc.name); + result.append(buffer); + snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled"); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- IOProfile class implementation + +AudioPolicyManagerBase::HwModule::HwModule(const char *name) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), mHandle(0) +{ +} + +AudioPolicyManagerBase::HwModule::~HwModule() +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + delete mOutputProfiles[i]; + } + for (size_t i = 0; i < mInputProfiles.size(); i++) { + delete mInputProfiles[i]; + } + free((void *)mName); +} + +void AudioPolicyManagerBase::HwModule::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - name: %s\n", mName); + result.append(buffer); + snprintf(buffer, SIZE, " - handle: %d\n", mHandle); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mOutputProfiles.size()) { + write(fd, " - outputs:\n", strlen(" - outputs:\n")); + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + snprintf(buffer, SIZE, " output %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mOutputProfiles[i]->dump(fd); + } + } + if (mInputProfiles.size()) { + write(fd, " - inputs:\n", strlen(" - inputs:\n")); + for (size_t i = 0; i < mInputProfiles.size(); i++) { + snprintf(buffer, SIZE, " input %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mInputProfiles[i]->dump(fd); + } + } +} + +AudioPolicyManagerBase::IOProfile::IOProfile(HwModule *module) + : mFlags((audio_output_flags_t)0), mModule(module) +{ +} + +AudioPolicyManagerBase::IOProfile::~IOProfile() +{ +} + +// checks if the IO profile is compatible with specified parameters. +// Sampling rate, format and channel mask must be specified in order to +// get a valid a match +bool AudioPolicyManagerBase::IOProfile::isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const +{ + if (samplingRate == 0 || !audio_is_valid_format(format) || channelMask == 0) { + return false; + } + + if ((mSupportedDevices & device) != device) { + return false; + } + if ((mFlags & flags) != flags) { + return false; + } + size_t i; + for (i = 0; i < mSamplingRates.size(); i++) + { + if (mSamplingRates[i] == samplingRate) { + break; + } + } + if (i == mSamplingRates.size()) { + return false; + } + for (i = 0; i < mFormats.size(); i++) + { + if (mFormats[i] == format) { + break; + } + } + if (i == mFormats.size()) { + return false; + } + for (i = 0; i < mChannelMasks.size(); i++) + { + if (mChannelMasks[i] == channelMask) { + break; + } + } + if (i == mChannelMasks.size()) { + return false; + } + return true; +} + +void AudioPolicyManagerBase::IOProfile::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - sampling rates: "); + result.append(buffer); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + snprintf(buffer, SIZE, "%d", mSamplingRates[i]); + result.append(buffer); + result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - channel masks: "); + result.append(buffer); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); + result.append(buffer); + result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - formats: "); + result.append(buffer); + for (size_t i = 0; i < mFormats.size(); i++) { + snprintf(buffer, SIZE, "0x%08x", mFormats[i]); + result.append(buffer); + result.append(i == (mFormats.size() - 1) ? "\n" : ", "); + } + + snprintf(buffer, SIZE, " - devices: 0x%04x\n", mSupportedDevices); + result.append(buffer); + snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); + result.append(buffer); + + write(fd, result.string(), result.size()); +} + +void AudioPolicyManagerBase::IOProfile::log() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + ALOGV(" - sampling rates: "); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + ALOGV(" %d", mSamplingRates[i]); + } + + ALOGV(" - channel masks: "); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV(" 0x%04x", mChannelMasks[i]); + } + + ALOGV(" - formats: "); + for (size_t i = 0; i < mFormats.size(); i++) { + ALOGV(" 0x%08x", mFormats[i]); + } + + ALOGV(" - devices: 0x%04x\n", mSupportedDevices); + ALOGV(" - flags: 0x%04x\n", mFlags); +} + +// --- audio_policy.conf file parsing + +struct StringToEnum { + const char *name; + uint32_t value; +}; + +#define STRING_TO_ENUM(string) { #string, string } +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +const struct StringToEnum sDeviceNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), +}; + +const struct StringToEnum sFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), +}; + +const struct StringToEnum sFormatNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_FLOAT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED), + STRING_TO_ENUM(AUDIO_FORMAT_MP3), + STRING_TO_ENUM(AUDIO_FORMAT_AAC), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), + STRING_TO_ENUM(AUDIO_FORMAT_OPUS), + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), +}; + +const struct StringToEnum sOutChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), +}; + +const struct StringToEnum sInChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK), +}; + + +uint32_t AudioPolicyManagerBase::stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name) +{ + for (size_t i = 0; i < size; i++) { + if (strcmp(table[i].name, name) == 0) { + ALOGV("stringToEnum() found %s", table[i].name); + return table[i].value; + } + } + return 0; +} + +bool AudioPolicyManagerBase::stringToBool(const char *value) +{ + return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); +} + +audio_output_flags_t AudioPolicyManagerBase::parseFlagNames(char *name) +{ + uint32_t flag = 0; + + // it is OK to cast name to non const here as we are not going to use it after + // strtok() modifies it + char *flagName = strtok(name, "|"); + while (flagName != NULL) { + if (strlen(flagName) != 0) { + flag |= stringToEnum(sFlagNameToEnumTable, + ARRAY_SIZE(sFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flag |= AUDIO_OUTPUT_FLAG_DIRECT; + } + + return (audio_output_flags_t)flag; +} + +audio_devices_t AudioPolicyManagerBase::parseDeviceNames(char *name) +{ + uint32_t device = 0; + + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + device |= stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + } + devName = strtok(NULL, "|"); + } + return device; +} + +void AudioPolicyManagerBase::loadSamplingRates(char *name, IOProfile *profile) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling + // rates should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mSamplingRates.add(0); + return; + } + + while (str != NULL) { + uint32_t rate = atoi(str); + if (rate != 0) { + ALOGV("loadSamplingRates() adding rate %d", rate); + profile->mSamplingRates.add(rate); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManagerBase::loadFormats(char *name, IOProfile *profile) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mFormats indicates the supported formats + // should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + return; + } + + while (str != NULL) { + audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + str); + if (format != AUDIO_FORMAT_DEFAULT) { + profile->mFormats.add(format); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManagerBase::loadInChannels(char *name, IOProfile *profile) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadInChannels() %s", name); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + str); + if (channelMask != 0) { + ALOGV("loadInChannels() adding channelMask %04x", channelMask); + profile->mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +void AudioPolicyManagerBase::loadOutChannels(char *name, IOProfile *profile) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadOutChannels() %s", name); + + // by convention, "0' in the first entry in mChannelMasks indicates the supported channel + // masks should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + profile->mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + str); + if (channelMask != 0) { + profile->mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +status_t AudioPolicyManagerBase::loadInput(cnode *root, HwModule *module) +{ + cnode *node = root->first_child; + + IOProfile *profile = new IOProfile(module); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + loadSamplingRates((char *)node->value, profile); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + loadFormats((char *)node->value, profile); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + loadInChannels((char *)node->value, profile); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices = parseDeviceNames((char *)node->value); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices == AUDIO_DEVICE_NONE, + "loadInput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadInput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadInput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadInput() invalid supported formats"); + if ((profile->mSupportedDevices != AUDIO_DEVICE_NONE) && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadInput() adding input mSupportedDevices 0x%X", profile->mSupportedDevices); + + module->mInputProfiles.add(profile); + return NO_ERROR; + } else { + delete profile; + return BAD_VALUE; + } +} + +status_t AudioPolicyManagerBase::loadOutput(cnode *root, HwModule *module) +{ + cnode *node = root->first_child; + + IOProfile *profile = new IOProfile(module); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + loadSamplingRates((char *)node->value, profile); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + loadFormats((char *)node->value, profile); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + loadOutChannels((char *)node->value, profile); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices = parseDeviceNames((char *)node->value); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = parseFlagNames((char *)node->value); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices == AUDIO_DEVICE_NONE, + "loadOutput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadOutput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadOutput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadOutput() invalid supported formats"); + if ((profile->mSupportedDevices != AUDIO_DEVICE_NONE) && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadOutput() adding output mSupportedDevices %04x, mFlags %04x", + profile->mSupportedDevices, profile->mFlags); + + module->mOutputProfiles.add(profile); + return NO_ERROR; + } else { + delete profile; + return BAD_VALUE; + } +} + +void AudioPolicyManagerBase::loadHwModule(cnode *root) +{ + cnode *node = config_find(root, OUTPUTS_TAG); + status_t status = NAME_NOT_FOUND; + + HwModule *module = new HwModule(root->name); + + if (node != NULL) { + if (strcmp(root->name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) { + mHasA2dp = true; + } else if (strcmp(root->name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) { + mHasUsb = true; + } else if (strcmp(root->name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) { + mHasRemoteSubmix = true; + } + + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading output %s", node->name); + status_t tmpStatus = loadOutput(node, module); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + node = config_find(root, INPUTS_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading input %s", node->name); + status_t tmpStatus = loadInput(node, module); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + if (status == NO_ERROR) { + mHwModules.add(module); + } else { + delete module; + } +} + +void AudioPolicyManagerBase::loadHwModules(cnode *root) +{ + cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); + if (node == NULL) { + return; + } + + node = node->first_child; + while (node) { + ALOGV("loadHwModules() loading module %s", node->name); + loadHwModule(node); + node = node->next; + } +} + +void AudioPolicyManagerBase::loadGlobalConfig(cnode *root) +{ + cnode *node = config_find(root, GLOBAL_CONFIG_TAG); + if (node == NULL) { + return; + } + node = node->first_child; + while (node) { + if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { + mAttachedOutputDevices = parseDeviceNames((char *)node->value); + ALOGW_IF(mAttachedOutputDevices == AUDIO_DEVICE_NONE, + "loadGlobalConfig() no attached output devices"); + ALOGV("loadGlobalConfig() mAttachedOutputDevices %04x", mAttachedOutputDevices); + } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { + mDefaultOutputDevice = (audio_devices_t)stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + (char *)node->value); + ALOGW_IF(mDefaultOutputDevice == AUDIO_DEVICE_NONE, + "loadGlobalConfig() default device not specified"); + ALOGV("loadGlobalConfig() mDefaultOutputDevice %04x", mDefaultOutputDevice); + } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { + mAvailableInputDevices = parseDeviceNames((char *)node->value) & ~AUDIO_DEVICE_BIT_IN; + ALOGV("loadGlobalConfig() mAvailableInputDevices %04x", mAvailableInputDevices); + } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { + mSpeakerDrcEnabled = stringToBool((char *)node->value); + ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); + } + node = node->next; + } +} + +status_t AudioPolicyManagerBase::loadAudioPolicyConfig(const char *path) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + loadGlobalConfig(root); + loadHwModules(root); + + config_free(root); + free(root); + free(data); + + ALOGI("loadAudioPolicyConfig() loaded %s\n", path); + + return NO_ERROR; +} + +void AudioPolicyManagerBase::defaultAudioPolicyConfig(void) +{ + HwModule *module; + IOProfile *profile; + + mDefaultOutputDevice = AUDIO_DEVICE_OUT_SPEAKER; + mAttachedOutputDevices = AUDIO_DEVICE_OUT_SPEAKER; + mAvailableInputDevices = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN; + + module = new HwModule("primary"); + + profile = new IOProfile(module); + profile->mSamplingRates.add(44100); + profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT); + profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO); + profile->mSupportedDevices = AUDIO_DEVICE_OUT_SPEAKER; + profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY; + module->mOutputProfiles.add(profile); + + profile = new IOProfile(module); + profile->mSamplingRates.add(8000); + profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT); + profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO); + profile->mSupportedDevices = AUDIO_DEVICE_IN_BUILTIN_MIC; + module->mInputProfiles.add(profile); + + mHwModules.add(module); +} + +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioPolicyManagerDefault.cpp b/libhardware_legacy/audio/AudioPolicyManagerDefault.cpp new file mode 100644 index 0000000..9083638 --- /dev/null +++ b/libhardware_legacy/audio/AudioPolicyManagerDefault.cpp
@@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyManagerDefault" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyManagerDefault.h" + +namespace android_audio_legacy { + +extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManagerDefault(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android
diff --git a/libhardware_legacy/audio/AudioPolicyManagerDefault.h b/libhardware_legacy/audio/AudioPolicyManagerDefault.h new file mode 100644 index 0000000..987fdf0 --- /dev/null +++ b/libhardware_legacy/audio/AudioPolicyManagerDefault.h
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 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 <stdint.h> +#include <stdbool.h> + +#include <hardware_legacy/AudioPolicyManagerBase.h> + +namespace android_audio_legacy { + +class AudioPolicyManagerDefault: public AudioPolicyManagerBase +{ + +public: + explicit AudioPolicyManagerDefault(AudioPolicyClientInterface *clientInterface) + : AudioPolicyManagerBase(clientInterface) {} + + virtual ~AudioPolicyManagerDefault() {} + +}; +};
diff --git a/libhardware_legacy/audio/audio_hw_hal.cpp b/libhardware_legacy/audio/audio_hw_hal.cpp new file mode 100644 index 0000000..d15044a --- /dev/null +++ b/libhardware_legacy/audio/audio_hw_hal.cpp
@@ -0,0 +1,713 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "legacy_audio_hw_hal" +//#define LOG_NDEBUG 0 + +#include <stdint.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <hardware/audio.h> + +#include <hardware_legacy/AudioHardwareInterface.h> +#include <hardware_legacy/AudioSystemLegacy.h> + +namespace android_audio_legacy { + +class AudioHardwareInterface; + +extern "C" { + +struct legacy_audio_module { + struct audio_module module; +}; + +struct legacy_audio_device { + struct audio_hw_device device; + + AudioHardwareInterface *hwif; +}; + +struct legacy_stream_out { + struct audio_stream_out stream; + + AudioStreamOut *legacy_out; +}; + +struct legacy_stream_in { + struct audio_stream_in stream; + + AudioStreamIn *legacy_in; +}; + + +enum { + HAL_API_REV_1_0, + HAL_API_REV_2_0, + HAL_API_REV_NUM +} hal_api_rev; + +static uint32_t audio_device_conv_table[][HAL_API_REV_NUM] = +{ + /* output devices */ + { AudioSystem::DEVICE_OUT_EARPIECE, AUDIO_DEVICE_OUT_EARPIECE }, + { AudioSystem::DEVICE_OUT_SPEAKER, AUDIO_DEVICE_OUT_SPEAKER }, + { AudioSystem::DEVICE_OUT_WIRED_HEADSET, AUDIO_DEVICE_OUT_WIRED_HEADSET }, + { AudioSystem::DEVICE_OUT_WIRED_HEADPHONE, AUDIO_DEVICE_OUT_WIRED_HEADPHONE }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, AUDIO_DEVICE_OUT_BLUETOOTH_SCO }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES }, + { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER }, + { AudioSystem::DEVICE_OUT_AUX_DIGITAL, AUDIO_DEVICE_OUT_AUX_DIGITAL }, + { AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET, AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET }, + { AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET }, + { AudioSystem::DEVICE_OUT_DEFAULT, AUDIO_DEVICE_OUT_DEFAULT }, + /* input devices */ + { AudioSystem::DEVICE_IN_COMMUNICATION, AUDIO_DEVICE_IN_COMMUNICATION }, + { AudioSystem::DEVICE_IN_AMBIENT, AUDIO_DEVICE_IN_AMBIENT }, + { AudioSystem::DEVICE_IN_BUILTIN_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC }, + { AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET }, + { AudioSystem::DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_WIRED_HEADSET }, + { AudioSystem::DEVICE_IN_AUX_DIGITAL, AUDIO_DEVICE_IN_AUX_DIGITAL }, + { AudioSystem::DEVICE_IN_VOICE_CALL, AUDIO_DEVICE_IN_VOICE_CALL }, + { AudioSystem::DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BACK_MIC }, + { AudioSystem::DEVICE_IN_DEFAULT, AUDIO_DEVICE_IN_DEFAULT }, +}; + +static uint32_t convert_audio_device(uint32_t from_device, int from_rev, int to_rev) +{ + const uint32_t k_num_devices = sizeof(audio_device_conv_table)/sizeof(uint32_t)/HAL_API_REV_NUM; + uint32_t to_device = AUDIO_DEVICE_NONE; + uint32_t in_bit = 0; + + if (from_rev != HAL_API_REV_1_0) { + in_bit = from_device & AUDIO_DEVICE_BIT_IN; + from_device &= ~AUDIO_DEVICE_BIT_IN; + } + + while (from_device) { + uint32_t i = 31 - __builtin_clz(from_device); + uint32_t cur_device = (1 << i) | in_bit; + + for (i = 0; i < k_num_devices; i++) { + if (audio_device_conv_table[i][from_rev] == cur_device) { + to_device |= audio_device_conv_table[i][to_rev]; + break; + } + } + from_device &= ~cur_device; + } + return to_device; +} + + +/** audio_stream_out implementation **/ +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return out->legacy_out->sampleRate(); +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + + ALOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__); + /* TODO: implement this */ + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return out->legacy_out->bufferSize(); +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return (audio_channel_mask_t) out->legacy_out->channels(); +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + // legacy API, don't change return type + return (audio_format_t) out->legacy_out->format(); +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + ALOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__); + /* TODO: implement me */ + return 0; +} + +static int out_standby(struct audio_stream *stream) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + return out->legacy_out->standby(); +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + Vector<String16> args; + return out->legacy_out->dump(fd, args); +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + int val; + String8 s8 = String8(kvpairs); + AudioParameter parms = AudioParameter(String8(kvpairs)); + + if (parms.getInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val) == NO_ERROR) { + val = convert_audio_device(val, HAL_API_REV_2_0, HAL_API_REV_1_0); + parms.remove(String8(AUDIO_PARAMETER_STREAM_ROUTING)); + parms.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val); + s8 = parms.toString(); + } + + return out->legacy_out->setParameters(s8); +} + +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + String8 s8; + int val; + + s8 = out->legacy_out->getParameters(String8(keys)); + + AudioParameter parms = AudioParameter(s8); + if (parms.getInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val) == NO_ERROR) { + val = convert_audio_device(val, HAL_API_REV_1_0, HAL_API_REV_2_0); + parms.remove(String8(AUDIO_PARAMETER_STREAM_ROUTING)); + parms.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val); + s8 = parms.toString(); + } + + return strdup(s8.string()); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return out->legacy_out->latency(); +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + return out->legacy_out->setVolume(left, right); +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + struct legacy_stream_out *out = + reinterpret_cast<struct legacy_stream_out *>(stream); + return out->legacy_out->write(buffer, bytes); +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return out->legacy_out->getRenderPosition(dsp_frames); +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + const struct legacy_stream_out *out = + reinterpret_cast<const struct legacy_stream_out *>(stream); + return out->legacy_out->getNextWriteTimestamp(timestamp); +} + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + return in->legacy_in->sampleRate(); +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + + ALOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__); + /* TODO: implement this */ + return 0; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + return in->legacy_in->bufferSize(); +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + return (audio_channel_mask_t) in->legacy_in->channels(); +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + // legacy API, don't change return type + return (audio_format_t) in->legacy_in->format(); +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + ALOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__); + /* TODO: implement me */ + return 0; +} + +static int in_standby(struct audio_stream *stream) +{ + struct legacy_stream_in *in = reinterpret_cast<struct legacy_stream_in *>(stream); + return in->legacy_in->standby(); +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + Vector<String16> args; + return in->legacy_in->dump(fd, args); +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + int val; + AudioParameter parms = AudioParameter(String8(kvpairs)); + String8 s8 = String8(kvpairs); + + if (parms.getInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val) == NO_ERROR) { + val = convert_audio_device(val, HAL_API_REV_2_0, HAL_API_REV_1_0); + parms.remove(String8(AUDIO_PARAMETER_STREAM_ROUTING)); + parms.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val); + s8 = parms.toString(); + } + + return in->legacy_in->setParameters(s8); +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + String8 s8; + int val; + + s8 = in->legacy_in->getParameters(String8(keys)); + + AudioParameter parms = AudioParameter(s8); + if (parms.getInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val) == NO_ERROR) { + val = convert_audio_device(val, HAL_API_REV_1_0, HAL_API_REV_2_0); + parms.remove(String8(AUDIO_PARAMETER_STREAM_ROUTING)); + parms.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val); + s8 = parms.toString(); + } + + return strdup(s8.string()); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + return in->legacy_in->setGain(gain); +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + return in->legacy_in->read(buffer, bytes); +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + return in->legacy_in->getInputFramesLost(); +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + return in->legacy_in->addAudioEffect(effect); +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + const struct legacy_stream_in *in = + reinterpret_cast<const struct legacy_stream_in *>(stream); + return in->legacy_in->removeAudioEffect(effect); +} + +/** audio_hw_device implementation **/ +static inline struct legacy_audio_device * to_ladev(struct audio_hw_device *dev) +{ + return reinterpret_cast<struct legacy_audio_device *>(dev); +} + +static inline const struct legacy_audio_device * to_cladev(const struct audio_hw_device *dev) +{ + return reinterpret_cast<const struct legacy_audio_device *>(dev); +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + const struct legacy_audio_device *ladev = to_cladev(dev); + + return ladev->hwif->initCheck(); +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + return ladev->hwif->setVoiceVolume(volume); +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + return ladev->hwif->setMasterVolume(volume); +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float* volume) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + return ladev->hwif->getMasterVolume(volume); +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + // as this is the legacy API, don't change it to use audio_mode_t instead of int + return ladev->hwif->setMode((int) mode); +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + return ladev->hwif->setMicMute(state); +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + const struct legacy_audio_device *ladev = to_cladev(dev); + return ladev->hwif->getMicMute(state); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + return ladev->hwif->setParameters(String8(kvpairs)); +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + const struct legacy_audio_device *ladev = to_cladev(dev); + String8 s8; + + s8 = ladev->hwif->getParameters(String8(keys)); + return strdup(s8.string()); +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + const struct legacy_audio_device *ladev = to_cladev(dev); + return ladev->hwif->getInputBufferSize(config->sample_rate, (int) config->format, + audio_channel_count_from_in_mask(config->channel_mask)); +} + +static int adev_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out, + const char *address __unused) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + status_t status; + struct legacy_stream_out *out; + int ret; + + out = (struct legacy_stream_out *)calloc(1, sizeof(*out)); + if (!out) + return -ENOMEM; + + devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0); + + out->legacy_out = ladev->hwif->openOutputStreamWithFlags(devices, flags, + (int *) &config->format, + &config->channel_mask, + &config->sample_rate, &status); + if (!out->legacy_out) { + ret = status; + goto err_open; + } + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + + *stream_out = &out->stream; + return 0; + +err_open: + free(out); + *stream_out = NULL; + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out* stream) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + struct legacy_stream_out *out = reinterpret_cast<struct legacy_stream_out *>(stream); + + ladev->hwif->closeOutputStream(out->legacy_out); + free(out); +} + +/** This method creates and opens the audio hardware input stream */ +static int adev_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + status_t status; + struct legacy_stream_in *in; + int ret; + + in = (struct legacy_stream_in *)calloc(1, sizeof(*in)); + if (!in) + return -ENOMEM; + + devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0); + + in->legacy_in = ladev->hwif->openInputStream(devices, (int *) &config->format, + &config->channel_mask, &config->sample_rate, + &status, (AudioSystem::audio_in_acoustics)0); + if (!in->legacy_in) { + ret = status; + goto err_open; + } + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + *stream_in = &in->stream; + return 0; + +err_open: + free(in); + *stream_in = NULL; + return ret; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) +{ + struct legacy_audio_device *ladev = to_ladev(dev); + struct legacy_stream_in *in = + reinterpret_cast<struct legacy_stream_in *>(stream); + + ladev->hwif->closeInputStream(in->legacy_in); + free(in); +} + +static int adev_dump(const struct audio_hw_device *dev, int fd) +{ + const struct legacy_audio_device *ladev = to_cladev(dev); + Vector<String16> args; + + return ladev->hwif->dumpState(fd, args); +} + +static int legacy_adev_close(hw_device_t* device) +{ + struct audio_hw_device *hwdev = + reinterpret_cast<struct audio_hw_device *>(device); + struct legacy_audio_device *ladev = to_ladev(hwdev); + + if (!ladev) + return 0; + + if (ladev->hwif) + delete ladev->hwif; + + free(ladev); + return 0; +} + +static int legacy_adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct legacy_audio_device *ladev; + int ret; + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + ladev = (struct legacy_audio_device *)calloc(1, sizeof(*ladev)); + if (!ladev) + return -ENOMEM; + + ladev->device.common.tag = HARDWARE_DEVICE_TAG; + ladev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + ladev->device.common.module = const_cast<hw_module_t*>(module); + ladev->device.common.close = legacy_adev_close; + + ladev->device.init_check = adev_init_check; + ladev->device.set_voice_volume = adev_set_voice_volume; + ladev->device.set_master_volume = adev_set_master_volume; + ladev->device.get_master_volume = adev_get_master_volume; + ladev->device.set_mode = adev_set_mode; + ladev->device.set_mic_mute = adev_set_mic_mute; + ladev->device.get_mic_mute = adev_get_mic_mute; + ladev->device.set_parameters = adev_set_parameters; + ladev->device.get_parameters = adev_get_parameters; + ladev->device.get_input_buffer_size = adev_get_input_buffer_size; + ladev->device.open_output_stream = adev_open_output_stream; + ladev->device.close_output_stream = adev_close_output_stream; + ladev->device.open_input_stream = adev_open_input_stream; + ladev->device.close_input_stream = adev_close_input_stream; + ladev->device.dump = adev_dump; + + ladev->hwif = createAudioHardware(); + if (!ladev->hwif) { + ret = -EIO; + goto err_create_audio_hw; + } + + *device = &ladev->device.common; + + return 0; + +err_create_audio_hw: + free(ladev); + return ret; +} + +static struct hw_module_methods_t legacy_audio_module_methods = { + open: legacy_adev_open +}; + +struct legacy_audio_module HAL_MODULE_INFO_SYM = { + module: { + common: { + tag: HARDWARE_MODULE_TAG, + module_api_version: AUDIO_MODULE_API_VERSION_0_1, + hal_api_version: HARDWARE_HAL_API_VERSION, + id: AUDIO_HARDWARE_MODULE_ID, + name: "LEGACY Audio HW HAL", + author: "The Android Open Source Project", + methods: &legacy_audio_module_methods, + dso : NULL, + reserved : {0}, + }, + }, +}; + +}; // extern "C" + +}; // namespace android_audio_legacy
diff --git a/libhardware_legacy/audio/audio_policy.conf b/libhardware_legacy/audio/audio_policy.conf new file mode 100644 index 0000000..3e29976 --- /dev/null +++ b/libhardware_legacy/audio/audio_policy.conf
@@ -0,0 +1,64 @@ +# +# Audio policy configuration for generic device builds (goldfish audio HAL - emulator) +# + +# Global configuration section: lists input and output devices always present on the device +# as well as the output device selected by default. +# Devices are designated by a string that corresponds to the enum in audio.h + +global_configuration { + attached_output_devices AUDIO_DEVICE_OUT_SPEAKER + default_output_device AUDIO_DEVICE_OUT_SPEAKER + attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX +} + +# audio hardware module section: contains descriptors for all audio hw modules present on the +# device. Each hw module node is named after the corresponding hw module library base name. +# For instance, "primary" corresponds to audio.primary.<device>.so. +# The "primary" module is mandatory and must include at least one output with +# AUDIO_OUTPUT_FLAG_PRIMARY flag. +# Each module descriptor contains one or more output profile descriptors and zero or more +# input profile descriptors. Each profile lists all the parameters supported by a given output +# or input stream category. +# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding +# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". + +audio_hw_modules { + primary { + outputs { + primary { + sampling_rates 44100 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_OUT_SPEAKER + flags AUDIO_OUTPUT_FLAG_PRIMARY + } + } + inputs { + primary { + sampling_rates 8000|16000 + channel_masks AUDIO_CHANNEL_IN_MONO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_BUILTIN_MIC + } + } + } + r_submix { + outputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX + } + } + inputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_IN_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_REMOTE_SUBMIX + } + } + } +}
diff --git a/libhardware_legacy/audio/audio_policy_hal.cpp b/libhardware_legacy/audio/audio_policy_hal.cpp new file mode 100644 index 0000000..87c4131 --- /dev/null +++ b/libhardware_legacy/audio/audio_policy_hal.cpp
@@ -0,0 +1,477 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "legacy_audio_policy_hal" +//#define LOG_NDEBUG 0 + +#include <stdint.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +#include <hardware_legacy/AudioPolicyInterface.h> +#include <hardware_legacy/AudioSystemLegacy.h> + +#include "AudioPolicyCompatClient.h" + +namespace android_audio_legacy { + +extern "C" { + +struct legacy_ap_module { + struct audio_policy_module module; +}; + +struct legacy_ap_device { + struct audio_policy_device device; +}; + +struct legacy_audio_policy { + struct audio_policy policy; + + void *service; + struct audio_policy_service_ops *aps_ops; + AudioPolicyCompatClient *service_client; + AudioPolicyInterface *apm; +}; + +static inline struct legacy_audio_policy * to_lap(struct audio_policy *pol) +{ + return reinterpret_cast<struct legacy_audio_policy *>(pol); +} + +static inline const struct legacy_audio_policy * to_clap(const struct audio_policy *pol) +{ + return reinterpret_cast<const struct legacy_audio_policy *>(pol); +} + + +static int ap_set_device_connection_state(struct audio_policy *pol, + audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->setDeviceConnectionState( + (AudioSystem::audio_devices)device, + (AudioSystem::device_connection_state)state, + device_address); +} + +static audio_policy_dev_state_t ap_get_device_connection_state( + const struct audio_policy *pol, + audio_devices_t device, + const char *device_address) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return (audio_policy_dev_state_t)lap->apm->getDeviceConnectionState( + (AudioSystem::audio_devices)device, + device_address); +} + +static void ap_set_phone_state(struct audio_policy *pol, audio_mode_t state) +{ + struct legacy_audio_policy *lap = to_lap(pol); + // as this is the legacy API, don't change it to use audio_mode_t instead of int + lap->apm->setPhoneState((int) state); +} + + /* indicate a change in ringer mode */ +static void ap_set_ringer_mode(struct audio_policy *pol, uint32_t mode, + uint32_t mask) +{ + // deprecated, never called +} + + /* force using a specific device category for the specified usage */ +static void ap_set_force_use(struct audio_policy *pol, + audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + struct legacy_audio_policy *lap = to_lap(pol); + lap->apm->setForceUse((AudioSystem::force_use)usage, + (AudioSystem::forced_config)config); +} + + /* retreive current device category forced for a given usage */ +static audio_policy_forced_cfg_t ap_get_force_use( + const struct audio_policy *pol, + audio_policy_force_use_t usage) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return (audio_policy_forced_cfg_t)lap->apm->getForceUse( + (AudioSystem::force_use)usage); +} + +/* if can_mute is true, then audio streams that are marked ENFORCED_AUDIBLE + * can still be muted. */ +static void ap_set_can_mute_enforced_audible(struct audio_policy *pol, + bool can_mute) +{ + struct legacy_audio_policy *lap = to_lap(pol); + lap->apm->setSystemProperty("ro.camera.sound.forced", can_mute ? "0" : "1"); +} + +static int ap_init_check(const struct audio_policy *pol) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->initCheck(); +} + +static audio_io_handle_t ap_get_output(struct audio_policy *pol, + audio_stream_type_t stream, + uint32_t sampling_rate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + struct legacy_audio_policy *lap = to_lap(pol); + + ALOGV("%s: tid %d", __func__, gettid()); + return lap->apm->getOutput((AudioSystem::stream_type)stream, + sampling_rate, format, channelMask, + (AudioSystem::output_flags)flags, + offloadInfo); +} + +static int ap_start_output(struct audio_policy *pol, audio_io_handle_t output, + audio_stream_type_t stream, int session) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->startOutput(output, (AudioSystem::stream_type)stream, + session); +} + +static int ap_stop_output(struct audio_policy *pol, audio_io_handle_t output, + audio_stream_type_t stream, int session) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->stopOutput(output, (AudioSystem::stream_type)stream, + session); +} + +static void ap_release_output(struct audio_policy *pol, + audio_io_handle_t output) +{ + struct legacy_audio_policy *lap = to_lap(pol); + lap->apm->releaseOutput(output); +} + +static audio_io_handle_t ap_get_input(struct audio_policy *pol, audio_source_t inputSource, + uint32_t sampling_rate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_in_acoustics_t acoustics) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->getInput((int) inputSource, sampling_rate, format, channelMask, + (AudioSystem::audio_in_acoustics)acoustics); +} + +static int ap_start_input(struct audio_policy *pol, audio_io_handle_t input) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->startInput(input); +} + +static int ap_stop_input(struct audio_policy *pol, audio_io_handle_t input) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->stopInput(input); +} + +static void ap_release_input(struct audio_policy *pol, audio_io_handle_t input) +{ + struct legacy_audio_policy *lap = to_lap(pol); + lap->apm->releaseInput(input); +} + +static void ap_init_stream_volume(struct audio_policy *pol, + audio_stream_type_t stream, int index_min, + int index_max) +{ + struct legacy_audio_policy *lap = to_lap(pol); + lap->apm->initStreamVolume((AudioSystem::stream_type)stream, index_min, + index_max); +} + +static int ap_set_stream_volume_index(struct audio_policy *pol, + audio_stream_type_t stream, + int index) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->setStreamVolumeIndex((AudioSystem::stream_type)stream, + index, + AUDIO_DEVICE_OUT_DEFAULT); +} + +static int ap_get_stream_volume_index(const struct audio_policy *pol, + audio_stream_type_t stream, + int *index) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->getStreamVolumeIndex((AudioSystem::stream_type)stream, + index, + AUDIO_DEVICE_OUT_DEFAULT); +} + +static int ap_set_stream_volume_index_for_device(struct audio_policy *pol, + audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->setStreamVolumeIndex((AudioSystem::stream_type)stream, + index, + device); +} + +static int ap_get_stream_volume_index_for_device(const struct audio_policy *pol, + audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->getStreamVolumeIndex((AudioSystem::stream_type)stream, + index, + device); +} + +static uint32_t ap_get_strategy_for_stream(const struct audio_policy *pol, + audio_stream_type_t stream) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->getStrategyForStream((AudioSystem::stream_type)stream); +} + +static audio_devices_t ap_get_devices_for_stream(const struct audio_policy *pol, + audio_stream_type_t stream) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->getDevicesForStream((AudioSystem::stream_type)stream); +} + +static audio_io_handle_t ap_get_output_for_effect(struct audio_policy *pol, + const struct effect_descriptor_s *desc) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->getOutputForEffect(desc); +} + +static int ap_register_effect(struct audio_policy *pol, + const struct effect_descriptor_s *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->registerEffect(desc, io, strategy, session, id); +} + +static int ap_unregister_effect(struct audio_policy *pol, int id) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->unregisterEffect(id); +} + +static int ap_set_effect_enabled(struct audio_policy *pol, int id, bool enabled) +{ + struct legacy_audio_policy *lap = to_lap(pol); + return lap->apm->setEffectEnabled(id, enabled); +} + +static bool ap_is_stream_active(const struct audio_policy *pol, audio_stream_type_t stream, + uint32_t in_past_ms) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->isStreamActive((int) stream, in_past_ms); +} + +static bool ap_is_stream_active_remotely(const struct audio_policy *pol, audio_stream_type_t stream, + uint32_t in_past_ms) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->isStreamActiveRemotely((int) stream, in_past_ms); +} + +static bool ap_is_source_active(const struct audio_policy *pol, audio_source_t source) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->isSourceActive(source); +} + +static int ap_dump(const struct audio_policy *pol, int fd) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->dump(fd); +} + +static bool ap_is_offload_supported(const struct audio_policy *pol, + const audio_offload_info_t *info) +{ + const struct legacy_audio_policy *lap = to_clap(pol); + return lap->apm->isOffloadSupported(*info); +} + +static int create_legacy_ap(const struct audio_policy_device *device, + struct audio_policy_service_ops *aps_ops, + void *service, + struct audio_policy **ap) +{ + struct legacy_audio_policy *lap; + int ret; + + if (!service || !aps_ops) + return -EINVAL; + + lap = (struct legacy_audio_policy *)calloc(1, sizeof(*lap)); + if (!lap) + return -ENOMEM; + + lap->policy.set_device_connection_state = ap_set_device_connection_state; + lap->policy.get_device_connection_state = ap_get_device_connection_state; + lap->policy.set_phone_state = ap_set_phone_state; + lap->policy.set_ringer_mode = ap_set_ringer_mode; + lap->policy.set_force_use = ap_set_force_use; + lap->policy.get_force_use = ap_get_force_use; + lap->policy.set_can_mute_enforced_audible = + ap_set_can_mute_enforced_audible; + lap->policy.init_check = ap_init_check; + lap->policy.get_output = ap_get_output; + lap->policy.start_output = ap_start_output; + lap->policy.stop_output = ap_stop_output; + lap->policy.release_output = ap_release_output; + lap->policy.get_input = ap_get_input; + lap->policy.start_input = ap_start_input; + lap->policy.stop_input = ap_stop_input; + lap->policy.release_input = ap_release_input; + lap->policy.init_stream_volume = ap_init_stream_volume; + lap->policy.set_stream_volume_index = ap_set_stream_volume_index; + lap->policy.get_stream_volume_index = ap_get_stream_volume_index; + lap->policy.set_stream_volume_index_for_device = ap_set_stream_volume_index_for_device; + lap->policy.get_stream_volume_index_for_device = ap_get_stream_volume_index_for_device; + lap->policy.get_strategy_for_stream = ap_get_strategy_for_stream; + lap->policy.get_devices_for_stream = ap_get_devices_for_stream; + lap->policy.get_output_for_effect = ap_get_output_for_effect; + lap->policy.register_effect = ap_register_effect; + lap->policy.unregister_effect = ap_unregister_effect; + lap->policy.set_effect_enabled = ap_set_effect_enabled; + lap->policy.is_stream_active = ap_is_stream_active; + lap->policy.is_stream_active_remotely = ap_is_stream_active_remotely; + lap->policy.is_source_active = ap_is_source_active; + lap->policy.dump = ap_dump; + lap->policy.is_offload_supported = ap_is_offload_supported; + + lap->service = service; + lap->aps_ops = aps_ops; + lap->service_client = + new AudioPolicyCompatClient(aps_ops, service); + if (!lap->service_client) { + ret = -ENOMEM; + goto err_new_compat_client; + } + + lap->apm = createAudioPolicyManager(lap->service_client); + if (!lap->apm) { + ret = -ENOMEM; + goto err_create_apm; + } + + *ap = &lap->policy; + return 0; + +err_create_apm: + delete lap->service_client; +err_new_compat_client: + free(lap); + *ap = NULL; + return ret; +} + +static int destroy_legacy_ap(const struct audio_policy_device *ap_dev, + struct audio_policy *ap) +{ + struct legacy_audio_policy *lap = to_lap(ap); + + if (!lap) + return 0; + + if (lap->apm) + destroyAudioPolicyManager(lap->apm); + if (lap->service_client) + delete lap->service_client; + free(lap); + return 0; +} + +static int legacy_ap_dev_close(hw_device_t* device) +{ + if (device) + free(device); + return 0; +} + +static int legacy_ap_dev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct legacy_ap_device *dev; + + if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0) + return -EINVAL; + + dev = (struct legacy_ap_device *)calloc(1, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast<hw_module_t*>(module); + dev->device.common.close = legacy_ap_dev_close; + dev->device.create_audio_policy = create_legacy_ap; + dev->device.destroy_audio_policy = destroy_legacy_ap; + + *device = &dev->device.common; + + return 0; +} + +static struct hw_module_methods_t legacy_ap_module_methods = { + .open = legacy_ap_dev_open +}; + +struct legacy_ap_module HAL_MODULE_INFO_SYM = { + .module = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_POLICY_HARDWARE_MODULE_ID, + .name = "LEGACY Audio Policy HAL", + .author = "The Android Open Source Project", + .methods = &legacy_ap_module_methods, + .dso = NULL, + .reserved = {0}, + }, + }, +}; + +}; // extern "C" + +}; // namespace android_audio_legacy
diff --git a/libhardware_legacy/include/hardware_legacy/AudioHardwareBase.h b/libhardware_legacy/include/hardware_legacy/AudioHardwareBase.h new file mode 100644 index 0000000..eac40dc --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/AudioHardwareBase.h
@@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_HARDWARE_BASE_H +#define ANDROID_AUDIO_HARDWARE_BASE_H + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include <system/audio.h> + +namespace android_audio_legacy { + +// ---------------------------------------------------------------------------- + +/** + * AudioHardwareBase is a convenient base class used for implementing the + * AudioHardwareInterface interface. + */ +class AudioHardwareBase : public AudioHardwareInterface +{ +public: + AudioHardwareBase(); + virtual ~AudioHardwareBase() { } + + /** + * setMode is called when the audio mode changes. NORMAL mode is for + * standard audio playback, RINGTONE when a ringtone is playing, IN_CALL + * when a telephony call is in progress, IN_COMMUNICATION when a VoIP call is in progress. + */ + virtual status_t setMode(int mode); + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + virtual status_t getMasterVolume(float *volume); + + /**This method dumps the state of the audio hardware */ + virtual status_t dumpState(int fd, const Vector<String16>& args); + +protected: + /** returns true if the given mode maps to a telephony or VoIP call is in progress */ + virtual bool isModeInCall(int mode) + { return ((mode == AudioSystem::MODE_IN_CALL) + || (mode == AudioSystem::MODE_IN_COMMUNICATION)); }; + /** returns true if a telephony or VoIP call is in progress */ + virtual bool isInCall() { return isModeInCall(mMode); }; + int mMode; +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_BASE_H
diff --git a/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h b/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h new file mode 100644 index 0000000..6328063 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h
@@ -0,0 +1,309 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_HARDWARE_INTERFACE_H +#define ANDROID_AUDIO_HARDWARE_INTERFACE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Vector.h> +#include <utils/String16.h> +#include <utils/String8.h> + +#include <media/IAudioFlinger.h> +#include <hardware_legacy/AudioSystemLegacy.h> + +#include <system/audio.h> +#include <hardware/audio.h> + +#include <cutils/bitops.h> + +namespace android_audio_legacy { + using android::Vector; + using android::String16; + using android::String8; + +// ---------------------------------------------------------------------------- + +/** + * AudioStreamOut is the abstraction interface for the audio output hardware. + * + * It provides information about various properties of the audio output hardware driver. + */ +class AudioStreamOut { +public: + virtual ~AudioStreamOut() = 0; + + /** return audio sampling rate in hz - eg. 44100 */ + virtual uint32_t sampleRate() const = 0; + + /** returns size of output buffer - eg. 4800 */ + virtual size_t bufferSize() const = 0; + + /** + * returns the output channel mask + */ + virtual uint32_t channels() const = 0; + + /** + * return audio format in 8bit or 16bit PCM format - + * eg. AudioSystem:PCM_16_BIT + */ + virtual int format() const = 0; + + /** + * return the frame size (number of bytes per sample). + */ + uint32_t frameSize() const { return audio_channel_count_from_out_mask(channels())* + ((format()==AUDIO_FORMAT_PCM_16_BIT)?sizeof(int16_t):sizeof(int8_t)); } + + /** + * return the audio hardware driver latency in milli seconds. + */ + virtual uint32_t latency() const = 0; + + /** + * Use this method in situations where audio mixing is done in the + * hardware. This method serves as a direct interface with hardware, + * allowing you to directly set the volume as apposed to via the framework. + * This method might produce multiple PCM outputs or hardware accelerated + * codecs, such as MP3 or AAC. + */ + virtual status_t setVolume(float left, float right) = 0; + + /** write audio buffer to driver. Returns number of bytes written */ + virtual ssize_t write(const void* buffer, size_t bytes) = 0; + + /** + * Put the audio hardware output into standby mode. Returns + * status based on include/utils/Errors.h + */ + virtual status_t standby() = 0; + + /** dump the state of the audio output device */ + virtual status_t dump(int fd, const Vector<String16>& args) = 0; + + // set/get audio output parameters. The function accepts a list of parameters + // key value pairs in the form: key1=value1;key2=value2;... + // Some keys are reserved for standard parameters (See AudioParameter class). + // If the implementation does not accept a parameter change while the output is + // active but the parameter is acceptable otherwise, it must return INVALID_OPERATION. + // The audio flinger will put the output in standby and then change the parameter value. + virtual status_t setParameters(const String8& keyValuePairs) = 0; + virtual String8 getParameters(const String8& keys) = 0; + + // return the number of audio frames written by the audio dsp to DAC since + // the output has exited standby + virtual status_t getRenderPosition(uint32_t *dspFrames) = 0; + + /** + * get the local time at which the next write to the audio driver will be + * presented + */ + virtual status_t getNextWriteTimestamp(int64_t *timestamp); + + /** + * Return a recent count of the number of audio frames presented to an external observer. + */ + virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); + +}; + +/** + * AudioStreamIn is the abstraction interface for the audio input hardware. + * + * It defines the various properties of the audio hardware input driver. + */ +class AudioStreamIn { +public: + virtual ~AudioStreamIn() = 0; + + /** return audio sampling rate in hz - eg. 44100 */ + virtual uint32_t sampleRate() const = 0; + + /** return the input buffer size allowed by audio driver */ + virtual size_t bufferSize() const = 0; + + /** return input channel mask */ + virtual uint32_t channels() const = 0; + + /** + * return audio format in 8bit or 16bit PCM format - + * eg. AudioSystem:PCM_16_BIT + */ + virtual int format() const = 0; + + /** + * return the frame size (number of bytes per sample). + */ + uint32_t frameSize() const { return audio_channel_count_from_in_mask(channels())* + ((format()==AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(int8_t)); } + + /** set the input gain for the audio driver. This method is for + * for future use */ + virtual status_t setGain(float gain) = 0; + + /** read audio buffer in from audio driver */ + virtual ssize_t read(void* buffer, ssize_t bytes) = 0; + + /** dump the state of the audio input device */ + virtual status_t dump(int fd, const Vector<String16>& args) = 0; + + /** + * Put the audio hardware input into standby mode. Returns + * status based on include/utils/Errors.h + */ + virtual status_t standby() = 0; + + // set/get audio input parameters. The function accepts a list of parameters + // key value pairs in the form: key1=value1;key2=value2;... + // Some keys are reserved for standard parameters (See AudioParameter class). + // If the implementation does not accept a parameter change while the output is + // active but the parameter is acceptable otherwise, it must return INVALID_OPERATION. + // The audio flinger will put the input in standby and then change the parameter value. + virtual status_t setParameters(const String8& keyValuePairs) = 0; + virtual String8 getParameters(const String8& keys) = 0; + + + // Return the number of input frames lost in the audio driver since the last call of this function. + // Audio driver is expected to reset the value to 0 and restart counting upon returning the current value by this function call. + // Such loss typically occurs when the user space process is blocked longer than the capacity of audio driver buffers. + // Unit: the number of input audio frames + virtual unsigned int getInputFramesLost() const = 0; + + virtual status_t addAudioEffect(effect_handle_t effect) = 0; + virtual status_t removeAudioEffect(effect_handle_t effect) = 0; +}; + +/** + * AudioHardwareInterface.h defines the interface to the audio hardware abstraction layer. + * + * The interface supports setting and getting parameters, selecting audio routing + * paths, and defining input and output streams. + * + * AudioFlinger initializes the audio hardware and immediately opens an output stream. + * You can set Audio routing to output to handset, speaker, Bluetooth, or a headset. + * + * The audio input stream is initialized when AudioFlinger is called to carry out + * a record operation. + */ +class AudioHardwareInterface +{ +public: + virtual ~AudioHardwareInterface() {} + + /** + * check to see if the audio hardware interface has been initialized. + * return status based on values defined in include/utils/Errors.h + */ + virtual status_t initCheck() = 0; + + /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */ + virtual status_t setVoiceVolume(float volume) = 0; + + /** + * set the audio volume for all audio activities other than voice call. + * Range between 0.0 and 1.0. If any value other than NO_ERROR is returned, + * the software mixer will emulate this capability. + */ + virtual status_t setMasterVolume(float volume) = 0; + + /** + * Get the current master volume value for the HAL, if the HAL supports + * master volume control. AudioFlinger will query this value from the + * primary audio HAL when the service starts and use the value for setting + * the initial master volume across all HALs. + */ + virtual status_t getMasterVolume(float *volume) = 0; + + /** + * setMode is called when the audio mode changes. NORMAL mode is for + * standard audio playback, RINGTONE when a ringtone is playing, and IN_CALL + * when a call is in progress. + */ + virtual status_t setMode(int mode) = 0; + + // mic mute + virtual status_t setMicMute(bool state) = 0; + virtual status_t getMicMute(bool* state) = 0; + + // set/get global audio parameters + virtual status_t setParameters(const String8& keyValuePairs) = 0; + virtual String8 getParameters(const String8& keys) = 0; + + // Returns audio input buffer size according to parameters passed or 0 if one of the + // parameters is not supported + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0; + + /** This method creates and opens the audio hardware output stream */ + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0) = 0; + virtual AudioStreamOut* openOutputStreamWithFlags( + uint32_t devices, + audio_output_flags_t flags=(audio_output_flags_t)0, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0) = 0; + virtual void closeOutputStream(AudioStreamOut* out) = 0; + + /** This method creates and opens the audio hardware input stream */ + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics) = 0; + virtual void closeInputStream(AudioStreamIn* in) = 0; + + /**This method dumps the state of the audio hardware */ + virtual status_t dumpState(int fd, const Vector<String16>& args) = 0; + + virtual status_t setMasterMute(bool muted) = 0; + + static AudioHardwareInterface* create(); + + virtual int createAudioPatch(unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *handle) = 0; + + virtual int releaseAudioPatch(audio_patch_handle_t handle) = 0; + + virtual int getAudioPort(struct audio_port *port) = 0; + + virtual int setAudioPortConfig(const struct audio_port_config *config) = 0; + +protected: + + virtual status_t dump(int fd, const Vector<String16>& args) = 0; +}; + +// ---------------------------------------------------------------------------- + +extern "C" AudioHardwareInterface* createAudioHardware(void); + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_INTERFACE_H
diff --git a/libhardware_legacy/include/hardware_legacy/AudioPolicyInterface.h b/libhardware_legacy/include/hardware_legacy/AudioPolicyInterface.h new file mode 100644 index 0000000..da03ee3 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/AudioPolicyInterface.h
@@ -0,0 +1,261 @@ +/* + * Copyright (C) 2009 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. + */ + +#ifndef ANDROID_AUDIOPOLICYINTERFACE_H +#define ANDROID_AUDIOPOLICYINTERFACE_H + +#include <media/AudioSystem.h> +#include <media/ToneGenerator.h> +#include <utils/String8.h> + +#include <hardware_legacy/AudioSystemLegacy.h> +#include <hardware/audio_policy.h> + +namespace android_audio_legacy { + using android::Vector; + using android::String8; + using android::ToneGenerator; + +// ---------------------------------------------------------------------------- + +// The AudioPolicyInterface and AudioPolicyClientInterface classes define the communication interfaces +// between the platform specific audio policy manager and Android generic audio policy manager. +// The platform specific audio policy manager must implement methods of the AudioPolicyInterface class. +// This implementation makes use of the AudioPolicyClientInterface to control the activity and +// configuration of audio input and output streams. +// +// The platform specific audio policy manager is in charge of the audio routing and volume control +// policies for a given platform. +// The main roles of this module are: +// - keep track of current system state (removable device connections, phone state, user requests...). +// System state changes and user actions are notified to audio policy manager with methods of the AudioPolicyInterface. +// - process getOutput() queries received when AudioTrack objects are created: Those queries +// return a handler on an output that has been selected, configured and opened by the audio policy manager and that +// must be used by the AudioTrack when registering to the AudioFlinger with the createTrack() method. +// When the AudioTrack object is released, a putOutput() query is received and the audio policy manager can decide +// to close or reconfigure the output depending on other streams using this output and current system state. +// - similarly process getInput() and putInput() queries received from AudioRecord objects and configure audio inputs. +// - process volume control requests: the stream volume is converted from an index value (received from UI) to a float value +// applicable to each output as a function of platform specific settings and current output route (destination device). It +// also make sure that streams are not muted if not allowed (e.g. camera shutter sound in some countries). +// +// The platform specific audio policy manager is provided as a shared library by platform vendors (as for libaudio.so) +// and is linked with libaudioflinger.so + + +// Audio Policy Manager Interface +class AudioPolicyInterface +{ + +public: + virtual ~AudioPolicyInterface() {} + // + // configuration functions + // + + // indicate a change in device connection status + virtual status_t setDeviceConnectionState(audio_devices_t device, + AudioSystem::device_connection_state state, + const char *device_address) = 0; + // retrieve a device connection status + virtual AudioSystem::device_connection_state getDeviceConnectionState(audio_devices_t device, + const char *device_address) = 0; + // indicate a change in phone state. Valid phones states are defined by AudioSystem::audio_mode + virtual void setPhoneState(int state) = 0; + // force using a specific device category for the specified usage + virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) = 0; + // retrieve current device category forced for a given usage + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage) = 0; + // set a system property (e.g. camera sound always audible) + virtual void setSystemProperty(const char* property, const char* value) = 0; + // check proper initialization + virtual status_t initCheck() = 0; + + // + // Audio routing query functions + // + + // request an output appropriate for playback of the supplied stream type and parameters + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::output_flags flags, + const audio_offload_info_t *offloadInfo) = 0; + // indicates to the audio policy manager that the output starts being used by corresponding stream. + virtual status_t startOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session = 0) = 0; + // indicates to the audio policy manager that the output stops being used by corresponding stream. + virtual status_t stopOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session = 0) = 0; + // releases the output. + virtual void releaseOutput(audio_io_handle_t output) = 0; + + // request an input appropriate for record from the supplied device with supplied parameters. + virtual audio_io_handle_t getInput(int inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::audio_in_acoustics acoustics) = 0; + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input) = 0; + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input) = 0; + // releases the input. + virtual void releaseInput(audio_io_handle_t input) = 0; + + // + // volume control functions + // + + // initialises stream volume conversion parameters by specifying volume index range. + virtual void initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) = 0; + + // sets the new stream volume at a level corresponding to the supplied index for the + // supplied device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // setting volume for all devices + virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, + int index, + audio_devices_t device) = 0; + + // retrieve current volume index for the specified stream and the + // specified device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // querying the volume of the active device. + virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, + int *index, + audio_devices_t device) = 0; + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream) = 0; + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(AudioSystem::stream_type stream) = 0; + + // Audio effect management + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0; + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) = 0; + virtual status_t unregisterEffect(int id) = 0; + virtual status_t setEffectEnabled(int id, bool enabled) = 0; + + virtual bool isStreamActive(int stream, uint32_t inPastMs = 0) const = 0; + virtual bool isStreamActiveRemotely(int stream, uint32_t inPastMs = 0) const = 0; + virtual bool isSourceActive(audio_source_t source) const = 0; + + //dump state + virtual status_t dump(int fd) = 0; + + virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0; +}; + + +// Audio Policy client Interface +class AudioPolicyClientInterface +{ +public: + virtual ~AudioPolicyClientInterface() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name) = 0; + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual audio_io_handle_t openOutput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo = NULL) = 0; + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2) = 0; + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output) = 0; + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output) = 0; + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output) = 0; + + // + // Audio input Control functions + // + + // opens an audio input + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) = 0; + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input) = 0; + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0) = 0; + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(AudioSystem::stream_type stream) = 0; + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0) = 0; + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) = 0; + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream) = 0; + virtual status_t stopTone() = 0; + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0) = 0; + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput) = 0; + +}; + +extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface); + + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYINTERFACE_H
diff --git a/libhardware_legacy/include/hardware_legacy/AudioPolicyManagerBase.h b/libhardware_legacy/include/hardware_legacy/AudioPolicyManagerBase.h new file mode 100644 index 0000000..cf03f78 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/AudioPolicyManagerBase.h
@@ -0,0 +1,600 @@ +/* + * Copyright (C) 2009 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 <stdint.h> +#include <sys/types.h> +#include <cutils/config_utils.h> +#include <cutils/misc.h> +#include <utils/Timers.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> +#include <hardware_legacy/AudioPolicyInterface.h> + + +namespace android_audio_legacy { + using android::KeyedVector; + using android::DefaultKeyedVector; + using android::SortedVector; + +// ---------------------------------------------------------------------------- + +#define MAX_DEVICE_ADDRESS_LEN 20 +// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB +#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5 +// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB +#define SONIFICATION_HEADSET_VOLUME_MIN 0.016 +// Time in milliseconds during which we consider that music is still active after a music +// track was stopped - see computeVolume() +#define SONIFICATION_HEADSET_MUSIC_DELAY 5000 +// Time in milliseconds after media stopped playing during which we consider that the +// sonification should be as unobtrusive as during the time media was playing. +#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 +// Time in milliseconds during witch some streams are muted while the audio path +// is switched +#define MUTE_TIME_MS 2000 + +#define NUM_TEST_OUTPUTS 5 + +#define NUM_VOL_CURVE_KNEES 2 + +// Default minimum length allowed for offloading a compressed track +// Can be overridden by the audio.offload.min.duration.secs property +#define OFFLOAD_DEFAULT_MIN_DURATION_SECS 60 + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerBase implements audio policy manager behavior common to all platforms. +// Each platform must implement an AudioPolicyManager class derived from AudioPolicyManagerBase +// and override methods for which the platform specific behavior differs from the implementation +// in AudioPolicyManagerBase. Even if no specific behavior is required, the AudioPolicyManager +// class must be implemented as well as the class factory function createAudioPolicyManager() +// and provided in a shared library libaudiopolicy.so. +// ---------------------------------------------------------------------------- + +class AudioPolicyManagerBase: public AudioPolicyInterface +#ifdef AUDIO_POLICY_TEST + , public Thread +#endif //AUDIO_POLICY_TEST +{ + +public: + AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface); + virtual ~AudioPolicyManagerBase(); + + // AudioPolicyInterface + virtual status_t setDeviceConnectionState(audio_devices_t device, + AudioSystem::device_connection_state state, + const char *device_address); + virtual AudioSystem::device_connection_state getDeviceConnectionState(audio_devices_t device, + const char *device_address); + virtual void setPhoneState(int state); + virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); + virtual void setSystemProperty(const char* property, const char* value); + virtual status_t initCheck(); + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::output_flags flags, + const audio_offload_info_t *offloadInfo); + virtual status_t startOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session = 0); + virtual status_t stopOutput(audio_io_handle_t output, + AudioSystem::stream_type stream, + int session = 0); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(int inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + AudioSystem::audio_in_acoustics acoustics); + + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input); + + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual void closeAllInputs(); + virtual void initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, + int index, + audio_devices_t device); + virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, + int *index, + audio_devices_t device); + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream); + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(AudioSystem::stream_type stream); + + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc = NULL); + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id); + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); + + virtual bool isStreamActive(int stream, uint32_t inPastMs = 0) const; + // return whether a stream is playing remotely, override to change the definition of + // local/remote playback, used for instance by notification manager to not make + // media players lose audio focus when not playing locally + virtual bool isStreamActiveRemotely(int stream, uint32_t inPastMs = 0) const; + virtual bool isSourceActive(audio_source_t source) const; + + virtual status_t dump(int fd); + + virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo); + +protected: + + enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + NUM_STRATEGIES + }; + + // 4 points to define the volume attenuation curve, each characterized by the volume + // index (from 0 to 100) at which they apply, and the attenuation in dB at that index. + // we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl() + + enum { VOLMIN = 0, VOLKNEE1 = 1, VOLKNEE2 = 2, VOLMAX = 3, VOLCNT = 4}; + + class VolumeCurvePoint + { + public: + int mIndex; + float mDBAttenuation; + }; + + // device categories used for volume curve management. + enum device_category { + DEVICE_CATEGORY_HEADSET, + DEVICE_CATEGORY_SPEAKER, + DEVICE_CATEGORY_EARPIECE, + DEVICE_CATEGORY_CNT + }; + + class IOProfile; + + class HwModule { + public: + HwModule(const char *name); + ~HwModule(); + + void dump(int fd); + + const char *const mName; // base name of the audio HW module (primary, a2dp ...) + audio_module_handle_t mHandle; + Vector <IOProfile *> mOutputProfiles; // output profiles exposed by this module + Vector <IOProfile *> mInputProfiles; // input profiles exposed by this module + }; + + // the IOProfile class describes the capabilities of an output or input stream. + // It is currently assumed that all combination of listed parameters are supported. + // It is used by the policy manager to determine if an output or input is suitable for + // a given use case, open/close it accordingly and connect/disconnect audio tracks + // to/from it. + class IOProfile + { + public: + IOProfile(HwModule *module); + ~IOProfile(); + + bool isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const; + + void dump(int fd); + void log(); + + // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats + // indicates the supported parameters should be read from the output stream + // after it is opened for the first time + Vector <uint32_t> mSamplingRates; // supported sampling rates + Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks + Vector <audio_format_t> mFormats; // supported audio formats + audio_devices_t mSupportedDevices; // supported devices (devices this output can be + // routed to) + audio_output_flags_t mFlags; // attribute flags (e.g primary output, + // direct output...). For outputs only. + HwModule *mModule; // audio HW module exposing this I/O stream + }; + + // default volume curve + static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + // default volume curve for media strategy + static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + // volume curve for media strategy on speakers + static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + // volume curve for sonification strategy on speakers + static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sHeadsetSystemVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sDefaultVoiceVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[AudioPolicyManagerBase::VOLCNT]; + // default volume curves per stream and device category. See initializeVolumeCurves() + static const VolumeCurvePoint *sVolumeProfiles[AudioSystem::NUM_STREAM_TYPES][DEVICE_CATEGORY_CNT]; + + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output + // and keep track of the usage of this output by each audio stream type. + class AudioOutputDescriptor + { + public: + AudioOutputDescriptor(const IOProfile *profile); + + status_t dump(int fd); + + audio_devices_t device() const; + void changeRefCount(AudioSystem::stream_type stream, int delta); + + bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } + audio_devices_t supportedDevices(); + uint32_t latency(); + bool sharesHwModuleWith(const AudioOutputDescriptor *outputDesc); + bool isActive(uint32_t inPastMs = 0) const; + bool isStreamActive(AudioSystem::stream_type stream, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + bool isStrategyActive(routing_strategy strategy, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + + audio_io_handle_t mId; // output handle + uint32_t mSamplingRate; // + audio_format_t mFormat; // + audio_channel_mask_t mChannelMask; // output configuration + uint32_t mLatency; // + audio_output_flags_t mFlags; // + audio_devices_t mDevice; // current device this output is routed to + uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output + nsecs_t mStopTime[AudioSystem::NUM_STREAM_TYPES]; + AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output + AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output + float mCurVolume[AudioSystem::NUM_STREAM_TYPES]; // current stream volume + int mMuteCount[AudioSystem::NUM_STREAM_TYPES]; // mute request counter + const IOProfile *mProfile; // I/O profile this output derives from + bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible + // device selection. See checkDeviceMuteStrategies() + uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) + bool mForceRouting; // Next routing for this output will be forced as current device routed is null + }; + + // descriptor for audio inputs. Used to maintain current configuration of each opened audio input + // and keep track of the usage of this input. + class AudioInputDescriptor + { + public: + AudioInputDescriptor(const IOProfile *profile); + + status_t dump(int fd); + + audio_io_handle_t mId; // input handle + uint32_t mSamplingRate; // + audio_format_t mFormat; // input configuration + audio_channel_mask_t mChannelMask; // + audio_devices_t mDevice; // current device this input is routed to + uint32_t mRefCount; // number of AudioRecord clients using this output + int mInputSource; // input source selected by application (mediarecorder.h) + const IOProfile *mProfile; // I/O profile this output derives from + }; + + // stream descriptor used for volume control + class StreamDescriptor + { + public: + StreamDescriptor(); + + int getVolumeIndex(audio_devices_t device); + void dump(int fd); + + int mIndexMin; // min volume index + int mIndexMax; // max volume index + KeyedVector<audio_devices_t, int> mIndexCur; // current volume index per device + bool mCanBeMuted; // true is the stream can be muted + + const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; + }; + + // stream descriptor used for volume control + class EffectDescriptor + { + public: + + status_t dump(int fd); + + int mIo; // io the effect is attached to + routing_strategy mStrategy; // routing strategy the effect is associated to + int mSession; // audio session the effect is on + effect_descriptor_t mDesc; // effect descriptor + bool mEnabled; // enabled state: CPU load being used or not + }; + + void addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc); + void addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc); + + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(AudioSystem::stream_type stream); + + // return appropriate device for streams handled by the specified strategy according to current + // phone state, connected devices... + // if fromCache is true, the device is returned from mDeviceForStrategy[], + // otherwise it is determine by current state + // (device connected,phone state, force use, a2dp output...) + // This allows to: + // 1 speed up process when the state is stable (when starting or stopping an output) + // 2 access to either current device selection (fromCache == true) or + // "future" device selection (fromCache == false) when called from a context + // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + // before updateDevicesAndOutputs() is called. + virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, + bool fromCache); + + // change the route of the specified output. Returns the number of ms we have slept to + // allow new routing to take effect in certain cases. + uint32_t setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force = false, + int delayMs = 0); + + // select input device corresponding to requested audio source + virtual audio_devices_t getDeviceForInputSource(int inputSource); + + // return io handle of active input or 0 if no input is active + // Only considers inputs from physical devices (e.g. main mic, headset mic) when + // ignoreVirtualInputs is true. + audio_io_handle_t getActiveInput(bool ignoreVirtualInputs = true); + + // initialize volume curves for each strategy and device category + void initializeVolumeCurves(); + + // compute the actual volume for a given stream according to the requested index and a particular + // device + virtual float computeVolume(int stream, int index, audio_io_handle_t output, audio_devices_t device); + + // check that volume change is permitted, compute and send new volume to audio hardware + status_t checkAndSetVolume(int stream, int index, audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false); + + // apply all stream volumes to the specified output and device + void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false); + + // Mute or unmute all streams handled by the specified strategy on the specified output + void setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // Mute or unmute the stream on the specified output + void setStreamMute(int stream, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // handle special cases for sonification strategy while in call: mute streams or replace by + // a special tone in the device used for communication + void handleIncallSonification(int stream, bool starting, bool stateChange); + + // true if device is in a telephony or VoIP call + virtual bool isInCall(); + + // true if given state represents a device in a telephony or VoIP call + virtual bool isStateInCall(int state); + + // when a device is connected, checks if an open output can be routed + // to this device. If none is open, tries to open one of the available outputs. + // Returns an output suitable to this device or 0. + // when a device is disconnected, checks if an output is not used any more and + // returns its handle if any. + // transfers the audio tracks and effects from one output thread to another accordingly. + status_t checkOutputsForDevice(audio_devices_t device, + AudioSystem::device_connection_state state, + SortedVector<audio_io_handle_t>& outputs, + const String8 paramStr); + + status_t checkInputsForDevice(audio_devices_t device, + AudioSystem::device_connection_state state, + SortedVector<audio_io_handle_t>& inputs, + const String8 paramStr); + + // close an output and its companion duplicating output. + void closeOutput(audio_io_handle_t output); + + // checks and if necessary changes outputs used for all strategies. + // must be called every time a condition that affects the output choice for a given strategy + // changes: connected device, phone state, force use... + // Must be called before updateDevicesAndOutputs() + void checkOutputForStrategy(routing_strategy strategy); + + // Same as checkOutputForStrategy() but for a all strategies in order of priority + void checkOutputForAllStrategies(); + + // manages A2DP output suspend/restore according to phone state and BT SCO usage + void checkA2dpSuspend(); + + // returns the A2DP output handle if it is open or 0 otherwise + audio_io_handle_t getA2dpOutput(); + + // selects the most appropriate device on output for current state + // must be called every time a condition that affects the device choice for a given output is + // changed: connected device, phone state, force use, output start, output stop.. + // see getDeviceForStrategy() for the use of fromCache parameter + + audio_devices_t getNewDevice(audio_io_handle_t output, bool fromCache); + // updates cache of device used by all strategies (mDeviceForStrategy[]) + // must be called every time a condition that affects the device choice for a given strategy is + // changed: connected device, phone state, force use... + // cached values are used by getDeviceForStrategy() if parameter fromCache is true. + // Must be called after checkOutputForAllStrategies() + + void updateDevicesAndOutputs(); + + virtual uint32_t getMaxEffectsCpuLoad(); + virtual uint32_t getMaxEffectsMemory(); +#ifdef AUDIO_POLICY_TEST + virtual bool threadLoop(); + void exit(); + int testOutputIndex(audio_io_handle_t output); +#endif //AUDIO_POLICY_TEST + + status_t setEffectEnabled(EffectDescriptor *pDesc, bool enabled); + + // returns the category the device belongs to with regard to volume curve management + static device_category getDeviceCategory(audio_devices_t device); + + // extract one device relevant for volume control from multiple device selection + static audio_devices_t getDeviceForVolume(audio_devices_t device); + + SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs); + bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2); + + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + // Returns the number of ms waited + uint32_t checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs); + + audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs, + AudioSystem::output_flags flags); + IOProfile *getInputProfile(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask); + IOProfile *getProfileForDirectOutput(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags); + + audio_io_handle_t selectOutputForEffects(const SortedVector<audio_io_handle_t>& outputs); + + bool isNonOffloadableEffectEnabled(); + + // + // Audio policy configuration file parsing (audio_policy.conf) + // + static uint32_t stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name); + static bool stringToBool(const char *value); + static audio_output_flags_t parseFlagNames(char *name); + static audio_devices_t parseDeviceNames(char *name); + void loadSamplingRates(char *name, IOProfile *profile); + void loadFormats(char *name, IOProfile *profile); + void loadOutChannels(char *name, IOProfile *profile); + void loadInChannels(char *name, IOProfile *profile); + status_t loadOutput(cnode *root, HwModule *module); + status_t loadInput(cnode *root, HwModule *module); + void loadHwModule(cnode *root); + void loadHwModules(cnode *root); + void loadGlobalConfig(cnode *root); + status_t loadAudioPolicyConfig(const char *path); + void defaultAudioPolicyConfig(void); + + + AudioPolicyClientInterface *mpClientInterface; // audio policy client interface + audio_io_handle_t mPrimaryOutput; // primary output handle + // list of descriptors for outputs currently opened + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; + // copy of mOutputs before setDeviceConnectionState() opens new outputs + // reset to mOutputs when updateDevicesAndOutputs() is called. + DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs; + + // list of input descriptors currently opened + DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; + + audio_devices_t mAvailableOutputDevices; // bit field of all available output devices + audio_devices_t mAvailableInputDevices; // bit field of all available input devices + // without AUDIO_DEVICE_BIT_IN to allow direct bit + // field comparisons + int mPhoneState; // current phone state + AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration + + StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control + String8 mA2dpDeviceAddress; // A2DP device MAC address + String8 mScoDeviceAddress; // SCO device MAC address + String8 mUsbOutCardAndDevice; // USB audio ALSA card and device numbers: + // card=<card_number>;device=<><device_number> + bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected + audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; + float mLastVoiceVolume; // last voice volume value sent to audio HAL + + // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units + static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000; + // Maximum memory allocated to audio effects in KB + static const uint32_t MAX_EFFECTS_MEMORY = 512; + uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects + uint32_t mTotalEffectsMemory; // current memory used by effects + KeyedVector<int, EffectDescriptor *> mEffects; // list of registered audio effects + bool mA2dpSuspended; // true if A2DP output is suspended + bool mHasA2dp; // true on platforms with support for bluetooth A2DP + bool mHasUsb; // true on platforms with support for USB audio + bool mHasRemoteSubmix; // true on platforms with support for remote presentation of a submix + audio_devices_t mAttachedOutputDevices; // output devices always available on the platform + audio_devices_t mDefaultOutputDevice; // output device selected by default at boot time + // (must be in mAttachedOutputDevices) + bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path + // to boost soft sounds, used to adjust volume curves accordingly + + Vector <HwModule *> mHwModules; + +#ifdef AUDIO_POLICY_TEST + Mutex mLock; + Condition mWaitWorkCV; + + int mCurOutput; + bool mDirectOutput; + audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; + int mTestInput; + uint32_t mTestDevice; + uint32_t mTestSamplingRate; + uint32_t mTestFormat; + uint32_t mTestChannels; + uint32_t mTestLatencyMs; +#endif //AUDIO_POLICY_TEST + +private: + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + // updates device caching and output for streams that can influence the + // routing of notifications + void handleNotificationRoutingForStream(AudioSystem::stream_type stream); + static bool isVirtualInputDevice(audio_devices_t device); +}; + +};
diff --git a/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h b/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h new file mode 100644 index 0000000..524e798 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h
@@ -0,0 +1,362 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_AUDIOSYSTEM_LEGACY_H_ +#define ANDROID_AUDIOSYSTEM_LEGACY_H_ + +#include <utils/Errors.h> +#include <media/AudioParameter.h> + +#include <system/audio.h> +#include <system/audio_policy.h> + +namespace android_audio_legacy { + +using android::status_t; +using android::AudioParameter; + +enum { + OK = android::OK, + NO_ERROR = android::NO_ERROR, + + UNKNOWN_ERROR = android::UNKNOWN_ERROR, + + NO_MEMORY = android::NO_MEMORY, + INVALID_OPERATION = android::INVALID_OPERATION, + BAD_VALUE = android::BAD_VALUE, + BAD_TYPE = android::BAD_TYPE, + NAME_NOT_FOUND = android::NAME_NOT_FOUND, + PERMISSION_DENIED = android::PERMISSION_DENIED, + NO_INIT = android::NO_INIT, + ALREADY_EXISTS = android::ALREADY_EXISTS, + DEAD_OBJECT = android::DEAD_OBJECT, + FAILED_TRANSACTION = android::FAILED_TRANSACTION, + BAD_INDEX = android::BAD_INDEX, + NOT_ENOUGH_DATA = android::NOT_ENOUGH_DATA, + WOULD_BLOCK = android::WOULD_BLOCK, + TIMED_OUT = android::TIMED_OUT, + UNKNOWN_TRANSACTION = android::UNKNOWN_TRANSACTION, +}; + +enum audio_source { + AUDIO_SOURCE_DEFAULT = 0, + AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_VOICE_UPLINK = 2, + AUDIO_SOURCE_VOICE_DOWNLINK = 3, + AUDIO_SOURCE_VOICE_CALL = 4, + AUDIO_SOURCE_CAMCORDER = 5, + AUDIO_SOURCE_VOICE_RECOGNITION = 6, + AUDIO_SOURCE_VOICE_COMMUNICATION = 7, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_COMMUNICATION, + + AUDIO_SOURCE_LIST_END // must be last - used to validate audio source type +}; + +class AudioSystem { +public: +#if 1 + enum stream_type { + DEFAULT =-1, + VOICE_CALL = 0, + SYSTEM = 1, + RING = 2, + MUSIC = 3, + ALARM = 4, + NOTIFICATION = 5, + BLUETOOTH_SCO = 6, + ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker + DTMF = 8, + TTS = 9, + NUM_STREAM_TYPES + }; + + // Audio sub formats (see AudioSystem::audio_format). + enum pcm_sub_format { + PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility + PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility + }; + + enum audio_sessions { + SESSION_OUTPUT_STAGE = AUDIO_SESSION_OUTPUT_STAGE, + SESSION_OUTPUT_MIX = AUDIO_SESSION_OUTPUT_MIX, + }; + + // MP3 sub format field definition : can use 11 LSBs in the same way as MP3 frame header to specify + // bit rate, stereo mode, version... + enum mp3_sub_format { + //TODO + }; + + // AMR NB/WB sub format field definition: specify frame block interleaving, bandwidth efficient or octet aligned, + // encoding mode for recording... + enum amr_sub_format { + //TODO + }; + + // AAC sub format field definition: specify profile or bitrate for recording... + enum aac_sub_format { + //TODO + }; + + // VORBIS sub format field definition: specify quality for recording... + enum vorbis_sub_format { + //TODO + }; + + // Audio format consists in a main format field (upper 8 bits) and a sub format field (lower 24 bits). + // The main format indicates the main codec type. The sub format field indicates options and parameters + // for each format. The sub format is mainly used for record to indicate for instance the requested bitrate + // or profile. It can also be used for certain formats to give informations not present in the encoded + // audio stream (e.g. octet alignement for AMR). + enum audio_format { + INVALID_FORMAT = -1, + FORMAT_DEFAULT = 0, + PCM = 0x00000000, // must be 0 for backward compatibility + MP3 = 0x01000000, + AMR_NB = 0x02000000, + AMR_WB = 0x03000000, + AAC = 0x04000000, + HE_AAC_V1 = 0x05000000, + HE_AAC_V2 = 0x06000000, + VORBIS = 0x07000000, + MAIN_FORMAT_MASK = 0xFF000000, + SUB_FORMAT_MASK = 0x00FFFFFF, + // Aliases + PCM_16_BIT = (PCM|PCM_SUB_16_BIT), + PCM_8_BIT = (PCM|PCM_SUB_8_BIT) + }; + + enum audio_channels { + // output channels + CHANNEL_OUT_FRONT_LEFT = 0x1, + CHANNEL_OUT_FRONT_RIGHT = 0x2, + CHANNEL_OUT_FRONT_CENTER = 0x4, + CHANNEL_OUT_LOW_FREQUENCY = 0x8, + CHANNEL_OUT_BACK_LEFT = 0x10, + CHANNEL_OUT_BACK_RIGHT = 0x20, + CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, + CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, + CHANNEL_OUT_BACK_CENTER = 0x100, + CHANNEL_OUT_SIDE_LEFT = 0x200, + CHANNEL_OUT_SIDE_RIGHT = 0x400, + CHANNEL_OUT_TOP_CENTER = 0x800, + CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, + CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, + CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, + CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, + CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, + CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, + + CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT, + CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT), + CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT | + CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT), + CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT | + CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER), + CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT | + CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | + CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT), + // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 + CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT | + CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | + CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT | + CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT), + CHANNEL_OUT_ALL = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT | + CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | + CHANNEL_OUT_BACK_RIGHT | CHANNEL_OUT_FRONT_LEFT_OF_CENTER | + CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | CHANNEL_OUT_BACK_CENTER | + CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT | CHANNEL_OUT_TOP_CENTER | + CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_CENTER | + CHANNEL_OUT_TOP_FRONT_RIGHT | CHANNEL_OUT_TOP_BACK_LEFT | + CHANNEL_OUT_TOP_BACK_CENTER | CHANNEL_OUT_TOP_BACK_RIGHT), + + // input channels + CHANNEL_IN_LEFT = 0x4, + CHANNEL_IN_RIGHT = 0x8, + CHANNEL_IN_FRONT = 0x10, + CHANNEL_IN_BACK = 0x20, + CHANNEL_IN_LEFT_PROCESSED = 0x40, + CHANNEL_IN_RIGHT_PROCESSED = 0x80, + CHANNEL_IN_FRONT_PROCESSED = 0x100, + CHANNEL_IN_BACK_PROCESSED = 0x200, + CHANNEL_IN_PRESSURE = 0x400, + CHANNEL_IN_X_AXIS = 0x800, + CHANNEL_IN_Y_AXIS = 0x1000, + CHANNEL_IN_Z_AXIS = 0x2000, + CHANNEL_IN_VOICE_UPLINK = 0x4000, + CHANNEL_IN_VOICE_DNLINK = 0x8000, + CHANNEL_IN_MONO = CHANNEL_IN_FRONT, + CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT), + CHANNEL_IN_ALL = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK| + CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED | CHANNEL_IN_FRONT_PROCESSED | CHANNEL_IN_BACK_PROCESSED| + CHANNEL_IN_PRESSURE | CHANNEL_IN_X_AXIS | CHANNEL_IN_Y_AXIS | CHANNEL_IN_Z_AXIS | + CHANNEL_IN_VOICE_UPLINK | CHANNEL_IN_VOICE_DNLINK) + }; + + enum audio_mode { + MODE_INVALID = -2, + MODE_CURRENT = -1, + MODE_NORMAL = 0, + MODE_RINGTONE, + MODE_IN_CALL, + MODE_IN_COMMUNICATION, + NUM_MODES // not a valid entry, denotes end-of-list + }; + + enum audio_in_acoustics { + AGC_ENABLE = 0x0001, + AGC_DISABLE = 0, + NS_ENABLE = 0x0002, + NS_DISABLE = 0, + TX_IIR_ENABLE = 0x0004, + TX_DISABLE = 0 + }; + + // DO NOT USE: the "audio_devices" enumeration below is obsolete, use type "audio_devices_t" and + // audio device enumeration from system/audio.h instead. + enum audio_devices { + // output devices + DEVICE_OUT_EARPIECE = 0x1, + DEVICE_OUT_SPEAKER = 0x2, + DEVICE_OUT_WIRED_HEADSET = 0x4, + DEVICE_OUT_WIRED_HEADPHONE = 0x8, + DEVICE_OUT_BLUETOOTH_SCO = 0x10, + DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, + DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, + DEVICE_OUT_BLUETOOTH_A2DP = 0x80, + DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, + DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, + DEVICE_OUT_AUX_DIGITAL = 0x400, + DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, + DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + DEVICE_OUT_DEFAULT = 0x8000, + DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET | + DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | + DEVICE_OUT_ANLG_DOCK_HEADSET | DEVICE_OUT_DGTL_DOCK_HEADSET | + DEVICE_OUT_DEFAULT), + DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + + // input devices + DEVICE_IN_COMMUNICATION = 0x10000, + DEVICE_IN_AMBIENT = 0x20000, + DEVICE_IN_BUILTIN_MIC = 0x40000, + DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000, + DEVICE_IN_WIRED_HEADSET = 0x100000, + DEVICE_IN_AUX_DIGITAL = 0x200000, + DEVICE_IN_VOICE_CALL = 0x400000, + DEVICE_IN_BACK_MIC = 0x800000, + DEVICE_IN_DEFAULT = 0x80000000, + + DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC | + DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL | + DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC | DEVICE_IN_DEFAULT) + }; + + // request to open a direct output with getOutput() (by opposition to sharing an output with other AudioTracks) + enum output_flags { + OUTPUT_FLAG_INDIRECT = 0x0, + OUTPUT_FLAG_DIRECT = 0x1 + }; + + // device categories used for setForceUse() + enum forced_config { + FORCE_NONE, + FORCE_SPEAKER, + FORCE_HEADPHONES, + FORCE_BT_SCO, + FORCE_BT_A2DP, + FORCE_WIRED_ACCESSORY, + FORCE_BT_CAR_DOCK, + FORCE_BT_DESK_DOCK, + FORCE_ANALOG_DOCK, + FORCE_DIGITAL_DOCK, + FORCE_NO_BT_A2DP, + FORCE_SYSTEM_ENFORCED, + NUM_FORCE_CONFIG, + FORCE_DEFAULT = FORCE_NONE + }; + + // usages used for setForceUse() + enum force_use { + FOR_COMMUNICATION, + FOR_MEDIA, + FOR_RECORD, + FOR_DOCK, + FOR_SYSTEM, + NUM_FORCE_USE + }; + + // + // AudioPolicyService interface + // + + // device connection states used for setDeviceConnectionState() + enum device_connection_state { + DEVICE_STATE_UNAVAILABLE, + DEVICE_STATE_AVAILABLE, + NUM_DEVICE_STATES + }; + +#endif + + static uint32_t popCount(uint32_t u) { + return popcount(u); + } + +#if 1 + static bool isOutputDevice(audio_devices device) { + if ((popcount(device) == 1) && ((device & ~DEVICE_OUT_ALL) == 0)) + return true; + else + return false; + } + static bool isInputDevice(audio_devices device) { + if ((popcount(device) == 1) && ((device & ~DEVICE_IN_ALL) == 0)) + return true; + else + return false; + } + static bool isA2dpDevice(audio_devices device) { + return audio_is_a2dp_device((audio_devices_t)device); + } + static bool isBluetoothScoDevice(audio_devices device) { + return audio_is_bluetooth_sco_device((audio_devices_t)device); + } + static bool isLowVisibility(stream_type stream) { + return audio_is_low_visibility((audio_stream_type_t)stream); + } + static bool isValidFormat(uint32_t format) { + return audio_is_valid_format((audio_format_t) format); + } + static bool isLinearPCM(uint32_t format) { + return audio_is_linear_pcm((audio_format_t) format); + } + static bool isOutputChannel(uint32_t channel) { + return audio_is_output_channel(channel); + } + static bool isInputChannel(uint32_t channel) { + return audio_is_input_channel(channel); + } + +#endif +}; + +}; // namespace android + +#endif // ANDROID_AUDIOSYSTEM_LEGACY_H_
diff --git a/libhardware_legacy/include/hardware_legacy/IMountService.h b/libhardware_legacy/include/hardware_legacy/IMountService.h new file mode 100644 index 0000000..257319c --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/IMountService.h
@@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 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. + */ + +// +#ifndef ANDROID_HARDWARE_IMOUNTSERVICE_H +#define ANDROID_HARDWARE_IMOUNTSERVICE_H + +#include <binder/IInterface.h> +#include <utils/String16.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class IMountService : public IInterface +{ +public: + static const int OperationSucceeded = 0; + static const int OperationFailedInternalError = -1; + static const int OperationFailedNoMedia = -2; + static const int OperationFailedMediaBlank = -3; + static const int OperationFailedMediaCorrupt = -4; + static const int OperationFailedVolumeNotMounted = -5; + + +public: + DECLARE_META_INTERFACE(MountService); + + virtual void getShareMethodList() = 0; + virtual bool getShareMethodAvailable(String16 method) = 0; + virtual int shareVolume(String16 path, String16 method) = 0; + virtual int unshareVolume(String16 path, String16 method) = 0; + virtual bool getVolumeShared(String16 path, String16 method) = 0; + virtual int mountVolume(String16 path) = 0; + virtual int unmountVolume(String16 path) = 0; + virtual int formatVolume(String16 path) = 0; + virtual String16 getVolumeState(String16 mountPoint) = 0; + virtual int createSecureContainer(String16 id, int sizeMb, String16 fstype, String16 key, int ownerUid) = 0; + virtual int finalizeSecureContainer(String16 id) = 0; + virtual int destroySecureContainer(String16 id) = 0; + virtual int mountSecureContainer(String16 id, String16 key, int ownerUid) = 0; + virtual int unmountSecureContainer(String16 id) = 0; + virtual int renameSecureContainer(String16 oldId, String16 newId) = 0; + virtual String16 getSecureContainerPath(String16 id) = 0; + virtual void getSecureContainerList() = 0; + virtual void shutdown() = 0; +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_HARDWARE_IMOUNTSERVICE_H
diff --git a/libhardware_legacy/include/hardware_legacy/audio_policy_conf.h b/libhardware_legacy/include/hardware_legacy/audio_policy_conf.h new file mode 100644 index 0000000..3ec2c94 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/audio_policy_conf.h
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 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. + */ + + +#ifndef ANDROID_AUDIO_POLICY_CONF_H +#define ANDROID_AUDIO_POLICY_CONF_H + + +///////////////////////////////////////////////// +// Definitions for audio policy configuration file (audio_policy.conf) +///////////////////////////////////////////////// + +#define AUDIO_HARDWARE_MODULE_ID_MAX_LEN 32 + +#define AUDIO_POLICY_CONFIG_FILE "/system/etc/audio_policy.conf" +#define AUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf" + +// global configuration +#define GLOBAL_CONFIG_TAG "global_configuration" + +#define ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices" +#define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device" +#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices" +#define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled" + +// hw modules descriptions +#define AUDIO_HW_MODULE_TAG "audio_hw_modules" + +#define OUTPUTS_TAG "outputs" +#define INPUTS_TAG "inputs" + +#define SAMPLING_RATES_TAG "sampling_rates" +#define FORMATS_TAG "formats" +#define CHANNELS_TAG "channel_masks" +#define DEVICES_TAG "devices" +#define FLAGS_TAG "flags" + +#define DYNAMIC_VALUE_TAG "dynamic" // special value for "channel_masks", "sampling_rates" and + // "formats" in outputs descriptors indicating that supported + // values should be queried after opening the output. + +#endif // ANDROID_AUDIO_POLICY_CONF_H
diff --git a/libhardware_legacy/include/hardware_legacy/gscan.h b/libhardware_legacy/include/hardware_legacy/gscan.h new file mode 100644 index 0000000..69e4159 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/gscan.h
@@ -0,0 +1,461 @@ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_GSCAN_H__ +#define __WIFI_HAL_GSCAN_H__ + +/* AP Scans */ + +typedef enum { + WIFI_BAND_UNSPECIFIED, + WIFI_BAND_BG = 1, // 2.4 GHz + WIFI_BAND_A = 2, // 5 GHz without DFS + WIFI_BAND_A_DFS = 4, // 5 GHz DFS only + WIFI_BAND_A_WITH_DFS = 6, // 5 GHz with DFS + WIFI_BAND_ABG = 3, // 2.4 GHz + 5 GHz; no DFS + WIFI_BAND_ABG_WITH_DFS = 7, // 2.4 GHz + 5 GHz with DFS +} wifi_band; + +const unsigned MAX_CHANNELS = 16; +const unsigned MAX_BUCKETS = 16; +const unsigned MAX_HOTLIST_APS = 128; +const unsigned MAX_SIGNIFICANT_CHANGE_APS = 64; +const unsigned MAX_PNO_SSID = 64; +const unsigned MAX_HOTLIST_SSID = 8; +const unsigned MAX_BLACKLIST_BSSID = 16; +const unsigned MAX_AP_CACHE_PER_SCAN = 32; + +wifi_error wifi_get_valid_channels(wifi_interface_handle handle, + int band, int max_channels, wifi_channel *channels, int *num_channels); + +typedef struct { + int max_scan_cache_size; // total space allocated for scan (in bytes) + int max_scan_buckets; // maximum number of channel buckets + int max_ap_cache_per_scan; // maximum number of APs that can be stored per scan + int max_rssi_sample_size; // number of RSSI samples used for averaging RSSI + int max_scan_reporting_threshold; // max possible report_threshold as described + // in wifi_scan_cmd_params + int max_hotlist_bssids; // maximum number of entries for hotlist BSSIDs + int max_hotlist_ssids; // maximum number of entries for hotlist SSIDs + int max_significant_wifi_change_aps; // maximum number of entries for + // significant wifi change APs + int max_bssid_history_entries; // number of BSSID/RSSI entries that device can hold + int max_number_epno_networks; // max number of epno entries + int max_number_epno_networks_by_ssid; // max number of epno entries if ssid is specified, + // that is, epno entries for which an exact match is + // required, or entries corresponding to hidden ssids + int max_number_of_white_listed_ssid; // max number of white listed SSIDs, M target is 2 to 4 +} wifi_gscan_capabilities; + +wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, + wifi_gscan_capabilities *capabilities); + +typedef enum { + WIFI_SCAN_BUFFER_FULL, + WIFI_SCAN_COMPLETE, +} wifi_scan_event; + + +/* Format of information elements found in the beacon */ +typedef struct { + byte id; // element identifier + byte len; // number of bytes to follow + byte data[]; +} wifi_information_element; + +typedef struct { + wifi_timestamp ts; // time since boot (in microsecond) when the result was + // retrieved + char ssid[32+1]; // null terminated + mac_addr bssid; + wifi_channel channel; // channel frequency in MHz + wifi_rssi rssi; // in db + wifi_timespan rtt; // in nanoseconds + wifi_timespan rtt_sd; // standard deviation in rtt + unsigned short beacon_period; // period advertised in the beacon + unsigned short capability; // capabilities advertised in the beacon + unsigned int ie_length; // size of the ie_data blob + char ie_data[1]; // blob of all the information elements found in the + // beacon; this data should be a packed list of + // wifi_information_element objects, one after the other. + // other fields +} wifi_scan_result; + +typedef struct { + /* reported when report_threshold is reached in scan cache */ + void (*on_scan_results_available) (wifi_request_id id, unsigned num_results_available); + + /* reported when each probe response is received, if report_events + * enabled in wifi_scan_cmd_params */ + void (*on_full_scan_result) (wifi_request_id id, wifi_scan_result *result); + + /* optional event - indicates progress of scanning statemachine */ + void (*on_scan_event) (wifi_scan_event event, unsigned status); + +} wifi_scan_result_handler; + +typedef struct { + wifi_channel channel; // frequency + int dwellTimeMs; // dwell time hint + int passive; // 0 => active, 1 => passive scan; ignored for DFS + /* Add channel class */ +} wifi_scan_channel_spec; + +#define REPORT_EVENTS_BUFFER_FULL 0 +#define REPORT_EVENTS_EACH_SCAN 1 +#define REPORT_EVENTS_FULL_RESULTS 2 +#define REPORT_EVENTS_NO_BATCH 4 + +typedef struct { + int bucket; // bucket index, 0 based + wifi_band band; // when UNSPECIFIED, use channel list + int period; // desired period, in millisecond; if this is too + // low, the firmware should choose to generate results as + // fast as it can instead of failing the command. + // for exponential backoff bucket this is the min_period + /* report_events semantics - + * This is a bit field; which defines following bits - + * REPORT_EVENTS_BUFFER_FULL => report only when scan history is % full + * REPORT_EVENTS_EACH_SCAN => report a scan completion event after scan + * REPORT_EVENTS_FULL_RESULTS => forward scan results (beacons/probe responses + IEs) + * in real time to HAL, in addition to completion events + * Note: To keep backward compatibility, fire completion + * events regardless of REPORT_EVENTS_EACH_SCAN. + * REPORT_EVENTS_NO_BATCH => controls batching, 0 => batching, 1 => no batching + */ + byte report_events; + int max_period; // if max_period is non zero or different than period, then this bucket is + // an exponential backoff bucket and the scan period will grow exponentially + // as per formula: actual_period(N) = period ^ (N/(step_count+1)) + // to a maximum period of max_period + int exponent; // for exponential back off bucket: multiplier: new_period=old_period*exponent + int step_count; // for exponential back off bucket, number of scans performed at a given + // period and until the exponent is applied + + int num_channels; + // channels to scan; these may include DFS channels + // Note that a given channel may appear in multiple buckets + wifi_scan_channel_spec channels[MAX_CHANNELS]; +} wifi_scan_bucket_spec; + +typedef struct { + int base_period; // base timer period in ms + int max_ap_per_scan; // number of APs to store in each scan ientryn the + // BSSID/RSSI history buffer (keep the highest RSSI APs) + int report_threshold_percent; // in %, when scan buffer is this much full, wake up AP + int report_threshold_num_scans; // in number of scans, wake up AP after these many scans + int num_buckets; + wifi_scan_bucket_spec buckets[MAX_BUCKETS]; +} wifi_scan_cmd_params; + +/* Start periodic GSCAN */ +wifi_error wifi_start_gscan(wifi_request_id id, wifi_interface_handle iface, + wifi_scan_cmd_params params, wifi_scan_result_handler handler); + +/* Stop periodic GSCAN */ +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface); + +typedef enum { + WIFI_SCAN_FLAG_INTERRUPTED = 1 // Indicates that scan results are not complete because + // probes were not sent on some channels +} wifi_scan_flags; + +/* Get the GSCAN cached scan results */ +typedef struct { + int scan_id; // a unique identifier for the scan unit + int flags; // a bitmask with additional + // information about scan + int num_results; // number of bssids retrieved by the scan + wifi_scan_result results[MAX_AP_CACHE_PER_SCAN]; // scan results - one for each bssid +} wifi_cached_scan_results; + +wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, + int max, wifi_cached_scan_results *results, int *num); + +/* BSSID Hotlist */ +typedef struct { + void (*on_hotlist_ap_found)(wifi_request_id id, + unsigned num_results, wifi_scan_result *results); + void (*on_hotlist_ap_lost)(wifi_request_id id, + unsigned num_results, wifi_scan_result *results); +} wifi_hotlist_ap_found_handler; + +typedef struct { + mac_addr bssid; // AP BSSID + wifi_rssi low; // low threshold + wifi_rssi high; // high threshold +} ap_threshold_param; + +typedef struct { + int lost_ap_sample_size; + int num_bssid; // number of hotlist APs + ap_threshold_param ap[MAX_HOTLIST_APS]; // hotlist APs +} wifi_bssid_hotlist_params; + +/* Set the BSSID Hotlist */ +wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, + wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler); + +/* Clear the BSSID Hotlist */ +wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface); + +/* SSID Hotlist */ +typedef struct { + void (*on_hotlist_ssid_found)(wifi_request_id id, + unsigned num_results, wifi_scan_result *results); + void (*on_hotlist_ssid_lost)(wifi_request_id id, + unsigned num_results, wifi_scan_result *results); +} wifi_hotlist_ssid_handler; + +typedef struct { + char ssid[32+1]; // SSID + wifi_band band; // band for this set of threshold params + wifi_rssi low; // low threshold + wifi_rssi high; // high threshold +} ssid_threshold_param; + +typedef struct { + int lost_ssid_sample_size; + int num_ssid; // number of hotlist SSIDs + ssid_threshold_param ssid[MAX_HOTLIST_SSID]; // hotlist SSIDs +} wifi_ssid_hotlist_params; + + +/* Set the SSID Hotlist */ +wifi_error wifi_set_ssid_hotlist(wifi_request_id id, wifi_interface_handle iface, + wifi_ssid_hotlist_params params, wifi_hotlist_ssid_handler handler); + +/* Clear the SSID Hotlist */ +wifi_error wifi_reset_ssid_hotlist(wifi_request_id id, wifi_interface_handle iface); + + +/* BSSID blacklist */ +typedef struct { + int num_bssid; // number of blacklisted BSSIDs + mac_addr bssids[MAX_BLACKLIST_BSSID]; // blacklisted BSSIDs +} wifi_bssid_params; + +/* Set the BSSID blacklist */ +wifi_error wifi_set_bssid_blacklist(wifi_request_id id, wifi_interface_handle iface, + wifi_bssid_params params); + + +/* Significant wifi change */ +typedef struct { + mac_addr bssid; // BSSID + wifi_channel channel; // channel frequency in MHz + int num_rssi; // number of rssi samples + wifi_rssi rssi[]; // RSSI history in db +} wifi_significant_change_result; + +typedef struct { + void (*on_significant_change)(wifi_request_id id, + unsigned num_results, wifi_significant_change_result **results); +} wifi_significant_change_handler; + +// The sample size parameters in the wifi_significant_change_params structure +// represent the number of occurence of a g-scan where the BSSID was seen and RSSI was +// collected for that BSSID, or, the BSSID was expected to be seen and didn't. +// for instance: lost_ap_sample_size : number of time a g-scan was performed on the +// channel the BSSID was seen last, and the BSSID was not seen during those g-scans +typedef struct { + int rssi_sample_size; // number of samples for averaging RSSI + int lost_ap_sample_size; // number of samples to confirm AP loss + int min_breaching; // number of APs breaching threshold + int num_bssid; // max 64 + ap_threshold_param ap[MAX_SIGNIFICANT_CHANGE_APS]; +} wifi_significant_change_params; + +/* Set the Signifcant AP change list */ +wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_significant_change_params params, wifi_significant_change_handler handler); + +/* Clear the Signifcant AP change list */ +wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface); + +/* Random MAC OUI for PNO */ +wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, oui scan_oui); + +// Whether directed scan needs to be performed (for hidden SSIDs) +#define WIFI_PNO_FLAG_DIRECTED_SCAN = 1 +// Whether PNO event shall be triggered if the network is found on A band +#define WIFI_PNO_FLAG_A_BAND = 2 +// Whether PNO event shall be triggered if the network is found on G band +#define WIFI_PNO_FLAG_G_BAND = 4 +// Whether strict matching is required (i.e. firmware shall not match on the entire SSID) +#define WIFI_PNO_FLAG_STRICT_MATCH = 8 + +// Code for matching the beacon AUTH IE - additional codes TBD +#define WIFI_PNO_AUTH_CODE_OPEN 1 // open +#define WIFI_PNO_AUTH_CODE_PSK 2 // WPA_PSK or WPA2PSK +#define WIFI_PNO_AUTH_CODE_EAPOL 4 // any EAPOL + +// Enhanced PNO: +// Enhanced PNO feature is expected to be enabled all of the time (e.g. screen lit) and may thus +// requires firmware to store a large number of networks, covering the whole list of known network. +// Therefore, it is acceptable for firmware to store a crc24, crc32 or other short hash of the SSID, +// such that a low but non-zero probability of collision exist. With that scheme it should be +// possible for firmware to keep an entry as small as 4 bytes for each pno network. +// For instance, a firmware pn0 entry can be implemented in the form of: +// PNO ENTRY = crc24(3 bytes) | RSSI_THRESHOLD>>3 (5 bits) | auth flags(3 bits) +// +// A PNO network shall be reported once, that is, once a network is reported by firmware +// its entry shall be marked as "done" until framework calls wifi_set_epno_list again. +// Calling wifi_set_epno_list shall reset the "done" status of pno networks in firmware. +typedef struct { + char ssid[32+1]; + byte rssi_threshold; // threshold for considering this SSID as found, required granularity for + // this threshold is 4dBm to 8dBm + byte flags; // WIFI_PNO_FLAG_XXX + byte auth_bit_field; // auth bit field for matching WPA IE +} wifi_epno_network; + +/* PNO list */ +typedef struct { + int num_networks; // number of SSIDs + wifi_epno_network networks[]; // PNO networks +} wifi_epno_params; + +typedef struct { + // on results + void (*on_network_found)(wifi_request_id id, + unsigned num_results, wifi_scan_result *results); +} wifi_epno_handler; + + +/* Set the PNO list */ +wifi_error wifi_set_epno_list(wifi_request_id id, wifi_interface_handle iface, + int num_networks, wifi_epno_network *networks, wifi_epno_handler handler); + + +/* SSID white list */ +/* Note that this feature requires firmware to be able to indicate to kernel sme and wpa_supplicant + * that the SSID of the network has changed + * and thus requires further changed in cfg80211 stack, for instance, + * the below function would change: + + void __cfg80211_roamed(struct wireless_dev *wdev, + struct cfg80211_bss *bss, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len) + * when firmware roam to a new SSID the corresponding link layer stats info need to be updated: + struct wifi_interface_link_layer_info; + */ +typedef struct { + char ssid[32+1]; // null terminated +} wifi_ssid; + +wifi_error wifi_set_ssid_white_list(wifi_request_id id, wifi_interface_handle iface, + int num_networks, wifi_ssid *ssids); + +/* Set G-SCAN roam parameters */ +/** + * Firmware roaming is implemented with two modes: + * 1- "Alert" mode roaming, (Note: alert roaming is the pre-L roaming, whereas firmware is + * "urgently" hunting for another BSSID because the RSSI is low, or because many successive + * beacons have been lost or other bad link conditions). + * 2- "Lazy" mode, where firmware is hunting for a better BSSID or white listed SSID even though + * the RSSI of the link is good. + * Lazy mode is configured thru G-scan, that is, the results of G-scans are compared to the + * current RSSI and fed thru the roaming engine. + * Lazy scan will be enabled (and or throttled down by reducing the number of G-scans) by + * framework only in certain conditions, such as: + * - no real time (VO/VI) traffic at the interface + * - low packet rate for BE/BK packets a the interface + * - system conditions (screen lit/dark) etc... + * + * For consistency, the roam parameters will always be configured by framework such that: + * + * condition 1- A_band_boost_threshold >= (alert_roam_rssi_trigger + 10) + * This condition ensures that Lazy roam doesn't cause the device to roam to a 5GHz BSSID whose RSSI + * is lower than the alert threshold, which would consequently trigger a roam to a low RSSI BSSID, + * hence triggering alert mode roaming. + * In other words, in alert mode, the A_band parameters may safely be ignored by WiFi chipset. + * + * condition 2- A_band_boost_threshold > A_band_penalty_factor + * + */ + +/** + * Example: + * A_band_boost_threshold = -65 + * A_band_penalty_threshold = -75 + * A_band_boost_factor = 4 + * A_band_penalty_factor = 2 + * A_band_max_boost = 50 + * + * a 5GHz RSSI value is transformed as below: + * -20 -> -20+ 50 = 30 + * -60 -> -60 + 4 * (-60 - A_band_boost_threshold) = -60 + 16 = -44 + * -70 -> -70 + * -80 -> -80 - 2 * (A_band_penalty_threshold - (-80)) = -80 - 10 = -90 + */ + +typedef struct { + // Lazy roam parameters + // A_band_XX parameters are applied to 5GHz BSSIDs when comparing with a 2.4GHz BSSID + // they may not be applied when comparing two 5GHz BSSIDs + int A_band_boost_threshold; // RSSI threshold above which 5GHz RSSI is favored + int A_band_penalty_threshold; // RSSI threshold below which 5GHz RSSI is penalized + int A_band_boost_factor; // factor by which 5GHz RSSI is boosted + // boost=RSSI_measured-5GHz_boost_threshold)*5GHz_boost_factor + int A_band_penalty_factor; // factor by which 5GHz RSSI is penalized + // penalty=(5GHz_penalty_factor-RSSI_measured)*5GHz_penalty_factor + int A_band_max_boost; // maximum boost that can be applied to a 5GHz RSSI + + // Hysteresis: ensuring the currently associated BSSID is favored + // so as to prevent ping-pong situations + int lazy_roam_hysteresis; // boost applied to current BSSID + + // Alert mode enable, i.e. configuring when firmware enters alert mode + int alert_roam_rssi_trigger; // RSSI below which "Alert" roam is enabled +} wifi_roam_params; + +wifi_error wifi_set_gscan_roam_params(wifi_request_id id, wifi_interface_handle iface, + wifi_roam_params * params); + +/** + * Enable/Disable "Lazy" roam + */ +wifi_error wifi_enable_lazy_roam(wifi_request_id id, wifi_interface_handle iface, int enable); + +/** + * Per BSSID preference + */ +typedef struct { + mac_addr bssid; + int rssi_modifier; // modifier applied to the RSSI of the BSSID for the purpose of comparing + // it with other roam candidate +} wifi_bssid_preference; + +wifi_error wifi_set_bssid_preference(wifi_request_id id, wifi_interface_handle iface, + int num_bssid, wifi_bssid_preference *prefs); + +typedef struct { + int id; // identifier of this network block, report this in event + char realm[256]; // null terminated UTF8 encoded realm, 0 if unspecified + int64_t roamingConsortiumIds[16]; // roaming consortium ids to match, 0s if unspecified + byte plmn[3]; // mcc/mnc combination as per rules, 0s if unspecified +} wifi_passpoint_network; + +typedef struct { + void (*on_passpoint_network_found)( + wifi_request_id id, + int net_id, // network block identifier for the matched network + wifi_scan_result *result, // scan result, with channel and beacon information + int anqp_len, // length of ANQP blob + byte *anqp // ANQP data, in the information_element format + ); +} wifi_passpoint_event_handler; + +/* Sets a list for passpoint networks for PNO purposes; it should be matched + * against any passpoint networks (designated by Interworking element) found + * during regular PNO scan. */ +wifi_error wifi_set_passpoint_list(wifi_request_id id, wifi_interface_handle iface, int num, + wifi_passpoint_network *networks, wifi_passpoint_event_handler handler); + +/* Reset passpoint network list - no Passpoint networks should be matched after this */ +wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface); + +#endif +
diff --git a/libhardware_legacy/include/hardware_legacy/link_layer_stats.h b/libhardware_legacy/include/hardware_legacy/link_layer_stats.h new file mode 100644 index 0000000..c6202ad --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/link_layer_stats.h
@@ -0,0 +1,253 @@ +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_STATS_H +#define __WIFI_HAL_STATS_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define STATS_MAJOR_VERSION 1 +#define STATS_MINOR_VERSION 0 +#define STATS_MICRO_VERSION 0 + +typedef enum { + WIFI_DISCONNECTED = 0, + WIFI_AUTHENTICATING = 1, + WIFI_ASSOCIATING = 2, + WIFI_ASSOCIATED = 3, + WIFI_EAPOL_STARTED = 4, // if done by firmware/driver + WIFI_EAPOL_COMPLETED = 5, // if done by firmware/driver +} wifi_connection_state; + +typedef enum { + WIFI_ROAMING_IDLE = 0, + WIFI_ROAMING_ACTIVE = 1, +} wifi_roam_state; + +typedef enum { + WIFI_INTERFACE_STA = 0, + WIFI_INTERFACE_SOFTAP = 1, + WIFI_INTERFACE_IBSS = 2, + WIFI_INTERFACE_P2P_CLIENT = 3, + WIFI_INTERFACE_P2P_GO = 4, + WIFI_INTERFACE_NAN = 5, + WIFI_INTERFACE_MESH = 6, + WIFI_INTERFACE_UNKNOWN = -1 + } wifi_interface_mode; + +#define WIFI_CAPABILITY_QOS 0x00000001 // set for QOS association +#define WIFI_CAPABILITY_PROTECTED 0x00000002 // set for protected association (802.11 beacon frame control protected bit set) +#define WIFI_CAPABILITY_INTERWORKING 0x00000004 // set if 802.11 Extended Capabilities element interworking bit is set +#define WIFI_CAPABILITY_HS20 0x00000008 // set for HS20 association +#define WIFI_CAPABILITY_SSID_UTF8 0x00000010 // set is 802.11 Extended Capabilities element UTF-8 SSID bit is set +#define WIFI_CAPABILITY_COUNTRY 0x00000020 // set is 802.11 Country Element is present + +typedef struct { + wifi_interface_mode mode; // interface mode + u8 mac_addr[6]; // interface mac address (self) + wifi_connection_state state; // connection state (valid for STA, CLI only) + wifi_roam_state roaming; // roaming state + u32 capabilities; // WIFI_CAPABILITY_XXX (self) + u8 ssid[33]; // null terminated SSID + u8 bssid[6]; // bssid + u8 ap_country_str[3]; // country string advertised by AP + u8 country_str[3]; // country string for this association +} wifi_interface_link_layer_info; + +/* channel information */ +typedef struct { + wifi_channel_width width; // channel width (20, 40, 80, 80+80, 160) + wifi_channel center_freq; // primary 20 MHz channel + wifi_channel center_freq0; // center frequency (MHz) first segment + wifi_channel center_freq1; // center frequency (MHz) second segment +} wifi_channel_info; + +/* wifi rate */ +typedef struct { + u32 preamble :3; // 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved + u32 nss :2; // 0:1x1, 1:2x2, 3:3x3, 4:4x4 + u32 bw :3; // 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz + u32 rateMcsIdx :8; // OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps + // HT/VHT it would be mcs index + u32 reserved :16; // reserved + u32 bitrate; // units of 100 Kbps +} wifi_rate; + +/* channel statistics */ +typedef struct { + wifi_channel_info channel; // channel + u32 on_time; // msecs the radio is awake (32 bits number accruing over time) + u32 cca_busy_time; // msecs the CCA register is busy (32 bits number accruing over time) +} wifi_channel_stat; + +/* radio statistics */ +typedef struct { + wifi_radio radio; // wifi radio (if multiple radio supported) + u32 on_time; // msecs the radio is awake (32 bits number accruing over time) + u32 tx_time; // msecs the radio is transmitting (32 bits number accruing over time) + u32 rx_time; // msecs the radio is in active receive (32 bits number accruing over time) + u32 on_time_scan; // msecs the radio is awake due to all scan (32 bits number accruing over time) + u32 on_time_nbd; // msecs the radio is awake due to NAN (32 bits number accruing over time) + u32 on_time_gscan; // msecs the radio is awake due to G?scan (32 bits number accruing over time) + u32 on_time_roam_scan; // msecs the radio is awake due to roam?scan (32 bits number accruing over time) + u32 on_time_pno_scan; // msecs the radio is awake due to PNO scan (32 bits number accruing over time) + u32 on_time_hs20; // msecs the radio is awake due to HS2.0 scans and GAS exchange (32 bits number accruing over time) + u32 num_channels; // number of channels + wifi_channel_stat channels[]; // channel statistics +} wifi_radio_stat; + +/** + * Packet statistics reporting by firmware is performed on MPDU basi (i.e. counters increase by 1 for each MPDU) + * As well, "data packet" in associated comments, shall be interpreted as 802.11 data packet, + * that is, 802.11 frame control subtype == 2 and excluding management and control frames. + * + * As an example, in the case of transmission of an MSDU fragmented in 16 MPDUs which are transmitted + * OTA in a 16 units long a-mpdu, for which a block ack is received with 5 bits set: + * tx_mpdu : shall increase by 5 + * retries : shall increase by 16 + * tx_ampdu : shall increase by 1 + * data packet counters shall not increase regardless of the number of BAR potentially sent by device for this a-mpdu + * data packet counters shall not increase regardless of the number of BA received by device for this a-mpdu + * + * For each subsequent retransmission of the 11 remaining non ACK'ed mpdus + * (regardless of the fact that they are transmitted in a-mpdu or not) + * retries : shall increase by 1 + * + * If no subsequent BA or ACK are received from AP, until packet lifetime expires for those 11 packet that were not ACK'ed + * mpdu_lost : shall increase by 11 + */ + +/* per rate statistics */ +typedef struct { + wifi_rate rate; // rate information + u32 tx_mpdu; // number of successfully transmitted data pkts (ACK rcvd) + u32 rx_mpdu; // number of received data pkts + u32 mpdu_lost; // number of data packet losses (no ACK) + u32 retries; // total number of data pkt retries + u32 retries_short; // number of short data pkt retries + u32 retries_long; // number of long data pkt retries +} wifi_rate_stat; + +/* access categories */ +typedef enum { + WIFI_AC_VO = 0, + WIFI_AC_VI = 1, + WIFI_AC_BE = 2, + WIFI_AC_BK = 3, + WIFI_AC_MAX = 4, +} wifi_traffic_ac; + +/* wifi peer type */ +typedef enum +{ + WIFI_PEER_STA, + WIFI_PEER_AP, + WIFI_PEER_P2P_GO, + WIFI_PEER_P2P_CLIENT, + WIFI_PEER_NAN, + WIFI_PEER_TDLS, + WIFI_PEER_INVALID, +} wifi_peer_type; + +/* per peer statistics */ +typedef struct { + wifi_peer_type type; // peer type (AP, TDLS, GO etc.) + u8 peer_mac_address[6]; // mac address + u32 capabilities; // peer WIFI_CAPABILITY_XXX + u32 num_rate; // number of rates + wifi_rate_stat rate_stats[]; // per rate statistics, number of entries = num_rate +} wifi_peer_info; + +/* Per access category statistics */ +typedef struct { + wifi_traffic_ac ac; // access category (VI, VO, BE, BK) + u32 tx_mpdu; // number of successfully transmitted unicast data pkts (ACK rcvd) + u32 rx_mpdu; // number of received unicast data packets + u32 tx_mcast; // number of succesfully transmitted multicast data packets + // STA case: implies ACK received from AP for the unicast packet in which mcast pkt was sent + u32 rx_mcast; // number of received multicast data packets + u32 rx_ampdu; // number of received unicast a-mpdus; support of this counter is optional + u32 tx_ampdu; // number of transmitted unicast a-mpdus; support of this counter is optional + u32 mpdu_lost; // number of data pkt losses (no ACK) + u32 retries; // total number of data pkt retries + u32 retries_short; // number of short data pkt retries + u32 retries_long; // number of long data pkt retries + u32 contention_time_min; // data pkt min contention time (usecs) + u32 contention_time_max; // data pkt max contention time (usecs) + u32 contention_time_avg; // data pkt avg contention time (usecs) + u32 contention_num_samples; // num of data pkts used for contention statistics +} wifi_wmm_ac_stat; + +/* interface statistics */ +typedef struct { + wifi_interface_handle iface; // wifi interface + wifi_interface_link_layer_info info; // current state of the interface + u32 beacon_rx; // access point beacon received count from connected AP + u64 average_tsf_offset; // average beacon offset encountered (beacon_TSF - TBTT) + // The average_tsf_offset field is used so as to calculate the + // typical beacon contention time on the channel as well may be + // used to debug beacon synchronization and related power consumption issue + u32 leaky_ap_detected; // indicate that this AP typically leaks packets beyond the driver guard time. + u32 leaky_ap_avg_num_frames_leaked; // average number of frame leaked by AP after frame with PM bit set was ACK'ed by AP + u32 leaky_ap_guard_time; // guard time currently in force (when implementing IEEE power management based on + // frame control PM bit), How long driver waits before shutting down the radio and + // after receiving an ACK for a data frame with PM bit set) + u32 mgmt_rx; // access point mgmt frames received count from connected AP (including Beacon) + u32 mgmt_action_rx; // action frames received count + u32 mgmt_action_tx; // action frames transmit count + wifi_rssi rssi_mgmt; // access Point Beacon and Management frames RSSI (averaged) + wifi_rssi rssi_data; // access Point Data Frames RSSI (averaged) from connected AP + wifi_rssi rssi_ack; // access Point ACK RSSI (averaged) from connected AP + wifi_wmm_ac_stat ac[WIFI_AC_MAX]; // per ac data packet statistics + u32 num_peers; // number of peers + wifi_peer_info peer_info[]; // per peer statistics +} wifi_iface_stat; + +/* configuration params */ +typedef struct { + u32 mpdu_size_threshold; // threshold to classify the pkts as short or long + // packet size < mpdu_size_threshold => short + u32 aggressive_statistics_gathering; // set for field debug mode. Driver should collect all statistics regardless of performance impact. +} wifi_link_layer_params; + +/* API to trigger the link layer statistics collection. + Unless his API is invoked - link layer statistics will not be collected. + Radio statistics (once started) do not stop or get reset unless wifi_clear_link_stats is invoked + Interface statistics (once started) reset and start afresh after each connection */ +wifi_error wifi_set_link_stats(wifi_interface_handle iface, wifi_link_layer_params params); + +/* callback for reporting link layer stats */ +typedef struct { + void (*on_link_stats_results) (wifi_request_id id, wifi_iface_stat *iface_stat, + int num_radios, wifi_radio_stat *radio_stat); +} wifi_stats_result_handler; + +/* api to collect the link layer statistics for a given iface and all the radio stats */ +wifi_error wifi_get_link_stats(wifi_request_id id, + wifi_interface_handle iface, wifi_stats_result_handler handler); + +/* wifi statistics bitmap */ +#define WIFI_STATS_RADIO 0x00000001 // all radio statistics +#define WIFI_STATS_RADIO_CCA 0x00000002 // cca_busy_time (within radio statistics) +#define WIFI_STATS_RADIO_CHANNELS 0x00000004 // all channel statistics (within radio statistics) +#define WIFI_STATS_RADIO_SCAN 0x00000008 // all scan statistics (within radio statistics) +#define WIFI_STATS_IFACE 0x00000010 // all interface statistics +#define WIFI_STATS_IFACE_TXRATE 0x00000020 // all tx rate statistics (within interface statistics) +#define WIFI_STATS_IFACE_AC 0x00000040 // all ac statistics (within interface statistics) +#define WIFI_STATS_IFACE_CONTENTION 0x00000080 // all contention (min, max, avg) statistics (within ac statisctics) + +/* clear api to reset statistics, stats_clear_rsp_mask identifies what stats have been cleared + stop_req = 1 will imply whether to stop the statistics collection. + stop_rsp = 1 will imply that stop_req was honored and statistics collection was stopped. + */ +wifi_error wifi_clear_link_stats(wifi_interface_handle iface, + u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*__WIFI_HAL_STATS_ */ +
diff --git a/libhardware_legacy/include/hardware_legacy/power.h b/libhardware_legacy/include/hardware_legacy/power.h new file mode 100644 index 0000000..604e0ed --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/power.h
@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _HARDWARE_POWER_H +#define _HARDWARE_POWER_H + +#include <stdint.h> + +#if __cplusplus +extern "C" { +#endif + +enum { + PARTIAL_WAKE_LOCK = 1, // the cpu stays on, but the screen is off + FULL_WAKE_LOCK = 2 // the screen is also on +}; + +// while you have a lock held, the device will stay on at least at the +// level you request. +int acquire_wake_lock(int lock, const char* id); +int release_wake_lock(const char* id); + + +#if __cplusplus +} // extern "C" +#endif + +#endif // _HARDWARE_POWER_H
diff --git a/libhardware_legacy/include/hardware_legacy/rtt.h b/libhardware_legacy/include/hardware_legacy/rtt.h new file mode 100644 index 0000000..2808723 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/rtt.h
@@ -0,0 +1,274 @@ + +#include "wifi_hal.h" +#include "gscan.h" + +#ifndef __WIFI_HAL_RTT_H__ +#define __WIFI_HAL_RTT_H__ + +/* Ranging status */ +typedef enum { + RTT_STATUS_SUCCESS = 0, + RTT_STATUS_FAILURE = 1, // general failure status + RTT_STATUS_FAIL_NO_RSP = 2, // target STA does not respond to request + RTT_STATUS_FAIL_REJECTED = 3, // request rejected. Applies to 2-sided RTT only + RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4, + RTT_STATUS_FAIL_TM_TIMEOUT = 5, // timing measurement times out + RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6, // Target on different channel, cannot range + RTT_STATUS_FAIL_NO_CAPABILITY = 7, // ranging not supported + RTT_STATUS_ABORTED = 8, // request aborted for unknown reason + RTT_STATUS_FAIL_INVALID_TS = 9, // Invalid T1-T4 timestamp + RTT_STATUS_FAIL_PROTOCOL = 10, // 11mc protocol failed + RTT_STATUS_FAIL_SCHEDULE = 11, // request could not be scheduled + RTT_STATUS_FAIL_BUSY_TRY_LATER = 12, // responder cannot collaborate at time of request + RTT_STATUS_INVALID_REQ = 13, // bad request args + RTT_STATUS_NO_WIFI = 14, // WiFi not enabled + RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE = 15 // Responder overrides param info, cannot range with new params +} wifi_rtt_status; + +/* RTT peer type */ +typedef enum { + RTT_PEER_AP = 0x1, + RTT_PEER_STA = 0x2, + RTT_PEER_P2P_GO = 0x3, + RTT_PEER_P2P_CLIENT = 0x4, + RTT_PEER_NAN = 0x5 +} rtt_peer_type; + +/* RTT Measurement Bandwidth */ +typedef enum { + WIFI_RTT_BW_5 = 0x01, + WIFI_RTT_BW_10 = 0x02, + WIFI_RTT_BW_20 = 0x04, + WIFI_RTT_BW_40 = 0x08, + WIFI_RTT_BW_80 = 0x10, + WIFI_RTT_BW_160 = 0x20 +} wifi_rtt_bw; + +/* RTT Measurement Preamble */ +typedef enum { + WIFI_RTT_PREAMBLE_LEGACY = 0x1, + WIFI_RTT_PREAMBLE_HT = 0x2, + WIFI_RTT_PREAMBLE_VHT = 0x4 +} wifi_rtt_preamble; + +/* RTT Type */ +typedef enum { + RTT_TYPE_1_SIDED = 0x1, + RTT_TYPE_2_SIDED = 0x2, +} wifi_rtt_type; + +/* RTT configuration */ +typedef struct { + mac_addr addr; // peer device mac address + wifi_rtt_type type; // 1-sided or 2-sided RTT + rtt_peer_type peer; // optional - peer device hint (STA, P2P, AP) + wifi_channel_info channel; // Required for STA-AP mode, optional for P2P, NBD etc. + unsigned burst_period; // Time interval between bursts (units: 100 ms). + // Applies to 1-sided and 2-sided RTT multi-burst requests. + // Range: 0-31, 0: no preference by initiator (2-sided RTT) + unsigned num_burst; // Total number of RTT bursts to be executed. It will be + // specified in the same way as the parameter "Number of + // Burst Exponent" found in the FTM frame format. It + // applies to both: 1-sided RTT and 2-sided RTT. Valid + // values are 0 to 15 as defined in 802.11mc std. + // 0 means single shot + // The implication of this parameter on the maximum + // number of RTT results is the following: + // for 1-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst) + // for 2-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst - 1) + unsigned num_frames_per_burst; // num of frames per burst. + // Minimum value = 1, Maximum value = 31 + // For 2-sided this equals the number of FTM frames + // to be attempted in a single burst. This also + // equals the number of FTM frames that the + // initiator will request that the responder send + // in a single frame. + unsigned num_retries_per_rtt_frame; // number of retries for a failed RTT frame. Applies + // to 1-sided RTT only. Minimum value = 0, Maximum value = 3 + + //following fields are only valid for 2-side RTT + unsigned num_retries_per_ftmr; // Maximum number of retries that the initiator can + // retry an FTMR frame. + // Minimum value = 0, Maximum value = 3 + byte LCI_request; // 1: request LCI, 0: do not request LCI + byte LCR_request; // 1: request LCR, 0: do not request LCR + unsigned burst_duration; // Applies to 1-sided and 2-sided RTT. Valid values will + // be 2-11 and 15 as specified by the 802.11mc std for + // the FTM parameter burst duration. In a multi-burst + // request, if responder overrides with larger value, + // the initiator will return failure. In a single-burst + // request if responder overrides with larger value, + // the initiator will sent TMR_STOP to terminate RTT + // at the end of the burst_duration it requested. + wifi_rtt_preamble preamble; // RTT preamble to be used in the RTT frames + wifi_rtt_bw bw; // RTT BW to be used in the RTT frames +} wifi_rtt_config; + +/* RTT results */ +typedef struct { + mac_addr addr; // device mac address + unsigned burst_num; // burst number in a multi-burst request + unsigned measurement_number; // Total RTT measurement frames attempted + unsigned success_number; // Total successful RTT measurement frames + byte number_per_burst_peer; // Maximum number of "FTM frames per burst" supported by + // the responder STA. Applies to 2-sided RTT only. + // If reponder overrides with larger value: + // - for single-burst request initiator will truncate the + // larger value and send a TMR_STOP after receiving as + // many frames as originally requested. + // - for multi-burst request, initiator will return + // failure right away. + wifi_rtt_status status; // ranging status + byte retry_after_duration; // When status == RTT_STATUS_FAIL_BUSY_TRY_LATER, + // this will be the time provided by the responder as to + // when the request can be tried again. Applies to 2-sided + // RTT only. In sec, 1-31sec. + wifi_rtt_type type; // RTT type + wifi_rssi rssi; // average rssi in 0.5 dB steps e.g. 143 implies -71.5 dB + wifi_rssi rssi_spread; // rssi spread in 0.5 dB steps e.g. 5 implies 2.5 dB spread (optional) + wifi_rate tx_rate; // 1-sided RTT: TX rate of RTT frame. + // 2-sided RTT: TX rate of initiator's Ack in response to FTM frame. + wifi_rate rx_rate; // 1-sided RTT: TX rate of Ack from other side. + // 2-sided RTT: TX rate of FTM frame coming from responder. + wifi_timespan rtt; // round trip time in 0.1 nanoseconds + wifi_timespan rtt_sd; // rtt standard deviation in 0.1 nanoseconds + wifi_timespan rtt_spread; // difference between max and min rtt times recorded + int distance; // distance in cm (optional) + int distance_sd; // standard deviation in cm (optional) + int distance_spread; // difference between max and min distance recorded (optional) + wifi_timestamp ts; // time of the measurement (in microseconds since boot) + int burst_duration; // in ms, actual time taken by the FW to finish one burst + // measurement. Applies to 1-sided and 2-sided RTT. + int negotiated_burst_num; // Number of bursts allowed by the responder. Applies + // to 2-sided RTT only. + wifi_information_element *LCI; // for 11mc only + wifi_information_element *LCR; // for 11mc only +} wifi_rtt_result; + +/* RTT result callback */ +typedef struct { + void (*on_rtt_results) (wifi_request_id id, unsigned num_results, wifi_rtt_result *rtt_result[]); +} wifi_rtt_event_handler; + +/* API to request RTT measurement */ +wifi_error wifi_rtt_range_request(wifi_request_id id, wifi_interface_handle iface, + unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler); + +/* API to cancel RTT measurements */ +wifi_error wifi_rtt_range_cancel(wifi_request_id id, wifi_interface_handle iface, + unsigned num_devices, mac_addr addr[]); + +/* NBD ranging channel map */ +typedef struct { + wifi_channel availablity[32]; // specifies the channel map for each of the 16 TU windows + // frequency of 0 => unspecified; which means firmware is + // free to do whatever it wants in this window. +} wifi_channel_map; + +/* API to start publishing the channel map on responder device in a NBD cluster. + Responder device will take this request and schedule broadcasting the channel map + in a NBD ranging attribute in a SDF. DE will automatically remove the ranging + attribute from the OTA queue after number of DW specified by num_dw + where Each DW is 512 TUs apart */ +wifi_error wifi_rtt_channel_map_set(wifi_request_id id, + wifi_interface_handle iface, wifi_channel_map *params, unsigned num_dw); + +/* API to clear the channel map on the responder device in a NBD cluster. + Responder device will cancel future ranging channel request, starting from “next” + DW interval and will also stop broadcasting NBD ranging attribute in SDF */ +wifi_error wifi_rtt_channel_map_clear(wifi_request_id id, wifi_interface_handle iface); + +// Preamble definition for bit mask used in wifi_rtt_capabilities +#define PREAMBLE_LEGACY 0x1 +#define PREAMBLE_HT 0x2 +#define PREAMBLE_VHT 0x4 + +// BW definition for bit mask used in wifi_rtt_capabilities +#define BW_5_SUPPORT 0x1 +#define BW_10_SUPPORT 0x2 +#define BW_20_SUPPORT 0x4 +#define BW_40_SUPPORT 0x8 +#define BW_80_SUPPORT 0x10 +#define BW_160_SUPPORT 0x20 + +/* RTT Capabilities */ +typedef struct { + byte rtt_one_sided_supported; // if 1-sided rtt data collection is supported + byte rtt_ftm_supported; // if ftm rtt data collection is supported + byte lci_support; // if initiator supports LCI request. Applies to 2-sided RTT + byte lcr_support; // if initiator supports LCR request. Applies to 2-sided RTT + byte preamble_support; // bit mask indicates what preamble is supported by initiator + byte bw_support; // bit mask indicates what BW is supported by initiator +} wifi_rtt_capabilities; + +/* RTT capabilities of the device */ +wifi_error wifi_get_rtt_capabilities(wifi_interface_handle iface, wifi_rtt_capabilities *capabilities); + +/* debugging definitions */ +enum { + RTT_DEBUG_DISABLE, + RTT_DEBUG_LOG, + RTT_DEBUG_PROTO, + RTT_DEBUG_BURST, + RTT_DEBUG_ACCURACY, + RTT_DEBUG_LOGDETAIL +}; //rtt debug type + +enum { + RTT_DEBUG_FORMAT_TXT, + RTT_DEBUG_FORMAT_BINARY +}; //rtt debug format + +typedef struct rtt_debug { + unsigned version; + unsigned len; // total length of after len field + unsigned type; // rtt debug type + unsigned format; //rtt debug format + char dbuf[0]; // debug content +} rtt_debug_t; + +/* set configuration for debug */ +wifi_error wifi_rtt_debug_cfg(wifi_interface_handle h, unsigned rtt_dbg_type, char *cfgbuf, unsigned cfg_buf_size); +/* get the debug information */ +wifi_error wifi_rtt_debug_get(wifi_interface_handle h, rtt_debug_t **debugbuf); +/* free the debug buffer */ +wifi_error wifi_rtt_debug_free(wifi_interface_handle h, rtt_debug_t *debugbuf); + +/* API for setting LCI/LCR information to be provided to a requestor */ +typedef enum { + WIFI_MOTION_NOT_EXPECTED = 0, // Not expected to change location + WIFI_MOTION_EXPECTED = 1, // Expected to change location + WIFI_MOTION_UNKNOWN = 2, // Movement pattern unknown +} wifi_motion_pattern; + +typedef struct { + long latitude; // latitude in degrees * 2^25 , 2's complement + long longitude; // latitude in degrees * 2^25 , 2's complement + int altitude; // Altitude in units of 1/256 m + byte latitude_unc; // As defined in Section 2.3.2 of IETF RFC 6225 + byte longitude_unc; // As defined in Section 2.3.2 of IETF RFC 6225 + byte altitude_unc; // As defined in Section 2.4.5 from IETF RFC 6225: + + //Following element for configuring the Z subelement + wifi_motion_pattern motion_pattern; + int floor; // floor in units of 1/16th of floor. 0x80000000 if unknown. + int height_above_floor; // in units of 1/64 m + int height_unc; // in units of 1/64 m. 0 if unknown +} wifi_lci_information; + +typedef struct { + char country_code[2]; // country code + int length; // length of the info field + char civic_info[256]; // Civic info to be copied in FTM frame +} wifi_lcr_information; + +// API to configure the LCI. Used in RTT Responder mode only +wifi_error wifi_set_lci(wifi_request_id id, wifi_interface_handle iface, + wifi_lci_information *lci); + +// API to configure the LCR. Used in RTT Responder mode only. +wifi_error wifi_set_lcr(wifi_request_id id, wifi_interface_handle iface, + wifi_lcr_information *lcr); + +#endif +
diff --git a/libhardware_legacy/include/hardware_legacy/tdls.h b/libhardware_legacy/include/hardware_legacy/tdls.h new file mode 100644 index 0000000..9ac225a --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/tdls.h
@@ -0,0 +1,85 @@ + +#include "wifi_hal.h" + +#ifndef _TDLS_H_ +#define _TDLS_H_ + +typedef enum { + WIFI_TDLS_DISABLED = 1, /* TDLS is not enabled, default status for all STAs */ + WIFI_TDLS_ENABLED, /* TDLS is enabled, but not yet tried */ + WIFI_TDLS_ESTABLISHED, /* Direct link is established */ + WIFI_TDLS_ESTABLISHED_OFF_CHANNEL, /* Direct link is established using MCC */ + WIFI_TDLS_DROPPED, /* Direct link was established, + * but is temporarily dropped now */ + WIFI_TDLS_FAILED /* TDLS permanent failed. Inform error to upper layer + * and go back to WIFI_TDLS_DISABLED */ +} wifi_tdls_state; + +typedef enum { + WIFI_TDLS_SUCCESS, /* Success */ + WIFI_TDLS_UNSPECIFIED = -1, /* Unspecified reason */ + WIFI_TDLS_NOT_SUPPORTED = -2, /* Remote side doesn't support TDLS */ + WIFI_TDLS_UNSUPPORTED_BAND = -3, /* Remote side doesn't support this band */ + WIFI_TDLS_NOT_BENEFICIAL = -4, /* Going to AP is better than going direct */ + WIFI_TDLS_DROPPED_BY_REMOTE = -5 /* Remote side doesn't want it anymore */ +} wifi_tdls_reason; + +typedef struct { + int channel; /* channel hint, in channel number (NOT frequency ) */ + int global_operating_class; /* operating class to use */ + int max_latency_ms; /* max latency that can be tolerated by apps */ + int min_bandwidth_kbps; /* bandwidth required by apps, in kilo bits per second */ +} wifi_tdls_params; + +typedef struct { + int channel; + int global_operating_class; + wifi_tdls_state state; + wifi_tdls_reason reason; +} wifi_tdls_status; + +typedef struct { + int max_concurrent_tdls_session_num; /* Maximum TDLS session number can be supported by the + * Firmware and hardware*/ + int is_global_tdls_supported; /* 1 -- support, 0 -- not support */ + int is_per_mac_tdls_supported; /* 1 -- support, 0 -- not support */ + int is_off_channel_tdls_supported; /* 1 -- support, 0 -- not support */ +} wifi_tdls_capabilities; + +typedef struct { + /* on_tdls_state_changed - reports state of TDLS link to framework + * Report this event when the state of TDLS link changes */ + void (*on_tdls_state_changed)(mac_addr addr, wifi_tdls_status status); +} wifi_tdls_handler; + + +/* wifi_enable_tdls - enables TDLS-auto mode for a specific route + * + * params specifies hints, which provide more information about + * why TDLS is being sought. The firmware should do its best to + * honor the hints before downgrading regular AP link + * If upper layer has no specific values, this should be NULL + * + * handler is used to inform the upper layer about the status change and the corresponding reason + */ +wifi_error wifi_enable_tdls(wifi_interface_handle iface, mac_addr addr, + wifi_tdls_params *params, wifi_tdls_handler handler); + +/* wifi_disable_tdls - disables TDLS-auto mode for a specific route + * + * This terminates any existing TDLS with addr device, and frees the + * device resources to make TDLS connections on new routes. + * + * DON'T fire any more events on 'handler' specified in earlier call to + * wifi_enable_tdls after this action. + */ +wifi_error wifi_disable_tdls(wifi_interface_handle iface, mac_addr addr); + +/* wifi_get_tdls_status - allows getting the status of TDLS for a specific route */ +wifi_error wifi_get_tdls_status(wifi_interface_handle iface, mac_addr addr, + wifi_tdls_status *status); + +/* return the current HW + Firmware combination's TDLS capabilities */ +wifi_error wifi_get_tdls_capabilities(wifi_interface_handle iface, + wifi_tdls_capabilities *capabilities); +#endif
diff --git a/libhardware_legacy/include/hardware_legacy/uevent.h b/libhardware_legacy/include/hardware_legacy/uevent.h new file mode 100644 index 0000000..bedfff5 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/uevent.h
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _HARDWARE_UEVENT_H +#define _HARDWARE_UEVENT_H + +#if __cplusplus +extern "C" { +#endif + +int uevent_init(); +int uevent_get_fd(); +int uevent_next_event(char* buffer, int buffer_length); +int uevent_add_native_handler(void (*handler)(void *data, const char *msg, int msg_len), + void *handler_data); +int uevent_remove_native_handler(void (*handler)(void *data, const char *msg, int msg_len)); + +#if __cplusplus +} // extern "C" +#endif + +#endif // _HARDWARE_UEVENT_H
diff --git a/libhardware_legacy/include/hardware_legacy/wifi.h b/libhardware_legacy/include/hardware_legacy/wifi.h new file mode 100644 index 0000000..93f0383 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi.h
@@ -0,0 +1,157 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _WIFI_H +#define _WIFI_H + +#if __cplusplus +extern "C" { +#endif + +/** + * Load the Wi-Fi driver. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_load_driver(); + +/** + * Unload the Wi-Fi driver. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_unload_driver(); + +/** + * Check if the Wi-Fi driver is loaded. + * Check if the Wi-Fi driver is loaded. + + * @return 0 on success, < 0 on failure. + */ +int is_wifi_driver_loaded(); + + +/** + * Start supplicant. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_start_supplicant(int p2pSupported); + +/** + * Stop supplicant. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_stop_supplicant(int p2pSupported); + +/** + * Open a connection to supplicant + * + * @return 0 on success, < 0 on failure. + */ +int wifi_connect_to_supplicant(); + +/** + * Close connection to supplicant + * + * @return 0 on success, < 0 on failure. + */ +void wifi_close_supplicant_connection(); + +/** + * wifi_wait_for_event() performs a blocking call to + * get a Wi-Fi event and returns a string representing + * a Wi-Fi event when it occurs. + * + * @param buf is the buffer that receives the event + * @param len is the maximum length of the buffer + * + * @returns number of bytes in buffer, 0 if no + * event (for instance, no connection), and less than 0 + * if there is an error. + */ +int wifi_wait_for_event(char *buf, size_t len); + +/** + * wifi_command() issues a command to the Wi-Fi driver. + * + * Android extends the standard commands listed at + * /link http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html + * to include support for sending commands to the driver: + * + * See wifi/java/android/net/wifi/WifiNative.java for the details of + * driver commands that are supported + * + * @param command is the string command (preallocated with 32 bytes) + * @param commandlen is command buffer length + * @param reply is a buffer to receive a reply string + * @param reply_len on entry, this is the maximum length of + * the reply buffer. On exit, the number of + * bytes in the reply buffer. + * + * @return 0 if successful, < 0 if an error. + */ +int wifi_command(const char *command, char *reply, size_t *reply_len); + +/** + * do_dhcp_request() issues a dhcp request and returns the acquired + * information. + * + * All IPV4 addresses/mask are in network byte order. + * + * @param ipaddr return the assigned IPV4 address + * @param gateway return the gateway being used + * @param mask return the IPV4 mask + * @param dns1 return the IPV4 address of a DNS server + * @param dns2 return the IPV4 address of a DNS server + * @param server return the IPV4 address of DHCP server + * @param lease return the length of lease in seconds. + * + * @return 0 if successful, < 0 if error. + */ +int do_dhcp_request(int *ipaddr, int *gateway, int *mask, + int *dns1, int *dns2, int *server, int *lease); + +/** + * Return the error string of the last do_dhcp_request(). + */ +const char *get_dhcp_error_string(); + +/** + * Return the path to requested firmware + */ +#define WIFI_GET_FW_PATH_STA 0 +#define WIFI_GET_FW_PATH_AP 1 +#define WIFI_GET_FW_PATH_P2P 2 +const char *wifi_get_fw_path(int fw_type); + +/** + * Change the path to firmware for the wlan driver + */ +int wifi_change_fw_path(const char *fwpath); + +/** + * Check and create if necessary initial entropy file + */ +#define WIFI_ENTROPY_FILE "/data/misc/wifi/entropy.bin" +int ensure_entropy_file_exists(); + +#if __cplusplus +}; // extern "C" +#endif + +#endif // _WIFI_H
diff --git a/libhardware_legacy/include/hardware_legacy/wifi_config.h b/libhardware_legacy/include/hardware_legacy/wifi_config.h new file mode 100644 index 0000000..8ad14f4 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi_config.h
@@ -0,0 +1,47 @@ +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_CONFIG_H +#define __WIFI_HAL_CONFIG_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define CONFIG_MAJOR_VERSION 1 +#define CONFIG_MINOR_VERSION 0 +#define CONFIG_MICRO_VERSION 0 + +typedef int wifi_radio; + +// whether the wifi chipset wakes at every dtim beacon or a multiple of the dtim period +// if extended_dtim is set to 3, the STA shall wake up every 3 DTIM beacons +wifi_error wifi_extended_dtim_config_set(wifi_request_id id, + wifi_interface_handle iface, int extended_dtim); + +//set the country code to driver +wifi_error wifi_set_country_code(wifi_interface_handle iface, const char* country_code); + +//set the wifi_iface stats averaging factor used to calculate +// statistics like average the TSF offset or average number of frame leaked +// For instance, upon beacon reception: +// current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000 +// For instance, when evaluating leaky APs: +// current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000 + +wifi_error wifi_set_beacon_wifi_iface_stats_averaging_factor(wifi_request_id id, wifi_interface_handle iface, + u16 factor); + +// configure guard time, i.e. when implementing IEEE power management based on +// frame control PM bit, how long driver waits before shutting down the radio and +// after receiving an ACK for a data frame with PM bit set +wifi_error wifi_set_guard_time(wifi_request_id id, wifi_interface_handle iface, + u32 guard_time); + +#ifdef __cplusplus +} + +#endif /* __cplusplus */ + +#endif /*__WIFI_HAL_STATS_ */ +
diff --git a/libhardware_legacy/include/hardware_legacy/wifi_hal.h b/libhardware_legacy/include/hardware_legacy/wifi_hal.h new file mode 100644 index 0000000..a0ce8fd --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi_hal.h
@@ -0,0 +1,267 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef __WIFI_HAL_H__ +#define __WIFI_HAL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif +#include <stdint.h> + +/* WiFi Common definitions */ +/* channel operating width */ +typedef enum { + WIFI_CHAN_WIDTH_20 = 0, + WIFI_CHAN_WIDTH_40 = 1, + WIFI_CHAN_WIDTH_80 = 2, + WIFI_CHAN_WIDTH_160 = 3, + WIFI_CHAN_WIDTH_80P80 = 4, + WIFI_CHAN_WIDTH_5 = 5, + WIFI_CHAN_WIDTH_10 = 6, + WIFI_CHAN_WIDTH_INVALID = -1 +} wifi_channel_width; + +typedef int wifi_radio; +typedef int wifi_channel; + +typedef struct { + wifi_channel_width width; + int center_frequency0; + int center_frequency1; + int primary_frequency; +} wifi_channel_spec; + +typedef enum { + WIFI_SUCCESS = 0, + WIFI_ERROR_NONE = 0, + WIFI_ERROR_UNKNOWN = -1, + WIFI_ERROR_UNINITIALIZED = -2, + WIFI_ERROR_NOT_SUPPORTED = -3, + WIFI_ERROR_NOT_AVAILABLE = -4, // Not available right now, but try later + WIFI_ERROR_INVALID_ARGS = -5, + WIFI_ERROR_INVALID_REQUEST_ID = -6, + WIFI_ERROR_TIMED_OUT = -7, + WIFI_ERROR_TOO_MANY_REQUESTS = -8, // Too many instances of this request + WIFI_ERROR_OUT_OF_MEMORY = -9 +} wifi_error; + +typedef unsigned char byte; +typedef unsigned char u8; +typedef signed char s8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +typedef int wifi_request_id; +typedef int wifi_channel; // indicates channel frequency in MHz +typedef int wifi_rssi; +typedef byte mac_addr[6]; +typedef byte oui[3]; +typedef int64_t wifi_timestamp; // In microseconds (us) +typedef int64_t wifi_timespan; // In nanoseconds (ns) + +typedef struct wifi_info *wifi_handle; +typedef struct wifi_interface_info *wifi_interface_handle; + +/* Initialize/Cleanup */ + +wifi_error wifi_initialize(wifi_handle *handle); +typedef void (*wifi_cleaned_up_handler) (wifi_handle handle); +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler); +void wifi_event_loop(wifi_handle handle); + +/* Error handling */ +void wifi_get_error_info(wifi_error err, const char **msg); // return a pointer to a static string + +/* Feature enums */ +#define WIFI_FEATURE_INFRA 0x0001 // Basic infrastructure mode +#define WIFI_FEATURE_INFRA_5G 0x0002 // Support for 5 GHz Band +#define WIFI_FEATURE_HOTSPOT 0x0004 // Support for GAS/ANQP +#define WIFI_FEATURE_P2P 0x0008 // Wifi-Direct +#define WIFI_FEATURE_SOFT_AP 0x0010 // Soft AP +#define WIFI_FEATURE_GSCAN 0x0020 // Google-Scan APIs +#define WIFI_FEATURE_NAN 0x0040 // Neighbor Awareness Networking +#define WIFI_FEATURE_D2D_RTT 0x0080 // Device-to-device RTT +#define WIFI_FEATURE_D2AP_RTT 0x0100 // Device-to-AP RTT +#define WIFI_FEATURE_BATCH_SCAN 0x0200 // Batched Scan (legacy) +#define WIFI_FEATURE_PNO 0x0400 // Preferred network offload +#define WIFI_FEATURE_ADDITIONAL_STA 0x0800 // Support for two STAs +#define WIFI_FEATURE_TDLS 0x1000 // Tunnel directed link setup +#define WIFI_FEATURE_TDLS_OFFCHANNEL 0x2000 // Support for TDLS off channel +#define WIFI_FEATURE_EPR 0x4000 // Enhanced power reporting +#define WIFI_FEATURE_AP_STA 0x8000 // Support for AP STA Concurrency +#define WIFI_FEATURE_LINK_LAYER_STATS 0x10000 // Link layer stats collection +#define WIFI_FEATURE_LOGGER 0x20000 // WiFi Logger +#define WIFI_FEATURE_HAL_EPNO 0x40000 // WiFi PNO enhanced +#define WIFI_FEATURE_RSSI_MONITOR 0x80000 // RSSI Monitor +#define WIFI_FEATURE_MKEEP_ALIVE 0x100000 // WiFi mkeep_alive + +// Add more features here + + +typedef int feature_set; + +#define IS_MASK_SET(mask, flags) (((flags) & (mask)) == (mask)) +#define IS_MASK_RESET(mask, flags) (((flags) & (mask)) == 0) + +#define IS_SUPPORTED_FEATURE(feature, featureSet) IS_MASK_SET(feature, fetureSet) +#define IS_UNSUPPORTED_FEATURE(feature, featureSet) IS_MASK_RESET(feature, fetureSet) + +/* Feature set */ +wifi_error wifi_get_supported_feature_set(wifi_interface_handle handle, feature_set *set); + +/* + * Each row represents a valid feature combination; + * all other combinations are invalid! + */ +wifi_error wifi_get_concurrency_matrix(wifi_interface_handle handle, int set_size_max, + feature_set set[], int *set_size); + +/* multiple interface support */ + +wifi_error wifi_get_ifaces(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces); +wifi_error wifi_get_iface_name(wifi_interface_handle iface, char *name, size_t size); + +/* Configuration events */ + +typedef struct { + void (*on_country_code_changed)(char code[2]); // We can get this from supplicant too + + // More event handlers +} wifi_event_handler; + +typedef struct { + void (*on_rssi_threshold_breached)(wifi_request_id id, u8 *cur_bssid, s8 cur_rssi); +} wifi_rssi_event_handler; + +wifi_error wifi_set_iface_event_handler(wifi_request_id id, wifi_interface_handle iface, wifi_event_handler eh); +wifi_error wifi_reset_iface_event_handler(wifi_request_id id, wifi_interface_handle iface); + +wifi_error wifi_set_nodfs_flag(wifi_interface_handle handle, u32 nodfs); + +/* include various feature headers */ + +#include "gscan.h" +#include "link_layer_stats.h" +#include "rtt.h" +#include "tdls.h" +#include "wifi_logger.h" +#include "wifi_config.h" +#include "wifi_nan.h" +#include "wifi_offload.h" + +//wifi HAL function pointer table +typedef struct { + wifi_error (* wifi_initialize) (wifi_handle *); + void (* wifi_cleanup) (wifi_handle, wifi_cleaned_up_handler); + void (*wifi_event_loop)(wifi_handle); + void (* wifi_get_error_info) (wifi_error , const char **); + wifi_error (* wifi_get_supported_feature_set) (wifi_interface_handle, feature_set *); + wifi_error (* wifi_get_concurrency_matrix) (wifi_interface_handle, int, feature_set *, int *); + wifi_error (* wifi_set_scanning_mac_oui) (wifi_interface_handle, unsigned char *); + wifi_error (* wifi_get_supported_channels)(wifi_handle, int *, wifi_channel *); + wifi_error (* wifi_is_epr_supported)(wifi_handle); + wifi_error (* wifi_get_ifaces) (wifi_handle , int *, wifi_interface_handle **); + wifi_error (* wifi_get_iface_name) (wifi_interface_handle, char *name, size_t); + wifi_error (* wifi_set_iface_event_handler) (wifi_request_id,wifi_interface_handle , + wifi_event_handler); + wifi_error (* wifi_reset_iface_event_handler) (wifi_request_id, wifi_interface_handle); + wifi_error (* wifi_start_gscan) (wifi_request_id, wifi_interface_handle, wifi_scan_cmd_params, + wifi_scan_result_handler); + wifi_error (* wifi_stop_gscan)(wifi_request_id, wifi_interface_handle); + wifi_error (* wifi_get_cached_gscan_results)(wifi_interface_handle, byte, int, + wifi_cached_scan_results *, int *); + wifi_error (* wifi_set_bssid_hotlist)(wifi_request_id, wifi_interface_handle, + wifi_bssid_hotlist_params, wifi_hotlist_ap_found_handler); + wifi_error (* wifi_reset_bssid_hotlist)(wifi_request_id, wifi_interface_handle); + wifi_error (* wifi_set_significant_change_handler)(wifi_request_id, wifi_interface_handle, + wifi_significant_change_params, wifi_significant_change_handler); + wifi_error (* wifi_reset_significant_change_handler)(wifi_request_id, wifi_interface_handle); + wifi_error (* wifi_get_gscan_capabilities)(wifi_interface_handle, wifi_gscan_capabilities *); + wifi_error (* wifi_set_link_stats) (wifi_interface_handle, wifi_link_layer_params); + wifi_error (* wifi_get_link_stats) (wifi_request_id,wifi_interface_handle, + wifi_stats_result_handler); + wifi_error (* wifi_clear_link_stats)(wifi_interface_handle,u32, u32 *, u8, u8 *); + wifi_error (* wifi_get_valid_channels)(wifi_interface_handle,int, int, wifi_channel *, int *); + wifi_error (* wifi_rtt_range_request)(wifi_request_id, wifi_interface_handle, unsigned, + wifi_rtt_config[], wifi_rtt_event_handler); + wifi_error (* wifi_rtt_range_cancel)(wifi_request_id, wifi_interface_handle, unsigned, + mac_addr[]); + wifi_error (* wifi_get_rtt_capabilities)(wifi_interface_handle, wifi_rtt_capabilities *); + wifi_error (* wifi_set_nodfs_flag)(wifi_interface_handle, u32); + wifi_error (* wifi_start_logging)(wifi_interface_handle, u32, u32, u32, u32, char *); + wifi_error (* wifi_set_epno_list)(int, wifi_interface_info *, int, wifi_epno_network *, + wifi_epno_handler); + wifi_error (* wifi_set_country_code)(wifi_interface_handle, const char *); + wifi_error (* wifi_get_firmware_memory_dump)( wifi_interface_handle iface, + wifi_firmware_memory_dump_handler handler); + wifi_error (* wifi_set_log_handler)(wifi_request_id id, wifi_interface_handle iface, + wifi_ring_buffer_data_handler handler); + wifi_error (* wifi_reset_log_handler)(wifi_request_id id, wifi_interface_handle iface); + wifi_error (* wifi_set_alert_handler)(wifi_request_id id, wifi_interface_handle iface, + wifi_alert_handler handler); + wifi_error (* wifi_reset_alert_handler)(wifi_request_id id, wifi_interface_handle iface); + wifi_error (* wifi_get_firmware_version)(wifi_interface_handle iface, char *buffer, + int buffer_size); + wifi_error (* wifi_get_ring_buffers_status)(wifi_interface_handle iface, + u32 *num_rings, wifi_ring_buffer_status *status); + wifi_error (* wifi_get_logger_supported_feature_set)(wifi_interface_handle iface, + unsigned int *support); + wifi_error (* wifi_get_ring_data)(wifi_interface_handle iface, char *ring_name); + wifi_error (* wifi_enable_tdls)(wifi_interface_handle, mac_addr, wifi_tdls_params *, + wifi_tdls_handler); + wifi_error (* wifi_disable_tdls)(wifi_interface_handle, mac_addr); + wifi_error (*wifi_get_tdls_status) (wifi_interface_handle, mac_addr, wifi_tdls_status *); + wifi_error (*wifi_get_tdls_capabilities)(wifi_interface_handle iface, + wifi_tdls_capabilities *capabilities); + wifi_error (* wifi_get_driver_version)(wifi_interface_handle iface, char *buffer, + int buffer_size); + wifi_error (* wifi_set_passpoint_list)(wifi_request_id id, wifi_interface_handle iface, + int num, wifi_passpoint_network *networks, wifi_passpoint_event_handler handler); + wifi_error (* wifi_reset_passpoint_list)(wifi_request_id id, wifi_interface_handle iface); + wifi_error (*wifi_set_bssid_blacklist)(wifi_request_id id, wifi_interface_handle iface, + wifi_bssid_params params); + wifi_error (*wifi_enable_lazy_roam)(wifi_request_id id, + wifi_interface_handle iface, int enable); + wifi_error (*wifi_set_bssid_preference)(wifi_request_id id, wifi_interface_handle iface, + int num_bssid, wifi_bssid_preference *prefs); + wifi_error (*wifi_set_gscan_roam_params)(wifi_request_id id, wifi_interface_handle iface, + wifi_roam_params * params); + wifi_error (*wifi_set_ssid_white_list)(wifi_request_id id, wifi_interface_handle iface, + int num_networks, wifi_ssid *ssids); + wifi_error (*wifi_set_lci) (wifi_request_id id, wifi_interface_handle iface, + wifi_lci_information *lci); + wifi_error (*wifi_set_lcr) (wifi_request_id id, wifi_interface_handle iface, + wifi_lcr_information *lcr); + wifi_error (*wifi_start_sending_offloaded_packet)(wifi_request_id id, + wifi_interface_handle iface, u8 *ip_packet, u16 ip_packet_len, + u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec); + wifi_error (*wifi_stop_sending_offloaded_packet)(wifi_request_id id, + wifi_interface_handle iface); + wifi_error (*wifi_start_rssi_monitoring)(wifi_request_id id, wifi_interface_handle + iface, s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh); + wifi_error (*wifi_stop_rssi_monitoring)(wifi_request_id id, wifi_interface_handle iface); +} wifi_hal_fn; +wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn); +#ifdef __cplusplus +} +#endif + +#endif +
diff --git a/libhardware_legacy/include/hardware_legacy/wifi_logger.h b/libhardware_legacy/include/hardware_legacy/wifi_logger.h new file mode 100644 index 0000000..9823aa3 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi_logger.h
@@ -0,0 +1,413 @@ +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_LOGGER_H +#define __WIFI_HAL_LOGGER_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define LOGGER_MAJOR_VERSION 1 +#define LOGGER_MINOR_VERSION 0 +#define LOGGER_MICRO_VERSION 0 + + + +/** + * WiFi logger life cycle is as follow: + * + * - At initialization time, framework will call wifi_get_ring_buffers_status + * so as to obtain the names and list of supported buffers. + * - When WiFi operation start framework will call wifi_start_logging + * so as to trigger log collection. + * - Developper UI will provide an option to the user, so as it can set the verbose level + * of individual buffer as reported by wifi_get_ring_buffers_status. + * - During wifi operations, driver will periodically report per ring data to framework + * by invoking the on_ring_buffer_data call back. + * - when capturing a bug report, framework will indicate to driver that all the data + * has to be uploaded, urgently, by calling wifi_get_ring_data. + * + * The data uploaded by driver will be stored by framework in separate files, with one stream + * of file per ring. + * Framework will store the files in pcapng format, allowing for easy merging and parsing + * with network analyzer tools. + */ + + +typedef int wifi_radio; +typedef int wifi_ring_buffer_id; + +#define PER_PACKET_ENTRY_FLAGS_DIRECTION_TX 1 // 0: TX, 1: RX +#define PER_PACKET_ENTRY_FLAGS_TX_SUCCESS 2 // whether packet was transmitted or + // received/decrypted successfully +#define PER_PACKET_ENTRY_FLAGS_80211_HEADER 4 // has full 802.11 header, else has 802.3 header +#define PER_PACKET_ENTRY_FLAGS_PROTECTED 8 // whether packet was encrypted + +typedef struct { + u8 flags; + u8 tid; // transmit or received tid + u16 MCS; // modulation and bandwidth + u8 rssi; // TX: RSSI of ACK for that packet + // RX: RSSI of packet + u8 num_retries; // number of attempted retries + u16 last_transmit_rate; // last transmit rate in .5 mbps + u16 link_layer_transmit_sequence; // transmit/reeive sequence for that MPDU packet + u64 firmware_entry_timestamp; // TX: firmware timestamp (us) when packet is queued within + // firmware buffer for SDIO/HSIC or into PCIe buffer + // RX: firmware receive timestamp + u64 start_contention_timestamp; // firmware timestamp (us) when packet start contending for the + // medium for the first time, at head of its AC queue, + // or as part of an MPDU or A-MPDU. This timestamp is + // not updated for each retry, only the first transmit attempt. + u64 transmit_success_timestamp; // fimrware timestamp (us) when packet is successfully + // transmitted or aborted because it has exhausted + // its maximum number of retries. + u8 data[0]; // packet data. The length of packet data is determined by the entry_size field of + // the wifi_ring_buffer_entry structure. It is expected that first bytes of the + // packet, or packet headers only (up to TCP or RTP/UDP headers) + // will be copied into the ring +} __attribute__((packed)) wifi_ring_per_packet_status_entry; + + +/* Below events refer to the wifi_connectivity_event ring and shall be supported */ +#define WIFI_EVENT_ASSOCIATION_REQUESTED 0 // driver receives association command from kernel +#define WIFI_EVENT_AUTH_COMPLETE 1 +#define WIFI_EVENT_ASSOC_COMPLETE 2 +#define WIFI_EVENT_FW_AUTH_STARTED 3 // fw event indicating auth frames are sent +#define WIFI_EVENT_FW_ASSOC_STARTED 4 // fw event indicating assoc frames are sent +#define WIFI_EVENT_FW_RE_ASSOC_STARTED 5 // fw event indicating reassoc frames are sent +#define WIFI_EVENT_DRIVER_SCAN_REQUESTED 6 +#define WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND 7 +#define WIFI_EVENT_DRIVER_SCAN_COMPLETE 8 +#define WIFI_EVENT_G_SCAN_STARTED 9 +#define WIFI_EVENT_G_SCAN_COMPLETE 10 +#define WIFI_EVENT_DISASSOCIATION_REQUESTED 11 +#define WIFI_EVENT_RE_ASSOCIATION_REQUESTED 12 +#define WIFI_EVENT_ROAM_REQUESTED 13 +#define WIFI_EVENT_BEACON_RECEIVED 14 // received beacon from AP (event enabled + // only in verbose mode) +#define WIFI_EVENT_ROAM_SCAN_STARTED 15 // firmware has triggered a roam scan (not g-scan) +#define WIFI_EVENT_ROAM_SCAN_COMPLETE 16 // firmware has completed a roam scan (not g-scan) +#define WIFI_EVENT_ROAM_SEARCH_STARTED 17 // firmware has started searching for roam + // candidates (with reason =xx) +#define WIFI_EVENT_ROAM_SEARCH_STOPPED 18 // firmware has stopped searching for roam + // candidates (with reason =xx) +#define WIFI_EVENT_CHANNEL_SWITCH_ANOUNCEMENT 20 // received channel switch anouncement from AP +#define WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_START 21 // fw start transmit eapol frame, with + // EAPOL index 1-4 +#define WIFI_EVENT_FW_EAPOL_FRAME_TRANSMIT_STOP 22 // fw gives up eapol frame, with rate, + // success/failure and number retries +#define WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED 23 // kernel queue EAPOL for transmission + // in driver with EAPOL index 1-4 +#define WIFI_EVENT_FW_EAPOL_FRAME_RECEIVED 24 // with rate, regardless of the fact that + // EAPOL frame is accepted or rejected by fw +#define WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED 26 // with rate, and eapol index, driver has + // received EAPOL frame and will queue it up + // to wpa_supplicant +#define WIFI_EVENT_BLOCK_ACK_NEGOTIATION_COMPLETE 27 // with success/failure, parameters +#define WIFI_EVENT_BT_COEX_BT_SCO_START 28 +#define WIFI_EVENT_BT_COEX_BT_SCO_STOP 29 +#define WIFI_EVENT_BT_COEX_BT_SCAN_START 30 // for paging/scan etc., when BT starts transmiting + // twice per BT slot +#define WIFI_EVENT_BT_COEX_BT_SCAN_STOP 31 +#define WIFI_EVENT_BT_COEX_BT_HID_START 32 +#define WIFI_EVENT_BT_COEX_BT_HID_STOP 33 +#define WIFI_EVENT_ROAM_AUTH_STARTED 34 // fw sends auth frame in roaming to next candidate +#define WIFI_EVENT_ROAM_AUTH_COMPLETE 35 // fw receive auth confirm from ap +#define WIFI_EVENT_ROAM_ASSOC_STARTED 36 // firmware sends assoc/reassoc frame in + // roaming to next candidate +#define WIFI_EVENT_ROAM_ASSOC_COMPLETE 37 // firmware receive assoc/reassoc confirm from ap +#define WIFI_EVENT_G_SCAN_STOP 38 // firmware sends stop G_SCAN +#define WIFI_EVENT_G_SCAN_CYCLE_STARTED 39 // firmware indicates G_SCAN scan cycle started +#define WIFI_EVENT_G_SCAN_CYCLE_COMPLETED 40 // firmware indicates G_SCAN scan cycle completed +#define WIFI_EVENT_G_SCAN_BUCKET_STARTED 41 // firmware indicates G_SCAN scan start + // for a particular bucket +#define WIFI_EVENT_G_SCAN_BUCKET_COMPLETED 42 // firmware indicates G_SCAN scan completed for + // for a particular bucket +#define WIFI_EVENT_G_SCAN_RESULTS_AVAILABLE 43 // Event received from firmware about G_SCAN scan + // results being available +#define WIFI_EVENT_G_SCAN_CAPABILITIES 44 // Event received from firmware with G_SCAN + // capabilities +#define WIFI_EVENT_ROAM_CANDIDATE_FOUND 45 // Event received from firmware when eligible + // candidate is found +#define WIFI_EVENT_ROAM_SCAN_CONFIG 46 // Event received from firmware when roam scan + // configuration gets enabled or disabled + +/** + * Parameters of wifi logger events are TLVs + * Event parameters tags are defined as: + */ +#define WIFI_TAG_VENDOR_SPECIFIC 0 // take a byte stream as parameter +#define WIFI_TAG_BSSID 1 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_ADDR 2 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_SSID 3 // takes a 32 bytes SSID address as parameter +#define WIFI_TAG_STATUS 4 // takes an integer as parameter +#define WIFI_TAG_CHANNEL_SPEC 5 // takes one or more wifi_channel_spec as parameter +#define WIFI_TAG_WAKE_LOCK_EVENT 6 // takes a wake_lock_event struct as parameter +#define WIFI_TAG_ADDR1 7 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_ADDR2 8 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_ADDR3 9 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_ADDR4 10 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_TSF 11 // take a 64 bits TSF value as parameter +#define WIFI_TAG_IE 12 // take one or more specific 802.11 IEs parameter, + // IEs are in turn indicated in TLV format as per + // 802.11 spec +#define WIFI_TAG_INTERFACE 13 // take interface name as parameter +#define WIFI_TAG_REASON_CODE 14 // take a reason code as per 802.11 as parameter +#define WIFI_TAG_RATE_MBPS 15 // take a wifi rate in 0.5 mbps +#define WIFI_TAG_REQUEST_ID 16 // take an integer as parameter +#define WIFI_TAG_BUCKET_ID 17 // take an integer as parameter +#define WIFI_TAG_GSCAN_PARAMS 18 // takes a wifi_scan_cmd_params struct as parameter +#define WIFI_TAG_GSCAN_CAPABILITIES 19 // takes a wifi_gscan_capabilities struct as parameter +#define WIFI_TAG_SCAN_ID 20 // take an integer as parameter +#define WIFI_TAG_RSSI 21 // take an integer as parameter +#define WIFI_TAG_CHANNEL 22 // take an integer as parameter +#define WIFI_TAG_LINK_ID 23 // take an integer as parameter +#define WIFI_TAG_LINK_ROLE 24 // take an integer as parameter +#define WIFI_TAG_LINK_STATE 25 // take an integer as parameter +#define WIFI_TAG_LINK_TYPE 26 // take an integer as parameter +#define WIFI_TAG_TSCO 27 // take an integer as parameter +#define WIFI_TAG_RSCO 28 // take an integer as parameter +#define WIFI_TAG_EAPOL_MESSAGE_TYPE 29 // take an integer as parameter + // M1-1, M2-2, M3-3, M4-4 + +typedef struct { + u16 tag; + u16 length; // length of value + u8 value[0]; +} __attribute__((packed)) tlv_log; + +typedef struct { + u16 event; + tlv_log tlvs[0]; // separate parameter structure per event to be provided and optional data + // the event_data is expected to include an official android part, with some + // parameter as transmit rate, num retries, num scan result found etc... + // as well, event_data can include a vendor proprietary part which is + // understood by the developer only. +} __attribute__((packed)) wifi_ring_buffer_driver_connectivity_event; + + +/** + * Ring buffer name for power events ring. note that power event are extremely frequents + * and thus should be stored in their own ring/file so as not to clobber connectivity events. + */ +typedef struct { + int status; // 0 taken, 1 released + int reason; // reason why this wake lock is taken + char name[0]; // null terminated +} __attribute__((packed)) wake_lock_event; + +typedef struct { + u16 event; + tlv_log tlvs[0]; +} __attribute__((packed)) wifi_power_event; + + +/** + * This structure represent a logger entry within a ring buffer. + * Wifi driver are responsible to manage the ring buffer and write the debug + * information into those rings. + * + * In general, the debug entries can be used to store meaningful 802.11 information (SME, MLME, + * connection and packet statistics) as well as vendor proprietary data that is specific to a + * specific driver or chipset. + * Binary entries can be used so as to store packet data or vendor specific information and + * will be treated as blobs of data by android. + * + * A user land process will be started by framework so as to periodically retrieve the + * data logged by drivers into their ring buffer, store the data into log files and include + * the logs into android bugreports. + */ +enum { + RING_BUFFER_ENTRY_FLAGS_HAS_BINARY = (1 << (0)), // set for binary entries + RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP = (1 << (1)) // set if 64 bits timestamp is present +}; + +enum { + ENTRY_TYPE_CONNECT_EVENT = 1, + ENTRY_TYPE_PKT, + ENTRY_TYPE_WAKE_LOCK, + ENTRY_TYPE_POWER_EVENT, + ENTRY_TYPE_DATA +}; + +typedef struct { + u16 entry_size; // the size of payload excluding the header. + u8 flags; + u8 type; // entry type + u64 timestamp; // present if has_timestamp bit is set. +} __attribute__((packed)) wifi_ring_buffer_entry; + +#define WIFI_RING_BUFFER_FLAG_HAS_BINARY_ENTRIES 0x00000001 // set if binary entries are present +#define WIFI_RING_BUFFER_FLAG_HAS_ASCII_ENTRIES 0x00000002 // set if ascii entries are present + + +/* ring buffer params */ +/** + * written_bytes and read_bytes implement a producer consumer API + * hence written_bytes >= read_bytes + * a modulo arithmetic of the buffer size has to be applied to those counters: + * actual offset into ring buffer = written_bytes % ring_buffer_byte_size + * + */ +typedef struct { + u8 name[32]; + u32 flags; + wifi_ring_buffer_id ring_id; // unique integer representing the ring + u32 ring_buffer_byte_size; // total memory size allocated for the buffer + u32 verbose_level; // verbose level for ring buffer + u32 written_bytes; // number of bytes that was written to the buffer by driver, + // monotonously increasing integer + u32 read_bytes; // number of bytes that was read from the buffer by user land, + // monotonously increasing integer + u32 written_records; // number of records that was written to the buffer by driver, + // monotonously increasing integer +} wifi_ring_buffer_status; + + +/** + * Callback for reporting ring data + * + * The ring buffer data collection is event based: + * - Driver calls on_ring_buffer_data when new records are available, the wifi_ring_buffer_status + * passed up to framework in the call back indicates to framework if more data is available in + * the ring buffer. It is not expected that driver will necessarily always empty the ring + * immediately as data is available, instead driver will report data every X seconds or if + * N bytes are available. + * - In the case where a bug report has to be captured, framework will require driver to upload + * all data immediately. This is indicated to driver when framework calls wifi_get_ringdata. + * When framework calls wifi_get_ring_data, driver will start sending all available data in the + * indicated ring by repeatedly invoking the on_ring_buffer_data callback. + * + * The callback is called by log handler whenever ring data comes in driver. + */ +typedef struct { + void (*on_ring_buffer_data) (char *ring_name, char *buffer, int buffer_size, + wifi_ring_buffer_status *status); +} wifi_ring_buffer_data_handler; + +/** + * API to set the log handler for getting ring data + * - Only a single instance of log handler can be instantiated for each ring buffer. + */ +wifi_error wifi_set_log_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_ring_buffer_data_handler handler); + +/* API to reset the log handler */ +wifi_error wifi_reset_log_handler(wifi_request_id id, wifi_interface_handle iface); + + +/** + * Callback for reporting FW dump + * + * The buffer data collection is event based such as FW health check or FW dump. + * The callback is called by alert handler. + */ +typedef struct { + void (*on_alert) (wifi_request_id id, char *buffer, int buffer_size, int err_code); +} wifi_alert_handler; + +/* + * API to set the alert handler for the alert case in Wi-Fi Chip + * - Only a single instance of alert handler can be instantiated. + */ +wifi_error wifi_set_alert_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_alert_handler handler); + +/* API to reset the alert handler */ +wifi_error wifi_reset_alert_handler(wifi_request_id id, wifi_interface_handle iface); + +/* API for framework to indicate driver has to upload and drain all data of a given ring */ +wifi_error wifi_get_ring_data(wifi_interface_handle iface, char *ring_name); + + +/** + * API to trigger the debug collection. + * Unless his API is invoked - logging is not triggered. + * - Verbose_level 0 corresponds to no collection, + * and it makes log handler stop by no more events from driver. + * - Verbose_level 1 correspond to normal log level, with minimal user impact. + * This is the default value. + * - Verbose_level 2 are enabled when user is lazily trying to reproduce a problem, + * wifi performances and power can be impacted but device should not otherwise be + * significantly impacted. + * - Verbose_level 3+ are used when trying to actively debug a problem. + * + * ring_name represent the name of the ring for which data collection shall start. + * + * flags: TBD parameter used to enable/disable specific events on a ring + * max_interval: maximum interval in seconds for driver to invoke on_ring_buffer_data, + * ignore if zero + * min_data_size: minimum data size in buffer for driver to invoke on_ring_buffer_data, + * ignore if zero + */ +wifi_error wifi_start_logging(wifi_interface_handle iface, u32 verbose_level, u32 flags, + u32 max_interval_sec, u32 min_data_size, char *ring_name); + +/** + * API to get the status of all ring buffers supported by driver. + * - Caller is responsible to allocate / free ring buffer status. + * - Maximum no of ring buffer would be 10. + */ +wifi_error wifi_get_ring_buffers_status(wifi_interface_handle iface, u32 *num_rings, + wifi_ring_buffer_status *status); + +/** + * Synchronous memory dump by user request. + * - Caller is responsible to store memory dump data into a local, + * e.g., /data/misc/wifi/memdump.bin + */ +typedef struct { + void (*on_firmware_memory_dump) (char *buffer, int buffer_size); +} wifi_firmware_memory_dump_handler; + +/** + * API to collect a firmware memory dump for a given iface by async memdump event. + * - Triggered by Alerthandler, esp. when FW problem or FW health check happens + * - Caller is responsible to store fw dump data into a local, + * e.g., /data/misc/wifi/alertdump-1.bin + */ +wifi_error wifi_get_firmware_memory_dump(wifi_interface_handle iface, + wifi_firmware_memory_dump_handler handler); + +/** + * API to collect a firmware version string. + * - Caller is responsible to allocate / free a buffer to retrieve firmware verion info. + * - Max string will be at most 256 bytes. + */ +wifi_error wifi_get_firmware_version(wifi_interface_handle iface, char *buffer, int buffer_size); + +/** + * API to collect a driver version string. + * - Caller is responsible to allocate / free a buffer to retrieve driver verion info. + * - Max string will be at most 256 bytes. + */ +wifi_error wifi_get_driver_version(wifi_interface_handle iface, char *buffer, int buffer_size); + + +/* Feature set */ +enum { + WIFI_LOGGER_MEMORY_DUMP_SUPPORTED = (1 << (0)), // Memory dump of FW + WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED = (1 << (1)), // PKT status + WIFI_LOGGER_CONNECT_EVENT_SUPPORTED = (1 << (2)), // Connectivity event + WIFI_LOGGER_POWER_EVENT_SUPPORTED = (1 << (3)), // POWER of Driver + WIFI_LOGGER_WAKE_LOCK_SUPPORTED = (1 << (4)), // WAKE LOCK of Driver + WIFI_LOGGER_VERBOSE_SUPPORTED = (1 << (5)), // verbose log of FW + WIFI_LOGGER_WATCHDOG_TIMER_SUPPORTED = (1 << (6)) // monitor the health of FW +}; + +/** + * API to retrieve the current supportive features. + * - An integer variable is enough to have bit mapping info by caller. + */ +wifi_error wifi_get_logger_supported_feature_set(wifi_interface_handle iface, + unsigned int *support); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*__WIFI_HAL_STATS_ */ +
diff --git a/libhardware_legacy/include/hardware_legacy/wifi_nan.h b/libhardware_legacy/include/hardware_legacy/wifi_nan.h new file mode 100755 index 0000000..70e97f1 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi_nan.h
@@ -0,0 +1,877 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef __NAN_H__ +#define __NAN_H__ + +#include "wifi_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/***************************************************************************** + * NAN Discovery Service Structures and Functions + *****************************************************************************/ + +/* + Definitions + All multi-byte fields within all NAN protocol stack messages are assumed to be in Little Endian order. +*/ + +typedef int NanVersion; + +#define NAN_MAC_ADDR_LEN 6 +#define NAN_COUNTRY_STRING_LEN 3 +#define NAN_MAJOR_VERSION 1 +#define NAN_MINOR_VERSION 0 +#define NAN_MICRO_VERSION 0 + +/* NAN Maximum Lengths */ +#define NAN_MAX_SERVICE_NAME_LEN 255 +#define NAN_MAX_MATCH_FILTER_LEN 255 +#define NAN_MAX_SERVICE_SPECIFIC_INFO_LEN 1024 + +/* + Definition of various NanRequestType +*/ +typedef enum { + NAN_REQUEST_ENABLE =0, + NAN_REQUEST_DISABLE =1, + NAN_REQUEST_PUBLISH =2, + NAN_REQUEST_PUBLISH_CANCEL =3, + NAN_REQUEST_TRANSMIT_FOLLOWUP =4, + NAN_REQUEST_SUBSCRIBE =5, + NAN_REQUEST_SUBSCRIBE_CANCEL =6, + NAN_REQUEST_STATS =7, + NAN_REQUEST_CONFIG =8, + NAN_REQUEST_TCA =9, + NAN_REQUEST_LAST =0xFFFF +} NanRequestType; + +/* + Definition of various NanResponseType +*/ +typedef enum { + NAN_RESPONSE_ENABLED =0, + NAN_RESPONSE_DISABLED =1, + NAN_RESPONSE_PUBLISH =2, + NAN_RESPONSE_PUBLISH_CANCEL =3, + NAN_RESPONSE_TRANSMIT_FOLLOWUP =4, + NAN_RESPONSE_SUBSCRIBE =5, + NAN_RESPONSE_SUBSCRIBE_CANCEL =6, + NAN_RESPONSE_STATS =7, + NAN_RESPONSE_CONFIG =8, + NAN_RESPONSE_TCA =9, + NAN_RESPONSE_ERROR =10, + NAN_RESPONSE_UNKNOWN =0xFFFF +} NanResponseType; + +/* + Definition of various NanIndication(events) +*/ +typedef enum { + NAN_INDICATION_PUBLISH_REPLIED =0, + NAN_INDICATION_PUBLISH_TERMINATED =1, + NAN_INDICATION_SUBSCRIBE_MATCH =2, + NAN_INDICATION_SUBSCRIBE_UNMATCH =3, + NAN_INDICATION_SUBSCRIBE_TERMINATED =4, + NAN_INDICATION_DE_EVENT =5, + NAN_INDICATION_FOLLOWUP =6, + NAN_INDICATION_DISABLED =7, + NAN_INDICATION_TCA =8, + NAN_INDICATION_UNKNOWN =0xFFFF +} NanIndicationType; + + +/* NAN Publish Types */ +typedef enum { + NAN_PUBLISH_TYPE_UNSOLICITED = 0, + NAN_PUBLISH_TYPE_SOLICITED, + NAN_PUBLISH_TYPE_UNSOLICITED_SOLICITED, + NAN_PUBLISH_TYPE_LAST, +} NanPublishType; + +/* NAN Transmit Priorities */ +typedef enum { + NAN_TX_PRIORITY_LOW = 0, + NAN_TX_PRIORITY_NORMAL, + NAN_TX_PRIORITY_HIGH, + NAN_TX_PRIORITY_LAST +} NanTxPriority; + +/* NAN Statistics Request ID Codes */ +typedef enum +{ + NAN_STATS_ID_FIRST = 0, + NAN_STATS_ID_DE_PUBLISH = NAN_STATS_ID_FIRST, + NAN_STATS_ID_DE_SUBSCRIBE, + NAN_STATS_ID_DE_MAC, + NAN_STATS_ID_DE_TIMING_SYNC, + NAN_STATS_ID_DE_DW, + NAN_STATS_ID_DE, + NAN_STATS_ID_LAST +} NanStatsId; + +/* NAN Protocol Event ID Codes */ +typedef enum +{ + NAN_EVENT_ID_FIRST = 0, + NAN_EVENT_ID_SELF_STA_MAC_ADDR = NAN_EVENT_ID_FIRST, + NAN_EVENT_ID_STARTED_CLUSTER, + NAN_EVENT_ID_JOINED_CLUSTER, + NAN_EVENT_ID_LAST +} NanEventId; + +/* TCA IDs */ +typedef enum +{ + NAN_TCA_ID_FIRST = 0, + NAN_TCA_ID_CLUSTER_SIZE = NAN_TCA_ID_FIRST, + NAN_TCA_ID_LAST +} NanTcaId; + +/* + Various NAN Protocol Response code +*/ +typedef enum +{ + /* NAN Protocol Response Codes */ + NAN_STATUS_SUCCESS = 0, + NAN_STATUS_TIMEOUT, + NAN_STATUS_DE_FAILURE, + NAN_STATUS_INVALID_MSG_VERSION, + NAN_STATUS_INVALID_MSG_LEN, + NAN_STATUS_INVALID_MSG_ID, + NAN_STATUS_INVALID_HANDLE, + NAN_STATUS_NO_SPACE_AVAILABLE, + NAN_STATUS_INVALID_PUBLISH_TYPE, + NAN_STATUS_INVALID_TX_TYPE, + NAN_STATUS_INVALID_MATCH_ALGORITHM, + NAN_STATUS_DISABLE_IN_PROGRESS, + NAN_STATUS_INVALID_TLV_LEN, + NAN_STATUS_INVALID_TLV_TYPE, + NAN_STATUS_MISSING_TLV_TYPE, + NAN_STATUS_INVALID_TOTAL_TLVS_LEN, + NAN_STATUS_INVALID_MATCH_HANDLE, + NAN_STATUS_INVALID_TLV_VALUE, + NAN_STATUS_INVALID_TX_PRIORITY, + NAN_STATUS_INVALID_TCA_ID, + NAN_STATUS_INVALID_STATS_ID, + + /* NAN Configuration Response codes */ + NAN_STATUS_INVALID_RSSI_CLOSE_VALUE = 128, + NAN_STATUS_INVALID_RSSI_MEDIUM_VALUE, + NAN_STATUS_INVALID_HOP_COUNT_LIMIT, + NAN_STATUS_INVALID_CLUSTER_JOIN_COUNT, + NAN_STATUS_INVALID_MIN_WAKE_DW_DURATION_VALUE, + NAN_STATUS_INVALID_OFDM_DATA_RATE_VALUE, + NAN_STATUS_INVALID_RANDOM_FACTOR_UPDATE_TIME_VALUE, + NAN_STATUS_INVALID_MASTER_PREFERENCE_VALUE, + NAN_STATUS_INVALID_EARLY_DW_WAKE_INTERVAL_VALUE, + NAN_STATUS_INVALID_LOW_CLUSTER_ID_VALUE, + NAN_STATUS_INVALID_HIGH_CLUSTER_ID_VALUE, + NAN_STATUS_INVALID_INITIAL_SCAN_PERIOD, + NAN_STATUS_INVALID_ONGOING_SCAN_PERIOD, + NAN_STATUS_INVALID_RSSI_PROXIMITY_VALUE +} NanStatusType; + +/* + Various NAN Terminated Indication Code +*/ +typedef enum +{ + NAN_TERMINATED_REASON_INVALID = 0, + NAN_TERMINATED_REASON_TIMEOUT, + NAN_TERMINATED_REASON_USER_REQUEST, + NAN_TERMINATED_REASON_FAILURE, + NAN_TERMINATED_REASON_COUNT_REACHED, + NAN_TERMINATED_REASON_DE_SHUTDOWN, + NAN_TERMINATED_REASON_DISABLE_IN_PROGRESS +} NanTerminatedStatus; + +/* NAN Transmit Types */ +typedef enum +{ + NAN_TX_TYPE_BROADCAST = 0, + NAN_TX_TYPE_UNICAST, + NAN_TX_TYPE_LAST +} NanTxType; + +/* NAN Subscribe Type Bit */ +#define NAN_SUBSCRIBE_TYPE_PASSIVE 0 +#define NAN_SUBSCRIBE_TYPE_ACTIVE 1 + +/* NAN Service Response Filter Attribute Bit */ +#define NAN_SRF_ATTR_BLOOM_FILTER 0 +#define NAN_SRF_ATTR_PARTIAL_MAC_ADDR 1 + +/* NAN Service Response Filter Include Bit */ +#define NAN_SRF_INCLUDE_DO_NOT_RESPOND 0 +#define NAN_SRF_INCLUDE_RESPOND 1 + +/* NAN Match Algorithms */ +typedef enum +{ + NAN_MATCH_ALG_FIRST = 0, + NAN_MATCH_ALG_MATCH_ONCE = NAN_MATCH_ALG_FIRST, + NAN_MATCH_ALG_MATCH_CONTINUOUS, + NAN_MATCH_ALG_LAST +} NanMatchAlg; + +/* NAN Header */ +typedef struct { + /* + 16-bit quantity which is allocated by the FW. + Pass the Handle as 0xFFFF if the Host would like to set up a new + Publish/Subscribe and the FW will pass back a valid handle in response msg. + To update an already opened Publish/Subscribe Host can pass a Handle + which has already been allocated by the FW. + */ + u16 handle; + + /* + 16-bit quantity which is allocated in 2 contexts. For all Request + messages the TransactionId is allocated by the Service Layer and + passed down to the DE. In all Indication messages the TransactionId + field is allocated by the DE. There is no correlation between the + TransactionIds allocated by the Service Layer and those allocated by the DE + */ + u16 transaction_id; +} NanHeader; + +/* + Enable Request Message Structure + The NanEnableReq message instructs the Discovery Engine to enter an operational state +*/ +typedef struct { + NanHeader header; + u8 support_5g; /* default = 0 */ + u16 cluster_low; /* default = 0 */ + u16 cluster_high; /* default = 0 */ + u8 sid_beacon; /* default = 0x80 */ + u8 sync_disc_5g; /* default = 1 i.e 5G Discovery frames only*/ + u8 rssi_close; /* default = 60 (-60 dBm) */ + u8 rssi_middle; /* default = 70 (-70 dBm) */ + u8 rssi_proximity; /* default = 70 (-70 dBm) */ + u8 hop_count_limit; /* default = 2 */ + u8 random_time; /* default = 120 (DWs) */ + u8 master_pref; /* default = 0 */ + u8 periodic_scan_interval; /* default = 20 seconds */ + /* TBD: Google specific IE */ +}NanEnableRequest; + +/* + Disable Request Message Structure + The NanDisableReq message instructs the Discovery Engine to exit an operational state. +*/ +typedef struct { + NanHeader header; +}NanDisableRequest; + +/* + Publish Msg Structure + Message is used to request the DE to publish the Service Name + using the parameters passed into the Discovery Window +*/ +typedef struct { + NanHeader header; + u16 ttl; /* how many seconds to run for. 0 means forever until canceled */ + u16 period; /* periodicity of OTA unsolicited publish. Specified in increments of 500 ms */ + u8 replied_event_flag; /* 1= RepliedEventInd needed, 0 = Not needed */ + NanPublishType publish_type;/* 0= unsolicited, solicited = 1, 2= both */ + NanTxType tx_type; /* 0 = broadcast, 1= unicast if solicited publish */ + u8 publish_count; /* number of OTA Publish, 0 means forever until canceled */ + u16 service_name_len; /* length of service name */ + u8 service_name[NAN_MAX_SERVICE_NAME_LEN];/* UTF-8 encoded string identifying the service */ + + /* Sequence of values which should be conveyed to the Discovery Engine of a + NAN Device that has invoked a Subscribe method corresponding to this Publish method + */ + u16 service_specific_info_len; + u8 service_specific_info[NAN_MAX_SERVICE_SPECIFIC_INFO_LEN]; + + /* Ordered sequence of <length, value> pairs which specify further response conditions + beyond the service name used to filter subscribe messages to respond to. + This is only needed when the PT is set to NAN_SOLICITED or NAN_SOLICITED_UNSOLICITED. + */ + u16 rx_match_filter_len; + u8 rx_match_filter[NAN_MAX_MATCH_FILTER_LEN]; + + /* Ordered sequence of <length, value> pairs to be included in the Discovery Frame. + If present it is always sent in a Discovery Frame + */ + u16 tx_match_filter_len; + u8 tx_match_filter[NAN_MAX_MATCH_FILTER_LEN]; +}NanPublishRequest; + +/* + Publish Cancel Msg Structure + The PublishServiceCancelReq Message is used to request the DE to stop publishing + the Service Name identified by the handle in the message. +*/ +typedef struct { + NanHeader header; +}NanPublishCancelRequest; + +/* + NAN Subscribe Structure + The SubscribeServiceReq message is sent to the Discovery Engine + whenever the Upper layers would like to listen for a Service Name +*/ +typedef struct { + NanHeader header; + u16 ttl; /* how many seconds to run for. 0 means forever until canceled */ + u16 period;/* periodicity of OTA Active Subscribe. Units in increments of 500 ms , 0 = attempt every DW*/ + + /* Flag which specifies how the Subscribe request shall be processed. */ + u8 subscribe_type; /* 0 - PASSIVE , 1- ACTIVE */ + + /* Flag which specifies on Active Subscribes how the Service Response Filter attribute is populated.*/ + u8 serviceResponseFilter; /* 0 - Bloom Filter, 1 - MAC Addr */ + + /* Flag which specifies how the Service Response Filter Include bit is populated.*/ + u8 serviceResponseInclude; /* 0=Do not respond if in the Address Set, 1= Respond */ + + /* Flag which specifies if the Service Response Filter should be used when creating Subscribes.*/ + u8 useServiceResponseFilter; /* 0=Do not send the Service Response Filter,1= send */ + + /* Flag which specifies if the Service Specific Info is needed in the Publish message before creating the MatchIndication*/ + u8 ssiRequiredForMatchIndication; /* 0=Not needed, 1= Required */ + + /* Field which allows the matching behavior to be controlled. */ + NanMatchAlg subscribe_match; /* 0 - Match Once, 1 - Match continuous */ + + /* The number of Subscribe Matches which should occur before the Subscribe request is automatically terminated.*/ + u8 subscribe_count; /* If this value is 0 this field is not used by the DE.*/ + + u16 service_name_len;/* length of service name */ + u8 service_name[NAN_MAX_SERVICE_NAME_LEN]; /* UTF-8 encoded string identifying the service */ + + /* Sequence of values which further specify the published service beyond the service name*/ + u16 service_specific_info_len; + u8 service_specific_info[NAN_MAX_SERVICE_SPECIFIC_INFO_LEN]; + + /* Ordered sequence of <length, value> pairs used to filter out received publish discovery messages. + This can be sent both for a Passive or an Active Subscribe + */ + u16 rx_match_filter_len; + u8 rx_match_filter[NAN_MAX_MATCH_FILTER_LEN]; + + /* Ordered sequence of <length, value> pairs included in the Discovery Frame when an Active Subscribe is used.*/ + u16 tx_match_filter_len; + u8 tx_match_filter[NAN_MAX_MATCH_FILTER_LEN]; +}NanSubscribeRequest; + + +/* + NAN Subscribe Cancel Structure + The SubscribeCancelReq Message is used to request the DE to stop looking for the Service Name. +*/ +typedef struct { + NanHeader header; +}NanSubscribeCancelRequest; + + +/* + Transmit follow up Structure + The TransmitFollowupReq message is sent to the DE to allow the sending of the Service_Specific_Info + to a particular MAC address. +*/ +typedef struct { + NanHeader header; + u8 addr[NAN_MAC_ADDR_LEN]; /* Can be a broadcast/multicast or unicast address */ + NanTxPriority priority; /* priority of the request 0 = low, 1=normal, 2=high */ + u8 dw_or_faw; /* 0= send in a DW, 1=send in FAW */ + + /* Sequence of values which further specify the published service beyond the service name + Treated as service specific info in case dw_or_faw is set to 0 + Treated as extended service specific info in case dw_or_faw is set to non-zero*/ + u16 service_specific_info_len; + u8 service_specific_info[NAN_MAX_SERVICE_SPECIFIC_INFO_LEN]; +}NanTransmitFollowupRequest; + +/* + Stats Request structure + The Discovery Engine can be queried at runtime by the Host processor for statistics + concerning various parts of the Discovery Engine. +*/ +typedef struct { + NanHeader header; + NanStatsId stats_id; /* NAN Statistics Request ID Codes */ + u8 clear; /* 0= Do not clear the stats and return the current contents , 1= Clear the associated stats */ +}NanStatsRequest; + +/* + Config Structure + The NanConfigurationReq message is sent by the Host to the + Discovery Engine in order to configure the Discovery Engine during runtime. +*/ +typedef struct { + NanHeader header; + u8 sid_beacon; /* default = 0x80 */ + u8 sync_disc_5g; /* default = 1 i.e 5G Discovery frames only*/ + u8 rssi_proximity; /* default = 70 (-70 dBm) */ + u8 random_time; /* default = 120 (DWs) */ + u8 master_pref; /* default = 0 */ + u8 periodic_scan_interval; /* default = 20 seconds */ + /* The number of Additional Discovery Window slots in + increments of 16 ms. Since each DW is 512 TUs apart + and the DW takes up 1 slot, the maximum number of additional + slots which can be specified is 31. This is a hint to the + scheduler and there is no guarantee that all 31 slots will + be available because of MCC and BT Coexistence channel usage + */ + u8 additional_disc_window_slots; /* default = 0.*/ +}NanConfigRequest; + +/* + TCA Structure + The Discovery Engine can be configured to send up Events whenever a configured + Threshold Crossing Alert (TCA) Id crosses an integral threshold in a particular direction. +*/ +typedef struct { + NanHeader header; + NanTcaId tca_id; /* Nan Protocol Threshold Crossing Alert (TCA) Codes */ + + /* flag which control whether or not an event is generated for the Rising direction */ + u8 rising_direction_evt_flag; /* 0 - no event, 1 - event */ + + /* flag which control whether or not an event is generated for the Falling direction */ + u8 falling_direction_evt_flag;/* 0 - no event, 1 - event */ + + /* flag which requests a previous TCA request to be cleared from the DE */ + u8 clear;/*0= Do not clear the TCA, 1=Clear the TCA */ + + /* 32 bit value which represents the threshold to be used.*/ + u32 threshold; +}NanTCARequest; + +/* Publish statistics. */ +typedef struct +{ + u32 validPublishServiceReqMsgs; + u32 validPublishServiceRspMsgs; + u32 validPublishServiceCancelReqMsgs; + u32 validPublishServiceCancelRspMsgs; + u32 validPublishRepliedIndMsgs; + u32 validPublishTerminatedIndMsgs; + u32 validActiveSubscribes; + u32 validMatches; + u32 validFollowups; + u32 invalidPublishServiceReqMsgs; + u32 invalidPublishServiceCancelReqMsgs; + u32 invalidActiveSubscribes; + u32 invalidMatches; + u32 invalidFollowups; + u32 publishCount; +} NanPublishStats; + +/* Subscribe statistics. */ +typedef struct +{ + u32 validSubscribeServiceReqMsgs; + u32 validSubscribeServiceRspMsgs; + u32 validSubscribeServiceCancelReqMsgs; + u32 validSubscribeServiceCancelRspMsgs; + u32 validSubscribeTerminatedIndMsgs; + u32 validSubscribeMatchIndMsgs; + u32 validSubscribeUnmatchIndMsgs; + u32 validSolicitedPublishes; + u32 validMatches; + u32 validFollowups; + u32 invalidSubscribeServiceReqMsgs; + u32 invalidSubscribeServiceCancelReqMsgs; + u32 invalidSubscribeFollowupReqMsgs; + u32 invalidSolicitedPublishes; + u32 invalidMatches; + u32 invalidFollowups; + u32 subscribeCount; + u32 bloomFilterIndex; +} NanSubscribeStats; + +/* NAN MAC Statistics. Used for MAC and DW statistics. */ +typedef struct +{ + /* RX stats */ + u32 validFrames; + u32 validActionFrames; + u32 validBeaconFrames; + u32 ignoredActionFrames; + u32 ignoredBeaconFrames; + u32 invalidFrames; + u32 invalidActionFrames; + u32 invalidBeaconFrames; + u32 invalidMacHeaders; + u32 invalidPafHeaders; + u32 nonNanBeaconFrames; + + u32 earlyActionFrames; + u32 inDwActionFrames; + u32 lateActionFrames; + + /* TX stats */ + u32 framesQueued; + u32 totalTRSpUpdates; + u32 completeByTRSp; + u32 completeByTp75DW; + u32 completeByTendDW; + u32 lateActionFramesTx; + + /* Misc stats - ignored for DW. */ + u32 twIncreases; + u32 twDecreases; + u32 twChanges; + u32 twHighwater; + u32 bloomFilterIndex; +} NanMacStats; + +/* NAN Sync Statistics*/ +typedef struct +{ + u64 currTsf; + u64 myRank; + u64 currAmRank; + u64 lastAmRank; + u32 currAmBTT; + u32 lastAmBTT; + u8 currAmHopCount; + u8 currRole; + u16 currClusterId; + + u64 timeSpentInCurrRole; + u64 totalTimeSpentAsMaster; + u64 totalTimeSpentAsNonMasterSync; + u64 totalTimeSpentAsNonMasterNonSync; + u32 transitionsToAnchorMaster; + u32 transitionsToMaster; + u32 transitionsToNonMasterSync; + u32 transitionsToNonMasterNonSync; + u32 amrUpdateCount; + u32 amrUpdateRankChangedCount; + u32 amrUpdateBTTChangedCount; + u32 amrUpdateHcChangedCount; + u32 amrUpdateNewDeviceCount; + u32 amrExpireCount; + u32 mergeCount; + u32 beaconsAboveHcLimit; + u32 beaconsBelowRssiThresh; + u32 beaconsIgnoredNoSpace; + u32 beaconsForOurCluster; + u32 beaconsForOtherCluster; + u32 beaconCancelRequests; + u32 beaconCancelFailures; + u32 beaconUpdateRequests; + u32 beaconUpdateFailures; + u32 syncBeaconTxAttempts; + u32 discBeaconTxAttempts; +} NanSyncStats; + +/* NAN Misc DE Statistics */ +typedef struct +{ + u32 validErrorRspMsgs; + u32 validTransmitFollowupReqMsgs; + u32 validTransmitFollowupRspMsgs; + u32 validFollowupIndMsgs; + u32 validConfigurationReqMsgs; + u32 validConfigurationRspMsgs; + u32 validStatsReqMsgs; + u32 validStatsRspMsgs; + u32 validEnableReqMsgs; + u32 validEnableRspMsgs; + u32 validDisableReqMsgs; + u32 validDisableRspMsgs; + u32 validDisableIndMsgs; + u32 validEventIndMsgs; + u32 validTcaReqMsgs; + u32 validTcaRspMsgs; + u32 validTcaIndMsgs; + u32 invalidTransmitFollowupReqMsgs; + u32 invalidConfigurationReqMsgs; + u32 invalidStatsReqMsgs; + u32 invalidEnableReqMsgs; + u32 invalidDisableReqMsgs; + u32 invalidTcaReqMsgs; +} NanDeStats; + +/* + Stats Response Message structure + The Discovery Engine response to a request by the Host for statistics. +*/ +typedef struct { + NanStatsId stats_id; + union { + NanPublishStats publish_stats; + NanSubscribeStats subscribe_stats; + NanMacStats mac_stats; + NanSyncStats sync_stats; + NanDeStats de_stats; + }data; +}NanStatsResponse; + +/* + NAN Response messages +*/ +typedef struct { + NanHeader header; + u16 status; /* contains the result code */ + u16 value; /* For error returns the value is returned which was in error */ + NanResponseType response_type; /* NanResponseType Definitions */ + union { + NanStatsResponse stats_response; + }body; +}NanResponseMsg; + + +/* + Publish Replied Indication + The PublishRepliedInd Message is sent by the DE when an Active Subscribe is + received over the air and it matches a Solicited PublishServiceReq which had + been created with the replied_event_flag set. +*/ +typedef struct { + NanHeader header; + u8 addr[NAN_MAC_ADDR_LEN]; +}NanPublishRepliedInd; + +/* + Publish Terminated + The PublishTerminatedInd message is sent by the DE whenever a Publish + terminates from a user-specified timeout or a unrecoverable error in the DE. +*/ +typedef struct { + NanHeader header; + NanTerminatedStatus reason; +}NanPublishTerminatedInd; + +/* + Subscribe Match Indication + The SubscribeMatchInd message is sent once per responding MAC address whenever + the Discovery Engine detects a match for a previous SubscribeServiceReq. +*/ +typedef struct { + NanHeader header; + + /* a 16 bit Handle which is sent to the Application. This handle will be sent in any subsequent + UnmatchInd messages rather than resending the Match_Filter/Service_Specific_Info TLVs + The Match_Handle is a DE resource and it is of limited quantity. In the event that the DE + runs out of Match_Handles the DE will still send the SubscribeMatchInd message but will + set the Match_Handle to MATCH_HANDLE_MATCH_POOL_EXHAUSTED=0xFFFF + */ + u16 match_handle; + u8 addr[NAN_MAC_ADDR_LEN]; + + /* Sequence of octets which were received in a Discovery Frame matching this + Subscribe Request.*/ + u16 service_specific_info_len; + u8 service_specific_info[NAN_MAX_SERVICE_NAME_LEN]; + + /* Ordered sequence of <length, value> pairs received in the Discovery Frame + matching this Subscribe Request.*/ + u16 sdf_match_filter_len; + u8 sdf_match_filter[NAN_MAX_MATCH_FILTER_LEN]; +}NanSubscribeMatchInd; + +/* + Subscribe UnMatch + The SubscribeUnmatchInd message is sent whenever the Discovery Engine detects that + a previously Matched Subscribed Service Name has been gone for too long. + If the previous SubscribeMatchInd message contained a Match_Handle equal to + MATCH_HANDLE_MATCH_POOL_EXHAUSTED then this message will not be sent to the Host. +*/ +typedef struct { + NanHeader header; + /* 16 bit value sent by the DE in a previous SubscribeMatchInd to the application. */ + u16 match_handle; +}NanSubscribeUnmatchInd; + +/* + Subscribe Terminated + The SubscribeTerminatedInd message is sent by the DE whenever a + Subscribe terminates from a user-specified timeout or a unrecoverable error in the DE. +*/ +typedef struct { + NanHeader header; + NanTerminatedStatus reason; +}NanSubscribeTerminatedInd; + +/* + Followup Indication Message + The FollowupInd message is sent by the DE to the Host whenever it receives a + Followup message from another peer. +*/ +typedef struct { + NanHeader header; + u8 addr[NAN_MAC_ADDR_LEN]; + + /* Flag which the DE uses to decide if received in a DW or a FAW*/ + u8 dw_or_faw; /* 0=Received in a DW, 1 = Received in a FAW*/ + + /* Sequence of values which further specify the published service beyond the service name + Service specific info in case dw_or_faw is set to 0 + Extended service specific info in case dw_or_faw is set to non-zero*/ + u16 service_specific_info_len; + u8 service_specific_info[NAN_MAX_SERVICE_SPECIFIC_INFO_LEN]; +}NanFollowupInd; + +/* Selfstaevent data*/ +typedef struct { + u8 addr[NAN_MAC_ADDR_LEN]; +}NanSelfStaEvent; + +/* joined or Started cluster data*/ +typedef struct { + u8 addr[NAN_MAC_ADDR_LEN]; +}NanClusterEventData; + +/* + Discovery Engine Event Indication + The Discovery Engine can inform the Host when significant events occur + The data following the EventId is dependent upon the EventId type. + In other words, each new event defined will carry a different + structure of information back to the host. +*/ +typedef struct { + NanHeader header; + NanEventId event_id; /* NAN Protocol Event Codes */ + union { + /* SelfStaEvent which will have 6 byte mac address + of the Discovery engine.*/ + NanSelfStaEvent self_sta; + /* Cluster Event Data which will be obtained when the + device starts a new cluster or joins a cluster. + The event data will have 6 byte octet string of the + cluster started or joined.*/ + NanClusterEventData cluster; + }data; +}NanDiscEngEventInd; + +/* Cluster size TCA data*/ +typedef struct { + u16 cluster_size; +}NanTcaClusterData; + +/* + NAN TCA Indication + The Discovery Engine can inform the Host when significant events occur. + The data following the TcaId is dependent upon the TcaId type. + In other words, each new event defined will carry a different structure + of information back to the host. +*/ +typedef struct { + NanHeader header; + NanTcaId tca_id; + union { + /* This event in obtained when the cluser size threshold + is crossed. Event will have the cluster size */ + NanTcaClusterData cluster; + }data; +}NanTCAInd; + +/* + NAN Disabled Indication + The NanDisableInd message indicates to the upper layers that the Discovery + Engine has flushed all state and has been shutdown. When this message is received + the DE is guaranteed to have left the NAN cluster it was part of and will have terminated + any in progress Publishes or Subscribes. +*/ +typedef struct { + NanHeader header; + NanStatusType reason; +}NanDisabledInd; + +/* Response and Event Callbacks */ +typedef struct { + /* NotifyResponse invoked to notify the status of the Request */ + void (*NotifyResponse)(NanResponseMsg* rsp_data); + /* Various Events Callback */ + void (*EventPublishReplied)(NanPublishRepliedInd* event); + void (*EventPublishTerminated)(NanPublishTerminatedInd* event); + void (*EventSubscribeMatch) (NanSubscribeMatchInd* event); + void (*EventSubscribeUnMatch) (NanSubscribeUnmatchInd* event); + void (*EventSubscribeTerminated) (NanSubscribeTerminatedInd* event); + void (*EventFollowup) (NanFollowupInd* event); + void (*EventDiscEngEvent) (NanDiscEngEventInd* event); + void (*EventDisabled) (NanDisabledInd* event); + void (*EventTca) (NanTCAInd* event); +} NanCallbackHandler; + + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_enable_request(wifi_request_id id, + wifi_handle handle, + NanEnableRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_disable_request(wifi_request_id id, + wifi_handle handle, + NanDisableRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_publish_request(wifi_request_id id, + wifi_handle handle, + NanPublishRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_publish_cancel_request(wifi_request_id id, + wifi_handle handle, + NanPublishCancelRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_subscribe_request(wifi_request_id id, + wifi_handle handle, + NanSubscribeRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_subscribe_cancel_request(wifi_request_id id, + wifi_handle handle, + NanSubscribeCancelRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_transmit_followup_request(wifi_request_id id, + wifi_handle handle, + NanTransmitFollowupRequest* msg); + +/* Function to send NAN statistics request to the wifi driver.*/ +wifi_error nan_stats_request(wifi_request_id id, + wifi_handle handle, + NanStatsRequest* msg); + +/* Function to send NAN configuration request to the wifi driver.*/ +wifi_error nan_config_request(wifi_request_id id, + wifi_handle handle, + NanConfigRequest* msg); + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_tca_request(wifi_request_id id, + wifi_handle handle, + NanTCARequest* msg); + +/* Function to register NAN callback */ +wifi_error nan_register_handler(wifi_handle handle, + NanCallbackHandler handlers); + +/* Function to get version of the NAN HAL */ +wifi_error nan_get_version(wifi_handle handle, + NanVersion* version); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __NAN_H__ */
diff --git a/libhardware_legacy/include/hardware_legacy/wifi_offload.h b/libhardware_legacy/include/hardware_legacy/wifi_offload.h new file mode 100644 index 0000000..f0326f1 --- /dev/null +++ b/libhardware_legacy/include/hardware_legacy/wifi_offload.h
@@ -0,0 +1,31 @@ +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_OFFLOAD_H +#define __WIFI_HAL_OFFLOAD_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define ETHER_ADDR_LEN 6 // Ethernet frame address length +#define N_AVAIL_ID 3 // available mkeep_alive IDs from 1 to 3 +#define MKEEP_ALIVE_IP_PKT_MAX 256 // max size of IP packet for keep alive + +/** + * Send specified keep alive packet periodically. + */ +wifi_error wifi_start_sending_offloaded_packet(wifi_request_id id, wifi_interface_handle iface, + u8 *ip_packet, u16 ip_packet_len, u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec); + +/** + * Stop sending keep alive packet. + */ +wifi_error wifi_stop_sending_offloaded_packet(wifi_request_id id, wifi_interface_handle iface); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*__WIFI_HAL_OFFLOAD_H */
diff --git a/libhardware_legacy/power/Android.mk b/libhardware_legacy/power/Android.mk new file mode 100644 index 0000000..3e3ff5d --- /dev/null +++ b/libhardware_legacy/power/Android.mk
@@ -0,0 +1,3 @@ +# Copyright 2006 The Android Open Source Project + +LOCAL_SRC_FILES += power/power.c
diff --git a/libhardware_legacy/power/power.c b/libhardware_legacy/power/power.c new file mode 100644 index 0000000..368de5b --- /dev/null +++ b/libhardware_legacy/power/power.c
@@ -0,0 +1,125 @@ +/* + * Copyright (C) 2008 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 <hardware_legacy/power.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <pthread.h> + +#define LOG_TAG "power" +#include <utils/Log.h> + +enum { + ACQUIRE_PARTIAL_WAKE_LOCK = 0, + RELEASE_WAKE_LOCK, + OUR_FD_COUNT +}; + +const char * const OLD_PATHS[] = { + "/sys/android_power/acquire_partial_wake_lock", + "/sys/android_power/release_wake_lock", +}; + +const char * const NEW_PATHS[] = { + "/sys/power/wake_lock", + "/sys/power/wake_unlock", +}; + +//XXX static pthread_once_t g_initialized = THREAD_ONCE_INIT; +static int g_initialized = 0; +static int g_fds[OUR_FD_COUNT]; +static int g_error = -1; + +static int +open_file_descriptors(const char * const paths[]) +{ + int i; + for (i=0; i<OUR_FD_COUNT; i++) { + int fd = open(paths[i], O_RDWR | O_CLOEXEC); + if (fd < 0) { + g_error = -errno; + fprintf(stderr, "fatal error opening \"%s\": %s\n", paths[i], + strerror(errno)); + return -1; + } + g_fds[i] = fd; + } + + g_error = 0; + return 0; +} + +static inline void +initialize_fds(void) +{ + // XXX: should be this: + //pthread_once(&g_initialized, open_file_descriptors); + // XXX: not this: + if (g_initialized == 0) { + if(open_file_descriptors(NEW_PATHS) < 0) + open_file_descriptors(OLD_PATHS); + g_initialized = 1; + } +} + +int +acquire_wake_lock(int lock, const char* id) +{ + initialize_fds(); + +// ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id); + + if (g_error) return g_error; + + int fd; + size_t len; + ssize_t ret; + + if (lock != PARTIAL_WAKE_LOCK) { + return -EINVAL; + } + + fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK]; + + ret = write(fd, id, strlen(id)); + if (ret < 0) { + return -errno; + } + + return ret; +} + +int +release_wake_lock(const char* id) +{ + initialize_fds(); + +// ALOGI("release_wake_lock id='%s'\n", id); + + if (g_error) return g_error; + + ssize_t len = write(g_fds[RELEASE_WAKE_LOCK], id, strlen(id)); + if (len < 0) { + return -errno; + } + return len; +}
diff --git a/libhardware_legacy/uevent/Android.mk b/libhardware_legacy/uevent/Android.mk new file mode 100644 index 0000000..2d8b524 --- /dev/null +++ b/libhardware_legacy/uevent/Android.mk
@@ -0,0 +1,3 @@ +# Copyright 2008 The Android Open Source Project + +LOCAL_SRC_FILES += uevent/uevent.c
diff --git a/libhardware_legacy/uevent/uevent.c b/libhardware_legacy/uevent/uevent.c new file mode 100644 index 0000000..e40aa2e --- /dev/null +++ b/libhardware_legacy/uevent/uevent.c
@@ -0,0 +1,137 @@ +/* + * Copyright (C) 2008 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 <hardware_legacy/uevent.h> + +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <poll.h> +#include <pthread.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/queue.h> +#include <linux/netlink.h> + + +LIST_HEAD(uevent_handler_head, uevent_handler) uevent_handler_list; +pthread_mutex_t uevent_handler_list_lock = PTHREAD_MUTEX_INITIALIZER; + +struct uevent_handler { + void (*handler)(void *data, const char *msg, int msg_len); + void *handler_data; + LIST_ENTRY(uevent_handler) list; +}; + +static int fd = -1; + +/* Returns 0 on failure, 1 on success */ +int uevent_init() +{ + struct sockaddr_nl addr; + int sz = 64*1024; + int s; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(s < 0) + return 0; + + setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(s); + return 0; + } + + fd = s; + return (fd > 0); +} + +int uevent_get_fd() +{ + return fd; +} + +int uevent_next_event(char* buffer, int buffer_length) +{ + while (1) { + struct pollfd fds; + int nr; + + fds.fd = fd; + fds.events = POLLIN; + fds.revents = 0; + nr = poll(&fds, 1, -1); + + if(nr > 0 && (fds.revents & POLLIN)) { + int count = recv(fd, buffer, buffer_length, 0); + if (count > 0) { + struct uevent_handler *h; + pthread_mutex_lock(&uevent_handler_list_lock); + LIST_FOREACH(h, &uevent_handler_list, list) + h->handler(h->handler_data, buffer, buffer_length); + pthread_mutex_unlock(&uevent_handler_list_lock); + + return count; + } + } + } + + // won't get here + return 0; +} + +int uevent_add_native_handler(void (*handler)(void *data, const char *msg, int msg_len), + void *handler_data) +{ + struct uevent_handler *h; + + h = malloc(sizeof(struct uevent_handler)); + if (h == NULL) + return -1; + h->handler = handler; + h->handler_data = handler_data; + + pthread_mutex_lock(&uevent_handler_list_lock); + LIST_INSERT_HEAD(&uevent_handler_list, h, list); + pthread_mutex_unlock(&uevent_handler_list_lock); + + return 0; +} + +int uevent_remove_native_handler(void (*handler)(void *data, const char *msg, int msg_len)) +{ + struct uevent_handler *h; + int err = -1; + + pthread_mutex_lock(&uevent_handler_list_lock); + LIST_FOREACH(h, &uevent_handler_list, list) { + if (h->handler == handler) { + LIST_REMOVE(h, list); + err = 0; + break; + } + } + pthread_mutex_unlock(&uevent_handler_list_lock); + + return err; +}
diff --git a/libhardware_legacy/wifi/Android.mk b/libhardware_legacy/wifi/Android.mk new file mode 100644 index 0000000..ed59b40 --- /dev/null +++ b/libhardware_legacy/wifi/Android.mk
@@ -0,0 +1,44 @@ +# Copyright 2006 The Android Open Source Project + +ifdef WIFI_DRIVER_MODULE_PATH +LOCAL_CFLAGS += -DWIFI_DRIVER_MODULE_PATH=\"$(WIFI_DRIVER_MODULE_PATH)\" +endif +ifdef WIFI_DRIVER_MODULE_ARG +LOCAL_CFLAGS += -DWIFI_DRIVER_MODULE_ARG=\"$(WIFI_DRIVER_MODULE_ARG)\" +endif +ifdef WIFI_DRIVER_MODULE_NAME +LOCAL_CFLAGS += -DWIFI_DRIVER_MODULE_NAME=\"$(WIFI_DRIVER_MODULE_NAME)\" +endif +ifdef WIFI_FIRMWARE_LOADER +LOCAL_CFLAGS += -DWIFI_FIRMWARE_LOADER=\"$(WIFI_FIRMWARE_LOADER)\" +endif +ifdef WIFI_DRIVER_FW_PATH_STA +LOCAL_CFLAGS += -DWIFI_DRIVER_FW_PATH_STA=\"$(WIFI_DRIVER_FW_PATH_STA)\" +endif +ifdef WIFI_DRIVER_FW_PATH_AP +LOCAL_CFLAGS += -DWIFI_DRIVER_FW_PATH_AP=\"$(WIFI_DRIVER_FW_PATH_AP)\" +endif +ifdef WIFI_DRIVER_FW_PATH_P2P +LOCAL_CFLAGS += -DWIFI_DRIVER_FW_PATH_P2P=\"$(WIFI_DRIVER_FW_PATH_P2P)\" +endif +ifdef WIFI_DRIVER_FW_PATH_PARAM +LOCAL_CFLAGS += -DWIFI_DRIVER_FW_PATH_PARAM=\"$(WIFI_DRIVER_FW_PATH_PARAM)\" +endif + +ifdef WIFI_DRIVER_STATE_CTRL_PARAM +LOCAL_CFLAGS += -DWIFI_DRIVER_STATE_CTRL_PARAM=\"$(WIFI_DRIVER_STATE_CTRL_PARAM)\" +endif +ifdef WIFI_DRIVER_STATE_ON +LOCAL_CFLAGS += -DWIFI_DRIVER_STATE_ON=\"$(WIFI_DRIVER_STATE_ON)\" +endif +ifdef WIFI_DRIVER_STATE_OFF +LOCAL_CFLAGS += -DWIFI_DRIVER_STATE_OFF=\"$(WIFI_DRIVER_STATE_OFF)\" +endif + +LOCAL_SRC_FILES += wifi/wifi.c + +ifdef WPA_SUPPLICANT_VERSION +LOCAL_CFLAGS += -DLIBWPA_CLIENT_EXISTS +LOCAL_SHARED_LIBRARIES += libwpa_client +endif +LOCAL_SHARED_LIBRARIES += libnetutils
diff --git a/libhardware_legacy/wifi/wifi.c b/libhardware_legacy/wifi/wifi.c new file mode 100644 index 0000000..880f6fc --- /dev/null +++ b/libhardware_legacy/wifi/wifi.c
@@ -0,0 +1,834 @@ +/* + * Copyright 2008, 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 <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <unistd.h> +#include <poll.h> + +#include "hardware_legacy/wifi.h" +#ifdef LIBWPA_CLIENT_EXISTS +#include "libwpa_client/wpa_ctrl.h" +#endif + +#define LOG_TAG "WifiHW" +#include "cutils/log.h" +#include "cutils/memory.h" +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "private/android_filesystem_config.h" + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + +extern int do_dhcp(); +extern int ifc_init(); +extern void ifc_close(); +extern char *dhcp_lasterror(); +extern void get_dhcp_info(); +extern int init_module(void *, unsigned long, const char *); +extern int delete_module(const char *, unsigned int); +void wifi_close_sockets(); + +#ifndef LIBWPA_CLIENT_EXISTS +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +struct wpa_ctrl {}; +void wpa_ctrl_cleanup(void) {} +struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path) { return NULL; } +void wpa_ctrl_close(struct wpa_ctrl *ctrl) {} +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) + { return 0; } +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return 0; } +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return 0; } +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) + { return 0; } +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return 0; } +#endif + +static struct wpa_ctrl *ctrl_conn; +static struct wpa_ctrl *monitor_conn; + +/* socket pair used to exit from a blocking read */ +static int exit_sockets[2]; + +static char primary_iface[PROPERTY_VALUE_MAX]; +// TODO: use new ANDROID_SOCKET mechanism, once support for multiple +// sockets is in + +#ifndef WIFI_DRIVER_MODULE_ARG +#define WIFI_DRIVER_MODULE_ARG "" +#endif +#ifndef WIFI_FIRMWARE_LOADER +#define WIFI_FIRMWARE_LOADER "" +#endif +#define WIFI_TEST_INTERFACE "sta" + +#ifndef WIFI_DRIVER_FW_PATH_STA +#define WIFI_DRIVER_FW_PATH_STA NULL +#endif +#ifndef WIFI_DRIVER_FW_PATH_AP +#define WIFI_DRIVER_FW_PATH_AP NULL +#endif +#ifndef WIFI_DRIVER_FW_PATH_P2P +#define WIFI_DRIVER_FW_PATH_P2P NULL +#endif + +#ifndef WIFI_DRIVER_FW_PATH_PARAM +#define WIFI_DRIVER_FW_PATH_PARAM "/sys/module/wlan/parameters/fwpath" +#endif + +#define WIFI_DRIVER_LOADER_DELAY 1000000 + +static const char IFACE_DIR[] = "/data/system/wpa_supplicant"; +#ifdef WIFI_DRIVER_MODULE_PATH +static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME; +static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " "; +static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH; +static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG; +#endif +static const char FIRMWARE_LOADER[] = WIFI_FIRMWARE_LOADER; +static const char DRIVER_PROP_NAME[] = "wlan.driver.status"; +static const char SUPPLICANT_NAME[] = "wpa_supplicant"; +static const char SUPP_PROP_NAME[] = "init.svc.wpa_supplicant"; +static const char P2P_SUPPLICANT_NAME[] = "p2p_supplicant"; +static const char P2P_PROP_NAME[] = "init.svc.p2p_supplicant"; +static const char SUPP_CONFIG_TEMPLATE[]= "/system/etc/wifi/wpa_supplicant.conf"; +static const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf"; +static const char P2P_CONFIG_FILE[] = "/data/misc/wifi/p2p_supplicant.conf"; +static const char CONTROL_IFACE_PATH[] = "/data/misc/wifi/sockets"; +static const char MODULE_FILE[] = "/proc/modules"; + +static const char IFNAME[] = "IFNAME="; +#define IFNAMELEN (sizeof(IFNAME) - 1) +static const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE "; + +static const char SUPP_ENTROPY_FILE[] = WIFI_ENTROPY_FILE; +static unsigned char dummy_key[21] = { 0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, + 0x68, 0x47, 0x84, 0x99, 0xa9, 0x2b, + 0x1c, 0xd3, 0xee, 0xff, 0xf1, 0xe2, + 0xf3, 0xf4, 0xf5 }; + +/* Is either SUPPLICANT_NAME or P2P_SUPPLICANT_NAME */ +static char supplicant_name[PROPERTY_VALUE_MAX]; +/* Is either SUPP_PROP_NAME or P2P_PROP_NAME */ +static char supplicant_prop_name[PROPERTY_KEY_MAX]; + +static int insmod(const char *filename, const char *args) +{ + void *module; + unsigned int size; + int ret; + + module = load_file(filename, &size); + if (!module) + return -1; + + ret = init_module(module, size, args); + + free(module); + + return ret; +} + +static int rmmod(const char *modname) +{ + int ret = -1; + int maxtry = 10; + + while (maxtry-- > 0) { + ret = delete_module(modname, O_NONBLOCK | O_EXCL); + if (ret < 0 && errno == EAGAIN) + usleep(500000); + else + break; + } + + if (ret != 0) + ALOGD("Unable to unload driver module \"%s\": %s\n", + modname, strerror(errno)); + return ret; +} + +int do_dhcp_request(int *ipaddr, int *gateway, int *mask, + int *dns1, int *dns2, int *server, int *lease) { + /* For test driver, always report success */ + if (strcmp(primary_iface, WIFI_TEST_INTERFACE) == 0) + return 0; + + if (ifc_init() < 0) + return -1; + + if (do_dhcp(primary_iface) < 0) { + ifc_close(); + return -1; + } + ifc_close(); + get_dhcp_info(ipaddr, gateway, mask, dns1, dns2, server, lease); + return 0; +} + +const char *get_dhcp_error_string() { + return dhcp_lasterror(); +} + +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM +int wifi_change_driver_state(const char *state) +{ + int len; + int fd; + int ret = 0; + + if (!state) + return -1; + fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY)); + if (fd < 0) { + ALOGE("Failed to open driver state control param (%s)", strerror(errno)); + return -1; + } + len = strlen(state) + 1; + if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) { + ALOGE("Failed to write driver state control param (%s)", strerror(errno)); + ret = -1; + } + close(fd); + return ret; +} +#endif + +int is_wifi_driver_loaded() { + char driver_status[PROPERTY_VALUE_MAX]; +#ifdef WIFI_DRIVER_MODULE_PATH + FILE *proc; + char line[sizeof(DRIVER_MODULE_TAG)+10]; +#endif + + if (!property_get(DRIVER_PROP_NAME, driver_status, NULL) + || strcmp(driver_status, "ok") != 0) { + return 0; /* driver not loaded */ + } +#ifdef WIFI_DRIVER_MODULE_PATH + /* + * If the property says the driver is loaded, check to + * make sure that the property setting isn't just left + * over from a previous manual shutdown or a runtime + * crash. + */ + if ((proc = fopen(MODULE_FILE, "r")) == NULL) { + ALOGW("Could not open %s: %s", MODULE_FILE, strerror(errno)); + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; + } + while ((fgets(line, sizeof(line), proc)) != NULL) { + if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) { + fclose(proc); + return 1; + } + } + fclose(proc); + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; +#else + return 1; +#endif +} + +int wifi_load_driver() +{ +#ifdef WIFI_DRIVER_MODULE_PATH + char driver_status[PROPERTY_VALUE_MAX]; + int count = 100; /* wait at most 20 seconds for completion */ + + if (is_wifi_driver_loaded()) { + return 0; + } + + if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) + return -1; + + if (strcmp(FIRMWARE_LOADER,"") == 0) { + /* usleep(WIFI_DRIVER_LOADER_DELAY); */ + property_set(DRIVER_PROP_NAME, "ok"); + } + else { + property_set("ctl.start", FIRMWARE_LOADER); + } + sched_yield(); + while (count-- > 0) { + if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) { + if (strcmp(driver_status, "ok") == 0) + return 0; + else if (strcmp(driver_status, "failed") == 0) { + wifi_unload_driver(); + return -1; + } + } + usleep(200000); + } + property_set(DRIVER_PROP_NAME, "timeout"); + wifi_unload_driver(); + return -1; +#else +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM + if (is_wifi_driver_loaded()) { + return 0; + } + + if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) + return -1; +#endif + property_set(DRIVER_PROP_NAME, "ok"); + return 0; +#endif +} + +int wifi_unload_driver() +{ + usleep(200000); /* allow to finish interface down */ +#ifdef WIFI_DRIVER_MODULE_PATH + if (rmmod(DRIVER_MODULE_NAME) == 0) { + int count = 20; /* wait at most 10 seconds for completion */ + while (count-- > 0) { + if (!is_wifi_driver_loaded()) + break; + usleep(500000); + } + usleep(500000); /* allow card removal */ + if (count) { + return 0; + } + return -1; + } else + return -1; +#else +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM + if (is_wifi_driver_loaded()) { + if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) + return -1; + } +#endif + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; +#endif +} + +int ensure_entropy_file_exists() +{ + int ret; + int destfd; + + ret = access(SUPP_ENTROPY_FILE, R_OK|W_OK); + if ((ret == 0) || (errno == EACCES)) { + if ((ret != 0) && + (chmod(SUPP_ENTROPY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) { + ALOGE("Cannot set RW to \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + return -1; + } + return 0; + } + destfd = TEMP_FAILURE_RETRY(open(SUPP_ENTROPY_FILE, O_CREAT|O_RDWR, 0660)); + if (destfd < 0) { + ALOGE("Cannot create \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + return -1; + } + + if (TEMP_FAILURE_RETRY(write(destfd, dummy_key, sizeof(dummy_key))) != sizeof(dummy_key)) { + ALOGE("Error writing \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + close(destfd); + return -1; + } + close(destfd); + + /* chmod is needed because open() didn't set permisions properly */ + if (chmod(SUPP_ENTROPY_FILE, 0660) < 0) { + ALOGE("Error changing permissions of %s to 0660: %s", + SUPP_ENTROPY_FILE, strerror(errno)); + unlink(SUPP_ENTROPY_FILE); + return -1; + } + + if (chown(SUPP_ENTROPY_FILE, AID_SYSTEM, AID_WIFI) < 0) { + ALOGE("Error changing group ownership of %s to %d: %s", + SUPP_ENTROPY_FILE, AID_WIFI, strerror(errno)); + unlink(SUPP_ENTROPY_FILE); + return -1; + } + return 0; +} + +int ensure_config_file_exists(const char *config_file) +{ + char buf[2048]; + int srcfd, destfd; + struct stat sb; + int nread; + int ret; + + ret = access(config_file, R_OK|W_OK); + if ((ret == 0) || (errno == EACCES)) { + if ((ret != 0) && + (chmod(config_file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) { + ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno)); + return -1; + } + return 0; + } else if (errno != ENOENT) { + ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno)); + return -1; + } + + srcfd = TEMP_FAILURE_RETRY(open(SUPP_CONFIG_TEMPLATE, O_RDONLY)); + if (srcfd < 0) { + ALOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); + return -1; + } + + destfd = TEMP_FAILURE_RETRY(open(config_file, O_CREAT|O_RDWR, 0660)); + if (destfd < 0) { + close(srcfd); + ALOGE("Cannot create \"%s\": %s", config_file, strerror(errno)); + return -1; + } + + while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) { + if (nread < 0) { + ALOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); + close(srcfd); + close(destfd); + unlink(config_file); + return -1; + } + TEMP_FAILURE_RETRY(write(destfd, buf, nread)); + } + + close(destfd); + close(srcfd); + + /* chmod is needed because open() didn't set permisions properly */ + if (chmod(config_file, 0660) < 0) { + ALOGE("Error changing permissions of %s to 0660: %s", + config_file, strerror(errno)); + unlink(config_file); + return -1; + } + + if (chown(config_file, AID_SYSTEM, AID_WIFI) < 0) { + ALOGE("Error changing group ownership of %s to %d: %s", + config_file, AID_WIFI, strerror(errno)); + unlink(config_file); + return -1; + } + return 0; +} + +int wifi_start_supplicant(int p2p_supported) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 200; /* wait at most 20 seconds for completion */ + const prop_info *pi; + unsigned serial = 0, i; + + if (p2p_supported) { + strcpy(supplicant_name, P2P_SUPPLICANT_NAME); + strcpy(supplicant_prop_name, P2P_PROP_NAME); + + /* Ensure p2p config file is created */ + if (ensure_config_file_exists(P2P_CONFIG_FILE) < 0) { + ALOGE("Failed to create a p2p config file"); + return -1; + } + + } else { + strcpy(supplicant_name, SUPPLICANT_NAME); + strcpy(supplicant_prop_name, SUPP_PROP_NAME); + } + + /* Check whether already running */ + if (property_get(supplicant_prop_name, supp_status, NULL) + && strcmp(supp_status, "running") == 0) { + return 0; + } + + /* Before starting the daemon, make sure its config file exists */ + if (ensure_config_file_exists(SUPP_CONFIG_FILE) < 0) { + ALOGE("Wi-Fi will not be enabled"); + return -1; + } + + if (ensure_entropy_file_exists() < 0) { + ALOGE("Wi-Fi entropy file was not created"); + } + + /* Clear out any stale socket files that might be left over. */ + wpa_ctrl_cleanup(); + + /* Reset sockets used for exiting from hung state */ + exit_sockets[0] = exit_sockets[1] = -1; + + /* + * Get a reference to the status property, so we can distinguish + * the case where it goes stopped => running => stopped (i.e., + * it start up, but fails right away) from the case in which + * it starts in the stopped state and never manages to start + * running at all. + */ + pi = __system_property_find(supplicant_prop_name); + if (pi != NULL) { + serial = __system_property_serial(pi); + } + property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE); + + property_set("ctl.start", supplicant_name); + sched_yield(); + + while (count-- > 0) { + if (pi == NULL) { + pi = __system_property_find(supplicant_prop_name); + } + if (pi != NULL) { + /* + * property serial updated means that init process is scheduled + * after we sched_yield, further property status checking is based on this */ + if (__system_property_serial(pi) != serial) { + __system_property_read(pi, NULL, supp_status); + if (strcmp(supp_status, "running") == 0) { + return 0; + } else if (strcmp(supp_status, "stopped") == 0) { + return -1; + } + } + } + usleep(100000); + } + return -1; +} + +int wifi_stop_supplicant(int p2p_supported) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 50; /* wait at most 5 seconds for completion */ + + if (p2p_supported) { + strcpy(supplicant_name, P2P_SUPPLICANT_NAME); + strcpy(supplicant_prop_name, P2P_PROP_NAME); + } else { + strcpy(supplicant_name, SUPPLICANT_NAME); + strcpy(supplicant_prop_name, SUPP_PROP_NAME); + } + + /* Check whether supplicant already stopped */ + if (property_get(supplicant_prop_name, supp_status, NULL) + && strcmp(supp_status, "stopped") == 0) { + return 0; + } + + property_set("ctl.stop", supplicant_name); + sched_yield(); + + while (count-- > 0) { + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return 0; + } + usleep(100000); + } + ALOGE("Failed to stop supplicant"); + return -1; +} + +int wifi_connect_on_socket_path(const char *path) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + /* Make sure supplicant is running */ + if (!property_get(supplicant_prop_name, supp_status, NULL) + || strcmp(supp_status, "running") != 0) { + ALOGE("Supplicant not running, cannot connect"); + return -1; + } + + ctrl_conn = wpa_ctrl_open(path); + if (ctrl_conn == NULL) { + ALOGE("Unable to open connection to supplicant on \"%s\": %s", + path, strerror(errno)); + return -1; + } + monitor_conn = wpa_ctrl_open(path); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + if (wpa_ctrl_attach(monitor_conn) != 0) { + wpa_ctrl_close(monitor_conn); + wpa_ctrl_close(ctrl_conn); + ctrl_conn = monitor_conn = NULL; + return -1; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) { + wpa_ctrl_close(monitor_conn); + wpa_ctrl_close(ctrl_conn); + ctrl_conn = monitor_conn = NULL; + return -1; + } + + return 0; +} + +/* Establishes the control and monitor socket connections on the interface */ +int wifi_connect_to_supplicant() +{ + static char path[PATH_MAX]; + + if (access(IFACE_DIR, F_OK) == 0) { + snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface); + } else { + snprintf(path, sizeof(path), "@android:wpa_%s", primary_iface); + } + return wifi_connect_on_socket_path(path); +} + +int wifi_send_command(const char *cmd, char *reply, size_t *reply_len) +{ + int ret; + if (ctrl_conn == NULL) { + ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd); + return -1; + } + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL); + if (ret == -2) { + ALOGD("'%s' command timed out.\n", cmd); + /* unblocks the monitor receive socket for termination */ + TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1)); + return -2; + } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) { + return -1; + } + if (strncmp(cmd, "PING", 4) == 0) { + reply[*reply_len] = '\0'; + } + return 0; +} + +int wifi_supplicant_connection_active() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return -1; + } + + return 0; +} + +int wifi_ctrl_recv(char *reply, size_t *reply_len) +{ + int res; + int ctrlfd = wpa_ctrl_get_fd(monitor_conn); + struct pollfd rfds[2]; + + memset(rfds, 0, 2 * sizeof(struct pollfd)); + rfds[0].fd = ctrlfd; + rfds[0].events |= POLLIN; + rfds[1].fd = exit_sockets[1]; + rfds[1].events |= POLLIN; + do { + res = TEMP_FAILURE_RETRY(poll(rfds, 2, 30000)); + if (res < 0) { + ALOGE("Error poll = %d", res); + return res; + } else if (res == 0) { + /* timed out, check if supplicant is active + * or not .. + */ + res = wifi_supplicant_connection_active(); + if (res < 0) + return -2; + } + } while (res == 0); + + if (rfds[0].revents & POLLIN) { + return wpa_ctrl_recv(monitor_conn, reply, reply_len); + } + + /* it is not rfds[0], then it must be rfts[1] (i.e. the exit socket) + * or we timed out. In either case, this call has failed .. + */ + return -2; +} + +int wifi_wait_on_socket(char *buf, size_t buflen) +{ + size_t nread = buflen - 1; + int result; + char *match, *match2; + + if (monitor_conn == NULL) { + return snprintf(buf, buflen, "IFNAME=%s %s - connection closed", + primary_iface, WPA_EVENT_TERMINATING); + } + + result = wifi_ctrl_recv(buf, &nread); + + /* Terminate reception on exit socket */ + if (result == -2) { + return snprintf(buf, buflen, "IFNAME=%s %s - connection closed", + primary_iface, WPA_EVENT_TERMINATING); + } + + if (result < 0) { + ALOGD("wifi_ctrl_recv failed: %s\n", strerror(errno)); + return snprintf(buf, buflen, "IFNAME=%s %s - recv error", + primary_iface, WPA_EVENT_TERMINATING); + } + buf[nread] = '\0'; + /* Check for EOF on the socket */ + if (result == 0 && nread == 0) { + /* Fabricate an event to pass up */ + ALOGD("Received EOF on supplicant socket\n"); + return snprintf(buf, buflen, "IFNAME=%s %s - signal 0 received", + primary_iface, WPA_EVENT_TERMINATING); + } + /* + * Events strings are in the format + * + * IFNAME=iface <N>CTRL-EVENT-XXX + * or + * <N>CTRL-EVENT-XXX + * + * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG, + * etc.) and XXX is the event name. The level information is not useful + * to us, so strip it off. + */ + + if (strncmp(buf, IFNAME, IFNAMELEN) == 0) { + match = strchr(buf, ' '); + if (match != NULL) { + if (match[1] == '<') { + match2 = strchr(match + 2, '>'); + if (match2 != NULL) { + nread -= (match2 - match); + memmove(match + 1, match2 + 1, nread - (match - buf) + 1); + } + } + } else { + return snprintf(buf, buflen, "%s", WPA_EVENT_IGNORE); + } + } else if (buf[0] == '<') { + match = strchr(buf, '>'); + if (match != NULL) { + nread -= (match + 1 - buf); + memmove(buf, match + 1, nread + 1); + ALOGV("supplicant generated event without interface - %s\n", buf); + } + } else { + /* let the event go as is! */ + ALOGW("supplicant generated event without interface and without message level - %s\n", buf); + } + + return nread; +} + +int wifi_wait_for_event(char *buf, size_t buflen) +{ + return wifi_wait_on_socket(buf, buflen); +} + +void wifi_close_sockets() +{ + if (ctrl_conn != NULL) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn != NULL) { + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + if (exit_sockets[0] >= 0) { + close(exit_sockets[0]); + exit_sockets[0] = -1; + } + + if (exit_sockets[1] >= 0) { + close(exit_sockets[1]); + exit_sockets[1] = -1; + } +} + +void wifi_close_supplicant_connection() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 50; /* wait at most 5 seconds to ensure init has stopped stupplicant */ + + wifi_close_sockets(); + + while (count-- > 0) { + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return; + } + usleep(100000); + } +} + +int wifi_command(const char *command, char *reply, size_t *reply_len) +{ + return wifi_send_command(command, reply, reply_len); +} + +const char *wifi_get_fw_path(int fw_type) +{ + switch (fw_type) { + case WIFI_GET_FW_PATH_STA: + return WIFI_DRIVER_FW_PATH_STA; + case WIFI_GET_FW_PATH_AP: + return WIFI_DRIVER_FW_PATH_AP; + case WIFI_GET_FW_PATH_P2P: + return WIFI_DRIVER_FW_PATH_P2P; + } + return NULL; +} + +int wifi_change_fw_path(const char *fwpath) +{ + int len; + int fd; + int ret = 0; + + if (!fwpath) + return ret; + fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY)); + if (fd < 0) { + ALOGE("Failed to open wlan fw path param (%s)", strerror(errno)); + return -1; + } + len = strlen(fwpath) + 1; + if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) { + ALOGE("Failed to write wlan fw path param (%s)", strerror(errno)); + ret = -1; + } + close(fd); + return ret; +}