summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoonas Kylmälä <joonas.kylmala@iki.fi>2020-04-10 10:41:00 -0400
committerJoonas Kylmälä <joonas.kylmala@iki.fi>2020-04-10 10:41:00 -0400
commit697e4dd0e6b483da2e7b17e1a82b0b47104dcc7f (patch)
tree5ccd90eed0e8980c65588ab0aae51a1eaab9a629
downloaddevice_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.mk61
-rw-r--r--LICENSE202
-rw-r--r--audio/Android.mk58
-rw-r--r--audio/MODULE_LICENSE_APACHE20
-rw-r--r--audio/NOTICE190
-rw-r--r--audio/audio_hw.c1674
-rw-r--r--audio/audio_hw_legacy.c714
-rw-r--r--gatekeeper/Android.mk45
-rw-r--r--gatekeeper/SoftGateKeeper.h182
-rw-r--r--gatekeeper/SoftGateKeeperDevice.cpp116
-rw-r--r--gatekeeper/SoftGateKeeperDevice.h76
-rw-r--r--gatekeeper/module.cpp127
-rw-r--r--hwcomposer/Android.mk68
-rw-r--r--hwcomposer/hwcomposer.cpp362
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
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -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, &current_position, &current_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, &current_position, &current_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, &current_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, &current_password_handle_buffer, &desired_password_buffer,
+ &current_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,
+ }
+};