diff options
author | Joonas Kylmälä <joonas.kylmala@iki.fi> | 2020-04-10 10:41:00 -0400 |
---|---|---|
committer | Joonas Kylmälä <joonas.kylmala@iki.fi> | 2020-04-10 10:41:00 -0400 |
commit | 697e4dd0e6b483da2e7b17e1a82b0b47104dcc7f (patch) | |
tree | 5ccd90eed0e8980c65588ab0aae51a1eaab9a629 | |
download | device_samsung_midas_common-697e4dd0e6b483da2e7b17e1a82b0b47104dcc7f.tar.gz device_samsung_midas_common-697e4dd0e6b483da2e7b17e1a82b0b47104dcc7f.tar.bz2 device_samsung_midas_common-697e4dd0e6b483da2e7b17e1a82b0b47104dcc7f.zip |
Initial commit
Signed-off-by: Joonas Kylmälä <joonas.kylmala@iki.fi>
-rw-r--r-- | BoardConfigCommon.mk | 61 | ||||
-rw-r--r-- | LICENSE | 202 | ||||
-rw-r--r-- | audio/Android.mk | 58 | ||||
-rw-r--r-- | audio/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | audio/NOTICE | 190 | ||||
-rw-r--r-- | audio/audio_hw.c | 1674 | ||||
-rw-r--r-- | audio/audio_hw_legacy.c | 714 | ||||
-rw-r--r-- | gatekeeper/Android.mk | 45 | ||||
-rw-r--r-- | gatekeeper/SoftGateKeeper.h | 182 | ||||
-rw-r--r-- | gatekeeper/SoftGateKeeperDevice.cpp | 116 | ||||
-rw-r--r-- | gatekeeper/SoftGateKeeperDevice.h | 76 | ||||
-rw-r--r-- | gatekeeper/module.cpp | 127 | ||||
-rw-r--r-- | hwcomposer/Android.mk | 68 | ||||
-rw-r--r-- | hwcomposer/hwcomposer.cpp | 362 |
14 files changed, 3875 insertions, 0 deletions
diff --git a/BoardConfigCommon.mk b/BoardConfigCommon.mk new file mode 100644 index 0000000..2245b7d --- /dev/null +++ b/BoardConfigCommon.mk @@ -0,0 +1,61 @@ +# +# Copyright 2018 Joonas Kylmälä +# +# 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. +# + +TARGET_ARCH := arm +TARGET_ARCH_VARIANT := armv7-a-neon + +TARGET_CPU_ABI := armeabi-v7a +TARGET_CPU_ABI2 := armeabi +TARGET_CPU_VARIANT := cortex-a9 + +TARGET_BOARD_PLATFORM := exynos4 +TARGET_BOOTLOADER_BOARD_NAME := smdk4x12 + +TARGET_NO_BOOTLOADER := true + +BOARD_VENDOR := samsung + +# Kernel +# To append the dtb to the zImage: +# - Use BOARD_DTB_IMAGE_NAME with the right dtb +# - Make sure that your kernel source doesn't have +# any Android patches that would add the zImage-dtb +# target. +# The vendor/lineage/build/tasks/kernel.mk file +# was modified to add support for that feature. +TARGET_KERNEL_SOURCE := kernel/replicant/linux +TARGET_KERNEL_CONFIG := replicant_defconfig +BOARD_KERNEL_IMAGE_NAME := zImage-dtb + +BOARD_USES_FULL_RECOVERY_IMAGE := false +BOARD_USES_RECOVERY_AS_BOOT := false + +TARGET_USERIMAGES_USE_EXT4 := true +TARGET_USES_MKE2FS := true + +# Disable VNDK at this point +PRODUCT_FULL_TREBLE_OVERRIDE := true +BOARD_VNDK_RUNTIME_DISABLE := true +PRODUCT_USE_VNDK_OVERRIDE := false +#BOARD_VNDK_VERSION := current +BOARD_GPU_DRIVERS := swrast + +TARGET_USES_64_BIT_BINDER := true +BOARD_BUILD_SYSTEM_ROOT_IMAGE := true + +BOARD_USES_GENERIC_AUDIO := true + +USE_XML_AUDIO_POLICY_CONF := 1 @@ -0,0 +1,202 @@ + + 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/audio/Android.mk b/audio/Android.mk new file mode 100644 index 0000000..7db7659 --- /dev/null +++ b/audio/Android.mk @@ -0,0 +1,58 @@ +# +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE := audio.primary.i9305 +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libcutils liblog + +LOCAL_SRC_FILES := audio_hw.c + +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + +LOCAL_SHARED_LIBRARIES += \ + libdl \ + libtinyalsa + +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_HEADER_LIBRARIES := libhardware_headers + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE := audio.primary.i9305_legacy +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libcutils liblog + +LOCAL_SRC_FILES := audio_hw_legacy.c + +LOCAL_SHARED_LIBRARIES += \ + libdl + +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_HEADER_LIBRARIES := libhardware_headers + +include $(BUILD_SHARED_LIBRARY) diff --git a/audio/MODULE_LICENSE_APACHE2 b/audio/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/audio/MODULE_LICENSE_APACHE2 diff --git a/audio/NOTICE b/audio/NOTICE new file mode 100644 index 0000000..3237da6 --- /dev/null +++ b/audio/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2008-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. + + 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/audio/audio_hw.c b/audio/audio_hw.c new file mode 100644 index 0000000..62a2daa --- /dev/null +++ b/audio/audio_hw.c @@ -0,0 +1,1674 @@ +/* + * 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. + */ + +#define LOG_TAG "audio_hw_generic" + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/time.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> + +#include <log/log.h> +#include <cutils/str_parms.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <hardware/audio.h> +#include <tinyalsa/asoundlib.h> + +#define PCM_CARD 0 +#define PCM_DEVICE 0 + + +#define OUT_PERIOD_MS 15 +#define OUT_PERIOD_COUNT 4 + +#define IN_PERIOD_MS 15 +#define IN_PERIOD_COUNT 4 + +struct generic_audio_device { + struct audio_hw_device device; // Constant after init + pthread_mutex_t lock; + bool mic_mute; // Proteced by this->lock + struct mixer* mixer; // Proteced by this->lock +}; + +/* If not NULL, this is a pointer to the fallback module. + * This really is the original default audio device /dev/eac which we will use + * if no alsa devices are detected. + */ +static struct audio_module* sFallback; +static pthread_once_t sFallbackOnce = PTHREAD_ONCE_INIT; +static void fallback_init(void); +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state); +static int adev_get_microphones(const audio_hw_device_t *dev, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count); + + +typedef struct audio_vbuffer { + pthread_mutex_t lock; + uint8_t * data; + size_t frame_size; + size_t frame_count; + size_t head; + size_t tail; + size_t live; +} audio_vbuffer_t; + +static int audio_vbuffer_init (audio_vbuffer_t * audio_vbuffer, size_t frame_count, + size_t frame_size) { + if (!audio_vbuffer) { + return -EINVAL; + } + audio_vbuffer->frame_size = frame_size; + audio_vbuffer->frame_count = frame_count; + size_t bytes = frame_count * frame_size; + audio_vbuffer->data = calloc(bytes, 1); + if (!audio_vbuffer->data) { + return -ENOMEM; + } + audio_vbuffer->head = 0; + audio_vbuffer->tail = 0; + audio_vbuffer->live = 0; + pthread_mutex_init (&audio_vbuffer->lock, (const pthread_mutexattr_t *) NULL); + return 0; +} + +static int audio_vbuffer_destroy (audio_vbuffer_t * audio_vbuffer) { + if (!audio_vbuffer) { + return -EINVAL; + } + free(audio_vbuffer->data); + pthread_mutex_destroy(&audio_vbuffer->lock); + return 0; +} + +static int audio_vbuffer_live (audio_vbuffer_t * audio_vbuffer) { + if (!audio_vbuffer) { + return -EINVAL; + } + pthread_mutex_lock (&audio_vbuffer->lock); + int live = audio_vbuffer->live; + pthread_mutex_unlock (&audio_vbuffer->lock); + return live; +} + +#define MIN(a,b) (((a)<(b))?(a):(b)) +static size_t audio_vbuffer_write (audio_vbuffer_t * audio_vbuffer, const void * buffer, size_t frame_count) { + size_t frames_written = 0; + pthread_mutex_lock (&audio_vbuffer->lock); + + while (frame_count != 0) { + int frames = 0; + if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) { + frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head); + } else if (audio_vbuffer->head < audio_vbuffer->tail) { + frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head)); + } else { + // Full + break; + } + memcpy(&audio_vbuffer->data[audio_vbuffer->head*audio_vbuffer->frame_size], + &((uint8_t*)buffer)[frames_written*audio_vbuffer->frame_size], + frames*audio_vbuffer->frame_size); + audio_vbuffer->live += frames; + frames_written += frames; + frame_count -= frames; + audio_vbuffer->head = (audio_vbuffer->head + frames) % audio_vbuffer->frame_count; + } + + pthread_mutex_unlock (&audio_vbuffer->lock); + return frames_written; +} + +static size_t audio_vbuffer_read (audio_vbuffer_t * audio_vbuffer, void * buffer, size_t frame_count) { + size_t frames_read = 0; + pthread_mutex_lock (&audio_vbuffer->lock); + + while (frame_count != 0) { + int frames = 0; + if (audio_vbuffer->live == audio_vbuffer->frame_count || + audio_vbuffer->tail > audio_vbuffer->head) { + frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail); + } else if (audio_vbuffer->tail < audio_vbuffer->head) { + frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail); + } else { + break; + } + memcpy(&((uint8_t*)buffer)[frames_read*audio_vbuffer->frame_size], + &audio_vbuffer->data[audio_vbuffer->tail*audio_vbuffer->frame_size], + frames*audio_vbuffer->frame_size); + audio_vbuffer->live -= frames; + frames_read += frames; + frame_count -= frames; + audio_vbuffer->tail = (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count; + } + + pthread_mutex_unlock (&audio_vbuffer->lock); + return frames_read; +} + +struct generic_stream_out { + struct audio_stream_out stream; // Constant after init + pthread_mutex_t lock; + struct generic_audio_device *dev; // Constant after init + audio_devices_t device; // Protected by this->lock + struct audio_config req_config; // Constant after init + struct pcm_config pcm_config; // Constant after init + audio_vbuffer_t buffer; // Constant after init + + // Time & Position Keeping + bool standby; // Protected by this->lock + uint64_t underrun_position; // Protected by this->lock + struct timespec underrun_time; // Protected by this->lock + uint64_t last_write_time_us; // Protected by this->lock + uint64_t frames_total_buffered; // Protected by this->lock + uint64_t frames_written; // Protected by this->lock + uint64_t frames_rendered; // Protected by this->lock + + // Worker + pthread_t worker_thread; // Constant after init + pthread_cond_t worker_wake; // Protected by this->lock + bool worker_standby; // Protected by this->lock + bool worker_exit; // Protected by this->lock +}; + +struct generic_stream_in { + struct audio_stream_in stream; // Constant after init + pthread_mutex_t lock; + struct generic_audio_device *dev; // Constant after init + audio_devices_t device; // Protected by this->lock + struct audio_config req_config; // Constant after init + struct pcm *pcm; // Protected by this->lock + struct pcm_config pcm_config; // Constant after init + int16_t *stereo_to_mono_buf; // Protected by this->lock + size_t stereo_to_mono_buf_size; // Protected by this->lock + audio_vbuffer_t buffer; // Protected by this->lock + + // Time & Position Keeping + bool standby; // Protected by this->lock + int64_t standby_position; // Protected by this->lock + struct timespec standby_exit_time;// Protected by this->lock + int64_t standby_frames_read; // Protected by this->lock + + // Worker + pthread_t worker_thread; // Constant after init + pthread_cond_t worker_wake; // Protected by this->lock + bool worker_standby; // Protected by this->lock + bool worker_exit; // Protected by this->lock +}; + +static struct pcm_config pcm_config_out = { + .channels = 2, + .rate = 0, + .period_size = 0, + .period_count = OUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, +}; + +static struct pcm_config pcm_config_in = { + .channels = 2, + .rate = 0, + .period_size = 0, + .period_count = IN_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, +}; + +static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned int audio_device_ref_count = 0; + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return out->req_config.sample_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + int size = out->pcm_config.period_size * + audio_stream_out_frame_size(&out->stream); + + return size; +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return out->req_config.channel_mask; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + + return out->req_config.format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + dprintf(fd, "\tout_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice: %08x\n" + "\t\taudio dev: %p\n\n", + out_get_sample_rate(stream), + out_get_buffer_size(stream), + out_get_channels(stream), + out_get_format(stream), + out->device, + out->dev); + pthread_mutex_unlock(&out->lock); + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct str_parms *parms; + char value[32]; + int ret = -ENOSYS; + int success; + long val; + char *end; + + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } + pthread_mutex_lock(&out->lock); + if (out->standby) { + parms = str_parms_create_str(kvpairs); + success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (success >= 0) { + errno = 0; + val = strtol(value, &end, 10); + if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) { + out->device = (int)val; + ret = 0; + } + } + + // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT + success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, + value, sizeof(value)); + if (success >= 0) { + ret = 0; + } + success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, + value, sizeof(value)); + if (success >= 0) { + ret = 0; + } + + if (ret != 0) { + ALOGD("%s Unsupported parameter %s", __FUNCTION__, kvpairs); + } + + str_parms_destroy(parms); + } + pthread_mutex_unlock(&out->lock); + return ret; +} + +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + bool get = false; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + pthread_mutex_lock(&out->lock); + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device); + pthread_mutex_unlock(&out->lock); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value); + get = true; + } + + if (get) { + str = strdup(str_parms_to_str(reply)); + } + else { + ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return (out->pcm_config.period_size * 1000) / out->pcm_config.rate; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + return -ENOSYS; +} + +static void *out_write_worker(void * args) +{ + struct generic_stream_out *out = (struct generic_stream_out *)args; + struct pcm *pcm = NULL; + uint8_t *buffer = NULL; + int buffer_frames; + int buffer_size; + bool restart = false; + bool shutdown = false; + while (true) { + pthread_mutex_lock(&out->lock); + while (out->worker_standby || restart) { + restart = false; + if (pcm) { + pcm_close(pcm); // Frees pcm + pcm = NULL; + free(buffer); + buffer=NULL; + } + if (out->worker_exit) { + break; + } + pthread_cond_wait(&out->worker_wake, &out->lock); + } + + if (out->worker_exit) { + if (!out->worker_standby) { + ALOGE("Out worker not in standby before exiting"); + } + shutdown = true; + } + + while (!shutdown && audio_vbuffer_live(&out->buffer) == 0) { + pthread_cond_wait(&out->worker_wake, &out->lock); + } + + if (shutdown) { + pthread_mutex_unlock(&out->lock); + break; + } + + if (!pcm) { + pcm = pcm_open(PCM_CARD, PCM_DEVICE, + PCM_OUT | PCM_MONOTONIC, &out->pcm_config); + if (!pcm_is_ready(pcm)) { + ALOGE("pcm_open(out) failed: %s: channels %d format %d rate %d", + pcm_get_error(pcm), + out->pcm_config.channels, + out->pcm_config.format, + out->pcm_config.rate + ); + pthread_mutex_unlock(&out->lock); + break; + } + buffer_frames = out->pcm_config.period_size; + buffer_size = pcm_frames_to_bytes(pcm, buffer_frames); + buffer = malloc(buffer_size); + if (!buffer) { + ALOGE("could not allocate write buffer"); + pthread_mutex_unlock(&out->lock); + break; + } + } + int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames); + pthread_mutex_unlock(&out->lock); + int ret = pcm_write(pcm, buffer, pcm_frames_to_bytes(pcm, frames)); + if (ret != 0) { + ALOGE("pcm_write failed %s", pcm_get_error(pcm)); + restart = true; + } + } + if (buffer) { + free(buffer); + } + + return NULL; +} + +// Call with in->lock held +static void get_current_output_position(struct generic_stream_out *out, + uint64_t * position, + struct timespec * timestamp) { + struct timespec curtime = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime(CLOCK_MONOTONIC, &curtime); + const int64_t now_us = (curtime.tv_sec * 1000000000LL + curtime.tv_nsec) / 1000; + if (timestamp) { + *timestamp = curtime; + } + int64_t position_since_underrun; + if (out->standby) { + position_since_underrun = 0; + } else { + const int64_t first_us = (out->underrun_time.tv_sec * 1000000000LL + + out->underrun_time.tv_nsec) / 1000; + position_since_underrun = (now_us - first_us) * + out_get_sample_rate(&out->stream.common) / + 1000000; + if (position_since_underrun < 0) { + position_since_underrun = 0; + } + } + *position = out->underrun_position + position_since_underrun; + + // The device will reuse the same output stream leading to periods of + // underrun. + if (*position > out->frames_written) { + ALOGW("Not supplying enough data to HAL, expected position %" PRIu64 " , only wrote " + "%" PRIu64, + *position, out->frames_written); + + *position = out->frames_written; + out->underrun_position = *position; + out->underrun_time = curtime; + out->frames_total_buffered = 0; + } +} + + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + const size_t frames = bytes / audio_stream_out_frame_size(stream); + + pthread_mutex_lock(&out->lock); + + if (out->worker_standby) { + out->worker_standby = false; + } + + uint64_t current_position; + struct timespec current_time; + + get_current_output_position(out, ¤t_position, ¤t_time); + const uint64_t now_us = (current_time.tv_sec * 1000000000LL + + current_time.tv_nsec) / 1000; + if (out->standby) { + out->standby = false; + out->underrun_time = current_time; + out->frames_rendered = 0; + out->frames_total_buffered = 0; + } + + size_t frames_written = audio_vbuffer_write(&out->buffer, buffer, frames); + pthread_cond_signal(&out->worker_wake); + + /* Implementation just consumes bytes if we start getting backed up */ + out->frames_written += frames; + out->frames_rendered += frames; + out->frames_total_buffered += frames; + + // We simulate the audio device blocking when it's write buffers become + // full. + + // At the beginning or after an underrun, try to fill up the vbuffer. + // This will be throttled by the PlaybackThread + int frames_sleep = out->frames_total_buffered < out->buffer.frame_count ? 0 : frames; + + uint64_t sleep_time_us = frames_sleep * 1000000LL / + out_get_sample_rate(&stream->common); + + // If the write calls are delayed, subtract time off of the sleep to + // compensate + uint64_t time_since_last_write_us = now_us - out->last_write_time_us; + if (time_since_last_write_us < sleep_time_us) { + sleep_time_us -= time_since_last_write_us; + } else { + sleep_time_us = 0; + } + out->last_write_time_us = now_us + sleep_time_us; + + pthread_mutex_unlock(&out->lock); + + if (sleep_time_us > 0) { + usleep(sleep_time_us); + } + + if (frames_written < frames) { + ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames", frames_written, frames); + } + + /* Always consume all bytes */ + return bytes; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) + +{ + if (stream == NULL || frames == NULL || timestamp == NULL) { + return -EINVAL; + } + struct generic_stream_out *out = (struct generic_stream_out *)stream; + + pthread_mutex_lock(&out->lock); + get_current_output_position(out, frames, timestamp); + pthread_mutex_unlock(&out->lock); + + return 0; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + if (stream == NULL || dsp_frames == NULL) { + return -EINVAL; + } + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + *dsp_frames = out->frames_rendered; + pthread_mutex_unlock(&out->lock); + return 0; +} + +// Must be called with out->lock held +static void do_out_standby(struct generic_stream_out *out) +{ + int frames_sleep = 0; + uint64_t sleep_time_us = 0; + if (out->standby) { + return; + } + while (true) { + get_current_output_position(out, &out->underrun_position, NULL); + frames_sleep = out->frames_written - out->underrun_position; + + if (frames_sleep == 0) { + break; + } + + sleep_time_us = frames_sleep * 1000000LL / + out_get_sample_rate(&out->stream.common); + + pthread_mutex_unlock(&out->lock); + usleep(sleep_time_us); + pthread_mutex_lock(&out->lock); + } + out->worker_standby = true; + out->standby = true; +} + +static int out_standby(struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + do_out_standby(out); + pthread_mutex_unlock(&out->lock); + return 0; +} + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // out_add_audio_effect is a no op + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // out_remove_audio_effect is a no op + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + return -ENOSYS; +} + +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.sample_rate; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + return -ENOSYS; +} + +static int refine_output_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask) +{ + static const uint32_t sample_rates [] = {8000,11025,16000,22050,24000,32000, + 44100,48000}; + static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t); + bool inval = false; + if (*format != AUDIO_FORMAT_PCM_16_BIT) { + *format = AUDIO_FORMAT_PCM_16_BIT; + inval = true; + } + + int channel_count = popcount(*channel_mask); + if (channel_count != 1 && channel_count != 2) { + *channel_mask = AUDIO_CHANNEL_IN_STEREO; + inval = true; + } + + int i; + for (i = 0; i < sample_rates_count; i++) { + if (*sample_rate < sample_rates[i]) { + *sample_rate = sample_rates[i]; + inval=true; + break; + } + else if (*sample_rate == sample_rates[i]) { + break; + } + else if (i == sample_rates_count-1) { + // Cap it to the highest rate we support + *sample_rate = sample_rates[i]; + inval=true; + } + } + + if (inval) { + return -EINVAL; + } + return 0; +} + +static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask) +{ + static const uint32_t sample_rates [] = {8000, 11025, 16000, 22050, 44100, 48000}; + static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t); + bool inval = false; + // Only PCM_16_bit is supported. If this is changed, stereo to mono drop + // must be fixed in in_read + if (*format != AUDIO_FORMAT_PCM_16_BIT) { + *format = AUDIO_FORMAT_PCM_16_BIT; + inval = true; + } + + int channel_count = popcount(*channel_mask); + if (channel_count != 1 && channel_count != 2) { + *channel_mask = AUDIO_CHANNEL_IN_STEREO; + inval = true; + } + + int i; + for (i = 0; i < sample_rates_count; i++) { + if (*sample_rate < sample_rates[i]) { + *sample_rate = sample_rates[i]; + inval=true; + break; + } + else if (*sample_rate == sample_rates[i]) { + break; + } + else if (i == sample_rates_count-1) { + // Cap it to the highest rate we support + *sample_rate = sample_rates[i]; + inval=true; + } + } + + if (inval) { + return -EINVAL; + } + return 0; +} + +static int check_input_parameters(uint32_t sample_rate, audio_format_t format, + audio_channel_mask_t channel_mask) +{ + return refine_input_parameters(&sample_rate, &format, &channel_mask); +} + +static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format, + audio_channel_mask_t channel_mask) +{ + size_t size; + int channel_count = popcount(channel_mask); + if (check_input_parameters(sample_rate, format, channel_mask) != 0) + return 0; + + size = sample_rate*IN_PERIOD_MS/1000; + // Audioflinger expects audio buffers to be multiple of 16 frames + size = ((size + 15) / 16) * 16; + size *= sizeof(short) * channel_count; + + return size; +} + + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + int size = get_input_buffer_size(in->req_config.sample_rate, + in->req_config.format, + in->req_config.channel_mask); + + return size; +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.channel_mask; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.format; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + + pthread_mutex_lock(&in->lock); + dprintf(fd, "\tin_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice: %08x\n" + "\t\taudio dev: %p\n\n", + in_get_sample_rate(stream), + in_get_buffer_size(stream), + in_get_channels(stream), + in_get_format(stream), + in->device, + in->dev); + pthread_mutex_unlock(&in->lock); + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct str_parms *parms; + char value[32]; + int ret = -ENOSYS; + int success; + long val; + char *end; + + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } + pthread_mutex_lock(&in->lock); + if (in->standby) { + parms = str_parms_create_str(kvpairs); + + success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (success >= 0) { + errno = 0; + val = strtol(value, &end, 10); + if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) { + in->device = (int)val; + ret = 0; + } + } + // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT + success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, + value, sizeof(value)); + if (success >= 0) { + ret = 0; + } + success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, + value, sizeof(value)); + if (success >= 0) { + ret = 0; + } + + if (ret != 0) { + ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs); + } + + str_parms_destroy(parms); + } + pthread_mutex_unlock(&in->lock); + return ret; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + bool get = false; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value); + get = true; + } + + if (get) { + str = strdup(str_parms_to_str(reply)); + } + else { + ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + // in_set_gain is a no op + return 0; +} + +// Call with in->lock held +static void get_current_input_position(struct generic_stream_in *in, + int64_t * position, + struct timespec * timestamp) { + struct timespec t = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime(CLOCK_MONOTONIC, &t); + const int64_t now_us = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000; + if (timestamp) { + *timestamp = t; + } + int64_t position_since_standby; + if (in->standby) { + position_since_standby = 0; + } else { + const int64_t first_us = (in->standby_exit_time.tv_sec * 1000000000LL + + in->standby_exit_time.tv_nsec) / 1000; + position_since_standby = (now_us - first_us) * + in_get_sample_rate(&in->stream.common) / + 1000000; + if (position_since_standby < 0) { + position_since_standby = 0; + } + } + *position = in->standby_position + position_since_standby; +} + +// Must be called with in->lock held +static void do_in_standby(struct generic_stream_in *in) +{ + if (in->standby) { + return; + } + in->worker_standby = true; + get_current_input_position(in, &in->standby_position, NULL); + in->standby = true; +} + +static int in_standby(struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + do_in_standby(in); + pthread_mutex_unlock(&in->lock); + return 0; +} + +static void *in_read_worker(void * args) +{ + struct generic_stream_in *in = (struct generic_stream_in *)args; + struct pcm *pcm = NULL; + uint8_t *buffer = NULL; + size_t buffer_frames; + int buffer_size; + + bool restart = false; + bool shutdown = false; + while (true) { + pthread_mutex_lock(&in->lock); + while (in->worker_standby || restart) { + restart = false; + if (pcm) { + pcm_close(pcm); // Frees pcm + pcm = NULL; + free(buffer); + buffer=NULL; + } + if (in->worker_exit) { + break; + } + pthread_cond_wait(&in->worker_wake, &in->lock); + } + + if (in->worker_exit) { + if (!in->worker_standby) { + ALOGE("In worker not in standby before exiting"); + } + shutdown = true; + } + if (shutdown) { + pthread_mutex_unlock(&in->lock); + break; + } + if (!pcm) { + pcm = pcm_open(PCM_CARD, PCM_DEVICE, + PCM_IN | PCM_MONOTONIC, &in->pcm_config); + if (!pcm_is_ready(pcm)) { + ALOGE("pcm_open(in) failed: %s: channels %d format %d rate %d", + pcm_get_error(pcm), + in->pcm_config.channels, + in->pcm_config.format, + in->pcm_config.rate + ); + pthread_mutex_unlock(&in->lock); + break; + } + buffer_frames = in->pcm_config.period_size; + buffer_size = pcm_frames_to_bytes(pcm, buffer_frames); + buffer = malloc(buffer_size); + if (!buffer) { + ALOGE("could not allocate worker read buffer"); + pthread_mutex_unlock(&in->lock); + break; + } + } + pthread_mutex_unlock(&in->lock); + int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames)); + if (ret != 0) { + ALOGW("pcm_read failed %s", pcm_get_error(pcm)); + restart = true; + continue; + } + + pthread_mutex_lock(&in->lock); + size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames); + pthread_mutex_unlock(&in->lock); + + if (frames_written != buffer_frames) { + ALOGW("in_read_worker only could write %zu / %zu frames", frames_written, buffer_frames); + } + } + if (buffer) { + free(buffer); + } + return NULL; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct generic_audio_device *adev = in->dev; + const size_t frames = bytes / audio_stream_in_frame_size(stream); + bool mic_mute = false; + size_t read_bytes = 0; + + adev_get_mic_mute(&adev->device, &mic_mute); + pthread_mutex_lock(&in->lock); + + if (in->worker_standby) { + in->worker_standby = false; + } + pthread_cond_signal(&in->worker_wake); + + int64_t current_position; + struct timespec current_time; + + get_current_input_position(in, ¤t_position, ¤t_time); + if (in->standby) { + in->standby = false; + in->standby_exit_time = current_time; + in->standby_frames_read = 0; + } + + const int64_t frames_available = current_position - in->standby_position - in->standby_frames_read; + assert(frames_available >= 0); + + const size_t frames_wait = ((uint64_t)frames_available > frames) ? 0 : frames - frames_available; + + int64_t sleep_time_us = frames_wait * 1000000LL / + in_get_sample_rate(&stream->common); + + pthread_mutex_unlock(&in->lock); + + if (sleep_time_us > 0) { + usleep(sleep_time_us); + } + + pthread_mutex_lock(&in->lock); + int read_frames = 0; + if (in->standby) { + ALOGW("Input put to sleep while read in progress"); + goto exit; + } + in->standby_frames_read += frames; + + if (popcount(in->req_config.channel_mask) == 1 && + in->pcm_config.channels == 2) { + // Need to resample to mono + if (in->stereo_to_mono_buf_size < bytes*2) { + in->stereo_to_mono_buf = realloc(in->stereo_to_mono_buf, + bytes*2); + if (!in->stereo_to_mono_buf) { + ALOGE("Failed to allocate stereo_to_mono_buff"); + goto exit; + } + } + + read_frames = audio_vbuffer_read(&in->buffer, in->stereo_to_mono_buf, frames); + + // Currently only pcm 16 is supported. + uint16_t *src = (uint16_t *)in->stereo_to_mono_buf; + uint16_t *dst = (uint16_t *)buffer; + size_t i; + // Resample stereo 16 to mono 16 by dropping one channel. + // The stereo stream is interleaved L-R-L-R + for (i = 0; i < frames; i++) { + *dst = *src; + src += 2; + dst += 1; + } + } else { + read_frames = audio_vbuffer_read(&in->buffer, buffer, frames); + } + +exit: + read_bytes = read_frames*audio_stream_in_frame_size(stream); + + if (mic_mute) { + read_bytes = 0; + } + + if (read_bytes < bytes) { + memset (&((uint8_t *)buffer)[read_bytes], 0, bytes-read_bytes); + } + + pthread_mutex_unlock(&in->lock); + + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + return 0; +} + +static int in_get_capture_position(const struct audio_stream_in *stream, + int64_t *frames, int64_t *time) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + struct timespec current_time; + get_current_input_position(in, frames, ¤t_time); + *time = (current_time.tv_sec * 1000000000LL + current_time.tv_nsec); + pthread_mutex_unlock(&in->lock); + return 0; +} + +static int in_get_active_microphones(const struct audio_stream_in *stream, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count) +{ + return adev_get_microphones(NULL, mic_array, mic_count); +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // in_add_audio_effect is a no op + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // in_add_audio_effect is a no op + return 0; +} + +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 generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_out *out; + int ret = 0; + + if (refine_output_parameters(&config->sample_rate, &config->format, &config->channel_mask)) { + ALOGE("Error opening output stream format %d, channel_mask %04x, sample_rate %u", + config->format, config->channel_mask, config->sample_rate); + ret = -EINVAL; + goto error; + } + + out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out)); + + if (!out) + return -ENOMEM; + + 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_presentation_position = out_get_presentation_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + + pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL); + out->dev = adev; + out->device = devices; + memcpy(&out->req_config, config, sizeof(struct audio_config)); + memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config)); + out->pcm_config.rate = config->sample_rate; + out->pcm_config.period_size = out->pcm_config.rate*OUT_PERIOD_MS/1000; + + out->standby = true; + out->underrun_position = 0; + out->underrun_time.tv_sec = 0; + out->underrun_time.tv_nsec = 0; + out->last_write_time_us = 0; + out->frames_total_buffered = 0; + out->frames_written = 0; + out->frames_rendered = 0; + + ret = audio_vbuffer_init(&out->buffer, + out->pcm_config.period_size*out->pcm_config.period_count, + out->pcm_config.channels * + pcm_format_to_bits(out->pcm_config.format) >> 3); + if (ret == 0) { + pthread_cond_init(&out->worker_wake, NULL); + out->worker_standby = true; + out->worker_exit = false; + pthread_create(&out->worker_thread, NULL, out_write_worker, out); + + } + *stream_out = &out->stream; + + +error: + + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + do_out_standby(out); + + out->worker_exit = true; + pthread_cond_signal(&out->worker_wake); + pthread_mutex_unlock(&out->lock); + + pthread_join(out->worker_thread, NULL); + pthread_mutex_destroy(&out->lock); + audio_vbuffer_destroy(&out->buffer); + free(stream); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + return 0; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + return strdup(""); +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + return 0; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + // adev_set_voice_volume is a no op (simulates phones) + return 0; +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) +{ + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) +{ + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) +{ + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + // adev_set_mode is a no op (simulates phones) + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + pthread_mutex_lock(&adev->lock); + adev->mic_mute = state; + pthread_mutex_unlock(&adev->lock); + return 0; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + pthread_mutex_lock(&adev->lock); + *state = adev->mic_mute; + pthread_mutex_unlock(&adev->lock); + return 0; +} + + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask); +} + + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + do_in_standby(in); + + in->worker_exit = true; + pthread_cond_signal(&in->worker_wake); + pthread_mutex_unlock(&in->lock); + pthread_join(in->worker_thread, NULL); + + if (in->stereo_to_mono_buf != NULL) { + free(in->stereo_to_mono_buf); + in->stereo_to_mono_buf_size = 0; + } + + pthread_mutex_destroy(&in->lock); + audio_vbuffer_destroy(&in->buffer); + free(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 generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_in *in; + int ret = 0; + if (refine_input_parameters(&config->sample_rate, &config->format, &config->channel_mask)) { + ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u", + config->format, config->channel_mask, config->sample_rate); + ret = -EINVAL; + goto error; + } + + in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in)); + if (!in) { + ret = -ENOMEM; + goto error; + } + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; // no op + 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; // no op + 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; // no op + in->stream.common.remove_audio_effect = in_remove_audio_effect; // no op + in->stream.set_gain = in_set_gain; // no op + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; // no op + in->stream.get_capture_position = in_get_capture_position; + in->stream.get_active_microphones = in_get_active_microphones; + + pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); + in->dev = adev; + in->device = devices; + memcpy(&in->req_config, config, sizeof(struct audio_config)); + memcpy(&in->pcm_config, &pcm_config_in, sizeof(struct pcm_config)); + in->pcm_config.rate = config->sample_rate; + in->pcm_config.period_size = in->pcm_config.rate*IN_PERIOD_MS/1000; + + in->stereo_to_mono_buf = NULL; + in->stereo_to_mono_buf_size = 0; + + in->standby = true; + in->standby_position = 0; + in->standby_exit_time.tv_sec = 0; + in->standby_exit_time.tv_nsec = 0; + in->standby_frames_read = 0; + + ret = audio_vbuffer_init(&in->buffer, + in->pcm_config.period_size*in->pcm_config.period_count, + in->pcm_config.channels * + pcm_format_to_bits(in->pcm_config.format) >> 3); + if (ret == 0) { + pthread_cond_init(&in->worker_wake, NULL); + in->worker_standby = true; + in->worker_exit = false; + pthread_create(&in->worker_thread, NULL, in_read_worker, in); + } + + *stream_in = &in->stream; + +error: + return ret; +} + + +static int adev_dump(const audio_hw_device_t *dev, int fd) +{ + return 0; +} + +static int adev_get_microphones(const audio_hw_device_t *dev, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count) +{ + if (mic_count == NULL) { + return -ENOSYS; + } + + if (*mic_count == 0) { + *mic_count = 1; + return 0; + } + + if (mic_array == NULL) { + return -ENOSYS; + } + + strncpy(mic_array->device_id, "mic_default", AUDIO_MICROPHONE_ID_MAX_LEN - 1); + mic_array->device = AUDIO_DEVICE_IN_BUILTIN_MIC; + strncpy(mic_array->address, AUDIO_BOTTOM_MICROPHONE_ADDRESS, + AUDIO_DEVICE_MAX_ADDRESS_LEN - 1); + memset(mic_array->channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED, + sizeof(mic_array->channel_mapping)); + mic_array->location = AUDIO_MICROPHONE_LOCATION_UNKNOWN; + mic_array->group = 0; + mic_array->index_in_the_group = 0; + mic_array->sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN; + mic_array->max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN; + mic_array->min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN; + mic_array->directionality = AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN; + mic_array->num_frequency_responses = 0; + mic_array->geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + + *mic_count = 1; + return 0; +} + +static int adev_close(hw_device_t *dev) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + int ret = 0; + if (!adev) + return 0; + + pthread_mutex_lock(&adev_init_lock); + + if (audio_device_ref_count == 0) { + ALOGE("adev_close called when ref_count 0"); + ret = -EINVAL; + goto error; + } + + if ((--audio_device_ref_count) == 0) { + if (adev->mixer) { + mixer_close(adev->mixer); + } + free(adev); + } + +error: + pthread_mutex_unlock(&adev_init_lock); + return ret; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + static struct generic_audio_device *adev; + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + pthread_once(&sFallbackOnce, fallback_init); + if (sFallback != NULL) { + return sFallback->common.methods->open(&sFallback->common, name, device); + } + + pthread_mutex_lock(&adev_init_lock); + if (audio_device_ref_count != 0) { + *device = &adev->device.common; + audio_device_ref_count++; + ALOGV("%s: returning existing instance of adev", __func__); + ALOGV("%s: exit", __func__); + goto unlock; + } + adev = calloc(1, sizeof(struct generic_audio_device)); + + pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL); + + adev->device.common.tag = HARDWARE_DEVICE_TAG; + adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->device.common.module = (struct hw_module_t *) module; + adev->device.common.close = adev_close; + + adev->device.init_check = adev_init_check; // no op + adev->device.set_voice_volume = adev_set_voice_volume; // no op + adev->device.set_master_volume = adev_set_master_volume; // no op + adev->device.get_master_volume = adev_get_master_volume; // no op + adev->device.set_master_mute = adev_set_master_mute; // no op + adev->device.get_master_mute = adev_get_master_mute; // no op + adev->device.set_mode = adev_set_mode; // no op + adev->device.set_mic_mute = adev_set_mic_mute; + adev->device.get_mic_mute = adev_get_mic_mute; + adev->device.set_parameters = adev_set_parameters; // no op + adev->device.get_parameters = adev_get_parameters; // no op + adev->device.get_input_buffer_size = adev_get_input_buffer_size; + adev->device.open_output_stream = adev_open_output_stream; + adev->device.close_output_stream = adev_close_output_stream; + adev->device.open_input_stream = adev_open_input_stream; + adev->device.close_input_stream = adev_close_input_stream; + adev->device.dump = adev_dump; + adev->device.get_microphones = adev_get_microphones; + + *device = &adev->device.common; + + adev->mixer = mixer_open(PCM_CARD); + struct mixer_ctl *ctl; + + // Set default mixer ctls + // Enable channels and set volume + for (int i = 0; i < (int)mixer_get_num_ctls(adev->mixer); i++) { + ctl = mixer_get_ctl(adev->mixer, i); + ALOGD("mixer %d name %s", i, mixer_ctl_get_name(ctl)); + if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Volume") || + !strcmp(mixer_ctl_get_name(ctl), "Capture Volume")) { + for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) { + ALOGD("set ctl %d to %d", z, 100); + mixer_ctl_set_percent(ctl, z, 100); + } + continue; + } + if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Switch") || + !strcmp(mixer_ctl_get_name(ctl), "Capture Switch")) { + for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) { + ALOGD("set ctl %d to %d", z, 1); + mixer_ctl_set_value(ctl, z, 1); + } + continue; + } + } + + audio_device_ref_count++; + +unlock: + pthread_mutex_unlock(&adev_init_lock); + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .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 = "Generic audio HW HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; + +/* This function detects whether or not we should be using an alsa audio device + * or fall back to the legacy default_audio driver. + */ +static void +fallback_init(void) +{ + void* module; + + FILE *fptr = fopen ("/proc/asound/pcm", "r"); + if (fptr != NULL) { + // asound/pcm is empty if there are no devices + int c = fgetc(fptr); + fclose(fptr); + if (c != EOF) { + ALOGD("Emulator host-side ALSA audio emulation detected."); + return; + } + } + + ALOGD("Emulator without host-side ALSA audio emulation detected."); +#if __LP64__ + module = dlopen("/vendor/lib64/hw/audio.primary.i9305_legacy.so", + RTLD_LAZY|RTLD_LOCAL); +#else + module = dlopen("/vendor/lib/hw/audio.primary.i9305_legacy.so", + RTLD_LAZY|RTLD_LOCAL); +#endif + if (module != NULL) { + sFallback = (struct audio_module *)(dlsym(module, HAL_MODULE_INFO_SYM_AS_STR)); + if (sFallback == NULL) { + dlclose(module); + } + } + if (sFallback == NULL) { + ALOGE("Could not find legacy fallback module!?"); + } +} diff --git a/audio/audio_hw_legacy.c b/audio/audio_hw_legacy.c new file mode 100644 index 0000000..3d6a253 --- /dev/null +++ b/audio/audio_hw_legacy.c @@ -0,0 +1,714 @@ +/* + * 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. + */ + +#define LOG_TAG "audio_hw_generic" +/*#define LOG_NDEBUG 0*/ + +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/time.h> +#include <fcntl.h> +#include <unistd.h> + +#include <log/log.h> +#include <cutils/str_parms.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <hardware/audio.h> + + +#define AUDIO_DEVICE_NAME "/dev/eac" +#define OUT_BUFFER_SIZE 4096 +#define OUT_LATENCY_MS 20 +#define IN_SAMPLING_RATE 8000 +#define IN_BUFFER_SIZE 320 + + +struct generic_audio_device { + struct audio_hw_device device; + pthread_mutex_t lock; + struct audio_stream_out *output; + struct audio_stream_in *input; + int fd; + bool mic_mute; +}; + + +struct generic_stream_out { + struct audio_stream_out stream; + struct generic_audio_device *dev; + audio_devices_t device; + uint32_t sample_rate; +}; + +struct generic_stream_in { + struct audio_stream_in stream; + struct generic_audio_device *dev; + audio_devices_t device; +}; + + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return out->sample_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + return OUT_BUFFER_SIZE; +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + return AUDIO_CHANNEL_OUT_STEREO; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + return AUDIO_FORMAT_PCM_16_BIT; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + // out_standby is a no op + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + + dprintf(fd, "\tout_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice: %08x\n" + "\t\taudio dev: %p\n\n", + out_get_sample_rate(stream), + out_get_buffer_size(stream), + out_get_channels(stream), + out_get_format(stream), + out->device, + out->dev); + + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct str_parms *parms; + char value[32]; + int ret; + long val; + char *end; + + parms = str_parms_create_str(kvpairs); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (ret >= 0) { + errno = 0; + val = strtol(value, &end, 10); + if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) { + out->device = (int)val; + } else { + ret = -EINVAL; + } + } + + str_parms_destroy(parms); + return ret; +} + +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device); + str = strdup(str_parms_to_str(reply)); + } else { + str = strdup(keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + return OUT_LATENCY_MS; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + return -ENOSYS; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct generic_audio_device *adev = out->dev; + + pthread_mutex_lock(&adev->lock); + if (adev->fd >= 0) + bytes = write(adev->fd, buffer, bytes); + pthread_mutex_unlock(&adev->lock); + + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // out_add_audio_effect is a no op + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // out_remove_audio_effect is a no op + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + return -ENOSYS; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + return IN_SAMPLING_RATE; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + return IN_BUFFER_SIZE; +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + return AUDIO_CHANNEL_IN_MONO; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + return AUDIO_FORMAT_PCM_16_BIT; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + // in_standby is a no op + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + + dprintf(fd, "\tin_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice: %08x\n" + "\t\taudio dev: %p\n\n", + in_get_sample_rate(stream), + in_get_buffer_size(stream), + in_get_channels(stream), + in_get_format(stream), + in->device, + in->dev); + + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct str_parms *parms; + char value[32]; + int ret; + long val; + char *end; + + parms = str_parms_create_str(kvpairs); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (ret >= 0) { + errno = 0; + val = strtol(value, &end, 10); + if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) { + in->device = (int)val; + } else { + ret = -EINVAL; + } + } + + str_parms_destroy(parms); + return ret; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device); + str = strdup(str_parms_to_str(reply)); + } else { + str = strdup(keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + // in_set_gain is a no op + return 0; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct generic_audio_device *adev = in->dev; + + pthread_mutex_lock(&adev->lock); + if (adev->fd >= 0) + bytes = read(adev->fd, buffer, bytes); + if (adev->mic_mute && (bytes > 0)) { + memset(buffer, 0, bytes); + } + pthread_mutex_unlock(&adev->lock); + + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // in_add_audio_effect is a no op + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + // in_add_audio_effect is a no op + return 0; +} + +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 generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_out *out; + static const uint32_t sample_rates [] = { 44100, 48000 }; + static const int sample_rates_count = sizeof(sample_rates)/sizeof(sample_rates[0]); + int ret = 0; + + pthread_mutex_lock(&adev->lock); + if (adev->output != NULL) { + ret = -ENOSYS; + goto error; + } + + if ((config->format != AUDIO_FORMAT_PCM_16_BIT) || + (config->channel_mask != AUDIO_CHANNEL_OUT_STEREO) ) { + ALOGE("Error opening output stream, format %d, channel_mask %04x", + config->format, config->channel_mask); + config->format = AUDIO_FORMAT_PCM_16_BIT; + config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + ret = -EINVAL; + } + + for (int idx = 0; idx < sample_rates_count; idx++) { + if (config->sample_rate < sample_rates[idx]) { + config->sample_rate = sample_rates[idx]; + ALOGE("Error opening output stream, sample_rate %u", config->sample_rate); + ret = -EINVAL; + break; + } else if (config->sample_rate == sample_rates[idx]) { + break; + } else if (idx == sample_rates_count-1) { + // Cap it to the highest rate we support + config->sample_rate = sample_rates[idx]; + ALOGE("Error opening output stream, sample_rate %u", config->sample_rate); + ret = -EINVAL; + } + } + + if (ret != 0) goto error; + + out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out)); + + 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; + out->sample_rate = config->sample_rate; + + out->dev = adev; + out->device = devices; + adev->output = (struct audio_stream_out *)out; + *stream_out = &out->stream; + +error: + pthread_mutex_unlock(&adev->lock); + + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + if (stream == adev->output) { + free(stream); + adev->output = NULL; + } + pthread_mutex_unlock(&adev->lock); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + return 0; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + return strdup(""); +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + if (adev->fd >= 0) + return 0; + + return -ENODEV; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + // adev_set_voice_volume is a no op (simulates phones) + return 0; +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) +{ + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) +{ + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) +{ + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + // adev_set_mode is a no op (simulates phones) + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + adev->mic_mute = state; + pthread_mutex_unlock(&adev->lock); + return 0; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + *state = adev->mic_mute; + pthread_mutex_unlock(&adev->lock); + + return 0; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + return IN_BUFFER_SIZE; +} + +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 generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_in *in; + int ret = 0; + + pthread_mutex_lock(&adev->lock); + if (adev->input != NULL) { + ret = -ENOSYS; + goto error; + } + + if ((config->format != AUDIO_FORMAT_PCM_16_BIT) || + (config->channel_mask != AUDIO_CHANNEL_IN_MONO) || + (config->sample_rate != IN_SAMPLING_RATE)) { + ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u", + config->format, config->channel_mask, config->sample_rate); + config->format = AUDIO_FORMAT_PCM_16_BIT; + config->channel_mask = AUDIO_CHANNEL_IN_MONO; + config->sample_rate = IN_SAMPLING_RATE; + ret = -EINVAL; + goto error; + } + + in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in)); + + 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; + + in->dev = adev; + in->device = devices; + adev->input = (struct audio_stream_in *)in; + *stream_in = &in->stream; + +error: + pthread_mutex_unlock(&adev->lock); + + return ret; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + if (stream == adev->input) { + free(stream); + adev->input = NULL; + } + pthread_mutex_unlock(&adev->lock); +} + +static int adev_dump(const audio_hw_device_t *dev, int fd) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + const size_t SIZE = 256; + char buffer[SIZE]; + + dprintf(fd, "\nadev_dump:\n" + "\tfd: %d\n" + "\tmic_mute: %s\n" + "\toutput: %p\n" + "\tinput: %p\n\n", + adev->fd, + adev->mic_mute ? "true": "false", + adev->output, + adev->input); + + if (adev->output != NULL) + out_dump((const struct audio_stream *)adev->output, fd); + if (adev->input != NULL) + in_dump((const struct audio_stream *)adev->input, fd); + + return 0; +} + +static int adev_close(hw_device_t *dev) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + + adev_close_output_stream((struct audio_hw_device *)dev, adev->output); + adev_close_input_stream((struct audio_hw_device *)dev, adev->input); + + if (adev->fd >= 0) + close(adev->fd); + + free(dev); + return 0; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct generic_audio_device *adev; + int fd; + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + fd = open(AUDIO_DEVICE_NAME, O_RDWR); + if (fd < 0) + return -ENOSYS; + + adev = calloc(1, sizeof(struct generic_audio_device)); + + adev->fd = fd; + + adev->device.common.tag = HARDWARE_DEVICE_TAG; + adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->device.common.module = (struct hw_module_t *) module; + adev->device.common.close = adev_close; + + adev->device.init_check = adev_init_check; + adev->device.set_voice_volume = adev_set_voice_volume; + adev->device.set_master_volume = adev_set_master_volume; + adev->device.get_master_volume = adev_get_master_volume; + adev->device.set_master_mute = adev_set_master_mute; + adev->device.get_master_mute = adev_get_master_mute; + adev->device.set_mode = adev_set_mode; + adev->device.set_mic_mute = adev_set_mic_mute; + adev->device.get_mic_mute = adev_get_mic_mute; + adev->device.set_parameters = adev_set_parameters; + adev->device.get_parameters = adev_get_parameters; + adev->device.get_input_buffer_size = adev_get_input_buffer_size; + adev->device.open_output_stream = adev_open_output_stream; + adev->device.close_output_stream = adev_close_output_stream; + adev->device.open_input_stream = adev_open_input_stream; + adev->device.close_input_stream = adev_close_input_stream; + adev->device.dump = adev_dump; + + *device = &adev->device.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .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 = "Generic audio HW HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; diff --git a/gatekeeper/Android.mk b/gatekeeper/Android.mk new file mode 100644 index 0000000..823bd2b --- /dev/null +++ b/gatekeeper/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2015 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE := gatekeeper.default +LOCAL_MODULE_RELATIVE_PATH := hw + +LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused +LOCAL_SRC_FILES := \ + module.cpp \ + SoftGateKeeperDevice.cpp + + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libgatekeeper \ + liblog \ + libhardware \ + libbase \ + libutils \ + libcrypto \ + libhidlbase \ + libhidltransport \ + libhwbinder \ + android.hardware.gatekeeper@1.0 \ + +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := external/scrypt/lib/crypto +include $(BUILD_SHARED_LIBRARY) diff --git a/gatekeeper/SoftGateKeeper.h b/gatekeeper/SoftGateKeeper.h new file mode 100644 index 0000000..2f4f4d7 --- /dev/null +++ b/gatekeeper/SoftGateKeeper.h @@ -0,0 +1,182 @@ +/* + * Copyright 2015 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 SOFT_GATEKEEPER_H_ +#define SOFT_GATEKEEPER_H_ + +extern "C" { +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include <crypto_scrypt.h> +} + +#include <android-base/memory.h> +#include <gatekeeper/gatekeeper.h> + +#include <iostream> +#include <unordered_map> +#include <memory> + +namespace gatekeeper { + +struct fast_hash_t { + uint64_t salt; + uint8_t digest[SHA256_DIGEST_LENGTH]; +}; + +class SoftGateKeeper : public GateKeeper { +public: + static const uint32_t SIGNATURE_LENGTH_BYTES = 32; + + // scrypt params + static const uint64_t N = 16384; + static const uint32_t r = 8; + static const uint32_t p = 1; + + static const int MAX_UINT_32_CHARS = 11; + + SoftGateKeeper() { + key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]); + memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES); + } + + virtual ~SoftGateKeeper() { + } + + virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, + uint32_t *length) const { + if (auth_token_key == NULL || length == NULL) return false; + uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *auth_token_key = auth_token_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + return true; + } + + virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) { + if (password_key == NULL || length == NULL) return; + uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES]; + memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES); + + *password_key = password_key_copy; + *length = SIGNATURE_LENGTH_BYTES; + } + + virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *password, + uint32_t password_length, salt_t salt) const { + if (signature == NULL) return; + crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt), + sizeof(salt), N, r, p, signature, signature_length); + } + + virtual void GetRandom(void *random, uint32_t requested_length) const { + if (random == NULL) return; + RAND_pseudo_bytes((uint8_t *) random, requested_length); + } + + virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length, + const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const { + if (signature == NULL) return; + memset(signature, 0, signature_length); + } + + virtual uint64_t GetMillisecondsSinceBoot() const { + struct timespec time; + int res = clock_gettime(CLOCK_BOOTTIME, &time); + if (res < 0) return 0; + return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); + } + + virtual bool IsHardwareBacked() const { + return false; + } + + virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record, + bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + if (user_id != stored->secure_user_id) { + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + } + memcpy(record, stored, sizeof(*record)); + return true; + } + + virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) { + failure_record_t *stored = &failure_map_[uid]; + stored->secure_user_id = user_id; + stored->last_checked_timestamp = 0; + stored->failure_counter = 0; + return true; + } + + virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) { + failure_map_[uid] = *record; + return true; + } + + fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) { + fast_hash_t fast_hash; + size_t digest_size = password.length + sizeof(salt); + std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]); + memcpy(digest.get(), &salt, sizeof(salt)); + memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length); + + SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest); + + fast_hash.salt = salt; + return fast_hash; + } + + bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) { + fast_hash_t computed = ComputeFastHash(password, fast_hash.salt); + return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0; + } + + bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) { + uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id); + FastHashMap::const_iterator it = fast_hash_map_.find(user_id); + if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) { + return true; + } else { + if (GateKeeper::DoVerify(expected_handle, password)) { + uint64_t salt; + GetRandom(&salt, sizeof(salt)); + fast_hash_map_[user_id] = ComputeFastHash(password, salt); + return true; + } + } + + return false; + } + +private: + + typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap; + typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap; + + std::unique_ptr<uint8_t[]> key_; + FailureRecordMap failure_map_; + FastHashMap fast_hash_map_; +}; +} + +#endif // SOFT_GATEKEEPER_H_ diff --git a/gatekeeper/SoftGateKeeperDevice.cpp b/gatekeeper/SoftGateKeeperDevice.cpp new file mode 100644 index 0000000..a504fba --- /dev/null +++ b/gatekeeper/SoftGateKeeperDevice.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 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 "SoftGateKeeper.h" +#include "SoftGateKeeperDevice.h" + +namespace i9305 { + +int SoftGateKeeperDevice::enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + + if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL || + desired_password == NULL || desired_password_length == 0) + return -EINVAL; + + // Current password and current password handle go together + if (current_password_handle == NULL || current_password_handle_length == 0 || + current_password == NULL || current_password_length == 0) { + current_password_handle = NULL; + current_password_handle_length = 0; + current_password = NULL; + current_password_length = 0; + } + + SizedBuffer desired_password_buffer(desired_password_length); + memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length); + + SizedBuffer current_password_handle_buffer(current_password_handle_length); + if (current_password_handle) { + memcpy(current_password_handle_buffer.buffer.get(), current_password_handle, + current_password_handle_length); + } + + SizedBuffer current_password_buffer(current_password_length); + if (current_password) { + memcpy(current_password_buffer.buffer.get(), current_password, current_password_length); + } + + EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer, + ¤t_password_buffer); + EnrollResponse response; + + impl_->Enroll(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + *enrolled_password_handle = response.enrolled_password_handle.buffer.release(); + gatekeeper::password_handle_t *handle = + reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle); + //FIXIT: We need to move this module to host with gatekeeper pipe + handle->hardware_backed = true; + + *enrolled_password_handle_length = response.enrolled_password_handle.length; + return 0; +} + +int SoftGateKeeperDevice::verify(uint32_t uid, + uint64_t challenge, const uint8_t *enrolled_password_handle, + uint32_t enrolled_password_handle_length, const uint8_t *provided_password, + uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, + bool *request_reenroll) { + + if (enrolled_password_handle == NULL || + provided_password == NULL) { + return -EINVAL; + } + + SizedBuffer password_handle_buffer(enrolled_password_handle_length); + memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle, + enrolled_password_handle_length); + SizedBuffer provided_password_buffer(provided_password_length); + memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length); + + VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer); + VerifyResponse response; + + impl_->Verify(request, &response); + + if (response.error == ERROR_RETRY) { + return response.retry_timeout; + } else if (response.error != ERROR_NONE) { + return -EINVAL; + } + + if (auth_token != NULL && auth_token_length != NULL) { + *auth_token = response.auth_token.buffer.release(); + *auth_token_length = response.auth_token.length; + } + + if (request_reenroll != NULL) { + *request_reenroll = response.request_reenroll; + } + + return 0; +} + +} // namespace i9305 diff --git a/gatekeeper/SoftGateKeeperDevice.h b/gatekeeper/SoftGateKeeperDevice.h new file mode 100644 index 0000000..05aceaa --- /dev/null +++ b/gatekeeper/SoftGateKeeperDevice.h @@ -0,0 +1,76 @@ +/* + * Copyright 2015 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 SOFT_GATEKEEPER_DEVICE_H_ +#define SOFT_GATEKEEPER_DEVICE_H_ + +#include "SoftGateKeeper.h" + +#include <memory> + +using namespace gatekeeper; + +namespace i9305 { + +/** + * Software based GateKeeper implementation + */ +class SoftGateKeeperDevice { +public: + SoftGateKeeperDevice() { + impl_.reset(new SoftGateKeeper()); + } + + // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API. + + /** + * Enrolls password_payload, which should be derived from a user selected pin or password, + * with the authentication factor private key used only for enrolling authentication + * factor data. + * + * Returns: 0 on success or an error code less than 0 on error. + * On error, enrolled_password_handle will not be allocated. + */ + int enroll(uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length); + + /** + * Verifies provided_password matches enrolled_password_handle. + * + * Implementations of this module may retain the result of this call + * to attest to the recency of authentication. + * + * On success, writes the address of a verification token to auth_token, + * usable to attest password verification to other trusted services. Clients + * may pass NULL for this value. + * + * Returns: 0 on success or an error code less than 0 on error + * On error, verification token will not be allocated + */ + int verify(uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll); +private: + std::unique_ptr<SoftGateKeeper> impl_; +}; + +} // namespace gatekeeper + +#endif //SOFT_GATEKEEPER_DEVICE_H_ diff --git a/gatekeeper/module.cpp b/gatekeeper/module.cpp new file mode 100644 index 0000000..a3d678c --- /dev/null +++ b/gatekeeper/module.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 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/hardware.h> +#include <hardware/gatekeeper.h> +#define LOG_TAG "I9305Gatekeeper" +#include <cutils/log.h> + +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include "SoftGateKeeper.h" +#include "SoftGateKeeperDevice.h" + +using i9305::SoftGateKeeperDevice; + +struct i9305_gatekeeper_device { + gatekeeper_device device; + SoftGateKeeperDevice *s_gatekeeper; +}; + +static i9305_gatekeeper_device s_device; + +static int enroll(const struct gatekeeper_device *dev __unused, uint32_t uid, + const uint8_t *current_password_handle, uint32_t current_password_handle_length, + const uint8_t *current_password, uint32_t current_password_length, + const uint8_t *desired_password, uint32_t desired_password_length, + uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) { + + SoftGateKeeperDevice *s_gatekeeper = ((i9305_gatekeeper_device*)(dev))->s_gatekeeper; + ALOGE("called %s with gate keeper %p device %p\n", __func__, s_gatekeeper, dev); + if (s_gatekeeper == nullptr) { + abort(); + return -EINVAL; + } + + return s_gatekeeper->enroll(uid, + current_password_handle, current_password_handle_length, + current_password, current_password_length, + desired_password, desired_password_length, + enrolled_password_handle, enrolled_password_handle_length); +} + +static int verify(const struct gatekeeper_device *dev __unused, uint32_t uid, uint64_t challenge, + const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, + const uint8_t *provided_password, uint32_t provided_password_length, + uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) { + SoftGateKeeperDevice *s_gatekeeper = ((i9305_gatekeeper_device*)(dev))->s_gatekeeper; + ALOGE("called %s with gate keeper %p device %p\n", __func__, s_gatekeeper, dev); + if (s_gatekeeper == nullptr) return -EINVAL; + return s_gatekeeper->verify(uid, challenge, + enrolled_password_handle, enrolled_password_handle_length, + provided_password, provided_password_length, + auth_token, auth_token_length, request_reenroll); +} + +static int close_device(hw_device_t* dev __unused) { + SoftGateKeeperDevice *s_gatekeeper = ((i9305_gatekeeper_device*)(dev))->s_gatekeeper; + if (s_gatekeeper == nullptr) return 0; + delete s_gatekeeper; + s_gatekeeper = nullptr; + ALOGE("called %s with gate keeper %p device %p\n", __func__, s_gatekeeper, dev); + return 0; +} + +static int i9305_gatekeeper_open(const hw_module_t *module, const char *name, + hw_device_t **device) { + + if (strcmp(name, HARDWARE_GATEKEEPER) != 0) { + abort(); + return -EINVAL; + } + + memset(&s_device, 0, sizeof(s_device)); + + SoftGateKeeperDevice *s_gatekeeper = new SoftGateKeeperDevice(); + if (s_gatekeeper == nullptr) return -ENOMEM; + + s_device.s_gatekeeper = s_gatekeeper; + + s_device.device.common.tag = HARDWARE_DEVICE_TAG; + s_device.device.common.version = 1; + s_device.device.common.module = const_cast<hw_module_t *>(module); + s_device.device.common.close = close_device; + + s_device.device.enroll = enroll; + s_device.device.verify = verify; + s_device.device.delete_user = nullptr; + s_device.device.delete_all_users = nullptr; + + *device = &s_device.device.common; + ALOGE("called %s with gate keeper %p device %p\n", __func__, s_gatekeeper, *device); + + return 0; +} + +static struct hw_module_methods_t gatekeeper_module_methods = { + .open = i9305_gatekeeper_open, +}; + +struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = GATEKEEPER_HARDWARE_MODULE_ID, + .name = "I9305 GateKeeper HAL", + .author = "The Android Open Source Project", + .methods = &gatekeeper_module_methods, + .dso = 0, + .reserved = {} + }, +}; diff --git a/hwcomposer/Android.mk b/hwcomposer/Android.mk new file mode 100644 index 0000000..55c66bb --- /dev/null +++ b/hwcomposer/Android.mk @@ -0,0 +1,68 @@ +# +# Copyright 2015 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_VENDOR_MODULE := true +emulator_hwcomposer_shared_libraries := \ + liblog \ + libutils \ + libcutils \ + libEGL \ + libutils \ + libhardware \ + libsync \ + libui \ + +emulator_hwcomposer_src_files := \ + hwcomposer.cpp + +emulator_hwcomposer_cflags += \ + -DLOG_TAG=\"hwcomposer\" + +emulator_hwcomposer_c_includes += \ + system/core/libsync \ + system/core/libsync/include + +emulator_hwcomposer_relative_path := hw + +# GOLDFISH BUILD +LOCAL_SHARED_LIBRARIES := $(emulator_hwcomposer_shared_libraries) +LOCAL_SRC_FILES := $(emulator_hwcomposer_src_files) +LOCAL_CFLAGS := $(emulator_hwcomposer_cflags) +LOCAL_C_INCLUDES := $(emulator_hwcomposer_c_includes) +LOCAL_MODULE_RELATIVE_PATH := $(emulator_hwcomposer_relative_path) + +LOCAL_MODULE := hwcomposer.goldfish +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +# RANCHU BUILD +include $(CLEAR_VARS) + +LOCAL_VENDOR_MODULE := true +LOCAL_SHARED_LIBRARIES := $(emulator_hwcomposer_shared_libraries) +LOCAL_SRC_FILES := $(emulator_hwcomposer_src_files) +LOCAL_CFLAGS := $(emulator_hwcomposer_cflags) +LOCAL_C_INCLUDES := $(emulator_hwcomposer_c_includes) +LOCAL_MODULE_RELATIVE_PATH := $(emulator_hwcomposer_relative_path) + +LOCAL_MODULE := hwcomposer.ranchu +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/hwcomposer/hwcomposer.cpp b/hwcomposer/hwcomposer.cpp new file mode 100644 index 0000000..cc0dc66 --- /dev/null +++ b/hwcomposer/hwcomposer.cpp @@ -0,0 +1,362 @@ +/* + * 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. + */ +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> + +#include <log/log.h> +#include <hardware/hwcomposer.h> +#include <sync/sync.h> + +struct ranchu_hwc_composer_device_1 { + hwc_composer_device_1_t base; // constant after init + const hwc_procs_t *procs; // constant after init + pthread_t vsync_thread; // constant after init + int32_t vsync_period_ns; // constant after init + framebuffer_device_t* fbdev; // constant after init + + pthread_mutex_t vsync_lock; + bool vsync_callback_enabled; // protected by this->vsync_lock +}; + +static int hwc_prepare(hwc_composer_device_1_t* dev __unused, + size_t numDisplays, hwc_display_contents_1_t** displays) { + + if (!numDisplays || !displays) return 0; + + hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY]; + + if (!contents) return 0; + + for (size_t i = 0; i < contents->numHwLayers; i++) { + // We do not handle any layers, so set composition type of any non + // HWC_FRAMEBUFFER_TARGET layer to to HWC_FRAMEBUFFER. + if (contents->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) { + continue; + } + contents->hwLayers[i].compositionType = HWC_FRAMEBUFFER; + } + return 0; +} + +static int hwc_set(struct hwc_composer_device_1* dev,size_t numDisplays, + hwc_display_contents_1_t** displays) { + struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev; + if (!numDisplays || !displays) { + return 0; + } + + hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY]; + + int retireFenceFd = -1; + int err = 0; + for (size_t layer = 0; layer < contents->numHwLayers; layer++) { + hwc_layer_1_t* fb_layer = &contents->hwLayers[layer]; + + int releaseFenceFd = -1; + if (fb_layer->acquireFenceFd > 0) { + const int kAcquireWarningMS= 3000; + err = sync_wait(fb_layer->acquireFenceFd, kAcquireWarningMS); + if (err < 0 && errno == ETIME) { + ALOGE("hwcomposer waited on fence %d for %d ms", + fb_layer->acquireFenceFd, kAcquireWarningMS); + } + close(fb_layer->acquireFenceFd); + + if (fb_layer->compositionType != HWC_FRAMEBUFFER_TARGET) { + ALOGE("hwcomposer found acquire fence on layer %d which is not an" + "HWC_FRAMEBUFFER_TARGET layer", layer); + } + + releaseFenceFd = dup(fb_layer->acquireFenceFd); + fb_layer->acquireFenceFd = -1; + } + + if (fb_layer->compositionType != HWC_FRAMEBUFFER_TARGET) { + continue; + } + + pdev->fbdev->post(pdev->fbdev, fb_layer->handle); + fb_layer->releaseFenceFd = releaseFenceFd; + + if (releaseFenceFd > 0) { + if (retireFenceFd == -1) { + retireFenceFd = dup(releaseFenceFd); + } else { + int mergedFenceFd = sync_merge("hwc_set retireFence", + releaseFenceFd, retireFenceFd); + close(retireFenceFd); + retireFenceFd = mergedFenceFd; + } + } + } + + contents->retireFenceFd = retireFenceFd; + return err; +} + +static int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) { + struct ranchu_hwc_composer_device_1* pdev = + (struct ranchu_hwc_composer_device_1*)dev; + + switch (what) { + case HWC_BACKGROUND_LAYER_SUPPORTED: + // we do not support the background layer + value[0] = 0; + break; + case HWC_VSYNC_PERIOD: + value[0] = pdev->vsync_period_ns; + break; + default: + // unsupported query + ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what); + return -EINVAL; + } + return 0; +} + +static int hwc_event_control(struct hwc_composer_device_1* dev, int dpy __unused, + int event, int enabled) { + struct ranchu_hwc_composer_device_1* pdev = + (struct ranchu_hwc_composer_device_1*)dev; + int ret = -EINVAL; + + // enabled can only be 0 or 1 + if (!(enabled & ~1)) { + if (event == HWC_EVENT_VSYNC) { + pthread_mutex_lock(&pdev->vsync_lock); + pdev->vsync_callback_enabled=enabled; + pthread_mutex_unlock(&pdev->vsync_lock); + ret = 0; + } + } + return ret; +} + +static int hwc_blank(struct hwc_composer_device_1* dev __unused, int disp, + int blank __unused) { + if (disp != HWC_DISPLAY_PRIMARY) { + return -EINVAL; + } + return 0; +} + +static void hwc_dump(hwc_composer_device_1* dev __unused, char* buff __unused, + int buff_len __unused) { + // This is run when running dumpsys. + // No-op for now. +} + + +static int hwc_get_display_configs(struct hwc_composer_device_1* dev __unused, + int disp, uint32_t* configs, size_t* numConfigs) { + if (*numConfigs == 0) { + return 0; + } + + if (disp == HWC_DISPLAY_PRIMARY) { + configs[0] = 0; + *numConfigs = 1; + return 0; + } + + return -EINVAL; +} + + +static int32_t hwc_attribute(struct ranchu_hwc_composer_device_1* pdev, + const uint32_t attribute) { + switch(attribute) { + case HWC_DISPLAY_VSYNC_PERIOD: + return pdev->vsync_period_ns; + case HWC_DISPLAY_WIDTH: + return pdev->fbdev->width; + case HWC_DISPLAY_HEIGHT: + return pdev->fbdev->height; + case HWC_DISPLAY_DPI_X: + return pdev->fbdev->xdpi*1000; + case HWC_DISPLAY_DPI_Y: + return pdev->fbdev->ydpi*1000; + default: + ALOGE("unknown display attribute %u", attribute); + return -EINVAL; + } +} + +static int hwc_get_display_attributes(struct hwc_composer_device_1* dev __unused, + int disp, uint32_t config __unused, + const uint32_t* attributes, int32_t* values) { + + struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev; + for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) { + if (disp == HWC_DISPLAY_PRIMARY) { + values[i] = hwc_attribute(pdev, attributes[i]); + if (values[i] == -EINVAL) { + return -EINVAL; + } + } else { + ALOGE("unknown display type %u", disp); + return -EINVAL; + } + } + + return 0; +} + +static int hwc_close(hw_device_t* dev) { + struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev; + pthread_kill(pdev->vsync_thread, SIGTERM); + pthread_join(pdev->vsync_thread, NULL); + free(dev); + return 0; +} + +static void* hwc_vsync_thread(void* data) { + struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)data; + setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + + struct timespec rt; + if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { + ALOGE("%s:%d error in vsync thread clock_gettime: %s", + __FILE__, __LINE__, strerror(errno)); + } + const int log_interval = 60; + int64_t last_logged = rt.tv_sec; + int sent = 0; + int last_sent = 0; + bool vsync_enabled = false; + struct timespec wait_time; + wait_time.tv_sec = 0; + wait_time.tv_nsec = pdev->vsync_period_ns; + + while (true) { + int err = nanosleep(&wait_time, NULL); + if (err == -1) { + if (errno == EINTR) { + break; + } + ALOGE("error in vsync thread: %s", strerror(errno)); + } + + pthread_mutex_lock(&pdev->vsync_lock); + vsync_enabled = pdev->vsync_callback_enabled; + pthread_mutex_unlock(&pdev->vsync_lock); + + if (!vsync_enabled) { + continue; + } + + if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { + ALOGE("%s:%d error in vsync thread clock_gettime: %s", + __FILE__, __LINE__, strerror(errno)); + } + + int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec; + pdev->procs->vsync(pdev->procs, 0, timestamp); + if (rt.tv_sec - last_logged >= log_interval) { + ALOGD("hw_composer sent %d syncs in %ds", (uint32_t)(sent - last_sent), + (uint32_t)(rt.tv_sec - last_logged)); + last_logged = rt.tv_sec; + last_sent = sent; + } + ++sent; + } + + return NULL; +} + +static void hwc_register_procs(struct hwc_composer_device_1* dev, + hwc_procs_t const* procs) { + struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev; + pdev->procs = procs; +} + +static int hwc_open(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) { + int ret = 0; + + if (strcmp(name, HWC_HARDWARE_COMPOSER)) { + ALOGE("%s called with bad name %s", __FUNCTION__, name); + return -EINVAL; + } + + ranchu_hwc_composer_device_1 *pdev = new ranchu_hwc_composer_device_1(); + if (!pdev) { + ALOGE("%s failed to allocate dev", __FUNCTION__); + return -ENOMEM; + } + + pdev->base.common.tag = HARDWARE_DEVICE_TAG; + pdev->base.common.version = HWC_DEVICE_API_VERSION_1_1; + pdev->base.common.module = const_cast<hw_module_t *>(module); + pdev->base.common.close = hwc_close; + + pdev->base.prepare = hwc_prepare; + pdev->base.set = hwc_set; + pdev->base.eventControl = hwc_event_control; + pdev->base.blank = hwc_blank; + pdev->base.query = hwc_query; + pdev->base.registerProcs = hwc_register_procs; + pdev->base.dump = hwc_dump; + pdev->base.getDisplayConfigs = hwc_get_display_configs; + pdev->base.getDisplayAttributes = hwc_get_display_attributes; + + pdev->vsync_period_ns = 1000*1000*1000/60; // vsync is 60 hz + + hw_module_t const* hw_module; + ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &hw_module); + if (ret != 0) { + ALOGE("ranchu_hw_composer hwc_open %s module not found", GRALLOC_HARDWARE_MODULE_ID); + return ret; + } + ret = framebuffer_open(hw_module, &pdev->fbdev); + if (ret != 0) { + ALOGE("ranchu_hw_composer hwc_open could not open framebuffer"); + } + + pthread_mutex_init(&pdev->vsync_lock, NULL); + pdev->vsync_callback_enabled = false; + + ret = pthread_create (&pdev->vsync_thread, NULL, hwc_vsync_thread, pdev); + if (ret) { + ALOGE("ranchu_hw_composer could not start vsync_thread\n"); + } + + *device = &pdev->base.common; + + return ret; +} + + +static struct hw_module_methods_t hwc_module_methods = { + .open = hwc_open, +}; + +hwc_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = HWC_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = HWC_HARDWARE_MODULE_ID, + .name = "Android Emulator hwcomposer module", + .author = "The Android Open Source Project", + .methods = &hwc_module_methods, + } +}; |