[automerger skipped] DO NOT MERGE - Merge pi-dev@5234907 into stage-aosp-master
am: 1782629867 -s ours
am skip reason: subject contains skip directive

Change-Id: Ifec292c1fba4aa39e9acae496723bd3273bbb60f
diff --git a/.gitignore b/.gitignore
index 2854f05..98c5c08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
 build
 cscope.*
-PRESUBMIT.cfg
diff --git a/OWNERS b/OWNERS
index 85070ac..79cff9d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,18 @@
-dgreid@chromium.org
-chinyue@chromium.org
 cychiang@chromium.org
-
-# For bluetooth support.
+dgreid@chromium.org
 hychao@chromium.org
+louiscollard@chromium.org
+paulhsia@chromium.org
+tzungbi@chromium.org
+wuchengli@chromium.org
+yuhsuan@chromium.org
+
+# For AOSP
+cychiang@google.com
+dgreid@google.com
+hychao@google.com
+louiscollard@google.com
+paulhsia@google.com
+tzungbi@google.com
+wuchengli@google.com
+yuhsuan@google.com
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..d4d5410
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,2 @@
+[Hook Overrides]
+tab_check: false
diff --git a/cras-config/cyan/cht-bsw-max98090 b/cras-config/cyan/cht-bsw-max98090
new file mode 100644
index 0000000..fba7d1b
--- /dev/null
+++ b/cras-config/cyan/cht-bsw-max98090
@@ -0,0 +1,107 @@
+[Speaker]
+  volume_curve = explicit
+  db_at_100 = -200
+  db_at_99 = -200
+  db_at_98 = -250
+  db_at_97 = -250
+  db_at_96 = -300
+  db_at_95 = -300
+  db_at_94 = -350
+  db_at_93 = -350
+  db_at_92 = -400
+  db_at_91 = -400
+  db_at_90 = -400
+  db_at_89 = -450
+  db_at_88 = -450
+  db_at_87 = -450
+  db_at_86 = -450
+  db_at_85 = -500
+  db_at_84 = -500
+  db_at_83 = -500
+  db_at_82 = -550
+  db_at_81 = -550
+  db_at_80 = -600
+  db_at_79 = -600
+  db_at_78 = -650
+  db_at_77 = -650
+  db_at_76 = -700
+  db_at_75 = -700
+  db_at_74 = -750
+  db_at_73 = -750
+  db_at_72 = -800
+  db_at_71 = -800
+  db_at_70 = -850
+  db_at_69 = -850
+  db_at_68 = -900
+  db_at_67 = -900
+  db_at_66 = -950
+  db_at_65 = -950
+  db_at_64 = -1000
+  db_at_63 = -1000
+  db_at_62 = -1050
+  db_at_61 = -1050
+  db_at_60 = -1100
+  db_at_59 = -1100
+  db_at_58 = -1150
+  db_at_57 = -1150
+  db_at_56 = -1200
+  db_at_55 = -1200
+  db_at_54 = -1250
+  db_at_53 = -1250
+  db_at_52 = -1300
+  db_at_51 = -1300
+  db_at_50 = -1350
+  db_at_49 = -1350
+  db_at_48 = -1400
+  db_at_47 = -1400
+  db_at_46 = -1450
+  db_at_45 = -1450
+  db_at_44 = -1500
+  db_at_43 = -1550
+  db_at_42 = -1600
+  db_at_41 = -1650
+  db_at_40 = -1700
+  db_at_39 = -1750
+  db_at_38 = -1850
+  db_at_37 = -1900
+  db_at_36 = -2000
+  db_at_35 = -2100
+  db_at_34 = -2200
+  db_at_33 = -2300
+  db_at_32 = -2400
+  db_at_31 = -2450
+  db_at_30 = -2500
+  db_at_29 = -2550
+  db_at_28 = -2600
+  db_at_27 = -2650
+  db_at_26 = -2700
+  db_at_25 = -2750
+  db_at_24 = -2800
+  db_at_23 = -2850
+  db_at_22 = -2950
+  db_at_21 = -3000
+  db_at_20 = -3100
+  db_at_19 = -3150
+  db_at_18 = -3250
+  db_at_17 = -3300
+  db_at_16 = -3400
+  db_at_15 = -3450
+  db_at_14 = -3550
+  db_at_13 = -3600
+  db_at_12 = -3700
+  db_at_11 = -3750
+  db_at_10 = -3850
+  db_at_9 = -3900
+  db_at_8 = -4000
+  db_at_7 = -4050
+  db_at_6 = -4150
+  db_at_5 = -4200
+  db_at_4 = -4300
+  db_at_3 = -4350
+  db_at_2 = -4450
+  db_at_1 = -4500
+  db_at_0 = -4600
+[Headphone]
+  volume_curve = simple_step
+  volume_step = 70
+  max_volume = 0
diff --git a/cras/.gitignore b/cras/.gitignore
index 40d4c80..dcbb180 100644
--- a/cras/.gitignore
+++ b/cras/.gitignore
@@ -1,23 +1,23 @@
 *.o
+*.la
 *.lo
+*.a
+*.so*
+*.log
 *.d
-src/cras
-src/libcras.a
-src/libcras.so*
-tags
-libcras.pc
-src/mix_unittest
-src/cras_monitor
-src/cras_test_client
-src/*_unittest
+*.swp
+*.trs
+.deps
+.dirstamp
+.libs
+
 /.__autoconf_trace_data
 /.elibtoolized
-/Makefile
-/Makefile.in
 /aclocal.m4
 /ar-lib
-autom4te.cache/
+/autom4te.cache/
 /compile
+/config.cache
 /config.guess
 /config.log
 /config.status
@@ -29,37 +29,17 @@
 /ltmain.sh
 /missing
 /test-driver
-src/*.la
-src/*.log
-src/*.trs
-src/.libs/
-src/Makefile
-src/Makefile.in
-src/common/.deps/
-src/common/.dirstamp
-src/common/cras_version.h
-src/.deps/
-src/libcras.la
-src/libcras/.deps/
-src/libcras/.dirstamp
-src/server/.deps/
-src/server/.dirstamp
-src/server/config/.deps/
-src/server/config/.dirstamp
-src/tests/.deps/
-src/tests/.dirstamp
-src/alsa_plugin/.deps/
-src/alsa_plugin/.dirstamp
-src/libasound_module_ctl_cras.la
-src/libasound_module_pcm_cras.la
-src/cmpraw
-src/crossover_test
-src/crossover2_test
-src/drc_test
-src/dsp/.deps/
-src/dsp/.dirstamp
-src/dsp/tests/.deps/
-src/dsp/tests/.dirstamp
-src/dsp_util_test
-src/eq2_test
-src/eq_test
+Makefile.in
+
+tags
+/Makefile
+/libcras.pc
+/src/Makefile
+/src/*_unittest
+/src/*_test
+/src/cmpraw
+/src/cras
+/src/cras_monitor
+/src/cras_router
+/src/cras_test_client
+/src/common/cras_version.h
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index 819f068..653bc5d 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -58,6 +58,10 @@
 				boolean input_mute
 				boolean output_user_mute
 
+		void GetDefaultOutputBufferSize()
+
+			Returns the default output buffer size in frames.
+
 		{dict},{dict},... GetNodes()
 
 			Returns information about nodes. A node can be either
@@ -118,6 +122,11 @@
 					The string is empty if the node type is
 					not HOTWORD.
 
+		void GetSystemAecSupported();
+
+			Returns 1 if system echo cancellation is supported,
+			otherwise return 0.
+
 		void SetActiveOutputNode(uint64 node_id);
 
 			Requests the specified node to be used for
@@ -145,6 +154,13 @@
 
 			Returns the number of streams currently using output hardware.
 
+		int32 IsAudioOutputActive()
+
+			Returns 1 if there are currently any active output streams,
+			excluding 'dummy' streams that are not actually outputting any
+			audio. Returns 0 if there are no active streams, or all active
+			streams are 'dummy' streams.
+
 		void SetGlobalOutputChannelRemix(int32 num_channels,
 						 array:double coefficient)
 
@@ -217,3 +233,12 @@
 		NumberOfActiveStreamsChanged(int32 num_active_streams)
 
 			Indicates the number of active streams has changed.
+
+		AudioOutputActiveStateChanged(boolean active)
+
+			Indicates active output state has changed.
+			See IsAudioOutputActive for details.
+
+		HotwordTriggered(int64 tv_sec, int64 tv_nsec)
+
+			Indicates that hotword was triggered at the given timestamp.
diff --git a/cras/configure.ac b/cras/configure.ac
index 97d0849..f00011c 100644
--- a/cras/configure.ac
+++ b/cras/configure.ac
@@ -5,7 +5,7 @@
              [cras], [http://www.chromium.org/])
 AC_PREREQ([2.59])
 
-AC_CANONICAL_TARGET
+AC_CANONICAL_HOST
 
 AM_INIT_AUTOMAKE([1.10 -Wall no-define])
 #AC_CONFIG_HEADERS([config.h])
@@ -18,23 +18,45 @@
 AC_LANG_C
 AM_PROG_CC_C_O
 PKG_PROG_PKG_CONFIG
-#AC_CONFIG_FILES([Makefile src/Makefile libcras.pc])
+AC_CONFIG_FILES([Makefile src/Makefile libcras.pc])
 
 PKG_CHECK_MODULES([LIBSPEEX], [ speexdsp >= 1.2 ])
 PKG_CHECK_MODULES([ASOUNDLIB], [ alsa >= 1.1.0 ])
 
-AC_ARG_ENABLE([DBUS], AS_HELP_STRING([--disable-DBUS], [Disable all DBUS uses]), have_dbus=$enableval, have_dbus=yes)
+AC_ARG_ENABLE([dbus], AS_HELP_STRING([--disable-dbus], [Disable all DBUS uses]), have_dbus=$enableval, have_dbus=yes)
 AM_CONDITIONAL(HAVE_DBUS, test "$have_dbus" = "yes")
 if test "$have_dbus" = "yes"; then
     PKG_CHECK_MODULES([DBUS], [ dbus-1 >= 1.4.12 ])
-    DBUS_CFLAGS+=-DCRAS_DBUS
-    AC_SUBST(DBUS_CFLAGS)
+    AS_VAR_APPEND([DBUS_CFLAGS], [" -DCRAS_DBUS"])
 else
     DBUS_CFLAGS=
-    AC_SUBST(DBUS_CFLAGS)
     DBUS_LIBS=
-    AC_SUBST(DBUS_LIBS)
 fi
+AC_SUBST(DBUS_LIBS)
+AC_SUBST(DBUS_CFLAGS)
+
+AC_ARG_ENABLE([selinux], AS_HELP_STRING([--enable-selinux], [Enable SELinux calls]), have_selinux=$enableval, have_selinux=no)
+AM_CONDITIONAL(HAVE_SELINUX, test "$have_selinux" = "yes")
+if test "$have_selinux" = "yes"; then
+    PKG_CHECK_MODULES([SELINUX], [ libselinux ])
+    AS_VAR_APPEND([SELINUX_CFLAGS], [" -DCRAS_SELINUX"])
+else
+    SELINUX_CFLAGS=
+    SELINUX_LIBS=
+fi
+AC_SUBST(SELINUX_CFLAGS)
+AC_SUBST(SELINUX_LIBS)
+
+# WEBRTC APM support
+AC_ARG_ENABLE([webrtc-apm], AS_HELP_STRING([--disable-webrtc-apm], [Disable webrtc-apm uses]), have_webrtc_apm=$enableval, have_webrtc_apm=yes)
+AM_CONDITIONAL(HAVE_WEBRTC_APM, test "$have_webrtc_apm" = "yes")
+if test "$have_webrtc_apm" = "yes"; then
+    PKG_CHECK_MODULES([WEBRTC_APM], [ libwebrtc_apm ])
+    AC_DEFINE(HAVE_WEBRTC_APM, 1, [Define to use webrtc-apm.])
+else
+    WEBRTC_APM_LIBS=
+fi
+AC_SUBST(WEBRTC_APM_LIBS)
 
 PKG_CHECK_MODULES([SBC], [ sbc >= 1.0 ])
 AC_CHECK_LIB(asound, snd_pcm_ioplug_create,,
@@ -43,6 +65,15 @@
 AC_ARG_ENABLE([alsa-plugin], AS_HELP_STRING([--disable-alsa-plugin],
                                             [Disable building of ALSA plugin]))
 
+AC_ARG_ENABLE([metrics], AS_HELP_STRING([--enable-metrics], [Enable metrics uses]), have_metrics=$enableval, have_metrics=no)
+if test "$have_metrics" = "yes"; then
+    AC_DEFINE(HAVE_LIB_METRICS, 1, [Define to use libmetrics])
+    METRICS_LIBS=-lmetrics-${BASE_VER}
+else
+    METRICS_LIBS=
+fi
+AC_SUBST(METRICS_LIBS)
+
 # Determine ALSA plugin directory.
 test "x$prefix" = xNONE && prefix=$ac_default_prefix
 test "x$exec_prefix" = xNONE && exec_prefix=$prefix
@@ -78,24 +109,9 @@
 AC_DEFINE_UNQUOTED(CRAS_SOCKET_FILE_DIR, "$socketdir",
                    [directory containing CRAS socket files])
 
-# Get iniparser library and include locations
-AC_ARG_WITH([iniparser-include-path],
-  [AS_HELP_STRING([--with-iniparser-include-path],
-      [location of the iniparser headers, defaults to /usr/include/])],
-        [INIPARSER_CFLAGS="-I$withval"],
-	  [INIPARSER_CFLAGS='-I/usr/include'])
-AC_SUBST([INIPARSER_CFLAGS])
-
-AC_ARG_WITH([iniparser-lib-path],
-  [AS_HELP_STRING([--with-iniparser-lib-path],
-      [location of the iniparser libraries])],
-        [INIPARSER_LIBS="-L$withval -liniparser"],
-          [INIPARSER_LIBS='-liniparser'])
-AC_SUBST([INIPARSER_LIBS])
-
 # SSE4_2 support
 AC_ARG_ENABLE(sse42, [AS_HELP_STRING([--enable-sse42],[enable SSE42 optimizations])], have_sse42=$enableval, have_sse42=yes)
-if  test "x$target_cpu" != xx86_64; then
+if  test "x$host_cpu" != xx86_64; then
 	have_sse42=no
 fi
 if test "$have_sse42" = "yes"; then
@@ -107,7 +123,7 @@
 
 # AVX support
 AC_ARG_ENABLE(avx, [AS_HELP_STRING([--enable-avx],[enable AVX optimizations])], have_avx=$enableval, have_avx=yes)
-if  test "x$target_cpu" != xx86_64; then
+if  test "x$host_cpu" != xx86_64; then
 	have_avx=no
 fi
 if test "$have_avx" = "yes"; then
@@ -119,7 +135,7 @@
 
 # AVX2 support
 AC_ARG_ENABLE(avx2, [AS_HELP_STRING([--enable-avx2],[enable AVX2 optimizations])], have_avx2=$enableval, have_avx2=yes)
-if  test "x$target_cpu" != xx86_64; then
+if  test "x$host_cpu" != xx86_64; then
 	have_avx2=no
 fi
 if test "$have_avx2" = "yes"; then
@@ -131,7 +147,7 @@
 
 # FMA support
 AC_ARG_ENABLE(fma, [AS_HELP_STRING([--enable-fma],[enable FMA optimizations])], have_fma=$enableval, have_fma=yes)
-if  test "x$target_cpu" != xx86_64; then
+if  test "x$host_cpu" != xx86_64; then
 	have_fma=no
 fi
 if test "$have_fma" = "yes"; then
@@ -141,20 +157,12 @@
 AM_CONDITIONAL(HAVE_FMA, test "$have_fma" = "yes")
 AC_SUBST(FMA_CFLAGS)
 
-AC_OUTPUT([
-	Makefile
-	src/Makefile
-	libcras.pc
+AC_OUTPUT
+
+AC_MSG_NOTICE([
+
+Enable SSE42:                  ${have_sse42}
+Enable AVX:                    ${have_avx}
+Enable AVX2:                   ${have_avx2}
+Enable FMA:                    ${have_fma}
 ])
-
-AS_IF([test "$have_sse42" = "yes"], ENABLE_SSE42=yes, ENABLE_SSE42=no)
-AS_IF([test "$have_avx" = "yes"], ENABLE_AVX=yes, ENABLE_AVX=no)
-AS_IF([test "$have_avx2" = "yes"], ENABLE_AVX2=yes, ENABLE_AVX2=no)
-AS_IF([test "$have_fma" = "yes"], ENABLE_FMA=yes, ENABLE_FMA=no)
-
-echo "
-Enable SSE42:                  ${ENABLE_SSE42}
-Enable AVX:                    ${ENABLE_AVX}
-Enable AVX2:                   ${ENABLE_AVX2}
-Enable FMA:                    ${ENABLE_FMA}
-"
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index e83e948..ddb53fa 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -29,6 +29,15 @@
 CRAS_FMA =
 endif
 
+if HAVE_WEBRTC_APM
+CRAS_WEBRTC_APM_SOURCES = \
+	server/cras_apm_list.c \
+	server/config/aec_config.c \
+	server/config/apm_config.c
+else
+CRAS_WEBRTC_APM_SOURCES =
+endif
+
 CRAS_UT_TMPDIR_CFLAGS=-DCRAS_UT_TMPDIR=\"/tmp\"
 COMMON_CPPFLAGS = -O2 -Wall -Werror -Wno-error=cpp
 COMMON_SIMD_CPPFLAGS = -O3 -Wall -Werror -Wno-error=cpp
@@ -56,13 +65,24 @@
 	server/cras_a2dp_endpoint.c \
 	server/cras_a2dp_info.c \
 	server/cras_a2dp_iodev.c \
-	server/cras_telephony.c
+	server/cras_telephony.c \
+	server/cras_utf8.c
 else
 CRAS_DBUS_SOURCES =
 endif
 
-cras_SOURCES = \
+if HAVE_SELINUX
+CRAS_SELINUX_SOURCES = common/cras_selinux_helper.c
+CRAS_SELINUX_UNITTEST_SOURCES = tests/cras_selinux_helper_unittest.c
+else
+CRAS_SELINUX_SOURCES =
+CRAS_SELINUX_UNITTEST_SOURCES =
+endif
+
+cras_server_SOURCES = \
 	$(CRAS_DBUS_SOURCES) \
+	$(CRAS_SELINUX_SOURCES) \
+	$(CRAS_WEBRTC_APM_SOURCES) \
 	common/cras_audio_format.c \
 	common/cras_checksum.c \
 	common/cras_config.c \
@@ -75,6 +95,7 @@
 	dsp/biquad.c \
 	dsp/crossover.c \
 	dsp/crossover2.c \
+	dsp/dcblock.c \
 	dsp/drc.c \
 	dsp/drc_kernel.c \
 	dsp/drc_math.c \
@@ -83,9 +104,9 @@
 	dsp/eq2.c \
 	server/audio_thread.c \
 	server/buffer_share.c \
+	server/config/cras_board_config.c \
 	server/config/cras_card_config.c \
 	server/config/cras_device_blacklist.c \
-	server/cras.c \
 	server/cras_alert.c \
 	server/cras_alsa_card.c \
 	server/cras_alsa_helpers.c \
@@ -96,6 +117,7 @@
 	server/cras_alsa_ucm.c \
 	server/cras_alsa_ucm_section.c \
 	server/cras_audio_area.c \
+	server/cras_audio_thread_monitor.c \
 	server/cras_device_monitor.c \
 	server/cras_dsp.c \
 	server/cras_dsp_ini.c \
@@ -106,29 +128,54 @@
 	server/cras_expr.c \
 	server/cras_fmt_conv.c \
 	server/cras_gpio_jack.c \
+	server/cras_hotword_handler.c \
 	server/cras_iodev.c \
 	server/cras_iodev_list.c \
 	server/cras_loopback_iodev.c \
 	server/cras_main_message.c \
 	server/cras_mix.c \
+	server/cras_non_empty_audio_handler.c \
 	server/cras_observer.c \
 	server/cras_ramp.c \
 	server/cras_rclient.c \
 	server/cras_rstream.c \
-	server/cras_utf8.c \
-	server/cras_server.c \
 	server/cras_server_metrics.c \
 	server/cras_system_state.c \
 	server/cras_tm.c \
 	server/cras_udev.c \
 	server/cras_volume_curve.c \
+	server/dev_io.c \
 	server/dev_stream.c \
+	server/input_data.c \
 	server/linear_resampler.c \
+	server/polled_interval_checker.c \
+	server/server_stream.c \
 	server/stream_list.c \
 	server/test_iodev.c \
 	server/rate_estimator.c \
 	server/softvol_curve.c
 
+libcrasserver_la_SOURCES = \
+	$(cras_server_SOURCES)
+libcrasserver_la_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+	-I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+	-I$(top_srcdir)/src/server/config \
+	$(DBUS_CFLAGS) $(SBC_CFLAGS) $(SELINUX_CFLAGS)
+libcrasserver_la_LIBADD = \
+	libcrasmix.la \
+	$(CRAS_SSE4_2) \
+	$(CRAS_AVX) \
+	$(CRAS_AVX2) \
+	$(CRAS_FMA) \
+	-lpthread -lasound -lrt -liniparser -ludev -ldl -lm -lspeexdsp \
+	$(SBC_LIBS) \
+	$(DBUS_LIBS) \
+	$(SELINUX_LIBS)
+
+cras_SOURCES = \
+	server/cras.c \
+	server/cras_server.c
+
 cras_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
 	-I$(top_srcdir)/src/server/config \
@@ -136,20 +183,24 @@
 
 cras_LDADD = \
 	libcrasmix.la \
+	libcrasserver.la \
 	$(CRAS_SSE4_2) \
 	$(CRAS_AVX) \
 	$(CRAS_AVX2) \
 	$(CRAS_FMA) \
 	-lpthread -lasound -lrt -liniparser -ludev -ldl -lm -lspeexdsp \
+	$(METRICS_LIBS) \
 	$(SBC_LIBS) \
-	$(DBUS_LIBS)
+	$(DBUS_LIBS) \
+	$(WEBRTC_APM_LIBS)
 
 noinst_LTLIBRARIES = \
 	$(CRAS_SSE4_2) \
 	$(CRAS_AVX) \
 	$(CRAS_AVX2) \
 	$(CRAS_FMA) \
-	libcrasmix.la
+	libcrasmix.la \
+	libcrasserver.la
 
 libcrasmix_la_SOURCES = \
 	server/cras_mix_ops.c
@@ -265,11 +316,20 @@
 DBUS_TESTS =
 endif
 
+if HAVE_WEBRTC_APM
+CRAS_WEBRTC_APM_TESTS = \
+	apm_list_unittest
+else
+CRAS_WEBRTC_APM_TESTS =
+endif
+
 TESTS = \
 	$(DBUS_TESTS) \
+	$(CRAS_WEBRTC_APM_TESTS) \
 	audio_area_unittest \
 	audio_format_unittest \
 	audio_thread_unittest \
+	audio_thread_monitor_unittest \
 	alert_unittest \
 	alsa_card_unittest \
 	alsa_helpers_unittest \
@@ -283,6 +343,7 @@
 	cras_client_unittest \
 	cras_tm_unittest \
 	device_monitor_unittest \
+	dev_io_unittest \
 	dev_stream_unittest \
 	device_blacklist_unittest \
 	dsp_core_unittest \
@@ -293,6 +354,7 @@
 	edid_utils_unittest \
 	expr_unittest \
 	file_wait_unittest \
+	float_buffer_unittest \
 	fmt_conv_unittest \
 	hfp_info_unittest \
 	buffer_share_unittest \
@@ -302,6 +364,7 @@
 	mix_unittest \
 	linear_resampler_unittest \
 	observer_unittest \
+	polled_interval_checker_unittest \
 	ramp_unittest \
 	rate_estimator_unittest \
 	rclient_unittest \
@@ -311,6 +374,7 @@
 	softvol_curve_unittest \
 	stream_list_unittest \
 	system_state_unittest \
+	timing_unittest \
 	utf8_unittest \
 	util_unittest \
 	volume_curve_unittest
@@ -357,45 +421,53 @@
 check_PROGRAMS += \
 	crossover_test \
 	crossover2_test \
+	dcblock_test \
 	drc_test \
 	dsp_util_test \
 	eq_test \
 	eq2_test \
 	cmpraw
 
+DSP_INCLUDE_PATHS = -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/common
+
 crossover_test_SOURCES = dsp/crossover.c dsp/biquad.c dsp/dsp_util.c \
 	dsp/tests/crossover_test.c dsp/tests/dsp_test_util.c dsp/tests/raw.c
 crossover_test_LDADD = -lrt -lm
-crossover_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+crossover_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 crossover2_test_SOURCES = dsp/crossover2.c dsp/biquad.c dsp/dsp_util.c \
 	dsp/tests/crossover2_test.c dsp/tests/dsp_test_util.c dsp/tests/raw.c
 crossover2_test_LDADD = -lrt -lm
-crossover2_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+crossover2_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
+
+dcblock_test_SOURCES = dsp/dcblock.c dsp/dsp_util.c dsp/tests/dcblock_test.c \
+	dsp/tests/dsp_test_util.c dsp/tests/raw.c
+dcblock_test_LDADD = -lrt -lm
+dcblock_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 drc_test_SOURCES = dsp/drc.c dsp/drc_kernel.c dsp/drc_math.c \
 	dsp/crossover2.c dsp/eq2.c dsp/biquad.c dsp/dsp_util.c \
 	dsp/tests/drc_test.c dsp/tests/dsp_test_util.c dsp/tests/raw.c
 drc_test_LDADD = -lrt -lm
-drc_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+drc_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 dsp_util_test_SOURCES = dsp/tests/dsp_util_test.c dsp/dsp_util.c
 dsp_util_test_LDADD = -lm
-dsp_util_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp -Wno-error=strict-aliasing
+dsp_util_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS) -Wno-error=strict-aliasing
 
 eq_test_SOURCES = dsp/biquad.c dsp/eq.c dsp/dsp_util.c dsp/tests/eq_test.c \
 	dsp/tests/dsp_test_util.c dsp/tests/raw.c
 eq_test_LDADD = -lrt -lm
-eq_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+eq_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 eq2_test_SOURCES = dsp/biquad.c dsp/eq2.c dsp/dsp_util.c dsp/tests/eq2_test.c \
 	dsp/tests/dsp_test_util.c dsp/tests/raw.c
 eq2_test_LDADD = -lrt -lm
-eq2_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+eq2_test_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 cmpraw_SOURCES = dsp/tests/cmpraw.c dsp/tests/raw.c
 cmpraw_LDADD = -lm
-cmpraw_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+cmpraw_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 
 # unit tests
 alert_unittest_SOURCES = tests/alert_unittest.cc \
@@ -450,7 +522,8 @@
 	common/sfh.c \
 	server/cras_alsa_ucm_section.c \
 	server/cras_alsa_mixer_name.c
-alsa_io_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
+alsa_io_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) $(DBUS_CFLAGS) \
+	-I$(top_srcdir)/src/server \
 	-I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/server/config
 alsa_io_unittest_LDADD = -lgtest -lpthread
@@ -481,15 +554,32 @@
 	-I$(top_srcdir)/src/server/config
 alsa_ucm_unittest_LDADD = -lgtest -lpthread
 
+if HAVE_WEBRTC_APM
+apm_list_unittest_SOURCES = tests/apm_list_unittest.cc \
+	server/cras_apm_list.c
+apm_list_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
+	$(DSP_INCLUDE_PATHS) \
+	-I$(top_srcdir)/src/server \
+	-I$(top_srcdir)/src/server/config \
+	$(WEBRTC_APM_CFLAGS)
+apm_list_unittest_LDADD = -lgtest
+endif
+
 array_unittest_SOURCES = tests/array_unittest.cc
 array_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
 array_unittest_LDADD = -lgtest -lpthread
 
-audio_thread_unittest_SOURCES = tests/audio_thread_unittest.cc
+audio_thread_unittest_SOURCES = tests/audio_thread_unittest.cc \
+	server/dev_io.c tests/empty_audio_stub.cc
 audio_thread_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
 	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server
 audio_thread_unittest_LDADD = -lgtest -lpthread -lrt
 
+audio_thread_monitor_unittest_SOURCES = tests/audio_thread_monitor_unittest.cc
+audio_thread_monitor_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server
+audio_thread_monitor_unittest_LDADD = -lgtest -lpthread -lrt
+
 if HAVE_DBUS
 bt_device_unittest_SOURCES = tests/bt_device_unittest.cc \
 	server/cras_bt_device.c
@@ -529,6 +619,39 @@
 	 -I$(top_srcdir)/src/server
 cras_tm_unittest_LDADD = -lgtest -lpthread
 
+dev_io_unittest_SOURCES = \
+	$(CRAS_SELINUX_UNITTEST_SOURCES) \
+	common/cras_audio_format.c \
+	common/cras_shm.c \
+	server/cras_audio_area.c \
+	server/cras_fmt_conv.c \
+	server/cras_mix.c \
+	server/cras_mix_ops.c \
+	server/dev_io.c \
+	server/dev_stream.c \
+	server/linear_resampler.c \
+	tests/dev_io_stubs.cc \
+	tests/iodev_stub.cc \
+	tests/empty_audio_stub.cc \
+	tests/rstream_stub.cc \
+	tests/dev_io_unittest.cc
+dev_io_unittest_CXXFLAGS = \
+	-std=c++11 -Wno-noexcept-type
+dev_io_unittest_CPPFLAGS = \
+	$(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/common \
+	-I$(top_srcdir)/src/server \
+	-I$(top_srcdir)/src/server/config \
+	$(SELINUX_CFLAGS)
+dev_io_unittest_LDADD = \
+	libcrasmix.la \
+	$(CRAS_SSE4_2) \
+	$(CRAS_AVX) \
+	$(CRAS_AVX2) \
+	$(CRAS_FMA) \
+	$(SELINUX_LIBS) \
+	-lgtest -lrt -lpthread -ldl -lm -lspeexdsp
+
 dev_stream_unittest_SOURCES = tests/dev_stream_unittest.cc \
 	server/dev_stream.c
 dev_stream_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
@@ -550,7 +673,7 @@
 dsp_core_unittest_SOURCES = tests/dsp_core_unittest.cc dsp/eq.c dsp/eq2.c \
 	dsp/biquad.c dsp/dsp_util.c dsp/crossover.c dsp/crossover2.c dsp/drc.c \
 	dsp/drc_kernel.c dsp/drc_math.c
-dsp_core_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+dsp_core_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) $(DSP_INCLUDE_PATHS)
 dsp_core_unittest_LDADD = -lgtest -lpthread
 
 dsp_ini_unittest_SOURCES = tests/dsp_ini_unittest.cc \
@@ -562,16 +685,16 @@
 dsp_pipeline_unittest_SOURCES = tests/cras_dsp_pipeline_unittest.cc \
 	server/cras_dsp_ini.c server/cras_expr.c server/cras_dsp_pipeline.c \
 	common/dumper.c dsp/dsp_util.c
-dsp_pipeline_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-	-I$(top_srcdir)/src/server -I$(top_srcdir)/src/dsp
+dsp_pipeline_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/server $(DSP_INCLUDE_PATHS)
 dsp_pipeline_unittest_LDADD = -lgtest -lrt -liniparser -lpthread
 
 dsp_unittest_SOURCES = tests/dsp_unittest.cc \
 	server/cras_dsp.c server/cras_dsp_ini.c server/cras_dsp_pipeline.c \
 	server/cras_expr.c common/dumper.c dsp/dsp_util.c \
 	dsp/tests/dsp_test_util.c
-dsp_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-	-I$(top_srcdir)/src/server -I$(top_srcdir)/src/dsp
+dsp_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/server $(DSP_INCLUDE_PATHS)
 dsp_unittest_LDADD = -lgtest -lrt -liniparser -lpthread
 
 dumper_unittest_SOURCES = tests/dumper_unittest.cc common/dumper.c
@@ -593,6 +716,12 @@
 	$(CRAS_UT_TMPDIR_CFLAGS)
 file_wait_unittest_LDADD = -lgtest -lpthread
 
+
+float_buffer_unittest_SOURCES = tests/float_buffer_unittest.cc
+float_buffer_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+	-I$(top_srcdir)/src/server
+float_buffer_unittest_LDADD = -lgtest -lpthread
+
 fmt_conv_unittest_SOURCES = tests/fmt_conv_unittest.cc server/cras_fmt_conv.c
 fmt_conv_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	 -I$(top_srcdir)/src/server
@@ -664,6 +793,12 @@
 	-I$(top_srcdir)/src/server
 observer_unittest_LDADD = -lgtest -lpthread
 
+polled_interval_checker_unittest_SOURCES = tests/polled_interval_checker_unittest.cc \
+    server/polled_interval_checker.c
+polled_interval_checker_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+	-I$(top_srcdir)/src/server
+polled_interval_checker_unittest_LDADD = -lgtest -lpthread
+
 ramp_unittest_SOURCES = tests/ramp_unittest.cc
 ramp_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/server
@@ -680,10 +815,11 @@
 rclient_unittest_LDADD = -lgtest -lpthread
 
 rstream_unittest_SOURCES = tests/rstream_unittest.cc server/cras_rstream.c \
-	common/cras_shm.c
+	common/cras_shm.c $(CRAS_SELINUX_UNITTEST_SOURCES)
 rstream_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-	 -I$(top_srcdir)/src/server
-rstream_unittest_LDADD = -lasound -lgtest -lpthread -lrt
+	 -I$(top_srcdir)/src/server $(SELINUX_CFLAGS)
+rstream_unittest_LDADD = $(SELINUX_LIBS) \
+	-lasound -lgtest -lpthread -lrt
 
 server_metrics_unittest_SOURCES = tests/server_metrics_unittest.cc
 server_metrics_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
@@ -706,11 +842,46 @@
 stream_list_unittest_LDADD = -lgtest -lpthread
 
 system_state_unittest_SOURCES = tests/system_state_unittest.cc \
-	server/cras_system_state.c common/cras_shm.c
+	server/cras_system_state.c common/cras_shm.c \
+	server/config/cras_board_config.c $(CRAS_SELINUX_UNITTEST_SOURCES)
 system_state_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
 	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
-	-I$(top_srcdir)/src/server/config
-system_state_unittest_LDADD = -lgtest -lrt -lpthread
+	-I$(top_srcdir)/src/server/config $(SELINUX_CFLAGS)
+system_state_unittest_LDADD = $(SELINUX_LIBS) \
+	-lgtest -liniparser -lpthread -lrt
+
+timing_unittest_SOURCES = \
+	$(CRAS_SELINUX_UNITTEST_SOURCES) \
+	common/cras_audio_format.c \
+	common/cras_shm.c \
+	server/cras_audio_area.c \
+	server/cras_fmt_conv.c \
+	server/cras_mix.c \
+	server/cras_mix_ops.c \
+	server/dev_io.c \
+	server/dev_stream.c \
+	server/linear_resampler.c \
+	tests/dev_io_stubs.cc \
+	tests/iodev_stub.cc \
+	tests/empty_audio_stub.cc \
+	tests/rstream_stub.cc \
+	tests/timing_unittest.cc
+timing_unittest_CXXFLAGS = \
+	-std=c++11 -Wno-noexcept-type
+timing_unittest_CPPFLAGS = \
+	$(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/common \
+	-I$(top_srcdir)/src/server \
+	-I$(top_srcdir)/src/server/config \
+	$(SELINUX_CFLAGS)
+timing_unittest_LDADD = \
+	libcrasmix.la \
+	$(CRAS_SSE4_2) \
+	$(CRAS_AVX) \
+	$(CRAS_AVX2) \
+	$(CRAS_FMA) \
+	$(SELINUX_LIBS) \
+	-lgtest -lrt -lpthread -ldl -lm -lspeexdsp
 
 utf8_unittest_SOURCES = tests/utf8_unittest.cc server/cras_utf8.c
 utf8_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
diff --git a/cras/src/alsa_plugin/pcm_cras.c b/cras/src/alsa_plugin/pcm_cras.c
index 93b61a3..f30c7cd 100644
--- a/cras/src/alsa_plugin/pcm_cras.c
+++ b/cras/src/alsa_plugin/pcm_cras.c
@@ -143,7 +143,8 @@
 	sample_bytes = snd_pcm_format_physical_width(io->format) / 8;
 
 	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
-		if (io->state != SND_PCM_STATE_RUNNING) {
+		if (io->state != SND_PCM_STATE_RUNNING &&
+		    io->state != SND_PCM_STATE_DRAINING) {
 			memset(samples, 0, nframes * frame_bytes);
 			return nframes;
 		}
@@ -228,29 +229,6 @@
 	return cras_client_connect(pcm_cras->client);
 }
 
-/* Get the pcm boundary value. */
-static int get_boundary(snd_pcm_t *pcm, snd_pcm_uframes_t *boundary)
-{
-	snd_pcm_sw_params_t *sw_params;
-	int rc;
-
-	snd_pcm_sw_params_alloca(&sw_params);
-
-	rc = snd_pcm_sw_params_current(pcm, sw_params);
-	if (rc < 0) {
-		fprintf(stderr, "sw_params_current: %s\n", snd_strerror(rc));
-		return rc;
-	}
-
-	rc = snd_pcm_sw_params_get_boundary(sw_params, boundary);
-	if (rc < 0) {
-		fprintf(stderr, "get_boundary: %s\n", snd_strerror(rc));
-		return rc;
-	}
-
-	return 0;
-}
-
 /* Called when an ALSA stream is started. */
 static int snd_pcm_cras_start(snd_pcm_ioplug_t *io)
 {
@@ -300,71 +278,8 @@
 	return rc;
 }
 
-static int snd_pcm_cras_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp)
-{
-	snd_pcm_uframes_t limit;
-	int rc;
-	struct snd_pcm_cras *pcm_cras;
-	struct timespec latency;
-
-	pcm_cras = (struct snd_pcm_cras *)io->private_data;
-
-	rc = get_boundary(io->pcm, &limit);
-	if ((rc < 0) || (limit == 0)) {
-		*delayp = 0;
-		return -EINVAL;
-	}
-
-	/* To get the delay, first calculate the latency between now and the
-	 * playback/capture sample time for the old hw_ptr we tracked, note that
-	 * for playback path this latency could be negative. Then add it up with
-	 * the difference between appl_ptr and the old hw_ptr.
-	 */
-	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
-		/* Do not compute latency if playback_sample_time is not set */
-		if (pcm_cras->playback_sample_time.tv_sec == 0 &&
-		    pcm_cras->playback_sample_time.tv_nsec == 0) {
-			latency.tv_sec = 0;
-			latency.tv_nsec = 0;
-		} else {
-			cras_client_calc_playback_latency(
-					&pcm_cras->playback_sample_time,
-					&latency);
-		}
-
-		*delayp = limit +
-			  io->appl_ptr -
-			  pcm_cras->playback_sample_index +
-			  latency.tv_sec * io->rate +
-			  latency.tv_nsec / (1000000000L / (long)io->rate);
-	} else {
-		/* Do not compute latency if capture_sample_time is not set */
-		if (pcm_cras->capture_sample_time.tv_sec == 0 &&
-		    pcm_cras->capture_sample_time.tv_nsec == 0) {
-			latency.tv_sec = 0;
-			latency.tv_nsec = 0;
-		} else {
-			cras_client_calc_capture_latency(
-					&pcm_cras->capture_sample_time,
-					&latency);
-		}
-
-		*delayp = limit +
-			  pcm_cras->capture_sample_index -
-			  io->appl_ptr +
-			  latency.tv_sec * io->rate +
-			  latency.tv_nsec / (1000000000L / (long)io->rate);
-	}
-
-	/* Both appl and hw pointers wrap at the pcm boundary. */
-	*delayp %= limit;
-
-	return 0;
-}
-
 static snd_pcm_ioplug_callback_t cras_pcm_callback = {
 	.close = snd_pcm_cras_close,
-	.delay = snd_pcm_cras_delay,
 	.start = snd_pcm_cras_start,
 	.stop = snd_pcm_cras_stop,
 	.pointer = snd_pcm_cras_pointer,
diff --git a/cras/src/common/byte_buffer.h b/cras/src/common/byte_buffer.h
index c949b71..2470d26 100644
--- a/cras/src/common/byte_buffer.h
+++ b/cras/src/common/byte_buffer.h
@@ -3,6 +3,9 @@
  * found in the LICENSE file.
  */
 
+#ifndef BYTE_BUFFER_H_
+#define BYTE_BUFFER_H_
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/param.h>
@@ -36,12 +39,13 @@
 }
 
 /* Destory a byte_buffer created with byte_buffer_create. */
-static inline void byte_buffer_destroy(struct byte_buffer *buf)
+static inline void byte_buffer_destroy(struct byte_buffer **buf)
 {
-	free(buf);
+	free(*buf);
+	*buf = NULL;
 }
 
-static inline unsigned int buf_writable_bytes(struct byte_buffer *buf)
+static inline unsigned int buf_writable(struct byte_buffer *buf)
 {
 	if (buf->level >= buf->used_size)
 		return 0;
@@ -51,7 +55,7 @@
 	return buf->used_size - buf->write_idx;
 }
 
-static inline unsigned int buf_readable_bytes(struct byte_buffer *buf)
+static inline unsigned int buf_readable(struct byte_buffer *buf)
 {
 	if (buf->level == 0)
 		return 0;
@@ -62,12 +66,12 @@
 	return buf->used_size - buf->read_idx;
 }
 
-static inline unsigned int buf_queued_bytes(struct byte_buffer *buf)
+static inline unsigned int buf_queued(struct byte_buffer *buf)
 {
 	return buf->level;
 }
 
-static inline unsigned int buf_available_bytes(const struct byte_buffer *buf)
+static inline unsigned int buf_available(const struct byte_buffer *buf)
 {
 	return buf->used_size - buf->level;
 }
@@ -80,7 +84,7 @@
 static inline uint8_t *buf_read_pointer_size(struct byte_buffer *buf,
 					     unsigned int *readable)
 {
-	*readable = buf_readable_bytes(buf);
+	*readable = buf_readable(buf);
 	return buf_read_pointer(buf);
 }
 
@@ -100,7 +104,7 @@
 static inline uint8_t *buf_write_pointer_size(struct byte_buffer *buf,
 					      unsigned int *writeable)
 {
-	*writeable = buf_writable_bytes(buf);
+	*writeable = buf_writable(buf);
 	return buf_write_pointer(buf);
 }
 
@@ -120,3 +124,5 @@
 	buf->read_idx = 0;
 	buf->level = 0;
 }
+
+#endif /* BYTE_BUFFER_H_ */
diff --git a/cras/src/common/cras_audio_format.c b/cras/src/common/cras_audio_format.c
index 3ff3ccb..e25c20c 100644
--- a/cras/src/common/cras_audio_format.c
+++ b/cras/src/common/cras_audio_format.c
@@ -19,8 +19,8 @@
 	/* FL FR RL RR FC LFE SL SR RC FLC FRC */
 	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* FL */
 	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* FR */
-	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* RL */
-	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* RR */
+	{  0, 0, 0, 0, 0, 0,  1, 0, 0, 0,  0 }, /* RL */
+	{  0, 0, 0, 0, 0, 0,  0, 1, 0, 0,  0 }, /* RR */
 	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* FC */
 	{  0, 0, 0, 0, 0, 0,  0, 0, 0, 0,  0 }, /* LFE */
 	{  0, 0, 1, 0, 0, 0,  0, 0, 0, 0,  0 }, /* SL */
diff --git a/cras/src/common/cras_audio_format.h b/cras/src/common/cras_audio_format.h
index 516a18d..acef686 100644
--- a/cras/src/common/cras_audio_format.h
+++ b/cras/src/common/cras_audio_format.h
@@ -124,6 +124,16 @@
 	return (size_t)bytes * fmt->num_channels;
 }
 
+/* Sets channel layout to a default value where channels [0, num_channels] are
+ * placed to the same position of its channel index, otherwise set to -1. */
+static inline void cras_audio_format_set_default_channel_layout(
+		struct cras_audio_format *format)
+{
+	unsigned int i;
+	for (i = 0; i < CRAS_CH_MAX; i++)
+		format->channel_layout[i] = i < format->num_channels ? i : -1;
+}
+
 /* Create an audio format structure. */
 struct cras_audio_format *cras_audio_format_create(snd_pcm_format_t format,
 						   size_t frame_rate,
diff --git a/cras/src/common/cras_file_wait.c b/cras/src/common/cras_file_wait.c
index 809bc68..5304f40 100644
--- a/cras/src/common/cras_file_wait.c
+++ b/cras/src/common/cras_file_wait.c
@@ -18,7 +18,7 @@
 
 #define CRAS_FILE_WAIT_EVENT_MIN_SIZE sizeof(struct inotify_event)
 #define CRAS_FILE_WAIT_EVENT_SIZE (CRAS_FILE_WAIT_EVENT_MIN_SIZE + NAME_MAX + 1)
-#define CRAS_FILE_WAIT_FLAG_MOCK_RACE (1 << 31)
+#define CRAS_FILE_WAIT_FLAG_MOCK_RACE (1u << 31)
 
 struct cras_file_wait {
 	cras_file_wait_callback_t callback;
diff --git a/cras/src/common/cras_iodev_info.h b/cras/src/common/cras_iodev_info.h
index 9b1dbb5..bc3c18c 100644
--- a/cras/src/common/cras_iodev_info.h
+++ b/cras/src/common/cras_iodev_info.h
@@ -7,7 +7,7 @@
 #define CRAS_IODEV_INFO_H_
 
 #include <stddef.h>
-#include <sys/time.h>
+#include <stdint.h>
 
 #define CRAS_IODEV_NAME_BUFFER_SIZE 64
 #define CRAS_NODE_TYPE_BUFFER_SIZE 32
diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h
index ab962df..c08d2bb 100644
--- a/cras/src/common/cras_messages.h
+++ b/cras/src/common/cras_messages.h
@@ -16,10 +16,14 @@
 
 /* Rev when message format changes. If new messages are added, or message ID
  * values change. */
-#define CRAS_PROTO_VER 1
+#define CRAS_PROTO_VER 2
 #define CRAS_SERV_MAX_MSG_SIZE 256
 #define CRAS_CLIENT_MAX_MSG_SIZE 256
 #define CRAS_HOTWORD_NAME_MAX_SIZE 8
+#define CRAS_MAX_HOTWORD_MODELS 244
+#define CRAS_MAX_REMIX_CHANNELS 32
+#define CRAS_MAX_TEST_DATA_LEN 224
+#define CRAS_AEC_DUMP_FILE_NAME_LEN 128
 
 /* Message IDs. */
 enum CRAS_SERVER_MESSAGE_ID {
@@ -39,6 +43,7 @@
 	CRAS_SERVER_RELOAD_DSP,
 	CRAS_SERVER_DUMP_DSP_INFO,
 	CRAS_SERVER_DUMP_AUDIO_THREAD,
+	CRAS_SERVER_DUMP_SNAPSHOTS,
 	CRAS_SERVER_ADD_ACTIVE_NODE,
 	CRAS_SERVER_RM_ACTIVE_NODE,
 	CRAS_SERVER_ADD_TEST_DEV,
@@ -49,6 +54,8 @@
 	CRAS_SERVER_GET_HOTWORD_MODELS,
 	CRAS_SERVER_SET_HOTWORD_MODEL,
 	CRAS_SERVER_REGISTER_NOTIFICATION,
+	CRAS_SERVER_SET_AEC_DUMP,
+	CRAS_SERVER_RELOAD_AEC_CONFIG,
 };
 
 enum CRAS_CLIENT_MESSAGE_ID {
@@ -100,7 +107,28 @@
 	uint32_t flags;
 	struct cras_audio_format_packed format; /* rate, channel, sample size */
 	uint32_t dev_idx; /* device to attach stream, 0 if none */
+	uint64_t effects; /* Bit map of requested effects. */
 };
+
+/*
+ * Old version of connect message without 'effects' member defined.
+ * Used to check against when receiving invalid size of connect message.
+ * Expected to have proto_version set to 1.
+ * TODO(hychao): remove when all clients migrate to latest libcras.
+ */
+struct __attribute__ ((__packed__)) cras_connect_message_old {
+	struct cras_server_message header;
+	uint32_t proto_version;
+	enum CRAS_STREAM_DIRECTION direction; /* input/output/loopback */
+	cras_stream_id_t stream_id; /* unique id for this stream */
+	enum CRAS_STREAM_TYPE stream_type; /* media, or call, etc. */
+	uint32_t buffer_frames; /* Buffer size in frames. */
+	uint32_t cb_threshold; /* callback client when this much is left */
+	uint32_t flags;
+	struct cras_audio_format_packed format; /* rate, channel, sample size */
+	uint32_t dev_idx; /* device to attach stream, 0 if none */
+};
+
 static inline void cras_fill_connect_message(struct cras_connect_message *m,
 					   enum CRAS_STREAM_DIRECTION direction,
 					   cras_stream_id_t stream_id,
@@ -108,6 +136,7 @@
 					   size_t buffer_frames,
 					   size_t cb_threshold,
 					   uint32_t flags,
+					   uint64_t effects,
 					   struct cras_audio_format format,
 					   uint32_t dev_idx)
 {
@@ -118,6 +147,7 @@
 	m->buffer_frames = buffer_frames;
 	m->cb_threshold = cb_threshold;
 	m->flags = flags;
+	m->effects = effects;
 	pack_cras_audio_format(&m->format, &format);
 	m->dev_idx = dev_idx;
 	m->header.id = CRAS_SERVER_CONNECT_STREAM;
@@ -325,6 +355,18 @@
 	m->header.length = sizeof(*m);
 }
 
+/* Dump current audio thread snapshots to shard memory with the client. */
+struct __attribute__ ((__packed__)) cras_dump_snapshots {
+	struct cras_server_message header;
+};
+
+static inline void cras_fill_dump_snapshots(
+		struct cras_dump_snapshots *m)
+{
+	m->header.id = CRAS_SERVER_DUMP_SNAPSHOTS;
+	m->header.length = sizeof(*m);
+}
+
 /* Add a test device. */
 struct __attribute__ ((__packed__)) cras_add_test_dev {
 	struct cras_server_message header;
@@ -345,7 +387,7 @@
 	unsigned int command;
 	unsigned int iodev_idx;
 	unsigned int data_len;
-	uint8_t data[];
+	uint8_t data[CRAS_MAX_TEST_DATA_LEN];
 };
 
 static inline void cras_fill_test_dev_command(struct cras_test_dev_command *m,
@@ -369,11 +411,14 @@
 	m->length = sizeof(*m);
 }
 
-/* Configures the global remix converter. */
+/*
+ * Configures the global remix converter.
+ * `num_channels` must be less than `CRAS_MAX_REMIX_CHANNELS`.
+ */
 struct __attribute__ ((__packed__)) cras_config_global_remix {
 	struct cras_server_message header;
 	unsigned int num_channels;
-	float coefficient[];
+	float coefficient[CRAS_MAX_REMIX_CHANNELS];
 };
 
 static inline void cras_fill_config_global_remix_command(
@@ -421,6 +466,35 @@
 	memcpy(m->model_name, model_name, CRAS_HOTWORD_NAME_MAX_SIZE);
 }
 
+/* Set aec dump to start or stop. */
+struct __attribute__ ((__packed__)) cras_set_aec_dump {
+	struct cras_server_message header;
+	cras_stream_id_t stream_id;
+	unsigned int start;
+};
+
+static inline void cras_fill_set_aec_dump_message(
+		struct cras_set_aec_dump *m,
+		cras_stream_id_t stream_id,
+		unsigned int start)
+{
+	m->header.id = CRAS_SERVER_SET_AEC_DUMP;
+	m->header.length = sizeof(*m);
+	m->stream_id = stream_id;
+	m->start = start;
+}
+
+/* Reload the aec configuration. */
+struct __attribute__ ((__packed__)) cras_reload_aec_config {
+	struct cras_server_message header;
+};
+static inline void cras_fill_reload_aec_config(
+		struct cras_reload_aec_config *m)
+{
+	m->header.id = CRAS_SERVER_RELOAD_AEC_CONFIG;
+	m->header.length = sizeof(*m);
+}
+
 struct __attribute__ ((__packed__)) cras_register_notification {
 		struct cras_server_message header;
 		uint32_t msg_id;
@@ -465,12 +539,40 @@
 	cras_stream_id_t stream_id;
 	struct cras_audio_format_packed format;
 	uint32_t shm_max_size;
+	uint64_t effects;
+};
+/*
+ * Old version of stream connected message without effects defined.
+ * TODO(hychao): remove when all clients migrate to latest libcras.
+ */
+struct __attribute__ ((__packed__)) cras_client_stream_connected_old {
+	struct cras_client_message header;
+	int32_t err;
+	cras_stream_id_t stream_id;
+	struct cras_audio_format_packed format;
+	uint32_t shm_max_size;
 };
 static inline void cras_fill_client_stream_connected(
 		struct cras_client_stream_connected *m,
 		int err,
 		cras_stream_id_t stream_id,
 		struct cras_audio_format *format,
+		size_t shm_max_size,
+		uint64_t effects)
+{
+	m->err = err;
+	m->stream_id = stream_id;
+	pack_cras_audio_format(&m->format, format);
+	m->shm_max_size = shm_max_size;
+	m->effects = effects;
+	m->header.id = CRAS_CLIENT_STREAM_CONNECTED;
+	m->header.length = sizeof(struct cras_client_stream_connected);
+}
+static inline void cras_fill_client_stream_connected_old(
+		struct cras_client_stream_connected_old *m,
+		int err,
+		cras_stream_id_t stream_id,
+		struct cras_audio_format *format,
 		size_t shm_max_size)
 {
 	m->err = err;
@@ -478,7 +580,7 @@
 	pack_cras_audio_format(&m->format, format);
 	m->shm_max_size = shm_max_size;
 	m->header.id = CRAS_CLIENT_STREAM_CONNECTED;
-	m->header.length = sizeof(struct cras_client_stream_connected);
+	m->header.length = sizeof(struct cras_client_stream_connected_old);
 }
 
 /* Sent from server to client when audio debug information is requested. */
@@ -496,7 +598,7 @@
 struct cras_client_get_hotword_models_ready {
 	struct cras_client_message header;
 	int32_t hotword_models_size;
-	uint8_t hotword_models[0];
+	uint8_t hotword_models[CRAS_MAX_HOTWORD_MODELS];
 };
 static inline void cras_fill_client_get_hotword_models_ready(
 		struct cras_client_get_hotword_models_ready *m,
@@ -640,6 +742,7 @@
 enum CRAS_AUDIO_MESSAGE_ID {
 	AUDIO_MESSAGE_REQUEST_DATA,
 	AUDIO_MESSAGE_DATA_READY,
+	AUDIO_MESSAGE_DATA_CAPTURED,
 	NUM_AUDIO_MESSAGES
 };
 
diff --git a/cras/src/common/cras_metrics.c b/cras/src/common/cras_metrics.c
index ac005ff..dc5957a 100644
--- a/cras/src/common/cras_metrics.c
+++ b/cras/src/common/cras_metrics.c
@@ -7,30 +7,55 @@
 #include <syslog.h>
 #include <unistd.h>
 
-#define METRICS_CLIENT "metrics_client"
+#ifdef HAVE_LIB_METRICS
+#include <metrics/c_metrics_library.h>
 
 void cras_metrics_log_event(const char *event)
 {
-	syslog(LOG_DEBUG, "Log event: %s", event);
-	if (!fork()) {
-		const char *argv[] = {METRICS_CLIENT, "-v", event, NULL} ;
-		execvp(argv[0], (char * const *)argv);
-		_exit(1);
-	}
+	CMetricsLibrary handle;
+
+	syslog(LOG_DEBUG, "UMA event: %s", event);
+	handle = CMetricsLibraryNew();
+	CMetricsLibraryInit(handle);
+	CMetricsLibrarySendCrosEventToUMA(handle, event);
+	CMetricsLibraryDelete(handle);
 }
 
 void cras_metrics_log_histogram(const char *name, int sample, int min,
 				int max, int nbuckets)
 {
-	if (!fork()) {
-		char tmp[4][16];
-		snprintf(tmp[0], 16, "%d", sample);
-		snprintf(tmp[1], 16, "%d", min);
-		snprintf(tmp[2], 16, "%d", max);
-		snprintf(tmp[3], 16, "%d", nbuckets);
-		const char *argv[] = {METRICS_CLIENT, name, tmp[0], tmp[1],
-				      tmp[2], tmp[3], NULL};
-		execvp(argv[0], (char * const *)argv);
-		_exit(1);
-	}
+	CMetricsLibrary handle;
+
+	syslog(LOG_DEBUG, "UMA name: %s", name);
+	handle = CMetricsLibraryNew();
+	CMetricsLibraryInit(handle);
+	CMetricsLibrarySendToUMA(handle, name, sample, min, max, nbuckets);
+	CMetricsLibraryDelete(handle);
 }
+
+void cras_metrics_log_sparse_histogram(const char *name, int sample)
+{
+	CMetricsLibrary handle;
+
+	syslog(LOG_DEBUG, "UMA name: %s", name);
+	handle = CMetricsLibraryNew();
+	CMetricsLibraryInit(handle);
+	CMetricsLibrarySendSparseToUMA(handle, name, sample);
+	CMetricsLibraryDelete(handle);
+}
+
+#else
+void cras_metrics_log_event(const char *event)
+{
+}
+void cras_metrics_log_histogram(const char *name, int sample, int min,
+				int max, int nbuckets)
+{
+}
+void cras_metrics_log_enum_histogram(const char *name, int sample, int max)
+{
+}
+void cras_metrics_log_sparse_histogram(const char *name, int sample)
+{
+}
+#endif
diff --git a/cras/src/common/cras_metrics.h b/cras/src/common/cras_metrics.h
index 2d482eb..82c1d21 100644
--- a/cras/src/common/cras_metrics.h
+++ b/cras/src/common/cras_metrics.h
@@ -13,4 +13,7 @@
 void cras_metrics_log_histogram(const char *name, int sample, int min,
 				int max, int nbuckets);
 
+/* Sends sparse histogram data. */
+void cras_metrics_log_sparse_histogram(const char *name, int sample);
+
 #endif /* CRAS_METRICS_H_ */
diff --git a/cras/src/common/cras_observer_ops.h b/cras/src/common/cras_observer_ops.h
index 7265d98..108f166 100644
--- a/cras/src/common/cras_observer_ops.h
+++ b/cras/src/common/cras_observer_ops.h
@@ -49,6 +49,12 @@
 	void (*num_active_streams_changed)(void *context,
 					   enum CRAS_STREAM_DIRECTION dir,
 					   uint32_t num_active_streams);
+	/* Hotword triggered. */
+	void (*hotword_triggered)(void *context,
+				  int64_t tv_sec, int64_t tv_nsec);
+	/* State regarding whether non-empty audio is being played/captured has
+	 * changed. */
+	void (*non_empty_audio_state_changed)(void *context, int non_empty);
 };
 
 #endif /* CRAS_OBSERVER_OPS_H */
diff --git a/cras/src/common/cras_selinux_helper.c b/cras/src/common/cras_selinux_helper.c
new file mode 100644
index 0000000..59717bb
--- /dev/null
+++ b/cras/src/common/cras_selinux_helper.c
@@ -0,0 +1,11 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <selinux/restorecon.h>
+
+#include "cras_shm.h"
+
+int cras_selinux_restorecon(const char *pathname) {
+	return selinux_restorecon(pathname, 0);
+}
diff --git a/cras/src/common/cras_shm.c b/cras/src/common/cras_shm.c
index 6b4085a..bf42347 100644
--- a/cras/src/common/cras_shm.c
+++ b/cras/src/common/cras_shm.c
@@ -12,10 +12,40 @@
 #endif
 #include <errno.h>
 #include <syslog.h>
+#include <stdio.h>
 #include <string.h>
 
 #include "cras_shm.h"
 
+/* Set the correct SELinux label for SHM fds. */
+static void cras_shm_restorecon(int fd)
+{
+#ifdef CRAS_SELINUX
+	char fd_proc_path[64];
+
+	if (snprintf(fd_proc_path, sizeof(fd_proc_path), "/proc/self/fd/%d", fd) < 0) {
+		syslog(LOG_WARNING,
+		       "Couldn't construct proc symlink path of fd: %d", fd);
+		return;
+	}
+
+	/* Get the actual file-path for this fd. */
+	char *path = realpath(fd_proc_path, NULL);
+	if (path == NULL) {
+		syslog(LOG_WARNING, "Couldn't run realpath() for %s: %s",
+		       fd_proc_path, strerror(errno));
+		return;
+	}
+
+	if (cras_selinux_restorecon(path) < 0) {
+		syslog(LOG_WARNING, "Restorecon on %s failed: %s",
+		       fd_proc_path, strerror(errno));
+	}
+
+	free(path);
+#endif
+}
+
 #ifdef __BIONIC__
 
 int cras_shm_open_rw (const char *name, size_t size)
@@ -73,6 +103,9 @@
 		       name, strerror(-rc));
 		return rc;
 	}
+
+	cras_shm_restorecon(fd);
+
 	return fd;
 }
 
@@ -96,3 +129,30 @@
 }
 
 #endif
+
+void *cras_shm_setup(const char *name,
+		     size_t mmap_size,
+		     int *rw_fd_out,
+		     int *ro_fd_out)
+{
+	int rw_shm_fd = cras_shm_open_rw(name, mmap_size);
+	if (rw_shm_fd < 0)
+		return NULL;
+
+	/* mmap shm. */
+	void *exp_state = mmap(NULL, mmap_size,
+			       PROT_READ | PROT_WRITE, MAP_SHARED,
+			       rw_shm_fd, 0);
+	if (exp_state == (void *)-1)
+		return NULL;
+
+	/* Open a read-only copy to dup and pass to clients. */
+	int ro_shm_fd = cras_shm_reopen_ro(name, rw_shm_fd);
+	if (ro_shm_fd < 0)
+		return NULL;
+
+	*rw_fd_out = rw_shm_fd;
+	*ro_fd_out = ro_shm_fd;
+
+	return exp_state;
+}
diff --git a/cras/src/common/cras_shm.h b/cras/src/common/cras_shm.h
index 9a54132..df5dfd8 100644
--- a/cras/src/common/cras_shm.h
+++ b/cras/src/common/cras_shm.h
@@ -150,11 +150,17 @@
 	unsigned i = shm->area->write_buf_idx & CRAS_SHM_BUFFERS_MASK;
 	unsigned write_offset;
 	const unsigned frame_bytes = shm->config.frame_bytes;
+	unsigned written;
 
 	write_offset = cras_shm_check_write_offset(shm,
 						   shm->area->write_offset[i]);
-	if (frames)
-		*frames = limit_frames - (write_offset / frame_bytes);
+	written = write_offset / frame_bytes;
+	if (frames) {
+		if (limit_frames >= written)
+			*frames = limit_frames - written;
+		else
+			*frames = 0;
+	}
 
 	return cras_shm_buff_for_idx(shm, i) + write_offset;
 }
@@ -353,13 +359,10 @@
 		buf_idx = (buf_idx + 1) & CRAS_SHM_BUFFERS_MASK;
 		if (remainder < area->write_offset[buf_idx]) {
 			area->read_offset[buf_idx] = remainder;
-		} else {
-			area->read_offset[buf_idx] = 0;
+		} else if (remainder) {
+			/* Read all of this buffer too. */
 			area->write_offset[buf_idx] = 0;
-			if (remainder) {
-				/* Read all of this buffer too. */
-				buf_idx = (buf_idx + 1) & CRAS_SHM_BUFFERS_MASK;
-			}
+			buf_idx = (buf_idx + 1) & CRAS_SHM_BUFFERS_MASK;
 		}
 		area->read_buf_idx = buf_idx;
 	}
@@ -510,4 +513,31 @@
  */
 void cras_shm_close_unlink (const char *name, int fd);
 
+/*
+ * Configure shared memory for the system state.
+ * Args:
+ *    name - Name of the shared-memory area.
+ *    mmap_size - Amount of shared memor to map.
+ *    rw_fd_out - Filled with the RW fd for the shm region.
+ *    ro_fd_out - Filled with the RO fd for the shm region.
+ * Returns a pointer to the new shared memory region. Or NULL on error.
+ */
+void *cras_shm_setup(const char *name,
+		     size_t mmap_size,
+		     int *rw_fd_out,
+		     int *ro_fd_out);
+
+#ifdef CRAS_SELINUX
+/*
+ * Wrapper around selinux_restorecon(). This is helpful in unit tests because
+ * we can mock out the selinux_restorecon() behaviour there. That is required
+ * because selinux_restorecon() would fail in the unit tests, since there
+ * is no file_contexts file.
+ * Args:
+ *    pathname - Name of the file on which to run restorecon
+ * Returns 0 on success, otherwise -1 and errno is set appropriately.
+ */
+int cras_selinux_restorecon(const char *pathname);
+#endif
+
 #endif /* CRAS_SHM_H_ */
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index 6c0ce0f..d0463fa 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -26,6 +26,7 @@
 	NO_DEVICE,
 	SILENT_RECORD_DEVICE,
 	SILENT_PLAYBACK_DEVICE,
+	SILENT_HOTWORD_DEVICE,
 	MAX_SPECIAL_DEVICE_IDX
 };
 
@@ -66,11 +67,17 @@
  *      device is ready. Input streams only.
  *  HOTWORD_STREAM - This stream is used only to listen for hotwords such as "OK
  *      Google".  Hardware will wake the device when this phrase is heard.
+ *  TRIGGER_ONLY - This stream only wants to receive when the data is available
+ *      and does not want to receive data. Used with HOTWORD_STREAM.
+ *  SERVER_ONLY - This stream doesn't associate to a client. It's used mainly
+ *      for audio data to flow from hardware through iodev's dsp pipeline.
  */
 enum CRAS_INPUT_STREAM_FLAG {
 	BULK_AUDIO_OK = 0x01,
 	USE_DEV_TIMING = 0x02,
 	HOTWORD_STREAM = BULK_AUDIO_OK | USE_DEV_TIMING,
+	TRIGGER_ONLY = 0x04,
+	SERVER_ONLY = 0x08,
 };
 
 /*
@@ -109,6 +116,7 @@
 	CRAS_STREAM_TYPE_VOICE_COMMUNICATION,
 	CRAS_STREAM_TYPE_SPEECH_RECOGNITION,
 	CRAS_STREAM_TYPE_PRO_AUDIO,
+	CRAS_STREAM_TYPE_ACCESSIBILITY,
 	CRAS_STREAM_NUM_TYPES,
 };
 
@@ -123,11 +131,20 @@
 	ENUM_STR(CRAS_STREAM_TYPE_VOICE_COMMUNICATION)
 	ENUM_STR(CRAS_STREAM_TYPE_SPEECH_RECOGNITION)
 	ENUM_STR(CRAS_STREAM_TYPE_PRO_AUDIO)
+	ENUM_STR(CRAS_STREAM_TYPE_ACCESSIBILITY)
 	default:
 		return "INVALID_STREAM_TYPE";
 	}
 }
 
+/* Effects that can be enabled for a CRAS stream. */
+enum CRAS_STREAM_EFFECT {
+	APM_ECHO_CANCELLATION = (1 << 0),
+	APM_NOISE_SUPRESSION = (1 << 1),
+	APM_GAIN_CONTROL = (1 << 2),
+	APM_VOICE_DETECTION = (1 << 3),
+};
+
 /* Information about a client attached to the server. */
 struct __attribute__ ((__packed__)) cras_attached_client_info {
 	uint32_t id;
@@ -160,6 +177,7 @@
 #define CRAS_MAX_IODEVS 20
 #define CRAS_MAX_IONODES 20
 #define CRAS_MAX_ATTACHED_CLIENTS 20
+#define CRAS_MAX_AUDIO_THREAD_SNAPSHOTS 10
 #define CRAS_HOTWORD_STRING_SIZE 256
 #define MAX_DEBUG_DEVS 4
 #define MAX_DEBUG_STREAMS 8
@@ -204,6 +222,7 @@
 	AUDIO_THREAD_ODEV_LEAVE_NO_STREAMS,
 	AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS,
 	AUDIO_THREAD_FILL_ODEV_ZEROS,
+	AUDIO_THREAD_UNDERRUN,
 	AUDIO_THREAD_SEVERE_UNDERRUN,
 };
 
@@ -234,6 +253,7 @@
 	uint8_t direction;
 	uint32_t num_underruns;
 	uint32_t num_severe_underruns;
+	uint32_t highest_hw_level;
 };
 
 struct __attribute__ ((__packed__)) audio_stream_debug_info {
@@ -243,6 +263,7 @@
 	uint32_t stream_type;
 	uint32_t buffer_frames;
 	uint32_t cb_threshold;
+	uint64_t effects;
 	uint32_t flags;
 	uint32_t frame_rate;
 	uint32_t num_channels;
@@ -261,6 +282,35 @@
 	struct audio_thread_event_log log;
 };
 
+/*
+ * All event enums should be less then AUDIO_THREAD_EVENT_TYPE_COUNT,
+ * or they will be ignored by the handler.
+ */
+enum CRAS_AUDIO_THREAD_EVENT_TYPE {
+	AUDIO_THREAD_EVENT_BUSYLOOP,
+	AUDIO_THREAD_EVENT_DEBUG,
+	AUDIO_THREAD_EVENT_SEVERE_UNDERRUN,
+	AUDIO_THREAD_EVENT_UNDERRUN,
+	AUDIO_THREAD_EVENT_TYPE_COUNT,
+};
+
+/*
+ * Structure of snapshot for audio thread.
+ */
+struct __attribute__ ((__packed__)) cras_audio_thread_snapshot {
+	struct timespec timestamp;
+	enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type;
+	struct audio_debug_info audio_debug_info;
+};
+
+/*
+ * Ring buffer for storing snapshots.
+ */
+struct __attribute__ ((__packed__)) cras_audio_thread_snapshot_buffer{
+	struct cras_audio_thread_snapshot snapshots[
+			CRAS_MAX_AUDIO_THREAD_SNAPSHOTS];
+	int pos;
+};
 
 /* The server state that is shared with clients.
  *    state_version - Version of this structure.
@@ -302,6 +352,11 @@
  *    audio_debug_info - Debug data filled in when a client requests it. This
  *        isn't protected against concurrent updating, only one client should
  *        use it.
+ *    default_output_buffer_size - Default output buffer size in frames.
+ *    non_empty_status - Whether any non-empty audio is being
+ *        played/captured.
+ *    aec_supported - Flag to indicate if system aec is supported.
+ *    snapshot_buffer - ring buffer for storing audio thread snapshots.
  */
 #define CRAS_SERVER_STATE_VERSION 2
 struct __attribute__ ((packed, aligned(4))) cras_server_state {
@@ -334,6 +389,10 @@
 	uint32_t num_active_streams[CRAS_NUM_DIRECTIONS];
 	struct cras_timespec last_active_stream_time;
 	struct audio_debug_info audio_debug_info;
+	int32_t default_output_buffer_size;
+	int32_t non_empty_status;
+	int32_t aec_supported;
+	struct cras_audio_thread_snapshot_buffer snapshot_buffer;
 };
 
 /* Actions for card add/remove/change. */
diff --git a/cras/src/common/edid_utils.c b/cras/src/common/edid_utils.c
index 4a1a0a6..2707b55 100644
--- a/cras/src/common/edid_utils.c
+++ b/cras/src/common/edid_utils.c
@@ -684,11 +684,11 @@
 		"Input: %s, vid level %d, %s, %s %s %s %s sync, %dx%dcm, Gamma %f\n",
 		(edid_data[EDID_VIDEO_IN] & 0x80) ? "digital" : "analog",
 		(edid_data[EDID_VIDEO_IN]>>5) & 3,
-		(edid_data[EDID_VIDEO_IN] * 0x10) ? "Blank to black" : "",
-		(edid_data[EDID_VIDEO_IN] * 0x08) ? "Separate" : "",
-		(edid_data[EDID_VIDEO_IN] * 0x04) ? "Composite" : "",
-		(edid_data[EDID_VIDEO_IN] * 0x02) ? "On-green" : "",
-		(edid_data[EDID_VIDEO_IN] * 0x01) ? "Serration V" : "",
+		(edid_data[EDID_VIDEO_IN] & 0x10) ? "Blank to black" : "",
+		(edid_data[EDID_VIDEO_IN] & 0x08) ? "Separate" : "",
+		(edid_data[EDID_VIDEO_IN] & 0x04) ? "Composite" : "",
+		(edid_data[EDID_VIDEO_IN] & 0x02) ? "On-green" : "",
+		(edid_data[EDID_VIDEO_IN] & 0x01) ? "Serration V" : "",
 		edid_data[EDID_MAX_HSIZE], edid_data[EDID_MAX_VSIZE],
 		1.0+((float)edid_data[EDID_GAMMA]/100.0));
 
diff --git a/cras/src/dsp/dcblock.c b/cras/src/dsp/dcblock.c
new file mode 100644
index 0000000..87379e4
--- /dev/null
+++ b/cras/src/dsp/dcblock.c
@@ -0,0 +1,42 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdlib.h>
+#include "dcblock.h"
+
+struct dcblock {
+        float R;
+        float x_prev;
+        float y_prev;
+};
+
+struct dcblock *dcblock_new(float R)
+{
+	struct dcblock *dcblock = (struct dcblock *)calloc(1, sizeof(*dcblock));
+	dcblock->R = R;
+	return dcblock;
+}
+
+void dcblock_free(struct dcblock *dcblock)
+{
+	free(dcblock);
+}
+
+/* This is the prototype of the processing loop. */
+void dcblock_process(struct dcblock *dcblock, float *data, int count)
+{
+	int n;
+	float x_prev = dcblock->x_prev;
+	float y_prev = dcblock->y_prev;
+	float R = dcblock->R;
+	for (n = 0; n < count; n++) {
+		float x = data[n];
+		data[n] = x - x_prev + R * y_prev;
+		y_prev = data[n];
+		x_prev = x;
+	}
+	dcblock->x_prev = x_prev;
+	dcblock->y_prev = y_prev;
+}
diff --git a/cras/src/dsp/dcblock.h b/cras/src/dsp/dcblock.h
new file mode 100644
index 0000000..b723470
--- /dev/null
+++ b/cras/src/dsp/dcblock.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef DCBLOCK_H_
+#define DCBLOCK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A DC blocking filter. */
+
+struct dcblock;
+
+/*
+ * Create a DC blocking filter.
+ *
+ * Transfer fn: (1 - z^-1) / (1 - R * z^-1)
+ */
+struct dcblock *dcblock_new(float R);
+
+/* Free a DC blocking filter. */
+void dcblock_free(struct dcblock *dcblock);
+
+/* Process a buffer of audio data through the filter.
+ * Args:
+ *    dcblock - The filter we want to use.
+ *    data - The array of audio samples.
+ *    count - The number of elements in the data array to process.
+ */
+void dcblock_process(struct dcblock *dcblock, float *data, int count);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* DCBLOCK_H_ */
diff --git a/cras/src/dsp/dsp_util.c b/cras/src/dsp/dsp_util.c
index 6de9a4e..c95f0c5 100644
--- a/cras/src/dsp/dsp_util.c
+++ b/cras/src/dsp/dsp_util.c
@@ -3,6 +3,9 @@
  * found in the LICENSE file.
  */
 
+#include <limits.h>
+#include <syslog.h>
+
 #include "dsp_util.h"
 
 #ifndef max
@@ -373,8 +376,8 @@
 #define interleave_stereo interleave_stereo
 #endif
 
-void dsp_util_deinterleave(int16_t *input, float *const *output, int channels,
-			   int frames)
+static void dsp_util_deinterleave_s16le(int16_t *input, float *const *output,
+				 int channels, int frames)
 {
 	float *output_ptr[channels];
 	int i, j;
@@ -394,8 +397,83 @@
 			*(output_ptr[j]++) = *input++ / 32768.0f;
 }
 
-void dsp_util_interleave(float *const *input, int16_t *output, int channels,
-			 int frames)
+
+static void dsp_util_deinterleave_s24le(int32_t *input, float *const *output,
+					int channels, int frames)
+{
+	float *output_ptr[channels];
+	int i, j;
+
+	for (i = 0; i < channels; i++)
+		output_ptr[i] = output[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, input++)
+			*(output_ptr[j]++) =
+				(*input << 8) / 2147483648.0f;
+}
+
+static void dsp_util_deinterleave_s243le(uint8_t *input, float *const *output,
+					 int channels, int frames)
+{
+	float *output_ptr[channels];
+	int32_t sample;
+	int i, j;
+
+	for (i = 0; i < channels; i++)
+		output_ptr[i] = output[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, input += 3) {
+			sample = 0;
+			memcpy((uint8_t *)&sample + 1, input, 3);
+			*(output_ptr[j]++) = sample / 2147483648.0f;
+		}
+}
+
+static void dsp_util_deinterleave_s32le(int32_t *input, float *const *output,
+					int channels, int frames)
+{
+	float *output_ptr[channels];
+	int i, j;
+
+	for (i = 0; i < channels; i++)
+		output_ptr[i] = output[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, input++)
+			*(output_ptr[j]++) = *input / 2147483648.0f;
+}
+
+int dsp_util_deinterleave(uint8_t *input, float *const *output, int channels,
+			  snd_pcm_format_t format, int frames)
+{
+	switch (format) {
+	case SND_PCM_FORMAT_S16_LE:
+		dsp_util_deinterleave_s16le((int16_t *)input, output,
+					    channels, frames);
+		break;
+	case SND_PCM_FORMAT_S24_LE:
+		dsp_util_deinterleave_s24le((int32_t *)input, output,
+					  channels, frames);
+		break;
+	case SND_PCM_FORMAT_S24_3LE:
+		dsp_util_deinterleave_s243le(input, output,
+					     channels, frames);
+		break;
+	case SND_PCM_FORMAT_S32_LE:
+		dsp_util_deinterleave_s32le((int32_t *)input, output,
+					     channels, frames);
+		break;
+	default:
+		syslog(LOG_ERR, "Invalid format to deinterleave");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void dsp_util_interleave_s16le(float *const *input, int16_t *output,
+				      int channels, int frames)
 {
 	float *input_ptr[channels];
 	int i, j;
@@ -418,6 +496,87 @@
 		}
 }
 
+static void dsp_util_interleave_s24le(float *const *input, int32_t *output,
+				      int channels, int frames)
+{
+	float *input_ptr[channels];
+	int i, j;
+
+	for (i = 0; i < channels; i++)
+		input_ptr[i] = input[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, output++) {
+			float f = *(input_ptr[j]++) * 2147483648.0f;
+			f += (f >= 0) ? 0.5f : -0.5f;
+			*output = max((float)INT_MIN, min((float)INT_MAX, f));
+			*output >>= 8;
+		}
+}
+
+static void dsp_util_interleave_s243le(float *const *input, uint8_t *output,
+				       int channels, int frames)
+{
+	float *input_ptr[channels];
+	int i, j;
+	int32_t tmp;
+
+	for (i = 0; i < channels; i++)
+		input_ptr[i] = input[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, output += 3) {
+			float f = *(input_ptr[j]++) * 2147483648.0f;
+			f += (f >= 0) ? 0.5f : -0.5f;
+			tmp = max((float)INT_MIN, min((float)INT_MAX, f));
+			tmp >>= 8;
+			memcpy(output, &tmp, 3);
+		}
+}
+
+static void dsp_util_interleave_s32le(float *const *input, int32_t *output,
+				      int channels, int frames)
+{
+	float *input_ptr[channels];
+	int i, j;
+
+	for (i = 0; i < channels; i++)
+		input_ptr[i] = input[i];
+
+	for (i = 0; i < frames; i++)
+		for (j = 0; j < channels; j++, output++) {
+			float f = *(input_ptr[j]++) * 2147483648.0f;
+			f += (f >= 0) ? 0.5f : -0.5f;
+			*output = max((float)INT_MIN, min((float)INT_MAX, f));
+		}
+}
+
+int dsp_util_interleave(float *const *input, uint8_t *output, int channels,
+			snd_pcm_format_t format, int frames)
+{
+	switch (format) {
+	case SND_PCM_FORMAT_S16_LE:
+		dsp_util_interleave_s16le(input, (int16_t *)output,
+					  channels, frames);
+		break;
+	case SND_PCM_FORMAT_S24_LE:
+		dsp_util_interleave_s24le(input, (int32_t *)output,
+					  channels, frames);
+		break;
+	case SND_PCM_FORMAT_S24_3LE:
+		dsp_util_interleave_s243le(input, output, channels, frames);
+		break;
+	case SND_PCM_FORMAT_S32_LE:
+		dsp_util_interleave_s32le(input, (int32_t *)output,
+					  channels, frames);
+		break;
+	default:
+		syslog(LOG_ERR, "Invalid format to interleave");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 void dsp_enable_flush_denormal_to_zero()
 {
 #if defined(__i386__) || defined(__x86_64__)
diff --git a/cras/src/dsp/dsp_util.h b/cras/src/dsp/dsp_util.h
index 19fc35a..7ca227a 100644
--- a/cras/src/dsp/dsp_util.h
+++ b/cras/src/dsp/dsp_util.h
@@ -12,6 +12,8 @@
 
 #include <stdint.h>
 
+#include "cras_audio_format.h"
+
 /* Converts from interleaved int16_t samples to non-interleaved float samples.
  * The int16_t samples have range [-32768, 32767], and the float samples have
  * range [-1.0, 1.0].
@@ -20,9 +22,11 @@
  *    output - Pointers to output buffers. There are "channels" output buffers.
  *    channels - The number of samples per frame.
  *    frames - The number of frames to convert.
+ * Returns:
+ *    Negative error if format isn't supported, otherwise 0.
  */
-void dsp_util_deinterleave(int16_t *input, float *const *output, int channels,
-			   int frames);
+int dsp_util_deinterleave(uint8_t *input, float *const *output, int channels,
+			  snd_pcm_format_t format, int frames);
 
 /* Converts from non-interleaved float samples to interleaved int16_t samples.
  * The int16_t samples have range [-32768, 32767], and the float samples have
@@ -33,9 +37,11 @@
  *        frame.
  *    channels - The number of samples per frame.
  *    frames - The number of frames to convert.
+ * Returns:
+ *    Negative error if format isn't supported, otherwise 0.
  */
-void dsp_util_interleave(float *const *input, int16_t *output, int channels,
-			 int frames);
+int dsp_util_interleave(float *const *input, uint8_t *output, int channels,
+			snd_pcm_format_t format, int frames);
 
 /* Disables denormal numbers in floating point calculation. Denormal numbers
  * happens often in IIR filters, and it can be very slow.
diff --git a/cras/src/dsp/tests/dcblock_test.c b/cras/src/dsp/tests/dcblock_test.c
new file mode 100644
index 0000000..2093a5d
--- /dev/null
+++ b/cras/src/dsp/tests/dcblock_test.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "dsp_test_util.h"
+#include "dsp_util.h"
+#include "dcblock.h"
+#include "raw.h"
+
+#ifndef min
+#define min(a, b) ({ __typeof__(a) _a = (a);	\
+			__typeof__(b) _b = (b);	\
+			_a < _b ? _a : _b; })
+#endif
+
+static double tp_diff(struct timespec *tp2, struct timespec *tp1)
+{
+	return (tp2->tv_sec - tp1->tv_sec)
+		+ (tp2->tv_nsec - tp1->tv_nsec) * 1e-9;
+}
+
+/* Processes a buffer of data chunk by chunk using the filter */
+static void process(struct dcblock *dcblock, float *data, int count)
+{
+	int start;
+	for (start = 0; start < count; start += 128)
+		dcblock_process(dcblock,
+                                data + start,
+                                min(128, count - start));
+}
+
+/* Runs the filters on an input file */
+static void test_file(const char *input_filename, const char *output_filename)
+{
+	size_t frames;
+	struct timespec tp1, tp2;
+	struct dcblock *dcblockl;
+	struct dcblock *dcblockr;
+
+	float *data = read_raw(input_filename, &frames);
+
+	dcblockl = dcblock_new(0.995);
+	dcblockr = dcblock_new(0.995);
+	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp1);
+	process(dcblockl, data, frames);
+	process(dcblockr, data+frames, frames);
+	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp2);
+	printf("processing takes %g seconds for %zu samples\n",
+	       tp_diff(&tp2, &tp1), frames);
+	dcblock_free(dcblockl);
+	dcblock_free(dcblockr);
+
+	write_raw(output_filename, data, frames);
+	free(data);
+}
+
+int main(int argc, char **argv)
+{
+	dsp_enable_flush_denormal_to_zero();
+	if (dsp_util_has_denormal())
+		printf("denormal still supported?\n");
+	else
+		printf("denormal disabled\n");
+	dsp_util_clear_fp_exceptions();
+
+	if (argc == 3)
+		test_file(argv[1], argv[2]);
+	else
+		printf("Usage: dcblock_test input.raw output.raw\n");
+
+	dsp_util_print_fp_exceptions();
+	return 0;
+}
diff --git a/cras/src/dsp/tests/dsp_util_test.c b/cras/src/dsp/tests/dsp_util_test.c
index 3a22db3..f545fc8 100644
--- a/cras/src/dsp/tests/dsp_util_test.c
+++ b/cras/src/dsp/tests/dsp_util_test.c
@@ -113,8 +113,8 @@
 
 	/* measure optimized interleave */
 	for (i = 0; i < ITERATIONS; ++i) {
-		dsp_util_interleave(out_floats_ptr_c, out_shorts_opt, 2,
-				    samples);
+		dsp_util_interleave(out_floats_ptr_c, (uint8_t *)out_shorts_opt,
+				    2, SND_PCM_FORMAT_S16_LE, samples);
 	}
 
 	max_diff = 0;
@@ -135,7 +135,8 @@
 					samples);
 
 	/* measure optimized deinterleave */
-	dsp_util_deinterleave(in_shorts, out_floats_ptr_opt, 2, samples);
+	dsp_util_deinterleave((uint8_t *)in_shorts, out_floats_ptr_opt, 2,
+			      SND_PCM_FORMAT_S16_LE, samples);
 
 	d = memcmp(out_floats_ptr_c[0], out_floats_ptr_opt[0], samples * 4);
 	if (d) printf("left compare %d, %f %f\n", d, out_floats_ptr_c[0][0],
@@ -307,8 +308,9 @@
 		/* measure optimized interleave */
 		clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
 		for (i = 0; i < ITERATIONS; ++i) {
-			dsp_util_interleave(out_floats_ptr_c, out_shorts_opt, 2,
-					    samples);
+			dsp_util_interleave(out_floats_ptr_c,
+					    (uint8_t *)out_shorts_opt, 2,
+					    SND_PCM_FORMAT_S16_LE, samples);
 		}
 		clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
 		diff = (BILLION * (end.tv_sec - start.tv_sec) +
@@ -344,8 +346,9 @@
 		/* Measure optimized deinterleave */
 		clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
 		for (i = 0; i < ITERATIONS; ++i) {
-			dsp_util_deinterleave(in_shorts, out_floats_ptr_opt, 2,
-					      samples);
+			dsp_util_deinterleave((uint8_t *)in_shorts,
+					      out_floats_ptr_opt, 2,
+					      SND_PCM_FORMAT_S16_LE, samples);
 		}
 		clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
 		diff = (BILLION * (end.tv_sec - start.tv_sec) +
@@ -373,4 +376,4 @@
 	free(out_shorts_opt);
 
 	return 0;
-}
\ No newline at end of file
+}
diff --git a/cras/src/fuzz/Dockerfile b/cras/src/fuzz/Dockerfile
new file mode 100644
index 0000000..cf08a6e
--- /dev/null
+++ b/cras/src/fuzz/Dockerfile
@@ -0,0 +1,57 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Defines a docker image that can build cras fuzzers.
+#
+FROM gcr.io/oss-fuzz-base/base-builder
+
+RUN apt-get -y update && \
+      apt-get install -y \
+      automake \
+      build-essential \
+      cmake \
+      ctags \
+      g++ \
+      gdb \
+      git \
+      ladspa-sdk \
+      libasound-dev \
+      libdbus-1-dev \
+      libgtest-dev \
+      libncurses5-dev \
+      libsbc-dev \
+      libsndfile-dev \
+      libspeexdsp-dev \
+      libtool \
+      libudev-dev \
+      wget
+RUN apt-get clean
+RUN cd /tmp && git clone https://github.com/ndevilla/iniparser.git && \
+      cd iniparser && \
+      make && \
+      cp libiniparser.* /usr/local/lib && \
+      cp src/dictionary.h src/iniparser.h /usr/local/include && \
+      chmod 644 /usr/local/include/dictionary.h /usr/local/include/iniparser.h && \
+      chmod 644 /usr/local/lib/libiniparser.a && \
+      chmod 755 /usr/local/lib/libiniparser.so.*
+RUN cd /usr/src/gtest && \
+      cmake . && \
+      make && \
+      chmod 644 *.a && \
+      cp *.a /usr/local/lib
+
+# Need to build and install alsa so there is a static lib.
+RUN mkdir -p /tmp/alsa-build && cd /tmp/alsa-build && \
+      wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.4.1.tar.bz2 && \
+      bzip2 -f -d alsa-lib-* && \
+      tar xf alsa-lib-* && \
+      cd alsa-lib-* && \
+      ./configure --enable-static --disable-shared && \
+      make clean && \
+      make -j$(nproc) all && \
+      make install
+
+
+COPY . /src/cras/
+COPY src/fuzz/build.sh /src/
diff --git a/cras/src/fuzz/README b/cras/src/fuzz/README
new file mode 100644
index 0000000..96e9076
--- /dev/null
+++ b/cras/src/fuzz/README
@@ -0,0 +1,23 @@
+This directory contains source code and build scripts for coverage-guided
+fuzzers.
+
+Detailed instructions are available at:
+
+  https://github.com/google/oss-fuzz/blob/master/docs/
+
+Quick start:
+
+  Build a container from the cras directory
+
+    $ sudo docker build -t ossfuzz/cras -f src/fuzz/Dockerfile .
+
+  Build fuzzers
+
+    $ sudo docker run -ti --rm -v $(pwd):/src/cras -v /tmp/fuzzers:/out \
+                 ossfuzz/cras
+
+  Look in /tmp/fuzzers to see the executables. Run them like so:
+
+    $ sudo docker run -ti -v $(pwd)/src/fuzz/corpus:/corpus \
+            -v /tmp/fuzzers:/out ossfuzz/base-runner /out/rclient_message \
+            /corpus -runs=100
diff --git a/cras/src/fuzz/build.sh b/cras/src/fuzz/build.sh
new file mode 100755
index 0000000..ac4ad2f
--- /dev/null
+++ b/cras/src/fuzz/build.sh
@@ -0,0 +1,22 @@
+#!/bin/bash -eux
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Builds fuzzers from within a container into /out/ directory.
+# Expects /src/cras to contain a cras checkout.
+
+mkdir $WORK/cras
+cd $SRC/cras
+./git_prepare.sh
+./configure --disable-dbus --disable-webrtc-apm
+make -j$(nproc)
+
+$CXX $CXXFLAGS $FUZZER_LDFLAGS \
+  $SRC/cras/src/fuzz/rclient_message.cc -o $OUT/rclient_message \
+  -I $SRC/cras/src/server \
+  -I $SRC/cras/src/common \
+  $SRC/cras/src/.libs/libcrasserver.a \
+  -lpthread -lrt -ludev -ldl -lm \
+  -lFuzzingEngine \
+  -Wl,-Bstatic -liniparser -lasound -lspeexdsp -Wl,-Bdynamic
diff --git a/cras/src/fuzz/corpus/06051efd871be7eb2b4baed61c3ba53fe0b27833 b/cras/src/fuzz/corpus/06051efd871be7eb2b4baed61c3ba53fe0b27833
new file mode 100644
index 0000000..09605ec
--- /dev/null
+++ b/cras/src/fuzz/corpus/06051efd871be7eb2b4baed61c3ba53fe0b27833
Binary files differ
diff --git a/cras/src/fuzz/corpus/06e2b48cb549540ab62c0a62a1480f3de7807896 b/cras/src/fuzz/corpus/06e2b48cb549540ab62c0a62a1480f3de7807896
new file mode 100644
index 0000000..fd0149b
--- /dev/null
+++ b/cras/src/fuzz/corpus/06e2b48cb549540ab62c0a62a1480f3de7807896
Binary files differ
diff --git a/cras/src/fuzz/corpus/1cea528aa30177cb48d9b279b7d1f115b5a60c01 b/cras/src/fuzz/corpus/1cea528aa30177cb48d9b279b7d1f115b5a60c01
new file mode 100644
index 0000000..ffd27be
--- /dev/null
+++ b/cras/src/fuzz/corpus/1cea528aa30177cb48d9b279b7d1f115b5a60c01
Binary files differ
diff --git a/cras/src/fuzz/corpus/348b671615268f526f174ed2f89d887a26ee8b7a b/cras/src/fuzz/corpus/348b671615268f526f174ed2f89d887a26ee8b7a
new file mode 100644
index 0000000..0b7a92f
--- /dev/null
+++ b/cras/src/fuzz/corpus/348b671615268f526f174ed2f89d887a26ee8b7a
Binary files differ
diff --git a/cras/src/fuzz/corpus/756692a278ad08a8f3a9e198b1436620c53247e9 b/cras/src/fuzz/corpus/756692a278ad08a8f3a9e198b1436620c53247e9
new file mode 100644
index 0000000..c768e08
--- /dev/null
+++ b/cras/src/fuzz/corpus/756692a278ad08a8f3a9e198b1436620c53247e9
Binary files differ
diff --git a/cras/src/fuzz/corpus/90c740f0e2e83b050ba7ccc2b963f2cb6bf23bc1 b/cras/src/fuzz/corpus/90c740f0e2e83b050ba7ccc2b963f2cb6bf23bc1
new file mode 100644
index 0000000..05718e1
--- /dev/null
+++ b/cras/src/fuzz/corpus/90c740f0e2e83b050ba7ccc2b963f2cb6bf23bc1
Binary files differ
diff --git a/cras/src/fuzz/corpus/9448261a4711bdb5e62490e57f78a9a8d99cfd90 b/cras/src/fuzz/corpus/9448261a4711bdb5e62490e57f78a9a8d99cfd90
new file mode 100644
index 0000000..6eaf65c
--- /dev/null
+++ b/cras/src/fuzz/corpus/9448261a4711bdb5e62490e57f78a9a8d99cfd90
Binary files differ
diff --git a/cras/src/fuzz/corpus/b148b3ca1a089964a122b42170eb39e41461e8de b/cras/src/fuzz/corpus/b148b3ca1a089964a122b42170eb39e41461e8de
new file mode 100644
index 0000000..12f9ff6
--- /dev/null
+++ b/cras/src/fuzz/corpus/b148b3ca1a089964a122b42170eb39e41461e8de
Binary files differ
diff --git a/cras/src/fuzz/corpus/c66fae687ac462af608f9f22ea1b9493b66fe844 b/cras/src/fuzz/corpus/c66fae687ac462af608f9f22ea1b9493b66fe844
new file mode 100644
index 0000000..74c5e05
--- /dev/null
+++ b/cras/src/fuzz/corpus/c66fae687ac462af608f9f22ea1b9493b66fe844
Binary files differ
diff --git a/cras/src/fuzz/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 b/cras/src/fuzz/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cras/src/fuzz/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709
diff --git a/cras/src/fuzz/corpus/da6b17d11511d4c4eb14933aa404715e753e2c02 b/cras/src/fuzz/corpus/da6b17d11511d4c4eb14933aa404715e753e2c02
new file mode 100644
index 0000000..99ceca0
--- /dev/null
+++ b/cras/src/fuzz/corpus/da6b17d11511d4c4eb14933aa404715e753e2c02
Binary files differ
diff --git a/cras/src/fuzz/corpus/e578774a32e52e17461c30848f554f78c3ffda66 b/cras/src/fuzz/corpus/e578774a32e52e17461c30848f554f78c3ffda66
new file mode 100644
index 0000000..6bfd3e0
--- /dev/null
+++ b/cras/src/fuzz/corpus/e578774a32e52e17461c30848f554f78c3ffda66
Binary files differ
diff --git a/cras/src/fuzz/corpus/e85fb46a837d5a798d371573b41fd60e1e1486ed b/cras/src/fuzz/corpus/e85fb46a837d5a798d371573b41fd60e1e1486ed
new file mode 100644
index 0000000..ed467b1
--- /dev/null
+++ b/cras/src/fuzz/corpus/e85fb46a837d5a798d371573b41fd60e1e1486ed
Binary files differ
diff --git a/cras/src/fuzz/rclient_message.cc b/cras/src/fuzz/rclient_message.cc
new file mode 100644
index 0000000..9ae5fb1
--- /dev/null
+++ b/cras/src/fuzz/rclient_message.cc
@@ -0,0 +1,50 @@
+/* Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+extern "C" {
+#include "cras_iodev_list.h"
+#include "cras_mix.h"
+#include "cras_observer.h"
+#include "cras_rclient.h"
+#include "cras_shm.h"
+#include "cras_system_state.h"
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  cras_rclient *client = cras_rclient_create(0, 0);
+  cras_rclient_buffer_from_client(client, data, size, -1);
+  cras_rclient_destroy(client);
+
+  return 0;
+}
+
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
+  char *shm_name;
+  if (asprintf(&shm_name, "/cras-%d", getpid()) < 0)
+    exit(-ENOMEM);
+  struct cras_server_state *exp_state = (struct cras_server_state *)
+    calloc(1, sizeof(*exp_state));
+  if (!exp_state)
+    exit(-1);
+  int rw_shm_fd = open("/dev/null", O_RDWR);
+  int ro_shm_fd = open("/dev/null", O_RDONLY);
+  cras_system_state_init("/tmp",
+                         shm_name,
+                         rw_shm_fd,
+                         ro_shm_fd,
+                         exp_state,
+                         sizeof(*exp_state));
+  free(shm_name);
+
+  cras_observer_server_init();
+  cras_mix_init(0);
+  cras_iodev_list_init();
+
+  return 0;
+}
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index 98acde1..4f9f131 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -57,6 +57,8 @@
 static const size_t MAX_CMD_MSG_LEN = 256;
 static const size_t SERVER_SHUTDOWN_TIMEOUT_US = 500000;
 static const size_t SERVER_CONNECT_TIMEOUT_MS = 1000;
+static const size_t HOTWORD_FRAME_RATE = 16000;
+static const size_t HOTWORD_BLOCK_SIZE = 320;
 
 /* Commands sent from the user to the running client. */
 enum {
@@ -123,6 +125,7 @@
 	size_t cb_threshold;
 	enum CRAS_STREAM_TYPE stream_type;
 	uint32_t flags;
+	uint64_t effects;
 	void *user_data;
 	cras_playback_cb_t aud_cb;
 	cras_unified_cb_t unified_cb;
@@ -256,6 +259,20 @@
 ((struct client_int *)((char *)cptr - offsetof(struct client_int, client)))
 
 /*
+ * Holds the hotword stream format, params, and ID used when waiting for a
+ * hotword. The structure is created by cras_client_enable_hotword_callback and
+ * destroyed by cras_client_disable_hotword_callback.
+ */
+struct cras_hotword_handle {
+	struct cras_audio_format *format;
+	struct cras_stream_params *params;
+	cras_stream_id_t stream_id;
+	cras_hotword_trigger_cb_t trigger_cb;
+	cras_hotword_error_cb_t err_cb;
+	void *user_data;
+};
+
+/*
  * Local Helpers
  */
 
@@ -1042,6 +1059,27 @@
 	cras_shm_buffer_read_current(&stream->capture_shm, num_frames);
 }
 
+static int send_capture_reply(struct client_stream *stream,
+			      unsigned int frames,
+			      int err)
+{
+	struct audio_message aud_msg;
+	int rc;
+
+	if (!cras_stream_uses_input_hw(stream->direction))
+		return 0;
+
+	aud_msg.id = AUDIO_MESSAGE_DATA_CAPTURED;
+	aud_msg.frames = frames;
+	aud_msg.error = err;
+
+	rc = write(stream->aud_fd, &aud_msg, sizeof(aud_msg));
+	if (rc != sizeof(aud_msg))
+		return -EPIPE;
+
+	return 0;
+}
+
 /* For capture streams this handles the message signalling that data is ready to
  * be passed to the user of this stream.  Calls the audio callback with the new
  * samples, and mark them as read.
@@ -1058,6 +1096,7 @@
 	struct cras_stream_params *config;
 	uint8_t *captured_frames;
 	struct timespec ts;
+	int rc = 0;
 
 	config = stream->config;
 	/* If this message is for an output stream, log error and drop it. */
@@ -1088,15 +1127,17 @@
 					num_frames,
 					&ts,
 					config->user_data);
-	if (frames == EOF) {
+	if (frames < 0) {
 		send_stream_message(stream, CLIENT_STREAM_EOF);
-		return EOF;
+		rc = frames;
+		goto reply_captured;
 	}
 	if (frames == 0)
 		return 0;
 
 	complete_capture_read_current(stream, frames);
-	return 0;
+reply_captured:
+	return send_capture_reply(stream, frames, rc);
 }
 
 /* Notifies the server that "frames" samples have been written. */
@@ -1313,7 +1354,7 @@
 		return -rc;
 	}
 
-	clock_gettime(CLOCK_MONOTONIC, &future);
+	clock_gettime(CLOCK_REALTIME, &future);
 	future.tv_sec += 2; /* Wait up to two seconds. */
 	rc = pthread_cond_timedwait(&stream->client->stream_start_cond,
 				    &stream->client->stream_start_lock, &future);
@@ -1472,6 +1513,7 @@
 				  stream->config->buffer_frames,
 				  stream->config->cb_threshold,
 				  stream->flags,
+				  stream->config->effects,
 				  stream->config->format,
 				  dev_idx);
 	rc = cras_send_with_fds(client->server_fd, &serv_msg, sizeof(serv_msg),
@@ -1689,8 +1731,23 @@
 			(struct cras_client_stream_connected *)msg;
 		struct client_stream *stream =
 			stream_from_id(client, cmsg->stream_id);
-		if (stream == NULL)
+		if (stream == NULL) {
+			if (num_fds != 2) {
+				syslog(LOG_ERR, "cras_client: Error receiving "
+				       "stream 0x%x connected message",
+				       cmsg->stream_id);
+				return -EINVAL;
+			}
+
+			/*
+			 * Usually, the fds should be closed in stream_connected
+			 * callback. However, sometimes a stream is removed
+			 * before it is connected.
+			 */
+			close(server_fds[0]);
+			close(server_fds[1]);
 			break;
+		}
 		rc = stream_connected(stream, cmsg, server_fds, num_fds);
 		if (rc < 0)
 			stream->config->err_cb(stream->client,
@@ -1702,6 +1759,7 @@
 	case CRAS_CLIENT_AUDIO_DEBUG_INFO_READY:
 		if (client->debug_info_callback)
 			client->debug_info_callback(client);
+		client->debug_info_callback = NULL;
 		break;
 	case CRAS_CLIENT_GET_HOTWORD_MODELS_READY: {
 		struct cras_client_get_hotword_models_ready *cmsg =
@@ -2172,6 +2230,7 @@
 	client->server_err_cb = NULL;
 	cras_client_stop(client);
 	server_disconnect(client);
+	close(client->server_event_fd);
 	close(client->command_fds[0]);
 	close(client->command_fds[1]);
 	close(client->stream_fds[0]);
@@ -2224,7 +2283,7 @@
 	params->direction = direction;
 	params->buffer_frames = buffer_frames;
 	params->cb_threshold = cb_threshold;
-
+	params->effects = 0;
 	params->stream_type = stream_type;
 	params->flags = flags;
 	params->user_data = user_data;
@@ -2235,6 +2294,46 @@
 	return params;
 }
 
+void cras_client_stream_params_enable_aec(struct cras_stream_params *params)
+{
+	params->effects |= APM_ECHO_CANCELLATION;
+}
+
+void cras_client_stream_params_disable_aec(struct cras_stream_params *params)
+{
+	params->effects &= ~APM_ECHO_CANCELLATION;
+}
+
+void cras_client_stream_params_enable_ns(struct cras_stream_params *params)
+{
+	params->effects |= APM_NOISE_SUPRESSION;
+}
+
+void cras_client_stream_params_disable_ns(struct cras_stream_params *params)
+{
+	params->effects &= ~APM_NOISE_SUPRESSION;
+}
+
+void cras_client_stream_params_enable_agc(struct cras_stream_params *params)
+{
+	params->effects |= APM_GAIN_CONTROL;
+}
+
+void cras_client_stream_params_disable_agc(struct cras_stream_params *params)
+{
+	params->effects &= ~APM_GAIN_CONTROL;
+}
+
+void cras_client_stream_params_enable_vad(struct cras_stream_params *params)
+{
+	params->effects |= APM_VOICE_DETECTION;
+}
+
+void cras_client_stream_params_disable_vad(struct cras_stream_params *params)
+{
+	params->effects &= ~APM_VOICE_DETECTION;
+}
+
 struct cras_stream_params *cras_client_unified_params_create(
 		enum CRAS_STREAM_DIRECTION direction,
 		unsigned int block_size,
@@ -2256,6 +2355,7 @@
 	params->cb_threshold = block_size;
 	params->stream_type = stream_type;
 	params->flags = flags;
+	params->effects = 0;
 	params->user_data = user_data;
 	params->aud_cb = 0;
 	params->unified_cb = unified_cb;
@@ -2593,6 +2693,22 @@
 	return debug_info;
 }
 
+const struct cras_audio_thread_snapshot_buffer*
+	cras_client_get_audio_thread_snapshot_buffer(
+		const struct cras_client *client)
+{
+	const struct cras_audio_thread_snapshot_buffer *snapshot_buffer;
+	int lock_rc;
+
+	lock_rc = server_state_rdlock(client);
+	if (lock_rc)
+		return 0;
+
+	snapshot_buffer = &client->server_state->snapshot_buffer;
+	server_state_unlock(client, lock_rc);
+	return snapshot_buffer;
+}
+
 unsigned cras_client_get_num_active_streams(const struct cras_client *client,
 					    struct timespec *ts)
 {
@@ -3029,12 +3145,31 @@
 	if (client == NULL)
 		return -EINVAL;
 
+	if (client->debug_info_callback != NULL)
+		return -EINVAL;
 	client->debug_info_callback = debug_info_cb;
 
 	cras_fill_dump_audio_thread(&msg);
 	return write_message_to_server(client, &msg.header);
 }
 
+int cras_client_update_audio_thread_snapshots(
+	struct cras_client *client,
+	void (*debug_info_cb)(struct cras_client *))
+{
+	struct cras_dump_snapshots msg;
+
+	if (client == NULL)
+		return -EINVAL;
+
+	if (client->debug_info_callback != NULL)
+		return -EINVAL;
+	client->debug_info_callback = debug_info_cb;
+
+	cras_fill_dump_snapshots(&msg);
+	return write_message_to_server(client, &msg.header);
+}
+
 int cras_client_set_node_volume(struct cras_client *client,
 				cras_node_id_t node_id,
 				uint8_t volume)
@@ -3118,10 +3253,10 @@
 	return rc;
 }
 
-/* Return the index of the device used for listening to hotwords. */
-int cras_client_get_first_dev_type_idx(const struct cras_client *client,
-				       enum CRAS_NODE_TYPE type,
-				       enum CRAS_STREAM_DIRECTION direction)
+int cras_client_get_first_node_type_idx(const struct cras_client *client,
+					enum CRAS_NODE_TYPE type,
+					enum CRAS_STREAM_DIRECTION direction,
+					cras_node_id_t *node_id)
 {
 	const struct cras_server_state *state;
 	unsigned int version;
@@ -3146,9 +3281,10 @@
 	}
 	for (i = 0; i < num_nodes; i++) {
 		if ((enum CRAS_NODE_TYPE)node_list[i].type_enum == type) {
-			int ret_idx = node_list[i].iodev_idx;
+			*node_id = cras_make_node_id(node_list[i].iodev_idx,
+						     node_list[i].ionode_idx);
 			server_state_unlock(client, lock_rc);
-			return ret_idx;
+			return 0;
 		}
 	}
 	if (end_server_state_read(state, version))
@@ -3158,6 +3294,21 @@
 	return -ENODEV;
 }
 
+int cras_client_get_first_dev_type_idx(const struct cras_client *client,
+				       enum CRAS_NODE_TYPE type,
+				       enum CRAS_STREAM_DIRECTION direction)
+{
+	cras_node_id_t node_id;
+	int rc;
+
+	rc = cras_client_get_first_node_type_idx(client, type, direction,
+						 &node_id);
+	if (rc)
+		return rc;
+
+	return dev_index_of(node_id);
+}
+
 int cras_client_set_suspend(struct cras_client *client, int suspend)
 {
 	struct cras_server_message msg;
@@ -3190,6 +3341,44 @@
 	return write_message_to_server(client, &msg.header);
 }
 
+int cras_client_set_aec_dump(struct cras_client *client,
+			      cras_stream_id_t stream_id,
+			      int start,
+			      int fd)
+{
+	struct cras_set_aec_dump msg;
+
+	cras_fill_set_aec_dump_message(&msg, stream_id, start);
+
+	if (fd != -1)
+		return cras_send_with_fds(client->server_fd, &msg,
+					  sizeof(msg), &fd, 1);
+	else
+		return write_message_to_server(client, &msg.header);
+}
+
+int cras_client_reload_aec_config(struct cras_client *client)
+{
+	struct cras_reload_aec_config msg;
+
+	cras_fill_reload_aec_config(&msg);
+	return write_message_to_server(client, &msg.header);
+}
+
+int cras_client_get_aec_supported(struct cras_client *client)
+{
+	int aec_supported;
+	int lock_rc;
+
+	lock_rc = server_state_rdlock(client);
+	if (lock_rc)
+		return 0;
+
+	aec_supported = client->server_state->aec_supported;
+	server_state_unlock(client, lock_rc);
+	return aec_supported;
+}
+
 void cras_client_set_state_change_callback_context(
 		struct cras_client *client, void *context)
 {
@@ -3400,3 +3589,106 @@
 	}
 	return 0;
 }
+
+static int hotword_read_cb(struct cras_client *client,
+			   cras_stream_id_t stream_id,
+			   uint8_t *captured_samples,
+			   uint8_t *playback_samples,
+			   unsigned int frames,
+			   const struct timespec *captured_time,
+			   const struct timespec *playback_time,
+			   void *user_arg)
+{
+	struct cras_hotword_handle *handle;
+
+	handle = (struct cras_hotword_handle *)user_arg;
+	if (handle->trigger_cb)
+		handle->trigger_cb(client, handle, handle->user_data);
+
+	return 0;
+}
+
+static int hotword_err_cb(struct cras_client *client,
+			  cras_stream_id_t stream_id,
+			  int error,
+			  void *user_arg)
+{
+	struct cras_hotword_handle *handle;
+
+	handle = (struct cras_hotword_handle *)user_arg;
+	if (handle->err_cb)
+		handle->err_cb(client, handle, error, handle->user_data);
+
+	return 0;
+}
+
+int cras_client_enable_hotword_callback(struct cras_client *client,
+					void *user_data,
+					cras_hotword_trigger_cb_t trigger_cb,
+					cras_hotword_error_cb_t err_cb,
+					struct cras_hotword_handle **handle_out)
+{
+	struct cras_hotword_handle *handle;
+	int ret = 0;
+
+	if (!client)
+		return -EINVAL;
+
+	handle = (struct cras_hotword_handle *)calloc(1, sizeof(*handle));
+	if (!handle)
+		return -ENOMEM;
+
+	handle->format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE,
+						  HOTWORD_FRAME_RATE, 1);
+	if (!handle->format) {
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	handle->params = cras_client_unified_params_create(
+		CRAS_STREAM_INPUT,
+		HOTWORD_BLOCK_SIZE,
+		CRAS_STREAM_TYPE_DEFAULT,
+		HOTWORD_STREAM | TRIGGER_ONLY,
+		(void *)handle,
+		hotword_read_cb,
+		hotword_err_cb,
+		handle->format);
+	if (!handle->params) {
+		ret = -ENOMEM;
+		goto cleanup_format;
+	}
+
+	handle->trigger_cb = trigger_cb;
+	handle->err_cb = err_cb;
+	handle->user_data = user_data;
+
+	ret = cras_client_add_stream(client, &handle->stream_id,
+				     handle->params);
+	if (ret)
+		goto cleanup_params;
+
+	*handle_out = handle;
+	return 0;
+
+cleanup_params:
+	cras_client_stream_params_destroy(handle->params);
+cleanup_format:
+	cras_audio_format_destroy(handle->format);
+cleanup:
+	free(handle);
+	return ret;
+}
+
+int cras_client_disable_hotword_callback(struct cras_client *client,
+					 struct cras_hotword_handle *handle)
+{
+	if (!client || !handle)
+		return -EINVAL;
+
+	cras_client_rm_stream(client, handle->stream_id);
+	cras_audio_format_destroy(handle->format);
+	cras_client_stream_params_destroy(handle->params);
+	free(handle);
+	return 0;
+}
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index d17b650..1817a73 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -59,6 +59,7 @@
 #include "cras_util.h"
 
 struct cras_client;
+struct cras_hotword_handle;
 struct cras_stream_params;
 
 /* Callback for audio received or transmitted.
@@ -177,6 +178,17 @@
 typedef void (*get_hotword_models_cb_t)(struct cras_client *client,
 					const char *hotword_models);
 
+/* Callback to wait for a hotword trigger. */
+typedef void (*cras_hotword_trigger_cb_t)(struct cras_client *client,
+					  struct cras_hotword_handle *handle,
+					  void *user_data);
+
+/* Callback for handling hotword errors. */
+typedef int (*cras_hotword_error_cb_t)(struct cras_client *client,
+				       struct cras_hotword_handle *handle,
+				       int error,
+				       void *user_data);
+
 /*
  * Client handling.
  */
@@ -492,6 +504,17 @@
 int cras_client_update_audio_debug_info(
 	struct cras_client *client, void (*cb)(struct cras_client *));
 
+/* Asks the server to dump current audio thread snapshots.
+ *
+ * Args:
+ *    client - The client from cras_client_create.
+ *    cb - A function to call when the data is received.
+ * Returns:
+ *    0 on success, -EINVAL if the client isn't valid or isn't running.
+ */
+int cras_client_update_audio_thread_snapshots(
+	struct cras_client *client, void (*cb)(struct cras_client *));
+
 /*
  * Stream handling.
  */
@@ -525,6 +548,19 @@
 		cras_error_cb_t err_cb,
 		struct cras_audio_format *format);
 
+/* Functions to enable or disable specific effect on given stream parameter.
+ * Args:
+ *    params - Stream configuration parameters.
+ */
+void cras_client_stream_params_enable_aec(struct cras_stream_params *params);
+void cras_client_stream_params_disable_aec(struct cras_stream_params *params);
+void cras_client_stream_params_enable_ns(struct cras_stream_params *params);
+void cras_client_stream_params_disable_ns(struct cras_stream_params *params);
+void cras_client_stream_params_enable_agc(struct cras_stream_params *params);
+void cras_client_stream_params_disable_agc(struct cras_stream_params *params);
+void cras_client_stream_params_enable_vad(struct cras_stream_params *params);
+void cras_client_stream_params_disable_vad(struct cras_stream_params *params);
+
 /* Setup stream configuration parameters.
  * Args:
  *    direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT) or
@@ -821,6 +857,21 @@
 const struct audio_debug_info *cras_client_get_audio_debug_info(
 		const struct cras_client *client);
 
+/* Gets audio thread snapshot buffer.
+ *
+ * Requires that the connection to the server has been established.
+ * Access to the resulting pointer is not thread-safe.
+ *
+ * Args:
+ *    client - The client from cras_client_create.
+ * Returns:
+ *    A pointer to the snapshot buffer.  This info is only updated when
+ *    requested by calling cras_client_update_audio_thread_snapshots.
+ */
+const struct cras_audio_thread_snapshot_buffer *
+	cras_client_get_audio_thread_snapshot_buffer(
+		const struct cras_client *client);
+
 /* Gets the number of streams currently attached to the server.
  *
  * This is the total number of capture and playback streams. If the ts argument
@@ -934,6 +985,25 @@
 				   unsigned int data_len,
 				   const uint8_t *data);
 
+/* Finds the first node of the given type.
+ *
+ * This is used for finding a special hotword node.
+ *
+ * Requires that the connection to the server has been established.
+ *
+ * Args:
+ *    client - The client from cras_client_create.
+ *    type - The type of device to find.
+ *    direction - Search input or output devices.
+ *    node_id - The found node on success.
+ * Returns:
+ *    0 on success, a negative error on failure.
+ */
+int cras_client_get_first_node_type_idx(const struct cras_client *client,
+					enum CRAS_NODE_TYPE type,
+					enum CRAS_STREAM_DIRECTION direction,
+					cras_node_id_t *node_id);
+
 /* Finds the first device that contains a node of the given type.
  *
  * This is used for finding a special hotword device.
@@ -1003,6 +1073,58 @@
 				  cras_node_id_t node_id,
 				  const char *model_name);
 
+/*
+ * Creates a hotword stream and waits for the hotword to trigger.
+ *
+ * Args:
+ *    client - The client to add the stream to (from cras_client_create).
+ *    user_data - Pointer that will be passed to the callback.
+ *    trigger_cb - Called when a hotword is triggered.
+ *    err_cb - Called when there is an error with the stream.
+ *    handle_out - On success will be filled with a cras_hotword_handle.
+ * Returns:
+ *    0 on success, negative error code on failure (from errno.h).
+ */
+int cras_client_enable_hotword_callback(
+		struct cras_client *client,
+		void *user_data,
+		cras_hotword_trigger_cb_t trigger_cb,
+		cras_hotword_error_cb_t err_cb,
+		struct cras_hotword_handle **handle_out);
+
+/*
+ * Closes a hotword stream that was created by cras_client_wait_for_hotword.
+ *
+ * Args:
+ *    client - Client to remove the stream (returned from cras_client_create).
+ *    handle - cras_hotword_handle returned from cras_client_wait_for_hotword.
+ * Returns:
+ *    0 on success negative error code on failure (from errno.h).
+ */
+int cras_client_disable_hotword_callback(struct cras_client *client,
+					 struct cras_hotword_handle *handle);
+
+/* Starts or stops the aec dump task on server side.
+ * Args:
+ *    client - The client from cras_client_create.
+ *    stream_id - The id of the input stream running with aec effect.
+ *    start - True to start APM debugging, otherwise to stop it.
+ *    fd - File descriptor of the file to store aec dump result.
+ */
+int cras_client_set_aec_dump(struct cras_client *client,
+			     cras_stream_id_t stream_id,
+			     int start,
+			     int fd);
+/*
+ * Reloads the aec.ini config file on server side.
+ */
+int cras_client_reload_aec_config(struct cras_client *client);
+
+/*
+ * Returns if AEC is supported.
+ */
+int cras_client_get_aec_supported(struct cras_client *client);
+
 /* Set the context pointer for system state change callbacks.
  * Args:
  *    client - The client from cras_client_create.
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index 8b34914..7d208d8 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -9,16 +9,16 @@
 
 #include <pthread.h>
 #include <poll.h>
+#include <stdbool.h>
 #include <sys/param.h>
 #include <syslog.h>
 
-#include "cras_audio_area.h"
 #include "audio_thread_log.h"
+#include "cras_audio_thread_monitor.h"
 #include "cras_config.h"
 #include "cras_fmt_conv.h"
 #include "cras_iodev.h"
 #include "cras_rstream.h"
-#include "cras_server_metrics.h"
 #include "cras_system_state.h"
 #include "cras_types.h"
 #include "cras_util.h"
@@ -29,14 +29,16 @@
 #define MIN_PROCESS_TIME_US 500 /* 0.5ms - min amount of time to mix/src. */
 #define SLEEP_FUZZ_FRAMES 10 /* # to consider "close enough" to sleep frames. */
 #define MIN_READ_WAIT_US 2000 /* 2ms */
-static const struct timespec playback_wake_fuzz_ts = {
-	0, 500 * 1000 /* 500 usec. */
-};
+/*
+ * # to check whether a busyloop event happens
+ */
+#define MAX_CONTINUOUS_ZERO_SLEEP_COUNT 2
 
 /* Messages that can be sent from the main context to the audio thread. */
 enum AUDIO_THREAD_COMMAND {
 	AUDIO_THREAD_ADD_OPEN_DEV,
 	AUDIO_THREAD_RM_OPEN_DEV,
+	AUDIO_THREAD_IS_DEV_OPEN,
 	AUDIO_THREAD_ADD_STREAM,
 	AUDIO_THREAD_DISCONNECT_STREAM,
 	AUDIO_THREAD_STOP,
@@ -45,6 +47,7 @@
 	AUDIO_THREAD_CONFIG_GLOBAL_REMIX,
 	AUDIO_THREAD_DEV_START_RAMP,
 	AUDIO_THREAD_REMOVE_CALLBACK,
+	AUDIO_THREAD_AEC_DUMP,
 };
 
 struct audio_thread_msg {
@@ -85,10 +88,15 @@
 	enum CRAS_IODEV_RAMP_REQUEST request;
 };
 
+struct audio_thread_aec_dump_msg {
+	struct audio_thread_msg header;
+	cras_stream_id_t stream_id;
+	unsigned int start; /* */
+	int fd;
+};
+
 /* Audio thread logging. */
 struct audio_thread_event_log *atlog;
-/* Global fmt converter used to remix output channels. */
-static struct cras_fmt_conv *remix_converter = NULL;
 
 static struct iodev_callback_list *iodev_callbacks;
 static struct timespec longest_wake;
@@ -174,6 +182,35 @@
 	return write(thread->to_main_fds[1], &rc, sizeof(rc));
 }
 
+/* Reads from a file descriptor until all bytes are read.
+ *
+ * Args:
+ *    fd - file descriptor to read
+ *    buf - the buffer to be written.
+ *    count - the number of bytes to read from fd
+ * Returns:
+ *    |count| on success, negative error code on failure.
+ */
+static int read_until_finished(int fd, void *buf, size_t count) {
+	int nread, count_left = count;
+
+	while (count_left > 0) {
+		nread = read(fd, (uint8_t *)buf + count - count_left,
+			     count_left);
+		if (nread < 0) {
+			if (errno == EINTR)
+				continue;
+			else
+				return nread;
+		} else if (nread == 0) {
+			syslog(LOG_ERR, "Pipe has been closed.");
+			return -EPIPE;
+		}
+		count_left -= nread;
+	}
+	return count;
+}
+
 /* Reads a command from the main thread.  Called from the playback/capture
  * thread.  This will read the next available command from the main thread and
  * put it in buf.
@@ -192,14 +229,17 @@
 	struct audio_thread_msg *msg = (struct audio_thread_msg *)buf;
 
 	/* Get the length of the message first */
-	nread = read(thread->to_thread_fds[0], buf, sizeof(msg->length));
+	nread = read_until_finished(
+			thread->to_thread_fds[0], buf, sizeof(msg->length));
 	if (nread < 0)
 		return nread;
+
 	if (msg->length > max_len)
 		return -ENOMEM;
 
-	to_read = msg->length - nread;
-	rc = read(thread->to_thread_fds[0], &buf[0] + nread, to_read);
+	to_read = msg->length - sizeof(msg->length);
+	rc = read_until_finished(thread->to_thread_fds[0],
+			&buf[0] + sizeof(msg->length), to_read);
 	if (rc < 0)
 		return rc;
 	return 0;
@@ -211,19 +251,6 @@
 	cras_iodev_fill_odev_zeros(odev, odev->min_buffer_level);
 }
 
-static void thread_rm_open_adev(struct audio_thread *thread,
-				struct open_dev *adev);
-
-static void delete_stream_from_dev(struct cras_iodev *dev,
-				   struct cras_rstream *stream)
-{
-	struct dev_stream *out;
-
-	out = cras_iodev_rm_stream(dev, stream);
-	if (out)
-		dev_stream_destroy(out);
-}
-
 /* Append a new stream to a specified set of iodevs. */
 static int append_stream(struct audio_thread *thread,
 			 struct cras_rstream *stream,
@@ -236,6 +263,7 @@
 	struct timespec init_cb_ts;
 	const struct timespec *stream_ts;
 	unsigned int i;
+	bool cb_ts_set = false;
 	int rc = 0;
 
 	for (i = 0; i < num_iodevs; i++) {
@@ -249,15 +277,26 @@
 		if (out)
 			continue;
 
-		/* If open device already has stream, get the first stream
-		 * and use its next callback time to align with. Otherwise
-		 * use the timestamp now as the initial callback time for
-		 * new stream.
+		/* For output, if open device already has stream, get the earliest next
+		 * callback time from these streams to align with. Otherwise, use the
+		 * timestamp now as the initial callback time for new stream so dev_stream
+		 * can set its own schedule.
+		 * If next callback time is too far from now, it will block writing and
+		 * lower hardware level. Else if we fetch the new stream immediately, it
+		 * may cause device buffer level stack up.
 		 */
-		if (dev->streams &&
-		    (stream_ts = dev_stream_next_cb_ts(dev->streams)))
-			init_cb_ts = *stream_ts;
-		else
+		if (stream->direction == CRAS_STREAM_OUTPUT && dev->streams) {
+			DL_FOREACH(dev->streams, out) {
+				stream_ts =  dev_stream_next_cb_ts(out);
+				if (stream_ts &&
+				    (!cb_ts_set || timespec_after(&init_cb_ts, stream_ts))) {
+					init_cb_ts = *stream_ts;
+					cb_ts_set = true;
+				}
+			}
+		}
+
+		if (!cb_ts_set)
 			clock_gettime(CLOCK_MONOTONIC_RAW, &init_cb_ts);
 
 		out = dev_stream_create(stream, dev->info.idx,
@@ -339,74 +378,46 @@
 	 */
 	if (iodev->direction == CRAS_STREAM_OUTPUT)
 		fill_odevs_zeros_min_level(iodev);
-	else
-		adev->input_streaming = 0;
 
-	ATLOG(atlog,
-				    AUDIO_THREAD_DEV_ADDED,
-				    iodev->info.idx, 0, 0);
+	ATLOG(atlog, AUDIO_THREAD_DEV_ADDED, iodev->info.idx, 0, 0);
 
 	DL_APPEND(thread->open_devs[iodev->direction], adev);
 
 	return 0;
 }
 
-static struct open_dev *find_adev(struct open_dev *adev_list,
-				  struct cras_iodev *dev)
-{
-	struct open_dev *adev;
-	DL_FOREACH(adev_list, adev)
-		if (adev->dev == dev)
-			return adev;
-	return NULL;
-}
-
-static void thread_rm_open_adev(struct audio_thread *thread,
-				struct open_dev *dev_to_rm)
-{
-	enum CRAS_STREAM_DIRECTION dir = dev_to_rm->dev->direction;
-	struct open_dev *adev;
-	struct dev_stream *dev_stream;
-
-	/* Do nothing if dev_to_rm wasn't already in the active dev list. */
-	adev = find_adev(thread->open_devs[dir], dev_to_rm->dev);
-	if (!adev)
-		return;
-
-	DL_DELETE(thread->open_devs[dir], dev_to_rm);
-
-	ATLOG(atlog,
-				    AUDIO_THREAD_DEV_REMOVED,
-				    dev_to_rm->dev->info.idx, 0, 0);
-
-	DL_FOREACH(dev_to_rm->dev->streams, dev_stream) {
-		cras_iodev_rm_stream(dev_to_rm->dev, dev_stream->stream);
-		dev_stream_destroy(dev_stream);
-	}
-
-	free(dev_to_rm);
-}
-
 /* Handles messages from the main thread to remove an active device. */
 static int thread_rm_open_dev(struct audio_thread *thread,
 			      struct cras_iodev *iodev)
 {
-	struct open_dev *adev = find_adev(
+	struct open_dev *adev = dev_io_find_open_dev(
 			thread->open_devs[iodev->direction], iodev);
 	if (!adev)
 		return -EINVAL;
 
-	thread_rm_open_adev(thread, adev);
+	dev_io_rm_open_dev(&thread->open_devs[iodev->direction], adev);
 	return 0;
 }
 
+/*
+ * Handles message from the main thread to check if an iodev is in the
+ * open dev list.
+ */
+static int thread_is_dev_open(struct audio_thread *thread,
+			      struct cras_iodev *iodev)
+{
+	struct open_dev *adev = dev_io_find_open_dev(
+			thread->open_devs[iodev->direction], iodev);
+	return !!adev;
+}
+
 /* Handles messages from the main thread to start ramping on a device. */
 static int thread_dev_start_ramp(struct audio_thread *thread,
 				 struct cras_iodev *iodev,
 				 enum CRAS_IODEV_RAMP_REQUEST request)
 {
 	/* Do nothing if device wasn't already in the active dev list. */
-	struct open_dev *adev = find_adev(
+	struct open_dev *adev = dev_io_find_open_dev(
 			thread->open_devs[iodev->direction], iodev);
 	if (!adev)
 		return -EINVAL;
@@ -430,45 +441,6 @@
 	return 0;
 }
 
-/* Remove stream from the audio thread. If this is the last stream to be
- * removed close the device.
- */
-static int thread_remove_stream(struct audio_thread *thread,
-				struct cras_rstream *stream,
-				struct cras_iodev *dev)
-{
-	struct open_dev *open_dev;
-	struct timespec delay;
-	unsigned fetch_delay_msec;
-
-	/* Metrics log the longest fetch delay of this stream. */
-	if (timespec_after(&stream->longest_fetch_interval,
-			   &stream->sleep_interval_ts)) {
-		subtract_timespecs(&stream->longest_fetch_interval,
-				   &stream->sleep_interval_ts,
-				   &delay);
-		fetch_delay_msec = delay.tv_sec * 1000 +
-				   delay.tv_nsec / 1000000;
-		if (fetch_delay_msec)
-			cras_server_metrics_longest_fetch_delay(
-					fetch_delay_msec);
-	}
-
-	ATLOG(atlog,
-				    AUDIO_THREAD_STREAM_REMOVED,
-				    stream->stream_id, 0, 0);
-
-	if (dev == NULL) {
-		DL_FOREACH(thread->open_devs[stream->direction], open_dev) {
-			delete_stream_from_dev(open_dev->dev, stream);
-		}
-	} else {
-		delete_stream_from_dev(dev, stream);
-	}
-
-	return 0;
-}
-
 /* Handles the disconnect_stream message from the main thread. */
 static int thread_disconnect_stream(struct audio_thread* thread,
 				    struct cras_rstream* stream,
@@ -479,7 +451,8 @@
 	if (!thread_find_stream(thread, stream))
 		return 0;
 
-	rc = thread_remove_stream(thread, stream, dev);
+	rc = dev_io_remove_stream(&thread->open_devs[stream->direction],
+				  stream, dev);
 
 	return rc;
 }
@@ -522,7 +495,8 @@
 
 	ms_left = thread_drain_stream_ms_remaining(thread, rstream);
 	if (ms_left == 0)
-		thread_remove_stream(thread, rstream, NULL);
+		dev_io_remove_stream(&thread->open_devs[rstream->direction],
+				     rstream, NULL);
 
 	return ms_left;
 }
@@ -539,224 +513,35 @@
 	if (rc < 0)
 		return rc;
 
-	ATLOG(atlog,
-				    AUDIO_THREAD_STREAM_ADDED,
-				    stream->stream_id,
-				    num_iodevs ? iodevs[0]->info.idx : 0,
-				    num_iodevs);
+	ATLOG(atlog, AUDIO_THREAD_STREAM_ADDED, stream->stream_id,
+	      num_iodevs ? iodevs[0]->info.idx : 0, num_iodevs);
 	return 0;
 }
 
-/* Reads any pending audio message from the socket. */
-static void flush_old_aud_messages(struct cras_audio_shm *shm, int fd)
+/* Starts or stops aec dump task. */
+static int thread_set_aec_dump(struct audio_thread *thread,
+			       cras_stream_id_t stream_id,
+			       unsigned int start,
+			       int fd)
 {
-	struct audio_message msg;
-	struct pollfd pollfd;
-	int err;
-
-	pollfd.fd = fd;
-	pollfd.events = POLLIN;
-
-	do {
-		err = poll(&pollfd, 1, 0);
-		if (pollfd.revents & POLLIN) {
-			err = read(fd, &msg, sizeof(msg));
-			cras_shm_set_callback_pending(shm, 0);
-		}
-	} while (err > 0);
-}
-
-/* Asks any stream with room for more data. Sets the time stamp for all streams.
- * Args:
- *    thread - The thread to fetch samples for.
- *    adev - The output device streams are attached to.
- * Returns:
- *    0 on success, negative error on failure. If failed, can assume that all
- *    streams have been removed from the device.
- */
-static int fetch_streams(struct audio_thread *thread,
-			 struct open_dev *adev)
-{
-	struct dev_stream *dev_stream;
-	struct cras_iodev *odev = adev->dev;
-	int rc;
-	int delay;
-
-	delay = cras_iodev_delay_frames(odev);
-	if (delay < 0)
-		return delay;
-
-	DL_FOREACH(adev->dev->streams, dev_stream) {
-		struct cras_rstream *rstream = dev_stream->stream;
-		struct cras_audio_shm *shm =
-			cras_rstream_output_shm(rstream);
-		int fd = cras_rstream_get_audio_fd(rstream);
-		const struct timespec *next_cb_ts;
-		struct timespec now;
-
-		clock_gettime(CLOCK_MONOTONIC_RAW, &now);
-
-		if (cras_shm_callback_pending(shm) && fd >= 0) {
-			flush_old_aud_messages(shm, fd);
-			cras_rstream_record_fetch_interval(dev_stream->stream,
-							   &now);
-		}
-
-		if (cras_shm_get_frames(shm) < 0)
-			cras_rstream_set_is_draining(rstream, 1);
-
-		if (cras_rstream_get_is_draining(dev_stream->stream))
-			continue;
-
-		next_cb_ts = dev_stream_next_cb_ts(dev_stream);
-		if (!next_cb_ts)
-			continue;
-
-		/* Check if it's time to get more data from this stream.
-		 * Allowing for waking up half a little early. */
-		add_timespecs(&now, &playback_wake_fuzz_ts);
-		if (!timespec_after(&now, next_cb_ts))
-			continue;
-
-		if (!dev_stream_can_fetch(dev_stream)) {
-			ATLOG(
-				atlog, AUDIO_THREAD_STREAM_SKIP_CB,
-				rstream->stream_id,
-				shm->area->write_offset[0],
-				shm->area->write_offset[1]);
-			continue;
-		}
-
-		dev_stream_set_delay(dev_stream, delay);
-
-		ATLOG(
-				atlog,
-				AUDIO_THREAD_FETCH_STREAM,
-				rstream->stream_id,
-				cras_rstream_get_cb_threshold(rstream), delay);
-
-		rc = dev_stream_request_playback_samples(dev_stream, &now);
-		if (rc < 0) {
-			syslog(LOG_ERR, "fetch err: %d for %x",
-			       rc, rstream->stream_id);
-			cras_rstream_set_is_draining(rstream, 1);
-		}
-	}
-
-	return 0;
-}
-
-/* Fill the buffer with samples from the attached streams.
- * Args:
- *    thread - The thread object the device is attached to.
- *    adev - The device to write to.
- *    dst - The buffer to put the samples in (returned from snd_pcm_mmap_begin)
- *    write_limit - The maximum number of frames to write to dst.
- *
- * Returns:
- *    The number of frames rendered on success, a negative error code otherwise.
- *    This number of frames is the minimum of the amount of frames each stream
- *    could provide which is the maximum that can currently be rendered.
- */
-static int write_streams(struct audio_thread *thread,
-			 struct open_dev *adev,
-			 uint8_t *dst,
-			 size_t write_limit)
-{
-	struct cras_iodev *odev = adev->dev;
-	struct dev_stream *curr;
-	unsigned int max_offset = 0;
-	unsigned int frame_bytes = cras_get_format_bytes(odev->ext_format);
-	unsigned int num_playing = 0;
-	unsigned int drain_limit = write_limit;
-
-	/* Mix as much as we can, the minimum fill level of any stream. */
-	max_offset = cras_iodev_max_stream_offset(odev);
-
-        /* Mix as much as we can, the minimum fill level of any stream. */
-	DL_FOREACH(adev->dev->streams, curr) {
-		int dev_frames;
-
-		/* If this is a single output dev stream, updates the latest
-		 * number of frames for playback. */
-		if (dev_stream_attached_devs(curr) == 1)
-			dev_stream_update_frames(curr);
-
-		dev_frames = dev_stream_playback_frames(curr);
-		if (dev_frames < 0) {
-			thread_remove_stream(thread, curr->stream, NULL);
-			continue;
-		}
-		ATLOG(atlog,
-				AUDIO_THREAD_WRITE_STREAMS_STREAM,
-				curr->stream->stream_id,
-				dev_frames,
-				cras_shm_callback_pending(cras_rstream_output_shm(curr->stream)));
-		if (cras_rstream_get_is_draining(curr->stream)) {
-			drain_limit = MIN((size_t)dev_frames, drain_limit);
-			if (!dev_frames)
-				thread_remove_stream(thread, curr->stream,
-						     NULL);
-		} else {
-			write_limit = MIN((size_t)dev_frames, write_limit);
-			num_playing++;
-		}
-	}
-
-	if (!num_playing)
-		write_limit = drain_limit;
-
-	if (write_limit > max_offset)
-		memset(dst + max_offset * frame_bytes, 0,
-		       (write_limit - max_offset) * frame_bytes);
-
-	ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIX,
-				    write_limit, max_offset, 0);
-
-	DL_FOREACH(adev->dev->streams, curr) {
-		unsigned int offset;
-		int nwritten;
-
-		offset = cras_iodev_stream_offset(odev, curr);
-		if (offset >= write_limit)
-			continue;
-		nwritten = dev_stream_mix(curr, odev->ext_format,
-					  dst + frame_bytes * offset,
-					  write_limit - offset);
-
-		if (nwritten < 0) {
-			thread_remove_stream(thread, curr->stream, NULL);
-			continue;
-		}
-
-		cras_iodev_stream_written(odev, curr, nwritten);
-	}
-
-	write_limit = cras_iodev_all_streams_written(odev);
-
-	ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIXED,
-				    write_limit, 0, 0);
-
-	return write_limit;
-}
-
-/* Gets the max delay frames of open input devices. */
-static int input_delay_frames(struct open_dev *adevs)
-{
+	struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
 	struct open_dev *adev;
-	int delay;
-	int max_delay = 0;
+	struct dev_stream *stream;
 
-	DL_FOREACH(adevs, adev) {
+	DL_FOREACH(idev_list, adev) {
 		if (!cras_iodev_is_open(adev->dev))
 			continue;
-		delay = cras_iodev_delay_frames(adev->dev);
-		if (delay < 0)
-			return delay;
-		if (delay > max_delay)
-			max_delay = delay;
+
+		DL_FOREACH(adev->dev->streams, stream) {
+			if ((stream->stream->apm_list == NULL) ||
+			    (stream->stream->stream_id != stream_id))
+				continue;
+
+			cras_apm_list_set_aec_dump(stream->stream->apm_list,
+						   adev->dev, start, fd);
+		}
 	}
-	return max_delay;
+	return 0;
 }
 
 /* Stop the playback thread */
@@ -778,6 +563,7 @@
 	di->num_underruns = cras_iodev_get_num_underruns(adev->dev);
 	di->num_severe_underruns = cras_iodev_get_num_severe_underruns(
 			adev->dev);
+	di->highest_hw_level = adev->dev->highest_hw_level;
 	if (fmt) {
 		di->frame_rate = fmt->frame_rate;
 		di->num_channels = fmt->num_channels;
@@ -812,6 +598,7 @@
 	si->longest_fetch_sec = stream->stream->longest_fetch_interval.tv_sec;
 	si->longest_fetch_nsec = stream->stream->longest_fetch_interval.tv_nsec;
 	si->num_overruns = cras_shm_num_overruns(&stream->stream->shm);
+	si->effects = cras_apm_list_get_effects(stream->stream->apm_list);
 
 	longest_wake.tv_sec = 0;
 	longest_wake.tv_nsec = 0;
@@ -835,10 +622,8 @@
 	case AUDIO_THREAD_ADD_STREAM: {
 		struct audio_thread_add_rm_stream_msg *amsg;
 		amsg = (struct audio_thread_add_rm_stream_msg *)msg;
-		ATLOG(
-			atlog,
-			AUDIO_THREAD_WRITE_STREAMS_WAIT,
-			amsg->stream->stream_id, 0, 0);
+		ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_WAIT,
+		      amsg->stream->stream_id, 0, 0);
 		ret = thread_add_stream(thread, amsg->stream, amsg->devs,
 				amsg->num_devs);
 		break;
@@ -866,6 +651,13 @@
 		ret = thread_rm_open_dev(thread, rmsg->dev);
 		break;
 	}
+	case AUDIO_THREAD_IS_DEV_OPEN: {
+		struct audio_thread_open_device_msg *rmsg;
+
+		rmsg = (struct audio_thread_open_device_msg *)msg;
+		ret = thread_is_dev_open(thread, rmsg->dev);
+		break;
+	}
 	case AUDIO_THREAD_STOP:
 		ret = 0;
 		err = audio_thread_send_response(thread, ret);
@@ -938,10 +730,10 @@
 
 		/* Respond the pointer to the old remix converter, so it can be
 		 * freed later in main thread. */
-		rsp = (void *)remix_converter;
+		rsp = (void *)thread->remix_converter;
 
 		rmsg = (struct audio_thread_config_global_remix *)msg;
-		remix_converter = rmsg->fmt_conv;
+		thread->remix_converter = rmsg->fmt_conv;
 
 		return write(thread->to_main_fds[1], &rsp, sizeof(rsp));
 	}
@@ -952,6 +744,13 @@
 		ret = thread_dev_start_ramp(thread, rmsg->dev, rmsg->request);
 		break;
 	}
+	case AUDIO_THREAD_AEC_DUMP: {
+		struct audio_thread_aec_dump_msg *rmsg;
+		rmsg = (struct audio_thread_aec_dump_msg *)msg;
+		ret = thread_set_aec_dump(thread, rmsg->stream_id,
+					  rmsg->start, rmsg->fd);
+		break;
+	}
 	default:
 		ret = -EINVAL;
 		break;
@@ -983,11 +782,9 @@
 		if (!next_cb_ts)
 			continue;
 
-		ATLOG(atlog,
-					    AUDIO_THREAD_STREAM_SLEEP_TIME,
-					    dev_stream->stream->stream_id,
-					    next_cb_ts->tv_sec,
-					    next_cb_ts->tv_nsec);
+		ATLOG(atlog, AUDIO_THREAD_STREAM_SLEEP_TIME,
+		      dev_stream->stream->stream_id, next_cb_ts->tv_sec,
+		      next_cb_ts->tv_nsec);
 		if (timespec_after(min_ts, next_cb_ts))
 			*min_ts = *next_cb_ts;
 		ret++;
@@ -996,53 +793,23 @@
 	return ret;
 }
 
-static int get_next_output_wake(struct audio_thread *thread,
-				 struct timespec *min_ts,
-				 const struct timespec *now)
+static int get_next_output_wake(struct open_dev **odevs,
+				struct timespec *min_ts,
+				const struct timespec *now)
 {
 	struct open_dev *adev;
-	struct timespec sleep_time;
-	double est_rate;
 	int ret = 0;
-	unsigned int frames_to_play_in_sleep;
-	unsigned int hw_level = 0;
 
-	DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev)
+	DL_FOREACH(*odevs, adev)
 		ret += get_next_stream_wake_from_list(
 				adev->dev->streams,
 				min_ts);
 
-	DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev) {
+	DL_FOREACH(*odevs, adev) {
 		if (!cras_iodev_odev_should_wake(adev->dev))
 			continue;
 
-		frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep(
-				adev->dev, &hw_level, &adev->wake_ts);
-		if (!timespec_is_nonzero(&adev->wake_ts))
-			adev->wake_ts = *now;
-
-		est_rate = adev->dev->ext_format->frame_rate *
-				cras_iodev_get_est_rate_ratio(adev->dev);
-
-		ATLOG(atlog,
-	              AUDIO_THREAD_SET_DEV_WAKE,
-		      adev->dev->info.idx,
-		      hw_level,
-		      frames_to_play_in_sleep);
-
-		cras_frames_to_time_precise(
-				frames_to_play_in_sleep,
-				est_rate,
-				&sleep_time);
-
-		add_timespecs(&adev->wake_ts, &sleep_time);
-
 		ret++;
-		ATLOG(atlog,
-					    AUDIO_THREAD_DEV_SLEEP_TIME,
-					    adev->dev->info.idx,
-					    adev->wake_ts.tv_sec,
-					    adev->wake_ts.tv_nsec);
 		if (timespec_after(min_ts, &adev->wake_ts))
 			*min_ts = adev->wake_ts;
 	}
@@ -1050,450 +817,13 @@
 	return ret;
 }
 
-static int input_adev_ignore_wake(const struct open_dev *adev)
-{
-	if (!cras_iodev_is_open(adev->dev))
-		return 1;
-
-	if (!adev->dev->active_node)
-		return 1;
-
-	if (adev->dev->active_node->type == CRAS_NODE_TYPE_HOTWORD &&
-	    !adev->input_streaming)
-		return 1;
-
-	return 0;
-}
-
-static int get_next_input_wake(struct audio_thread *thread,
-			       struct timespec *min_ts,
-			       const struct timespec *now)
-{
-	struct open_dev *adev;
-	int ret = 0; /* The total number of devices to wait on. */
-
-	DL_FOREACH(thread->open_devs[CRAS_STREAM_INPUT], adev) {
-		if (input_adev_ignore_wake(adev))
-			continue;
-		ret++;
-		ATLOG(atlog,
-					    AUDIO_THREAD_DEV_SLEEP_TIME,
-					    adev->dev->info.idx,
-					    adev->wake_ts.tv_sec,
-					    adev->wake_ts.tv_nsec);
-		if (timespec_after(min_ts, &adev->wake_ts))
-			*min_ts = adev->wake_ts;
-	}
-
-	return ret;
-}
-
-static int output_stream_fetch(struct audio_thread *thread)
-{
-	struct open_dev *odev_list = thread->open_devs[CRAS_STREAM_OUTPUT];
-	struct open_dev *adev;
-
-	DL_FOREACH(odev_list, adev) {
-		if (!cras_iodev_is_open(adev->dev))
-			continue;
-		fetch_streams(thread, adev);
-	}
-
-	return 0;
-}
-
-static int wait_pending_output_streams(struct audio_thread *thread)
-{
-	/* TODO(dgreid) - is this needed? */
-	return 0;
-}
-
-/* Gets the master device which the stream is attached to. */
-static inline
-struct cras_iodev *get_master_dev(const struct dev_stream *stream)
-{
-	return (struct cras_iodev *)stream->stream->master_dev.dev_ptr;
-}
-
-/* Updates the estimated sample rate of open device to all attached
- * streams.
- */
-static void update_estimated_rate(struct audio_thread *thread,
-				  struct open_dev *adev)
-{
-	struct cras_iodev *master_dev;
-	struct cras_iodev *dev = adev->dev;
-	struct dev_stream *dev_stream;
-
-	DL_FOREACH(dev->streams, dev_stream) {
-		master_dev = get_master_dev(dev_stream);
-		if (master_dev == NULL) {
-			syslog(LOG_ERR, "Fail to find master open dev.");
-			continue;
-		}
-
-		dev_stream_set_dev_rate(dev_stream,
-				dev->ext_format->frame_rate,
-				cras_iodev_get_est_rate_ratio(dev),
-				cras_iodev_get_est_rate_ratio(master_dev),
-				adev->coarse_rate_adjust);
-	}
-}
-
-/* Returns 0 on success negative error on device failure. */
-static int write_output_samples(struct audio_thread *thread,
-				struct open_dev *adev)
-{
-	struct cras_iodev *odev = adev->dev;
-	unsigned int hw_level;
-	struct timespec hw_tstamp;
-	unsigned int frames, fr_to_req;
-	snd_pcm_sframes_t written;
-	snd_pcm_uframes_t total_written = 0;
-	int rc;
-	uint8_t *dst = NULL;
-	struct cras_audio_area *area = NULL;
-
-	/* Possibly fill zeros for no_stream state and possibly transit state.
-	 */
-	rc = cras_iodev_prepare_output_before_write_samples(odev);
-	if (rc < 0) {
-		syslog(LOG_ERR, "Failed to prepare output dev for write");
-		return rc;
-	}
-
-	if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN)
-		return 0;
-
-	rc = cras_iodev_frames_queued(odev, &hw_tstamp);
-	if (rc < 0)
-		return rc;
-	hw_level = rc;
-
-	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx,
-	      hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
-	if (timespec_is_nonzero(&hw_tstamp)) {
-		if (hw_level < odev->min_cb_level / 2)
-			adev->coarse_rate_adjust = 1;
-		else if (hw_level > odev->max_cb_level * 2)
-			adev->coarse_rate_adjust = -1;
-		else
-			adev->coarse_rate_adjust = 0;
-
-		if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp))
-			update_estimated_rate(thread, adev);
-	}
-	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO,
-				    adev->dev->info.idx, hw_level, 0);
-
-	/* Don't request more than hardware can hold. Note that min_buffer_level
-	 * has been subtracted from the actual hw_level so we need to take it
-	 * into account here. */
-	fr_to_req = cras_iodev_buffer_avail(odev, hw_level);
-
-	/* Have to loop writing to the device, will be at most 2 loops, this
-	 * only happens when the circular buffer is at the end and returns us a
-	 * partial area to write to from mmap_begin */
-	while (total_written < fr_to_req) {
-		frames = fr_to_req - total_written;
-		rc = cras_iodev_get_output_buffer(odev, &area, &frames);
-		if (rc < 0)
-			return rc;
-
-		/* TODO(dgreid) - This assumes interleaved audio. */
-		dst = area->channels[0].buf;
-		written = write_streams(thread, adev, dst, frames);
-		if (written < 0) /* pcm has been closed */
-			return (int)written;
-
-		if (written < (snd_pcm_sframes_t)frames)
-			/* Got all the samples from client that we can, but it
-			 * won't fill the request. */
-			fr_to_req = 0; /* break out after committing samples */
-
-		rc = cras_iodev_put_output_buffer(odev, dst, written);
-		if (rc < 0)
-			return rc;
-		total_written += written;
-	}
-
-	/* Empty hardware and nothing written, zero fill it if it is running. */
-	if (!hw_level && !total_written &&
-	    odev->min_cb_level < odev->buffer_size)
-		cras_iodev_output_underrun(odev);
-
-	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE,
-			hw_level, total_written, odev->min_cb_level);
-	return 0;
-}
-
-static int do_playback(struct audio_thread *thread)
-{
-	struct open_dev *adev;
-	struct dev_stream *curr;
-	int rc;
-
-	/* For multiple output case, update the number of queued frames in shm
-	 * of all streams before starting write output samples. */
-	adev = thread->open_devs[CRAS_STREAM_OUTPUT];
-	if (adev && adev->next) {
-		DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev) {
-			DL_FOREACH(adev->dev->streams, curr)
-				dev_stream_update_frames(curr);
-		}
-	}
-
-	DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev) {
-		if (!cras_iodev_is_open(adev->dev))
-			continue;
-
-		rc = write_output_samples(thread, adev);
-		if (rc < 0) {
-			if (rc == -EPIPE) {
-				/* Handle severe underrun. */
-				ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN,
-				      adev->dev->info.idx, 0, 0);
-				cras_iodev_reset_request(adev->dev);
-			} else {
-				/* Device error, close it. */
-				thread_rm_open_adev(thread, adev);
-			}
-		}
-	}
-
-	/* TODO(dgreid) - once per rstream, not once per dev_stream. */
-	DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev) {
-		struct dev_stream *stream;
-		if (!cras_iodev_is_open(adev->dev))
-			continue;
-		DL_FOREACH(adev->dev->streams, stream) {
-			dev_stream_playback_update_rstream(stream);
-		}
-	}
-
-	return 0;
-}
-
-/* Gets the minimum amount of space available for writing across all streams.
- * Args:
- *    adev - The device to capture from.
- *    write_limit - Initial limit to number of frames to capture.
- */
-static unsigned int get_stream_limit_set_delay(struct open_dev *adev,
-					      unsigned int write_limit)
-{
-	struct cras_rstream *rstream;
-	struct cras_audio_shm *shm;
-	struct dev_stream *stream;
-	int delay;
-	unsigned int avail;
-
-	/* TODO(dgreid) - Setting delay from last dev only. */
-	delay = input_delay_frames(adev);
-
-	DL_FOREACH(adev->dev->streams, stream) {
-		rstream = stream->stream;
-
-		shm = cras_rstream_input_shm(rstream);
-		if (cras_shm_check_write_overrun(shm))
-			ATLOG(atlog, AUDIO_THREAD_READ_OVERRUN,
-			      adev->dev->info.idx, rstream->stream_id,
-			      shm->area->num_overruns);
-		dev_stream_set_delay(stream, delay);
-		avail = dev_stream_capture_avail(stream);
-		write_limit = MIN(write_limit, avail);
-	}
-
-	return write_limit;
-}
-
-/* Read samples from an input device to the specified stream.
- * Args:
- *    adev - The device to capture samples from.
- * Returns 0 on success.
- */
-static int capture_to_streams(struct audio_thread *thread,
-			      struct open_dev *adev)
-{
-	struct cras_iodev *idev = adev->dev;
-	snd_pcm_uframes_t remainder, hw_level, cap_limit;
-	struct timespec hw_tstamp;
-	int rc;
-
-	rc = cras_iodev_frames_queued(idev, &hw_tstamp);
-	if (rc < 0)
-		return rc;
-	hw_level = rc;
-
-	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx,
-	      hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
-	if (timespec_is_nonzero(&hw_tstamp)) {
-		if (hw_level)
-			adev->input_streaming = 1;
-
-		if (hw_level < idev->min_cb_level / 2)
-			adev->coarse_rate_adjust = 1;
-		else if (hw_level > idev->max_cb_level * 2)
-			adev->coarse_rate_adjust = -1;
-		else
-			adev->coarse_rate_adjust = 0;
-		if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp))
-			update_estimated_rate(thread, adev);
-	}
-
-	cap_limit = get_stream_limit_set_delay(adev, hw_level);
-	remainder = MIN(hw_level, cap_limit);
-
-	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO,
-				    idev->info.idx, hw_level, remainder);
-
-	if (cras_iodev_state(idev) != CRAS_IODEV_STATE_NORMAL_RUN)
-		return 0;
-
-	while (remainder > 0) {
-		struct cras_audio_area *area = NULL;
-		struct dev_stream *stream;
-		unsigned int nread, total_read;
-
-		nread = remainder;
-
-		rc = cras_iodev_get_input_buffer(idev, &area, &nread);
-		if (rc < 0 || nread == 0)
-			return rc;
-
-		DL_FOREACH(adev->dev->streams, stream) {
-			unsigned int this_read;
-			unsigned int area_offset;
-
-			area_offset = cras_iodev_stream_offset(idev, stream);
-			this_read = dev_stream_capture(
-				stream, area, area_offset,
-				cras_iodev_get_software_gain_scaler(idev));
-
-			cras_iodev_stream_written(idev, stream, this_read);
-		}
-		if (adev->dev->streams)
-			total_read = cras_iodev_all_streams_written(idev);
-		else
-			total_read = nread; /* No streams, drop. */
-
-		rc = cras_iodev_put_input_buffer(idev, total_read);
-		if (rc < 0)
-			return rc;
-		remainder -= nread;
-
-		if (total_read < nread)
-			break;
-	}
-
-	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE,
-				    remainder, 0, 0);
-
-	return 0;
-}
-
-static int do_capture(struct audio_thread *thread)
-{
-	struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
-	struct open_dev *adev;
-
-	DL_FOREACH(idev_list, adev) {
-		if (!cras_iodev_is_open(adev->dev))
-			continue;
-		if (capture_to_streams(thread, adev) < 0)
-			thread_rm_open_adev(thread, adev);
-	}
-
-	return 0;
-}
-
-/*
- * Set wake_ts for this device to be the earliest wake up time for
- * dev_streams.
- */
-static int set_input_dev_wake_ts(struct open_dev *adev)
-{
-	int rc;
-	struct timespec level_tstamp, wake_time_out, min_ts, now;
-	unsigned int curr_level;
-	struct dev_stream *stream;
-
-	/* Limit the sleep time to 20 seconds. */
-	min_ts.tv_sec = 20;
-	min_ts.tv_nsec = 0;
-	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
-	add_timespecs(&min_ts, &now);
-
-	curr_level = cras_iodev_frames_queued(adev->dev, &level_tstamp);
-	if (!timespec_is_nonzero(&level_tstamp))
-		clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
-
-	/*
-	 * Loop through streams to find the earliest time audio thread
-	 * should wake up.
-	 */
-	DL_FOREACH(adev->dev->streams, stream) {
-		rc = dev_stream_wake_time(
-			stream,
-			curr_level,
-			&level_tstamp,
-			&wake_time_out);
-
-		if (rc < 0)
-			return rc;
-
-		if (timespec_after(&min_ts, &wake_time_out)) {
-			min_ts = wake_time_out;
-		}
-	}
-	adev->wake_ts = min_ts;
-	return 0;
-}
-
-static int send_captured_samples(struct audio_thread *thread)
-{
-	struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
-	struct open_dev *adev;
-	int rc;
-
-	// TODO(dgreid) - once per rstream, not once per dev_stream.
-	DL_FOREACH(idev_list, adev) {
-		struct dev_stream *stream;
-
-		if (!cras_iodev_is_open(adev->dev))
-			continue;
-
-		/* Post samples to rstream if there are enough samples. */
-		DL_FOREACH(adev->dev->streams, stream) {
-			dev_stream_capture_update_rstream(stream);
-		}
-
-		/* Set wake_ts for this device. */
-		rc = set_input_dev_wake_ts(adev);
-		if (rc < 0)
-			return rc;
-	}
-
-	return 0;
-}
-
-/* Reads and/or writes audio sampels from/to the devices. */
-static int stream_dev_io(struct audio_thread *thread)
-{
-	output_stream_fetch(thread);
-	do_capture(thread);
-	send_captured_samples(thread);
-	wait_pending_output_streams(thread);
-	do_playback(thread);
-
-	return 0;
-}
-
-int fill_next_sleep_interval(struct audio_thread *thread, struct timespec *ts)
+/* Returns the number of active streams plus the number of active devices. */
+static int fill_next_sleep_interval(struct audio_thread *thread,
+				    struct timespec *ts)
 {
 	struct timespec min_ts;
 	struct timespec now;
-	int ret; /* The sum of active streams and devices. */
+	int ret;
 
 	ts->tv_sec = 0;
 	ts->tv_nsec = 0;
@@ -1502,14 +832,53 @@
 	min_ts.tv_nsec = 0;
 	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
 	add_timespecs(&min_ts, &now);
-	ret = get_next_output_wake(thread, &min_ts, &now);
-	ret += get_next_input_wake(thread, &min_ts, &now);
+	ret = get_next_output_wake(&thread->open_devs[CRAS_STREAM_OUTPUT],
+				   &min_ts, &now);
+	ret += dev_io_next_input_wake(&thread->open_devs[CRAS_STREAM_INPUT],
+				      &min_ts);
 	if (timespec_after(&min_ts, &now))
 		subtract_timespecs(&min_ts, &now, ts);
 
 	return ret;
 }
 
+static struct pollfd *add_pollfd(struct audio_thread *thread,
+				 int fd, int is_write)
+{
+	thread->pollfds[thread->num_pollfds].fd = fd;
+	if (is_write)
+		thread->pollfds[thread->num_pollfds].events = POLLOUT;
+	else
+		thread->pollfds[thread->num_pollfds].events = POLLIN;
+	thread->num_pollfds++;
+	if (thread->num_pollfds >= thread->pollfds_size) {
+		thread->pollfds_size *= 2;
+		thread->pollfds =
+			(struct pollfd *)realloc(thread->pollfds,
+						 sizeof(*thread->pollfds) *
+						 thread->pollfds_size);
+		return NULL;
+	}
+
+	return &thread->pollfds[thread->num_pollfds - 1];
+}
+
+static int continuous_zero_sleep_count = 0;
+static void check_busyloop(struct timespec* wait_ts)
+{
+	if(wait_ts->tv_sec == 0 && wait_ts->tv_nsec == 0)
+	{
+		continuous_zero_sleep_count ++;
+		if(continuous_zero_sleep_count ==
+		   MAX_CONTINUOUS_ZERO_SLEEP_COUNT)
+			cras_audio_thread_busyloop();
+	}
+	else
+	{
+		continuous_zero_sleep_count = 0;
+	}
+}
+
 /* For playback, fill the audio buffer when needed, for capture, pull out
  * samples when they are ready.
  * This thread will attempt to run at a high priority to allow for low latency
@@ -1523,9 +892,6 @@
 	struct open_dev *adev;
 	struct dev_stream *curr;
 	struct timespec ts, now, last_wake;
-	struct pollfd *pollfds;
-	unsigned int num_pollfds;
-	unsigned int pollfds_size = 32;
 	int msg_fd;
 	int rc;
 
@@ -1539,44 +905,34 @@
 	longest_wake.tv_sec = 0;
 	longest_wake.tv_nsec = 0;
 
-	pollfds = (struct pollfd *)malloc(sizeof(*pollfds) * pollfds_size);
-	pollfds[0].fd = msg_fd;
-	pollfds[0].events = POLLIN;
+	thread->pollfds[0].fd = msg_fd;
+	thread->pollfds[0].events = POLLIN;
 
 	while (1) {
 		struct timespec *wait_ts;
 		struct iodev_callback_list *iodev_cb;
 
 		wait_ts = NULL;
-		num_pollfds = 1;
+		thread->num_pollfds = 1;
 
 		/* device opened */
-		rc = stream_dev_io(thread);
-		if (rc < 0)
-			syslog(LOG_ERR, "audio cb error %d", rc);
+		dev_io_run(&thread->open_devs[CRAS_STREAM_OUTPUT],
+			   &thread->open_devs[CRAS_STREAM_INPUT],
+			   thread->remix_converter);
 
 		if (fill_next_sleep_interval(thread, &ts))
 			wait_ts = &ts;
 
 restart_poll_loop:
-		num_pollfds = 1;
+		thread->num_pollfds = 1;
 
 		DL_FOREACH(iodev_callbacks, iodev_cb) {
 			if (!iodev_cb->enabled)
 				continue;
-			pollfds[num_pollfds].fd = iodev_cb->fd;
-			iodev_cb->pollfd = &pollfds[num_pollfds];
-			if (iodev_cb->is_write)
-				pollfds[num_pollfds].events = POLLOUT;
-			else
-				pollfds[num_pollfds].events = POLLIN;
-			num_pollfds++;
-			if (num_pollfds >= pollfds_size) {
-				pollfds_size *= 2;
-				pollfds = (struct pollfd *)realloc(pollfds,
-					sizeof(*pollfds) * pollfds_size);
-				goto restart_poll_loop;
-			}
+			iodev_cb->pollfd = add_pollfd(thread, iodev_cb->fd,
+						      iodev_cb->is_write);
+			if (!iodev_cb->pollfd)
+			    goto restart_poll_loop;
 		}
 
 		/* TODO(dgreid) - once per rstream not per dev_stream */
@@ -1585,17 +941,17 @@
 				int fd = dev_stream_poll_stream_fd(curr);
 				if (fd < 0)
 					continue;
-				pollfds[num_pollfds].fd = fd;
-				pollfds[num_pollfds].events = POLLIN;
-				num_pollfds++;
-				if (num_pollfds >= pollfds_size) {
-					pollfds_size *= 2;
-					pollfds = (struct pollfd *)realloc(
-							pollfds,
-							sizeof(*pollfds) *
-								pollfds_size);
+				if (!add_pollfd(thread, fd, 0))
 					goto restart_poll_loop;
-				}
+			}
+		}
+		DL_FOREACH(thread->open_devs[CRAS_STREAM_INPUT], adev) {
+			DL_FOREACH(adev->dev->streams, curr) {
+				int fd = dev_stream_poll_stream_fd(curr);
+				if (fd < 0)
+					continue;
+				if (!add_pollfd(thread, fd, 0))
+					goto restart_poll_loop;
 			}
 		}
 
@@ -1607,17 +963,17 @@
 				longest_wake = this_wake;
 		}
 
-		ATLOG(atlog, AUDIO_THREAD_SLEEP,
-					    wait_ts ? wait_ts->tv_sec : 0,
-					    wait_ts ? wait_ts->tv_nsec : 0,
-					    longest_wake.tv_nsec);
-		rc = ppoll(pollfds, num_pollfds, wait_ts, NULL);
+		ATLOG(atlog, AUDIO_THREAD_SLEEP, wait_ts ? wait_ts->tv_sec : 0,
+		      wait_ts ? wait_ts->tv_nsec : 0, longest_wake.tv_nsec);
+		if(wait_ts)
+			check_busyloop(wait_ts);
+		rc = ppoll(thread->pollfds, thread->num_pollfds, wait_ts, NULL);
 		clock_gettime(CLOCK_MONOTONIC_RAW, &last_wake);
 		ATLOG(atlog, AUDIO_THREAD_WAKE, rc, 0, 0);
 		if (rc <= 0)
 			continue;
 
-		if (pollfds[0].revents & POLLIN) {
+		if (thread->pollfds[0].revents & POLLIN) {
 			rc = handle_playback_thread_message(thread);
 			if (rc < 0)
 				syslog(LOG_INFO, "handle message %d", rc);
@@ -1626,9 +982,8 @@
 		DL_FOREACH(iodev_callbacks, iodev_cb) {
 			if (iodev_cb->pollfd &&
 			    iodev_cb->pollfd->revents & (POLLIN | POLLOUT)) {
-				ATLOG(
-					atlog, AUDIO_THREAD_IODEV_CB,
-					iodev_cb->is_write, 0, 0);
+				ATLOG(atlog, AUDIO_THREAD_IODEV_CB,
+				      iodev_cb->is_write, 0, 0);
 				iodev_cb->cb(iodev_cb->cb_data);
 			}
 		}
@@ -1651,8 +1006,7 @@
 static int audio_thread_post_message(struct audio_thread *thread,
 				     struct audio_thread_msg *msg)
 {
-	int err;
-	void *rsp;
+	int err, rsp;
 
 	err = write(thread->to_thread_fds[1], msg, msg->length);
 	if (err < 0) {
@@ -1660,13 +1014,13 @@
 		return err;
 	}
 	/* Synchronous action, wait for response. */
-	err = read(thread->to_main_fds[0], &rsp, sizeof(rsp));
+	err = read_until_finished(thread->to_main_fds[0], &rsp, sizeof(rsp));
 	if (err < 0) {
 		syslog(LOG_ERR, "Failed to read reply from thread.");
 		return err;
 	}
 
-	return (intptr_t)rsp;
+	return rsp;
 }
 
 static void init_open_device_msg(struct audio_thread_open_device_msg *msg,
@@ -1777,6 +1131,22 @@
 	return audio_thread_post_message(thread, &msg.header);
 }
 
+int audio_thread_set_aec_dump(struct audio_thread *thread,
+			       cras_stream_id_t stream_id,
+			       unsigned int start,
+			       int fd)
+{
+	struct audio_thread_aec_dump_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.header.id = AUDIO_THREAD_AEC_DUMP;
+	msg.header.length = sizeof(msg);
+	msg.stream_id = stream_id;
+	msg.start = start;
+	msg.fd = fd;
+	return audio_thread_post_message(thread, &msg.header);
+}
+
 int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd) {
 	struct audio_thread_rm_callback_msg msg;
 
@@ -1810,9 +1180,10 @@
 		}
 		for (j = i + 1; j < num_channels; j++) {
 			if (coefficient[i * num_channels + j] != 0 ||
-			    coefficient[j * num_channels + i] != 0)
+			    coefficient[j * num_channels + i] != 0) {
 				identity_remix = 0;
 				break;
+			}
 		}
 	}
 
@@ -1829,22 +1200,17 @@
 		return err;
 	}
 	/* Synchronous action, wait for response. */
-	err = read(thread->to_main_fds[0], &rsp, sizeof(rsp));
+	err = read_until_finished(thread->to_main_fds[0], &rsp, sizeof(rsp));
 	if (err < 0) {
 		syslog(LOG_ERR, "Failed to read reply from thread.");
 		return err;
 	}
 
 	if (rsp)
-		cras_fmt_conv_destroy((struct cras_fmt_conv *)rsp);
+		cras_fmt_conv_destroy((struct cras_fmt_conv **)&rsp);
 	return 0;
 }
 
-struct cras_fmt_conv *audio_thread_get_global_remix_converter()
-{
-	return remix_converter;
-}
-
 struct audio_thread *audio_thread_create()
 {
 	int rc;
@@ -1875,6 +1241,11 @@
 
 	atlog = audio_thread_event_log_init();
 
+	thread->pollfds_size = 32;
+	thread->pollfds =
+		(struct pollfd *)malloc(sizeof(*thread->pollfds)
+					* thread->pollfds_size);
+
 	return thread;
 }
 
@@ -1905,6 +1276,18 @@
 	return audio_thread_post_message(thread, &msg.header);
 }
 
+int audio_thread_is_dev_open(struct audio_thread *thread,
+			     struct cras_iodev *dev)
+{
+	struct audio_thread_open_device_msg msg;
+
+	if (!dev)
+		return 0;
+
+	init_open_device_msg(&msg, AUDIO_THREAD_IS_DEV_OPEN, dev);
+	return audio_thread_post_message(thread, &msg.header);
+}
+
 int audio_thread_dev_start_ramp(struct audio_thread *thread,
 				struct cras_iodev *dev,
 				enum CRAS_IODEV_RAMP_REQUEST request)
@@ -1938,8 +1321,6 @@
 
 void audio_thread_destroy(struct audio_thread *thread)
 {
-	audio_thread_event_log_deinit(atlog);
-
 	if (thread->started) {
 		struct audio_thread_msg msg;
 
@@ -1949,6 +1330,10 @@
 		pthread_join(thread->tid, NULL);
 	}
 
+	free(thread->pollfds);
+
+	audio_thread_event_log_deinit(atlog);
+
 	if (thread->to_thread_fds[0] != -1) {
 		close(thread->to_thread_fds[0]);
 		close(thread->to_thread_fds[1]);
@@ -1958,8 +1343,8 @@
 		close(thread->to_main_fds[1]);
 	}
 
-	if (remix_converter)
-		cras_fmt_conv_destroy(remix_converter);
+	if (thread->remix_converter)
+		cras_fmt_conv_destroy(&thread->remix_converter);
 
 	free(thread);
 }
diff --git a/cras/src/server/audio_thread.h b/cras/src/server/audio_thread.h
index 1131e0c..e2c0d72 100644
--- a/cras/src/server/audio_thread.h
+++ b/cras/src/server/audio_thread.h
@@ -11,26 +11,14 @@
 
 #include "cras_iodev.h"
 #include "cras_types.h"
+#include "dev_io.h"
 
 struct buffer_share;
+struct cras_fmt_conv;
 struct cras_iodev;
 struct cras_rstream;
 struct dev_stream;
 
-/* Open input/output devices.
- *    dev - The device.
- *    wake_ts - When callback is needed to avoid xrun.
- *    coarse_rate_adjust - Hack for when the sample rate needs heavy correction.
- *    input_streaming - For capture, has the input started?
- */
-struct open_dev {
-	struct cras_iodev *dev;
-	struct timespec wake_ts;
-	int coarse_rate_adjust;
-	int input_streaming;
-	struct open_dev *prev, *next;
-};
-
 /* Hold communication pipes and pthread info for the thread used to play or
  * record audio.
  *    to_thread_fds - Send a message from main to running thread.
@@ -39,6 +27,10 @@
  *    started - Non-zero if the thread has started successfully.
  *    suspended - Non-zero if the thread is suspended.
  *    open_devs - Lists of open input and output devices.
+ *    pollfds - What FDs wake up this thread.
+ *    pollfds_size - Number of available poll fds.
+ *    num_pollfds - Number of currently registered poll fds.
+ *    remix_converter - Format converter used to remix output channels.
  */
 struct audio_thread {
 	int to_thread_fds[2];
@@ -47,7 +39,10 @@
 	int started;
 	int suspended;
 	struct open_dev *open_devs[CRAS_NUM_DIRECTIONS];
-
+	struct pollfd *pollfds;
+	size_t pollfds_size;
+	size_t num_pollfds;
+	struct cras_fmt_conv *remix_converter;
 };
 
 /* Callback function to be handled in main loop in audio thread.
@@ -58,7 +53,7 @@
 
 /* Creates an audio thread.
  * Returns:
- *    A pointer to the newly create audio thread.  It must be freed by calling
+ *    A pointer to the newly created audio thread.  It must be freed by calling
  *    audio_thread_destroy().  Returns NULL on error.
  */
 struct audio_thread *audio_thread_create();
@@ -79,6 +74,14 @@
 int audio_thread_rm_open_dev(struct audio_thread *thread,
 			     struct cras_iodev *dev);
 
+/* Checks if dev is open and used by audio thread.
+ * Args:
+ *    thread - The thread accessing open devs.
+ *    dev - The device to check if it has already been open.
+ */
+int audio_thread_is_dev_open(struct audio_thread *thread,
+			     struct cras_iodev *dev);
+
 /* Adds an thread_callback to audio thread.
  * Args:
  *    fd - The file descriptor to be polled for the callback.
@@ -172,16 +175,24 @@
 int audio_thread_dump_thread_info(struct audio_thread *thread,
 				  struct audio_debug_info *info);
 
+/* Starts or stops the aec dump task.
+ * Args:
+ *    thread - pointer to the audio thread.
+ *    stream_id - id of the target stream for aec dump.
+ *    start - True to start the aec dump, false to stop.
+ *    fd - File to store aec dump result.
+ */
+int audio_thread_set_aec_dump(struct audio_thread *thread,
+			      cras_stream_id_t stream_id,
+			      unsigned int start,
+			      int fd);
+
 /* Configures the global converter for output remixing. Called by main
  * thread. */
 int audio_thread_config_global_remix(struct audio_thread *thread,
 				     unsigned int num_channels,
 				     const float *coefficient);
 
-/* Gets the global remix converter. */
-struct cras_fmt_conv *audio_thread_get_global_remix_converter();
-
-
 /* Start ramping on a device.
  *
  * Ramping is started/updated in audio thread. This function lets the main
diff --git a/cras/src/server/config/aec_config.c b/cras/src/server/config/aec_config.c
new file mode 100644
index 0000000..d51d4b8
--- /dev/null
+++ b/cras/src/server/config/aec_config.c
@@ -0,0 +1,479 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "aec_config.h"
+#include "iniparser_wrapper.h"
+
+static const unsigned int MAX_INI_NAME_LEN = 63;
+
+#define AEC_CONFIG_NAME "aec.ini"
+
+#define AEC_GET_INT(ini, category, key)			\
+	iniparser_getint(				\
+		ini, AEC_ ## category ## _ ## key,	\
+		AEC_ ## category ## _ ## key ## _VALUE)
+
+#define AEC_GET_FLOAT(ini, category, key)		\
+	iniparser_getdouble(				\
+		ini, AEC_ ## category ## _ ## key,	\
+		AEC_ ## category ## _ ## key ## _VALUE)
+
+struct aec_config *aec_config_get(const char *device_config_dir)
+{
+	struct aec_config *config;
+	char ini_name[MAX_INI_NAME_LEN + 1];
+	dictionary *ini;
+
+	snprintf(ini_name, MAX_INI_NAME_LEN, "%s/%s",
+		 device_config_dir, AEC_CONFIG_NAME);
+	ini_name[MAX_INI_NAME_LEN] = '\0';
+
+	ini = iniparser_load_wrapper(ini_name);
+	if (ini == NULL) {
+		syslog(LOG_DEBUG, "No ini file %s", ini_name);
+		return NULL;
+	}
+
+	config = (struct aec_config *)calloc(1, sizeof(*config));
+
+	config->delay.default_delay =
+		AEC_GET_INT(ini, DELAY, DEFAULT_DELAY);
+	config->delay.down_sampling_factor =
+		AEC_GET_INT(ini, DELAY, DOWN_SAMPLING_FACTOR);
+	config->delay.num_filters =
+		AEC_GET_INT(ini, DELAY, NUM_FILTERS);
+	config->delay.api_call_jitter_blocks =
+		AEC_GET_INT(ini, DELAY, API_CALL_JITTER_BLOCKS);
+	config->delay.min_echo_path_delay_blocks =
+		AEC_GET_INT(ini, DELAY, MIN_ECHO_PATH_DELAY_BLOCKS);
+	config->delay.delay_headroom_blocks =
+		AEC_GET_INT(ini, DELAY, DELAY_HEADROOM_BLOCKS);
+	config->delay.hysteresis_limit_1_blocks =
+		AEC_GET_INT(ini, DELAY, HYSTERESIS_LIMIT_1_BLOCKS);
+	config->delay.hysteresis_limit_2_blocks =
+		AEC_GET_INT(ini, DELAY, HYSTERESIS_LIMIT_2_BLOCKS);
+	config->delay.skew_hysteresis_blocks =
+		AEC_GET_INT(ini, DELAY, SKEW_HYSTERESIS_BLOCKS);
+	config->delay.fixed_capture_delay_samples =
+		AEC_GET_INT(ini, DELAY, FIXED_CAPTURE_DELAY_SAMPLES);
+
+	config->filter.main.length_blocks =
+		AEC_GET_INT(ini, FILTER_MAIN, LENGTH_BLOCKS);
+	config->filter.main.leakage_converged =
+		AEC_GET_FLOAT(ini, FILTER_MAIN, LEAKAGE_CONVERGED);
+	config->filter.main.leakage_diverged =
+		AEC_GET_FLOAT(ini, FILTER_MAIN, LEAKAGE_DIVERGED);
+	config->filter.main.error_floor =
+		AEC_GET_FLOAT(ini, FILTER_MAIN, ERROR_FLOOR);
+	config->filter.main.noise_gate =
+		AEC_GET_FLOAT(ini, FILTER_MAIN, NOISE_GATE);
+
+	config->filter.shadow.length_blocks =
+		AEC_GET_INT(ini, FILTER_SHADOW, LENGTH_BLOCKS);
+	config->filter.shadow.rate =
+		AEC_GET_FLOAT(ini, FILTER_SHADOW, RATE);
+	config->filter.shadow.noise_gate =
+		AEC_GET_FLOAT(ini, FILTER_SHADOW, NOISE_GATE);
+
+	config->filter.main_initial.length_blocks =
+		AEC_GET_INT(ini, FILTER_MAIN_INIT, LENGTH_BLOCKS);
+	config->filter.main_initial.leakage_converged =
+		AEC_GET_FLOAT(ini, FILTER_MAIN_INIT, LEAKAGE_CONVERGED);
+	config->filter.main_initial.leakage_diverged =
+		AEC_GET_FLOAT(ini, FILTER_MAIN_INIT, LEAKAGE_DIVERGED);
+	config->filter.main_initial.error_floor =
+		AEC_GET_FLOAT(ini, FILTER_MAIN_INIT, ERROR_FLOOR);
+	config->filter.main_initial.noise_gate =
+		AEC_GET_FLOAT(ini, FILTER_MAIN_INIT, NOISE_GATE);
+
+	config->filter.shadow_initial.length_blocks =
+		AEC_GET_INT(ini, FILTER_SHADOW_INIT, LENGTH_BLOCKS);
+	config->filter.shadow_initial.rate =
+		AEC_GET_FLOAT(ini, FILTER_SHADOW_INIT, RATE);
+	config->filter.shadow_initial.noise_gate =
+		AEC_GET_FLOAT(ini, FILTER_SHADOW_INIT, NOISE_GATE);
+
+	config->filter.config_change_duration_blocks =
+		AEC_GET_INT(ini, FILTER, CONFIG_CHANGE_DURATION_BLOCKS);
+	config->filter.initial_state_seconds =
+		AEC_GET_FLOAT(ini, FILTER, INITIAL_STATE_SECONDS);
+	config->filter.conservative_initial_phase =
+		AEC_GET_INT(ini, FILTER, CONSERVATIVE_INITIAL_PHASE);
+	config->filter.enable_shadow_filter_output_usage =
+		AEC_GET_INT(ini, FILTER, ENABLE_SHADOW_FILTER_OUTPUT_USAGE);
+
+	config->erle.min =
+		AEC_GET_FLOAT(ini, ERLE, MIN);
+	config->erle.max_l =
+		AEC_GET_FLOAT(ini, ERLE, MAX_L);
+	config->erle.max_h =
+		AEC_GET_FLOAT(ini, ERLE, MAX_H);
+	config->erle.onset_detection =
+		AEC_GET_INT(ini, ERLE, ONSET_DETECTION);
+
+	config->ep_strength.lf =
+		AEC_GET_FLOAT(ini, EP_STRENGTH, LF);
+	config->ep_strength.mf =
+		AEC_GET_FLOAT(ini, EP_STRENGTH, MF);
+	config->ep_strength.hf =
+		AEC_GET_FLOAT(ini, EP_STRENGTH, HF);
+	config->ep_strength.default_len =
+		AEC_GET_FLOAT(ini, EP_STRENGTH, DEFAULT_LEN);
+	config->ep_strength.reverb_based_on_render =
+		AEC_GET_INT(ini, EP_STRENGTH, REVERB_BASED_ON_RENDER);
+	config->ep_strength.bounded_erl =
+		AEC_GET_INT(ini, EP_STRENGTH, BOUNDED_ERL);
+	config->ep_strength.echo_can_saturate =
+		AEC_GET_INT(ini, EP_STRENGTH, ECHO_CAN_SATURATE);
+
+	config->gain_mask.m0 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M0);
+	config->gain_mask.m1 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M1);
+	config->gain_mask.m2 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M2);
+	config->gain_mask.m3 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M3);
+	config->gain_mask.m5 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M5);
+	config->gain_mask.m6 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M6);
+	config->gain_mask.m7 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M7);
+	config->gain_mask.m8 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M8);
+	config->gain_mask.m9 =
+		AEC_GET_FLOAT(ini, GAIN_MASK, M9);
+	config->gain_mask.gain_curve_offset =
+		AEC_GET_FLOAT(ini, GAIN_MASK, GAIN_CURVE_OFFSET);
+	config->gain_mask.gain_curve_slope =
+		AEC_GET_FLOAT(ini, GAIN_MASK, GAIN_CURVE_SLOPE);
+	config->gain_mask.temporal_masking_lf =
+		AEC_GET_FLOAT(ini, GAIN_MASK, TEMPORAL_MASKING_LF);
+	config->gain_mask.temporal_masking_hf =
+		AEC_GET_FLOAT(ini, GAIN_MASK, TEMPORAL_MASKING_HF);
+	config->gain_mask.temporal_masking_lf_bands =
+		AEC_GET_INT(ini, GAIN_MASK, TEMPORAL_MASKING_LF_BANDS);
+
+	config->echo_audibility.low_render_limit =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, LOW_RENDER_LIMIT);
+	config->echo_audibility.normal_render_limit =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, NORMAL_RENDER_LIMIT);
+	config->echo_audibility.floor_power =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, FLOOR_POWER);
+	config->echo_audibility.audibility_threshold_lf =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, AUDIBILITY_THRESHOLD_LF);
+	config->echo_audibility.audibility_threshold_mf =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, AUDIBILITY_THRESHOLD_MF);
+	config->echo_audibility.audibility_threshold_hf =
+		AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, AUDIBILITY_THRESHOLD_HF);
+	config->echo_audibility.use_stationary_properties =
+		AEC_GET_INT(ini, ECHO_AUDIBILITY, USE_STATIONARY_PROPERTIES);
+
+	config->render_levels.active_render_limit =
+		AEC_GET_FLOAT(ini, RENDER_LEVELS, ACTIVE_RENDER_LIMIT);
+	config->render_levels.poor_excitation_render_limit =
+		AEC_GET_FLOAT(ini, RENDER_LEVELS, POOR_EXCITATION_RENDER_LIMIT);
+	config->render_levels.poor_excitation_render_limit_ds8 =
+		AEC_GET_FLOAT(ini, RENDER_LEVELS, POOR_EXCITATION_RENDER_LIMIT_DS8);
+
+	config->echo_removal_control.gain_rampup.initial_gain =
+		AEC_GET_FLOAT(ini, ECHO_REMOVAL_CTL, INITIAL_GAIN);
+	config->echo_removal_control.gain_rampup.first_non_zero_gain =
+		AEC_GET_FLOAT(ini, ECHO_REMOVAL_CTL, FIRST_NON_ZERO_GAIN);
+	config->echo_removal_control.gain_rampup.non_zero_gain_blocks =
+		AEC_GET_INT(ini, ECHO_REMOVAL_CTL, NON_ZERO_GAIN_BLOCKS);
+	config->echo_removal_control.gain_rampup.full_gain_blocks =
+		AEC_GET_INT(ini, ECHO_REMOVAL_CTL, FULL_GAIN_BLOCKS);
+	config->echo_removal_control.has_clock_drift =
+		AEC_GET_INT(ini, ECHO_REMOVAL_CTL, HAS_CLOCK_DRIFT);
+	config->echo_removal_control.linear_and_stable_echo_path =
+		AEC_GET_INT(ini, ECHO_REMOVAL_CTL, LINEAR_AND_STABLE_ECHO_PATH);
+
+	config->echo_model.noise_floor_hold =
+		AEC_GET_INT(ini, ECHO_MODEL, NOISE_FLOOR_HOLD);
+	config->echo_model.min_noise_floor_power =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, MIN_NOISE_FLOOR_POWER);
+	config->echo_model.stationary_gate_slope =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, STATIONARY_GATE_SLOPE);
+	config->echo_model.noise_gate_power =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, NOISE_GATE_POWER);
+	config->echo_model.noise_gate_slope =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, NOISE_GATE_SLOPE);
+	config->echo_model.render_pre_window_size =
+		AEC_GET_INT(ini, ECHO_MODEL, RENDER_PRE_WINDOW_SIZE);
+	config->echo_model.render_post_window_size =
+		AEC_GET_INT(ini, ECHO_MODEL, RENDER_POST_WINDOW_SIZE);
+	config->echo_model.render_pre_window_size_init =
+		AEC_GET_INT(ini, ECHO_MODEL, RENDER_PRE_WINDOW_SIZE_INIT);
+	config->echo_model.render_post_window_size_init =
+		AEC_GET_INT(ini, ECHO_MODEL, RENDER_POST_WINDOW_SIZE_INIT);
+	config->echo_model.nonlinear_hold =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, NONLINEAR_HOLD);
+	config->echo_model.nonlinear_release =
+		AEC_GET_FLOAT(ini, ECHO_MODEL, NONLINEAR_RELEASE);
+
+	config->suppressor.nearend_average_blocks =
+		AEC_GET_INT(ini, SUPPRESSOR, NEAREND_AVERAGE_BLOCKS);
+
+	config->suppressor.normal_tuning.mask_lf.enr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_LF_ENR_TRANSPARENT);
+	config->suppressor.normal_tuning.mask_lf.enr_suppress =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_LF_ENR_SUPPRESS);
+	config->suppressor.normal_tuning.mask_lf.emr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_LF_EMR_TRANSPARENT);
+	config->suppressor.normal_tuning.mask_hf.enr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_HF_ENR_TRANSPARENT);
+	config->suppressor.normal_tuning.mask_hf.enr_suppress =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_HF_ENR_SUPPRESS);
+	config->suppressor.normal_tuning.mask_hf.emr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING,
+			      MASK_HF_EMR_TRANSPARENT);
+	config->suppressor.normal_tuning.max_inc_factor =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING, MAX_INC_FACTOR);
+	config->suppressor.normal_tuning.max_dec_factor_lf =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NORMAL_TUNING, MAX_DEC_FACTOR_LF);
+
+	config->suppressor.nearend_tuning.mask_lf.enr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_LF_ENR_TRANSPARENT);
+	config->suppressor.nearend_tuning.mask_lf.enr_suppress =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_LF_ENR_SUPPRESS);
+	config->suppressor.nearend_tuning.mask_lf.emr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_LF_EMR_TRANSPARENT);
+	config->suppressor.nearend_tuning.mask_hf.enr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_HF_ENR_TRANSPARENT);
+	config->suppressor.nearend_tuning.mask_hf.enr_suppress =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_HF_ENR_SUPPRESS);
+	config->suppressor.nearend_tuning.mask_hf.emr_transparent =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING,
+			      MASK_HF_EMR_TRANSPARENT);
+	config->suppressor.nearend_tuning.max_inc_factor =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING, MAX_INC_FACTOR);
+	config->suppressor.nearend_tuning.max_dec_factor_lf =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_NEAREND_TUNING, MAX_DEC_FACTOR_LF);
+
+	config->suppressor.dominant_nearend_detection.enr_threshold =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_DOMINANT_NEAREND_DETECTION,
+			ENR_THRESHOLD);
+	config->suppressor.dominant_nearend_detection.snr_threshold =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_DOMINANT_NEAREND_DETECTION,
+			SNR_THRESHOLD);
+	config->suppressor.dominant_nearend_detection.hold_duration =
+		AEC_GET_INT(ini, SUPPRESSOR_DOMINANT_NEAREND_DETECTION,
+			HOLD_DURATION);
+	config->suppressor.dominant_nearend_detection.trigger_threshold =
+		AEC_GET_INT(ini, SUPPRESSOR_DOMINANT_NEAREND_DETECTION,
+			TRIGGER_THRESHOLD);
+
+	config->suppressor.high_bands_suppression.enr_threshold =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_HIGH_BANDS_SUPPRESSION,
+			ENR_THRESHOLD);
+	config->suppressor.high_bands_suppression.max_gain_during_echo =
+		AEC_GET_FLOAT(ini, SUPPRESSOR_HIGH_BANDS_SUPPRESSION,
+			MAX_GAIN_DURING_ECHO);
+
+	config->suppressor.floor_first_increase =
+		AEC_GET_FLOAT(ini, SUPPRESSOR, FLOOR_FIRST_INCREASE);
+	config->suppressor.enforce_transparent =
+		AEC_GET_INT(ini, SUPPRESSOR, ENFORCE_TRANSPARENT);
+	config->suppressor.enforce_empty_higher_bands =
+		AEC_GET_INT(ini, SUPPRESSOR, ENFORCE_EMPTY_HIGHER_BANDS);
+
+	return config;
+}
+
+void aec_config_dump(struct aec_config *config)
+{
+	syslog(LOG_ERR, "---- aec config dump ----");
+	syslog(LOG_ERR, "Delay:");
+	syslog(LOG_ERR, "    default_delay %zu sampling_factor %zu num_filters %zu",
+			config->delay.default_delay,
+			config->delay.down_sampling_factor,
+			config->delay.num_filters);
+	syslog(LOG_ERR, "    api_call_jitter_blocks %zu, min_echo_path_delay_blocks %zu",
+			config->delay.api_call_jitter_blocks,
+			config->delay.min_echo_path_delay_blocks);
+	syslog(LOG_ERR, "    delay_headroom_blocks %zu, hysteresis_limit_1_blocks %zu",
+			config->delay.delay_headroom_blocks,
+			config->delay.hysteresis_limit_1_blocks);
+	syslog(LOG_ERR, "    hysteresis_limit_2_blocks %zu, skew_hysteresis_blocks %zu",
+			config->delay.hysteresis_limit_2_blocks,
+			config->delay.skew_hysteresis_blocks);
+	syslog(LOG_ERR, "    fixed_capture_delay_samples %zu",
+			config->delay.fixed_capture_delay_samples);
+
+	syslog(LOG_ERR, "Filter main configuration:");
+	syslog(LOG_ERR, "    length_blocks %zu, leakage_converged %f, leakage_diverged %f",
+			config->filter.main.length_blocks,
+			config->filter.main.leakage_converged,
+			config->filter.main.leakage_diverged);
+	syslog(LOG_ERR, "    error_floor %f, noise_gate %f",
+			config->filter.main.error_floor,
+			config->filter.main.noise_gate);
+	syslog(LOG_ERR, "Filter shadow configuration:");
+	syslog(LOG_ERR, "    length_blocks %zu, rate %f, noise_gate %f",
+			config->filter.shadow.length_blocks,
+			config->filter.shadow.rate,
+			config->filter.shadow.noise_gate);
+	syslog(LOG_ERR, "Filter main initial configuration:");
+	syslog(LOG_ERR, "    length_blocks %zu, leakage_converged %f",
+			config->filter.main_initial.length_blocks,
+			config->filter.main_initial.leakage_converged);
+	syslog(LOG_ERR, "    leakage_diverged %f, error_floor %f, noise_gate %f",
+			config->filter.main_initial.leakage_diverged,
+			config->filter.main_initial.error_floor,
+			config->filter.main_initial.noise_gate);
+	syslog(LOG_ERR, "Filter shadow initial configuration:");
+	syslog(LOG_ERR, "    length_blocks %zu, rate %f, noise_gate %f",
+			config->filter.shadow_initial.length_blocks,
+			config->filter.shadow_initial.rate,
+			config->filter.shadow_initial.noise_gate);
+	syslog(LOG_ERR, "Filter:    config_change_duration_blocks %d",
+			config->filter.config_change_duration_blocks);
+	syslog(LOG_ERR, "    initial_state_seconds %f",
+			config->filter.initial_state_seconds);
+	syslog(LOG_ERR, "    conservative_initial_phase %d",
+			config->filter.conservative_initial_phase);
+	syslog(LOG_ERR, "    enable_shadow_filter_output_usage %d",
+			config->filter.enable_shadow_filter_output_usage);
+	syslog(LOG_ERR, "Erle: min %f max_l %f max_h %f onset_detection %d",
+			config->erle.min, config->erle.max_l,
+			config->erle.max_h, config->erle.onset_detection);
+	syslog(LOG_ERR, "Ep strength: lf %f mf %f hf %f default_len %f",
+			config->ep_strength.lf,
+			config->ep_strength.mf,
+			config->ep_strength.hf,
+			config->ep_strength.default_len);
+	syslog(LOG_ERR, "    echo_can_saturate %d, bounded_erl %d,"
+			"    ep_strength.reverb_based_on_render %d",
+			config->ep_strength.echo_can_saturate,
+			config->ep_strength.bounded_erl,
+			config->ep_strength.reverb_based_on_render);
+	syslog(LOG_ERR, "Gain mask: m0 %f m1 %f m2 %f m3 %f m5 %f",
+			config->gain_mask.m0,
+			config->gain_mask.m1,
+			config->gain_mask.m2,
+			config->gain_mask.m3,
+			config->gain_mask.m5);
+	syslog(LOG_ERR, "    m6 %f m7 %f m8 %f m9 %f",
+			config->gain_mask.m6,
+			config->gain_mask.m7,
+			config->gain_mask.m8,
+			config->gain_mask.m9);
+	syslog(LOG_ERR, "    gain_curve offset %f, gain_curve_slope %f",
+			config->gain_mask.gain_curve_offset,
+			config->gain_mask.gain_curve_slope);
+	syslog(LOG_ERR, "    temporal_masking_lf %f, temporal_masking_hf %f",
+			config->gain_mask.temporal_masking_lf,
+			config->gain_mask.temporal_masking_hf);
+	syslog(LOG_ERR, "    temporal_masking_lf_bands %zu",
+			config->gain_mask.temporal_masking_lf_bands);
+	syslog(LOG_ERR, "Echo audibility:");
+	syslog(LOG_ERR, "    low_render_limit %f, normal_render_limit %f",
+			config->echo_audibility.low_render_limit,
+			config->echo_audibility.normal_render_limit);
+	syslog(LOG_ERR, "    floor_power %f, audibility_threshold_lf %f",
+			config->echo_audibility.floor_power,
+			config->echo_audibility.audibility_threshold_lf);
+	syslog(LOG_ERR, "    audibility_threshold_mf %f",
+			config->echo_audibility.audibility_threshold_mf);
+	syslog(LOG_ERR, "    audibility_threshold_hf %f",
+			config->echo_audibility.audibility_threshold_hf);
+	syslog(LOG_ERR, "    use_stationary_properties %d",
+			config->echo_audibility.use_stationary_properties);
+	syslog(LOG_ERR, "Render levels:");
+	syslog(LOG_ERR, "    active_render_limit %f",
+			config->render_levels.active_render_limit);
+	syslog(LOG_ERR, "    poor_excitation_render_limit %f",
+			config->render_levels.poor_excitation_render_limit);
+	syslog(LOG_ERR, "    poor_excitation_render_limit_ds8 %f",
+			config->render_levels.poor_excitation_render_limit_ds8);
+	syslog(LOG_ERR, "Echo removal control:");
+	syslog(LOG_ERR, "    gain rampup:");
+	syslog(LOG_ERR, "        initial_gain %f, first_non_zero_gain %f",
+			config->echo_removal_control.gain_rampup.initial_gain,
+			config->echo_removal_control.gain_rampup.first_non_zero_gain);
+	syslog(LOG_ERR, "        non_zero_gain_blocks %d, full_gain_blocks %d",
+			config->echo_removal_control.gain_rampup.non_zero_gain_blocks,
+			config->echo_removal_control.gain_rampup.full_gain_blocks);
+	syslog(LOG_ERR, "    has_clock_drift %d",
+			config->echo_removal_control.has_clock_drift);
+	syslog(LOG_ERR, "    linear_and_stable_echo_path %d",
+			config->echo_removal_control.linear_and_stable_echo_path);
+	syslog(LOG_ERR, "Echo model:");
+	syslog(LOG_ERR, "    noise_floor_hold %zu, min_noise_floor_power %f",
+			config->echo_model.noise_floor_hold,
+			config->echo_model.min_noise_floor_power);
+	syslog(LOG_ERR, "    stationary_gate_slope %f, noise_gate_power %f",
+			config->echo_model.stationary_gate_slope,
+			config->echo_model.noise_gate_power);
+	syslog(LOG_ERR, "    noise_gate_slope %f, render_pre_window_size %zu",
+			config->echo_model.noise_gate_slope,
+			config->echo_model.render_pre_window_size);
+	syslog(LOG_ERR, "    render_post_window_size %zu nonlinear_hold %f",
+			config->echo_model.render_post_window_size,
+			config->echo_model.nonlinear_hold);
+	syslog(LOG_ERR, "    render_pre_window_size_init %u, "
+			"render_post_window_size_init %u",
+			config->echo_model.render_pre_window_size_init,
+			config->echo_model.render_post_window_size_init);
+	syslog(LOG_ERR, "    nonlinear_release %f",
+			config->echo_model.nonlinear_release);
+	syslog(LOG_ERR, "Suppressor:");
+	syslog(LOG_ERR, "    nearend_average_blocks %u",
+			config->suppressor.nearend_average_blocks);
+	syslog(LOG_ERR, "    Normal tuning, mask_lf %f %f %f",
+			config->suppressor.normal_tuning.mask_lf.enr_transparent,
+			config->suppressor.normal_tuning.mask_lf.enr_suppress,
+			config->suppressor.normal_tuning.mask_lf.emr_transparent);
+	syslog(LOG_ERR, "                   mask_hf %f %f %f",
+			config->suppressor.normal_tuning.mask_hf.enr_transparent,
+			config->suppressor.normal_tuning.mask_hf.enr_suppress,
+			config->suppressor.normal_tuning.mask_hf.emr_transparent);
+	syslog(LOG_ERR, "                   max_inc_factor %f max_dec_factor_lf %f",
+			config->suppressor.normal_tuning.max_inc_factor,
+			config->suppressor.normal_tuning.max_dec_factor_lf);
+	syslog(LOG_ERR, "    Nearend tuning, mask_lf %f %f %f",
+			config->suppressor.nearend_tuning.mask_lf.enr_transparent,
+			config->suppressor.nearend_tuning.mask_lf.enr_suppress,
+			config->suppressor.nearend_tuning.mask_lf.emr_transparent);
+	syslog(LOG_ERR, "                   mask_hf %f %f %f",
+			config->suppressor.nearend_tuning.mask_hf.enr_transparent,
+			config->suppressor.nearend_tuning.mask_hf.enr_suppress,
+			config->suppressor.nearend_tuning.mask_hf.emr_transparent);
+	syslog(LOG_ERR, "                   max_inc_factor %f max_dec_factor_lf %f",
+			config->suppressor.nearend_tuning.max_inc_factor,
+			config->suppressor.nearend_tuning.max_dec_factor_lf);
+	syslog(LOG_ERR, "    Dominant nearend detection:");
+	syslog(LOG_ERR, "        enr_threshold %f",
+			config->suppressor.dominant_nearend_detection.enr_threshold);
+	syslog(LOG_ERR, "        snr_threshold %f",
+			config->suppressor.dominant_nearend_detection.snr_threshold);
+	syslog(LOG_ERR, "        hold_duration %d",
+			config->suppressor.dominant_nearend_detection.hold_duration);
+	syslog(LOG_ERR, "        trigger_threshold %d",
+			config->suppressor.dominant_nearend_detection.trigger_threshold);
+	syslog(LOG_ERR, "    High bands suppression:");
+	syslog(LOG_ERR, "        enr_threshold %f max_gain_during_echo %f",
+			config->suppressor.high_bands_suppression.enr_threshold,
+			config->suppressor.high_bands_suppression.max_gain_during_echo);
+	syslog(LOG_ERR, "    floor_first_increase %f, enforce_transparent %d",
+			config->suppressor.floor_first_increase,
+			config->suppressor.enforce_transparent);
+	syslog(LOG_ERR, "    enforce_empty_higher_bands %f",
+			config->suppressor.enforce_empty_higher_bands);
+}
diff --git a/cras/src/server/config/aec_config.h b/cras/src/server/config/aec_config.h
new file mode 100644
index 0000000..015ca76
--- /dev/null
+++ b/cras/src/server/config/aec_config.h
@@ -0,0 +1,318 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AEC_CONFIG_H_
+#define AEC_CONFIG_H_
+
+#include <webrtc-apm/webrtc_apm.h>
+
+#define AEC_DELAY_DEFAULT_DELAY "delay:default_delay"
+#define AEC_DELAY_DEFAULT_DELAY_VALUE 5
+#define AEC_DELAY_DOWN_SAMPLING_FACTOR "delay:down_sampling_factor"
+#define AEC_DELAY_DOWN_SAMPLING_FACTOR_VALUE 4
+#define AEC_DELAY_NUM_FILTERS "delay:num_filters"
+#define AEC_DELAY_NUM_FILTERS_VALUE 6
+#define AEC_DELAY_API_CALL_JITTER_BLOCKS "delay:api_call_jitter_blocks"
+#define AEC_DELAY_API_CALL_JITTER_BLOCKS_VALUE 26
+#define AEC_DELAY_MIN_ECHO_PATH_DELAY_BLOCKS "delay:min_echo_path_delay_blocks"
+#define AEC_DELAY_MIN_ECHO_PATH_DELAY_BLOCKS_VALUE 0
+#define AEC_DELAY_DELAY_HEADROOM_BLOCKS "delay:delay_headroom_blocks"
+#define AEC_DELAY_DELAY_HEADROOM_BLOCKS_VALUE 2
+#define AEC_DELAY_HYSTERESIS_LIMIT_1_BLOCKS "delay:hysteresis_limit_1_blocks"
+#define AEC_DELAY_HYSTERESIS_LIMIT_1_BLOCKS_VALUE 1
+#define AEC_DELAY_HYSTERESIS_LIMIT_2_BLOCKS "delay:hysteresis_limit_2_blocks"
+#define AEC_DELAY_HYSTERESIS_LIMIT_2_BLOCKS_VALUE 1
+#define AEC_DELAY_SKEW_HYSTERESIS_BLOCKS "delay:skew_hysteresis_blocks"
+#define AEC_DELAY_SKEW_HYSTERESIS_BLOCKS_VALUE 3
+#define AEC_DELAY_FIXED_CAPTURE_DELAY_SAMPLES \
+	"delay:fixed_capture_delay_samples"
+#define AEC_DELAY_FIXED_CAPTURE_DELAY_SAMPLES_VALUE 0
+
+// Filter Main configuration
+#define AEC_FILTER_MAIN_LENGTH_BLOCKS "filter.main:length_blocks"
+#define AEC_FILTER_MAIN_LENGTH_BLOCKS_VALUE 13
+#define AEC_FILTER_MAIN_LEAKAGE_CONVERGED "filter.main:leakage_converged"
+#define AEC_FILTER_MAIN_LEAKAGE_CONVERGED_VALUE 0.00005f
+#define AEC_FILTER_MAIN_LEAKAGE_DIVERGED "filter.main:leakage_diverged"
+#define AEC_FILTER_MAIN_LEAKAGE_DIVERGED_VALUE 0.01f
+#define AEC_FILTER_MAIN_ERROR_FLOOR "filter.main:error_floor"
+#define AEC_FILTER_MAIN_ERROR_FLOOR_VALUE 0.1f
+#define AEC_FILTER_MAIN_NOISE_GATE "filter.main:noise_gate"
+#define AEC_FILTER_MAIN_NOISE_GATE_VALUE 20075344.f
+
+// Filter Shadow configuration
+#define AEC_FILTER_SHADOW_LENGTH_BLOCKS "filter.shadow:length_blocks"
+#define AEC_FILTER_SHADOW_LENGTH_BLOCKS_VALUE 13
+#define AEC_FILTER_SHADOW_RATE "filter.shadow:rate"
+#define AEC_FILTER_SHADOW_RATE_VALUE 0.7f
+#define AEC_FILTER_SHADOW_NOISE_GATE "filter.shadow:noise_gate"
+#define AEC_FILTER_SHADOW_NOISE_GATE_VALUE 20075344.f
+
+// Filter Main initial configuration
+#define AEC_FILTER_MAIN_INIT_LENGTH_BLOCKS "filter.main_initial:length_blocks"
+#define AEC_FILTER_MAIN_INIT_LENGTH_BLOCKS_VALUE 12
+#define AEC_FILTER_MAIN_INIT_LEAKAGE_CONVERGED \
+	"filter.main_initial:leakage_converged"
+#define AEC_FILTER_MAIN_INIT_LEAKAGE_CONVERGED_VALUE 0.005f
+#define AEC_FILTER_MAIN_INIT_LEAKAGE_DIVERGED \
+	"filter.main_initial:leakage_diverged"
+#define AEC_FILTER_MAIN_INIT_LEAKAGE_DIVERGED_VALUE 0.5f
+#define AEC_FILTER_MAIN_INIT_ERROR_FLOOR "filter.main_initial:error_floor"
+#define AEC_FILTER_MAIN_INIT_ERROR_FLOOR_VALUE 0.001f
+#define AEC_FILTER_MAIN_INIT_NOISE_GATE "filter.main_initial:noise_gate"
+#define AEC_FILTER_MAIN_INIT_NOISE_GATE_VALUE 20075344.f
+
+// Filter Shadow initial configuration
+#define AEC_FILTER_SHADOW_INIT_LENGTH_BLOCKS \
+	"filter.shadow_initial:length_blocks"
+#define AEC_FILTER_SHADOW_INIT_LENGTH_BLOCKS_VALUE 12
+#define AEC_FILTER_SHADOW_INIT_RATE "filter.shadow_initial:rate"
+#define AEC_FILTER_SHADOW_INIT_RATE_VALUE 0.9f
+#define AEC_FILTER_SHADOW_INIT_NOISE_GATE "filter.shadow_initial:noise_gate"
+#define AEC_FILTER_SHADOW_INIT_NOISE_GATE_VALUE 20075344.f
+
+#define AEC_FILTER_CONFIG_CHANGE_DURATION_BLOCKS \
+	"filter:config_change_duration_blocks"
+#define AEC_FILTER_CONFIG_CHANGE_DURATION_BLOCKS_VALUE 250
+#define AEC_FILTER_INITIAL_STATE_SECONDS "filter:initial_state_seconds"
+#define AEC_FILTER_INITIAL_STATE_SECONDS_VALUE 2.5f
+#define AEC_FILTER_CONSERVATIVE_INITIAL_PHASE \
+	"filter:conservative_initial_phase"
+#define AEC_FILTER_CONSERVATIVE_INITIAL_PHASE_VALUE 0
+#define AEC_FILTER_ENABLE_SHADOW_FILTER_OUTPUT_USAGE \
+	"filter:enable_shadow_filter_output_usage"
+#define AEC_FILTER_ENABLE_SHADOW_FILTER_OUTPUT_USAGE_VALUE 1
+
+// Erle
+#define AEC_ERLE_MIN "erle:min"
+#define AEC_ERLE_MIN_VALUE 1.f
+#define AEC_ERLE_MAX_L "erle:max_l"
+#define AEC_ERLE_MAX_L_VALUE 4.f
+#define AEC_ERLE_MAX_H "erle:max_h"
+#define AEC_ERLE_MAX_H_VALUE 1.5f
+#define AEC_ERLE_ONSET_DETECTION "erle:onset_detection"
+#define AEC_ERLE_ONSET_DETECTION_VALUE 1
+
+// EpStrength
+#define AEC_EP_STRENGTH_LF "ep_strength:lf"
+#define AEC_EP_STRENGTH_LF_VALUE 1.f
+#define AEC_EP_STRENGTH_MF "ep_strength:mf"
+#define AEC_EP_STRENGTH_MF_VALUE 1.f
+#define AEC_EP_STRENGTH_HF "ep_strength:hf"
+#define AEC_EP_STRENGTH_HF_VALUE 1.f
+#define AEC_EP_STRENGTH_DEFAULT_LEN "ep_strength:default_len"
+#define AEC_EP_STRENGTH_DEFAULT_LEN_VALUE 0.88f
+#define AEC_EP_STRENGTH_REVERB_BASED_ON_RENDER \
+	"ep_strength:reverb_based_on_render"
+#define AEC_EP_STRENGTH_REVERB_BASED_ON_RENDER_VALUE 1
+#define AEC_EP_STRENGTH_ECHO_CAN_SATURATE "ep_strength:echo_can_saturate"
+#define AEC_EP_STRENGTH_ECHO_CAN_SATURATE_VALUE 1
+#define AEC_EP_STRENGTH_BOUNDED_ERL "ep_strength:bounded_erl"
+#define AEC_EP_STRENGTH_BOUNDED_ERL_VALUE 0
+
+// Gain mask
+#define AEC_GAIN_MASK_M0 "gain_mask:m0"
+#define AEC_GAIN_MASK_M0_VALUE 0.1f
+#define AEC_GAIN_MASK_M1 "gain_mask:m1"
+#define AEC_GAIN_MASK_M1_VALUE 0.01f
+#define AEC_GAIN_MASK_M2 "gain_mask:m2"
+#define AEC_GAIN_MASK_M2_VALUE 0.0001f
+#define AEC_GAIN_MASK_M3 "gain_mask:m3"
+#define AEC_GAIN_MASK_M3_VALUE 0.01f
+// m4 was removed intentionally.
+// https://webrtc-review.googlesource.com/c/src/+/70421
+#define AEC_GAIN_MASK_M5 "gain_mask:m5"
+#define AEC_GAIN_MASK_M5_VALUE 0.01f
+#define AEC_GAIN_MASK_M6 "gain_mask:m6"
+#define AEC_GAIN_MASK_M6_VALUE 0.0001f
+#define AEC_GAIN_MASK_M7 "gain_mask:m7"
+#define AEC_GAIN_MASK_M7_VALUE 0.01f
+#define AEC_GAIN_MASK_M8 "gain_mask:m8"
+#define AEC_GAIN_MASK_M8_VALUE 0.0001f
+#define AEC_GAIN_MASK_M9 "gain_mask:m9"
+#define AEC_GAIN_MASK_M9_VALUE 0.1f
+#define AEC_GAIN_MASK_GAIN_CURVE_OFFSET "gain_mask:gain_curve_offset"
+#define AEC_GAIN_MASK_GAIN_CURVE_OFFSET_VALUE 1.45f
+#define AEC_GAIN_MASK_GAIN_CURVE_SLOPE "gain_mask:gain_curve_slope"
+#define AEC_GAIN_MASK_GAIN_CURVE_SLOPE_VALUE 5.f
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_LF "gain_mask:temporal_masking_lf"
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_LF_VALUE 0.9f
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_HF "gain_mask:temporal_masking_hf"
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_HF_VALUE 0.6f
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_LF_BANDS \
+	"gain_mask:temporal_masking_lf_bands"
+#define AEC_GAIN_MASK_TEMPORAL_MASKING_LF_BANDS_VALUE 3
+
+#define AEC_ECHO_AUDIBILITY_LOW_RENDER_LIMIT "echo_audibility:low_render_limit"
+#define AEC_ECHO_AUDIBILITY_LOW_RENDER_LIMIT_VALUE 4 * 64.f
+#define AEC_ECHO_AUDIBILITY_NORMAL_RENDER_LIMIT \
+	"echo_audibility:normal_render_limit"
+#define AEC_ECHO_AUDIBILITY_NORMAL_RENDER_LIMIT_VALUE 64.f
+#define AEC_ECHO_AUDIBILITY_FLOOR_POWER "echo_audibility:floor_power"
+#define AEC_ECHO_AUDIBILITY_FLOOR_POWER_VALUE 2 * 64.f
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_LF \
+	"echo_audibility:audibility_threshold_lf"
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_LF_VALUE 10
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_MF \
+	"echo_audibility:audibility_threshold_mf"
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_MF_VALUE 10
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_HF \
+	"echo_audibility:audibility_threshold_hf"
+#define AEC_ECHO_AUDIBILITY_AUDIBILITY_THRESHOLD_HF_VALUE 10
+#define AEC_ECHO_AUDIBILITY_USE_STATIONARY_PROPERTIES \
+	"echo_audibility:use_stationary_properties"
+#define AEC_ECHO_AUDIBILITY_USE_STATIONARY_PROPERTIES_VALUE 1
+
+// Rendering levels
+#define AEC_RENDER_LEVELS_ACTIVE_RENDER_LIMIT \
+	"render_levels:active_render_limit"
+#define AEC_RENDER_LEVELS_ACTIVE_RENDER_LIMIT_VALUE 100.f
+#define AEC_RENDER_LEVELS_POOR_EXCITATION_RENDER_LIMIT \
+	"render_levels:poor_excitation_render_limit"
+#define AEC_RENDER_LEVELS_POOR_EXCITATION_RENDER_LIMIT_VALUE 150.f
+#define AEC_RENDER_LEVELS_POOR_EXCITATION_RENDER_LIMIT_DS8 \
+	"render_levels:poor_excitation_render_limit_ds8"
+#define AEC_RENDER_LEVELS_POOR_EXCITATION_RENDER_LIMIT_DS8_VALUE 20.f
+
+// Echo removal controls
+#define AEC_ECHO_REMOVAL_CTL_INITIAL_GAIN "echo_removal_control:initial_gain"
+#define AEC_ECHO_REMOVAL_CTL_INITIAL_GAIN_VALUE 0.0f
+#define AEC_ECHO_REMOVAL_CTL_FIRST_NON_ZERO_GAIN \
+	"echo_removal_control:first_non_zero_gain"
+#define AEC_ECHO_REMOVAL_CTL_FIRST_NON_ZERO_GAIN_VALUE 0.001f
+#define AEC_ECHO_REMOVAL_CTL_NON_ZERO_GAIN_BLOCKS \
+	"echo_removal_control:non_zero_gain_blocks"
+#define AEC_ECHO_REMOVAL_CTL_NON_ZERO_GAIN_BLOCKS_VALUE 187
+#define AEC_ECHO_REMOVAL_CTL_FULL_GAIN_BLOCKS \
+	"echo_removal_control:full_gain_blocks"
+#define AEC_ECHO_REMOVAL_CTL_FULL_GAIN_BLOCKS_VALUE 312
+#define AEC_ECHO_REMOVAL_CTL_HAS_CLOCK_DRIFT \
+	"echo_removal_control:has_clock_drift"
+#define AEC_ECHO_REMOVAL_CTL_HAS_CLOCK_DRIFT_VALUE 0
+#define AEC_ECHO_REMOVAL_CTL_LINEAR_AND_STABLE_ECHO_PATH \
+	"echo_removal_control:linear_and_stable_echo_path"
+#define AEC_ECHO_REMOVAL_CTL_LINEAR_AND_STABLE_ECHO_PATH_VALUE 0
+
+// EchoModel
+#define AEC_ECHO_MODEL_NOISE_FLOOR_HOLD "echo_model:noise_floor_hold"
+#define AEC_ECHO_MODEL_NOISE_FLOOR_HOLD_VALUE 50
+#define AEC_ECHO_MODEL_MIN_NOISE_FLOOR_POWER "echo_model:min_noise_floor_power"
+#define AEC_ECHO_MODEL_MIN_NOISE_FLOOR_POWER_VALUE 1638400.f
+#define AEC_ECHO_MODEL_STATIONARY_GATE_SLOPE "echo_model:stationary_gate_slope"
+#define AEC_ECHO_MODEL_STATIONARY_GATE_SLOPE_VALUE 10.f
+#define AEC_ECHO_MODEL_NOISE_GATE_POWER "echo_model:noise_gate_power"
+#define AEC_ECHO_MODEL_NOISE_GATE_POWER_VALUE 27509.42f
+#define AEC_ECHO_MODEL_NOISE_GATE_SLOPE "echo_model:noise_gate_slope"
+#define AEC_ECHO_MODEL_NOISE_GATE_SLOPE_VALUE 0.3f
+#define AEC_ECHO_MODEL_RENDER_PRE_WINDOW_SIZE \
+	"echo_model:render_pre_window_size"
+#define AEC_ECHO_MODEL_RENDER_PRE_WINDOW_SIZE_VALUE 1
+#define AEC_ECHO_MODEL_RENDER_POST_WINDOW_SIZE \
+	"echo_model:render_post_window_size"
+#define AEC_ECHO_MODEL_RENDER_POST_WINDOW_SIZE_VALUE 1
+#define AEC_ECHO_MODEL_RENDER_PRE_WINDOW_SIZE_INIT \
+	"echo_model:render_pre_window_size_init"
+#define AEC_ECHO_MODEL_RENDER_PRE_WINDOW_SIZE_INIT_VALUE 10
+#define AEC_ECHO_MODEL_RENDER_POST_WINDOW_SIZE_INIT \
+	"echo_model:render_post_window_size_init"
+#define AEC_ECHO_MODEL_RENDER_POST_WINDOW_SIZE_INIT_VALUE 10
+#define AEC_ECHO_MODEL_NONLINEAR_HOLD "echo_model:nonlinear_hold"
+#define AEC_ECHO_MODEL_NONLINEAR_HOLD_VALUE 1
+#define AEC_ECHO_MODEL_NONLINEAR_RELEASE "echo_model:nonlinear_release"
+#define AEC_ECHO_MODEL_NONLINEAR_RELEASE_VALUE 0.001f
+
+#define AEC_SUPPRESSOR_NEAREND_AVERAGE_BLOCKS \
+	"suppressor:nearend_average_blocks"
+#define AEC_SUPPRESSOR_NEAREND_AVERAGE_BLOCKS_VALUE 4
+
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_ENR_TRANSPARENT \
+	"suppressor.normal_tuning:mask_lf_enr_transparent"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_ENR_TRANSPARENT_VALUE .2f
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_ENR_SUPPRESS \
+	"suppressor.normal_tuning:mask_lf_enr_suppress"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_ENR_SUPPRESS_VALUE .3f
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_EMR_TRANSPARENT \
+	"suppressor.normal_tuning:mask_lf_emr_transparent"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_LF_EMR_TRANSPARENT_VALUE .3f
+
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_ENR_TRANSPARENT \
+	"suppressor.normal_tuning:mask_hf_enr_transparent"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_ENR_TRANSPARENT_VALUE .07f
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_ENR_SUPPRESS \
+	"suppressor.normal_tuning:mask_hf_enr_suppress"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_ENR_SUPPRESS_VALUE .1f
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_EMR_TRANSPARENT \
+	"suppressor.normal_tuning:mask_hf_emr_transparent"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MASK_HF_EMR_TRANSPARENT_VALUE .3f
+
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MAX_INC_FACTOR \
+	"suppressor.normal_tuning:max_inc_factor"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MAX_INC_FACTOR_VALUE 2.0f
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MAX_DEC_FACTOR_LF \
+	"suppressor.normal_tuning:max_dec_factor_lf"
+#define AEC_SUPPRESSOR_NORMAL_TUNING_MAX_DEC_FACTOR_LF_VALUE 0.25f
+
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_ENR_TRANSPARENT \
+	"suppressor.nearend_tuning:mask_lf_enr_transparent"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_ENR_TRANSPARENT_VALUE .2f
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_ENR_SUPPRESS \
+	"suppressor.nearend_tuning:mask_lf_enr_suppress"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_ENR_SUPPRESS_VALUE .3f
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_EMR_TRANSPARENT \
+	"suppressor.nearend_tuning:mask_lf_emr_transparent"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_LF_EMR_TRANSPARENT_VALUE .3f
+
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_ENR_TRANSPARENT \
+	"suppressor.nearend_tuning:mask_hf_enr_transparent"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_ENR_TRANSPARENT_VALUE .07f
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_ENR_SUPPRESS \
+	"suppressor.nearend_tuning:mask_hf_enr_suppress"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_ENR_SUPPRESS_VALUE .1f
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_EMR_TRANSPARENT \
+	"suppressor.nearend_tuning:mask_hf_emr_transparent"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MASK_HF_EMR_TRANSPARENT_VALUE .3f
+
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MAX_INC_FACTOR \
+	"suppressor.nearend_tuning:max_inc_factor"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MAX_INC_FACTOR_VALUE 2.0f
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MAX_DEC_FACTOR_LF \
+	"suppressor.nearend_tuning:max_dec_factor_lf"
+#define AEC_SUPPRESSOR_NEAREND_TUNING_MAX_DEC_FACTOR_LF_VALUE 0.25f
+
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_ENR_THRESHOLD \
+	"suppressor.dominant_nearend_detection:enr_threshold"
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_ENR_THRESHOLD_VALUE 10.f
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_SNR_THRESHOLD \
+	"suppressor.dominant_nearend_detection:snr_threshold"
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_SNR_THRESHOLD_VALUE 10.f
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_HOLD_DURATION \
+	"suppressor.dominant_nearend_detection:hold_duration"
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_HOLD_DURATION_VALUE 25
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_TRIGGER_THRESHOLD \
+	"suppressor.dominant_nearend_detection:trigger_threshold"
+#define AEC_SUPPRESSOR_DOMINANT_NEAREND_DETECTION_TRIGGER_THRESHOLD_VALUE 15
+
+#define AEC_SUPPRESSOR_HIGH_BANDS_SUPPRESSION_ENR_THRESHOLD \
+	"suppressor.high_bands_suppression:enr_threshold"
+#define AEC_SUPPRESSOR_HIGH_BANDS_SUPPRESSION_ENR_THRESHOLD_VALUE 1.f
+#define AEC_SUPPRESSOR_HIGH_BANDS_SUPPRESSION_MAX_GAIN_DURING_ECHO \
+	"suppressor.high_bands_suppression:max_gain_during_echo"
+#define AEC_SUPPRESSOR_HIGH_BANDS_SUPPRESSION_MAX_GAIN_DURING_ECHO_VALUE 1.f
+
+#define AEC_SUPPRESSOR_FLOOR_FIRST_INCREASE "suppressor:floor_first_increase"
+#define AEC_SUPPRESSOR_FLOOR_FIRST_INCREASE_VALUE 0.00001f
+#define AEC_SUPPRESSOR_ENFORCE_TRANSPARENT "suppressor:enforce_transparent"
+#define AEC_SUPPRESSOR_ENFORCE_TRANSPARENT_VALUE 0
+#define AEC_SUPPRESSOR_ENFORCE_EMPTY_HIGHER_BANDS \
+	"suppressor:enforce_empty_higher_bands"
+#define AEC_SUPPRESSOR_ENFORCE_EMPTY_HIGHER_BANDS_VALUE 0
+
+/* Gets the aec config from given config directory. */
+struct aec_config *aec_config_get(const char *device_config_dir);
+
+/* Prints the config content to syslog. */
+void aec_config_dump(struct aec_config *config);
+
+#endif /* AEC_CONFIG_H_ */
diff --git a/cras/src/server/config/apm_config.c b/cras/src/server/config/apm_config.c
new file mode 100644
index 0000000..ae5df69
--- /dev/null
+++ b/cras/src/server/config/apm_config.c
@@ -0,0 +1,94 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "apm_config.h"
+#include "iniparser_wrapper.h"
+
+static const unsigned int MAX_INI_NAME_LEN = 63;
+
+#define APM_CONFIG_NAME "apm.ini"
+
+#define APM_GET_INT(ini, key)	\
+	iniparser_getint(	\
+		ini, key,	\
+		key ## _VALUE)
+#define APM_GET_FLOAT(ini, key)	\
+	iniparser_getdouble(	\
+		ini, key,	\
+		key ## _VALUE)
+
+struct apm_config *apm_config_get(const char *device_config_dir)
+{
+	struct apm_config *config;
+	char ini_name[MAX_INI_NAME_LEN + 1];
+	dictionary *ini;
+
+	snprintf(ini_name, MAX_INI_NAME_LEN, "%s/%s",
+		 device_config_dir, APM_CONFIG_NAME);
+	ini_name[MAX_INI_NAME_LEN] = '\0';
+
+	ini = iniparser_load_wrapper(ini_name);
+	if (ini == NULL) {
+		syslog(LOG_DEBUG, "No ini file %s", ini_name);
+		return NULL;
+	}
+
+	config = (struct apm_config *)calloc(1, sizeof(*config));
+	config->residual_echo_detector_enabled =
+			APM_GET_INT(ini, APM_RESIDUAL_ECHO_DETECTOR_ENABLED);
+	config->high_pass_filter_enabled =
+			APM_GET_INT(ini, APM_HIGH_PASS_FILTER_ENABLED);
+	config->pre_amplifier_enabled =
+			APM_GET_INT(ini, APM_PRE_AMPLIFIER_ENABLED);
+	config->pre_amplifier_fixed_gain_factor =
+			APM_GET_FLOAT(ini, APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR);
+	config->gain_controller2_enabled =
+			APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ENABLED);
+	config->gain_controller2_fixed_gain_db =
+			APM_GET_FLOAT(ini, APM_GAIN_CONTROLLER2_FIXED_GAIN_DB);
+	config->gain_controller2_adaptive_digital_mode =
+			APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ADAPTIVE_DIGITAL_MODE);
+	config->gain_control_compression_gain_db =
+			APM_GET_INT(ini, APM_GAIN_CONTROL_COMPRESSION_GAIN_DB);
+	config->agc_mode = (enum gain_control_mode)
+			APM_GET_INT(ini, APM_GAIN_CONTROL_MODE);
+	config->gain_control_enabled =
+			APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLED);
+	config->ns_level = (enum noise_suppression_level)
+			APM_GET_INT(ini, APM_NOISE_SUPPRESSION_LEVEL);
+	config->noise_suppression_enabled =
+			APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ENABLED);
+
+	return config;
+}
+
+void apm_config_dump(struct apm_config *config)
+{
+	syslog(LOG_ERR, "---- apm config dump ----");
+	syslog(LOG_ERR, "residual_echo_detector_enabled %u",
+		config->residual_echo_detector_enabled);
+	syslog(LOG_ERR, "high_pass_filter_enabled %u",
+		config->high_pass_filter_enabled);
+	syslog(LOG_ERR, "pre_amplifier_enabled %u",
+		config->pre_amplifier_enabled);
+	syslog(LOG_ERR, "pre_amplifier_fixed_gain_factor %f",
+		config->pre_amplifier_fixed_gain_factor);
+	syslog(LOG_ERR, "gain_controller2_enabled %u",
+		config->gain_controller2_enabled);
+	syslog(LOG_ERR, "gain_controller2_fixed_gain_db %f",
+		config->gain_controller2_fixed_gain_db);
+	syslog(LOG_ERR, "gain_controller2_adaptive_digital_mode %d",
+		config->gain_controller2_adaptive_digital_mode);
+	syslog(LOG_ERR, "gain_control_compression_gain_db %u",
+		config->gain_control_compression_gain_db);
+	syslog(LOG_ERR, "gain_control_mode %u", config->agc_mode);
+	syslog(LOG_ERR, "gain_control_enabled %u",
+		config->gain_control_enabled);
+	syslog(LOG_ERR, "noise_suppression_level %u", config->ns_level);
+	syslog(LOG_ERR, "noise_suppression_enabled %u",
+		config->noise_suppression_enabled);
+}
diff --git a/cras/src/server/config/apm_config.h b/cras/src/server/config/apm_config.h
new file mode 100644
index 0000000..b0e39be
--- /dev/null
+++ b/cras/src/server/config/apm_config.h
@@ -0,0 +1,45 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef APM_CONFIG_H_
+#define APM_CONFIG_H_
+
+#include <webrtc-apm/webrtc_apm.h>
+#include <webrtc-apm/webrtc_apm.h>
+
+#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED "apm:residual_echo_detector_enabled"
+#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED_VALUE 1
+#define APM_HIGH_PASS_FILTER_ENABLED "apm:high_pass_filter_enabled"
+#define APM_HIGH_PASS_FILTER_ENABLED_VALUE 0
+#define APM_PRE_AMPLIFIER_ENABLED "apm:pre_amplifier_enabled"
+#define APM_PRE_AMPLIFIER_ENABLED_VALUE 0
+#define APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR "apm:pre_amplifier_fixed_gain_factor"
+#define APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR_VALUE 1.f
+#define APM_GAIN_CONTROLLER2_ENABLED "apm:gain_controller2_enabled"
+#define APM_GAIN_CONTROLLER2_ENABLED_VALUE 0
+#define APM_GAIN_CONTROLLER2_FIXED_GAIN_DB "apm:gain_controller2_fixed_gain_db"
+#define APM_GAIN_CONTROLLER2_FIXED_GAIN_DB_VALUE 0.f
+#define APM_GAIN_CONTROLLER2_ADAPTIVE_DIGITAL_MODE "apm:gain_controller2_adaptive_digital_mode"
+#define APM_GAIN_CONTROLLER2_ADAPTIVE_DIGITAL_MODE_VALUE 1
+#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB "apm:gain_control_compression_gain_db"
+#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB_VALUE 9
+/* 0: adaptive analog, 1: adaptive digital, 2: fixed digital */
+#define APM_GAIN_CONTROL_MODE "apm:gain_control_mode"
+#define APM_GAIN_CONTROL_MODE_VALUE 0
+#define APM_GAIN_CONTROL_ENABLED "apm:gain_control_enabled"
+#define APM_GAIN_CONTROL_ENABLED_VALUE 0
+/* 0: low, 1: moderate, 2: high, 3: very high*/
+#define APM_NOISE_SUPPRESSION_LEVEL "apm:noise_suppression_level"
+#define APM_NOISE_SUPPRESSION_LEVEL_VALUE 0
+#define APM_NOISE_SUPPRESSION_ENABLED "apm:noise_suppression_enabled"
+#define APM_NOISE_SUPPRESSION_ENABLED_VALUE 0
+
+/* Gets apm config from given config directory. */
+struct apm_config *apm_config_get(const char *device_config_dir);
+
+/* Prints config content to syslog. */
+void apm_config_dump(struct apm_config *config);
+
+#endif /* APM_CONFIG_H_ */
diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c
new file mode 100644
index 0000000..a920b63
--- /dev/null
+++ b/cras/src/server/config/cras_board_config.c
@@ -0,0 +1,56 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "cras_board_config.h"
+#include "iniparser_wrapper.h"
+
+/* Allocate 63 chars + 1 for null where declared. */
+static const unsigned int MAX_INI_NAME_LEN = 63;
+static const unsigned int MAX_KEY_LEN = 63;
+static const int32_t DEFAULT_OUTPUT_BUFFER_SIZE = 512;
+static const int32_t AEC_SUPPORTED_DEFAULT = 0;
+
+#define CONFIG_NAME "board.ini"
+#define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size"
+#define AEC_SUPPORTED_INI_KEY "processing:aec_supported"
+
+
+void cras_board_config_get(const char *config_path,
+		struct cras_board_config *board_config)
+{
+	char ini_name[MAX_INI_NAME_LEN + 1];
+	char ini_key[MAX_KEY_LEN + 1];
+	dictionary *ini;
+
+	board_config->default_output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
+	board_config->aec_supported = AEC_SUPPORTED_DEFAULT;
+	if (config_path == NULL)
+		return;
+
+	snprintf(ini_name, MAX_INI_NAME_LEN, "%s/%s", config_path,
+		CONFIG_NAME);
+	ini_name[MAX_INI_NAME_LEN] = '\0';
+	ini = iniparser_load_wrapper(ini_name);
+	if (ini == NULL) {
+		syslog(LOG_DEBUG, "No ini file %s", ini_name);
+		return;
+	}
+
+	snprintf(ini_key, MAX_KEY_LEN, DEFAULT_OUTPUT_BUF_SIZE_INI_KEY);
+	ini_key[MAX_KEY_LEN] = 0;
+	board_config->default_output_buffer_size =
+		iniparser_getint(ini, ini_key, DEFAULT_OUTPUT_BUFFER_SIZE);
+
+	snprintf(ini_key, MAX_KEY_LEN, AEC_SUPPORTED_INI_KEY);
+	ini_key[MAX_KEY_LEN] = 0;
+	board_config->aec_supported =
+		iniparser_getint(ini, ini_key, AEC_SUPPORTED_DEFAULT);
+
+	iniparser_freedict(ini);
+	syslog(LOG_DEBUG, "Loaded ini file %s", ini_name);
+}
+
diff --git a/cras/src/server/config/cras_board_config.h b/cras/src/server/config/cras_board_config.h
new file mode 100644
index 0000000..f06f92b
--- /dev/null
+++ b/cras/src/server/config/cras_board_config.h
@@ -0,0 +1,24 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CRAS_BOARD_CONFIG_H_
+#define CRAS_BOARD_CONFIG_H_
+
+#include <stdint.h>
+
+struct cras_board_config {
+	int32_t default_output_buffer_size;
+	int32_t aec_supported;
+};
+
+/* Gets a configuration based on the config file specified.
+ * Args:
+ *    config_path - Path containing the config files.
+ *    board_config - The returned configs.
+ */
+void cras_board_config_get(const char *config_path,
+			   struct cras_board_config *board_config);
+
+#endif /* CRAS_BOARD_CONFIG_H_ */
diff --git a/cras/src/server/config/cras_card_config.c b/cras/src/server/config/cras_card_config.c
index ba39ce9..c6f5edd 100644
--- a/cras/src/server/config/cras_card_config.c
+++ b/cras/src/server/config/cras_card_config.c
@@ -3,11 +3,11 @@
  * found in the LICENSE file.
  */
 
-#include <iniparser.h>
 #include <syslog.h>
 
 #include "cras_util.h"
 #include "cras_volume_curve.h"
+#include "iniparser_wrapper.h"
 #include "utlist.h"
 
 /* Allocate 63 chars + 1 for null where declared. */
@@ -66,7 +66,7 @@
 
 	snprintf(ini_name, MAX_INI_NAME_LEN, "%s/%s", config_path, card_name);
 	ini_name[MAX_INI_NAME_LEN] = '\0';
-	ini = iniparser_load(ini_name);
+	ini = iniparser_load_wrapper(ini_name);
 	if (ini == NULL) {
 		syslog(LOG_DEBUG, "No ini file %s", ini_name);
 		return NULL;
diff --git a/cras/src/server/config/cras_device_blacklist.c b/cras/src/server/config/cras_device_blacklist.c
index 5706ea8..f3000a8 100644
--- a/cras/src/server/config/cras_device_blacklist.c
+++ b/cras/src/server/config/cras_device_blacklist.c
@@ -3,9 +3,8 @@
  * found in the LICENSE file.
  */
 
-#include <iniparser.h>
-
 #include "cras_device_blacklist.h"
+#include "iniparser_wrapper.h"
 #include "utlist.h"
 
 /* Allocate 63 chars + 1 for null where declared. */
@@ -33,7 +32,7 @@
 	snprintf(ini_name, MAX_INI_NAME_LEN, "%s/%s",
 		 config_path, "device_blacklist");
 	ini_name[MAX_INI_NAME_LEN] = '\0';
-	blacklist->ini = iniparser_load(ini_name);
+	blacklist->ini = iniparser_load_wrapper(ini_name);
 
 	return blacklist;
 }
diff --git a/cras/src/server/cras.c b/cras/src/server/cras.c
index 29806a8..cc213ab 100644
--- a/cras/src/server/cras.c
+++ b/cras/src/server/cras.c
@@ -3,13 +3,17 @@
  * found in the LICENSE file.
  */
 
+#define _GNU_SOURCE /* for asprintf */
 #include <getopt.h>
 #include <signal.h>
+#include <stdio.h>
 #include <syslog.h>
 
+#include "cras_apm_list.h"
 #include "cras_config.h"
 #include "cras_iodev_list.h"
 #include "cras_server.h"
+#include "cras_shm.h"
 #include "cras_system_state.h"
 #include "cras_dsp.h"
 
@@ -108,10 +112,29 @@
 
 	/* Initialize system. */
 	cras_server_init();
-	cras_system_state_init(device_config_dir);
+        char *shm_name;
+        if (asprintf(&shm_name, "/cras-%d", getpid()) < 0)
+		exit(-1);
+	int rw_shm_fd;
+	int ro_shm_fd;
+        struct cras_server_state *exp_state = (struct cras_server_state *)
+		cras_shm_setup(shm_name,
+			       sizeof(*exp_state),
+			       &rw_shm_fd,
+			       &ro_shm_fd);
+	if (!exp_state)
+		exit(-1);
+	cras_system_state_init(device_config_dir,
+			       shm_name,
+			       rw_shm_fd,
+			       ro_shm_fd,
+			       exp_state,
+			       sizeof(*exp_state));
+        free(shm_name);
 	if (internal_ucm_suffix)
 		cras_system_state_set_internal_ucm_suffix(internal_ucm_suffix);
 	cras_dsp_init(dsp_config);
+	cras_apm_list_init(device_config_dir);
 	cras_iodev_list_init();
 
 	/* Start the server. */
diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c
index a9551a1..ec8c345 100644
--- a/cras/src/server/cras_a2dp_iodev.c
+++ b/cras/src/server/cras_a2dp_iodev.c
@@ -8,6 +8,7 @@
 #include <sys/param.h>
 #include <linux/sockios.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <syslog.h>
 #include <time.h>
 
@@ -128,14 +129,14 @@
 	int estimate_queued_frames = bt_queued_frames(iodev, 0);
 	int local_queued_frames =
 			a2dp_queued_frames(&a2dpio->a2dp) +
-			buf_queued_bytes(a2dpio->pcm_buf) /
+			buf_queued(a2dpio->pcm_buf) /
 				cras_get_format_bytes(iodev->format);
 	clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
 	return MIN(iodev->buffer_size,
 		   MAX(estimate_queued_frames, local_queued_frames));
 }
 
-static int open_dev(struct cras_iodev *iodev)
+static int configure_dev(struct cras_iodev *iodev)
 {
 	struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
 	int sock_depth;
@@ -214,7 +215,7 @@
 	if (device)
 		cras_bt_device_cancel_suspend(device);
 	a2dp_drain(&a2dpio->a2dp);
-	byte_buffer_destroy(a2dpio->pcm_buf);
+	byte_buffer_destroy(&a2dpio->pcm_buf);
 	cras_iodev_free_format(iodev);
 	cras_iodev_free_audio_area(iodev);
 	return 0;
@@ -279,17 +280,17 @@
 		return -EINVAL;
 
 encode_more:
-	while (buf_queued_bytes(a2dpio->pcm_buf)) {
+	while (buf_queued(a2dpio->pcm_buf)) {
 		processed = a2dp_encode(
 				&a2dpio->a2dp,
 				buf_read_pointer(a2dpio->pcm_buf),
-				buf_readable_bytes(a2dpio->pcm_buf),
+				buf_readable(a2dpio->pcm_buf),
 				format_bytes,
 				cras_bt_transport_write_mtu(a2dpio->transport));
 		ATLOG(atlog, AUDIO_THREAD_A2DP_ENCODE,
 					    processed,
-					    buf_queued_bytes(a2dpio->pcm_buf),
-					    buf_readable_bytes(a2dpio->pcm_buf)
+					    buf_queued(a2dpio->pcm_buf),
+					    buf_readable(a2dpio->pcm_buf)
 					    );
 		if (processed == -ENOSPC || processed == 0)
 			break;
@@ -328,7 +329,7 @@
 	 * encode more. But avoid the case when PCM buffer level is too close
 	 * to min_buffer_level so that another A2DP write could causes underrun.
 	 */
-	queued_frames = buf_queued_bytes(a2dpio->pcm_buf) / format_bytes;
+	queued_frames = buf_queued(a2dpio->pcm_buf) / format_bytes;
 	if (written && (iodev->min_buffer_level + written < queued_frames))
 		goto encode_more;
 
@@ -362,7 +363,7 @@
 	if (iodev->direction != CRAS_STREAM_OUTPUT)
 		return 0;
 
-	*frames = MIN(*frames, buf_writable_bytes(a2dpio->pcm_buf) /
+	*frames = MIN(*frames, buf_writable(a2dpio->pcm_buf) /
 					format_bytes);
 	iodev->area->frames = *frames;
 	cras_audio_area_config_buf_pointers(
@@ -381,7 +382,7 @@
 	format_bytes = cras_get_format_bytes(iodev->format);
 	written_bytes = nwritten * format_bytes;
 
-	if (written_bytes > buf_writable_bytes(a2dpio->pcm_buf))
+	if (written_bytes > buf_writable(a2dpio->pcm_buf))
 		return -EINVAL;
 
 	buf_increment_write(a2dpio->pcm_buf, written_bytes);
@@ -437,6 +438,7 @@
 	}
 	free(a2dpio->base.supported_channel_counts);
 	free(a2dpio->base.supported_rates);
+	free(a2dpio->base.supported_formats);
 	destroy_a2dp(&a2dpio->a2dp);
 }
 
@@ -484,7 +486,7 @@
 			strlen(cras_bt_device_object_path(device)));
 	iodev->info.stable_id_new = iodev->info.stable_id;
 
-	iodev->open_dev = open_dev;
+	iodev->configure_dev = configure_dev;
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
 	iodev->get_buffer = get_buffer;
diff --git a/cras/src/server/cras_alsa_card.c b/cras/src/server/cras_alsa_card.c
index 16a6ef5..7f7a620 100644
--- a/cras/src/server/cras_alsa_card.c
+++ b/cras/src/server/cras_alsa_card.c
@@ -180,6 +180,8 @@
 						  CRAS_STREAM_OUTPUT);
 		if (!dev)
 			DL_DELETE(controls, control);
+		else
+			free(dev);
 	}
 	return controls;
 }
@@ -410,6 +412,41 @@
 	return rc;
 }
 
+static void configure_echo_reference_dev(struct cras_alsa_card *alsa_card)
+{
+	struct iodev_list_node *dev_node, *echo_ref_node;
+	const char *echo_ref_name;
+
+	if (!alsa_card->ucm)
+		return;
+
+	DL_FOREACH(alsa_card->iodevs, dev_node) {
+		if (!dev_node->iodev->nodes)
+			continue;
+
+		echo_ref_name = ucm_get_echo_reference_dev_name_for_dev(
+				alsa_card->ucm,
+				dev_node->iodev->nodes->name);
+		if (!echo_ref_name)
+			continue;
+		DL_FOREACH(alsa_card->iodevs, echo_ref_node) {
+			if (echo_ref_node->iodev->nodes == NULL)
+				continue;
+			if (!strcmp(echo_ref_name,
+				    echo_ref_node->iodev->nodes->name))
+				break;
+		}
+		if (echo_ref_node)
+			dev_node->iodev->echo_reference_dev =
+					echo_ref_node->iodev;
+		else
+			syslog(LOG_ERR,
+			       "Echo ref dev %s doesn't exist on card %s",
+			       echo_ref_name, alsa_card->name);
+		free((void *)echo_ref_name);
+	}
+}
+
 /*
  * Exported Interface.
  */
@@ -528,6 +565,8 @@
 	if (rc)
 		goto error_bail;
 
+	configure_echo_reference_dev(alsa_card);
+
 	n = alsa_card->hctl ?
 		snd_hctl_poll_descriptors_count(alsa_card->hctl) : 0;
 	if (n != 0 && card_has_hctl_jack(alsa_card)) {
diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c
index a8719c2..d2520e7 100644
--- a/cras/src/server/cras_alsa_helpers.c
+++ b/cras/src/server/cras_alsa_helpers.c
@@ -53,6 +53,7 @@
 	4,
 	2,
 	1,
+	8,
 	0
 };
 
@@ -304,7 +305,6 @@
 int cras_alsa_set_channel_map(snd_pcm_t *handle,
 			      struct cras_audio_format *fmt)
 {
-	int rc = 0;
 	size_t i, ch;
 	snd_pcm_chmap_query_t **chmaps;
 	snd_pcm_chmap_query_t *match;
@@ -315,14 +315,12 @@
 	chmaps = snd_pcm_query_chmaps(handle);
 	if (chmaps == NULL) {
 		syslog(LOG_WARNING, "No chmap queried! Skip chmap set");
-		rc = 0;
 		goto done;
 	}
 
 	match = cras_chmap_caps_best(handle, chmaps, fmt);
 	if (!match) {
 		syslog(LOG_ERR, "Unable to find the best channel map");
-		rc = -1;
 		goto done;
 	}
 
@@ -337,11 +335,12 @@
 		if (ch != CRAS_CH_MAX)
 			match->map.pos[i] = CH_TO_ALSA(ch);
 	}
-	rc = snd_pcm_set_chmap(handle, &match->map);
+	if (snd_pcm_set_chmap(handle, &match->map) != 0)
+		syslog(LOG_ERR, "Unable to set channel map");
 
 done:
 	snd_pcm_free_chmaps(chmaps);
-	return rc;
+	return 0;
 }
 
 int cras_alsa_get_channel_map(snd_pcm_t *handle,
@@ -354,7 +353,6 @@
 
 	chmaps = snd_pcm_query_chmaps(handle);
 	if (chmaps == NULL) {
-		syslog(LOG_ERR, "No chmap queried!");
 		rc = -EINVAL;
 		goto done;
 	}
@@ -385,48 +383,34 @@
 	return rc;
 }
 
-int cras_alsa_fill_properties(const char *dev, snd_pcm_stream_t stream,
+int cras_alsa_fill_properties(snd_pcm_t *handle,
 			      size_t **rates, size_t **channel_counts,
 			      snd_pcm_format_t **formats)
 {
 	int rc;
-	snd_pcm_t *handle;
 	size_t i, num_found;
 	snd_pcm_hw_params_t *params;
 
 	snd_pcm_hw_params_alloca(&params);
 
-	rc = cras_alsa_pcm_open(&handle,
-				dev,
-				stream);
-	if (rc < 0) {
-		syslog(LOG_ERR, "snd_pcm_open_failed: %s", snd_strerror(rc));
-		return rc;
-	}
-
 	rc = snd_pcm_hw_params_any(handle, params);
 	if (rc < 0) {
-		snd_pcm_close(handle);
 		syslog(LOG_ERR, "snd_pcm_hw_params_any: %s", snd_strerror(rc));
 		return rc;
 	}
 
 	*rates = (size_t *)malloc(sizeof(test_sample_rates));
-	if (*rates == NULL) {
-		snd_pcm_close(handle);
+	if (*rates == NULL)
 		return -ENOMEM;
-	}
 	*channel_counts = (size_t *)malloc(sizeof(test_channel_counts));
 	if (*channel_counts == NULL) {
 		free(*rates);
-		snd_pcm_close(handle);
 		return -ENOMEM;
 	}
 	*formats = (snd_pcm_format_t *)malloc(sizeof(test_formats));
 	if (*formats == NULL) {
 		free(*channel_counts);
 		free(*rates);
-		snd_pcm_close(handle);
 		return -ENOMEM;
 	}
 
@@ -438,6 +422,10 @@
 			(*rates)[num_found++] = test_sample_rates[i];
 	}
 	(*rates)[num_found] = 0;
+	if (num_found == 0) {
+		syslog(LOG_WARNING, "No valid sample rates.");
+		return -EINVAL;
+	}
 
 	num_found = 0;
 	for (i = 0; test_channel_counts[i] != 0; i++) {
@@ -447,6 +435,10 @@
 			(*channel_counts)[num_found++] = test_channel_counts[i];
 	}
 	(*channel_counts)[num_found] = 0;
+	if (num_found == 0) {
+		syslog(LOG_WARNING, "No valid channel counts found.");
+		return -EINVAL;
+	}
 
 	num_found = 0;
 	for (i = 0; test_formats[i] != 0; i++) {
@@ -456,8 +448,10 @@
 			(*formats)[num_found++] = test_formats[i];
 	}
 	(*formats)[num_found] = (snd_pcm_format_t)0;
-
-	snd_pcm_close(handle);
+	if (num_found == 0) {
+		syslog(LOG_WARNING, "No valid sample formats.");
+		return -EINVAL;
+	}
 
 	return 0;
 }
@@ -669,8 +663,7 @@
 			       snd_pcm_uframes_t severe_underrun_frames,
 			       const char *dev_name,
 			       snd_pcm_uframes_t *avail,
-			       struct timespec *tstamp,
-			       unsigned int *underruns)
+			       struct timespec *tstamp)
 {
 	snd_pcm_sframes_t frames;
 	int rc = 0;
@@ -694,17 +687,16 @@
 		syslog(LOG_ERR, "pcm_avail error %s, %s\n",
 		       dev_name, snd_strerror(rc));
 		goto error;
-	} else if (frames >= (snd_pcm_sframes_t)buf_size) {
+	} else if (frames > (snd_pcm_sframes_t)buf_size) {
 		struct timespec tstamp_now;
-		*underruns = *underruns + 1;
 		clock_gettime(CLOCK_MONOTONIC_RAW, &tstamp_now);
 		/* Limit the log rate. */
 		if ((tstamp_now.tv_sec - tstamp_last_underrun_log.tv_sec) >
 		    UNDERRUN_LOG_TIME_SECS) {
 			syslog(LOG_ERR,
 			       "pcm_avail returned frames larger than buf_size: "
-			       "%s: %ld > %lu for %u times\n",
-			       dev_name, frames, buf_size, *underruns);
+			       "%s: %ld > %lu\n",
+			       dev_name, frames, buf_size);
 			tstamp_last_underrun_log.tv_sec = tstamp_now.tv_sec;
 			tstamp_last_underrun_log.tv_nsec = tstamp_now.tv_nsec;
 		}
@@ -741,6 +733,13 @@
 	return 0;
 }
 
+/*
+ * Attempts to resume a PCM.
+ * Note that this path does not get executed for default playback/capture
+ * stream. Default playback/capture stream are removed from the device
+ * upon suspend, and re-attached to the device after resume.
+ * The only stream that lives across suspend resume is hotword stream.
+ */
 int cras_alsa_attempt_resume(snd_pcm_t *handle)
 {
 	int rc;
@@ -749,18 +748,31 @@
 	while ((rc = snd_pcm_resume(handle)) == -EAGAIN)
 		usleep(ALSA_SUSPENDED_SLEEP_TIME_US);
 	if (rc < 0) {
+		/*
+		 * Some devices do not support snd_pcm_resume, that is
+		 * acceptable.
+		 */
 		syslog(LOG_INFO, "System suspended, failed to resume %s.",
 		       snd_strerror(rc));
 		rc = snd_pcm_prepare(handle);
-		if (rc < 0)
-			syslog(LOG_INFO, "Suspended, failed to prepare: %s.",
+		if (rc < 0) {
+			syslog(LOG_ERR, "Suspended, failed to prepare: %s.",
 			       snd_strerror(rc));
+		}
+		/*
+		 * CRAS does not use auto-start (start_threshold = 0), so start
+		 * PCM after it is prepared. This is only for hotword stream.
+		 */
+		rc = snd_pcm_start(handle);
+		if (rc < 0) {
+			syslog(LOG_ERR, "Suspended, failed to start: %s.",
+			       snd_strerror(rc));
+		}
 	}
 	return rc;
 }
 
-int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
-				    unsigned int *underruns)
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst)
 {
 	snd_pcm_uframes_t offset;
 	/* The purpose of calling cras_alsa_mmap_begin is to get the base
@@ -773,13 +785,12 @@
 	 */
 	snd_pcm_uframes_t frames = 1;
 
-	return cras_alsa_mmap_begin(
-			handle, 0, dst, &offset, &frames, underruns);
+	return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames);
 }
 
 int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes,
 			 uint8_t **dst, snd_pcm_uframes_t *offset,
-			 snd_pcm_uframes_t *frames, unsigned int *underruns)
+			 snd_pcm_uframes_t *frames)
 {
 	int rc;
 	unsigned int attempts = 0;
@@ -794,7 +805,6 @@
 				return rc;
 			continue; /* Recovered from suspend, try again. */
 		} else if (rc < 0) {
-			*underruns = *underruns + 1;
 			/* If we can recover, continue and try again. */
 			if (snd_pcm_recover(handle, rc, 0) == 0)
 				continue;
@@ -819,7 +829,7 @@
 }
 
 int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
-			  snd_pcm_uframes_t frames, unsigned int *underruns)
+			  snd_pcm_uframes_t frames)
 {
 	int rc;
 	snd_pcm_sframes_t res;
@@ -833,7 +843,6 @@
 			if (rc < 0)
 				return rc;
 		} else {
-			*underruns = *underruns + 1;
 			/* If we can recover, continue and try again. */
 			rc = snd_pcm_recover(handle, res, 0);
 			if (rc < 0) {
diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h
index 33c3390..81ed645 100644
--- a/cras/src/server/cras_alsa_helpers.h
+++ b/cras/src/server/cras_alsa_helpers.h
@@ -105,8 +105,7 @@
 
 /* Probes properties of the alsa device.
  * Args:
- *    dev - Path to the alsa device to test.
- *    stream - Alsa stream type, input or output.
+ *    handle - The open PCM to configure.
  *    rates - Pointer that will be set to the arrary of valid samples rates.
  *            Must be freed by the caller.
  *    channel_counts - Pointer that will be set to the array of valid channel
@@ -116,7 +115,7 @@
  * Returns:
  *   0 on success.  On failure an error code from alsa or -ENOMEM.
  */
-int cras_alsa_fill_properties(const char *dev, snd_pcm_stream_t stream,
+int cras_alsa_fill_properties(snd_pcm_t *handle,
 			      size_t **rates, size_t **channel_counts,
 			      snd_pcm_format_t **formats);
 
@@ -162,8 +161,6 @@
  *    tstamp[out] - Filled with the hardware timestamp for the available frames.
  *                  This value is {0, 0} when the device hasn't actually started
  *                  reading or writing frames.
- *    underruns[in,out] - Pointer to the underrun counter updated if there was
- *                        an underrun.
  * Returns:
  *    0 on success, negative error on failure. -EPIPE if severe underrun
  *    happens.
@@ -172,8 +169,7 @@
 			       snd_pcm_uframes_t severe_underrun_frames,
 			       const char *dev_name,
 			       snd_pcm_uframes_t *avail,
-			       struct timespec *tstamp,
-			       unsigned int *underruns);
+			       struct timespec *tstamp);
 
 /* Get the current alsa delay, make sure it's no bigger than the buffer size.
  * Args:
@@ -191,12 +187,10 @@
  * Args:
  *    handle - The open PCM to configure.
  *    dst - Pointer set to the area for reading/writing the audio.
- *    underruns - counter to increment if an under-run occurs.
  * Returns:
  *    zero on success, negative error code for fatal errors.
  */
-int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
-				    unsigned int *underrun);
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst);
 
 /* Wrapper for snd_pcm_mmap_begin
  * Args:
@@ -206,27 +200,25 @@
  *    offset - Filled with the offset to pass back to commit.
  *    frames - Passed with the max number of frames to request. Filled with the
  *        max number to use.
- *    underruns - counter to increment if an under-run occurs.
  * Returns:
  *    zero on success, negative error code for fatal
  *    errors.
  */
 int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes,
 			 uint8_t **dst, snd_pcm_uframes_t *offset,
-			 snd_pcm_uframes_t *frames, unsigned int *underruns);
+			 snd_pcm_uframes_t *frames);
 
 /* Wrapper for snd_pcm_mmap_commit
  * Args:
  *    handle - The open PCM to configure.
  *    offset - offset from call to mmap_begin.
  *    frames - # of frames written/read.
- *    underruns - counter to increment if an under-run occurs.
  * Returns:
  *    zero on success, negative error code for fatal
  *    errors.
  */
 int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
-			  snd_pcm_uframes_t frames, unsigned int *underruns);
+			  snd_pcm_uframes_t frames);
 
 /* When the stream is suspended, due to a system suspend, loop until we can
  * resume it. Won't actually loop very much because the system will be
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index 80a8c3b..d9169f1 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -23,6 +23,7 @@
 #include "cras_audio_area.h"
 #include "cras_config.h"
 #include "cras_utf8.h"
+#include "cras_hotword_handler.h"
 #include "cras_iodev.h"
 #include "cras_iodev_list.h"
 #include "cras_messages.h"
@@ -44,6 +45,8 @@
 #define INTERNAL_MICROPHONE "Internal Mic"
 #define INTERNAL_SPEAKER "Speaker"
 #define KEYBOARD_MIC "Keyboard Mic"
+#define HEADPHONE "Headphone"
+#define MIC "Mic"
 #define USB "USB"
 
 /*
@@ -151,6 +154,7 @@
 	unsigned int filled_zeros_for_draining;
 	snd_pcm_uframes_t severe_underrun_frames;
 	struct cras_volume_curve *default_volume_curve;
+	int hwparams_set;
 };
 
 static void init_device_settings(struct alsa_io *aio);
@@ -244,6 +248,31 @@
 	},
 };
 
+static int set_hwparams(struct cras_iodev *iodev)
+{
+	struct alsa_io *aio = (struct alsa_io *)iodev;
+	int period_wakeup;
+	int rc;
+
+	/* Only need to set hardware params once. */
+	if (aio->hwparams_set)
+		return 0;
+
+	/* If it's a wake on voice device, period_wakeups are required. */
+	period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD);
+
+	/* Sets frame rate and channel count to alsa device before
+	 * we test channel mapping. */
+	rc = cras_alsa_set_hwparams(aio->handle, iodev->format,
+				    &iodev->buffer_size, period_wakeup,
+				    aio->dma_period_set_microsecs);
+	if (rc < 0)
+		return rc;
+
+	aio->hwparams_set = 1;
+	return 0;
+}
+
 /*
  * iodev callbacks.
  */
@@ -259,8 +288,7 @@
 					aio->base.buffer_size,
 					aio->severe_underrun_frames,
 					iodev->info.name,
-					&frames, tstamp,
-					&aio->num_underruns);
+					&frames, tstamp);
 	if (rc < 0) {
 		if (rc == -EPIPE)
 			aio->num_severe_underruns++;
@@ -305,6 +333,7 @@
 	aio->handle = NULL;
 	aio->is_free_running = 0;
 	aio->filled_zeros_for_draining = 0;
+	aio->hwparams_set = 0;
 	cras_iodev_free_format(&aio->base);
 	cras_iodev_free_audio_area(&aio->base);
 	return 0;
@@ -316,6 +345,11 @@
 	struct alsa_io *aio = (struct alsa_io *)arg;
 	audio_thread_rm_callback(aio->poll_fd);
 	aio->poll_fd = -1;
+	aio->base.input_streaming = 1;
+
+	/* Send hotword triggered signal. */
+	cras_hotword_send_triggered_msg();
+
 	return 0;
 }
 
@@ -323,7 +357,20 @@
 {
 	struct alsa_io *aio = (struct alsa_io *)iodev;
 	snd_pcm_t *handle;
-	int period_wakeup;
+	int rc;
+
+	rc = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
+	if (rc < 0)
+		return rc;
+
+	aio->handle = handle;
+
+	return 0;
+}
+
+static int configure_dev(struct cras_iodev *iodev)
+{
+	struct alsa_io *aio = (struct alsa_io *)iodev;
 	int rc;
 
 	/* This is called after the first stream added so configure for it.
@@ -342,39 +389,23 @@
 	syslog(LOG_DEBUG, "Configure alsa device %s rate %zuHz, %zu channels",
 	       aio->dev, iodev->format->frame_rate,
 	       iodev->format->num_channels);
-	handle = 0; /* Avoid unused warning. */
-	rc = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
+
+	rc = set_hwparams(iodev);
 	if (rc < 0)
 		return rc;
 
-	/* If it's a wake on voice device, period_wakeups are required. */
-	period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD);
-
-	rc = cras_alsa_set_hwparams(handle, iodev->format,
-				    &iodev->buffer_size, period_wakeup,
-				    aio->dma_period_set_microsecs);
-	if (rc < 0) {
-		cras_alsa_pcm_close(handle);
-		return rc;
-	}
-
 	/* Set channel map to device */
-	rc = cras_alsa_set_channel_map(handle,
+	rc = cras_alsa_set_channel_map(aio->handle,
 				       iodev->format);
-	if (rc < 0) {
-		cras_alsa_pcm_close(handle);
+	if (rc < 0)
 		return rc;
-	}
 
 	/* Configure software params. */
-	rc = cras_alsa_set_swparams(handle, &aio->enable_htimestamp);
-	if (rc < 0) {
-		cras_alsa_pcm_close(handle);
+	rc = cras_alsa_set_swparams(aio->handle, &aio->enable_htimestamp);
+	if (rc < 0)
 		return rc;
-	}
 
-	/* Assign pcm handle then initialize device settings. */
-	aio->handle = handle;
+	/* Initialize device settings. */
 	init_device_settings(aio);
 
 	aio->poll_fd = -1;
@@ -382,7 +413,7 @@
 		struct pollfd *ufds;
 		int count, i;
 
-		count = snd_pcm_poll_descriptors_count(handle);
+		count = snd_pcm_poll_descriptors_count(aio->handle);
 		if (count <= 0) {
 			syslog(LOG_ERR, "Invalid poll descriptors count\n");
 			return count;
@@ -392,7 +423,7 @@
 		if (ufds == NULL)
 			return -ENOMEM;
 
-		rc = snd_pcm_poll_descriptors(handle, ufds, count);
+		rc = snd_pcm_poll_descriptors(aio->handle, ufds, count);
 		if (rc < 0) {
 			syslog(LOG_ERR,
 			       "Getting hotword poll descriptors: %s\n",
@@ -480,8 +511,7 @@
 				  format_bytes,
 				  &dst,
 				  &aio->mmap_offset,
-				  &nframes,
-				  &aio->num_underruns);
+				  &nframes);
 
 	iodev->area->frames = nframes;
 	cras_audio_area_config_buf_pointers(iodev->area, iodev->format, dst);
@@ -498,8 +528,7 @@
 
 	return cras_alsa_mmap_commit(aio->handle,
 				     aio->mmap_offset,
-				     nwritten,
-				     &aio->num_underruns);
+				     nwritten);
 }
 
 static int flush_buffer(struct cras_iodev *iodev)
@@ -508,6 +537,7 @@
 	snd_pcm_uframes_t nframes;
 
 	if (iodev->direction == CRAS_STREAM_INPUT) {
+		nframes = snd_pcm_avail(aio->handle);
 		nframes = snd_pcm_forwardable(aio->handle);
 		return snd_pcm_forward(aio->handle, nframes);
 	}
@@ -552,8 +582,6 @@
 static int update_channel_layout(struct cras_iodev *iodev)
 {
 	struct alsa_io *aio = (struct alsa_io *)iodev;
-	snd_pcm_t *handle = NULL;
-	snd_pcm_uframes_t buf_size = 0;
 	int err = 0;
 
 	/* If the capture channel map is specified in UCM, prefer it over
@@ -570,25 +598,11 @@
 		}
 	}
 
-	err = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
-	if (err < 0) {
-		syslog(LOG_ERR, "snd_pcm_open_failed: %s", snd_strerror(err));
+	err = set_hwparams(iodev);
+	if (err < 0)
 		return err;
-	}
 
-	/* Sets frame rate and channel count to alsa device before
-	 * we test channel mapping. */
-	err = cras_alsa_set_hwparams(handle, iodev->format, &buf_size, 0,
-				     aio->dma_period_set_microsecs);
-	if (err < 0) {
-		cras_alsa_pcm_close(handle);
-		return err;
-	}
-
-	err = cras_alsa_get_channel_map(handle, iodev->format);
-
-	cras_alsa_pcm_close(handle);
-	return err;
+	return cras_alsa_get_channel_map(aio->handle, iodev->format);
 }
 
 static int set_hotword_model(struct cras_iodev *iodev, const char *model_name)
@@ -795,7 +809,8 @@
 			mixer_input = ain->mixer_input;
 
 		if (cras_iodev_software_volume_needed(&aio->base)) {
-			min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN;
+			min_capture_gain = cras_iodev_minimum_software_gain(
+					&aio->base);
 			max_capture_gain = cras_iodev_maximum_software_gain(
 					&aio->base);
 		} else {
@@ -881,6 +896,7 @@
 	return n >= m && !strcmp(s + (n - m), suffix);
 }
 
+#ifdef CRAS_DBUS
 /*
  * Drop the node name and replace it with node type.
  */
@@ -897,6 +913,7 @@
 		strcpy(node->name, DEFAULT);
 	}
 }
+#endif
 
 /*
  * Sets the initial plugged state and type of a node based on its
@@ -923,18 +940,27 @@
 			break;
 		}
 
-	/* If we didn't find a matching name above, but the node is a jack node,
-	 * set its type to headphone/mic. This matches node names like "DAISY-I2S Mic
-	 * Jack".
+	/*
+	 * If we didn't find a matching name above, but the node is a jack node,
+	 * and there is no "HDMI" in the node name, then this must be a 3.5mm
+	 * headphone/mic.
+	 * Set its type and name to headphone/mic. The name is important because
+	 * it associates the UCM section to the node so the properties like
+	 * default node gain can be obtained.
+	 * This matches node names like "DAISY-I2S Mic Jack".
 	 * If HDMI is in the node name, set its type to HDMI. This matches node names
 	 * like "Rockchip HDMI Jack".
 	 */
 	if (i == ARRAY_SIZE(node_defaults)) {
-		if (endswith(node->name, "Jack")) {
-			if (node->dev->direction == CRAS_STREAM_OUTPUT)
+		if (endswith(node->name, "Jack") && !strstr(node->name, HDMI)) {
+			if (node->dev->direction == CRAS_STREAM_OUTPUT) {
 				node->type = CRAS_NODE_TYPE_HEADPHONE;
-			else
+				strncpy(node->name, HEADPHONE, sizeof(node->name) - 1);
+			}
+			else {
 				node->type = CRAS_NODE_TYPE_MIC;
+				strncpy(node->name, MIC, sizeof(node->name) - 1);
+			}
 		}
 		if (strstr(node->name, HDMI) &&
 		    node->dev->direction == CRAS_STREAM_OUTPUT)
@@ -949,8 +975,10 @@
 		node->position = NODE_POSITION_EXTERNAL;
 	}
 
+#ifdef CRAS_DBUS
 	if (!is_utf8_string(node->name))
 		drop_node_name(node);
+#endif
 }
 
 static int get_ucm_flag_integer(struct alsa_io *aio,
@@ -1043,18 +1071,22 @@
 static void set_input_node_software_volume_needed(
 	struct alsa_input_node *input, struct alsa_io *aio)
 {
+	long min_software_gain;
 	long max_software_gain;
 	int rc;
 
 	input->base.software_volume_needed = 0;
+	input->base.min_software_gain = DEFAULT_MIN_CAPTURE_GAIN;
 	input->base.max_software_gain = 0;
 
-	/* Enable software gain only if max software gain is specified in UCM.*/
+	/* Enable software gain only if max software gain is specified in UCM. */
 	if (!aio->ucm)
 		return;
 
 	rc = ucm_get_max_software_gain(aio->ucm, input->base.name,
 	                               &max_software_gain);
+
+	/* If max software gain doesn't exist, skip min software gain setting. */
 	if (rc)
 		return;
 
@@ -1063,6 +1095,24 @@
 	syslog(LOG_INFO,
 	       "Use software gain for %s with max %ld because it is specified"
 	       " in UCM", input->base.name, max_software_gain);
+
+	/* Enable min software gain if it is specified in UCM. */
+	rc = ucm_get_min_software_gain(aio->ucm, input->base.name,
+	                               &min_software_gain);
+	if (rc)
+		return;
+
+	if (min_software_gain > max_software_gain) {
+		syslog(LOG_ERR,
+		       "Ignore MinSoftwareGain %ld because it is larger than "
+		       "MaxSoftwareGain %ld", min_software_gain, max_software_gain);
+		return;
+	}
+
+	syslog(LOG_INFO,
+	       "Use software gain for %s with min %ld because it is specified"
+	       " in UCM", input->base.name, min_software_gain);
+	input->base.min_software_gain = min_software_gain;
 }
 
 static void set_input_default_node_gain(struct alsa_input_node *input,
@@ -1167,9 +1217,10 @@
 	        return;
 
 	if (aio->card_type == ALSA_CARD_TYPE_USB) {
-		snprintf(node_name, sizeof(node_name), "%s: %s",
-			aio->base.info.name, ctl_name);
-		new_output(aio, cras_output, node_name);
+		if (snprintf(node_name, sizeof(node_name), "%s: %s",
+			     aio->base.info.name, ctl_name) > 0) {
+			new_output(aio, cras_output, node_name);
+		}
 	} else {
 		new_output(aio, cras_output, ctl_name);
 	}
@@ -1203,6 +1254,7 @@
 static struct alsa_input_node *new_input(struct alsa_io *aio,
 		struct mixer_control *cras_input, const char *name)
 {
+	struct cras_iodev *iodev = &aio->base;
 	struct alsa_input_node *input;
 	char *mic_positions;
 	int err;
@@ -1248,6 +1300,12 @@
 			free(input->channel_layout);
 			input->channel_layout = 0;
 		}
+		if (ucm_get_preempt_hotword(aio->ucm, name)) {
+			iodev->pre_open_iodev_hook =
+				cras_iodev_list_suspend_hotword_streams;
+			iodev->post_close_iodev_hook =
+				cras_iodev_list_resume_hotword_stream;
+		}
 	}
 
 	cras_iodev_add_node(&aio->base, &input->base);
@@ -1264,8 +1322,11 @@
 	const char *ctl_name = cras_alsa_mixer_get_control_name(cras_input);
 
 	if (aio->card_type == ALSA_CARD_TYPE_USB) {
-		snprintf(node_name , sizeof(node_name), "%s: %s",
-			 aio->base.info.name, ctl_name);
+		int ret = snprintf(node_name , sizeof(node_name), "%s: %s",
+				   aio->base.info.name, ctl_name);
+		// Truncation is OK, but add a check to make the compiler happy.
+		if (ret == sizeof(node_name))
+			node_name[sizeof(node_name) - 1] = '\0';
 		new_input(aio, cras_input, node_name);
 	} else {
 		new_input(aio, cras_input, ctl_name);
@@ -1317,6 +1378,21 @@
 	return ain;
 }
 
+static const struct cras_alsa_jack *get_jack_from_node(struct cras_ionode *node)
+{
+	const struct cras_alsa_jack *jack = NULL;
+
+	if (node == NULL)
+		return NULL;
+
+	if (node->dev->direction == CRAS_STREAM_OUTPUT)
+		jack = ((struct alsa_output_node *)node)->jack;
+	else if (node->dev->direction == CRAS_STREAM_INPUT)
+		jack = ((struct alsa_input_node *)node)->jack;
+
+	return jack;
+}
+
 /*
  * Returns the dsp name specified in the ucm config. If there is a dsp
  * name specified for the jack of the active node, use that. Otherwise
@@ -1417,9 +1493,12 @@
 
 	cras_alsa_jack_update_monitor_name(jack, node->base.name,
 					   sizeof(node->base.name));
+
+#ifdef CRAS_DBUS
 	/* The name got from jack might be an invalid UTF8 string. */
 	if (!is_utf8_string(node->base.name))
 		drop_node_name(&node->base);
+#endif
 
 	cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged);
 
@@ -1569,7 +1648,7 @@
 	free(iodev->supported_formats);
 	iodev->supported_formats = NULL;
 
-	err = cras_alsa_fill_properties(aio->dev, aio->alsa_stream,
+	err = cras_alsa_fill_properties(aio->handle,
 					&iodev->supported_rates,
 					&iodev->supported_channel_counts,
 					&iodev->supported_formats);
@@ -1641,8 +1720,7 @@
 	size_t format_bytes;
 
 	/* Fill whole buffer with zeros. */
-	rc = cras_alsa_mmap_get_whole_buffer(
-			aio->handle, &dst, &aio->num_underruns);
+	rc = cras_alsa_mmap_get_whole_buffer(aio->handle, &dst);
 
 	if (rc < 0) {
 		syslog(LOG_ERR, "Failed to get whole buffer: %s",
@@ -1668,9 +1746,55 @@
 			odev->min_buffer_level + odev->min_cb_level);
 }
 
+/* This function is for leaving no-stream state but still not in free run yet.
+ * The device may have valid samples remaining. We need to adjust appl_ptr to
+ * the correct position, which is MAX(min_cb_level + min_buffer_level,
+ * valid_sample) */
+static int adjust_appl_ptr_samples_remaining(struct cras_iodev *odev)
+{
+	struct alsa_io *aio = (struct alsa_io *)odev;
+	int rc;
+	unsigned int real_hw_level, valid_sample, offset;
+	struct timespec hw_tstamp;
+
+	/* Get the amount of valid samples which haven't been played yet.
+	 * The real_hw_level is the real hw_level in device buffer. It doesn't
+	 * subtract min_buffer_level. */
+	valid_sample = 0;
+	rc = odev->frames_queued(odev, &hw_tstamp);
+	if(rc < 0)
+		return rc;
+	real_hw_level = rc;
+
+	/*
+	 * If underrun happened, handle it. Because alsa_output_underrun function
+	 * has already called adjust_appl_ptr, we don't need to call it again.
+	 */
+	if (real_hw_level < odev->min_buffer_level)
+		return odev->output_underrun(odev);
+
+	if (real_hw_level > aio->filled_zeros_for_draining)
+		valid_sample = real_hw_level - aio->filled_zeros_for_draining;
+
+	offset = MAX(odev->min_buffer_level + odev->min_cb_level, valid_sample);
+
+	/* Fill zeros to make sure there are enough zero samples in device buffer.*/
+	if (offset > real_hw_level) {
+		rc = cras_iodev_fill_odev_zeros(odev, offset - real_hw_level);
+		if (rc)
+			return rc;
+	}
+	return cras_alsa_resume_appl_ptr(aio->handle, offset);
+}
+
 static int alsa_output_underrun(struct cras_iodev *odev)
 {
+	struct alsa_io *aio = (struct alsa_io *)odev;
 	int rc;
+
+	/* Update number of underruns we got. */
+	aio->num_underruns++;
+
 	/* Fill whole buffer with zeros. This avoids samples left in buffer causing
 	 * noise when device plays them. */
 	rc = fill_whole_buffer_with_zeros(odev);
@@ -1684,21 +1808,31 @@
 {
 	struct alsa_io *aio = (struct alsa_io *)odev;
 	int rc;
-	unsigned int hw_level, fr_to_write;
-	unsigned int target_hw_level = odev->min_cb_level * 2;
+	unsigned int real_hw_level, fr_to_write;
+	unsigned int target_hw_level = odev->min_cb_level * 2 + odev->min_buffer_level;
 	struct timespec hw_tstamp;
 
 	if (aio->is_free_running)
 		return 0;
 
-	/* Check if all valid samples are played.
-	 * If all valid samples are played, fill whole buffer with zeros. */
-	rc = cras_iodev_frames_queued(odev, &hw_tstamp);
-	if (rc < 0)
+	/* Check if all valid samples are played. If all valid samples are played,
+	 * fill whole buffer with zeros. The real_hw_level is the real hw_level in
+	 * device buffer. It doesn't subtract min_buffer_level.*/
+	rc = odev->frames_queued(odev, &hw_tstamp);
+	if(rc < 0)
 		return rc;
-	hw_level = rc;
+	real_hw_level = rc;
 
-	if (hw_level < aio->filled_zeros_for_draining || hw_level == 0) {
+	/* If underrun happened, handle it and enter free run state. */
+	if (real_hw_level < odev->min_buffer_level) {
+		rc = odev->output_underrun(odev);
+		if (rc < 0)
+			return rc;
+		aio->is_free_running = 1;
+		return 0;
+	}
+
+	if (real_hw_level <= aio->filled_zeros_for_draining || real_hw_level == 0) {
 		rc = fill_whole_buffer_with_zeros(odev);
 		if (rc < 0)
 			return rc;
@@ -1707,10 +1841,9 @@
 	}
 
 	/* Fill some zeros to drain valid samples. */
-	fr_to_write = cras_iodev_buffer_avail(odev, hw_level);
-
-	if (hw_level <= target_hw_level) {
-		fr_to_write = MIN(target_hw_level - hw_level, fr_to_write);
+	fr_to_write = odev->buffer_size - real_hw_level;
+	if (real_hw_level < target_hw_level) {
+		fr_to_write = MIN(target_hw_level - real_hw_level, fr_to_write);
 		rc = cras_iodev_fill_odev_zeros(odev, fr_to_write);
 		if (rc)
 			return rc;
@@ -1725,10 +1858,10 @@
 	struct alsa_io *aio = (struct alsa_io *)odev;
 	int rc;
 
-	if (!aio->is_free_running)
-		return 0;
-
-	rc = adjust_appl_ptr(odev);
+	if (aio->is_free_running)
+		rc = adjust_appl_ptr(odev);
+	else
+		rc = adjust_appl_ptr_samples_remaining(odev);
 	if (rc) {
 		syslog(LOG_ERR, "device %s failed to leave free run, rc = %d",
 		       odev->info.name, rc);
@@ -1861,6 +1994,7 @@
 		aio->base.output_underrun = alsa_output_underrun;
 	}
 	iodev->open_dev = open_dev;
+	iodev->configure_dev = configure_dev;
 	iodev->close_dev = close_dev;
 	iodev->update_supported_formats = update_supported_formats;
 	iodev->frames_queued = frames_queued;
@@ -1899,6 +2033,7 @@
 	aio->ucm = ucm;
 	if (ucm) {
 		unsigned int level;
+		int rc;
 
 		aio->dsp_name_default = ucm_get_dsp_name_default(ucm,
 								 direction);
@@ -1908,8 +2043,8 @@
 			aio->base.set_swap_mode_for_node =
 				set_alsa_node_swapped;
 
-		level = ucm_get_min_buffer_level(ucm);
-		if (level && direction == CRAS_STREAM_OUTPUT)
+		rc = ucm_get_min_buffer_level(ucm, &level);
+		if (!rc && direction == CRAS_STREAM_OUTPUT)
 			iodev->min_buffer_level = level;
 
 		aio->enable_htimestamp =
@@ -2029,8 +2164,10 @@
 				   first_plugged_node(&aio->base),
 				   0);
 
-	/* Set plugged for the first USB device per card when it appears. */
-	if (aio->card_type == ALSA_CARD_TYPE_USB && is_first)
+	/* Set plugged for the first USB device per card when it appears if
+	 * there is no jack reporting plug status. */
+	if (aio->card_type == ALSA_CARD_TYPE_USB && is_first &&
+			!get_jack_from_node(iodev->active_node))
 		cras_iodev_set_node_attr(iodev->active_node,
 					 IONODE_ATTR_PLUGGED, 1);
 
@@ -2116,8 +2253,10 @@
 				   first_plugged_node(&aio->base),
 				   0);
 
-	/* Set plugged for the first USB device per card when it appears. */
-	if (aio->card_type == ALSA_CARD_TYPE_USB && aio->is_first)
+	/* Set plugged for the first USB device per card when it appears if
+	 * there is no jack reporting plug status. */
+	if (aio->card_type == ALSA_CARD_TYPE_USB && aio->is_first &&
+			!get_jack_from_node(iodev->active_node))
 		cras_iodev_set_node_attr(iodev->active_node,
 					 IONODE_ATTR_PLUGGED, 1);
 
diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c
index 2f69e3b..b74af25 100644
--- a/cras/src/server/cras_alsa_jack.c
+++ b/cras/src/server/cras_alsa_jack.c
@@ -74,6 +74,12 @@
  *    edid_file - File to read the EDID from (if available, HDMI only).
  *    display_info_timer - Timer used to poll display info for HDMI jacks.
  *    display_info_retries - Number of times to retry reading display info.
+ *
+ *    mixer_output/mixer_input fields are only used to find the node for this
+ *    jack. These are not used for setting volume or mute. There should be a
+ *    1:1 map between node and jack. node->jack follows the pointer; jack->node
+ *    is done by either searching node->jack pointers or searching the node that
+ *    has the same mixer_control as the jack.
  */
 struct cras_alsa_jack {
 	unsigned is_gpio;	/* !0 -> 'gpio' valid
@@ -863,6 +869,15 @@
 	return 0;
 }
 
+/* Check if the given name is a jack created for the connector control of a
+ * input/output terminal entity on a USB Audio Class 2.0 device. */
+static int is_jack_uac2(const char *jack_name,
+			enum CRAS_STREAM_DIRECTION direction)
+{
+	return jack_matches_regex(jack_name, direction == CRAS_STREAM_OUTPUT ?
+				  "^.* - Output Jack$" : "^.* - Input Jack$");
+}
+
 /* Looks for any JACK controls.  Monitors any found controls for changes and
  * decides to route based on plug/unlpug events. */
 static int find_jack_controls(struct cras_alsa_jack_list *jack_list)
@@ -882,7 +897,6 @@
 	static const char eld_control_name[] = "ELD";
 	const char * const *jack_names;
 	unsigned int num_jack_names;
-	char device_name[6];
 
 	if (!jack_list->hctl) {
 		syslog(LOG_WARNING, "Can't search hctl for jacks.");
@@ -905,7 +919,8 @@
 		if (iface != SND_CTL_ELEM_IFACE_CARD)
 			continue;
 		name = snd_hctl_elem_get_name(elem);
-		if (!is_jack_control_in_list(jack_names, num_jack_names, name))
+		if (!is_jack_control_in_list(jack_names, num_jack_names, name) &&
+		    !is_jack_uac2(name, jack_list->direction))
 			continue;
 		if (hctl_jack_device_index(name) != jack_list->device_index)
 			continue;
@@ -917,7 +932,6 @@
 		jack->jack_list = jack_list;
 		DL_APPEND(jack_list->jacks, jack);
 
-		syslog(LOG_DEBUG, "Found Jack: %s for %s", name, device_name);
 		snd_hctl_elem_set_callback(elem, hctl_jack_cb);
 		snd_hctl_elem_set_callback_private(elem, jack);
 
diff --git a/cras/src/server/cras_alsa_mixer.c b/cras/src/server/cras_alsa_mixer.c
index 4cb665a..27ea4a6 100644
--- a/cras/src/server/cras_alsa_mixer.c
+++ b/cras/src/server/cras_alsa_mixer.c
@@ -770,7 +770,7 @@
 				       "Failed to add mixer control '%s'"
 				       " with type '%d'",
 				       control->name, control->type);
-				return rc;
+				goto out;
 			}
 			found = 1;
 		}
@@ -806,7 +806,7 @@
 				       "Failed to add mixer control '%s'"
 				       " with type '%d'",
 				       control->name, control->type);
-				return rc;
+				goto out;
 			}
 			found = 1;
 		}
@@ -836,7 +836,7 @@
 				"Speaker", coupled_controls);
 		if (rc) {
 			syslog(LOG_ERR, "Could not add coupled output");
-			return rc;
+			goto out;
 		}
 	}
 
@@ -848,10 +848,12 @@
 		rc = add_main_volume_control(cmix, other_elem);
 		if (rc) {
 			syslog(LOG_ERR, "Could not add other volume control");
-			return rc;
+			goto out;
 		}
 	}
 
+out:
+	mixer_name_free(default_controls);
 	return rc;
 }
 
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index 8aa4891..2508b8b 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -10,6 +10,7 @@
 #include <syslog.h>
 
 #include "cras_alsa_ucm.h"
+#include "cras_util.h"
 #include "utlist.h"
 
 static const char jack_var[] = "JackName";
@@ -32,10 +33,23 @@
 static const char capture_device_rate_var[] = "CaptureRate";
 static const char capture_channel_map_var[] = "CaptureChannelMap";
 static const char coupled_mixers[] = "CoupledMixers";
-/* Set this value in a SectionDevice to specify the maximum software gain in dBm
- * and enable software gain on this node. */
+static const char preempt_hotword_var[] = "PreemptHotword";
+static const char echo_reference_dev_name_var[] = "EchoReferenceDev";
+/*
+ * Set this value in a SectionDevice to specify the minimum software gain in
+ * 0.01 dB and enable software gain on this node. It must be used with
+ * MaxSoftwareGain. If not, the value will be ignored.
+ */
+static const char min_software_gain[] = "MinSoftwareGain";
+/*
+ * Set this value in a SectionDevice to specify the maximum software gain in
+ * 0.01 dB and enable software gain on this node.
+ */
 static const char max_software_gain[] = "MaxSoftwareGain";
-/* Set this value in a SectionDevice to specify the default node gain in dBm. */
+/*
+ * Set this value in a SectionDevice to specify the default node gain in
+ * 0.01 dB.
+ */
 static const char default_node_gain[] = "DefaultNodeGain";
 static const char hotword_model_prefix[] = "Hotword Model";
 static const char fully_specified_ucm_var[] = "FullySpecifiedUCM";
@@ -49,6 +63,7 @@
 	"Voice Call",
 	"Speech",
 	"Pro Audio",
+	"Accessibility",
 };
 
 /* Represents a list of section names found in UCM. */
@@ -334,7 +349,8 @@
 {
 	const char *names_in_string = NULL;
 	int rc;
-	char *tokens, *name, *laststr;
+	char *tokens, *name;
+	char *laststr = NULL;
 	struct mixer_name *names = NULL;
 
 	rc = get_var(mgr, var, dev, uc_verb(mgr), &names_in_string);
@@ -361,6 +377,8 @@
 	const char **list;
 	int num_verbs, i, j;
 
+	assert_on_compile(ARRAY_SIZE(use_case_verbs) == CRAS_STREAM_NUM_TYPES);
+
 	if (!name)
 		return NULL;
 
@@ -614,16 +632,18 @@
 	return ucm_get_dsp_name(mgr, "", direction);
 }
 
-unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr)
+int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr,
+			     unsigned int *level)
 {
 	int value;
 	int rc;
 
 	rc = get_int(mgr, min_buffer_level_var, "", uc_verb(mgr), &value);
 	if (rc)
-		return 0;
+		return -ENOENT;
+	*level = value;
 
-	return value;
+	return 0;
 }
 
 unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr)
@@ -638,6 +658,19 @@
 	return value;
 }
 
+int ucm_get_min_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+			      long *gain)
+{
+	int value;
+	int rc;
+
+	rc = get_int(mgr, min_software_gain, dev, uc_verb(mgr), &value);
+	if (rc)
+		return rc;
+	*gain = value;
+	return 0;
+}
+
 int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
 			      long *gain)
 {
@@ -664,6 +697,17 @@
 	return 0;
 }
 
+int ucm_get_preempt_hotword(struct cras_use_case_mgr *mgr, const char *dev)
+{
+	int value;
+	int rc;
+
+	rc = get_int(mgr, preempt_hotword_var, dev, uc_verb(mgr), &value);
+	if (rc)
+		return 0;
+	return value;
+}
+
 const char *ucm_get_device_name_for_dev(
 	struct cras_use_case_mgr *mgr, const char *dev,
 	enum CRAS_STREAM_DIRECTION direction)
@@ -675,6 +719,19 @@
 	return NULL;
 }
 
+const char *ucm_get_echo_reference_dev_name_for_dev(
+		struct cras_use_case_mgr *mgr, const char *dev)
+{
+	const char *name = NULL;
+	int rc;
+
+	rc = get_var(mgr, echo_reference_dev_name_var, dev,
+		     uc_verb(mgr), &name);
+	if (rc)
+		return NULL;
+	return name;
+}
+
 int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev,
 				enum CRAS_STREAM_DIRECTION direction)
 {
@@ -760,10 +817,10 @@
 
 	/* snd_use_case_get_list fills list with pairs of device name and
 	 * comment, so device names are in even-indexed elements. */
+	const char *dev_name;
 	for (i = 0; i < num_devs; i += 2) {
 		enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_UNDEFINED;
 		int dev_idx = -1;
-		const char *dev_name = strdup(list[i]);
 		const char *jack_name;
 		const char *jack_type;
 		const char *mixer_name;
@@ -771,6 +828,7 @@
 		int rc;
 		const char *target_device_name;
 
+		dev_name = strdup(list[i]);
 		if (!dev_name)
 			continue;
 
@@ -840,6 +898,7 @@
 
 		DL_APPEND(sections, dev_sec);
 		ucm_section_dump(dev_sec);
+		free((void *)dev_name);
 	}
 
 	if (num_devs > 0)
@@ -850,6 +909,7 @@
 	if (num_devs > 0)
 		snd_use_case_free_list(list, num_devs);
 	ucm_section_free_list(sections);
+	free((void *)dev_name);
 	return NULL;
 }
 
@@ -920,7 +980,7 @@
 
 enable_mod:
 	ucm_set_modifier_enabled(mgr, model_mod, 1);
-
+	free((void *)model_mod);
 	return 0;
 }
 
@@ -1018,6 +1078,8 @@
 
 	if (strcmp(name, "hctl") && strcmp(name, "gpio")) {
 		syslog(LOG_ERR, "Unknown jack type: %s", name);
+		if(name)
+			free((void *)name);
 		return NULL;
 	}
 	return name;
diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h
index 622bbf8..38377a0 100644
--- a/cras/src/server/cras_alsa_ucm.h
+++ b/cras/src/server/cras_alsa_ucm.h
@@ -184,15 +184,31 @@
  * unreliable dma residue.
  * Args:
  *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ *    level - The pointer to the returned value.
+ *
  */
-unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr);
+int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr,
+			     unsigned int *level);
 
 /* Gets the flag for disabling software volume.
  * Args:
  *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ *    0 on success, -ENOENT on failure.
  */
 unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr);
 
+/* Gets the value for minimum software gain.
+ * Args:
+ *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ *    dev - The device to check for minimum software gain.
+ *    gain - The pointer to the returned value;
+ * Returns:
+ *    0 on success, other error codes on failure.
+ */
+int ucm_get_min_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+			      long *gain);
+
 /* Gets the value for maximum software gain.
  * Args:
  *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
@@ -215,6 +231,16 @@
 int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev,
 			      long *gain);
 
+/* Gets the flag if an input device can preempt hotword recording.
+ * Args:
+ *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ *    dev - The device to check for preempt hotword flag.
+ * Returns:
+ *    Non-zero value means input can preempt hotword recording, otherwise
+ *    return zero.
+ */
+int ucm_get_preempt_hotword(struct cras_use_case_mgr *mgr, const char *dev);
+
 /* Gets the device name of this device on the card..
  *
  * Args:
@@ -230,6 +256,18 @@
 		struct cras_use_case_mgr *mgr, const char *dev,
 		enum CRAS_STREAM_DIRECTION direction);
 
+/* Gets the node name of the echo reference device on the card.
+ * Args:
+ *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ *    dev - The device to check echo reference for.
+ * Returns:
+ *    String containing the node name of the echo reference to this
+ *    dev, caller is responsible to free it later. NULL if echo reference
+ *    doesn't exist.
+ */
+const char *ucm_get_echo_reference_dev_name_for_dev(
+		struct cras_use_case_mgr *mgr, const char *dev);
+
 /* Gets the sample rate at which to run this device.
  *
  * Args:
diff --git a/cras/src/server/cras_apm_list.c b/cras/src/server/cras_apm_list.c
new file mode 100644
index 0000000..ac0935e
--- /dev/null
+++ b/cras/src/server/cras_apm_list.c
@@ -0,0 +1,643 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <string.h>
+#include <syslog.h>
+
+#include <webrtc-apm/webrtc_apm.h>
+
+#include "aec_config.h"
+#include "apm_config.h"
+#include "byte_buffer.h"
+#include "cras_apm_list.h"
+#include "cras_audio_area.h"
+#include "cras_audio_format.h"
+#include "cras_dsp_pipeline.h"
+#include "cras_iodev.h"
+#include "cras_iodev_list.h"
+#include "dsp_util.h"
+#include "dumper.h"
+#include "float_buffer.h"
+#include "utlist.h"
+
+
+/*
+ * Structure holding a WebRTC audio processing module and necessary
+ * info to process and transfer input buffer from device to stream.
+ *
+ * Below chart describes the buffer structure inside APM and how an input buffer
+ * flows from a device through the APM to stream. APM processes audio buffers in
+ * fixed 10ms width, and that's the main reason we need two copies of the
+ * buffer:
+ * (1) to cache input buffer from device until 10ms size is filled.
+ * (2) to store the interleaved buffer, of 10ms size also, after APM processing.
+ *
+ *  ________   _______     _______________________________
+ *  |      |   |     |     |_____________APM ____________|
+ *  |input |-> | DSP |---> ||           |    |          || -> stream 1
+ *  |device|   |     | |   || float buf | -> | byte buf ||
+ *  |______|   |_____| |   ||___________|    |__________||
+ *                     |   |_____________________________|
+ *                     |   _______________________________
+ *                     |-> |             APM 2           | -> stream 2
+ *                     |   |_____________________________|
+ *                     |                                       ...
+ *                     |
+ *                     |------------------------------------> stream N
+ *
+ * Members:
+ *    apm_ptr - An APM instance from libwebrtc_audio_processing
+ *    dev_ptr - Pointer to the device this APM is associated with.
+ *    buffer - Stores the processed/interleaved data ready for stream to read.
+ *    fbuffer - Stores the floating pointer buffer from input device waiting
+ *        for APM to process.
+ *    dev_fmt - The format used by the iodev this APM attaches to.
+ *    fmt - The audio data format configured for this APM.
+ *    area - The cras_audio_area used for copying processed data to client
+ *        stream.
+ *    work_queue - A task queue instance created and destroyed by
+ *        libwebrtc_apm.
+ */
+struct cras_apm {
+	webrtc_apm apm_ptr;
+	void *dev_ptr;
+	struct byte_buffer *buffer;
+	struct float_buffer *fbuffer;
+	struct cras_audio_format dev_fmt;
+	struct cras_audio_format fmt;
+	struct cras_audio_area *area;
+	void *work_queue;
+	struct cras_apm *prev, *next;
+};
+
+/*
+ * Lists of cras_apm instances created for a stream. A stream may
+ * have more than one cras_apm when multiple input devices are
+ * enabled. The most common scenario is the silent input iodev be
+ * enabled when CRAS switches active input device.
+ */
+struct cras_apm_list {
+	void *stream_ptr;
+	uint64_t effects;
+	struct cras_apm *apms;
+	struct cras_apm_list *prev, *next;
+};
+
+/*
+ * Object used to analyze playback audio from output iodev. It is responsible
+ * to get buffer containing latest output data and provide it to the APM
+ * instances which want to analyze reverse stream.
+ * Member:
+ *    ext - The interface implemented to process reverse(output) stream
+ *        data in various formats.
+ *    fbuf - Middle buffer holding reverse data for APMs to analyze.
+ *    odev - Pointer to the output iodev playing audio as the reverse
+ *        stream. NULL if there's no playback stream.
+ *    dev_rate - The sample rate odev is opened for.
+ *    process_reverse - Flag to indicate if there's APM has effect that
+ *        needs to process reverse stream.
+ */
+struct cras_apm_reverse_module {
+	struct ext_dsp_module ext;
+	struct float_buffer *fbuf;
+	struct cras_iodev *odev;
+	unsigned int dev_rate;
+	unsigned process_reverse;
+};
+
+static struct cras_apm_reverse_module *rmodule = NULL;
+static struct cras_apm_list *apm_list = NULL;
+static struct aec_config *aec_config = NULL;
+static struct apm_config *apm_config = NULL;
+static const char *aec_config_dir = NULL;
+
+/* Update the global process reverse flag. Should be called when apms are added
+ * or removed. */
+static void update_process_reverse_flag()
+{
+	struct cras_apm_list *list;
+
+	if (!rmodule)
+		return;
+	rmodule->process_reverse = 0;
+	DL_FOREACH(apm_list, list) {
+		rmodule->process_reverse |=
+			!!(list->effects & APM_ECHO_CANCELLATION);
+	}
+}
+
+static void apm_destroy(struct cras_apm **apm)
+{
+	if (*apm == NULL)
+		return;
+	byte_buffer_destroy(&(*apm)->buffer);
+	float_buffer_destroy(&(*apm)->fbuffer);
+	cras_audio_area_destroy((*apm)->area);
+
+	/* Any unfinished AEC dump handle will be closed. */
+	webrtc_apm_destroy((*apm)->apm_ptr);
+	free(*apm);
+	*apm = NULL;
+}
+
+struct cras_apm_list *cras_apm_list_create(void *stream_ptr,
+					   uint64_t effects)
+{
+	struct cras_apm_list *list;
+
+	if (effects == 0)
+		return NULL;
+
+	DL_SEARCH_SCALAR(apm_list, list, stream_ptr, stream_ptr);
+	if (list)
+		return list;
+
+	list = (struct cras_apm_list *)calloc(1, sizeof(*list));
+	list->stream_ptr = stream_ptr;
+	list->effects = effects;
+	list->apms = NULL;
+	DL_APPEND(apm_list, list);
+
+	return list;
+}
+
+struct cras_apm *cras_apm_list_get(struct cras_apm_list *list, void *dev_ptr)
+{
+	struct cras_apm *apm;
+
+	if (list == NULL)
+		return NULL;
+
+	DL_FOREACH(list->apms, apm) {
+		if (apm->dev_ptr == dev_ptr)
+			return apm;
+	}
+	return NULL;
+}
+
+uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
+{
+	if (list == NULL)
+		return 0;
+	else
+		return list->effects;
+}
+
+void cras_apm_list_remove(struct cras_apm_list *list, void *dev_ptr)
+{
+	struct cras_apm *apm;
+
+	DL_FOREACH(list->apms, apm) {
+		if (apm->dev_ptr == dev_ptr ) {
+			DL_DELETE(list->apms, apm);
+			apm_destroy(&apm);
+		}
+	}
+}
+
+/*
+ * WebRTC APM handles no more than stereo + keyboard mic channels.
+ * Ignore keyboard mic feature for now because that requires processing on
+ * mixed buffer from two input devices. Based on that we should modify the best
+ * channel layout for APM use.
+ * Args:
+ *    apm_fmt - Pointer to a format struct already filled with the value of
+ *        the open device format. Its content may be modified for APM use.
+ */
+static void get_best_channels(struct cras_audio_format *apm_fmt)
+{
+	int ch;
+	int8_t layout[CRAS_CH_MAX];
+
+	/* Assume device format has correct channel layout populated. */
+	if (apm_fmt->num_channels <= 2)
+		return;
+
+	/* If the device provides recording from more channels than we care
+	 * about, construct a new channel layout containing subset of original
+	 * channels that matches either FL, FR, or FC.
+	 * TODO(hychao): extend the logic when we have a stream that wants
+	 * to record channels like RR(rear right).
+	 */
+	for (ch = 0 ; ch < CRAS_CH_MAX; ch++)
+		layout[ch] = -1;
+
+	apm_fmt->num_channels = 0;
+	if (apm_fmt->channel_layout[CRAS_CH_FL] != -1)
+		layout[CRAS_CH_FL] = apm_fmt->num_channels++;
+	if (apm_fmt->channel_layout[CRAS_CH_FR] != -1)
+		layout[CRAS_CH_FR] = apm_fmt->num_channels++;
+	if (apm_fmt->channel_layout[CRAS_CH_FC] != -1)
+		layout[CRAS_CH_FC] = apm_fmt->num_channels++;
+
+	for (ch = 0 ; ch < CRAS_CH_MAX; ch++)
+		apm_fmt->channel_layout[ch] = layout[ch];
+}
+
+struct cras_apm *cras_apm_list_add(struct cras_apm_list *list,
+				   void *dev_ptr,
+				   const struct cras_audio_format *dev_fmt)
+{
+	struct cras_apm *apm;
+
+	DL_FOREACH(list->apms, apm) {
+		if (apm->dev_ptr == dev_ptr) {
+			DL_DELETE(list->apms, apm);
+			apm_destroy(&apm);
+		}
+	}
+
+	// TODO(hychao): Remove the check when we enable more effects.
+	if (!(list->effects & APM_ECHO_CANCELLATION))
+		return NULL;
+
+	apm = (struct cras_apm *)calloc(1, sizeof(*apm));
+
+	/* Configures APM to the format used by input device. If the channel
+	 * count is larger than stereo, use the standard channel count/layout
+	 * in APM. */
+	apm->dev_fmt = *dev_fmt;
+	apm->fmt = *dev_fmt;
+	get_best_channels(&apm->fmt);
+
+	apm->apm_ptr = webrtc_apm_create(
+			apm->fmt.num_channels,
+			apm->fmt.frame_rate,
+			aec_config,
+			apm_config);
+	if (apm->apm_ptr == NULL) {
+		syslog(LOG_ERR, "Fail to create webrtc apm for ch %zu"
+				" rate %zu effect %lu",
+				dev_fmt->num_channels,
+				dev_fmt->frame_rate,
+				list->effects);
+		free(apm);
+		return NULL;
+	}
+
+	apm->dev_ptr = dev_ptr;
+	apm->work_queue = NULL;
+
+	/* WebRTC APM wants 10 ms equivalence of data to process. */
+	apm->buffer = byte_buffer_create(10 * apm->fmt.frame_rate / 1000 *
+					 cras_get_format_bytes(&apm->fmt));
+	apm->fbuffer = float_buffer_create(10 * apm->fmt.frame_rate / 1000,
+					   apm->fmt.num_channels);
+	apm->area = cras_audio_area_create(apm->fmt.num_channels);
+	cras_audio_area_config_channels(apm->area, &apm->fmt);
+
+	DL_APPEND(list->apms, apm);
+	update_process_reverse_flag();
+
+	return apm;
+}
+
+int cras_apm_list_destroy(struct cras_apm_list *list)
+{
+	struct cras_apm_list *tmp;
+	struct cras_apm *apm;
+
+	DL_FOREACH(apm_list, tmp) {
+		if (tmp == list) {
+			DL_DELETE(apm_list, tmp);
+			break;
+		}
+	}
+
+	if (tmp == NULL)
+		return 0;
+
+	DL_FOREACH(list->apms, apm) {
+		DL_DELETE(list->apms, apm);
+		apm_destroy(&apm);
+	}
+	free(list);
+
+	update_process_reverse_flag();
+
+	return 0;
+}
+
+/*
+ * Determines the iodev to be used as the echo reference for APM reverse
+ * analysis. If there exists the special purpose "echo reference dev" then
+ * use it. Otherwise just use this output iodev.
+ */
+static struct cras_iodev *get_echo_reference_target(struct cras_iodev *iodev)
+{
+	return iodev->echo_reference_dev
+			? iodev->echo_reference_dev
+			: iodev;
+}
+
+/*
+ * Updates the first enabled output iodev in the list, determine the echo
+ * reference target base on this output iodev, and register rmodule as ext dsp
+ * module to this echo reference target.
+ * When this echo reference iodev is opened and audio data flows through its
+ * dsp pipeline, APMs will anaylize the reverse stream. This is expected to be
+ * called in main thread when output devices enable/dsiable state changes.
+ */
+static void update_first_output_dev_to_process()
+{
+	struct cras_iodev *echo_ref;
+	struct cras_iodev *iodev =
+			cras_iodev_list_get_first_enabled_iodev(
+				CRAS_STREAM_OUTPUT);
+
+	if (iodev == NULL)
+		return;
+
+	echo_ref = get_echo_reference_target(iodev);
+	rmodule->odev = echo_ref;
+	cras_iodev_set_ext_dsp_module(echo_ref, &rmodule->ext);
+}
+
+static void handle_device_enabled(struct cras_iodev *iodev, void *cb_data)
+{
+	if (iodev->direction != CRAS_STREAM_OUTPUT)
+		return;
+
+	/* Register to the first enabled output device. */
+	update_first_output_dev_to_process();
+}
+
+static void handle_device_disabled(struct cras_iodev *iodev, void *cb_data)
+{
+	struct cras_iodev *echo_ref;
+
+	if (iodev->direction != CRAS_STREAM_OUTPUT)
+		return;
+
+	echo_ref = get_echo_reference_target(iodev);
+
+	if (rmodule->odev == echo_ref) {
+		cras_iodev_set_ext_dsp_module(echo_ref, NULL);
+		rmodule->odev = NULL;
+	}
+
+	/* Register to the first enabled output device. */
+	update_first_output_dev_to_process();
+}
+
+static int process_reverse(struct float_buffer *fbuf, unsigned int frame_rate)
+{
+	struct cras_apm_list *list;
+	struct cras_apm *apm;
+	int ret;
+	float *const *wp;
+
+	if (float_buffer_writable(fbuf))
+		return 0;
+
+	wp = float_buffer_write_pointer(fbuf);
+
+	DL_FOREACH(apm_list, list) {
+		if (!(list->effects & APM_ECHO_CANCELLATION))
+			continue;
+
+		DL_FOREACH(list->apms, apm) {
+			ret = webrtc_apm_process_reverse_stream_f(
+					apm->apm_ptr,
+					fbuf->num_channels,
+					frame_rate,
+					wp);
+			if (ret) {
+				syslog(LOG_ERR,
+				       "APM process reverse err");
+				return ret;
+			}
+		}
+	}
+	float_buffer_reset(fbuf);
+	return 0;
+}
+
+void reverse_data_run(struct ext_dsp_module *ext,
+		      unsigned int nframes)
+{
+	struct cras_apm_reverse_module *rmod =
+			(struct cras_apm_reverse_module *)ext;
+	unsigned int writable;
+	int i, offset = 0;
+	float *const *wp;
+
+	if (!rmod->process_reverse)
+		return;
+
+	while (nframes) {
+		process_reverse(rmod->fbuf, rmod->dev_rate);
+		writable = float_buffer_writable(rmod->fbuf);
+		writable = MIN(nframes, writable);
+		wp = float_buffer_write_pointer(rmod->fbuf);
+		for (i = 0; i < rmod->fbuf->num_channels; i++)
+			memcpy(wp[i], ext->ports[i] + offset,
+			       writable * sizeof(float));
+
+		offset += writable;
+		float_buffer_written(rmod->fbuf, writable);
+		nframes -= writable;
+	}
+}
+
+void reverse_data_configure(struct ext_dsp_module *ext,
+			    unsigned int buffer_size,
+			    unsigned int num_channels,
+			    unsigned int rate)
+{
+	struct cras_apm_reverse_module *rmod =
+			(struct cras_apm_reverse_module *)ext;
+	if (rmod->fbuf)
+		float_buffer_destroy(&rmod->fbuf);
+	rmod->fbuf = float_buffer_create(rate / 100,
+					 num_channels);
+	rmod->dev_rate = rate;
+}
+
+int cras_apm_list_init(const char *device_config_dir)
+{
+	if (rmodule == NULL) {
+		rmodule = (struct cras_apm_reverse_module *)
+				calloc(1, sizeof(*rmodule));
+		rmodule->ext.run = reverse_data_run;
+		rmodule->ext.configure = reverse_data_configure;
+	}
+
+	aec_config_dir = device_config_dir;
+	if (aec_config)
+		free(aec_config);
+	aec_config = aec_config_get(device_config_dir);
+	if (apm_config)
+		free(apm_config);
+	apm_config = apm_config_get(device_config_dir);
+
+	update_first_output_dev_to_process();
+	cras_iodev_list_set_device_enabled_callback(
+			handle_device_enabled,
+			handle_device_disabled,
+			rmodule);
+
+	return 0;
+}
+
+void cras_apm_list_reload_aec_config()
+{
+	if (NULL == aec_config_dir)
+		return;
+
+	if (aec_config)
+		free(aec_config);
+	aec_config = aec_config_get(aec_config_dir);
+
+	/* Dump the config content at reload only, for debug. */
+	if (aec_config)
+		aec_config_dump(aec_config);
+
+	if (apm_config)
+		free(apm_config);
+	apm_config = apm_config_get(aec_config_dir);
+
+	/* Dump the config content at reload only, for debug. */
+	if (apm_config)
+		apm_config_dump(apm_config);
+}
+
+int cras_apm_list_deinit()
+{
+	if (rmodule) {
+		if (rmodule->fbuf)
+			float_buffer_destroy(&rmodule->fbuf);
+		free(rmodule);
+	}
+	return 0;
+}
+
+int cras_apm_list_process(struct cras_apm *apm,
+			  struct float_buffer *input,
+			  unsigned int offset)
+{
+	unsigned int writable, nframes, nread;
+	int ch, i, j, ret;
+	float *const *wp;
+	float *const *rp;
+
+	nread = float_buffer_level(input);
+	if (nread < offset) {
+		syslog(LOG_ERR, "Process offset exceeds read level");
+		return -EINVAL;
+	}
+
+	writable = float_buffer_writable(apm->fbuffer);
+	writable = MIN(nread - offset, writable);
+
+	nframes = writable;
+	while (nframes) {
+		nread = nframes;
+		wp = float_buffer_write_pointer(apm->fbuffer);
+		rp = float_buffer_read_pointer(input, offset, &nread);
+
+		for (i = 0; i < apm->fbuffer->num_channels; i++) {
+			/* Look up the channel position and copy from
+			 * the correct index of |input| buffer.
+			 */
+			for (ch = 0; ch < CRAS_CH_MAX; ch++)
+				if (apm->fmt.channel_layout[ch] == i)
+					break;
+			if (ch == CRAS_CH_MAX)
+				continue;
+
+			j = apm->dev_fmt.channel_layout[ch];
+			if (j == -1)
+				continue;
+
+			memcpy(wp[i], rp[j], nread * sizeof(float));
+		}
+
+		nframes -= nread;
+		offset += nread;
+
+		float_buffer_written(apm->fbuffer, nread);
+	}
+
+	/* process and move to int buffer */
+	if ((float_buffer_writable(apm->fbuffer) == 0) &&
+            (buf_queued(apm->buffer) == 0)) {
+		nread = float_buffer_level(apm->fbuffer);
+		rp = float_buffer_read_pointer(apm->fbuffer, 0, &nread);
+		ret = webrtc_apm_process_stream_f(apm->apm_ptr,
+						  apm->fmt.num_channels,
+						  apm->fmt.frame_rate,
+						  rp);
+		if (ret) {
+			syslog(LOG_ERR, "APM process stream f err");
+			return ret;
+		}
+
+		dsp_util_interleave(rp,
+				    buf_write_pointer(apm->buffer),
+				    apm->fbuffer->num_channels,
+				    apm->fmt.format,
+				    nread);
+		buf_increment_write(apm->buffer,
+				    nread * cras_get_format_bytes(&apm->fmt));
+		float_buffer_reset(apm->fbuffer);
+	}
+
+	return writable;
+}
+
+struct cras_audio_area *cras_apm_list_get_processed(struct cras_apm *apm)
+{
+	uint8_t *buf_ptr;
+
+	buf_ptr = buf_read_pointer_size(apm->buffer, &apm->area->frames);
+	apm->area->frames /= cras_get_format_bytes(&apm->fmt);
+	cras_audio_area_config_buf_pointers(apm->area, &apm->fmt, buf_ptr);
+	return apm->area;
+}
+
+void cras_apm_list_put_processed(struct cras_apm *apm, unsigned int frames)
+{
+	buf_increment_read(apm->buffer,
+			   frames * cras_get_format_bytes(&apm->fmt));
+}
+
+struct cras_audio_format *cras_apm_list_get_format(struct cras_apm *apm)
+{
+	return &apm->fmt;
+}
+
+void cras_apm_list_set_aec_dump(struct cras_apm_list *list, void *dev_ptr,
+				int start, int fd)
+{
+	struct cras_apm *apm;
+	char file_name[256];
+	int rc;
+	FILE *handle;
+
+	DL_SEARCH_SCALAR(list->apms, apm, dev_ptr, dev_ptr);
+	if (apm == NULL)
+		return;
+
+	if (start) {
+		handle = fdopen(fd, "w");
+		if (handle == NULL) {
+			syslog(LOG_ERR, "Create dump handle fail, errno %d",
+			       errno);
+			return ;
+		}
+		/* webrtc apm will own the FILE handle and close it. */
+		rc = webrtc_apm_aec_dump(apm->apm_ptr, &apm->work_queue, start,
+					 handle);
+		if (rc)
+			syslog(LOG_ERR, "Fail to dump debug file %s, rc %d",
+			       file_name, rc);
+	} else {
+		rc = webrtc_apm_aec_dump(apm->apm_ptr, &apm->work_queue, 0,
+					 NULL);
+		if (rc)
+			syslog(LOG_ERR, "Failed to stop apm debug, rc %d", rc);
+	}
+}
diff --git a/cras/src/server/cras_apm_list.h b/cras/src/server/cras_apm_list.h
new file mode 100644
index 0000000..c7450d3
--- /dev/null
+++ b/cras/src/server/cras_apm_list.h
@@ -0,0 +1,201 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CRAS_APM_LIST_H_
+#define CRAS_APM_LIST_H_
+
+#include "cras_types.h"
+
+struct cras_audio_area;
+struct cras_audio_format;
+struct cras_apm;
+struct cras_apm_list;
+struct float_buffer;
+
+#ifdef HAVE_WEBRTC_APM
+
+/* Initialize the apm list for analyzing output data. */
+int cras_apm_list_init(const char *device_config_dir);
+
+/* Reloads the aec config. Used for debug and tuning. */
+void cras_apm_list_reload_aec_config();
+
+/* Deinitialize apm list to free all allocated resources. */
+int cras_apm_list_deinit();
+
+/*
+ * Creates an list to hold all APM instances created when a stream
+ * attaches to an iodev.
+ * Args:
+ *    stream_ptr - Pointer to the stream.
+ *    effects - Bit map specifying the enabled effects on this stream.
+ */
+struct cras_apm_list *cras_apm_list_create(void *stream_ptr,
+					   uint64_t effects);
+
+/*
+ * Creates a cras_apm and adds it to the list.
+ * Args:
+ *    list - The list holding APM instances.
+ *    dev_ptr - Pointer to the iodev to add new APM for.
+ *    fmt - Format of the audio data used for this cras_apm.
+ */
+struct cras_apm *cras_apm_list_add(struct cras_apm_list *list,
+				   void *dev_ptr,
+				   const struct cras_audio_format *fmt);
+
+/*
+ * Gets the cras_apm instance in the list that associates with given dev.
+ * Args:
+ *    list - The list holding APM instances.
+ *    dev_ptr - The iodev as key to look up associated APM.
+ */
+struct cras_apm *cras_apm_list_get(struct cras_apm_list *list,
+				   void *dev_ptr);
+
+/*
+ * Gets the effects bit map of the APM list.
+ * Args:
+ *    list - The list holding APM instances.
+ */
+uint64_t cras_apm_list_get_effects(struct cras_apm_list *list);
+
+/* Removes a cras_apm from list and destroys it. */
+int cras_apm_list_destroy(struct cras_apm_list *list);
+
+/*
+ * Removes an APM from the list, expected to be used when an iodev is no
+ * longer open for the client stream holding the APM list.
+ * Args:
+ *    list - The list holding APM instances.
+ *    dev_ptr - Device pointer used to look up which apm to remove.
+ */
+void cras_apm_list_remove(struct cras_apm_list *list, void *dev_ptr);
+
+/* Passes audio data from hardware for cras_apm to process.
+ * Args:
+ *    apm - The cras_apm instance.
+ *    input - Float buffer from device for apm to process.
+ *    offset - Offset in |input| to note the data position to start
+ *        reading.
+ */
+int cras_apm_list_process(struct cras_apm *apm,
+			  struct float_buffer *input,
+			  unsigned int offset);
+
+/* Gets the APM processed data in the form of audio area.
+ * Args:
+ *    apm - The cras_apm instance that owns the audio area pointer and
+ *        processed data.
+ * Returns:
+ *    The audio area used to read processed data. No need to free
+ *    by caller.
+ */
+struct cras_audio_area *cras_apm_list_get_processed(struct cras_apm *apm);
+
+/* Tells |apm| that |frames| of processed data has been used, so |apm|
+ * can allocate space to read more from input device.
+ * Args:
+ *    apm - The cras_apm instance owns the processed data.
+ *    frames - The number in frames of processed data to mark as used.
+ */
+void cras_apm_list_put_processed(struct cras_apm *apm, unsigned int frames);
+
+/* Gets the format of the actual data processed by webrtc-apm library.
+ * Args:
+ *    apm - The cras_apm instance holding audio data and format info.
+ */
+struct cras_audio_format *cras_apm_list_get_format(struct cras_apm *apm);
+
+/* Sets debug recording to start or stop.
+ * Args:
+ *    list - List contains the apm instance to start/stop debug recording.
+ *    dev_ptr - Use as key to look up specific apm to do aec dump.
+ *    start - True to set debug recording start, otherwise stop.
+ *    fd - File descriptor to aec dump destination.
+ */
+void cras_apm_list_set_aec_dump(struct cras_apm_list *list,
+				void *dev_ptr,
+			        int start,
+			        int fd);
+
+#else
+
+/*
+ * If webrtc audio processing library is not available then define all
+ * cras_apm_list functions as dummy. As long as cras_apm_list_add returns
+ * NULL, non of the other functions should be called.
+ */
+static inline int cras_apm_list_init(const char *device_config_dir)
+{
+	return 0;
+}
+static inline void cras_apm_list_reload_aec_config()
+{
+}
+static inline struct cras_apm_list *cras_apm_list_create(void *stream_ptr,
+							 unsigned int effects)
+{
+	return NULL;
+}
+static inline struct cras_apm *cras_apm_list_add(
+		struct cras_apm_list *list,
+		void *dev_ptr,
+		const struct cras_audio_format *fmt)
+{
+	return NULL;
+}
+static inline struct cras_apm *cras_apm_list_get(struct cras_apm_list *list,
+						 void *dev_ptr)
+{
+	return NULL;
+}
+static inline uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
+{
+	return 0;
+}
+static inline int cras_apm_list_destroy(struct cras_apm_list *list)
+{
+	return 0;
+}
+static inline void cras_apm_list_remove(struct cras_apm_list *list,
+					void *dev_ptr)
+{
+}
+
+static inline int cras_apm_list_process(struct cras_apm *apm,
+					struct float_buffer *input,
+					unsigned int offset)
+{
+	return 0;
+}
+
+static inline struct cras_audio_area *cras_apm_list_get_processed(
+		struct cras_apm *apm)
+{
+	return NULL;
+}
+
+static inline void cras_apm_list_put_processed(struct cras_apm *apm,
+					       unsigned int frames)
+{
+}
+
+static inline struct cras_audio_format *cras_apm_list_get_format(
+		struct cras_apm *apm)
+{
+	return NULL;
+}
+
+static inline void cras_apm_list_set_aec_dump(struct cras_apm_list *list,
+					      void *dev_ptr,
+					      int start,
+					      int fd)
+{
+}
+
+#endif /* HAVE_WEBRTC_APM */
+
+#endif /* CRAS_APM_LIST_H_ */
diff --git a/cras/src/server/cras_audio_thread_monitor.c b/cras/src/server/cras_audio_thread_monitor.c
new file mode 100644
index 0000000..9a23b00
--- /dev/null
+++ b/cras/src/server/cras_audio_thread_monitor.c
@@ -0,0 +1,128 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdbool.h>
+#include <syslog.h>
+#include "audio_thread.h"
+#include "cras_iodev_list.h"
+#include "cras_main_message.h"
+#include "cras_system_state.h"
+#include "cras_types.h"
+#include "cras_util.h"
+
+#define MIN_WAIT_SECOND 30
+
+struct cras_audio_thread_event_message {
+	struct cras_main_message header;
+	enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type;
+};
+
+static void take_snapshot(enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type)
+{
+	struct cras_audio_thread_snapshot *snapshot;
+
+	snapshot = (struct cras_audio_thread_snapshot*)
+		calloc(1, sizeof(struct cras_audio_thread_snapshot));
+	struct timespec now_time;
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now_time);
+	snapshot->timestamp = now_time;
+	snapshot->event_type = event_type;
+	audio_thread_dump_thread_info(cras_iodev_list_get_audio_thread(),
+				      &snapshot->audio_debug_info);
+	cras_system_state_add_snapshot(snapshot);
+}
+
+static void cras_audio_thread_event_message_init(
+		struct cras_audio_thread_event_message *msg,
+		enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type)
+{
+	msg->header.type = CRAS_MAIN_AUDIO_THREAD_EVENT;
+	msg->header.length = sizeof(*msg);
+	msg->event_type = event_type;
+}
+
+int cras_audio_thread_event_send(enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type)
+{
+	struct cras_audio_thread_event_message msg;
+	int err;
+	cras_audio_thread_event_message_init(&msg, event_type);
+	err = cras_main_message_send(&msg.header);
+	if (err < 0) {
+		syslog(LOG_ERR, "Failed to send audio thread event message %d",
+		       event_type);
+		return err;
+	}
+	return 0;
+}
+
+int cras_audio_thread_debug()
+{
+	return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_DEBUG);
+}
+
+int cras_audio_thread_busyloop()
+{
+	return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_BUSYLOOP);
+}
+
+int cras_audio_thread_underrun()
+{
+	return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_UNDERRUN);
+}
+
+int cras_audio_thread_severe_underrun()
+{
+	return cras_audio_thread_event_send(
+		AUDIO_THREAD_EVENT_SEVERE_UNDERRUN);
+}
+
+static struct timespec last_event_snapshot_time[AUDIO_THREAD_EVENT_TYPE_COUNT];
+
+/*
+ * Callback function for handling audio thread events in main thread,
+ * which takes a snapshot of the audio thread and waits at least 30 seconds
+ * for the same event type. Events with the same event type within 30 seconds
+ * will be ignored by the handle function.
+ */
+static void handle_audio_thread_event_message(
+		struct cras_main_message *msg,
+		void *arg) {
+	struct cras_audio_thread_event_message *audio_thread_msg =
+		(struct cras_audio_thread_event_message *)msg;
+	struct timespec now_time;
+
+	/*
+	 * Skip invalid event types
+	 */
+	if(audio_thread_msg->event_type >= AUDIO_THREAD_EVENT_TYPE_COUNT)
+		return;
+
+	struct timespec *last_snapshot_time =
+		&last_event_snapshot_time[audio_thread_msg->event_type];
+
+	clock_gettime(CLOCK_REALTIME, &now_time);
+
+	/*
+	 * Wait at least 30 seconds for the same event type
+	 */
+	struct timespec diff_time;
+	subtract_timespecs(&now_time, last_snapshot_time, &diff_time);
+	if((last_snapshot_time->tv_sec == 0 &&
+	    last_snapshot_time->tv_nsec == 0) ||
+	   diff_time.tv_sec >= MIN_WAIT_SECOND)
+	{
+		take_snapshot(audio_thread_msg->event_type);
+		*last_snapshot_time = now_time;
+	}
+}
+
+int cras_audio_thread_monitor_init()
+{
+	memset(last_event_snapshot_time,
+	       0, sizeof(struct timespec) * AUDIO_THREAD_EVENT_TYPE_COUNT);
+	cras_main_message_add_handler(CRAS_MAIN_AUDIO_THREAD_EVENT,
+				      handle_audio_thread_event_message, NULL);
+	return 0;
+}
diff --git a/cras/src/server/cras_audio_thread_monitor.h b/cras/src/server/cras_audio_thread_monitor.h
new file mode 100644
index 0000000..f2044bf
--- /dev/null
+++ b/cras/src/server/cras_audio_thread_monitor.h
@@ -0,0 +1,34 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CRAS_AUDIO_THREAD_MONITOR_H_
+#define CRAS_AUDIO_THREAD_MONITOR_H_
+
+/*
+ * Sends a debug event to the audio thread for debugging.
+ */
+int cras_audio_thread_debug();
+
+/*
+ * Notifies the main thread when a busyloop event happens.
+ */
+int cras_audio_thread_busyloop();
+
+/*
+ * Notifies the main thread when a underrun event happens.
+ */
+int cras_audio_thread_underrun();
+
+/*
+ * Notifies the main thread when a severe underrun event happens.
+ */
+int cras_audio_thread_severe_underrun();
+
+/*
+ * Initializes audio thread monitor and sets main thread callback.
+ */
+int cras_audio_thread_monitor_init();
+
+#endif /* CRAS_AUDIO_THREAD_MONITOR_H_ */
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 682edba..f993626 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -11,6 +11,7 @@
 
 #include <errno.h>
 #include <poll.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -1015,6 +1016,18 @@
 	iodev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
 	if (!iodev)
 		return;
+
+	/*
+	 * During the |PROFILE_SWITCH_DELAY_MS| time interval, BT iodev could
+	 * have been enabled by others, and its active profile may have changed.
+	 * If iodev has been enabled, that means it has already picked up a
+	 * reasonable profile to use and audio thread is accessing iodev now.
+	 * We should NOT call into update_active_node from main thread
+	 * because that may mess up the active node content.
+	 */
+	if (cras_iodev_list_dev_is_enabled(iodev))
+		return;
+
 	iodev->update_active_node(iodev, 0, 1);
 	cras_iodev_list_enable_dev(iodev);
 }
@@ -1053,7 +1066,7 @@
 		if (!iodev)
 			continue;
 		was_enabled[dir] = cras_iodev_list_dev_is_enabled(iodev);
-		cras_iodev_list_disable_dev(iodev);
+		cras_iodev_list_disable_dev(iodev, false);
 	}
 
 	for (dir = 0; dir < CRAS_NUM_DIRECTIONS; dir++) {
@@ -1116,6 +1129,16 @@
 static void bt_device_process_msg(struct cras_main_message *msg, void *arg)
 {
 	struct bt_device_msg *bt_msg = (struct bt_device_msg *)msg;
+	struct cras_bt_device *device = NULL;
+
+	DL_FOREACH(devices, device) {
+		if (device == bt_msg->device)
+			break;
+	}
+
+	/* Do nothing if target device no longer exists. */
+	if (device == NULL)
+		return;
 
 	switch (bt_msg->cmd) {
 	case BT_DEVICE_SWITCH_PROFILE:
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 1363d90..84ac5ca 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -3,6 +3,7 @@
  * found in the LICENSE file.
  */
 
+#include <sys/time.h>
 #include <syslog.h>
 
 #include "cras_bt_io.h"
@@ -112,20 +113,17 @@
 	return cras_bt_device_get_active_profile(device) & profile;
 }
 
-/* Checks if the condition is met to switch to a different profile
- * when trying to set the format to btio before open it. Base on two
- * rules:
+/* Checks if the condition is met to switch to a different profile based
+ * on two rules:
  * (1) Prefer to use A2DP for output since the audio quality is better.
  * (2) Must use HFP/HSP for input since A2DP doesn't support audio input.
  *
  * If the profile switch happens, return non-zero error code, otherwise
  * return zero.
  */
-static int update_supported_formats(struct cras_iodev *iodev)
+static int open_dev(struct cras_iodev *iodev)
 {
 	struct bt_io *btio = (struct bt_io *)iodev;
-	struct cras_iodev *dev = active_profile_dev(iodev);
-	int rc, length, i;
 
 	/* Force to use HFP if opening input dev. */
 	if (device_using_profile(btio->device,
@@ -137,6 +135,14 @@
 		return -EAGAIN;
 	}
 
+	return 0;
+}
+
+static int update_supported_formats(struct cras_iodev *iodev)
+{
+	struct cras_iodev *dev = active_profile_dev(iodev);
+	int rc, length, i;
+
 	if (dev->format == NULL) {
 		dev->format = (struct cras_audio_format *)
 				malloc(sizeof(*dev->format));
@@ -173,7 +179,7 @@
 	return 0;
 }
 
-static int open_dev(struct cras_iodev *iodev)
+static int configure_dev(struct cras_iodev *iodev)
 {
 	int rc;
 	struct cras_iodev *dev = active_profile_dev(iodev);
@@ -183,7 +189,7 @@
 	/* Fill back the format iodev is using. */
 	*dev->format = *iodev->format;
 
-	rc = dev->open_dev(dev);
+	rc = dev->configure_dev(dev);
 	if (rc) {
 		/* Free format here to assure the update_supported_format
 		 * callback will be called before any future open_dev call. */
@@ -332,6 +338,7 @@
 	iodev->info.stable_id_new = dev->info.stable_id_new;
 
 	iodev->open_dev = open_dev;
+	iodev->configure_dev = configure_dev;
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
 	iodev->get_buffer = get_buffer;
@@ -340,10 +347,19 @@
 	iodev->close_dev = close_dev;
 	iodev->update_supported_formats = update_supported_formats;
 	iodev->update_active_node = update_active_node;
-	iodev->software_volume_needed = 1;
-	iodev->set_volume = set_bt_volume;
 	iodev->no_stream = cras_iodev_default_no_stream_playback;
 
+	/* Input also checks |software_volume_needed| flag for using software
+	 * gain. Keep it as false for BT input.
+	 * TODO(hychao): after wide band speech mode is supported, consider
+	 * enable software gain.
+	 */
+	if (dev->direction == CRAS_STREAM_OUTPUT) {
+		iodev->software_volume_needed =
+				!cras_bt_device_get_use_hardware_volume(device);
+		iodev->set_volume = set_bt_volume;
+	}
+
 	/* Create the dummy node set to plugged so it's the only node exposed
 	 * to UI, and point it to the first profile dev. */
 	active = (struct bt_node *)calloc(1, sizeof(*active));
@@ -394,12 +410,28 @@
 	return NULL;
 }
 
+void cras_bt_io_free_resources(struct cras_iodev *bt_iodev)
+{
+	struct cras_ionode *node;
+	struct bt_node *n;
+
+	free(bt_iodev->supported_rates);
+	free(bt_iodev->supported_channel_counts);
+	free(bt_iodev->supported_formats);
+
+	DL_FOREACH(bt_iodev->nodes, node) {
+		n = (struct bt_node *)node;
+		cras_iodev_rm_node(bt_iodev, node);
+		free(n);
+	}
+
+	cras_iodev_free_resources(bt_iodev);
+}
+
 void cras_bt_io_destroy(struct cras_iodev *bt_iodev)
 {
 	int rc;
 	struct bt_io *btio = (struct bt_io *)bt_iodev;
-	struct cras_ionode *node;
-	struct bt_node *n;
 
 	if (bt_iodev->direction == CRAS_STREAM_OUTPUT)
 		rc = cras_iodev_list_rm_output(bt_iodev);
@@ -408,11 +440,7 @@
 	if (rc == -EBUSY)
 		return;
 
-	DL_FOREACH(bt_iodev->nodes, node) {
-		n = (struct bt_node *)node;
-		cras_iodev_rm_node(bt_iodev, node);
-		free(n);
-	}
+	cras_bt_io_free_resources(bt_iodev);
 	free(btio);
 }
 
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 2100224..56effce 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -63,9 +63,15 @@
     "      <arg name=\"input_mute\" type=\"b\" direction=\"out\"/>\n"   \
     "      <arg name=\"output_user_mute\" type=\"b\" direction=\"out\"/>\n"\
     "    </method>\n"                                                   \
+    "    <method name=\"GetDefaultOutputBufferSize\">\n"                    \
+    "      <arg name=\"buffer_size\" type=\"i\" direction=\"out\"/>\n"  \
+    "    </method>\n"                                                   \
     "    <method name=\"GetNodes\">\n"                                  \
     "      <arg name=\"nodes\" type=\"a{sv}\" direction=\"out\"/>\n"    \
     "    </method>\n"                                                   \
+    "    <method name=\"GetSystemAecSupported\">\n"                     \
+    "      <arg name=\"supported\" type=\"b\" direction=\"out\"/>\n"    \
+    "    </method>\n"                                                   \
     "    <method name=\"SetActiveOutputNode\">\n"                       \
     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
     "    </method>\n"                                                   \
@@ -101,6 +107,9 @@
     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
     "      <arg name=\"model_name\" type=\"s\" direction=\"in\"/>\n"    \
     "    </method>\n"                                                   \
+    "    <method name=\"IsAudioOutputActive\">\n"                       \
+    "      <arg name=\"active\" type=\"b\" direction=\"out\"/>\n"       \
+    "    </method>\n"                                                   \
     "  </interface>\n"                                                  \
     "  <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"        \
     "    <method name=\"Introspect\">\n"                                \
@@ -408,6 +417,29 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static DBusHandlerResult handle_get_default_output_buffer_size(
+	DBusConnection *conn,
+	DBusMessage *message,
+	void *arg)
+{
+	DBusMessage *reply;
+	dbus_uint32_t serial = 0;
+	dbus_int32_t buffer_size;
+
+	reply = dbus_message_new_method_return(message);
+
+	buffer_size = cras_system_get_default_output_buffer_size();
+	dbus_message_append_args(reply,
+				 DBUS_TYPE_INT32, &buffer_size,
+				 DBUS_TYPE_INVALID);
+
+	dbus_connection_send(conn, reply, &serial);
+
+	dbus_message_unref(reply);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 /* Appends the information about a node to the dbus message. Returns
  * false if not enough memory. */
 static dbus_bool_t append_node_dict(DBusMessageIter *iter,
@@ -547,6 +579,29 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static DBusHandlerResult handle_get_system_aec_supported(
+	DBusConnection *conn,
+	DBusMessage *message,
+	void *arg)
+{
+	DBusMessage *reply;
+	dbus_uint32_t serial = 0;
+	dbus_bool_t system_aec_supported;
+
+	reply = dbus_message_new_method_return(message);
+
+	system_aec_supported = cras_system_get_aec_supported();
+	dbus_message_append_args(reply,
+				 DBUS_TYPE_BOOLEAN, &system_aec_supported,
+				 DBUS_TYPE_INVALID);
+
+	dbus_connection_send(conn, reply, &serial);
+
+	dbus_message_unref(reply);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 static DBusHandlerResult
 handle_set_active_node(DBusConnection *conn,
 		       DBusMessage *message,
@@ -721,6 +776,18 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static DBusHandlerResult handle_is_audio_active(
+		DBusConnection *conn,
+		DBusMessage *message,
+		void* arg)
+{
+	dbus_int32_t active = cras_system_state_get_non_empty_status();
+
+	send_int32_reply(conn, message, active);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 /* Handle incoming messages. */
 static DBusHandlerResult handle_control_message(DBusConnection *conn,
 						DBusMessage *message,
@@ -792,10 +859,18 @@
 		return handle_get_volume_state(conn, message, arg);
 	} else if (dbus_message_is_method_call(message,
 					       CRAS_CONTROL_INTERFACE,
+					       "GetDefaultOutputBufferSize")) {
+		return handle_get_default_output_buffer_size(conn, message, arg);
+	} else if (dbus_message_is_method_call(message,
+					       CRAS_CONTROL_INTERFACE,
 					       "GetNodes")) {
 		return handle_get_nodes(conn, message, arg);
 	} else if (dbus_message_is_method_call(message,
 					       CRAS_CONTROL_INTERFACE,
+					       "GetSystemAecSupported")) {
+		return handle_get_system_aec_supported(conn, message, arg);
+	} else if (dbus_message_is_method_call(message,
+					       CRAS_CONTROL_INTERFACE,
 					       "SetActiveOutputNode")) {
 		return handle_set_active_node(conn, message, arg,
 					      CRAS_STREAM_OUTPUT);
@@ -847,9 +922,12 @@
 					       CRAS_CONTROL_INTERFACE,
 					       "SetHotwordModel")) {
 		return handle_set_hotword_model(conn, message, arg);
+	} else if (dbus_message_is_method_call(message,
+					       CRAS_CONTROL_INTERFACE,
+					       "IsAudioOutputActive")) {
+		return handle_is_audio_active(conn, message, arg);
 	}
 
-
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
@@ -1057,6 +1135,45 @@
 	dbus_message_unref(msg);
 }
 
+static void signal_hotword_triggered(void *context,
+				     int64_t tv_sec,
+				     int64_t tv_nsec)
+{
+	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
+	dbus_uint32_t serial = 0;
+	DBusMessage *msg;
+
+	msg = create_dbus_message("HotwordTriggered");
+	if (!msg)
+		return;
+
+	dbus_message_append_args(msg,
+				 DBUS_TYPE_INT64, &tv_sec,
+				 DBUS_TYPE_INT64, &tv_nsec,
+				 DBUS_TYPE_INVALID);
+	dbus_connection_send(control->conn, msg, &serial);
+	dbus_message_unref(msg);
+}
+
+static void signal_non_empty_audio_state_changed(void *context, int non_empty)
+{
+	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
+
+	dbus_uint32_t serial = 0;
+	DBusMessage *msg;
+
+	msg = create_dbus_message("AudioOutputActiveStateChanged");
+	if (!msg)
+		return;
+
+	dbus_message_append_args(msg,
+				 DBUS_TYPE_BOOLEAN, &non_empty,
+				 DBUS_TYPE_INVALID);
+
+	dbus_connection_send(control->conn, msg, &serial);
+	dbus_message_unref(msg);
+}
+
 /* Exported Interface */
 
 void cras_dbus_control_start(DBusConnection *conn)
@@ -1095,6 +1212,9 @@
 	observer_ops.output_node_volume_changed = signal_node_volume_changed;
 	observer_ops.node_left_right_swapped_changed =
 			signal_node_left_right_swapped_changed;
+	observer_ops.hotword_triggered = signal_hotword_triggered;
+	observer_ops.non_empty_audio_state_changed =
+			signal_non_empty_audio_state_changed;
 
 	dbus_control.observer = cras_observer_add(&observer_ops, &dbus_control);
 }
diff --git a/cras/src/server/cras_device_monitor.c b/cras/src/server/cras_device_monitor.c
index be9d086..7dd8f27 100644
--- a/cras/src/server/cras_device_monitor.c
+++ b/cras/src/server/cras_device_monitor.c
@@ -3,6 +3,7 @@
  * found in the LICENSE file.
  */
 
+#include <stdbool.h>
 #include <syslog.h>
 
 #include "cras_device_monitor.h"
@@ -80,7 +81,7 @@
 	case RESET_DEVICE:
 		syslog(LOG_ERR, "trying to recover device 0x%x by resetting it",
 		       iodev->info.idx);
-		cras_iodev_list_disable_dev(iodev);
+		cras_iodev_list_disable_dev(iodev, true);
 		cras_iodev_list_enable_dev(iodev);
 		break;
 	case SET_MUTE_STATE:
diff --git a/cras/src/server/cras_dsp.c b/cras/src/server/cras_dsp.c
index 8052a04..97e3a85 100644
--- a/cras/src/server/cras_dsp.c
+++ b/cras/src/server/cras_dsp.c
@@ -48,15 +48,14 @@
 	cras_expr_env_set_variable_boolean(env, "swap_lr_disabled", 1);
 }
 
-static struct pipeline *prepare_pipeline(struct cras_dsp_context *ctx)
+static struct pipeline *prepare_pipeline(
+		struct cras_dsp_context *ctx,
+		struct ini *target_ini)
 {
 	struct pipeline *pipeline;
 	const char *purpose = ctx->purpose;
 
-	if (!ini)
-		return NULL;
-
-	pipeline = cras_dsp_pipeline_create(ini, &ctx->env, purpose);
+	pipeline = cras_dsp_pipeline_create(target_ini, &ctx->env, purpose);
 
 	if (pipeline) {
 		syslog(LOG_DEBUG, "pipeline created");
@@ -90,11 +89,12 @@
 	return NULL;
 }
 
-static void cmd_load_pipeline(struct cras_dsp_context *ctx)
+static void cmd_load_pipeline(struct cras_dsp_context *ctx,
+			      struct ini *target_ini)
 {
 	struct pipeline *pipeline, *old_pipeline;
 
-	pipeline = prepare_pipeline(ctx);
+	pipeline = target_ini ? prepare_pipeline(ctx, target_ini) : NULL;
 
 	/* This locking is short to avoild blocking audio thread. */
 	pthread_mutex_lock(&ctx->mutex);
@@ -112,11 +112,14 @@
 	struct cras_dsp_context *ctx;
 
 	ini = cras_dsp_ini_create(ini_filename);
-	if (!ini)
+	if (!ini) {
 		syslog(LOG_ERR, "cannot create dsp ini");
+		return;
+	}
+
 
 	DL_FOREACH(context_list, ctx) {
-		cmd_load_pipeline(ctx);
+		cmd_load_pipeline(ctx, ini);
 	}
 
 	if (old_ini)
@@ -186,7 +189,16 @@
 
 void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
 {
-	cmd_load_pipeline(ctx);
+	cmd_load_pipeline(ctx, ini);
+}
+
+void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx,
+				  unsigned int num_channels)
+{
+	struct ini *dummy_ini;
+	dummy_ini = create_dummy_ini(ctx->purpose, num_channels);
+	cmd_load_pipeline(ctx, dummy_ini);
+	cras_dsp_ini_free((dummy_ini));
 }
 
 struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx)
diff --git a/cras/src/server/cras_dsp.h b/cras/src/server/cras_dsp.h
index 18c45f7..6c7af18 100644
--- a/cras/src/server/cras_dsp.h
+++ b/cras/src/server/cras_dsp.h
@@ -54,6 +54,12 @@
  * blocking the audio thread. */
 void cras_dsp_load_pipeline(struct cras_dsp_context *ctx);
 
+/* Loads a dummy pipeline of source directly connects to sink, of given
+ * number of channels.
+ */
+void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx,
+				  unsigned int num_channels);
+
 /* Locks the pipeline in the context for access. Returns NULL if the
  * pipeline is still being loaded or cannot be loaded. */
 struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx);
diff --git a/cras/src/server/cras_dsp_ini.c b/cras/src/server/cras_dsp_ini.c
index a4014c8..a8b3a9c 100644
--- a/cras/src/server/cras_dsp_ini.c
+++ b/cras/src/server/cras_dsp_ini.c
@@ -7,10 +7,12 @@
 #include <stdlib.h>
 #include <syslog.h>
 #include "cras_dsp_ini.h"
+#include "iniparser_wrapper.h"
 
 #define MAX_INI_KEY_LENGTH 64  /* names like "output_source:output_0" */
 #define MAX_NR_PORT 128	/* the max number of ports for a plugin */
 #define MAX_PORT_NAME_LENGTH 20 /* names like "output_32" */
+#define MAX_DUMMY_INI_CH 8 /* Max number of channels to create dummy ini */
 
 /* Format of the ini file (See dsp.ini.sample for an example).
 
@@ -312,6 +314,55 @@
 	return 0;
 }
 
+struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels)
+{
+	static char dummy_flow_names[MAX_DUMMY_INI_CH][8] = {
+		"{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}",
+		"{tmp:4}", "{tmp:5}", "{tmp:6}", "{tmp:7}",
+	};
+	struct ini *ini;
+	struct plugin *source, *sink;
+	int tmp_flow_ids[MAX_DUMMY_INI_CH];
+	int i;
+
+	if (num_channels > MAX_DUMMY_INI_CH) {
+		syslog(LOG_ERR, "Unable to create %u channels of dummy ini",
+		       num_channels);
+		return NULL;
+	}
+
+	ini = calloc(1, sizeof(struct ini));
+	if (!ini) {
+		syslog(LOG_ERR, "no memory for ini struct");
+		return NULL;
+	}
+
+	for (i = 0; i < num_channels; i++)
+		tmp_flow_ids[i] = add_new_flow(ini, dummy_flow_names[i]);
+
+	source = ARRAY_APPEND_ZERO(&ini->plugins);
+	source->title = "source";
+	source->library = "builtin";
+	source->label = "source";
+	source->purpose = purpose;
+
+	for (i = 0; i < num_channels; i++)
+		add_audio_port(ini, source, tmp_flow_ids[i], PORT_OUTPUT);
+
+	sink = ARRAY_APPEND_ZERO(&ini->plugins);
+	sink->title = "sink";
+	sink->library = "builtin";
+	sink->label = "sink";
+	sink->purpose = purpose;
+
+	for (i = 0; i < num_channels; i++)
+		add_audio_port(ini, sink, tmp_flow_ids[i], PORT_INPUT);
+
+	fill_flow_info(ini);
+
+	return ini;
+}
+
 struct ini *cras_dsp_ini_create(const char *ini_filename)
 {
 	struct ini *ini;
@@ -327,7 +378,7 @@
 		return NULL;
 	}
 
-	dict = iniparser_load((char *)ini_filename);
+	dict = iniparser_load_wrapper((char *)ini_filename);
 	if (!dict) {
 		syslog(LOG_ERR, "no ini file %s", ini_filename);
 		goto bail;
diff --git a/cras/src/server/cras_dsp_ini.h b/cras/src/server/cras_dsp_ini.h
index d90a87c..0fdd69f 100644
--- a/cras/src/server/cras_dsp_ini.h
+++ b/cras/src/server/cras_dsp_ini.h
@@ -70,6 +70,24 @@
 	flow_array flows;
 };
 
+/*
+ * Creates a dummy ini structure equivalent to:
+ *
+ * [src]
+ * out0={tmp:0}
+ * out1={tmp:1}
+ * ...
+ *
+ * [sink]
+ * in0={tmp:0}
+ * in1={tmp:1}
+ * ...
+ *
+ * The caller of this function is responsible to free the returned
+ * ini by calling cras_dsp_ini_free().
+ */
+struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels);
+
 /* Reads the ini file into the ini structure */
 struct ini *cras_dsp_ini_create(const char *ini_filename);
 /* Frees the dsp structure. */
diff --git a/cras/src/server/cras_dsp_mod_builtin.c b/cras/src/server/cras_dsp_mod_builtin.c
index c694879..25106de 100644
--- a/cras/src/server/cras_dsp_mod_builtin.c
+++ b/cras/src/server/cras_dsp_mod_builtin.c
@@ -7,6 +7,7 @@
 #include "cras_dsp_module.h"
 #include "drc.h"
 #include "dsp_util.h"
+#include "dcblock.h"
 #include "eq.h"
 #include "eq2.h"
 
@@ -201,6 +202,71 @@
 }
 
 /*
+ *  dcblock module functions
+ */
+struct dcblock_data {
+	struct dcblock *dcblockl;
+	struct dcblock *dcblockr;
+
+	/* One port for input, one for output, and 1 parameter */
+	float *ports[5];
+};
+
+static int dcblock_instantiate(struct dsp_module *module,
+			       unsigned long sample_rate)
+{
+	module->data = calloc(1, sizeof(struct dcblock_data));
+	return 0;
+}
+
+static void dcblock_connect_port(struct dsp_module *module,
+				 unsigned long port, float *data_location)
+{
+	struct dcblock_data *data = (struct dcblock_data *) module->data;
+	data->ports[port] = data_location;
+}
+
+static void dcblock_run(struct dsp_module *module, unsigned long sample_count)
+{
+	struct dcblock_data *data = (struct dcblock_data *) module->data;
+	if (!data->dcblockl)
+		data->dcblockl = dcblock_new(*data->ports[4]);
+	if (!data->dcblockr)
+		data->dcblockr = dcblock_new(*data->ports[4]);
+	if (data->ports[0] != data->ports[2])
+		memcpy(data->ports[2], data->ports[0],
+		       sizeof(float) * sample_count);
+	if (data->ports[1] != data->ports[3])
+		memcpy(data->ports[3], data->ports[1],
+		       sizeof(float) * sample_count);
+
+	dcblock_process(data->dcblockl, data->ports[2], (int) sample_count);
+	dcblock_process(data->dcblockr, data->ports[3], (int) sample_count);
+}
+
+static void dcblock_deinstantiate(struct dsp_module *module)
+{
+	struct dcblock_data *data = (struct dcblock_data *) module->data;
+	if (data->dcblockl)
+		dcblock_free(data->dcblockl);
+	if (data->dcblockr)
+		dcblock_free(data->dcblockr);
+	free(data);
+}
+
+static void dcblock_init_module(struct dsp_module *module)
+{
+	module->instantiate = &dcblock_instantiate;
+	module->connect_port = &dcblock_connect_port;
+	module->get_delay = &empty_get_delay;
+	module->run = &dcblock_run;
+	module->deinstantiate = &dcblock_deinstantiate;
+	module->free_module = &empty_free_module;
+	module->get_properties = &empty_get_properties;
+	module->dump = &empty_dump;
+}
+
+/*
  *  eq module functions
  */
 struct eq_data {
@@ -454,6 +520,66 @@
 }
 
 /*
+ * sink module functions
+ */
+struct sink_data {
+	struct ext_dsp_module *ext_module;
+	float *ports[MAX_EXT_DSP_PORTS];
+};
+
+static int sink_instantiate(struct dsp_module *module,
+			    unsigned long sample_rate)
+{
+	module->data = (struct sink_data *)calloc(1, sizeof(struct sink_data));
+	return 0;
+}
+
+static void sink_deinstantiate(struct dsp_module *module)
+{
+	struct sink_data *data = (struct sink_data *)module->data;
+	free(data);
+}
+
+static void sink_connect_port(struct dsp_module *module, unsigned long port,
+				  float *data_location)
+{
+	struct sink_data *data = (struct sink_data *)module->data;
+	data->ports[port] = data_location;
+}
+
+static void sink_run(struct dsp_module *module, unsigned long sample_count)
+{
+	struct sink_data *data = (struct sink_data *)module->data;
+
+	if (!data->ext_module)
+		return;
+	data->ext_module->run(data->ext_module, sample_count);
+}
+
+static void sink_init_module(struct dsp_module *module)
+{
+	module->instantiate = &sink_instantiate;
+	module->connect_port = &sink_connect_port;
+	module->get_delay = &empty_get_delay;
+	module->run = &sink_run;
+	module->deinstantiate = &sink_deinstantiate;
+	module->free_module = &empty_free_module;
+	module->get_properties = &empty_get_properties;
+	module->dump = &empty_dump;
+}
+
+void cras_dsp_module_set_sink_ext_module(struct dsp_module *module,
+					 struct ext_dsp_module *ext_module)
+{
+	struct sink_data *data = (struct sink_data *)module->data;
+	int i;
+	data->ext_module = ext_module;
+
+	for (i = 0; i < MAX_EXT_DSP_PORTS; i++)
+		ext_module->ports[i] = data->ports[i];
+}
+
+/*
  *  builtin module dispatcher
  */
 struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin)
@@ -468,6 +594,8 @@
 		mix_stereo_init_module(module);
 	} else if (strcmp(plugin->label, "invert_lr") == 0) {
 		invert_lr_init_module(module);
+	} else if (strcmp(plugin->label, "dcblock") == 0) {
+		dcblock_init_module(module);
 	} else if (strcmp(plugin->label, "eq") == 0) {
 		eq_init_module(module);
 	} else if (strcmp(plugin->label, "eq2") == 0) {
@@ -476,6 +604,8 @@
 		drc_init_module(module);
 	} else if (strcmp(plugin->label, "swap_lr") == 0) {
 		swap_lr_init_module(module);
+	} else if (strcmp(plugin->label, "sink") == 0) {
+		sink_init_module(module);
 	} else {
 		empty_init_module(module);
 	}
diff --git a/cras/src/server/cras_dsp_module.h b/cras/src/server/cras_dsp_module.h
index 59e6fbe..728418c 100644
--- a/cras/src/server/cras_dsp_module.h
+++ b/cras/src/server/cras_dsp_module.h
@@ -12,6 +12,8 @@
 
 #include "cras_dsp_ini.h"
 
+#define MAX_EXT_DSP_PORTS 8
+
 /* Holds the functions we can use on a dsp module. */
 struct dsp_module {
 	/* Opaque data used by the implementation of this module */
@@ -70,10 +72,49 @@
 	void (*dump)(struct dsp_module *mod, struct dumper *d);
 };
 
+
+/* An external module interface working with existing dsp pipeline.
+ * __________  ___________        ____________      __________
+ * |        |  |         |        |          |      |        |
+ * |        |->| dsp mod |-> ...->| dsp mod  | ---> |        |
+ * | device |  |_________|        |__________|      | stream |
+ * |        |                      | ___________    |        |
+ * |        |                      | | ext     |    |        |
+ * |        |                      ->| dsp mod | -> |        |
+ * |________|                        |_________|    |________|
+ *
+ * According to above diagram, an ext_dsp_module works by appending to
+ * the sink of existing dsp pipeline. For audio input, this creates a
+ * multiple output pipeline that stream can read processed buffer from.
+ * This is useful for a stream to apply special processing effects while
+ * sharing the common dsp with the other streams.
+ *
+ * Members:
+ *    ports - A list of ports can connect to existing dsp ports in a pipeline.
+ *    run - Processes |nframes| of data.
+ *    configure - Configures given external dsp module by the device buffer
+ *        size, rate, and number of channels of the format of the device that
+ *        the associated pipeline runs for.
+ */
+struct ext_dsp_module {
+	float *ports[MAX_EXT_DSP_PORTS];
+	void (*run)(struct ext_dsp_module *ext,
+		    unsigned int nframes);
+	void (*configure)(struct ext_dsp_module *ext,
+			  unsigned int buffer_size,
+			  unsigned int num_channels,
+			  unsigned int rate);
+};
+
+
 enum {
 	MODULE_INPLACE_BROKEN = 1  /* See ladspa.h for explanation */
 };
 
+/* Connects an external dsp module to a builtin sink module. */
+void cras_dsp_module_set_sink_ext_module(struct dsp_module *module,
+					 struct ext_dsp_module *ext_module);
+
 struct dsp_module *cras_dsp_module_load_ladspa(struct plugin *plugin);
 struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin);
 
diff --git a/cras/src/server/cras_dsp_pipeline.c b/cras/src/server/cras_dsp_pipeline.c
index 332ed1c..699c86b 100644
--- a/cras/src/server/cras_dsp_pipeline.c
+++ b/cras/src/server/cras_dsp_pipeline.c
@@ -780,6 +780,14 @@
 			   index);
 }
 
+void cras_dsp_pipeline_set_sink_ext_module(struct pipeline *pipeline,
+					   struct ext_dsp_module *ext_module)
+{
+	cras_dsp_module_set_sink_ext_module(
+			pipeline->sink_instance->module,
+			ext_module);
+}
+
 void cras_dsp_pipeline_run(struct pipeline *pipeline, int sample_count)
 {
 	int i;
@@ -814,26 +822,24 @@
 	pipeline->total_time += t;
 }
 
-void cras_dsp_pipeline_apply(struct pipeline *pipeline,
-			     uint8_t *buf, unsigned int frames)
+int cras_dsp_pipeline_apply(struct pipeline *pipeline, uint8_t *buf,
+			    snd_pcm_format_t format, unsigned int frames)
 {
 	size_t remaining;
 	size_t chunk;
 	size_t i;
-	int16_t *target;
 	unsigned int input_channels = pipeline->input_channels;
 	unsigned int output_channels = pipeline->output_channels;
 	float *source[input_channels];
 	float *sink[output_channels];
 	struct timespec begin, end, delta;
+	int rc;
 
 	if (!pipeline || frames == 0)
-		return;
+		return 0;
 
 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &begin);
 
-	target = (int16_t *)buf;
-
 	/* get pointers to source and sink buffers */
 	for (i = 0; i < input_channels; i++)
 		source[i] = cras_dsp_pipeline_get_source_buffer(pipeline, i);
@@ -847,21 +853,28 @@
 		chunk = MIN(remaining, (size_t)DSP_BUFFER_SIZE);
 
 		/* deinterleave and convert to float */
-		dsp_util_deinterleave(target, source, input_channels, chunk);
+		rc = dsp_util_deinterleave(buf, source, input_channels,
+					   format, chunk);
+		if (rc)
+			return rc;
 
 		/* Run the pipeline */
 		cras_dsp_pipeline_run(pipeline, chunk);
 
 		/* interleave and convert back to int16_t */
-		dsp_util_interleave(sink, target, output_channels, chunk);
+		rc = dsp_util_interleave(sink, buf, output_channels,
+					 format, chunk);
+		if (rc)
+			return rc;
 
-		target += chunk * output_channels;
+		buf += chunk * output_channels * PCM_FORMAT_WIDTH(format) / 8;
 		remaining -= chunk;
 	}
 
 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
 	subtract_timespecs(&end, &begin, &delta);
 	cras_dsp_pipeline_add_statistic(pipeline, &delta, frames);
+	return 0;
 }
 
 void cras_dsp_pipeline_free(struct pipeline *pipeline)
diff --git a/cras/src/server/cras_dsp_pipeline.h b/cras/src/server/cras_dsp_pipeline.h
index 4803c40..c299f3a 100644
--- a/cras/src/server/cras_dsp_pipeline.h
+++ b/cras/src/server/cras_dsp_pipeline.h
@@ -13,7 +13,9 @@
 #include <stdint.h>
 
 #include "dumper.h"
+#include "cras_audio_format.h"
 #include "cras_dsp_ini.h"
+#include "cras_dsp_module.h"
 
 /* These are the functions to create and use dsp pipelines. A dsp
  * pipeline is a collection of dsp plugins that process audio
@@ -100,6 +102,15 @@
  */
 float *cras_dsp_pipeline_get_sink_buffer(struct pipeline *pipeline, int index);
 
+/*
+ * Connects |ext_module| to the sink of given dsp pipeline.
+ * Args:
+ *    pipeline - The pipeline whose sink should connect to ext_module.
+ *    ext_module - The external dsp module to connect to pipeline sink.
+ */
+void cras_dsp_pipeline_set_sink_ext_module(struct pipeline *pipeline,
+					   struct ext_dsp_module *ext_module);
+
 /* Returns the number of internal audio buffers allocated by the
  * pipeline. This is used by the unit test only */
 int cras_dsp_pipeline_get_peak_audio_buffers(struct pipeline *pipeline);
@@ -127,10 +138,13 @@
  * Args:
  *    pipeline - The pipeline to run.
  *    buf - The samples to be processed, interleaved.
+ *    format - Sample format of the buffer.
  *    frames - the numver of samples in the buffer.
+ * Returns:
+ *    Negative code if error, otherwise 0.
  */
-void cras_dsp_pipeline_apply(struct pipeline *pipeline,
-			     uint8_t *buf, unsigned int frames);
+int cras_dsp_pipeline_apply(struct pipeline *pipeline, uint8_t *buf,
+			    snd_pcm_format_t format, unsigned int frames);
 
 /* Dumps the current state of the pipeline. For debugging only */
 void cras_dsp_pipeline_dump(struct dumper *d, struct pipeline *pipeline);
diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c
index 4616cd3..09defa0 100644
--- a/cras/src/server/cras_empty_iodev.c
+++ b/cras/src/server/cras_empty_iodev.c
@@ -52,6 +52,9 @@
 	struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev;
 	unsigned int frames, frames_since_last;
 
+	if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD)
+		return 0;
+
 	frames = empty_iodev->buffer_level;
 	frames_since_last = cras_frames_since_time(
 			&empty_iodev->last_buffer_access,
@@ -93,7 +96,7 @@
 	return 0;
 }
 
-static int open_dev(struct cras_iodev *iodev)
+static int configure_dev(struct cras_iodev *iodev)
 {
 	struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev;
 
@@ -176,7 +179,8 @@
  * Exported Interface.
  */
 
-struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction)
+struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction,
+				      enum CRAS_NODE_TYPE node_type)
 {
 	struct empty_iodev *empty_iodev;
 	struct cras_iodev *iodev;
@@ -196,7 +200,7 @@
 	iodev->supported_formats = empty_supported_formats;
 	iodev->buffer_size = EMPTY_FRAMES;
 
-	iodev->open_dev = open_dev;
+	iodev->configure_dev = configure_dev;
 	iodev->close_dev = close_dev;
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
@@ -209,7 +213,7 @@
 	/* Create a dummy ionode */
 	node = (struct cras_ionode *)calloc(1, sizeof(*node));
 	node->dev = iodev;
-	node->type = CRAS_NODE_TYPE_UNKNOWN;
+	node->type = node_type;
 	node->volume = 100;
 	strcpy(node->name, "(default)");
 	cras_iodev_add_node(iodev, node);
@@ -217,11 +221,21 @@
 
 	/* Finally add it to the appropriate iodev list. */
 	if (direction == CRAS_STREAM_INPUT) {
-		snprintf(iodev->info.name,
-			 ARRAY_SIZE(iodev->info.name),
-			 "Silent record device.");
-		iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0';
-		iodev->info.idx = SILENT_RECORD_DEVICE;
+		if (node->type == CRAS_NODE_TYPE_HOTWORD) {
+			snprintf(iodev->info.name,
+				 ARRAY_SIZE(iodev->info.name),
+				 "Silent hotword device.");
+			iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] =
+				'\0';
+			iodev->info.idx = SILENT_HOTWORD_DEVICE;
+		} else {
+			snprintf(iodev->info.name,
+				 ARRAY_SIZE(iodev->info.name),
+				 "Silent record device.");
+			iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] =
+				'\0';
+			iodev->info.idx = SILENT_RECORD_DEVICE;
+		}
 	} else {
 		snprintf(iodev->info.name,
 			 ARRAY_SIZE(iodev->info.name),
diff --git a/cras/src/server/cras_empty_iodev.h b/cras/src/server/cras_empty_iodev.h
index 0d28a5d..d85b9e0 100644
--- a/cras/src/server/cras_empty_iodev.h
+++ b/cras/src/server/cras_empty_iodev.h
@@ -15,10 +15,12 @@
  * until a new iodev becomes available.
  * Args:
  *    direciton - input or output.
+ *    node_type - the default node type.
  * Returns:
  *    A pointer to the newly created iodev if successful, NULL otherwise.
  */
-struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction);
+struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction,
+				      enum CRAS_NODE_TYPE node_type);
 
 /* Destroys an empty_iodev created with empty_iodev_create. */
 void empty_iodev_destroy(struct cras_iodev *iodev);
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index 729b347..5b4bbeb 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -69,7 +69,7 @@
 	uint16_t *_out = (uint16_t *)out;
 
 	for (i = 0; i < in_samples; i++, in++, _out++)
-		*_out = ((int16_t)*in - 0x80) << 8;
+		*_out = (uint16_t)((int16_t)*in - 0x80) << 8;
 }
 
 /* Converts from S24 to S16. */
@@ -131,7 +131,7 @@
 	uint32_t *_out = (uint32_t *)out;
 
 	for (i = 0; i < in_samples; i++, _in++, _out++)
-		*_out = ((int32_t)*_in << 8);
+		*_out = ((uint32_t)(int32_t)*_in << 8);
 }
 
 /* Converts from S16 to S32. */
@@ -143,7 +143,7 @@
 	uint32_t *_out = (uint32_t *)out;
 
 	for (i = 0; i < in_samples; i++, _in++, _out++)
-		*_out = ((int32_t)*_in << 16);
+		*_out = ((uint32_t)(int32_t)*_in << 16);
 }
 
 /* Converts from S16 to S24_3LE. */
@@ -289,6 +289,79 @@
 	return in_frames;
 }
 
+/* Converts S16 stereo to quad (front L/R, rear L/R). Fit left/right of input
+ * to the front left/right of output respectively and fill others
+ * with zero.
+ */
+static size_t s16_stereo_to_quad(struct cras_fmt_conv *conv,
+				 const int16_t *in, size_t in_frames,
+				 int16_t *out)
+{
+	size_t i, front_left, front_right, rear_left, rear_right;
+
+	front_left = conv->out_fmt.channel_layout[CRAS_CH_FL];
+	front_right = conv->out_fmt.channel_layout[CRAS_CH_FR];
+	rear_left = conv->out_fmt.channel_layout[CRAS_CH_RL];
+	rear_right = conv->out_fmt.channel_layout[CRAS_CH_RR];
+
+	if (front_left != -1 && front_right != -1 &&
+	    rear_left != -1 && rear_right != -1)
+		for (i = 0; i < in_frames; i++) {
+			out[4 * i + front_left] = in[2 * i];
+			out[4 * i + front_right] = in[2 * i + 1];
+			out[4 * i + rear_left] = in[2 * i];
+			out[4 * i + rear_right] = in[2 * i + 1];
+		}
+	else
+		/* Select the first four channels to convert to as the
+		 * default behavior.
+		 */
+		for (i = 0; i < in_frames; i++) {
+			out[4 * i] = in[2 * i];
+			out[4 * i + 1] = in[2 * i + 1];
+			out[4 * i + 2] = in[2 * i];
+			out[4 * i + 3] = in[2 * i + 1];
+		}
+
+	return in_frames;
+}
+
+/* Converts S16 quad (front L/R, rear L/R) to S16 stereo. The out buffer
+ * can have room for just stereo samples.
+ */
+static size_t s16_quad_to_stereo(struct cras_fmt_conv *conv,
+			       const int16_t *in, size_t in_frames,
+			       int16_t *out)
+{
+	size_t i;
+	unsigned int left_idx =
+			conv->in_fmt.channel_layout[CRAS_CH_FL];
+	unsigned int right_idx =
+			conv->in_fmt.channel_layout[CRAS_CH_FR];
+	unsigned int left_rear_idx =
+			conv->in_fmt.channel_layout[CRAS_CH_RL];
+	unsigned int right_rear_idx =
+			conv->in_fmt.channel_layout[CRAS_CH_RR];
+
+	if (left_idx == -1 || right_idx == -1 ||
+	    left_rear_idx == -1 || right_rear_idx == -1) {
+		left_idx = 0;
+		right_idx = 1;
+		left_rear_idx = 2;
+		right_rear_idx = 3;
+	}
+
+	for (i = 0; i < in_frames; i++) {
+		out[2 * i] = s16_add_and_clip(
+		    in[4 * i + left_idx],
+		    in[4 * i + left_rear_idx] / 4);
+		out[2 * i + 1] = s16_add_and_clip(
+		    in[4 * i + right_idx],
+		    in[4 * i + right_rear_idx] / 4);
+	}
+	return in_frames;
+}
+
 /* Converts S16 N channels to S16 M channels.  The out buffer must have room for
  * M channel. This convert function is used as the default behavior when channel
  * layout is not set from the client side. */
@@ -300,8 +373,7 @@
 	unsigned int num_out_ch = conv->out_fmt.num_channels;
 	unsigned int in_ch, out_ch, i;
 
-	memset(out, 0, num_out_ch * in_frames *
-				cras_get_format_bytes(&conv->out_fmt));
+	memset(out, 0, in_frames * cras_get_format_bytes(&conv->out_fmt));
 	for (out_ch = 0; out_ch < num_out_ch; out_ch++) {
 		for (in_ch = 0; in_ch < num_in_ch; in_ch++) {
 			for (i = 0; i < in_frames; i++) {
@@ -456,7 +528,7 @@
 			break;
 		default:
 			syslog(LOG_WARNING, "Invalid format %d", in->format);
-			cras_fmt_conv_destroy(conv);
+			cras_fmt_conv_destroy(&conv);
 			return NULL;
 		}
 	}
@@ -479,7 +551,7 @@
 			break;
 		default:
 			syslog(LOG_WARNING, "Invalid format %d", out->format);
-			cras_fmt_conv_destroy(conv);
+			cras_fmt_conv_destroy(&conv);
 			return NULL;
 		}
 	}
@@ -498,6 +570,10 @@
 			conv->channel_converter = s16_mono_to_51;
 		} else if (in->num_channels == 2 && out->num_channels == 1) {
 			conv->channel_converter = s16_stereo_to_mono;
+		} else if (in->num_channels == 2 && out->num_channels == 4) {
+			conv->channel_converter = s16_stereo_to_quad;
+		} else if (in->num_channels == 4 && out->num_channels == 2) {
+			conv->channel_converter = s16_quad_to_stereo;
 		} else if (in->num_channels == 2 && out->num_channels == 6) {
 			conv->channel_converter = s16_stereo_to_51;
 		} else if (in->num_channels == 6 && out->num_channels == 2) {
@@ -516,7 +592,7 @@
 						in->num_channels,
 						out->num_channels);
 				if (conv->ch_conv_mtx == NULL) {
-					cras_fmt_conv_destroy(conv);
+					cras_fmt_conv_destroy(&conv);
 					return NULL;
 				}
 				conv->channel_converter = convert_channels;
@@ -538,7 +614,7 @@
 		conv->ch_conv_mtx = cras_channel_conv_matrix_create(in, out);
 		if (conv->ch_conv_mtx == NULL) {
 			syslog(LOG_ERR, "Failed to create channel conversion matrix");
-			cras_fmt_conv_destroy(conv);
+			cras_fmt_conv_destroy(&conv);
 			return NULL;
 		}
 		conv->channel_converter = convert_channels;
@@ -559,7 +635,7 @@
 			       in->frame_rate,
 			       out->frame_rate,
 			       rc);
-			cras_fmt_conv_destroy(conv);
+			cras_fmt_conv_destroy(&conv);
 			return NULL;
 		}
 	}
@@ -573,7 +649,7 @@
 			out->frame_rate);
 	if (conv->resampler == NULL) {
 		syslog(LOG_ERR, "Fail to create linear resampler");
-		cras_fmt_conv_destroy(conv);
+		cras_fmt_conv_destroy(&conv);
 		return NULL;
 	}
 
@@ -585,7 +661,7 @@
 			4 * /* width in bytes largest format. */
 			MAX(in->num_channels, out->num_channels));
 		if (conv->tmp_bufs[i] == NULL) {
-			cras_fmt_conv_destroy(conv);
+			cras_fmt_conv_destroy(&conv);
 			return NULL;
 		}
 	}
@@ -595,9 +671,11 @@
 	return conv;
 }
 
-void cras_fmt_conv_destroy(struct cras_fmt_conv *conv)
+void cras_fmt_conv_destroy(struct cras_fmt_conv **convp)
 {
 	unsigned i;
+	struct cras_fmt_conv *conv = *convp;
+
 	if (conv->ch_conv_mtx)
 		cras_channel_conv_matrix_destroy(conv->ch_conv_mtx,
 						 conv->out_fmt.num_channels);
@@ -608,6 +686,7 @@
 	for (i = 0; i < MAX_NUM_CONVERTERS - 1; i++)
 		free(conv->tmp_bufs[i]);
 	free(conv);
+	*convp = NULL;
 }
 
 struct cras_fmt_conv *cras_channel_remix_conv_create(
@@ -736,6 +815,7 @@
 	unsigned int linear_resample_fr = 0;
 
 	assert(conv);
+	assert(*in_frames <= conv->tmp_buf_frames);
 
 	if (linear_resampler_needed(conv->resampler)) {
 		post_linear_resample = !conv->pre_linear_resample;
@@ -778,9 +858,19 @@
 		 * resample limit and round it to the lower bound in order
 		 * not to convert too many frames in the pre linear resampler.
 		 */
-		if (conv->speex_state != NULL)
-			resample_limit = resample_limit * conv->in_fmt.frame_rate /
+		if (conv->speex_state != NULL) {
+			resample_limit = resample_limit *
+					conv->in_fmt.frame_rate /
 					conv->out_fmt.frame_rate;
+			/*
+			 * However if the limit frames count is less than
+			 * |out_rate / in_rate|, the final limit value could be
+			 * rounded to zero so it confuses linear resampler to
+			 * do nothing. Make sure it's non-zero in that case.
+			 */
+			if (resample_limit == 0)
+				resample_limit = 1;
+		}
 
 		resample_limit = MIN(resample_limit, conv->tmp_buf_frames);
 		fr_in = linear_resampler_resample(
@@ -857,10 +947,23 @@
 		buf_idx++;
 	}
 
-	if (pre_linear_resample)
+	if (pre_linear_resample) {
 		*in_frames = linear_resample_fr;
-	else
+
+		/* When buffer sizes are small, there's a corner case that
+		 * speex library resamples 0 frame to N-1 frames, where N
+		 * is the integer ratio of output and input rate. For example,
+		 * 16KHz to 48KHz. In this case fmt_conv should claim zero
+		 * frames processed, instead of using the linear resampler
+		 * processed frames count. Otherwise there will be a frame
+		 * leak and, if accumulated, causes delay in multiple devices
+		 * use case.
+		 */
+		if (conv->speex_state && (fr_in == 0))
+			*in_frames = 0;
+	} else {
 		*in_frames = fr_in;
+	}
 	return fr_out;
 }
 
diff --git a/cras/src/server/cras_fmt_conv.h b/cras/src/server/cras_fmt_conv.h
index 732a517..0f6042a 100644
--- a/cras/src/server/cras_fmt_conv.h
+++ b/cras/src/server/cras_fmt_conv.h
@@ -23,7 +23,7 @@
 					   const struct cras_audio_format *out,
 					   size_t max_frames,
 					   size_t pre_linear_resample);
-void cras_fmt_conv_destroy(struct cras_fmt_conv *conv);
+void cras_fmt_conv_destroy(struct cras_fmt_conv **conv);
 
 /* Creates the format converter for channel remixing. The conversion takes
  * a N by N float matrix, to multiply each N-channels sample.
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index d85d285..4127a00 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -136,9 +136,9 @@
 	format_bytes = cras_get_format_bytes(dev->format);
 
 	if (dev->direction == CRAS_STREAM_OUTPUT)
-		return buf_queued_bytes(info->playback_buf) / format_bytes;
+		return buf_queued(info->playback_buf) / format_bytes;
 	else
-		return buf_queued_bytes(info->capture_buf) / format_bytes;
+		return buf_queued(info->capture_buf) / format_bytes;
 }
 
 int hfp_write(struct hfp_info *info)
@@ -318,9 +318,9 @@
 error:
 	if (info) {
 		if (info->capture_buf)
-			byte_buffer_destroy(info->capture_buf);
+			byte_buffer_destroy(&info->capture_buf);
 		if (info->playback_buf)
-			byte_buffer_destroy(info->playback_buf);
+			byte_buffer_destroy(&info->playback_buf);
 		free(info);
 	}
 	return NULL;
@@ -368,10 +368,10 @@
 void hfp_info_destroy(struct hfp_info *info)
 {
 	if (info->capture_buf)
-		byte_buffer_destroy(info->capture_buf);
+		byte_buffer_destroy(&info->capture_buf);
 
 	if (info->playback_buf)
-		byte_buffer_destroy(info->playback_buf);
+		byte_buffer_destroy(&info->playback_buf);
 
 	free(info);
 }
diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c
index 10314e6..dd8296e 100644
--- a/cras/src/server/cras_hfp_iodev.c
+++ b/cras/src/server/cras_hfp_iodev.c
@@ -4,6 +4,7 @@
  */
 
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <syslog.h>
 
 #include "cras_audio_area.h"
@@ -74,7 +75,7 @@
 	cras_bt_device_iodev_buffer_size_changed(hfpio->device);
 }
 
-static int open_dev(struct cras_iodev *iodev)
+static int configure_dev(struct cras_iodev *iodev)
 {
 	struct hfp_io *hfpio = (struct hfp_io *)iodev;
 	int sk, err, mtu;
@@ -205,6 +206,8 @@
 	}
 	free(hfpio->base.supported_channel_counts);
 	free(hfpio->base.supported_rates);
+	free(hfpio->base.supported_formats);
+	cras_iodev_free_resources(&hfpio->base);
 }
 
 struct cras_iodev *hfp_iodev_create(
@@ -242,7 +245,7 @@
 			strlen(cras_bt_device_object_path(device)));
 	iodev->info.stable_id_new = iodev->info.stable_id;
 
-	iodev->open_dev= open_dev;
+	iodev->configure_dev= configure_dev;
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
 	iodev->get_buffer = get_buffer;
diff --git a/cras/src/server/cras_hotword_handler.c b/cras/src/server/cras_hotword_handler.c
new file mode 100644
index 0000000..055294a
--- /dev/null
+++ b/cras/src/server/cras_hotword_handler.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cras_main_message.h"
+#include "cras_observer.h"
+
+struct hotword_triggered_msg {
+	struct cras_main_message header;
+	int64_t tv_sec;
+	int64_t tv_nsec;
+};
+
+/* The following functions are called from audio thread. */
+
+static void init_hotword_triggered_msg(struct hotword_triggered_msg *msg)
+{
+	struct timespec now;
+
+	clock_gettime(CLOCK_MONOTONIC, &now);
+
+	memset(msg, 0, sizeof(*msg));
+	msg->header.type = CRAS_MAIN_HOTWORD_TRIGGERED;
+	msg->header.length = sizeof(*msg);
+	msg->tv_sec = now.tv_sec;
+	msg->tv_nsec = now.tv_nsec;
+}
+
+int cras_hotword_send_triggered_msg()
+{
+	struct hotword_triggered_msg msg;
+	int rc;
+
+	init_hotword_triggered_msg(&msg);
+
+	rc = cras_main_message_send((struct cras_main_message *)&msg);
+	if (rc < 0)
+		syslog(LOG_ERR, "Failed to send hotword triggered message!");
+
+	return rc;
+}
+
+/* The following functions are called from main thread. */
+
+static void handle_hotword_message(struct cras_main_message *msg, void *arg)
+{
+	struct hotword_triggered_msg *hotword_msg =
+		(struct hotword_triggered_msg *)msg;
+
+	cras_observer_notify_hotword_triggered(hotword_msg->tv_sec,
+					       hotword_msg->tv_nsec);
+}
+
+int cras_hotword_handler_init()
+{
+	cras_main_message_add_handler(CRAS_MAIN_HOTWORD_TRIGGERED,
+				      handle_hotword_message, NULL);
+	return 0;
+}
diff --git a/cras/src/server/cras_hotword_handler.h b/cras/src/server/cras_hotword_handler.h
new file mode 100644
index 0000000..48fc405
--- /dev/null
+++ b/cras/src/server/cras_hotword_handler.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The hotword handler is used to send a DBus signal when a hotword device is
+ * triggered.
+ *
+ * cras_hotword_send_triggered_msg() is called from audio thread to send a
+ * hotword message to main thread which in turn sends the DBus signal.
+ *
+ * cras_hotword_handler_init() is used to setup message handler in main thread
+ * to handle the hotword message from audio thread.
+ */
+
+#ifndef CRAS_HOTWORD_HANDLER_H_
+#define CRAS_HOTWORD_HANDLER_H_
+
+/* Send hotword triggered message. */
+int cras_hotword_send_triggered_msg();
+
+/* Initialize hotword handler. */
+int cras_hotword_handler_init();
+
+#endif /* CRAS_HOTWORD_HANDLER_H_ */
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index 69e65db..3130b30 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -4,6 +4,7 @@
  */
 
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <sys/param.h>
 #include <sys/time.h>
@@ -14,6 +15,7 @@
 #include "audio_thread_log.h"
 #include "buffer_share.h"
 #include "cras_audio_area.h"
+#include "cras_audio_thread_monitor.h"
 #include "cras_device_monitor.h"
 #include "cras_dsp.h"
 #include "cras_dsp_pipeline.h"
@@ -26,6 +28,7 @@
 #include "cras_system_state.h"
 #include "cras_util.h"
 #include "dev_stream.h"
+#include "input_data.h"
 #include "utlist.h"
 #include "rate_estimator.h"
 #include "softvol_curve.h"
@@ -34,10 +37,14 @@
 static const float RAMP_NEW_STREAM_DURATION_SECS = 0.01;
 static const float RAMP_MUTE_DURATION_SECS = 0.1;
 
+/*
+ * Check issu b/72496547 and commit message for the history of
+ * rate estimator tuning.
+ */
 static const struct timespec rate_estimation_window_sz = {
-	20, 0 /* 20 sec. */
+	5, 0 /* 5 sec. */
 };
-static const double rate_estimation_smooth_factor = 0.9f;
+static const double rate_estimation_smooth_factor = 0.3f;
 
 static void cras_iodev_alloc_dsp(struct cras_iodev *iodev);
 
@@ -54,6 +61,18 @@
 		return rc;
 	hw_level = rc;
 
+	/* If underrun happened, handle underrun and get hw_level again. */
+	if (hw_level == 0) {
+		rc = cras_iodev_output_underrun(odev);
+		if (rc < 0)
+			return rc;
+
+		rc = cras_iodev_frames_queued(odev, &hw_tstamp);
+		if (rc < 0)
+			return rc;
+		hw_level = rc;
+	}
+
 	ATLOG(atlog, AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS,
 	      odev->info.idx, hw_level, target_hw_level);
 
@@ -324,38 +343,28 @@
 	return iodev->supported_formats[0];
 }
 
-/* Set default channel layout to an iodev. */
-static void set_default_channel_layout(struct cras_iodev *iodev)
-{
-	int8_t default_layout[CRAS_CH_MAX];
-	size_t i;
-
-	for (i = 0; i < CRAS_CH_MAX; i++)
-		default_layout[i] = i < iodev->format->num_channels ? i : -1;
-
-	cras_audio_format_set_channel_layout(iodev->format, default_layout);
-	cras_audio_format_set_channel_layout(iodev->ext_format, default_layout);
-}
-
 /* Applies the DSP to the samples for the iodev if applicable. */
-static void apply_dsp(struct cras_iodev *iodev, uint8_t *buf, size_t frames)
+static int apply_dsp(struct cras_iodev *iodev, uint8_t *buf, size_t frames)
 {
 	struct cras_dsp_context *ctx;
 	struct pipeline *pipeline;
+	int rc;
 
 	ctx = iodev->dsp_context;
 	if (!ctx)
-		return;
+		return 0;
 
 	pipeline = cras_dsp_get_pipeline(ctx);
 	if (!pipeline)
-		return;
+		return 0;
 
-	cras_dsp_pipeline_apply(pipeline,
-				buf,
-				frames);
+	rc = cras_dsp_pipeline_apply(pipeline,
+				     buf,
+				     iodev->format->format,
+				     frames);
 
 	cras_dsp_put_pipeline(ctx);
+	return rc;
 }
 
 static void cras_iodev_free_dsp(struct cras_iodev *iodev)
@@ -399,24 +408,13 @@
 {
 	int rc;
 
-	/*
-	 * Output devices like internal speakers and headphones are 2-channel
-	 * and do not need to update channel layout.
-	 * For HDMI and USB devices that might have more than 2 channels, update
-	 * channel layout only if more than 2 channel is requested.
-	 */
-	if (iodev->direction == CRAS_STREAM_OUTPUT &&
-	    iodev->format->num_channels <= 2) {
-		set_default_channel_layout(iodev);
-		return;
-	}
-
 	if (iodev->update_channel_layout == NULL)
 		return;
 
 	rc = iodev->update_channel_layout(iodev);
 	if (rc < 0) {
-		set_default_channel_layout(iodev);
+		cras_audio_format_set_default_channel_layout(iodev->format);
+		cras_audio_format_set_default_channel_layout(iodev->ext_format);
 	} else {
 		cras_audio_format_set_channel_layout(
 				iodev->ext_format,
@@ -424,6 +422,18 @@
 	}
 }
 
+/*
+ * For the specified format, removes any channels from the channel layout that
+ * are higher than the supported number of channels. Should be used when the
+ * number of channels of the format been reduced.
+ */
+static void trim_channel_layout(struct cras_audio_format *fmt) {
+	int i;
+	for (i = 0; i < CRAS_CH_MAX; i++)
+		if (fmt->channel_layout[i] >= fmt->num_channels)
+			fmt->channel_layout[i] = -1;
+}
+
 int cras_iodev_set_format(struct cras_iodev *iodev,
 			  const struct cras_audio_format *fmt)
 {
@@ -457,6 +467,7 @@
 		iodev->ext_format->frame_rate = actual_rate;
 
 		cras_iodev_alloc_dsp(iodev);
+		cras_iodev_update_dsp(iodev);
 		if (iodev->dsp_context)
 			adjust_dev_channel_for_dsp(iodev);
 
@@ -474,7 +485,9 @@
 		if (iodev->format->num_channels != actual_num_channels) {
 			/* If the DSP for this device doesn't match, drop it. */
 			iodev->format->num_channels = actual_num_channels;
+			trim_channel_layout(iodev->format);
 			iodev->ext_format->num_channels = actual_num_channels;
+			trim_channel_layout(iodev->ext_format);
 			cras_iodev_free_dsp(iodev);
 		}
 
@@ -499,6 +512,51 @@
 	return rc;
 }
 
+/*
+ * Configures the external dsp module and adds it to the existing dsp pipeline.
+ */
+static void add_ext_dsp_module_to_pipeline(struct cras_iodev *iodev)
+{
+	struct pipeline *pipeline;
+
+	if (!iodev->ext_dsp_module)
+		return;
+
+	iodev->ext_dsp_module->configure(
+			iodev->ext_dsp_module,
+			iodev->buffer_size,
+			iodev->ext_format->num_channels,
+			iodev->ext_format->frame_rate);
+
+	pipeline = iodev->dsp_context
+			? cras_dsp_get_pipeline(iodev->dsp_context)
+			: NULL;
+
+	if (!pipeline) {
+		cras_iodev_alloc_dsp(iodev);
+		cras_dsp_load_dummy_pipeline(
+			iodev->dsp_context,
+			iodev->format->num_channels);
+		pipeline = cras_dsp_get_pipeline(iodev->dsp_context);
+	}
+
+	cras_dsp_pipeline_set_sink_ext_module(
+			pipeline,
+			iodev->ext_dsp_module);
+
+	cras_dsp_put_pipeline(iodev->dsp_context);
+}
+
+void cras_iodev_set_ext_dsp_module(struct cras_iodev *iodev,
+				   struct ext_dsp_module *ext)
+{
+	iodev->ext_dsp_module = ext;
+
+	if (!ext || !cras_iodev_is_open(iodev))
+		return;
+	add_ext_dsp_module_to_pipeline(iodev);
+}
+
 void cras_iodev_update_dsp(struct cras_iodev *iodev)
 {
 	char swap_lr_disabled = 1;
@@ -585,7 +643,6 @@
 	cras_iodev_free_dsp(iodev);
 	iodev->dsp_context = cras_dsp_context_new(iodev->format->frame_rate,
 						  purpose);
-	cras_iodev_update_dsp(iodev);
 }
 
 void cras_iodev_fill_time_from_frames(size_t frames,
@@ -614,7 +671,7 @@
 	if (plugged) {
 		gettimeofday(&node->plugged_time, NULL);
 	} else if (node == node->dev->active_node) {
-		cras_iodev_list_disable_dev(node->dev);
+		cras_iodev_list_disable_dev(node->dev, false);
 	}
 	cras_iodev_list_notify_nodes_changed();
 }
@@ -740,7 +797,13 @@
 
 	if (!iodev->buf_state)
 		iodev->buf_state = buffer_share_create(iodev->buffer_size);
-	buffer_share_add_id(iodev->buf_state, stream->stream->stream_id, NULL);
+	/*
+	 * TRIGGER_ONLY streams do not want to receive data, so do not add them
+	 * to buffer_share, otherwise they'll affect other streams to receive.
+	 */
+	if (!(stream->stream->flags & TRIGGER_ONLY))
+		buffer_share_add_id(iodev->buf_state, stream->stream->stream_id,
+				    NULL);
 
 	iodev->min_cb_level = MIN(iodev->min_cb_level, cb_threshold);
 	iodev->max_cb_level = MAX(iodev->max_cb_level, cb_threshold);
@@ -753,7 +816,6 @@
 	struct dev_stream *out;
 	struct dev_stream *ret = NULL;
 	unsigned int cb_threshold;
-	unsigned int old_min_cb_level = iodev->min_cb_level;
 
 	iodev->min_cb_level = iodev->buffer_size / 2;
 	iodev->max_cb_level = 0;
@@ -773,7 +835,7 @@
 	if (!iodev->streams) {
 		buffer_share_destroy(iodev->buf_state);
 		iodev->buf_state = NULL;
-		iodev->min_cb_level = old_min_cb_level;
+		iodev->min_cb_level = iodev->buffer_size / 2;
 		/* Let output device transit into no stream state if it's
 		 * in normal run state now. Leave input device in normal
 		 * run state. */
@@ -820,20 +882,47 @@
 	return max;
 }
 
-int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level)
+int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level,
+		    const struct cras_audio_format *fmt)
 {
 	int rc;
 
-	rc = iodev->open_dev(iodev);
-	if (rc < 0)
-		return rc;
+	if (iodev->pre_open_iodev_hook)
+		iodev->pre_open_iodev_hook();
 
+	if (iodev->open_dev) {
+		rc = iodev->open_dev(iodev);
+		if (rc)
+			return rc;
+	}
+
+	if (iodev->ext_format == NULL) {
+		rc = cras_iodev_set_format(iodev, fmt);
+		if (rc) {
+			iodev->close_dev(iodev);
+			return rc;
+		}
+	}
+
+	rc = iodev->configure_dev(iodev);
+	if (rc < 0) {
+		iodev->close_dev(iodev);
+		return rc;
+	}
+
+	/*
+	 * Convert cb_level from input format to device format
+	 */
+	cb_level = cras_frames_at_rate(fmt->frame_rate,
+				       cb_level,
+				       iodev->ext_format->frame_rate);
 	/* Make sure the min_cb_level doesn't get too large. */
 	iodev->min_cb_level = MIN(iodev->buffer_size / 2, cb_level);
 	iodev->max_cb_level = 0;
 
 	iodev->reset_request_pending = 0;
 	iodev->state = CRAS_IODEV_STATE_OPEN;
+	iodev->highest_hw_level = 0;
 
 	if (iodev->direction == CRAS_STREAM_OUTPUT) {
 		/* If device supports start ops, device can be in open state.
@@ -843,12 +932,23 @@
 		else
 			iodev->state = CRAS_IODEV_STATE_NO_STREAM_RUN;
 	} else {
+		iodev->input_data = input_data_create(iodev);
+		/* If this is the echo reference dev, its ext_dsp_module will
+		 * be set to APM reverse module. Do not override it to its
+		 * input data. */
+		if (iodev->ext_dsp_module == NULL)
+			iodev->ext_dsp_module = &iodev->input_data->ext;
+
 		/* Input device starts running right after opening.
 		 * No stream state is only for output device. Input device
 		 * should be in normal run state. */
 		iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+		/* Initialize the input_streaming flag to zero.*/
+		iodev->input_streaming = 0;
 	}
 
+	add_ext_dsp_module_to_pipeline(iodev);
+
 	return 0;
 }
 
@@ -860,37 +960,61 @@
 int cras_iodev_close(struct cras_iodev *iodev)
 {
 	int rc;
+
 	if (!cras_iodev_is_open(iodev))
 		return 0;
 
+	if (iodev->input_data) {
+		if (iodev->ext_dsp_module == &iodev->input_data->ext)
+			iodev->ext_dsp_module = NULL;
+		input_data_destroy(&iodev->input_data);
+	}
+
 	rc = iodev->close_dev(iodev);
 	if (rc)
 		return rc;
 	iodev->state = CRAS_IODEV_STATE_CLOSE;
 	if (iodev->ramp)
 		cras_ramp_reset(iodev->ramp);
+
+	if (iodev->post_close_iodev_hook)
+		iodev->post_close_iodev_hook();
 	return 0;
 }
 
-int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes)
+int cras_iodev_put_input_buffer(struct cras_iodev *iodev)
 {
-	rate_estimator_add_frames(iodev->rate_est, -nframes);
-	return iodev->put_buffer(iodev, nframes);
+	unsigned int min_frames;
+	unsigned int dsp_frames;
+	struct input_data *data = iodev->input_data;
+
+	if (iodev->streams)
+		min_frames = buffer_share_get_new_write_point(iodev->buf_state);
+	else
+		min_frames = data->area->frames;
+
+	// Update the max number of frames has applied input dsp.
+	dsp_frames = MAX(iodev->input_frames_read, iodev->input_dsp_offset);
+	iodev->input_dsp_offset = dsp_frames - min_frames;
+
+	input_data_set_all_streams_read(data, min_frames);
+	rate_estimator_add_frames(iodev->rate_est, -min_frames);
+	return iodev->put_buffer(iodev, min_frames);
 }
 
 int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
-				 unsigned int nframes)
+				 unsigned int nframes, int *is_non_empty,
+				 struct cras_fmt_conv *remix_converter)
 {
 	const struct cras_audio_format *fmt = iodev->format;
-	struct cras_fmt_conv * remix_converter =
-			audio_thread_get_global_remix_converter();
 	struct cras_ramp_action ramp_action = {
 		.type = CRAS_RAMP_ACTION_NONE,
 		.scaler = 0.0f,
 		.increment = 0.0f,
 	};
-	float software_volume_scaler;
+	float software_volume_scaler = 1.0;
 	int software_volume_needed = cras_iodev_software_volume_needed(iodev);
+	int rc;
 
 	if (iodev->pre_dsp_hook)
 		iodev->pre_dsp_hook(frames, nframes, iodev->ext_format,
@@ -900,48 +1024,53 @@
 		ramp_action = cras_ramp_get_current_action(iodev->ramp);
 	}
 
+	rc = apply_dsp(iodev, frames, nframes);
+	if (rc)
+		return rc;
+
+	if (iodev->post_dsp_hook)
+		iodev->post_dsp_hook(frames, nframes, fmt,
+				     iodev->post_dsp_hook_cb_data);
+
 	/* Mute samples if adjusted volume is 0 or system is muted, plus
 	 * that this device is not ramping. */
 	if (output_should_mute(iodev) &&
 	    ramp_action.type != CRAS_RAMP_ACTION_PARTIAL) {
 		const unsigned int frame_bytes = cras_get_format_bytes(fmt);
 		cras_mix_mute_buffer(frames, frame_bytes, nframes);
-	} else {
-		apply_dsp(iodev, frames, nframes);
 
-		if (iodev->post_dsp_hook)
-			iodev->post_dsp_hook(frames, nframes, fmt,
-					     iodev->post_dsp_hook_cb_data);
+		// Skip non-empty check, since we know it's empty.
+		is_non_empty = NULL;
+	}
 
-		/* Compute scaler for software volume if needed. */
+	/* Compute scaler for software volume if needed. */
+	if (software_volume_needed) {
+		software_volume_scaler =
+			cras_iodev_get_software_volume_scaler(iodev);
+	}
+
+	if (ramp_action.type == CRAS_RAMP_ACTION_PARTIAL) {
+		/* Scale with increment for ramp and possibly
+		 * software volume using cras_scale_buffer_increment.*/
+		float starting_scaler = ramp_action.scaler;
+		float increment = ramp_action.increment;
+
 		if (software_volume_needed) {
-			software_volume_scaler =
-				cras_iodev_get_software_volume_scaler(iodev);
+			starting_scaler *= software_volume_scaler;
+			increment *= software_volume_scaler;
 		}
 
-		if (ramp_action.type == CRAS_RAMP_ACTION_PARTIAL) {
-			/* Scale with increment for ramp and possibly
-			 * software volume using cras_scale_buffer_increment.*/
-			float starting_scaler = ramp_action.scaler;
-			float increment = ramp_action.increment;
-
-			if (software_volume_needed) {
-				starting_scaler *= software_volume_scaler;
-				increment *= software_volume_scaler;
-			}
-
-			cras_scale_buffer_increment(
-					fmt->format, frames, nframes,
-					starting_scaler, increment,
-					fmt->num_channels);
-			cras_ramp_update_ramped_frames(iodev->ramp, nframes);
-		} else if (software_volume_needed) {
-			/* Just scale for software volume using
-			 * cras_scale_buffer. */
-			unsigned int nsamples = nframes * fmt->num_channels;
-			cras_scale_buffer(fmt->format, frames,
-					  nsamples, software_volume_scaler);
-		}
+		cras_scale_buffer_increment(
+				fmt->format, frames, nframes,
+				starting_scaler, increment,
+				fmt->num_channels);
+		cras_ramp_update_ramped_frames(iodev->ramp, nframes);
+	} else if (!output_should_mute(iodev) && software_volume_needed) {
+		/* Just scale for software volume using
+		 * cras_scale_buffer. */
+		unsigned int nsamples = nframes * fmt->num_channels;
+		cras_scale_buffer(fmt->format, frames,
+				  nsamples, software_volume_scaler);
 	}
 
 	if (remix_converter)
@@ -950,22 +1079,33 @@
 				   frames,
 				   nframes);
 	rate_estimator_add_frames(iodev->rate_est, nframes);
+
+	// Calculate whether the final output was non-empty, if requested.
+	if (is_non_empty) {
+		unsigned int i;
+		for (i = 0; i < nframes * cras_get_format_bytes(fmt); i++) {
+			if (frames[i]) {
+				*is_non_empty = 1;
+				break;
+			}
+		}
+	}
+
 	return iodev->put_buffer(iodev, nframes);
 }
 
-int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
-				struct cras_audio_area **area,
-				unsigned *frames)
+int cras_iodev_get_input_buffer(struct cras_iodev *iodev, unsigned int *frames)
 {
-	const struct cras_audio_format *fmt = iodev->format;
-	const unsigned int frame_bytes = cras_get_format_bytes(fmt);
-	uint8_t *hw_buffer;
+	const unsigned int frame_bytes = cras_get_format_bytes(iodev->format);
+	struct input_data *data = iodev->input_data;
 	int rc;
+	uint8_t *hw_buffer;
 	unsigned frame_requested = *frames;
 
-	rc = iodev->get_buffer(iodev, area, frames);
+	rc = iodev->get_buffer(iodev, &data->area, frames);
 	if (rc < 0 || *frames == 0)
 		return rc;
+
 	if (*frames > frame_requested) {
 		syslog(LOG_ERR,
 		       "frames returned from get_buffer is greater than "
@@ -973,13 +1113,31 @@
 		return -EINVAL;
 	}
 
-	/* TODO(dgreid) - This assumes interleaved audio. */
-	hw_buffer = (*area)->channels[0].buf;
+	iodev->input_frames_read = *frames;
+
+	/* TODO(hychao) - This assumes interleaved audio. */
+	hw_buffer = data->area->channels[0].buf;
+
+
+	/*
+	 * input_dsp_offset records the position where input dsp has applied to
+	 * last time. It's possible the requested |frames| count is smaller
+	 * than the tracked offset. That could happen when client stream uses
+	 * small buffer size and runs APM processing (which requires 10 ms
+	 * equivalent of data to process).
+	 * Only apply input dsp to the part of read buffer beyond where we've
+	 * already applied dsp.
+	 */
+	if (*frames > iodev->input_dsp_offset) {
+		rc = apply_dsp(iodev, hw_buffer +
+			       iodev->input_dsp_offset * frame_bytes,
+			       *frames - iodev->input_dsp_offset);
+		if (rc)
+			return rc;
+	}
 
 	if (cras_system_get_capture_mute())
 		cras_mix_mute_buffer(hw_buffer, frame_bytes, *frames);
-	else
-		apply_dsp(iodev, hw_buffer, *frames); /* TODO-applied 2x */
 
 	return rc;
 }
@@ -1004,6 +1162,11 @@
 int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
 			   struct timespec *level_tstamp)
 {
+	/* If output underruns, reset to avoid incorrect estimated rate. */
+	if ((iodev->direction == CRAS_STREAM_OUTPUT) && !level)
+		rate_estimator_reset_rate(iodev->rate_est,
+					  iodev->ext_format->frame_rate);
+
 	return rate_estimator_check(iodev->rate_est, level, level_tstamp);
 }
 
@@ -1046,9 +1209,18 @@
 	int rc;
 
 	rc = iodev->frames_queued(iodev, hw_tstamp);
-	if (rc < 0 || iodev->direction == CRAS_STREAM_INPUT)
+	if(rc == -EPIPE)
+		cras_audio_thread_severe_underrun();
+
+	if (rc < 0)
 		return rc;
 
+	if (iodev->direction == CRAS_STREAM_INPUT) {
+		if (rc > 0)
+			iodev->input_streaming = 1;
+		return rc;
+	}
+
 	if (rc < iodev->min_buffer_level)
 		return 0;
 
@@ -1106,7 +1278,8 @@
 		/* This assumes consecutive channel areas. */
 		buf = area->channels[0].buf;
 		memset(buf, 0, frames_written * frame_bytes);
-		cras_iodev_put_output_buffer(odev, buf, frames_written);
+		cras_iodev_put_output_buffer(odev, buf, frames_written,
+					     NULL, NULL);
 		frames -= frames_written;
 	}
 
@@ -1114,6 +1287,7 @@
 }
 
 int cras_iodev_output_underrun(struct cras_iodev *odev) {
+	cras_audio_thread_underrun();
 	if (odev->output_underrun)
 		return odev->output_underrun(odev);
 	else
@@ -1137,18 +1311,22 @@
 						unsigned int *hw_level,
 						struct timespec *hw_tstamp)
 {
-	int rc;
-
-	rc = cras_iodev_frames_queued(odev, hw_tstamp);
-	*hw_level = (rc < 0) ? 0 : rc;
+	int rc = cras_iodev_frames_queued(odev, hw_tstamp);
+	unsigned int level = (rc < 0) ? 0 : rc;
+	*hw_level = level;
 
 	if (odev->streams) {
-		/* Schedule that audio thread will wake up when
-		 * hw_level drops to 0.
-		 * This should not cause underrun because audio thread
-		 * should be waken up by the reply from client. */
-		return *hw_level;
+		/* Schedule that audio thread will wake up when hw_level drops to
+		 * 1ms. Normally, this isn't hit because the client will wake us
+		 * up before then. This helps with cases where the hardware
+		 * buffer is smaller than the client stream buffer. */
+		unsigned int one_ms_frames = odev->format->frame_rate / 1000;
+		if (level > one_ms_frames)
+			return level - one_ms_frames;
+		else
+			return level;
 	}
+
 	/* When this device has no stream, schedule audio thread to wake up
 	 * when hw_level drops to min_cb_level so audio thread can fill
 	 * zeros to it. */
@@ -1286,3 +1464,20 @@
 		iodev->set_mute(iodev);
 	return 0;
 }
+
+int cras_iodev_has_pinned_stream(const struct cras_iodev *dev)
+{
+	const struct dev_stream *out;
+	DL_FOREACH(dev->streams, out) {
+		if (out->stream->is_pinned)
+			return 1;
+	}
+	return 0;
+}
+
+void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
+		unsigned int hw_level)
+{
+	iodev->highest_hw_level = MAX(iodev->highest_hw_level, hw_level);
+}
+
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index 51a0003..d40af91 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -19,6 +19,7 @@
 #include "cras_messages.h"
 
 struct buffer_share;
+struct cras_fmt_conv;
 struct cras_ramp;
 struct cras_rstream;
 struct cras_audio_area;
@@ -34,6 +35,9 @@
 			       const struct cras_audio_format *fmt,
 			       void *cb_data);
 
+/* Callback type for an iodev event. */
+typedef int (*iodev_hook_t)();
+
 /* State of an iodev.
  * no_stream state is only supported on output device.
  * Open state is only supported for device supporting start ops.
@@ -66,7 +70,8 @@
  *    software_volume_needed - For output: True if the volume range of the node
  *      is smaller than desired. For input: True if this node needs software
  *      gain.
- *    max_software_gain - The maximum software gain in dBm if needed.
+ *    min_software_gain - The minimum software gain in 0.01 dB if needed.
+ *    max_software_gain - The maximum software gain in 0.01 dB if needed.
  *    stable_id - id for node that doesn't change after unplug/plug.
  *    stable_id_new - New stable_id, it will be deprecated and be put on
  *      stable_id.
@@ -86,6 +91,7 @@
 	char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
 	float *softvol_scalers;
 	int software_volume_needed;
+	long min_software_gain;
 	long max_software_gain;
 	unsigned int stable_id;
 	unsigned int stable_id_new;
@@ -99,6 +105,7 @@
  * set_capture_mute - Function to call if the system capture mute state changes.
  * set_swap_mode_for_node - Function to call to set swap mode for the node.
  * open_dev - Opens the device.
+ * configure_dev - Configures the device.
  * close_dev - Closes the device if it is open.
  * update_supported_formats - Refresh supported frame rates and channel counts.
  * frames_queued - The number of frames in the audio buffer, and fills tstamp
@@ -150,6 +157,9 @@
  * min_buffer_level - Extra frames to keep queued in addition to requested.
  * dsp_context - The context used for dsp processing on the audio data.
  * dsp_name - The "dsp_name" dsp variable specified in the ucm config.
+ * echo_reference_dev - Used only for playback iodev. Pointer to the input
+ *        iodev, which can be used to record what is playing out from this
+ *        iodev. This will be used as the echo reference for echo cancellation.
  * is_enabled - True if this iodev is enabled, false otherwise.
  * software_volume_needed - True if volume control is not supported by hardware.
  * streams - List of audio streams serviced by dev.
@@ -166,9 +176,20 @@
  *     reference.
  * pre_dsp_hook_cb_data - Callback data that will be passing to pre_dsp_hook.
  * post_dsp_hook_cb_data - Callback data that will be passing to post_dsp_hook.
+ * pre_open_iodev_hook - Optional callback to call before iodev open.
+ * post_close_iodev_hook - Optional callback to call after iodev close.
+ * ext_dsp_module - External dsp module to process audio data in stream level
+ *        after dsp_context.
  * reset_request_pending - The flag for pending reset request.
  * ramp - The cras_ramp struct to control ramping up/down at mute/unmute and
  *        start of playback.
+ * input_streaming - For capture only. Indicate if input has started.
+ * input_frames_read - The number of frames read from the device, but that
+ *                     haven't been "put" yet.
+ * input_dsp_offset - The number of frames in the HW buffer that have already
+ *                    been processed by the input DSP.
+ * input_data - Used to pass audio input data to streams with or without
+ *              stream side processing.
  */
 struct cras_iodev {
 	void (*set_volume)(struct cras_iodev *iodev);
@@ -179,6 +200,7 @@
 				      struct cras_ionode *node,
 				      int enable);
 	int (*open_dev)(struct cras_iodev *iodev);
+	int (*configure_dev)(struct cras_iodev *iodev);
 	int (*close_dev)(struct cras_iodev *iodev);
 	int (*update_supported_formats)(struct cras_iodev *iodev);
 	int (*frames_queued)(const struct cras_iodev *iodev,
@@ -216,6 +238,7 @@
 	unsigned int min_buffer_level;
 	struct cras_dsp_context *dsp_context;
 	const char *dsp_name;
+	struct cras_iodev *echo_reference_dev;
 	int is_enabled;
 	int software_volume_needed;
 	struct dev_stream *streams;
@@ -228,8 +251,16 @@
 	loopback_hook_t post_dsp_hook;
 	void *pre_dsp_hook_cb_data;
 	void *post_dsp_hook_cb_data;
+	iodev_hook_t pre_open_iodev_hook;
+	iodev_hook_t post_close_iodev_hook;
+	struct ext_dsp_module *ext_dsp_module;
 	int reset_request_pending;
 	struct cras_ramp* ramp;
+	int input_streaming;
+	unsigned int input_frames_read;
+	unsigned int input_dsp_offset;
+	unsigned int highest_hw_level;
+	struct input_data *input_data;
 	struct cras_iodev *prev, *next;
 };
 
@@ -318,27 +349,6 @@
 				      size_t frame_rate,
 				      struct timespec *ts);
 
-/* Sets the timestamp for when the next sample will be rendered.  Determined by
- * combining the current time with the playback latency specified in frames.
- * Args:
- *    frame_rate - in Hz.
- *    frames - Delay specified in frames.
- *    ts - Filled with the time that the next sample will be played.
- */
-void cras_iodev_set_playback_timestamp(size_t frame_rate,
-				       size_t frames,
-				       struct cras_timespec *ts);
-
-/* Sets the time that the first sample in the buffer was captured at the ADC.
- * Args:
- *    frame_rate - in Hz.
- *    frames - Delay specified in frames.
- *    ts - Filled with the time that the next sample was captured.
- */
-void cras_iodev_set_capture_timestamp(size_t frame_rate,
-				      size_t frames,
-				      struct cras_timespec *ts);
-
 /* Update the "dsp_name" dsp variable. This may cause the dsp pipeline to be
  * reloaded.
  * Args:
@@ -437,6 +447,22 @@
 	return iodev->active_node->software_volume_needed;
 }
 
+/* Returns minimum software gain for the iodev.
+ * Args:
+ *    iodev - The device.
+ * Returs:
+ *    0 if software gain is not needed, or if there is no active node.
+ *    Returns min_software_gain on active node if there is one. */
+static inline long cras_iodev_minimum_software_gain(
+		const struct cras_iodev *iodev)
+{
+	if (!cras_iodev_software_volume_needed(iodev))
+		return 0;
+	if (!iodev->active_node)
+		return 0;
+	return iodev->active_node->min_software_gain;
+}
+
 /* Returns maximum software gain for the iodev.
  * Args:
  *    iodev - The device.
@@ -457,7 +483,7 @@
  * Args:
  *    iodev - The device.
  * Returns:
- *    A scaler translated from system gain and active node gain dBm value.
+ *    A scaler translated from system gain and active node gain.
  *    Returns 1.0 if software gain is not needed. */
 float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev);
 
@@ -495,7 +521,8 @@
 enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev);
 
 /* Open an iodev, does setup and invokes the open_dev callback. */
-int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level);
+int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level,
+		    const struct cras_audio_format *fmt);
 
 /* Open an iodev, does teardown and invokes the close_dev callback. */
 int cras_iodev_close(struct cras_iodev *iodev);
@@ -504,21 +531,19 @@
 int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level);
 
 /* Marks a buffer from get_buffer as read. */
-int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes);
+int cras_iodev_put_input_buffer(struct cras_iodev *iodev);
 
 /* Marks a buffer from get_buffer as written. */
 int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
-				 unsigned int nframes);
+				 unsigned int nframes, int *is_non_empty,
+				 struct cras_fmt_conv *remix_converter);
 
 /* Returns a buffer to read from.
  * Args:
  *    iodev - The device.
- *    area - Filled with a pointer to the audio to read/write.
  *    frames - Filled with the number of frames that can be read/written.
  */
-int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
-				struct cras_audio_area **area,
-				unsigned *frames);
+int cras_iodev_get_input_buffer(struct cras_iodev *iodev, unsigned *frames);
 
 /* Returns a buffer to read from.
  * Args:
@@ -561,6 +586,12 @@
 	return iodev->delay_frames(iodev) + cras_iodev_get_dsp_delay(iodev);
 }
 
+/* Returns if input iodev has started streaming. */
+static inline int cras_iodev_input_streaming(const struct cras_iodev *iodev)
+{
+	return iodev->input_streaming;
+}
+
 /* Returns true if the device is open. */
 static inline int cras_iodev_is_open(const struct cras_iodev *iodev)
 {
@@ -579,6 +610,17 @@
 				       loopback_hook_t loop_cb,
 				       void *cb_data);
 
+/*
+ * Sets the external dsp module for |iodev| and configures the module
+ * accordingly if iodev is already open. This function should be called
+ * in main thread.
+ * Args:
+ *    iodev - The iodev to hold the dsp module.
+ *    ext - External dsp module to set to iodev.
+ */
+void cras_iodev_set_ext_dsp_module(struct cras_iodev *iodev,
+				   struct ext_dsp_module *ext);
+
 /* Put 'frames' worth of zero samples into odev. */
 int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames);
 
@@ -701,4 +743,21 @@
  */
 int cras_iodev_is_zero_volume(const struct cras_iodev *odev);
 
+/*
+ * Checks if a iodev has pinned stream attached to it.
+ * Args:
+ *    dev[in] - The device.
+ * Returns:
+ *    1 if device has pinned stream. 0 otherwise.
+ */
+int cras_iodev_has_pinned_stream(const struct cras_iodev *dev);
+
+/*
+ * Updates the highest hardware level of the device.
+ * Args:
+ *    iodev - The device.
+ */
+void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
+		unsigned int hw_level);
+
 #endif /* CRAS_IODEV_H_ */
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index b89767e..5328af4 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -17,6 +17,7 @@
 #include "cras_tm.h"
 #include "cras_types.h"
 #include "cras_system_state.h"
+#include "server_stream.h"
 #include "stream_list.h"
 #include "test_iodev.h"
 #include "utlist.h"
@@ -38,10 +39,22 @@
  */
 struct enabled_dev {
 	struct cras_iodev *dev;
-	struct cras_timer *init_timer;
 	struct enabled_dev *prev, *next;
 };
 
+struct dev_init_retry {
+	int dev_idx;
+	struct cras_timer *init_timer;
+	struct dev_init_retry *next, *prev;
+};
+
+struct device_enabled_cb {
+	device_enabled_callback_t enabled_cb;
+	device_disabled_callback_t disabled_cb;
+	void *cb_data;
+	struct device_enabled_cb *next, *prev;
+};
+
 /* Lists for devs[CRAS_STREAM_INPUT] and devs[CRAS_STREAM_OUTPUT]. */
 static struct iodev_list devs[CRAS_NUM_DIRECTIONS];
 /* The observer client iodev_list used to listen on various events. */
@@ -50,13 +63,21 @@
 static struct enabled_dev *enabled_devs[CRAS_NUM_DIRECTIONS];
 /* Keep an empty device per direction. */
 static struct cras_iodev *fallback_devs[CRAS_NUM_DIRECTIONS];
+/* Special empty device for hotword streams. */
+static struct cras_iodev *empty_hotword_dev;
+/* Loopback devices. */
+static struct cras_iodev *loopdev_post_mix;
+static struct cras_iodev *loopdev_post_dsp;
+/* List of pending device init retries. */
+static struct dev_init_retry *init_retries;
+
 /* Keep a constantly increasing index for iodevs. Index 0 is reserved
  * to mean "no device". */
 static uint32_t next_iodev_idx = MAX_SPECIAL_DEVICE_IDX;
 
 /* Call when a device is enabled or disabled. */
-static device_enabled_callback_t device_enabled_callback;
-static void *device_enabled_cb_data;
+struct device_enabled_cb *device_enable_cbs;
+
 /* Thread that handles audio input and output. */
 static struct audio_thread *audio_thread;
 /* List of all streams. */
@@ -67,6 +88,8 @@
 static int stream_list_suspended = 0;
 /* If init device failed, retry after 1 second. */
 static const unsigned int INIT_DEV_DELAY_MS = 1000;
+/* Flag to indicate that hotword streams are suspended. */
+static int hotword_suspended = 0;
 
 static void idle_dev_check(struct cras_timer *timer, void *data);
 
@@ -350,25 +373,73 @@
 	}
 }
 
-static int dev_has_pinned_stream(unsigned int dev_idx)
+static void remove_all_streams_from_dev(struct cras_iodev *dev)
 {
-	const struct cras_rstream *rstream;
+	struct cras_rstream *rstream;
+
+	audio_thread_rm_open_dev(audio_thread, dev);
 
 	DL_FOREACH(stream_list_get(stream_list), rstream) {
-		if (rstream->is_pinned && (rstream->pinned_dev_idx == dev_idx))
-			return 1;
+		if (rstream->apm_list == NULL)
+			continue;
+		cras_apm_list_remove(rstream->apm_list, dev);
 	}
+}
+
+/*
+ * If output dev has an echo reference dev associated, add a server
+ * stream to read audio data from it so APM can analyze.
+ */
+static void possibly_enable_echo_reference(struct cras_iodev *dev)
+{
+	if (dev->direction != CRAS_STREAM_OUTPUT)
+		return;
+
+	if (dev->echo_reference_dev == NULL)
+		return;
+
+	server_stream_create(stream_list, dev->echo_reference_dev->info.idx);
+}
+
+/*
+ * If output dev has an echo reference dev associated, check if there
+ * is server stream opened for it and remove it.
+ */
+static void possibly_disable_echo_reference(struct cras_iodev *dev)
+{
+	if (dev->echo_reference_dev == NULL)
+		return;
+
+	server_stream_destroy(stream_list, dev->echo_reference_dev->info.idx);
+}
+
+/*
+ * Close dev if it's opened, without the extra call to idle_dev_check.
+ * This is useful for closing a dev inside idle_dev_check function to
+ * avoid infinite recursive call.
+ *
+ * Returns:
+ *    -EINVAL if device was not opened, otherwise return 0.
+ */
+static int close_dev_without_idle_check(struct cras_iodev *dev)
+{
+	if (!cras_iodev_is_open(dev))
+	       return -EINVAL;
+	if (cras_iodev_has_pinned_stream(dev))
+		syslog(LOG_ERR, "Closing device with pinned streams.");
+
+	remove_all_streams_from_dev(dev);
+	dev->idle_timeout.tv_sec = 0;
+	cras_iodev_close(dev);
+	possibly_disable_echo_reference(dev);
 	return 0;
 }
 
 static void close_dev(struct cras_iodev *dev)
 {
-	if (!cras_iodev_is_open(dev) ||
-	    dev_has_pinned_stream(dev->info.idx))
+	if (close_dev_without_idle_check(dev))
 		return;
-	audio_thread_rm_open_dev(audio_thread, dev);
-	dev->idle_timeout.tv_sec = 0;
-	cras_iodev_close(dev);
+
 	if (idle_timer)
 		cras_tm_cancel_timer(cras_system_state_get_tm(), idle_timer);
 	idle_dev_check(NULL, NULL);
@@ -390,9 +461,7 @@
 		if (edev->dev->idle_timeout.tv_sec == 0)
 			continue;
 		if (timespec_after(&now, &edev->dev->idle_timeout)) {
-			audio_thread_rm_open_dev(audio_thread, edev->dev);
-			edev->dev->idle_timeout.tv_sec = 0;
-			cras_iodev_close(edev->dev);
+			close_dev_without_idle_check(edev->dev);
 			continue;
 		}
 		num_idle_devs++;
@@ -419,6 +488,24 @@
 					  idle_dev_check, NULL);
 }
 
+/*
+ * Cancel pending init tries. Called at device initialization or when device
+ * is disabled.
+ */
+static void cancel_pending_init_retries(unsigned int dev_idx)
+{
+	struct dev_init_retry *retry;
+
+	DL_FOREACH(init_retries, retry) {
+		if (retry->dev_idx != dev_idx)
+			continue;
+		cras_tm_cancel_timer(cras_system_state_get_tm(),
+				     retry->init_timer);
+		DL_DELETE(init_retries, retry);
+		free(retry);
+	}
+}
+
 /* Open the device potentially filling the output with a pre buffer. */
 static int init_device(struct cras_iodev *dev,
 		       struct cras_rstream *rstream)
@@ -429,20 +516,18 @@
 
 	if (cras_iodev_is_open(dev))
 		return 0;
+	cancel_pending_init_retries(dev->info.idx);
 
-	if (dev->ext_format == NULL) {
-		rc = cras_iodev_set_format(dev, &rstream->format);
-		if (rc)
-			return rc;
-	}
-
-	rc = cras_iodev_open(dev, rstream->cb_threshold);
+	rc = cras_iodev_open(dev, rstream->cb_threshold, &rstream->format);
 	if (rc)
 		return rc;
 
 	rc = audio_thread_add_open_dev(audio_thread, dev);
 	if (rc)
 		cras_iodev_close(dev);
+
+	possibly_enable_echo_reference(dev);
+
 	return rc;
 }
 
@@ -455,6 +540,9 @@
 		if (rstream->is_pinned) {
 			struct cras_iodev *dev;
 
+			if ((rstream->flags & HOTWORD_STREAM) == HOTWORD_STREAM)
+				continue;
+
 			dev = find_dev(rstream->pinned_dev_idx);
 			if (dev) {
 				audio_thread_disconnect_stream(audio_thread,
@@ -484,8 +572,11 @@
 	struct cras_rstream *rstream;
 
 	stream_list_suspended = 0;
-	DL_FOREACH(stream_list_get(stream_list), rstream)
+	DL_FOREACH(stream_list_get(stream_list), rstream) {
+		if ((rstream->flags & HOTWORD_STREAM) == HOTWORD_STREAM)
+			continue;
 		stream_added_cb(rstream);
+	}
 }
 
 /* Called when the system audio is suspended or resumed. */
@@ -521,7 +612,7 @@
 	}
 }
 
-static int disable_device(struct enabled_dev *edev);
+static int disable_device(struct enabled_dev *edev, bool force);
 static int enable_device(struct cras_iodev *dev);
 
 static void possibly_disable_fallback(enum CRAS_STREAM_DIRECTION dir)
@@ -530,39 +621,74 @@
 
 	DL_FOREACH(enabled_devs[dir], edev) {
 		if (edev->dev == fallback_devs[dir])
-			disable_device(edev);
+			disable_device(edev, false);
 	}
 }
 
 static void possibly_enable_fallback(enum CRAS_STREAM_DIRECTION dir)
 {
+	if (fallback_devs[dir] == NULL)
+		return;
 	if (!cras_iodev_list_dev_is_enabled(fallback_devs[dir]))
 		enable_device(fallback_devs[dir]);
 }
 
+/*
+ * Adds stream to one or more open iodevs. If the stream has processing effect
+ * turned on, create new APM instance and add to the list. This makes sure the
+ * time consuming APM creation happens in main thread.
+ */
+static int add_stream_to_open_devs(struct cras_rstream *stream,
+				    struct cras_iodev **iodevs,
+				    unsigned int num_iodevs)
+{
+	int i;
+	if (stream->apm_list) {
+		for (i = 0; i < num_iodevs; i++)
+			cras_apm_list_add(stream->apm_list,
+					  iodevs[i],
+					  iodevs[i]->ext_format);
+	}
+	return audio_thread_add_stream(audio_thread,
+				       stream, iodevs, num_iodevs);
+}
+
 static int init_and_attach_streams(struct cras_iodev *dev)
 {
 	int rc;
 	enum CRAS_STREAM_DIRECTION dir = dev->direction;
 	struct cras_rstream *stream;
+	int dev_enabled = cras_iodev_list_dev_is_enabled(dev);
 
 	/* If called after suspend, for example bluetooth
 	 * profile switching, don't add back the stream list. */
-	if (!stream_list_suspended) {
-		/* If there are active streams to attach to this device,
-		 * open it. */
-		DL_FOREACH(stream_list_get(stream_list), stream) {
-			if (stream->direction != dir || stream->is_pinned)
-				continue;
-			rc = init_device(dev, stream);
-			if (rc) {
-				syslog(LOG_ERR, "Enable %s failed, rc = %d",
-				       dev->info.name, rc);
-				return rc;
-			}
-			audio_thread_add_stream(audio_thread,
-						stream, &dev, 1);
+	if (stream_list_suspended)
+		return 0;
+
+	/* If there are active streams to attach to this device,
+	 * open it. */
+	DL_FOREACH(stream_list_get(stream_list), stream) {
+		if (stream->direction != dir)
+			continue;
+		/*
+		 * Don't attach this stream if (1) this stream pins to a
+		 * different device, or (2) this is a normal stream, but
+		 * device is not enabled.
+		 */
+		if(stream->is_pinned) {
+		   if (stream->pinned_dev_idx != dev->info.idx)
+			continue;
+		} else if (!dev_enabled) {
+			continue;
 		}
+
+		rc = init_device(dev, stream);
+		if (rc) {
+			syslog(LOG_ERR, "Enable %s failed, rc = %d",
+			       dev->info.name, rc);
+			return rc;
+		}
+		add_stream_to_open_devs(stream, &dev, 1);
 	}
 	return 0;
 }
@@ -570,10 +696,16 @@
 static void init_device_cb(struct cras_timer *timer, void *arg)
 {
 	int rc;
-	struct enabled_dev *edev = (struct enabled_dev *)arg;
-	struct cras_iodev *dev = edev->dev;
+	struct dev_init_retry *retry = (struct dev_init_retry *)arg;
+	struct cras_iodev *dev = find_dev(retry->dev_idx);
 
-	edev->init_timer = NULL;
+	/*
+	 * First of all, remove retry record to avoid confusion to the
+	 * actual device init work.
+	 */
+	DL_DELETE(init_retries, retry);
+	free(retry);
+
 	if (cras_iodev_is_open(dev))
 		return;
 
@@ -584,24 +716,29 @@
 		possibly_disable_fallback(dev->direction);
 }
 
-static void schedule_init_device_retry(struct enabled_dev *edev)
+static int schedule_init_device_retry(struct cras_iodev *dev)
 {
+	struct dev_init_retry *retry;
 	struct cras_tm *tm = cras_system_state_get_tm();
 
-	if (edev->init_timer == NULL)
-		edev->init_timer = cras_tm_create_timer(
-				tm, INIT_DEV_DELAY_MS, init_device_cb, edev);
+	retry = (struct dev_init_retry *)calloc(1, sizeof(*retry));
+	if (!retry)
+		return -ENOMEM;
+
+	retry->dev_idx = dev->info.idx;
+	retry->init_timer = cras_tm_create_timer(
+			tm, INIT_DEV_DELAY_MS, init_device_cb, retry);
+	DL_APPEND(init_retries, retry);
+	return 0;
 }
 
-static int pinned_stream_added(struct cras_rstream *rstream)
+static int init_pinned_device(struct cras_iodev *dev,
+			      struct cras_rstream *rstream)
 {
-	struct cras_iodev *dev;
 	int rc;
 
-	/* Check that the target device is valid for pinned streams. */
-	dev = find_dev(rstream->pinned_dev_idx);
-	if (!dev)
-		return -EINVAL;
+	if (audio_thread_is_dev_open(audio_thread, dev))
+		return 0;
 
 	/* Make sure the active node is configured properly, it could be
 	 * disabled when last normal stream removed. */
@@ -611,8 +748,54 @@
 	rc = init_device(dev, rstream);
 	if (rc && (rc != -EAGAIN))
 		return rc;
+	return 0;
+}
 
-	return audio_thread_add_stream(audio_thread, rstream, &dev, 1);
+static int close_pinned_device(struct cras_iodev *dev)
+{
+	close_dev(dev);
+	dev->update_active_node(dev, dev->active_node->idx, 0);
+	return 0;
+}
+
+static struct cras_iodev *find_pinned_device(struct cras_rstream *rstream)
+{
+	struct cras_iodev *dev;
+	if (!rstream->is_pinned)
+		return NULL;
+
+	dev = find_dev(rstream->pinned_dev_idx);
+
+	if ((rstream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
+		return dev;
+
+	/* Double check node type for hotword stream */
+	if (dev && dev->active_node->type != CRAS_NODE_TYPE_HOTWORD) {
+		syslog(LOG_ERR, "Hotword stream pinned to invalid dev %u",
+		       dev->info.idx);
+		return NULL;
+	}
+
+	return hotword_suspended ? empty_hotword_dev : dev;
+}
+
+static int pinned_stream_added(struct cras_rstream *rstream)
+{
+	struct cras_iodev *dev;
+	int rc;
+
+	/* Check that the target device is valid for pinned streams. */
+	dev = find_pinned_device(rstream);
+	if (!dev)
+		return -EINVAL;
+
+	rc = init_pinned_device(dev, rstream);
+	if (rc) {
+		syslog(LOG_INFO, "init_pinned_device failed, rc %d", rc);
+		return schedule_init_device_retry(dev);
+	}
+
+	return add_stream_to_open_devs(rstream, &dev, 1);
 }
 
 static int stream_added_cb(struct cras_rstream *rstream)
@@ -644,15 +827,14 @@
 			 */
 			syslog(LOG_ERR, "Init %s failed, rc = %d",
 			       edev->dev->info.name, rc);
-			schedule_init_device_retry(edev);
+			schedule_init_device_retry(edev->dev);
 			continue;
 		}
 
 		iodevs[num_iodevs++] = edev->dev;
 	}
 	if (num_iodevs) {
-		rc = audio_thread_add_stream(audio_thread, rstream,
-					     iodevs, num_iodevs);
+		rc = add_stream_to_open_devs(rstream, iodevs, num_iodevs);
 		if (rc) {
 			syslog(LOG_ERR, "adding stream to thread fail");
 			return rc;
@@ -687,7 +869,7 @@
 	/* No more default streams, close any device that doesn't have a stream
 	 * pinned to it. */
 	DL_FOREACH(enabled_devs[dir], edev) {
-		if (dev_has_pinned_stream(edev->dev->info.idx))
+		if (cras_iodev_has_pinned_stream(edev->dev))
 			continue;
 		if (dir == CRAS_STREAM_INPUT) {
 			close_dev(edev->dev);
@@ -706,11 +888,12 @@
 {
 	struct cras_iodev *dev;
 
-	dev = find_dev(rstream->pinned_dev_idx);
-	if (!cras_iodev_list_dev_is_enabled(dev)) {
-		close_dev(dev);
-		dev->update_active_node(dev, dev->active_node->idx, 0);
-	}
+	dev = find_pinned_device(rstream);
+	if (!dev)
+		return;
+	if (!cras_iodev_list_dev_is_enabled(dev) &&
+	    !cras_iodev_has_pinned_stream(dev))
+		close_pinned_device(dev);
 }
 
 /* Returns the number of milliseconds left to drain this stream.  This is passed
@@ -737,6 +920,7 @@
 	int rc;
 	struct enabled_dev *edev;
 	enum CRAS_STREAM_DIRECTION dir = dev->direction;
+	struct device_enabled_cb *callback;
 
 	DL_FOREACH(enabled_devs[dir], edev) {
 		if (edev->dev == dev)
@@ -745,45 +929,55 @@
 
 	edev = calloc(1, sizeof(*edev));
 	edev->dev = dev;
-	edev->init_timer = NULL;
 	DL_APPEND(enabled_devs[dir], edev);
 	dev->is_enabled = 1;
 
 	rc = init_and_attach_streams(dev);
 	if (rc < 0) {
-		schedule_init_device_retry(edev);
+		syslog(LOG_INFO, "Enable device fail, rc %d", rc);
+		schedule_init_device_retry(dev);
 		return rc;
 	}
 
-	if (device_enabled_callback)
-		device_enabled_callback(dev, 1, device_enabled_cb_data);
+	DL_FOREACH(device_enable_cbs, callback)
+		callback->enabled_cb(dev, callback->cb_data);
 
 	return 0;
 }
 
-static int disable_device(struct enabled_dev *edev)
+/* Set `force to true to flush any pinned streams before closing the device. */
+static int disable_device(struct enabled_dev *edev, bool force)
 {
 	struct cras_iodev *dev = edev->dev;
 	enum CRAS_STREAM_DIRECTION dir = dev->direction;
 	struct cras_rstream *stream;
+	struct device_enabled_cb *callback;
 
+	/*
+	 * Remove from enabled dev list. However this dev could have a stream
+	 * pinned to it, only cancel pending init timers when force flag is set.
+	 */
 	DL_DELETE(enabled_devs[dir], edev);
-	if (edev->init_timer) {
-		cras_tm_cancel_timer(cras_system_state_get_tm(),
-				     edev->init_timer);
-		edev->init_timer = NULL;
-	}
 	free(edev);
 	dev->is_enabled = 0;
+	if (force)
+		cancel_pending_init_retries(dev->info.idx);
 
-	/* Pull all default streams off this device. */
+	/*
+	 * Pull all default streams off this device.
+	 * Pull all pinned streams off as well if force is true.
+	 */
 	DL_FOREACH(stream_list_get(stream_list), stream) {
-		if (stream->direction != dev->direction || stream->is_pinned)
+		if (stream->direction != dev->direction)
+			continue;
+		if (stream->is_pinned && !force)
 			continue;
 		audio_thread_disconnect_stream(audio_thread, stream, dev);
 	}
-	if (device_enabled_callback)
-		device_enabled_callback(dev, 0, device_enabled_cb_data);
+	if (cras_iodev_has_pinned_stream(dev))
+		return 0;
+	DL_FOREACH(device_enable_cbs, callback)
+		callback->disabled_cb(dev, callback->cb_data);
 	close_dev(dev);
 	dev->update_active_node(dev, dev->active_node->idx, 0);
 
@@ -791,6 +985,34 @@
 }
 
 /*
+ * Assume the device is not in enabled_devs list.
+ * Assume there is no default stream on the device.
+ * An example is that this device is unplugged while it is playing
+ * a pinned stream. The device and stream may have been removed in
+ * audio thread due to I/O error handling.
+ */
+static int force_close_pinned_only_device(struct cras_iodev *dev)
+{
+	struct cras_rstream *rstream;
+
+	/* Pull pinned streams off this device. */
+	DL_FOREACH(stream_list_get(stream_list), rstream) {
+		if (rstream->direction != dev->direction)
+			continue;
+		if (!rstream->is_pinned)
+			continue;
+		if (dev->info.idx != rstream->pinned_dev_idx)
+			continue;
+		audio_thread_disconnect_stream(audio_thread, rstream, dev);
+	}
+	if (cras_iodev_has_pinned_stream(dev))
+		return -EEXIST;
+	close_dev(dev);
+	dev->update_active_node(dev, dev->active_node->idx, 0);
+	return 0;
+}
+
+/*
  * Exported Interface.
  */
 
@@ -815,16 +1037,22 @@
 
 	/* Add an empty device so there is always something to play to or
 	 * capture from. */
-	fallback_devs[CRAS_STREAM_OUTPUT] =
-			empty_iodev_create(CRAS_STREAM_OUTPUT);
-	fallback_devs[CRAS_STREAM_INPUT] =
-			empty_iodev_create(CRAS_STREAM_INPUT);
+	fallback_devs[CRAS_STREAM_OUTPUT] = empty_iodev_create(
+			CRAS_STREAM_OUTPUT,
+			CRAS_NODE_TYPE_UNKNOWN);
+	fallback_devs[CRAS_STREAM_INPUT] = empty_iodev_create(
+			CRAS_STREAM_INPUT,
+			CRAS_NODE_TYPE_UNKNOWN);
 	enable_device(fallback_devs[CRAS_STREAM_OUTPUT]);
 	enable_device(fallback_devs[CRAS_STREAM_INPUT]);
 
+	empty_hotword_dev = empty_iodev_create(
+			CRAS_STREAM_INPUT,
+			CRAS_NODE_TYPE_HOTWORD);
+
 	/* Create loopback devices. */
-	loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
-	loopback_iodev_create(LOOPBACK_POST_DSP);
+	loopdev_post_mix = loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
+	loopdev_post_dsp = loopback_iodev_create(LOOPBACK_POST_DSP);
 
 	audio_thread = audio_thread_create();
 	if (!audio_thread) {
@@ -838,12 +1066,17 @@
 
 void cras_iodev_list_deinit()
 {
+	audio_thread_destroy(audio_thread);
+	loopback_iodev_destroy(loopdev_post_dsp);
+	loopback_iodev_destroy(loopdev_post_mix);
+	empty_iodev_destroy(empty_hotword_dev);
+	empty_iodev_destroy(fallback_devs[CRAS_STREAM_INPUT]);
+	empty_iodev_destroy(fallback_devs[CRAS_STREAM_OUTPUT]);
+	stream_list_destroy(stream_list);
 	if (list_observer) {
 		cras_observer_remove(list_observer);
 		list_observer = NULL;
 	}
-	audio_thread_destroy(audio_thread);
-	stream_list_destroy(stream_list);
 }
 
 int cras_iodev_list_dev_is_enabled(const struct cras_iodev *dev)
@@ -861,6 +1094,8 @@
 void cras_iodev_list_enable_dev(struct cras_iodev *dev)
 {
 	possibly_disable_fallback(dev->direction);
+	/* Enable ucm setting of active node. */
+	dev->update_active_node(dev, dev->active_node->idx, 1);
 	enable_device(dev);
 	cras_iodev_list_notify_active_node_changed(dev->direction);
 }
@@ -873,11 +1108,25 @@
 	if (!new_dev || new_dev->direction != dir)
 		return;
 
+	/* If the new dev is already enabled but its active node needs to be
+	 * changed. Disable new dev first, update active node, and then
+	 * re-enable it again.
+	 */
+	if (cras_iodev_list_dev_is_enabled(new_dev)) {
+		if (node_index_of(node_id) == new_dev->active_node->idx)
+			return;
+		else
+			cras_iodev_list_disable_dev(new_dev, true);
+	}
+
 	new_dev->update_active_node(new_dev, node_index_of(node_id), 1);
 	cras_iodev_list_enable_dev(new_dev);
 }
 
-void cras_iodev_list_disable_dev(struct cras_iodev *dev)
+/*
+ * Disables device which may or may not be in enabled_devs list.
+ */
+void cras_iodev_list_disable_dev(struct cras_iodev *dev, bool force_close)
 {
 	struct enabled_dev *edev, *edev_to_disable = NULL;
 
@@ -890,16 +1139,25 @@
 			is_the_only_enabled_device = 0;
 	}
 
-	if (!edev_to_disable)
+	/*
+	 * Disables the device for these two cases:
+	 * 1. Disable a device in the enabled_devs list.
+	 * 2. Force close a device that is not in the enabled_devs list,
+	 *    but it is running a pinned stream.
+	 */
+	if (!edev_to_disable) {
+		if (force_close)
+			force_close_pinned_only_device(dev);
 		return;
+	}
 
 	/* If the device to be closed is the only enabled device, we should
 	 * enable the fallback device first then disable the target
 	 * device. */
-	if (is_the_only_enabled_device)
+	if (is_the_only_enabled_device && fallback_devs[dev->direction])
 		enable_device(fallback_devs[dev->direction]);
 
-	disable_device(edev_to_disable);
+	disable_device(edev_to_disable, force_close);
 
 	cras_iodev_list_notify_active_node_changed(dev->direction);
 	return;
@@ -914,7 +1172,7 @@
 	if (!dev)
 		return;
 
-	cras_iodev_list_disable_dev(dev);
+	cras_iodev_list_disable_dev(dev, false);
 }
 
 int cras_iodev_list_add_output(struct cras_iodev *output)
@@ -952,7 +1210,7 @@
 	/* Retire the current active output device before removing it from
 	 * list, otherwise it could be busy and remain in the list.
 	 */
-	cras_iodev_list_disable_dev(dev);
+	cras_iodev_list_disable_dev(dev, true);
 	res = rm_dev_from_list(dev);
 	if (res == 0)
 		cras_iodev_list_update_device_list();
@@ -966,7 +1224,7 @@
 	/* Retire the current active input device before removing it from
 	 * list, otherwise it could be busy and remain in the list.
 	 */
-	cras_iodev_list_disable_dev(dev);
+	cras_iodev_list_disable_dev(dev, true);
 	res = rm_dev_from_list(dev);
 	if (res == 0)
 		cras_iodev_list_update_device_list();
@@ -1028,6 +1286,97 @@
 	cras_system_state_update_complete();
 }
 
+/* Look up the first hotword stream and the device it pins to. */
+int find_hotword_stream_dev(struct cras_iodev **dev,
+			    struct cras_rstream **stream)
+{
+	DL_FOREACH(stream_list_get(stream_list), *stream) {
+		if (((*stream)->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
+			continue;
+
+		*dev = find_dev((*stream)->pinned_dev_idx);
+		if (*dev == NULL)
+			return -ENOENT;
+		break;
+	}
+	return 0;
+}
+
+/* Suspend/resume hotword streams functions are used to provide seamless
+ * experience to cras clients when there's hardware limitation about concurrent
+ * DSP and normal recording. The empty hotword iodev is used to hold all
+ * hotword streams during suspend, so client side will not know about the
+ * transition, and can still remove or add streams. At resume, the real hotword
+ * device will be initialized and opened again to re-arm the DSP.
+ */
+int cras_iodev_list_suspend_hotword_streams()
+{
+	struct cras_iodev *hotword_dev;
+	struct cras_rstream *stream = NULL;
+	int rc;
+
+	rc = find_hotword_stream_dev(&hotword_dev, &stream);
+	if (rc)
+		return rc;
+
+	if (stream == NULL) {
+		hotword_suspended = 1;
+		return 0;
+	}
+	/* Move all existing hotword streams to the empty hotword iodev. */
+	init_pinned_device(empty_hotword_dev, stream);
+	DL_FOREACH(stream_list_get(stream_list), stream) {
+		if ((stream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
+			continue;
+		if (stream->pinned_dev_idx != hotword_dev->info.idx) {
+			syslog(LOG_ERR,
+			       "Failed to suspend hotword stream on dev %u",
+				stream->pinned_dev_idx);
+			continue;
+		}
+
+		audio_thread_disconnect_stream(audio_thread, stream, hotword_dev);
+		audio_thread_add_stream(audio_thread, stream, &empty_hotword_dev, 1);
+	}
+	close_pinned_device(hotword_dev);
+	hotword_suspended = 1;
+	return 0;
+}
+
+int cras_iodev_list_resume_hotword_stream()
+{
+	struct cras_iodev *hotword_dev;
+	struct cras_rstream *stream = NULL;
+	int rc;
+
+	rc = find_hotword_stream_dev(&hotword_dev, &stream);
+	if (rc)
+		return rc;
+
+	if (stream == NULL) {
+		hotword_suspended = 0;
+		return 0;
+	}
+	/* Move all existing hotword streams to the real hotword iodev. */
+	init_pinned_device(hotword_dev, stream);
+	DL_FOREACH(stream_list_get(stream_list), stream) {
+		if ((stream->flags & HOTWORD_STREAM) != HOTWORD_STREAM)
+			continue;
+		if (stream->pinned_dev_idx != hotword_dev->info.idx) {
+			syslog(LOG_ERR,
+			       "Fail to resume hotword stream on dev %u",
+			       stream->pinned_dev_idx);
+			continue;
+		}
+
+		audio_thread_disconnect_stream(audio_thread, stream, empty_hotword_dev);
+		audio_thread_add_stream(audio_thread, stream, &hotword_dev, 1);
+	}
+	close_pinned_device(empty_hotword_dev);
+	hotword_suspended = 0;
+	return 0;
+}
+
 char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id)
 {
 	struct cras_iodev *dev = NULL;
@@ -1112,7 +1461,7 @@
 	DL_FOREACH(enabled_devs[direction], edev) {
 		if (edev->dev != fallback_devs[direction] &&
 		    !(new_node_already_enabled && edev->dev == new_dev)) {
-			disable_device(edev);
+			disable_device(edev, false);
 		}
 	}
 
@@ -1197,22 +1546,29 @@
 }
 
 int cras_iodev_list_set_device_enabled_callback(
-		device_enabled_callback_t device_enabled_cb, void *cb_data)
+		device_enabled_callback_t enabled_cb,
+		device_disabled_callback_t disabled_cb,
+		void *cb_data)
 {
-	if (!device_enabled_cb) {
-		device_enabled_callback = NULL;
-		device_enabled_cb_data = NULL;
-		return 0;
+	struct device_enabled_cb *callback;
+
+	DL_FOREACH(device_enable_cbs, callback) {
+		if (callback->cb_data != cb_data)
+			continue;
+
+		DL_DELETE(device_enable_cbs, callback);
+		free(callback);
 	}
 
-	/* TODO(chinyue): Allow multiple callbacks to be registered. */
-	if (device_enabled_callback) {
-		syslog(LOG_ERR, "Device enabled callback already registered.");
-		return -EEXIST;
+	if (enabled_cb && disabled_cb) {
+		callback = (struct device_enabled_cb *)
+				calloc(1, sizeof(*callback));
+		callback->enabled_cb = enabled_cb;
+		callback->disabled_cb = disabled_cb;
+		callback->cb_data = cb_data;
+		DL_APPEND(device_enable_cbs, callback);
 	}
 
-	device_enabled_callback = device_enabled_cb;
-	device_enabled_cb_data = cb_data;
 	return 0;
 }
 
diff --git a/cras/src/server/cras_iodev_list.h b/cras/src/server/cras_iodev_list.h
index 3599f1b..96c7306 100644
--- a/cras/src/server/cras_iodev_list.h
+++ b/cras/src/server/cras_iodev_list.h
@@ -9,6 +9,7 @@
 #ifndef CRAS_IODEV_LIST_H_
 #define CRAS_IODEV_LIST_H_
 
+#include <stdbool.h>
 #include <stdint.h>
 
 #include "cras_types.h"
@@ -21,11 +22,9 @@
 struct cras_audio_format;
 struct stream_list;
 
-/* Device enabled/disabled callback.
- * enabled=1 when a device is enabled, enabled=0 when a device is disabled.
- */
-typedef void (*device_enabled_callback_t)(struct cras_iodev *dev, int enabled,
-					  void *cb_data);
+/* Device enabled/disabled callback. */
+typedef void (*device_enabled_callback_t)(struct cras_iodev *dev, void *cb_data);
+typedef void (*device_disabled_callback_t)(struct cras_iodev *dev, void *cb_data);
 
 /* Initialize the list of iodevs. */
 void cras_iodev_list_init();
@@ -157,9 +156,13 @@
  * call will disable it. */
 void cras_iodev_list_enable_dev(struct cras_iodev *dev);
 
-/* Disables an iodev. If this is the last device to disable, the
- * fallback devices will be enabled accordingly. */
-void cras_iodev_list_disable_dev(struct cras_iodev *dev);
+/*
+ * Disables an iodev. If this is the last device to disable, the
+ * fallback devices will be enabled accordingly.
+ * Set `foce_close` to true if the device must be closed regardless of having
+ * pinned streams attached.
+ */
+void cras_iodev_list_disable_dev(struct cras_iodev *dev, bool force_close);
 
 /* Adds a node to the active devices list.
  * Args:
@@ -206,7 +209,15 @@
 
 /* Sets the function to call when a device is enabled or disabled. */
 int cras_iodev_list_set_device_enabled_callback(
-		device_enabled_callback_t device_enabled_cb, void *cb_data);
+		device_enabled_callback_t enabled_cb,
+		device_disabled_callback_t disabled_cb,
+		void *cb_data);
+
+/* Suspends all hotwording streams. */
+int cras_iodev_list_suspend_hotword_streams();
+
+/* Resumes all hotwording streams. */
+int cras_iodev_list_resume_hotword_stream();
 
 /* For unit test only. */
 void cras_iodev_list_reset();
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index ff33aca..2598089 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -64,7 +64,7 @@
 	if (!edev || !edev->streams)
 		return 0;
 
-	frames_to_copy = MIN(buf_writable_bytes(sbuf) / frame_bytes, nframes);
+	frames_to_copy = MIN(buf_writable(sbuf) / frame_bytes, nframes);
 	if (!frames_to_copy)
 		return 0;
 
@@ -91,8 +91,7 @@
 		cras_iodev_register_post_dsp_hook(iodev, hook, cb_data);
 }
 
-static void device_enabled_hook(struct cras_iodev *iodev, int enabled,
-				void *cb_data)
+static void device_enabled_hook(struct cras_iodev *iodev, void *cb_data)
 {
 	struct loopback_iodev *loopdev = (struct loopback_iodev *)cb_data;
 	struct cras_iodev *edev;
@@ -100,17 +99,23 @@
 	if (iodev->direction != CRAS_STREAM_OUTPUT)
 		return;
 
-	if (!enabled) {
-		/* Unregister loopback hook from disabled iodev. */
-		register_loopback_hook(loopdev->loopback_type, iodev, NULL,
-				       NULL);
-	} else {
-		/* Register loopback hook onto first enabled iodev. */
-		edev = cras_iodev_list_get_first_enabled_iodev(
-				CRAS_STREAM_OUTPUT);
-		register_loopback_hook(loopdev->loopback_type, edev,
-				       sample_hook, cb_data);
-	}
+	/* Register loopback hook onto first enabled iodev. */
+	edev = cras_iodev_list_get_first_enabled_iodev(
+			CRAS_STREAM_OUTPUT);
+	register_loopback_hook(loopdev->loopback_type, edev,
+			       sample_hook, cb_data);
+}
+
+static void device_disabled_hook(struct cras_iodev *iodev, void *cb_data)
+{
+	struct loopback_iodev *loopdev = (struct loopback_iodev *)cb_data;
+
+	if (iodev->direction != CRAS_STREAM_OUTPUT)
+		return;
+
+	/* Unregister loopback hook from disabled iodev. */
+	register_loopback_hook(loopdev->loopback_type, iodev, NULL,
+			       NULL);
 }
 
 /*
@@ -132,7 +137,7 @@
 		frames_since_last = cras_frames_since_time(
 				&loopdev->last_filled,
 				iodev->format->frame_rate);
-		frames_to_fill = MIN(buf_writable_bytes(sbuf) / frame_bytes,
+		frames_to_fill = MIN(buf_writable(sbuf) / frame_bytes,
 				     frames_since_last);
 		if (frames_to_fill > 0) {
 			bytes_to_fill = frames_to_fill * frame_bytes;
@@ -143,7 +148,7 @@
 		}
 	}
 	*hw_tstamp = loopdev->last_filled;
-	return buf_queued_bytes(sbuf) / frame_bytes;
+	return buf_queued(sbuf) / frame_bytes;
 }
 
 static int delay_frames(const struct cras_iodev *iodev)
@@ -165,12 +170,12 @@
 
 	edev = cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT);
 	register_loopback_hook(loopdev->loopback_type, edev, NULL, NULL);
-	cras_iodev_list_set_device_enabled_callback(NULL, NULL);
+	cras_iodev_list_set_device_enabled_callback(NULL, NULL, (void *)iodev);
 
 	return 0;
 }
 
-static int open_record_dev(struct cras_iodev *iodev)
+static int configure_record_dev(struct cras_iodev *iodev)
 {
 	struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
 	struct cras_iodev *edev;
@@ -182,6 +187,7 @@
 	register_loopback_hook(loopdev->loopback_type, edev, sample_hook,
 			       (void *)iodev);
 	cras_iodev_list_set_device_enabled_callback(device_enabled_hook,
+						    device_disabled_hook,
 						    (void *)iodev);
 
 	return 0;
@@ -194,7 +200,7 @@
 	struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
 	struct byte_buffer *sbuf = loopdev->sample_buffer;
 	unsigned int frame_bytes = cras_get_format_bytes(iodev->format);
-	unsigned int avail_frames = buf_readable_bytes(sbuf) / frame_bytes;
+	unsigned int avail_frames = buf_readable(sbuf) / frame_bytes;
 
 	*frames = MIN(avail_frames, *frames);
 	iodev->area->frames = *frames;
@@ -220,7 +226,7 @@
 {
 	struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
 	struct byte_buffer *sbuf = loopdev->sample_buffer;
-	unsigned int queued_bytes = buf_queued_bytes(sbuf);
+	unsigned int queued_bytes = buf_queued(sbuf);
 	buf_increment_read(sbuf, queued_bytes);
 	return 0;
 }
@@ -265,7 +271,7 @@
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
 	iodev->update_active_node = update_active_node;
-	iodev->open_dev = open_record_dev;
+	iodev->configure_dev = configure_record_dev;
 	iodev->close_dev = close_record_dev;
 	iodev->get_buffer = get_record_buffer;
 	iodev->put_buffer = put_record_buffer;
@@ -324,7 +330,8 @@
 	struct byte_buffer *sbuf = loopdev->sample_buffer;
 
 	cras_iodev_list_rm_input(iodev);
+        free(iodev->nodes);
 
-	byte_buffer_destroy(sbuf);
+	byte_buffer_destroy(&sbuf);
 	free(loopdev);
 }
diff --git a/cras/src/server/cras_main_message.h b/cras/src/server/cras_main_message.h
index ba6b02a..5a2bb28 100644
--- a/cras/src/server/cras_main_message.h
+++ b/cras/src/server/cras_main_message.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
@@ -14,9 +14,12 @@
 enum CRAS_MAIN_MESSAGE_TYPE {
 	/* Audio thread -> main thread */
 	CRAS_MAIN_A2DP,
+	CRAS_MAIN_AUDIO_THREAD_EVENT,
 	CRAS_MAIN_BT,
 	CRAS_MAIN_METRICS,
 	CRAS_MAIN_MONITOR_DEVICE,
+	CRAS_MAIN_HOTWORD_TRIGGERED,
+	CRAS_MAIN_NON_EMPTY_AUDIO_STATE,
 };
 
 /* Structure of the header of the message handled by main thread.
diff --git a/cras/src/server/cras_non_empty_audio_handler.c b/cras/src/server/cras_non_empty_audio_handler.c
new file mode 100644
index 0000000..9482453
--- /dev/null
+++ b/cras/src/server/cras_non_empty_audio_handler.c
@@ -0,0 +1,60 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cras_main_message.h"
+#include "cras_observer.h"
+#include "cras_system_state.h"
+
+struct non_empty_audio_msg {
+	struct cras_main_message header;
+	int32_t non_empty;
+};
+
+/* The following functions are called from audio thread. */
+
+static void init_non_empty_audio_msg(struct non_empty_audio_msg *msg)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->header.type = CRAS_MAIN_NON_EMPTY_AUDIO_STATE;
+	msg->header.length = sizeof(*msg);
+}
+
+int cras_non_empty_audio_send_msg(int32_t non_empty)
+{
+	struct non_empty_audio_msg msg;
+	int rc;
+
+	init_non_empty_audio_msg(&msg);
+	msg.non_empty = non_empty;
+
+	rc = cras_main_message_send((struct cras_main_message *)&msg);
+	if (rc < 0)
+		syslog(LOG_ERR, "Failed to send non-empty audio message!");
+
+	return rc;
+}
+
+/* The following functions are called from main thread. */
+
+static void handle_non_empty_audio_message(struct cras_main_message *msg,
+					   void *arg)
+{
+	struct non_empty_audio_msg *audio_msg =
+			(struct non_empty_audio_msg *)msg;
+
+	cras_system_state_set_non_empty_status(audio_msg->non_empty);
+	cras_observer_notify_non_empty_audio_state_changed(audio_msg->non_empty);
+}
+
+int cras_non_empty_audio_handler_init()
+{
+	cras_main_message_add_handler(CRAS_MAIN_NON_EMPTY_AUDIO_STATE,
+				      handle_non_empty_audio_message, NULL);
+	return 0;
+}
\ No newline at end of file
diff --git a/cras/src/server/cras_non_empty_audio_handler.h b/cras/src/server/cras_non_empty_audio_handler.h
new file mode 100644
index 0000000..dac6b22
--- /dev/null
+++ b/cras/src/server/cras_non_empty_audio_handler.h
@@ -0,0 +1,25 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The non-empty audio state handler is used to send a DBus signal when the
+ * system-level non-empty audio state changes.
+ *
+ * cras_non_empty_audio_msg() is called from audio thread to update the
+ * non-empty audio state in the main thread, which in turn sends the DBus
+ * signal.
+ *
+ * cras_non_empty_audio_handler_init() is used to setup the message handler
+ * in the main thread to handle the non-empty audiomessage from audio thread.
+ */
+
+#ifndef CRAS_NON_EMPTY_AUDIO_HANDLER_H_
+#define CRAS_NON_EMPTY_AUDIO_HANDLER_H_
+
+/* Send non-empty audio state message. */
+int cras_non_empty_audio_send_msg(int32_t non_empty);
+
+/* Initialize non-empty audio handler. */
+int cras_non_empty_audio_handler_init();
+
+#endif /* CRAS_NON_EMPTY_AUDIO_HANDLER_H_ */
diff --git a/cras/src/server/cras_observer.c b/cras/src/server/cras_observer.c
index d0366d5..caff00d 100644
--- a/cras/src/server/cras_observer.c
+++ b/cras/src/server/cras_observer.c
@@ -26,12 +26,14 @@
 	struct cras_alert *node_left_right_swapped;
 	struct cras_alert *input_node_gain;
 	struct cras_alert *suspend_changed;
+	struct cras_alert *hotword_triggered;
 	/* If all events for active streams went through a single alert then
          * we might miss some because the alert code does not send every
          * alert message. To ensure that the event sent contains the correct
          * number of active streams per direction, make the alerts
          * per-direciton. */
 	struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS];
+	struct cras_alert *non_empty_audio_state_changed;
 };
 
 struct cras_observer_server {
@@ -73,6 +75,15 @@
 	uint32_t num_active_streams;
 };
 
+struct cras_observer_alert_data_hotword_triggered {
+	int64_t tv_sec;
+	int64_t tv_nsec;
+};
+
+struct cras_observer_non_empty_audio_state {
+	int non_empty;
+};
+
 /* Global observer instance. */
 static struct cras_observer_server *g_observer;
 
@@ -246,6 +257,36 @@
 	}
 }
 
+static void hotword_triggered_alert(void *arg, void *data)
+{
+	struct cras_observer_client *client;
+	struct cras_observer_alert_data_hotword_triggered *triggered_data =
+		(struct cras_observer_alert_data_hotword_triggered *)data;
+
+	DL_FOREACH(g_observer->clients, client) {
+		if (client->ops.hotword_triggered)
+			client->ops.hotword_triggered(
+					client->context,
+					triggered_data->tv_sec,
+					triggered_data->tv_nsec);
+	}
+}
+
+static void non_empty_audio_state_changed_alert(void *arg, void *data)
+{
+	struct cras_observer_client *client;
+	struct cras_observer_non_empty_audio_state *non_empty_audio_data =
+			(struct cras_observer_non_empty_audio_state *)data;
+
+	DL_FOREACH(g_observer->clients, client) {
+		if (client->ops.non_empty_audio_state_changed) {
+			client->ops.non_empty_audio_state_changed(
+					client->context,
+					non_empty_audio_data->non_empty);
+		}
+	}
+}
+
 static int cras_observer_server_set_alert(struct cras_alert **alert,
 					  cras_alert_cb cb,
 					  cras_alert_prepare prepare,
@@ -300,6 +341,8 @@
 	CRAS_OBSERVER_SET_ALERT(node_left_right_swapped, NULL, 0);
 	CRAS_OBSERVER_SET_ALERT(input_node_gain, NULL, 0);
 	CRAS_OBSERVER_SET_ALERT(suspend_changed, NULL, 0);
+	CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0);
+	CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0);
 
 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(
 		num_active_streams, CRAS_STREAM_OUTPUT);
@@ -307,6 +350,7 @@
 		num_active_streams, CRAS_STREAM_INPUT);
 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(
 		num_active_streams, CRAS_STREAM_POST_MIX_PRE_DSP);
+
 	return 0;
 
 error:
@@ -328,6 +372,8 @@
 	cras_alert_destroy(g_observer->alerts.node_left_right_swapped);
 	cras_alert_destroy(g_observer->alerts.input_node_gain);
 	cras_alert_destroy(g_observer->alerts.suspend_changed);
+	cras_alert_destroy(g_observer->alerts.hotword_triggered);
+	cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed);
 	cras_alert_destroy(g_observer->alerts.num_active_streams[
 							CRAS_STREAM_OUTPUT]);
 	cras_alert_destroy(g_observer->alerts.num_active_streams[
@@ -505,3 +551,23 @@
 
 	cras_alert_pending_data(alert, &data, sizeof(data));
 }
+
+void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec)
+{
+	struct cras_observer_alert_data_hotword_triggered data;
+
+	data.tv_sec = tv_sec;
+	data.tv_nsec = tv_nsec;
+	cras_alert_pending_data(g_observer->alerts.hotword_triggered,
+				&data, sizeof(data));
+}
+
+void cras_observer_notify_non_empty_audio_state_changed(int non_empty)
+{
+	struct cras_observer_non_empty_audio_state data;
+
+	data.non_empty = non_empty;
+
+	cras_alert_pending_data(g_observer->alerts.non_empty_audio_state_changed,
+				&data, sizeof(data));
+}
\ No newline at end of file
diff --git a/cras/src/server/cras_observer.h b/cras/src/server/cras_observer.h
index bbbaf89..440a8ce 100644
--- a/cras/src/server/cras_observer.h
+++ b/cras/src/server/cras_observer.h
@@ -94,4 +94,10 @@
 void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
 					     uint32_t num_active_streams);
 
+/* Notify observers of the timestamp when hotword triggered. */
+void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec);
+
+/* Notify observers the non-empty audio state changed. */
+void cras_observer_notify_non_empty_audio_state_changed(int active);
+
 #endif /* CRAS_OBSERVER_H */
diff --git a/cras/src/server/cras_rclient.c b/cras/src/server/cras_rclient.c
index 6a68351..3131725 100644
--- a/cras/src/server/cras_rclient.c
+++ b/cras/src/server/cras_rclient.c
@@ -8,6 +8,7 @@
 #include <syslog.h>
 
 #include "audio_thread.h"
+#include "cras_apm_list.h"
 #include "cras_config.h"
 #include "cras_dsp.h"
 #include "cras_iodev.h"
@@ -16,6 +17,7 @@
 #include "cras_observer.h"
 #include "cras_rclient.h"
 #include "cras_rstream.h"
+#include "cras_server_metrics.h"
 #include "cras_system_state.h"
 #include "cras_types.h"
 #include "cras_util.h"
@@ -38,7 +40,9 @@
 					int aud_fd)
 {
 	struct cras_rstream *stream;
-	struct cras_client_stream_connected reply;
+	struct cras_client_stream_connected stream_connected;
+	struct cras_client_stream_connected_old stream_connected_old;
+	struct cras_client_message *reply;
 	struct cras_audio_format remote_fmt;
 	struct cras_rstream_config stream_config;
 	int rc;
@@ -61,6 +65,7 @@
 	stream_config.direction = msg->direction;
 	stream_config.dev_idx = msg->dev_idx;
 	stream_config.flags = msg->flags;
+	stream_config.effects = msg->effects;
 	stream_config.format = &remote_fmt;
 	stream_config.buffer_frames = msg->buffer_frames;
 	stream_config.cb_threshold = msg->cb_threshold;
@@ -75,15 +80,27 @@
 
 	/* Tell client about the stream setup. */
 	syslog(LOG_DEBUG, "Send connected for stream %x\n", msg->stream_id);
-	cras_fill_client_stream_connected(
-			&reply,
-			0, /* No error. */
-			msg->stream_id,
-			&remote_fmt,
-			cras_rstream_get_total_shm_size(stream));
+	if (msg->proto_version == CRAS_PROTO_VER) {
+		cras_fill_client_stream_connected(
+				&stream_connected,
+				0, /* No error. */
+				msg->stream_id,
+				&remote_fmt,
+				cras_rstream_get_total_shm_size(stream),
+				cras_rstream_get_effects(stream));
+		reply = &stream_connected.header;
+	} else {
+		cras_fill_client_stream_connected_old(
+				&stream_connected_old,
+				0, /* No error. */
+				msg->stream_id,
+				&remote_fmt,
+				cras_rstream_get_total_shm_size(stream));
+		reply = &stream_connected_old.header;
+	}
 	stream_fds[0] = cras_rstream_input_shm_fd(stream);
 	stream_fds[1] = cras_rstream_output_shm_fd(stream);
-	rc = cras_rclient_send_message(client, &reply.header, stream_fds, 2);
+	rc = cras_rclient_send_message(client, reply, stream_fds, 2);
 	if (rc < 0) {
 		syslog(LOG_ERR, "Failed to send connected messaged\n");
 		stream_list_rm(cras_iodev_list_get_stream_list(),
@@ -91,13 +108,25 @@
 		goto reply_err;
 	}
 
+	/* Metrics logs the stream configurations. */
+	cras_server_metrics_stream_config(&stream_config);
+
 	return 0;
 
 reply_err:
 	/* Send the error code to the client. */
-	cras_fill_client_stream_connected(&reply, rc, msg->stream_id,
-					  &remote_fmt, 0);
-	cras_rclient_send_message(client, &reply.header, NULL, 0);
+	if (msg->proto_version == CRAS_PROTO_VER) {
+		cras_fill_client_stream_connected(
+				&stream_connected, rc, msg->stream_id,
+				&remote_fmt, 0, msg->effects);
+		reply = &stream_connected.header;
+	} else {
+		cras_fill_client_stream_connected_old(
+				&stream_connected_old, rc, msg->stream_id,
+				&remote_fmt, 0);
+		reply = &stream_connected_old.header;
+	}
+	cras_rclient_send_message(client, reply, NULL, 0);
 
 	if (aud_fd >= 0)
 		close(aud_fd);
@@ -128,6 +157,16 @@
 	cras_rclient_send_message(client, &msg.header, NULL, 0);
 }
 
+/* Handles dumping audio snapshots to shared memory for the client. */
+static void dump_audio_thread_snapshots(struct cras_rclient *client)
+{
+	struct cras_client_audio_debug_info_ready msg;
+
+	cras_fill_client_audio_debug_info_ready(&msg);
+	cras_system_state_dump_snapshots();
+	cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
 static void handle_get_hotword_models(struct cras_rclient *client,
 				      cras_node_id_t node_id)
 {
@@ -369,15 +408,68 @@
 
 /* Entry point for handling a message from the client.  Called from the main
  * server context. */
+int cras_rclient_buffer_from_client(struct cras_rclient *client,
+				    const uint8_t *buf,
+				    size_t buf_len,
+				    int fd) {
+	struct cras_server_message *msg = (struct cras_server_message *)buf;
+
+	if (buf_len < sizeof(*msg))
+		return -EINVAL;
+	if (msg->length != buf_len)
+		return -EINVAL;
+	cras_rclient_message_from_client(client, msg, fd);
+	return 0;
+}
+
+static int direction_valid(enum CRAS_STREAM_DIRECTION direction)
+{
+	return direction < CRAS_NUM_DIRECTIONS &&
+		direction != CRAS_STREAM_UNDEFINED;
+}
+
+#define MSG_LEN_VALID(msg, type) ((msg)->length >= sizeof(type))
+
+/*
+ * Check if client is sending an old version of connect message
+ * and converts it to the correct cras_connect_message.
+ * Note that this is special check only for libcras transition in
+ * clients, from CRAS_PROTO_VER = 1 to 2.
+ * TODO(hychao): clean up the check once clients transition is done.
+ */
+static int is_connect_msg_old(const struct cras_server_message *msg,
+			      struct cras_connect_message *cmsg)
+{
+	struct cras_connect_message_old *old;
+
+	if (!MSG_LEN_VALID(msg, struct cras_connect_message_old))
+		return 0;
+
+	old = (struct cras_connect_message_old *)msg;
+	if (old->proto_version + 1 != CRAS_PROTO_VER)
+		return 0;
+
+	memcpy(cmsg, old, sizeof(*old));
+	cmsg->effects = 0;
+	return 1;
+}
+
+/* Entry point for handling a message from the client.  Called from the main
+ * server context. */
 int cras_rclient_message_from_client(struct cras_rclient *client,
 				     const struct cras_server_message *msg,
 				     int fd) {
+	struct cras_connect_message cmsg;
+
 	assert(client && msg);
 
 	/* Most messages should not have a file descriptor. */
 	switch (msg->id) {
 	case CRAS_SERVER_CONNECT_STREAM:
 		break;
+	case CRAS_SERVER_SET_AEC_DUMP:
+		syslog(LOG_ERR, "client msg for APM debug, fd %d", fd);
+		break;
 	default:
 		if (fd != -1) {
 			syslog(LOG_ERR,
@@ -391,64 +483,97 @@
 
 	switch (msg->id) {
 	case CRAS_SERVER_CONNECT_STREAM:
-		handle_client_stream_connect(client,
-			(const struct cras_connect_message *)msg, fd);
+		if (MSG_LEN_VALID(msg, struct cras_connect_message)) {
+			handle_client_stream_connect(client,
+				(const struct cras_connect_message *)msg, fd);
+		} else if (is_connect_msg_old(msg, &cmsg)) {
+			handle_client_stream_connect(client, &cmsg, fd);
+		} else {
+			return -EINVAL;
+		}
 		break;
 	case CRAS_SERVER_DISCONNECT_STREAM:
+		if (!MSG_LEN_VALID(msg, struct cras_disconnect_stream_message))
+			return -EINVAL;
 		handle_client_stream_disconnect(client,
 			(const struct cras_disconnect_stream_message *)msg);
 		break;
 	case CRAS_SERVER_SET_SYSTEM_VOLUME:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_volume))
+			return -EINVAL;
 		cras_system_set_volume(
 			((const struct cras_set_system_volume *)msg)->volume);
 		break;
 	case CRAS_SERVER_SET_SYSTEM_MUTE:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
+			return -EINVAL;
 		cras_system_set_mute(
 			((const struct cras_set_system_mute *)msg)->mute);
 		break;
 	case CRAS_SERVER_SET_USER_MUTE:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
+			return -EINVAL;
 		cras_system_set_user_mute(
 			((const struct cras_set_system_mute *)msg)->mute);
 		break;
 	case CRAS_SERVER_SET_SYSTEM_MUTE_LOCKED:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
+			return -EINVAL;
 		cras_system_set_mute_locked(
 			((const struct cras_set_system_mute *)msg)->mute);
 		break;
 	case CRAS_SERVER_SET_SYSTEM_CAPTURE_GAIN: {
 		const struct cras_set_system_capture_gain *m =
 			(const struct cras_set_system_capture_gain *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_capture_gain))
+			return -EINVAL;
 		cras_system_set_capture_gain(m->gain);
 		break;
 	}
 	case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
+			return -EINVAL;
 		cras_system_set_capture_mute(
 			((const struct cras_set_system_mute *)msg)->mute);
 		break;
 	case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE_LOCKED:
+		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
+			return -EINVAL;
 		cras_system_set_capture_mute_locked(
 			((const struct cras_set_system_mute *)msg)->mute);
 		break;
 	case CRAS_SERVER_SET_NODE_ATTR: {
 		const struct cras_set_node_attr *m =
 			(const struct cras_set_node_attr *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_set_node_attr))
+			return -EINVAL;
 		cras_iodev_list_set_node_attr(m->node_id, m->attr, m->value);
 		break;
 	}
 	case CRAS_SERVER_SELECT_NODE: {
 		const struct cras_select_node *m =
 			(const struct cras_select_node *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_select_node) ||
+		    !direction_valid(m->direction))
+			return -EINVAL;
 		cras_iodev_list_select_node(m->direction, m->node_id);
 		break;
 	}
 	case CRAS_SERVER_ADD_ACTIVE_NODE: {
 		const struct cras_add_active_node *m =
 			(const struct cras_add_active_node *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_add_active_node) ||
+		    !direction_valid(m->direction))
+			return -EINVAL;
 		cras_iodev_list_add_active_node(m->direction, m->node_id);
 		break;
 	}
 	case CRAS_SERVER_RM_ACTIVE_NODE: {
 		const struct cras_rm_active_node *m =
 			(const struct cras_rm_active_node *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_rm_active_node) ||
+		    !direction_valid(m->direction))
+			return -EINVAL;
 		cras_iodev_list_rm_active_node(m->direction, m->node_id);
 		break;
 	}
@@ -461,15 +586,22 @@
 	case CRAS_SERVER_DUMP_AUDIO_THREAD:
 		dump_audio_thread_info(client);
 		break;
+	case CRAS_SERVER_DUMP_SNAPSHOTS:
+		dump_audio_thread_snapshots(client);
+		break;
 	case CRAS_SERVER_ADD_TEST_DEV: {
 		const struct cras_add_test_dev *m =
 			(const struct cras_add_test_dev *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_add_test_dev))
+			return -EINVAL;
 		cras_iodev_list_add_test_dev(m->type);
 		break;
 	}
 	case CRAS_SERVER_TEST_DEV_COMMAND: {
 		const struct cras_test_dev_command *m =
 			(const struct cras_test_dev_command *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_test_dev_command))
+			return -EINVAL;
 		cras_iodev_list_test_dev_command(
 			m->iodev_idx, (enum CRAS_TEST_IODEV_CMD)m->command,
 			m->data_len, m->data);
@@ -484,6 +616,14 @@
 	case CRAS_CONFIG_GLOBAL_REMIX: {
 		const struct cras_config_global_remix *m =
 			(const struct cras_config_global_remix *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_config_global_remix) ||
+		    m->num_channels > CRAS_MAX_REMIX_CHANNELS)
+			return -EINVAL;
+		size_t size_with_coefficients = sizeof(*m) +
+			m->num_channels * m->num_channels *
+			sizeof(m->coefficient[0]);
+		if (size_with_coefficients != msg->length)
+			return -EINVAL;
 		audio_thread_config_global_remix(
 				cras_iodev_list_get_audio_thread(),
 				m->num_channels,
@@ -491,6 +631,8 @@
 		break;
 	}
 	case CRAS_SERVER_GET_HOTWORD_MODELS: {
+		if (!MSG_LEN_VALID(msg, struct cras_get_hotword_models))
+			return -EINVAL;
 		handle_get_hotword_models(client,
 			((const struct cras_get_hotword_models *)msg)->node_id);
 		break;
@@ -498,6 +640,8 @@
 	case CRAS_SERVER_SET_HOTWORD_MODEL: {
 		const struct cras_set_hotword_model *m =
 			(const struct cras_set_hotword_model *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_set_hotword_model))
+			return -EINVAL;
 		cras_iodev_list_set_hotword_model(m->node_id,
 						  m->model_name);
 		break;
@@ -505,11 +649,27 @@
 	case CRAS_SERVER_REGISTER_NOTIFICATION: {
 		const struct cras_register_notification *m =
 			(struct cras_register_notification *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_register_notification))
+			return -EINVAL;
 		register_for_notification(
 			client, (enum CRAS_CLIENT_MESSAGE_ID)m->msg_id,
 			m->do_register);
 		break;
 	}
+	case CRAS_SERVER_SET_AEC_DUMP: {
+		const struct cras_set_aec_dump *m =
+			(const struct cras_set_aec_dump *)msg;
+		if (!MSG_LEN_VALID(msg, struct cras_set_aec_dump))
+			return -EINVAL;
+		audio_thread_set_aec_dump(
+				cras_iodev_list_get_audio_thread(),
+				m->stream_id,
+				m->start, fd);
+		break;
+	}
+	case CRAS_SERVER_RELOAD_AEC_CONFIG:
+		cras_apm_list_reload_aec_config();
+		break;
 	default:
 		break;
 	}
diff --git a/cras/src/server/cras_rclient.h b/cras/src/server/cras_rclient.h
index 26e4f3b..af0457f 100644
--- a/cras/src/server/cras_rclient.h
+++ b/cras/src/server/cras_rclient.h
@@ -9,8 +9,10 @@
 #ifndef CRAS_RCLIENT_H_
 #define CRAS_RCLIENT_H_
 
+struct cras_client_message;
 struct cras_message;
 struct cras_rclient;
+struct cras_server_message;
 
 /* Creates an rclient structure.
  * Args:
@@ -40,6 +42,22 @@
 				     const struct cras_server_message *msg,
 				     int fd);
 
+/* Handles a received buffer from the client.
+ * Args:
+ *    client - The client that received this message.
+ *    buf - The raw byte buffer the client sent. It should contain a valid
+ *      cras_server_message.
+ *    buf_len - The length of |buf|.
+ *    fd - The file descriptor that was sent by the remote client (or -1 if no
+ *         file descriptor was sent).
+ * Returns:
+ *    0 on success, otherwise a negative error code.
+ */
+int cras_rclient_buffer_from_client(struct cras_rclient *client,
+				    const uint8_t *buf,
+                                    size_t buf_len,
+                                    int fd);
+
 /* Sends a message to the client.
  * Args:
  *    client - The client to send the message to.
diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c
index 8b9b046..d527dd7 100644
--- a/cras/src/server/cras_rstream.c
+++ b/cras/src/server/cras_rstream.c
@@ -128,6 +128,85 @@
 	return 0;
 }
 
+/*
+ * Setting pending reply is only needed inside this module.
+ */
+static void set_pending_reply(struct cras_rstream *stream)
+{
+	cras_shm_set_callback_pending(&stream->shm, 1);
+}
+
+/*
+ * Clearing pending reply is only needed inside this module.
+ */
+static void clear_pending_reply(struct cras_rstream *stream)
+{
+	cras_shm_set_callback_pending(&stream->shm, 0);
+}
+
+/*
+ * Reads one response of audio request from client.
+ * Args:
+ *   stream[in]: A pointer to cras_rstream.
+ *   msg[out]: A pointer to audio_message to hold the message.
+ * Returns:
+ *   Number of bytes read from the socket.
+ *   A negative error code if read fails or the message from client
+ *   has errors.
+ */
+static int get_audio_request_reply(
+	const struct cras_rstream *stream, struct audio_message *msg)
+{
+	int rc;
+
+	rc = read(stream->fd, msg, sizeof(*msg));
+	if (rc < 0)
+		return -errno;
+	if (rc == 0)
+		return rc;
+	if (msg->error < 0)
+		return msg->error;
+	return rc;
+}
+
+/*
+ * Reads and handles one audio message from client.
+ * Returns:
+ *   Number of bytes read from the socket.
+ *   A negative error code if read fails or the message from client
+ *   has errors.
+ */
+static int read_and_handle_client_message(struct cras_rstream *stream) {
+
+	struct audio_message msg;
+	int rc;
+
+	rc = get_audio_request_reply(stream, &msg);
+	if (rc <= 0) {
+		syslog(LOG_ERR, "Got error from client: rc: %d", rc);
+		clear_pending_reply(stream);
+		return rc;
+	}
+
+	/*
+	 * Got client reply that data in the input stream is captured.
+	 */
+	if (stream->direction == CRAS_STREAM_INPUT &&
+	    msg.id == AUDIO_MESSAGE_DATA_CAPTURED) {
+		clear_pending_reply(stream);
+	}
+
+	/*
+	 * Got client reply that data for output stream is ready in shm.
+	 */
+	if (stream->direction == CRAS_STREAM_OUTPUT &&
+	    msg.id == AUDIO_MESSAGE_DATA_READY) {
+		clear_pending_reply(stream);
+	}
+
+	return rc;
+}
+
 /* Exported functions */
 
 int cras_rstream_create(struct cras_rstream_config *config,
@@ -171,6 +250,9 @@
 	}
 
 	stream->buf_state = buffer_share_create(stream->buffer_frames);
+	stream->apm_list = (stream->direction == CRAS_STREAM_INPUT)
+			? cras_apm_list_create(stream, config->effects)
+			: NULL;
 
 	syslog(LOG_DEBUG, "stream %x frames %zu, cb_thresh %zu",
 	       config->stream_id, config->buffer_frames, config->cb_threshold);
@@ -192,9 +274,32 @@
 		cras_audio_area_destroy(stream->audio_area);
 	}
 	buffer_share_destroy(stream->buf_state);
+	if (stream->apm_list)
+		cras_apm_list_destroy(stream->apm_list);
 	free(stream);
 }
 
+unsigned int cras_rstream_get_effects(const struct cras_rstream *stream)
+{
+	return stream->apm_list
+			? cras_apm_list_get_effects(stream->apm_list)
+			: 0;
+}
+
+struct cras_audio_format *cras_rstream_post_processing_format(
+		const struct cras_rstream *stream, void *dev_ptr)
+{
+	struct cras_apm *apm;
+
+	if (NULL == stream->apm_list)
+		return NULL;
+
+	apm = cras_apm_list_get(stream->apm_list, dev_ptr);
+	if (NULL == apm)
+		return NULL;
+	return cras_apm_list_get_format(apm);
+}
+
 void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
 					const struct timespec *now)
 {
@@ -233,6 +338,9 @@
 	rc = write(stream->fd, &msg, sizeof(msg));
 	if (rc < 0)
 		return -errno;
+
+	set_pending_reply(stream);
+
 	return rc;
 }
 
@@ -243,26 +351,22 @@
 
 	cras_shm_buffer_write_complete(&stream->shm);
 
+	/* Mark shm as used. */
+	if (stream_is_server_only(stream)) {
+		cras_shm_buffer_read_current(&stream->shm, count);
+		return 0;
+	}
+
 	init_audio_message(&msg, AUDIO_MESSAGE_DATA_READY, count);
 	rc = write(stream->fd, &msg, sizeof(msg));
 	if (rc < 0)
 		return -errno;
+
+	set_pending_reply(stream);
+
 	return rc;
 }
 
-int cras_rstream_get_audio_request_reply(const struct cras_rstream *stream)
-{
-	struct audio_message msg;
-	int rc;
-
-	rc = read(stream->fd, &msg, sizeof(msg));
-	if (rc < 0)
-		return -errno;
-	if (msg.error < 0)
-		return msg.error;
-	return 0;
-}
-
 void cras_rstream_dev_attach(struct cras_rstream *rstream,
 			     unsigned int dev_id,
 			     void *dev_ptr)
@@ -365,3 +469,32 @@
 {
 	return cras_shm_get_mute(&rstream->shm);
 }
+
+int cras_rstream_is_pending_reply(const struct cras_rstream *stream)
+{
+	return cras_shm_callback_pending(&stream->shm);
+}
+
+int cras_rstream_flush_old_audio_messages(struct cras_rstream *stream)
+{
+	struct pollfd pollfd;
+	int err;
+
+	if (!stream->fd)
+		return 0;
+
+	if (stream_is_server_only(stream))
+		return 0;
+
+	pollfd.fd = stream->fd;
+	pollfd.events = POLLIN;
+
+	do {
+		err = poll(&pollfd, 1, 0);
+		if (pollfd.revents & POLLIN) {
+			err = read_and_handle_client_message(stream);
+		}
+	} while (err > 0);
+
+	return 0;
+}
diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h
index 1a22717..d19fc47 100644
--- a/cras/src/server/cras_rstream.h
+++ b/cras/src/server/cras_rstream.h
@@ -9,6 +9,7 @@
 #ifndef CRAS_RSTREAM_H_
 #define CRAS_RSTREAM_H_
 
+#include "cras_apm_list.h"
 #include "cras_shm.h"
 #include "cras_types.h"
 
@@ -59,10 +60,12 @@
  *    last_fetch_ts - The time of the last stream fetch.
  *    longest_fetch_interval_ts - Longest interval between two fetches.
  *    buf_state - State of the buffer from all devices for this stream.
+ *    apm_list - List of audio processing module instances.
  *    num_attached_devs - Number of iodevs this stream has attached to.
  *    queued_frames - Cached value of the number of queued frames in shm.
  *    is_pinned - True if the stream is a pinned stream, false otherwise.
  *    pinned_dev_idx - device the stream is pinned, 0 if none.
+ *    triggered - True if already notified TRIGGER_ONLY stream, false otherwise.
  */
 struct cras_rstream {
 	cras_stream_id_t stream_id;
@@ -84,10 +87,12 @@
 	struct timespec last_fetch_ts;
 	struct timespec longest_fetch_interval;
 	struct buffer_share *buf_state;
+	struct cras_apm_list *apm_list;
 	int num_attached_devs;
 	int queued_frames;
 	int is_pinned;
 	uint32_t pinned_dev_idx;
+	int triggered;
 	struct cras_rstream *prev, *next;
 };
 
@@ -96,6 +101,7 @@
  *    direction - CRAS_STREAM_OUTPUT or CRAS_STREAM_INPUT.
  *    dev_idx - Pin to this device if != NO_DEVICE.
  *    flags - Any special handling for this stream.
+ *    effects - Bit map of effects to be enabled on this stream.
  *    format - The audio format the stream wishes to use.
  *    buffer_frames - Total number of audio frames to buffer.
  *    cb_threshold - # of frames when to request more from the client.
@@ -108,6 +114,7 @@
 	enum CRAS_STREAM_DIRECTION direction;
 	uint32_t dev_idx;
 	uint32_t flags;
+	uint32_t effects;
 	const struct cras_audio_format *format;
 	size_t buffer_frames;
 	size_t cb_threshold;
@@ -129,6 +136,13 @@
 /* Destroys an rstream. */
 void cras_rstream_destroy(struct cras_rstream *stream);
 
+/* Gets the id of the stream */
+static inline cras_stream_id_t cras_rstream_id(
+		const struct cras_rstream *stream)
+{
+	return stream->stream_id;
+}
+
 /* Gets the total buffer size in frames for the given client stream. */
 static inline size_t cras_rstream_get_buffer_frames(
 		const struct cras_rstream *stream)
@@ -250,6 +264,18 @@
 	return cras_stream_uses_input_hw(s->direction);
 }
 
+static inline int stream_is_server_only(const struct cras_rstream *s)
+{
+	return s->flags & SERVER_ONLY;
+}
+
+/* Gets the enabled effects of this stream. */
+unsigned int cras_rstream_get_effects(const struct cras_rstream *stream);
+
+/* Gets the format of data after stream specific processing. */
+struct cras_audio_format *cras_rstream_post_processing_format(
+		const struct cras_rstream *stream, void *dev_ptr);
+
 /* Checks how much time has passed since last stream fetch and records
  * the longest fetch interval. */
 void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
@@ -261,8 +287,6 @@
 
 /* Tells a capture client that count frames are ready. */
 int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count);
-/* Waits for the response to a request for audio. */
-int cras_rstream_get_audio_request_reply(const struct cras_rstream *stream);
 
 /* Let the rstream know when a device is added or removed. */
 void cras_rstream_dev_attach(struct cras_rstream *rstream,
@@ -314,4 +338,18 @@
 /* Returns non-zero if the stream is muted. */
 int cras_rstream_get_mute(const struct cras_rstream *rstream);
 
+/*
+ * Returns non-zero if the stream is pending a reply from client.
+ * - For playback, stream is waiting for AUDIO_MESSAGE_DATA_READY message from
+ *   client.
+ * - For capture, stream is waiting for AUDIO_MESSAGE_DATA_CAPTURED message
+ *   from client.
+ */
+int cras_rstream_is_pending_reply(const struct cras_rstream *stream);
+
+/*
+ * Reads any pending audio message from the socket.
+ */
+int cras_rstream_flush_old_audio_messages(struct cras_rstream *stream);
+
 #endif /* CRAS_RSTREAM_H_ */
diff --git a/cras/src/server/cras_server.c b/cras/src/server/cras_server.c
index ff5e601..51f3507 100644
--- a/cras/src/server/cras_server.c
+++ b/cras/src/server/cras_server.c
@@ -34,12 +34,15 @@
 #include "cras_telephony.h"
 #endif
 #include "cras_alert.h"
+#include "cras_audio_thread_monitor.h"
 #include "cras_config.h"
 #include "cras_device_monitor.h"
+#include "cras_hotword_handler.h"
 #include "cras_iodev_list.h"
 #include "cras_main_message.h"
 #include "cras_messages.h"
 #include "cras_metrics.h"
+#include "cras_non_empty_audio_handler.h"
 #include "cras_observer.h"
 #include "cras_rclient.h"
 #include "cras_server.h"
@@ -88,11 +91,19 @@
 	struct client_callback *prev, *next;
 };
 
+/* Stores callback function and argument data to be executed later. */
+struct system_task {
+	void (*callback)(void *);
+	void *callback_data;
+	struct system_task *next, *prev;
+};
+
 /* Local server data. */
 struct server_data {
 	struct attached_client *clients_head;
 	size_t num_clients;
 	struct client_callback *client_callbacks;
+	struct system_task *system_tasks;
 	size_t num_client_callbacks;
 	size_t next_client_id;
 } server_instance;
@@ -114,18 +125,15 @@
 static void handle_message_from_client(struct attached_client *client)
 {
 	uint8_t buf[CRAS_SERV_MAX_MSG_SIZE];
-	struct cras_server_message *msg;
 	int nread;
 	int fd;
 	unsigned int num_fds = 1;
 
-	msg = (struct cras_server_message *)buf;
 	nread = cras_recv_with_fds(client->fd, buf, sizeof(buf), &fd, &num_fds);
-	if (nread < sizeof(msg->length))
+        if (nread < 0)
+                goto read_error;
+        if (cras_rclient_buffer_from_client(client->client, buf, nread, fd) < 0)
 		goto read_error;
-	if (msg->length != nread)
-		goto read_error;
-	cras_rclient_message_from_client(client->client, msg, fd);
 	return;
 
 read_error:
@@ -291,6 +299,31 @@
 			client_cb->deleted = 1;
 }
 
+/* Creates a new task entry and append to system_tasks list, which will be
+ * executed in main loop later without wait time.
+ */
+static int add_task(void (*cb)(void *data),
+		    void *callback_data,
+		    void *server_data)
+{
+	struct server_data *serv;
+	struct system_task *new_task;
+
+	serv = (struct server_data *)server_data;
+	if (serv == NULL)
+		return -EINVAL;
+
+	new_task = (struct system_task *)calloc(1, sizeof(*new_task));
+	if (new_task == NULL)
+		return -ENOMEM;
+
+	new_task->callback = cb;
+	new_task->callback_data = callback_data;
+
+	DL_APPEND(serv->system_tasks, new_task);
+	return 0;
+}
+
 /* Cleans up the file descriptor list removing items deleted during the main
  * loop iteration. */
 static void cleanup_select_fds(void *server_data)
@@ -381,6 +414,8 @@
 	/* Log to syslog. */
 	openlog("cras_server", LOG_PID, LOG_USER);
 
+	server_instance.next_client_id = RESERVED_CLIENT_IDS;
+
 	/* Initialize global observer. */
 	cras_observer_server_init();
 
@@ -392,6 +427,7 @@
 	 * from the list that are passed to select in the main loop below. */
 	cras_system_set_select_handler(add_select_fd, rm_select_fd,
 				       &server_instance);
+	cras_system_set_add_task_handler(add_task, &server_instance);
 	cras_main_message_init();
 
 	return 0;
@@ -409,8 +445,10 @@
 	struct sockaddr_un addr;
 	struct attached_client *elm;
 	struct client_callback *client_cb;
+	struct system_task *tasks;
+	struct system_task *system_task;
 	struct cras_tm *tm;
-	struct timespec ts;
+	struct timespec ts, *poll_timeout;
 	int timers_active;
 	struct pollfd *pollfds;
 	unsigned int pollfds_size = 32;
@@ -427,6 +465,12 @@
 
 	cras_device_monitor_init();
 
+	cras_hotword_handler_init();
+
+	cras_non_empty_audio_handler_init();
+
+	cras_audio_thread_monitor_init();
+
 #ifdef CRAS_DBUS
 	dbus_threads_init_default();
 	dbus_conn = cras_dbus_connect_system_bus();
@@ -527,10 +571,26 @@
 			num_pollfds++;
 		}
 
+		tasks = server_instance.system_tasks;
+		server_instance.system_tasks = NULL;
+		DL_FOREACH(tasks, system_task) {
+			system_task->callback(system_task->callback_data);
+			DL_DELETE(tasks, system_task);
+			free(system_task);
+		}
+
 		timers_active = cras_tm_get_next_timeout(tm, &ts);
 
-		rc = ppoll(pollfds, num_pollfds,
-			   timers_active ? &ts : NULL, NULL);
+		/*
+		 * If new client task has been scheduled, no need to wait
+		 * for timeout, just do another loop to execute them.
+		 */
+		if (server_instance.system_tasks)
+			poll_timeout = NULL;
+		else
+			poll_timeout = timers_active ? &ts : NULL;
+
+		rc = ppoll(pollfds, num_pollfds, poll_timeout, NULL);
 		if  (rc < 0)
 			continue;
 
diff --git a/cras/src/server/cras_server.h b/cras/src/server/cras_server.h
index aff9b7a..d17314b 100644
--- a/cras/src/server/cras_server.h
+++ b/cras/src/server/cras_server.h
@@ -16,6 +16,10 @@
 #define CRAS_SERVER_PROFILE_MASK_HSP	(1 << 1)
 #define CRAS_SERVER_PROFILE_MASK_A2DP	(1 << 2)
 
+/* Reserver client id 0-15 for internal server usage. */
+#define RESERVED_CLIENT_IDS 16
+#define SERVER_STREAM_CLIENT_ID 0
+
 struct cras_client_message;
 
 /* Initialize some server setup. Mainly to add the select handler first
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index c824407..c78ad64 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -10,25 +10,49 @@
 
 #include "cras_metrics.h"
 #include "cras_main_message.h"
+#include "cras_rstream.h"
 
+const char kHighestInputHardwareLevel[] = "Cras.HighestInputHardwareLevel";
+const char kHighestOutputHardwareLevel[] = "Cras.HighestOutputHardwareLevel";
 const char kNoCodecsFoundMetric[] = "Cras.NoCodecsFoundAtBoot";
 const char kStreamTimeoutMilliSeconds[] = "Cras.StreamTimeoutMilliSeconds";
+const char kStreamCallbackThreshold[] = "Cras.StreamCallbackThreshold";
+const char kStreamFlags[] = "Cras.StreamFlags";
+const char kStreamSamplingFormat[] = "Cras.StreamSamplingFormat";
+const char kStreamSamplingRate[] = "Cras.StreamSamplingRate";
+const char kUnderrunsPerDevice[] = "Cras.UnderrunsPerDevice";
 
 /* Type of metrics to log. */
 enum CRAS_SERVER_METRICS_TYPE {
+	HIGHEST_INPUT_HW_LEVEL,
+	HIGHEST_OUTPUT_HW_LEVEL,
 	LONGEST_FETCH_DELAY,
+	NUM_UNDERRUNS,
+	STREAM_CONFIG
+};
+
+struct cras_server_metrics_stream_config {
+	unsigned cb_threshold;
+	unsigned flags;
+	int format;
+	unsigned rate;
+};
+
+union cras_server_metrics_data {
+	unsigned value;
+	struct cras_server_metrics_stream_config stream_config;
 };
 
 struct cras_server_metrics_message {
 	struct cras_main_message header;
 	enum CRAS_SERVER_METRICS_TYPE metrics_type;
-	unsigned data;
+	union cras_server_metrics_data data;
 };
 
-static void init_longest_fetch_delay_msg(
+static void init_server_metrics_msg(
 		struct cras_server_metrics_message *msg,
 		enum CRAS_SERVER_METRICS_TYPE type,
-		unsigned data)
+		union cras_server_metrics_data data)
 {
 	memset(msg, 0, sizeof(*msg));
 	msg->header.type = CRAS_MAIN_METRICS;
@@ -37,32 +61,112 @@
 	msg->data = data;
 }
 
-int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
+int cras_server_metrics_highest_hw_level(unsigned hw_level,
+		enum CRAS_STREAM_DIRECTION direction)
 {
 	struct cras_server_metrics_message msg;
+	union cras_server_metrics_data data;
 	int err;
 
-	init_longest_fetch_delay_msg(&msg, LONGEST_FETCH_DELAY, delay_msec);
+	data.value = hw_level;
+
+	switch (direction) {
+	case CRAS_STREAM_INPUT:
+		init_server_metrics_msg(&msg, HIGHEST_INPUT_HW_LEVEL, data);
+		break;
+	case CRAS_STREAM_OUTPUT:
+		init_server_metrics_msg(&msg, HIGHEST_OUTPUT_HW_LEVEL, data);
+		break;
+	default:
+		return 0;
+	}
+
 	err = cras_main_message_send((struct cras_main_message *)&msg);
 	if (err < 0) {
-		syslog(LOG_ERR, "Failed to send metrics message");
+		syslog(LOG_ERR,
+		       "Failed to send metrics message: HIGHEST_HW_LEVEL");
 		return err;
 	}
 
 	return 0;
 }
 
-static void metrics_longest_fetch_delay(unsigned delay_msec)
+int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
 {
-	static const int fetch_delay_min_msec = 1;
-	static const int fetch_delay_max_msec = 10000;
-	static const int fetch_delay_nbuckets = 10;
+	struct cras_server_metrics_message msg;
+	union cras_server_metrics_data data;
+	int err;
 
-	cras_metrics_log_histogram(kStreamTimeoutMilliSeconds,
-				   delay_msec,
-				   fetch_delay_min_msec,
-				   fetch_delay_max_msec,
-				   fetch_delay_nbuckets);
+	data.value = delay_msec;
+	init_server_metrics_msg(&msg, LONGEST_FETCH_DELAY, data);
+	err = cras_main_message_send((struct cras_main_message *)&msg);
+	if (err < 0) {
+		syslog(LOG_ERR,
+		       "Failed to send metrics message: LONGEST_FETCH_DELAY");
+		return err;
+	}
+
+	return 0;
+}
+
+int cras_server_metrics_num_underruns(unsigned num_underruns)
+{
+	struct cras_server_metrics_message msg;
+	union cras_server_metrics_data data;
+	int err;
+
+	data.value = num_underruns;
+	init_server_metrics_msg(&msg, NUM_UNDERRUNS, data);
+	err = cras_main_message_send((struct cras_main_message *)&msg);
+	if (err < 0) {
+		syslog(LOG_ERR,
+		       "Failed to send metrics message: NUM_UNDERRUNS");
+		return err;
+	}
+
+	return 0;
+}
+
+int cras_server_metrics_stream_config(struct cras_rstream_config *config)
+{
+	struct cras_server_metrics_message msg;
+	union cras_server_metrics_data data;
+	int err;
+
+	data.stream_config.cb_threshold = (unsigned)config->cb_threshold;
+	data.stream_config.flags = (unsigned)config->flags;
+	data.stream_config.format = (int)config->format->format;
+	data.stream_config.rate = (unsigned)config->format->frame_rate;
+
+	init_server_metrics_msg(&msg, STREAM_CONFIG, data);
+	err = cras_main_message_send((struct cras_main_message *)&msg);
+	if (err < 0) {
+		syslog(LOG_ERR,
+			"Failed to send metrics message: STREAM_CONFIG");
+		return err;
+	}
+
+	return 0;
+}
+
+static void metrics_stream_config(
+		struct cras_server_metrics_stream_config config)
+{
+	/* Logs stream callback threshold. */
+	cras_metrics_log_sparse_histogram(kStreamCallbackThreshold,
+					  config.cb_threshold);
+
+	/* Logs stream flags. */
+	cras_metrics_log_sparse_histogram(kStreamFlags,
+					  config.flags);
+
+	/* Logs stream sampling format. */
+	cras_metrics_log_sparse_histogram(kStreamSamplingFormat,
+					  config.format);
+
+	/* Logs stream sampling rate. */
+	cras_metrics_log_sparse_histogram(kStreamSamplingRate,
+					  config.rate);
 }
 
 static void handle_metrics_message(struct cras_main_message *msg, void *arg)
@@ -70,8 +174,24 @@
 	struct cras_server_metrics_message *metrics_msg =
 			(struct cras_server_metrics_message *)msg;
 	switch (metrics_msg->metrics_type) {
+	case HIGHEST_INPUT_HW_LEVEL:
+		cras_metrics_log_histogram(kHighestInputHardwareLevel,
+				metrics_msg->data.value, 1, 10000, 20);
+		break;
+	case HIGHEST_OUTPUT_HW_LEVEL:
+		cras_metrics_log_histogram(kHighestOutputHardwareLevel,
+				metrics_msg->data.value, 1, 10000, 20);
+		break;
 	case LONGEST_FETCH_DELAY:
-		metrics_longest_fetch_delay(metrics_msg->data);
+		cras_metrics_log_histogram(kStreamTimeoutMilliSeconds,
+				metrics_msg->data.value, 1, 20000, 10);
+		break;
+	case NUM_UNDERRUNS:
+		cras_metrics_log_histogram(kUnderrunsPerDevice,
+				metrics_msg->data.value, 0, 1000, 10);
+		break;
+	case STREAM_CONFIG:
+		metrics_stream_config(metrics_msg->data.stream_config);
 		break;
 	default:
 		syslog(LOG_ERR, "Unknown metrics type %u",
diff --git a/cras/src/server/cras_server_metrics.h b/cras/src/server/cras_server_metrics.h
index b64614f..f2b206a 100644
--- a/cras/src/server/cras_server_metrics.h
+++ b/cras/src/server/cras_server_metrics.h
@@ -6,12 +6,31 @@
 #ifndef CRAS_SERVER_METRICS_H_
 #define CRAS_SERVER_METRICS_H_
 
+#include "cras_rstream.h"
+
 extern const char kNoCodecsFoundMetric[];
+extern const char kHighestInputHardwareLevel[];
+extern const char kHighestOutputHardwareLevel[];
 extern const char kStreamTimeoutMilliSeconds[];
+extern const char kStreamCallbackThreshold[];
+extern const char kStreamFlags[];
+extern const char kStreamSamplingFormat[];
+extern const char kStreamSamplingRate[];
+extern const char kUnderrunsPerDevice[];
+
+/* Logs the highest hardware level of a device. */
+int cras_server_metrics_highest_hw_level(unsigned hw_level,
+		enum CRAS_STREAM_DIRECTION direction);
 
 /* Logs the longest fetch delay of a stream in millisecond. */
 int cras_server_metrics_longest_fetch_delay(int delay_msec);
 
+/* Logs the number of underruns of a device. */
+int cras_server_metrics_num_underruns(unsigned num_underruns);
+
+/* Logs the stream configurations from clients. */
+int cras_server_metrics_stream_config(struct cras_rstream_config *config);
+
 /* Initialize metrics logging stuff. */
 int cras_server_metrics_init();
 
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index 3483ebc..1eaa5ec 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -13,6 +13,7 @@
 #include <syslog.h>
 
 #include "cras_alsa_card.h"
+#include "cras_board_config.h"
 #include "cras_config.h"
 #include "cras_device_blacklist.h"
 #include "cras_observer.h"
@@ -34,6 +35,7 @@
  *    shm_name - Name of posix shm region for exported state.
  *    shm_fd - fd for shm area of system_state struct.
  *    shm_fd_ro - fd for shm area of system_state struct, opened read-only.
+ *        This copy is to dup and pass to clients.
  *    shm_size - Size of the shm area.
  *    device_config_dir - Directory of device configs where volume curves live.
  *    internal_ucm_suffix - The suffix to append to internal card name to
@@ -43,6 +45,8 @@
  *    update_lock - Protects the update_count, as audio threads can update the
  *      stream count.
  *    tm - The system-wide timer manager.
+ *    add_task - Function to handle adding a task for main thread to execute.
+ *    task_data - Data to be passed to add_task handler function.
  */
 static struct {
 	struct cras_server_state *exp_state;
@@ -61,35 +65,38 @@
 		      void *cb_data, void *select_data);
 	void (*fd_rm)(int fd, void *select_data);
 	void *select_data;
+	int (*add_task)(void (*callback)(void *data),
+					 void *callback_data,
+					 void *task_data);
+	void *task_data;
+	struct cras_audio_thread_snapshot_buffer snapshot_buffer;
 } state;
 
 /*
  * Exported Interface.
  */
 
-void cras_system_state_init(const char *device_config_dir)
+void cras_system_state_init(const char *device_config_dir,
+                            const char *shm_name,
+                            int rw_shm_fd,
+                            int ro_shm_fd,
+                            struct cras_server_state *exp_state,
+                            size_t exp_state_size)
 {
-	struct cras_server_state *exp_state;
+	struct cras_board_config board_config;
 	int rc;
 
+        assert(sizeof(*exp_state) == exp_state_size);
 	state.shm_size = sizeof(*exp_state);
 
-	snprintf(state.shm_name, sizeof(state.shm_name), "/cras-%d", getpid());
-	state.shm_fd = cras_shm_open_rw(state.shm_name, state.shm_size);
-	if (state.shm_fd < 0)
-		exit(state.shm_fd);
+        strncpy(state.shm_name, shm_name, sizeof(state.shm_name));
+        state.shm_name[sizeof(state.shm_name) - 1] = '\0';
+	state.shm_fd = rw_shm_fd;
+	state.shm_fd_ro = ro_shm_fd;
 
-	/* mmap shm. */
-	exp_state = mmap(NULL, state.shm_size,
-			 PROT_READ | PROT_WRITE, MAP_SHARED,
-			 state.shm_fd, 0);
-	if (exp_state == (struct cras_server_state *)-1)
-		exit(-ENOMEM);
-
-	/* Open a read-only copy to dup and pass to clients. */
-	state.shm_fd_ro = cras_shm_reopen_ro(state.shm_name, state.shm_fd);
-	if (state.shm_fd_ro < 0)
-		exit(state.shm_fd_ro);
+	/* Read board config. */
+	memset(&board_config, 0, sizeof(board_config));
+	cras_board_config_get(device_config_dir, &board_config);
 
 	/* Initial system state. */
 	exp_state->state_version = CRAS_SERVER_STATE_VERSION;
@@ -106,6 +113,10 @@
 	exp_state->min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN;
 	exp_state->max_capture_gain = DEFAULT_MAX_CAPTURE_GAIN;
 	exp_state->num_streams_attached = 0;
+	exp_state->default_output_buffer_size =
+		board_config.default_output_buffer_size;
+	exp_state->aec_supported =
+		board_config.aec_supported;
 
 	if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) {
 		syslog(LOG_ERR, "Fatal: system state mutex init");
@@ -130,6 +141,10 @@
 	/* Read config file for blacklisted devices. */
 	state.device_blacklist =
 		cras_device_blacklist_create(CRAS_CONFIG_FILE_DIR);
+
+	/* Initialize snapshot buffer memory */
+	memset(&state.snapshot_buffer, 0,
+	       sizeof(struct cras_audio_thread_snapshot_buffer));
 }
 
 void cras_system_state_set_internal_ucm_suffix(const char *internal_ucm_suffix)
@@ -193,15 +208,23 @@
 
 void cras_system_set_user_mute(int mute)
 {
+	int current_mute = cras_system_get_mute();
+
 	if (state.exp_state->user_mute == !!mute)
 		return;
 
 	state.exp_state->user_mute = !!mute;
+
+	if (current_mute == (mute || state.exp_state->mute))
+		return;
+
 	cras_system_notify_mute();
 }
 
 void cras_system_set_mute(int mute)
 {
+	int current_mute = cras_system_get_mute();
+
 	if (state.exp_state->mute_locked)
 		return;
 
@@ -209,6 +232,10 @@
 		return;
 
 	state.exp_state->mute = !!mute;
+
+	if (current_mute == (mute || state.exp_state->user_mute))
+		return;
+
 	cras_system_notify_mute();
 }
 
@@ -218,7 +245,6 @@
 		return;
 
 	state.exp_state->mute_locked = !!locked;
-	cras_system_notify_mute();
 }
 
 int cras_system_get_mute()
@@ -317,6 +343,16 @@
 	return state.exp_state->max_capture_gain;
 }
 
+int cras_system_get_default_output_buffer_size()
+{
+	return state.exp_state->default_output_buffer_size;
+}
+
+int cras_system_get_aec_supported()
+{
+	return state.exp_state->aec_supported;
+}
+
 int cras_system_add_alsa_card(struct cras_alsa_card_info *alsa_card_info)
 {
 	struct card_list *card;
@@ -330,7 +366,7 @@
 
 	DL_FOREACH(state.cards, card) {
 		if (card_index == cras_alsa_card_get_index(card->card))
-			return -EINVAL;
+			return -EEXIST;
 	}
 	alsa_card = cras_alsa_card_create(
 			alsa_card_info,
@@ -400,6 +436,27 @@
 			    state.select_data);
 }
 
+int cras_system_set_add_task_handler(int (*add_task)(void (*cb)(void *data),
+						     void *callback_data,
+						     void *task_data),
+				     void *task_data)
+{
+	if (state.add_task != NULL)
+		return -EEXIST;
+
+	state.add_task = add_task;
+	state.task_data = task_data;
+	return 0;
+}
+
+int cras_system_add_task(void (*callback)(void *data), void *callback_data)
+{
+	if (state.add_task == NULL)
+		return -EINVAL;
+
+	return state.add_task(callback, callback_data, state.task_data);
+}
+
 void cras_system_rm_select_fd(int fd)
 {
 	if (state.fd_rm != NULL)
@@ -491,6 +548,16 @@
 	return state.exp_state->num_input_nodes;
 }
 
+void cras_system_state_set_non_empty_status(int non_empty)
+{
+	state.exp_state->non_empty_status = non_empty;
+}
+
+int cras_system_state_get_non_empty_status()
+{
+	return state.exp_state->non_empty_status;
+}
+
 struct cras_server_state *cras_system_state_update_begin()
 {
 	if (pthread_mutex_lock(&state.update_lock)) {
@@ -522,3 +589,19 @@
 {
 	return state.tm;
 }
+
+
+void cras_system_state_dump_snapshots()
+{
+	memcpy(&state.exp_state->snapshot_buffer, &state.snapshot_buffer,
+			sizeof(struct cras_audio_thread_snapshot_buffer));
+}
+
+void cras_system_state_add_snapshot(
+	struct cras_audio_thread_snapshot *snapshot)
+{
+	state.snapshot_buffer.snapshots[state.snapshot_buffer.pos++] =
+			(*snapshot);
+	state.snapshot_buffer.pos %=
+		CRAS_MAX_AUDIO_THREAD_SNAPSHOTS;
+}
diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h
index f0bb98a..e5c3980 100644
--- a/cras/src/server/cras_system_state.h
+++ b/cras/src/server/cras_system_state.h
@@ -31,8 +31,18 @@
  *
  * Args:
  *    device_config_dir - Directory for device configs where volume curves live.
+ *    shm_name - Name of the shared memory region used to store the state.
+ *    rw_shm_fd - FD of the shm region.
+ *    ro_shm_fd - FD of the shm region opened RO for sharing with clients.
+ *    exp_state - Shared memory region for storing state.
+ *    exp_state_size - Size of |exp_state|.
  */
-void cras_system_state_init(const char *device_config_dir);
+void cras_system_state_init(const char *device_config_dir,
+                            const char *shm_name,
+                            int rw_shm_fd,
+                            int ro_shm_fd,
+                            struct cras_server_state *exp_state,
+                            size_t exp_state_size);
 void cras_system_state_deinit();
 
 /* Sets the suffix string to control which UCM config fo load. */
@@ -107,6 +117,12 @@
 /* Returns the min value allowed for capture gain in dB * 100. */
 long cras_system_get_max_capture_gain();
 
+/* Returns the default value of output buffer size in frames. */
+int cras_system_get_default_output_buffer_size();
+
+/* Returns if system aec is supported. */
+int cras_system_get_aec_supported();
+
 /* Adds a card at the given index to the system.  When a new card is found
  * (through a udev event notification) this will add the card to the system,
  * causing its devices to become available for playback/capture.
@@ -175,6 +191,30 @@
  */
 void cras_system_rm_select_fd(int fd);
 
+/*
+ * Register the function to use to add a task for main thread to execute.
+ * Args:
+ *    add_task - The function to call when new task is added.
+ *    task_data - Additional data to pass back to add_task.
+ * Returns:
+ *    0 on success, or -EEXIST if there's already a registered handler.
+ */
+int cras_system_set_add_task_handler(int (*add_task)(void (*cb)(void *data),
+						     void *callback_data,
+						     void *task_data),
+				     void *task_data);
+
+/*
+ * Adds a task callback and data pair, to be executed in the next main thread
+ * loop without additional wait time.
+ * Args:
+ *    callback - The function to execute.
+ *    callback_data - The data to be passed to callback when executed.
+ * Returns:
+ *    0 on success, or -EINVAL if there's no handler for adding task.
+ */
+int cras_system_add_task(void (*callback)(void *data), void *callback_data);
+
 /* Signals that an audio input or output stream has been added to the system.
  * This allows the count of active streams can be used to notice when the audio
  * subsystem is idle.
@@ -238,6 +278,16 @@
  */
 int cras_system_state_get_input_nodes(const struct cras_ionode_info **nodes);
 
+/*
+ * Sets the non-empty audio status.
+ */
+void cras_system_state_set_non_empty_status(int non_empty);
+
+/*
+ * Returns the non-empty audio status.
+ */
+int cras_system_state_get_non_empty_status();
+
 /* Returns a pointer to the current system state that is shared with clients.
  * This also 'locks' the structure by incrementing the update count to an odd
  * value.
@@ -259,4 +309,14 @@
 /* Returns the timer manager. */
 struct cras_tm *cras_system_state_get_tm();
 
+/*
+ * Add snapshot to snapshot buffer in system state
+ */
+void cras_system_state_add_snapshot(struct cras_audio_thread_snapshot *);
+
+/*
+ * Dump snapshots from system state to shared memory with client
+ */
+void cras_system_state_dump_snapshots();
+
 #endif /* CRAS_SYSTEM_STATE_H_ */
diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c
new file mode 100644
index 0000000..f51e58a
--- /dev/null
+++ b/cras/src/server/dev_io.c
@@ -0,0 +1,1013 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <poll.h>
+#include <syslog.h>
+
+#include "audio_thread_log.h"
+#include "cras_audio_area.h"
+#include "cras_iodev.h"
+#include "cras_non_empty_audio_handler.h"
+#include "cras_rstream.h"
+#include "cras_server_metrics.h"
+#include "dev_stream.h"
+#include "input_data.h"
+#include "polled_interval_checker.h"
+#include "utlist.h"
+
+#include "dev_io.h"
+
+static const struct timespec playback_wake_fuzz_ts = {
+	0, 500 * 1000 /* 500 usec. */
+};
+
+/* The maximum time to wait before checking the device's non-empty status. */
+static const int NON_EMPTY_UPDATE_INTERVAL_SEC = 5;
+
+/*
+ * The minimum number of consecutive seconds of empty audio that must be
+ * played before a device is considered to be playing empty audio.
+ */
+static const int MIN_EMPTY_PERIOD_SEC = 30;
+
+/* The number of devices playing/capturing non-empty stream(s). */
+static int non_empty_device_count = 0;
+
+/* Gets the master device which the stream is attached to. */
+static inline
+struct cras_iodev *get_master_dev(const struct dev_stream *stream)
+{
+	return (struct cras_iodev *)stream->stream->master_dev.dev_ptr;
+}
+
+/* Updates the estimated sample rate of open device to all attached
+ * streams.
+ */
+static void update_estimated_rate(struct open_dev *adev)
+{
+	struct cras_iodev *master_dev;
+	struct cras_iodev *dev = adev->dev;
+	struct dev_stream *dev_stream;
+
+	DL_FOREACH(dev->streams, dev_stream) {
+		master_dev = get_master_dev(dev_stream);
+		if (master_dev == NULL) {
+			syslog(LOG_ERR, "Fail to find master open dev.");
+			continue;
+		}
+
+		dev_stream_set_dev_rate(dev_stream,
+				dev->ext_format->frame_rate,
+				cras_iodev_get_est_rate_ratio(dev),
+				cras_iodev_get_est_rate_ratio(master_dev),
+				adev->coarse_rate_adjust);
+	}
+}
+
+/*
+ * Counts the number of devices which are currently playing/capturing non-empty
+ * audio.
+ */
+static inline int count_non_empty_dev(struct open_dev *adevs) {
+	int count = 0;
+	struct open_dev *adev;
+	DL_FOREACH(adevs, adev) {
+		if (!adev->empty_pi || !pic_interval_elapsed(adev->empty_pi))
+			count++;
+	}
+	return count;
+}
+
+static void check_non_empty_state_transition(struct open_dev *adevs) {
+	int new_non_empty_dev_count = count_non_empty_dev(adevs);
+
+	// If we have transitioned to or from a state with 0 non-empty devices,
+	// notify the main thread to update system state.
+	if ((non_empty_device_count == 0) != (new_non_empty_dev_count == 0))
+		cras_non_empty_audio_send_msg(
+			new_non_empty_dev_count > 0 ? 1 : 0);
+
+	non_empty_device_count = new_non_empty_dev_count;
+}
+
+/* Asks any stream with room for more data. Sets the time stamp for all streams.
+ * Args:
+ *    adev - The output device streams are attached to.
+ * Returns:
+ *    0 on success, negative error on failure. If failed, can assume that all
+ *    streams have been removed from the device.
+ */
+static int fetch_streams(struct open_dev *adev)
+{
+	struct dev_stream *dev_stream;
+	struct cras_iodev *odev = adev->dev;
+	int rc;
+	int delay;
+
+	delay = cras_iodev_delay_frames(odev);
+	if (delay < 0)
+		return delay;
+
+	DL_FOREACH(adev->dev->streams, dev_stream) {
+		struct cras_rstream *rstream = dev_stream->stream;
+		struct cras_audio_shm *shm =
+			cras_rstream_output_shm(rstream);
+		const struct timespec *next_cb_ts;
+		struct timespec now;
+
+		clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+
+		if (dev_stream_is_pending_reply(dev_stream)) {
+			dev_stream_flush_old_audio_messages(dev_stream);
+			cras_rstream_record_fetch_interval(dev_stream->stream,
+							   &now);
+		}
+
+		if (cras_shm_get_frames(shm) < 0)
+			cras_rstream_set_is_draining(rstream, 1);
+
+		if (cras_rstream_get_is_draining(dev_stream->stream))
+			continue;
+
+		next_cb_ts = dev_stream_next_cb_ts(dev_stream);
+		if (!next_cb_ts)
+			continue;
+
+		/* Check if it's time to get more data from this stream.
+		 * Allow for waking up a little early. */
+		add_timespecs(&now, &playback_wake_fuzz_ts);
+		if (!timespec_after(&now, next_cb_ts))
+			continue;
+
+		if (!dev_stream_can_fetch(dev_stream)) {
+			ATLOG(atlog, AUDIO_THREAD_STREAM_SKIP_CB,
+			      cras_rstream_id(rstream),
+			      shm->area->write_offset[0],
+			      shm->area->write_offset[1]);
+			continue;
+		}
+
+		dev_stream_set_delay(dev_stream, delay);
+
+		ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id,
+		      cras_rstream_get_cb_threshold(rstream), delay);
+
+		rc = dev_stream_request_playback_samples(dev_stream, &now);
+		if (rc < 0) {
+			syslog(LOG_ERR, "fetch err: %d for %x",
+			       rc, cras_rstream_id(rstream));
+			cras_rstream_set_is_draining(rstream, 1);
+		}
+	}
+
+	return 0;
+}
+
+/* Gets the max delay frames of open input devices. */
+static int input_delay_frames(struct open_dev *adevs)
+{
+	struct open_dev *adev;
+	int delay;
+	int max_delay = 0;
+
+	DL_FOREACH(adevs, adev) {
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+		delay = cras_iodev_delay_frames(adev->dev);
+		if (delay < 0)
+			return delay;
+		if (delay > max_delay)
+			max_delay = delay;
+	}
+	return max_delay;
+}
+
+/* Sets the stream delay.
+ * Args:
+ *    adev[in] - The device to capture from.
+ */
+static unsigned int set_stream_delay(struct open_dev *adev)
+{
+	struct dev_stream *stream;
+	int delay;
+
+	/* TODO(dgreid) - Setting delay from last dev only. */
+	delay = input_delay_frames(adev);
+
+	DL_FOREACH(adev->dev->streams, stream) {
+		if (stream->stream->flags & TRIGGER_ONLY)
+			continue;
+
+		dev_stream_set_delay(stream, delay);
+	}
+
+	return 0;
+}
+
+/* Gets the minimum amount of space available for writing across all streams.
+ * Args:
+ *    adev[in] - The device to capture from.
+ *    write_limit[in] - Initial limit to number of frames to capture.
+ *    limit_stream[out] - The pointer to the pointer of stream which
+ *                        causes capture limit.
+ *                        Output NULL if there is no stream that causes
+ *                        capture limit less than the initial limit.
+ */
+static unsigned int get_stream_limit(
+		struct open_dev *adev,
+		unsigned int write_limit,
+		struct dev_stream **limit_stream)
+{
+	struct cras_rstream *rstream;
+	struct cras_audio_shm *shm;
+	struct dev_stream *stream;
+	unsigned int avail;
+
+	*limit_stream = NULL;
+
+	DL_FOREACH(adev->dev->streams, stream) {
+		rstream = stream->stream;
+		if (rstream->flags & TRIGGER_ONLY)
+			continue;
+
+		shm = cras_rstream_input_shm(rstream);
+		if (cras_shm_check_write_overrun(shm))
+			ATLOG(atlog, AUDIO_THREAD_READ_OVERRUN,
+			      adev->dev->info.idx, rstream->stream_id,
+			      shm->area->num_overruns);
+		avail = dev_stream_capture_avail(stream);
+		if (avail < write_limit) {
+			write_limit = avail;
+			*limit_stream = stream;
+		}
+	}
+
+	return write_limit;
+}
+
+/*
+ * The minimum wake time for a input device, which is 5ms. It's only used by
+ * function get_input_dev_max_wake_ts.
+ */
+static const struct timespec min_input_dev_wake_ts = {
+	0, 5 * 1000 * 1000 /* 5 ms. */
+};
+
+/*
+ * Get input device maximum sleep time, which is the approximate time that the
+ * device will have hw_level = buffer_size / 2 samples. Some devices have
+ * capture period = 2 so the audio_thread should wake up and consume some
+ * samples from hardware at that time. To prevent busy loop occurs, the returned
+ * sleep time should be >= 5ms.
+ *
+ * Returns: 0 on success negative error on device failure.
+ */
+static int get_input_dev_max_wake_ts(
+	struct open_dev *adev,
+	unsigned int curr_level,
+	struct timespec *res_ts)
+{
+	struct timespec dev_wake_ts, now;
+	unsigned int dev_rate, half_buffer_size, target_frames;
+
+	if(!adev || !adev->dev || !adev->dev->format ||
+	   !adev->dev->format->frame_rate || !adev->dev->buffer_size)
+		return -EINVAL;
+
+	*res_ts = min_input_dev_wake_ts;
+
+	dev_rate = adev->dev->format->frame_rate;
+	half_buffer_size = adev->dev->buffer_size / 2;
+	if(curr_level < half_buffer_size)
+		target_frames = half_buffer_size - curr_level;
+	else
+		target_frames = 0;
+
+	cras_frames_to_time(target_frames, dev_rate, &dev_wake_ts);
+
+	if (timespec_after(&dev_wake_ts, res_ts)) {
+		*res_ts = dev_wake_ts;
+	}
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+	add_timespecs(res_ts, &now);
+	return 0;
+}
+
+/*
+ * Set wake_ts for this device to be the earliest wake up time for
+ * dev_streams.
+ */
+static int set_input_dev_wake_ts(struct open_dev *adev)
+{
+	int rc;
+	struct timespec level_tstamp, wake_time_out, min_ts, now, dev_wake_ts;
+	unsigned int curr_level, cap_limit;
+	struct dev_stream *stream;
+	struct dev_stream *cap_limit_stream;
+
+	/* Limit the sleep time to 20 seconds. */
+	min_ts.tv_sec = 20;
+	min_ts.tv_nsec = 0;
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+	add_timespecs(&min_ts, &now);
+
+	rc = cras_iodev_frames_queued(adev->dev, &level_tstamp);
+	if (rc < 0)
+		return rc;
+	curr_level = rc;
+	if (!timespec_is_nonzero(&level_tstamp))
+		clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
+
+
+	cap_limit = get_stream_limit(adev, UINT_MAX, &cap_limit_stream);
+
+	/*
+	 * Loop through streams to find the earliest time audio thread
+	 * should wake up.
+	 */
+	DL_FOREACH(adev->dev->streams, stream) {
+		wake_time_out = min_ts;
+		rc = dev_stream_wake_time(
+			stream,
+			curr_level,
+			&level_tstamp,
+			cap_limit,
+			cap_limit_stream == stream,
+			&wake_time_out);
+
+		/*
+		 * rc > 0 means there is no need to set wake up time for this
+		 * stream.
+		 */
+		if (rc > 0)
+			continue;
+
+		if (rc < 0)
+			return rc;
+
+		if (timespec_after(&min_ts, &wake_time_out)) {
+			min_ts = wake_time_out;
+		}
+	}
+
+	if(adev->dev->active_node &&
+	   adev->dev->active_node->type != CRAS_NODE_TYPE_HOTWORD) {
+		rc = get_input_dev_max_wake_ts(adev, curr_level, &dev_wake_ts);
+		if(rc < 0) {
+			syslog(LOG_ERR,
+			       "Failed to call get_input_dev_max_wake_ts."
+			       "rc = %d", rc);
+		} else if(timespec_after(&min_ts, &dev_wake_ts)) {
+			min_ts = dev_wake_ts;
+		}
+	}
+
+	adev->wake_ts = min_ts;
+	return rc;
+}
+
+/* Read samples from an input device to the specified stream.
+ * Args:
+ *    adev - The device to capture samples from.
+ * Returns 0 on success.
+ */
+static int capture_to_streams(struct open_dev *adev)
+{
+	struct cras_iodev *idev = adev->dev;
+	snd_pcm_uframes_t remainder, hw_level, cap_limit;
+	struct timespec hw_tstamp;
+	int rc;
+	struct dev_stream *cap_limit_stream;
+	struct dev_stream *stream;
+
+	DL_FOREACH(adev->dev->streams, stream)
+		dev_stream_flush_old_audio_messages(stream);
+
+	rc = cras_iodev_frames_queued(idev, &hw_tstamp);
+	if (rc < 0)
+		return rc;
+	hw_level = rc;
+
+	cras_iodev_update_highest_hw_level(idev, hw_level);
+
+	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx,
+	      hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+	if (timespec_is_nonzero(&hw_tstamp)) {
+		if (hw_level < idev->min_cb_level / 2)
+			adev->coarse_rate_adjust = 1;
+		else if (hw_level > idev->max_cb_level * 2)
+			adev->coarse_rate_adjust = -1;
+		else
+			adev->coarse_rate_adjust = 0;
+		if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp))
+			update_estimated_rate(adev);
+	}
+
+	cap_limit = get_stream_limit(adev, hw_level, &cap_limit_stream);
+	set_stream_delay(adev);
+
+	remainder = MIN(hw_level, cap_limit);
+
+	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO, idev->info.idx,
+	      hw_level, remainder);
+
+	if (cras_iodev_state(idev) != CRAS_IODEV_STATE_NORMAL_RUN)
+		return 0;
+
+	while (remainder > 0) {
+		struct cras_audio_area *area = NULL;
+		unsigned int nread, total_read;
+
+		nread = remainder;
+
+		rc = cras_iodev_get_input_buffer(idev, &nread);
+		if (rc < 0 || nread == 0)
+			return rc;
+
+		DL_FOREACH(adev->dev->streams, stream) {
+			unsigned int this_read;
+			unsigned int area_offset;
+			float software_gain_scaler;
+
+			if ((stream->stream->flags & TRIGGER_ONLY) &&
+			    stream->stream->triggered)
+				continue;
+
+			input_data_get_for_stream(idev->input_data, stream->stream,
+						  idev->buf_state,
+						  &area, &area_offset);
+			/*
+			 * APM has more advanced gain control mechanism, so
+			 * don't apply the CRAS software gain to this stream
+			 * if APM is used.
+			 */
+			software_gain_scaler = stream->stream->apm_list
+				? 1.0f
+				: cras_iodev_get_software_gain_scaler(idev);
+
+			this_read = dev_stream_capture(
+					stream, area, area_offset,
+					software_gain_scaler);
+
+			input_data_put_for_stream(idev->input_data, stream->stream,
+						  idev->buf_state, this_read);
+		}
+
+		rc = cras_iodev_put_input_buffer(idev);
+		if (rc < 0)
+			return rc;
+
+		total_read = rc;
+		remainder -= nread;
+
+		if (total_read < nread)
+			break;
+	}
+
+	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0);
+
+	return 0;
+}
+
+/* Fill the buffer with samples from the attached streams.
+ * Args:
+ *    odevs - The list of open output devices, provided so streams can be
+ *            removed from all devices on error.
+ *    adev - The device to write to.
+ *    dst - The buffer to put the samples in (returned from snd_pcm_mmap_begin)
+ *    write_limit - The maximum number of frames to write to dst.
+ *
+ * Returns:
+ *    The number of frames rendered on success, a negative error code otherwise.
+ *    This number of frames is the minimum of the amount of frames each stream
+ *    could provide which is the maximum that can currently be rendered.
+ */
+static int write_streams(struct open_dev **odevs,
+			 struct open_dev *adev,
+			 uint8_t *dst,
+			 size_t write_limit)
+{
+	struct cras_iodev *odev = adev->dev;
+	struct dev_stream *curr;
+	unsigned int max_offset = 0;
+	unsigned int frame_bytes = cras_get_format_bytes(odev->ext_format);
+	unsigned int num_playing = 0;
+	unsigned int drain_limit = write_limit;
+
+	/* Mix as much as we can, the minimum fill level of any stream. */
+	max_offset = cras_iodev_max_stream_offset(odev);
+
+        /* Mix as much as we can, the minimum fill level of any stream. */
+	DL_FOREACH(adev->dev->streams, curr) {
+		int dev_frames;
+
+		/* If this is a single output dev stream, updates the latest
+		 * number of frames for playback. */
+		if (dev_stream_attached_devs(curr) == 1)
+			dev_stream_update_frames(curr);
+
+		dev_frames = dev_stream_playback_frames(curr);
+		if (dev_frames < 0) {
+			dev_io_remove_stream(
+				odevs,
+				curr->stream, NULL);
+			continue;
+		}
+		ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_STREAM,
+		      curr->stream->stream_id, dev_frames,
+		      dev_stream_is_pending_reply(curr));
+		if (cras_rstream_get_is_draining(curr->stream)) {
+			drain_limit = MIN((size_t)dev_frames, drain_limit);
+			if (!dev_frames)
+				dev_io_remove_stream(
+					odevs,
+					curr->stream, NULL);
+		} else {
+			write_limit = MIN((size_t)dev_frames, write_limit);
+			num_playing++;
+		}
+	}
+
+	if (!num_playing)
+		write_limit = drain_limit;
+
+	if (write_limit > max_offset)
+		memset(dst + max_offset * frame_bytes, 0,
+		       (write_limit - max_offset) * frame_bytes);
+
+	ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIX,
+	      write_limit, max_offset, 0);
+
+	DL_FOREACH(adev->dev->streams, curr) {
+		unsigned int offset;
+		int nwritten;
+
+		offset = cras_iodev_stream_offset(odev, curr);
+		if (offset >= write_limit)
+			continue;
+		nwritten = dev_stream_mix(curr, odev->ext_format,
+					  dst + frame_bytes * offset,
+					  write_limit - offset);
+
+		if (nwritten < 0) {
+			dev_io_remove_stream(odevs, curr->stream, NULL);
+			continue;
+		}
+
+		cras_iodev_stream_written(odev, curr, nwritten);
+	}
+
+	write_limit = cras_iodev_all_streams_written(odev);
+
+	ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIXED, write_limit, 0, 0);
+
+	return write_limit;
+}
+
+/* Update next wake up time of the device.
+ * Args:
+ *    adev[in] - The device to update to.
+ *    hw_level[out] - Pointer to number of frames in hardware.
+ */
+void update_dev_wakeup_time(struct open_dev *adev, unsigned int *hw_level)
+{
+	struct timespec now;
+	struct timespec sleep_time;
+	double est_rate;
+	unsigned int frames_to_play_in_sleep;
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+
+	frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep(
+			adev->dev, hw_level, &adev->wake_ts);
+	if (!timespec_is_nonzero(&adev->wake_ts))
+		adev->wake_ts = now;
+
+	if (cras_iodev_state(adev->dev) == CRAS_IODEV_STATE_NORMAL_RUN)
+		cras_iodev_update_highest_hw_level(adev->dev, *hw_level);
+
+	est_rate = adev->dev->ext_format->frame_rate *
+			cras_iodev_get_est_rate_ratio(adev->dev);
+
+	ATLOG(atlog, AUDIO_THREAD_SET_DEV_WAKE, adev->dev->info.idx,
+	      *hw_level, frames_to_play_in_sleep);
+
+	cras_frames_to_time_precise(
+			frames_to_play_in_sleep,
+			est_rate,
+			&sleep_time);
+
+	add_timespecs(&adev->wake_ts, &sleep_time);
+
+	ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx,
+	      adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec);
+}
+
+/* Returns 0 on success negative error on device failure. */
+int write_output_samples(struct open_dev **odevs,
+			 struct open_dev *adev,
+			 struct cras_fmt_conv *output_converter)
+{
+	struct cras_iodev *odev = adev->dev;
+	unsigned int hw_level;
+	struct timespec hw_tstamp;
+	unsigned int frames, fr_to_req;
+	snd_pcm_sframes_t written;
+	snd_pcm_uframes_t total_written = 0;
+	int rc;
+	int non_empty = 0;
+	int *non_empty_ptr = NULL;
+	uint8_t *dst = NULL;
+	struct cras_audio_area *area = NULL;
+
+	/* Possibly fill zeros for no_stream state and possibly transit state.
+	 */
+	rc = cras_iodev_prepare_output_before_write_samples(odev);
+	if (rc < 0) {
+		syslog(LOG_ERR, "Failed to prepare output dev for write");
+		return rc;
+	}
+
+	if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN)
+		return 0;
+
+	rc = cras_iodev_frames_queued(odev, &hw_tstamp);
+	if (rc < 0)
+		return rc;
+	hw_level = rc;
+
+	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx,
+	      hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+	if (timespec_is_nonzero(&hw_tstamp)) {
+		if (hw_level < odev->min_cb_level / 2)
+			adev->coarse_rate_adjust = 1;
+		else if (hw_level > odev->max_cb_level * 2)
+			adev->coarse_rate_adjust = -1;
+		else
+			adev->coarse_rate_adjust = 0;
+
+		if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp))
+			update_estimated_rate(adev);
+	}
+	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, 0);
+
+	/* Don't request more than hardware can hold. Note that min_buffer_level
+	 * has been subtracted from the actual hw_level so we need to take it
+	 * into account here. */
+	fr_to_req = cras_iodev_buffer_avail(odev, hw_level);
+
+	/* Have to loop writing to the device, will be at most 2 loops, this
+	 * only happens when the circular buffer is at the end and returns us a
+	 * partial area to write to from mmap_begin */
+	while (total_written < fr_to_req) {
+		frames = fr_to_req - total_written;
+		rc = cras_iodev_get_output_buffer(odev, &area, &frames);
+		if (rc < 0)
+			return rc;
+
+		/* TODO(dgreid) - This assumes interleaved audio. */
+		dst = area->channels[0].buf;
+		written = write_streams(odevs, adev, dst, frames);
+		if (written < 0) /* pcm has been closed */
+			return (int)written;
+
+		if (written < (snd_pcm_sframes_t)frames)
+			/* Got all the samples from client that we can, but it
+			 * won't fill the request. */
+			fr_to_req = 0; /* break out after committing samples */
+
+		// This interval is lazily initialized once per device.
+		// Note that newly opened devices are considered non-empty
+		// (until their status is updated through the normal flow).
+		if (!adev->non_empty_check_pi) {
+			adev->non_empty_check_pi = pic_polled_interval_create(
+				NON_EMPTY_UPDATE_INTERVAL_SEC);
+		}
+
+		// If we were empty last iteration, or the sampling interval
+		// has elapsed, check for emptiness.
+		if (adev->empty_pi ||
+			pic_interval_elapsed(adev->non_empty_check_pi)) {
+			non_empty_ptr = &non_empty;
+			pic_interval_reset(adev->non_empty_check_pi);
+		}
+
+		rc = cras_iodev_put_output_buffer(odev, dst, written,
+						  non_empty_ptr,
+						  output_converter);
+
+		if (rc < 0)
+			return rc;
+		total_written += written;
+
+		if (non_empty && adev->empty_pi) {
+			// We're not empty, but we were previously.
+			// Reset the empty period.
+			pic_polled_interval_destroy(&adev->empty_pi);
+		}
+
+		if (non_empty_ptr && !non_empty && !adev->empty_pi)
+			// We checked for emptiness, we were empty, and we
+			// previously weren't. Start the empty period.
+			adev->empty_pi = pic_polled_interval_create(
+				MIN_EMPTY_PERIOD_SEC);
+	}
+
+	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level,
+	      total_written, odev->min_cb_level);
+
+	return total_written;
+}
+
+/*
+ * Public funcitons.
+ */
+
+int dev_io_send_captured_samples(struct open_dev *idev_list)
+{
+	struct open_dev *adev;
+	int rc;
+
+	// TODO(dgreid) - once per rstream, not once per dev_stream.
+	DL_FOREACH(idev_list, adev) {
+		struct dev_stream *stream;
+
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+
+		/* Post samples to rstream if there are enough samples. */
+		DL_FOREACH(adev->dev->streams, stream) {
+			dev_stream_capture_update_rstream(stream);
+		}
+
+		/* Set wake_ts for this device. */
+		rc = set_input_dev_wake_ts(adev);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static void handle_dev_err(
+		int err_rc,
+		struct open_dev **odevs,
+		struct open_dev *adev)
+{
+	if (err_rc == -EPIPE) {
+		/* Handle severe underrun. */
+		ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN,
+		      adev->dev->info.idx, 0, 0);
+		cras_iodev_reset_request(adev->dev);
+	} else {
+		/* Device error, close it. */
+		dev_io_rm_open_dev(odevs, adev);
+	}
+}
+
+int dev_io_capture(struct open_dev **list)
+{
+	struct open_dev *idev_list = *list;
+	struct open_dev *adev;
+	int rc;
+
+	DL_FOREACH(idev_list, adev) {
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+		rc = capture_to_streams(adev);
+		if (rc < 0)
+			handle_dev_err(rc, list, adev);
+	}
+
+	return 0;
+}
+
+void dev_io_playback_fetch(struct open_dev *odev_list)
+{
+	struct open_dev *adev;
+
+	DL_FOREACH(odev_list, adev) {
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+		fetch_streams(adev);
+	}
+}
+
+int dev_io_playback_write(struct open_dev **odevs,
+			  struct cras_fmt_conv *output_converter)
+{
+	struct open_dev *adev;
+	struct dev_stream *curr;
+	int rc;
+	unsigned int hw_level, total_written;
+
+	/* For multiple output case, update the number of queued frames in shm
+	 * of all streams before starting write output samples. */
+	adev = *odevs;
+	if (adev && adev->next) {
+		DL_FOREACH(*odevs, adev) {
+			DL_FOREACH(adev->dev->streams, curr)
+				dev_stream_update_frames(curr);
+		}
+	}
+
+	DL_FOREACH(*odevs, adev) {
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+
+		rc = write_output_samples(odevs, adev, output_converter);
+		if (rc < 0) {
+			handle_dev_err(rc, odevs, adev);
+		} else {
+			total_written = rc;
+
+			/*
+			 * Skip the underrun check and device wake up time update if
+			 * device should not wake up.
+			 */
+			if (!cras_iodev_odev_should_wake(adev->dev))
+				continue;
+
+			/*
+			 * Update device wake up time and get the new hardware
+			 * level.
+			 */
+			update_dev_wakeup_time(adev, &hw_level);
+
+			/*
+			 * If new hardware level is less than or equal to the
+			 * written frames, we can suppose underrun happened. But
+			 * keep in mind there may have a false positive. If
+			 * hardware level changed just after frames being
+			 * written, we may get hw_level <= total_written here
+			 * without underrun happened. However, we can still
+			 * treat it as underrun because it is an abnormal state
+			 * we should handle it.
+			 */
+			if (hw_level <= total_written) {
+				ATLOG(atlog, AUDIO_THREAD_UNDERRUN,
+				      adev->dev->info.idx,
+				      hw_level, total_written);
+				rc = cras_iodev_output_underrun(adev->dev);
+				if(rc < 0) {
+					handle_dev_err(rc, odevs, adev);
+				} else {
+					update_dev_wakeup_time(adev, &hw_level);
+				}
+			}
+		}
+	}
+
+	/* TODO(dgreid) - once per rstream, not once per dev_stream. */
+	DL_FOREACH(*odevs, adev) {
+		struct dev_stream *stream;
+		if (!cras_iodev_is_open(adev->dev))
+			continue;
+		DL_FOREACH(adev->dev->streams, stream) {
+			dev_stream_playback_update_rstream(stream);
+		}
+	}
+
+	return 0;
+}
+
+void dev_io_run(struct open_dev **odevs, struct open_dev **idevs,
+		struct cras_fmt_conv *output_converter)
+{
+	pic_update_current_time();
+
+	dev_io_playback_fetch(*odevs);
+	dev_io_capture(idevs);
+	dev_io_send_captured_samples(*idevs);
+	dev_io_playback_write(odevs, output_converter);
+
+	check_non_empty_state_transition(*odevs);
+}
+
+static int input_adev_ignore_wake(const struct open_dev *adev)
+{
+	if (!cras_iodev_is_open(adev->dev))
+		return 1;
+
+	if (!adev->dev->active_node)
+		return 1;
+
+	if (adev->dev->active_node->type == CRAS_NODE_TYPE_HOTWORD &&
+	    !cras_iodev_input_streaming(adev->dev))
+		return 1;
+
+	return 0;
+}
+
+int dev_io_next_input_wake(struct open_dev **idevs, struct timespec *min_ts)
+{
+	struct open_dev *adev;
+	int ret = 0; /* The total number of devices to wait on. */
+
+	DL_FOREACH(*idevs, adev) {
+		if (input_adev_ignore_wake(adev))
+			continue;
+		ret++;
+		ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx,
+		      adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec);
+		if (timespec_after(min_ts, &adev->wake_ts))
+			*min_ts = adev->wake_ts;
+	}
+
+	return ret;
+}
+
+struct open_dev *dev_io_find_open_dev(struct open_dev *odev_list,
+				      const struct cras_iodev *dev)
+{
+	struct open_dev *odev;
+	DL_FOREACH(odev_list, odev)
+		if (odev->dev == dev)
+			return odev;
+	return NULL;
+}
+
+void dev_io_rm_open_dev(struct open_dev **odev_list,
+			struct open_dev *dev_to_rm)
+{
+	struct open_dev *odev;
+	struct dev_stream *dev_stream;
+
+	/* Do nothing if dev_to_rm wasn't already in the active dev list. */
+	odev = dev_io_find_open_dev(*odev_list, dev_to_rm->dev);
+	if (!odev)
+		return;
+
+
+	DL_DELETE(*odev_list, dev_to_rm);
+
+	/* Metrics logs the number of underruns of this device. */
+	cras_server_metrics_num_underruns(
+		cras_iodev_get_num_underruns(dev_to_rm->dev));
+
+	/* Metrics logs the highest_hw_level of this device. */
+	cras_server_metrics_highest_hw_level(
+		dev_to_rm->dev->highest_hw_level, dev_to_rm->dev->direction);
+
+	check_non_empty_state_transition(*odev_list);
+
+	ATLOG(atlog, AUDIO_THREAD_DEV_REMOVED, dev_to_rm->dev->info.idx, 0, 0);
+
+	DL_FOREACH(dev_to_rm->dev->streams, dev_stream) {
+		cras_iodev_rm_stream(dev_to_rm->dev, dev_stream->stream);
+		dev_stream_destroy(dev_stream);
+	}
+
+	if (dev_to_rm->empty_pi)
+		pic_polled_interval_destroy(&dev_to_rm->empty_pi);
+	if (dev_to_rm->non_empty_check_pi)
+		pic_polled_interval_destroy(&dev_to_rm->non_empty_check_pi);
+	free(dev_to_rm);
+}
+
+static void delete_stream_from_dev(struct cras_iodev *dev,
+				   struct cras_rstream *stream)
+{
+	struct dev_stream *out;
+
+	out = cras_iodev_rm_stream(dev, stream);
+	if (out)
+		dev_stream_destroy(out);
+}
+
+int dev_io_remove_stream(struct open_dev **dev_list,
+			 struct cras_rstream *stream,
+			 struct cras_iodev *dev)
+{
+	struct open_dev *open_dev;
+	struct timespec delay;
+	unsigned fetch_delay_msec;
+
+	/* Metrics log the longest fetch delay of this stream. */
+	if (timespec_after(&stream->longest_fetch_interval,
+			   &stream->sleep_interval_ts)) {
+		subtract_timespecs(&stream->longest_fetch_interval,
+				   &stream->sleep_interval_ts,
+				   &delay);
+		fetch_delay_msec = delay.tv_sec * 1000 +
+				   delay.tv_nsec / 1000000;
+		if (fetch_delay_msec)
+			cras_server_metrics_longest_fetch_delay(
+					fetch_delay_msec);
+	}
+
+	ATLOG(atlog, AUDIO_THREAD_STREAM_REMOVED, stream->stream_id, 0, 0);
+
+	if (dev == NULL) {
+		DL_FOREACH(*dev_list, open_dev) {
+			delete_stream_from_dev(open_dev->dev, stream);
+		}
+	} else {
+		delete_stream_from_dev(dev, stream);
+	}
+
+	return 0;
+}
diff --git a/cras/src/server/dev_io.h b/cras/src/server/dev_io.h
new file mode 100644
index 0000000..83e16e0
--- /dev/null
+++ b/cras/src/server/dev_io.h
@@ -0,0 +1,91 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * `dev_io` Handles playback to and capture from open devices.  It runs only on
+ * the audio thread.
+ */
+
+#ifndef DEV_IO_H_
+#define DEV_IO_H_
+
+#include "cras_iodev.h"
+#include "cras_types.h"
+#include "polled_interval_checker.h"
+
+/*
+ * Open input/output devices.
+ *    dev - The device.
+ *    wake_ts - When callback is needed to avoid xrun.
+ *    last_non_empty_ts - The last time we know the device played/captured
+ *        non-empty (zero) audio.
+ *    coarse_rate_adjust - Hack for when the sample rate needs heavy correction.
+ */
+struct open_dev {
+	struct cras_iodev *dev;
+	struct timespec wake_ts;
+	struct polled_interval *non_empty_check_pi;
+	struct polled_interval *empty_pi;
+	int coarse_rate_adjust;
+	struct open_dev *prev, *next;
+};
+
+/*
+ * Fetches streams from each device in `odev_list`.
+ *    odev_list - The list of open devices.
+ */
+void dev_io_playback_fetch(struct open_dev *odev_list);
+
+/*
+ * Writes the samples fetched from the streams to the playback devices.
+ *    odev_list - The list of open devices.  Devices will be removed when
+ *                writing returns an error.
+ */
+int dev_io_playback_write(struct open_dev **odevs,
+			  struct cras_fmt_conv *output_converter);
+
+/* Only public for testing. */
+int write_output_samples(struct open_dev **odevs,
+			 struct open_dev *adev,
+			 struct cras_fmt_conv *output_converter);
+
+/*
+ * Captures samples from each device in the list.
+ *    list - Pointer to the list of input devices.  Devices that fail to read
+ *           will be removed from the list.
+ */
+int dev_io_capture(struct open_dev **list);
+
+/*
+ * Send samples that have been captured to their streams.
+ */
+int dev_io_send_captured_samples(struct open_dev *idev_list);
+
+/* Reads and/or writes audio samples from/to the devices. */
+void dev_io_run(struct open_dev **odevs, struct open_dev **idevs,
+		struct cras_fmt_conv *output_converter);
+
+/*
+ * Fills min_ts with the next time the system should wake to service input.
+ * Returns the number of devices waiting.
+ */
+int dev_io_next_input_wake(struct open_dev **idevs, struct timespec *min_ts);
+
+/*
+ * Removes a device from a list of devices.
+ *    odev_list - A pointer to the list to modify.
+ *    dev_to_rm - Find this device in the list and remove it.
+ */
+void dev_io_rm_open_dev(struct open_dev **odev_list,
+			struct open_dev *dev_to_rm);
+
+/* Returns a pointer to an open_dev if it is in the list, otherwise NULL. */
+struct open_dev *dev_io_find_open_dev(struct open_dev *odev_list,
+                                      const struct cras_iodev *dev);
+
+/* Remove a stream from the provided list of devices. */
+int dev_io_remove_stream(struct open_dev **dev_list,
+			 struct cras_rstream *stream,
+			 struct cras_iodev *dev);
+
+#endif /* DEV_IO_H_ */
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index 4d893cc..a625d58 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -32,6 +32,38 @@
 	.tv_nsec = 1000000, /* 1 ms. */
 };
 
+/*
+ * Returns the size in frames that a format converter must allocate for its
+ * temporary buffers to be able to convert the specified number of stream
+ * frames to or from the corresponding number of device frames, at the
+ * specified device rate.
+ */
+unsigned int max_frames_for_conversion(unsigned int stream_frames,
+				       unsigned int stream_rate,
+				       unsigned int device_rate) {
+	/*
+	 * There are multiple temp buffers in the format converter,
+	 * which are all the same size. Some of these contain audio
+	 * in the source sample rate, and others in the converted
+	 * sample rate. We need to make sure the converter is large
+	 * enough to hold either.
+	 */
+	return MAX(
+	    // Number of stream frames does not require conversion.
+	    stream_frames,
+	    // Calculate corresponding number of frames at device rate.
+	    cras_frames_at_rate(stream_rate,
+				stream_frames,
+				device_rate))
+			/*
+			 * Add 1 because the linear resampler's frame rate
+			 * conversion does this, and is used to calculate
+			 * how many frames to read from the device.
+			 * See linear_resampler_{in,out}_frames_to_{out,in}(..)
+			 */
+			+ 1;
+}
+
 struct dev_stream *dev_stream_create(struct cras_rstream *stream,
 				     unsigned int dev_id,
 				     const struct cras_audio_format *dev_fmt,
@@ -49,24 +81,27 @@
 	out->stream = stream;
 	out->dev_rate = dev_fmt->frame_rate;
 
+	max_frames = max_frames_for_conversion(stream->buffer_frames,
+					       stream_fmt->frame_rate,
+					       dev_fmt->frame_rate);
+
 	if (stream->direction == CRAS_STREAM_OUTPUT) {
-		max_frames = MAX(stream->buffer_frames,
-				 cras_frames_at_rate(stream_fmt->frame_rate,
-						     stream->buffer_frames,
-						     dev_fmt->frame_rate));
 		rc = config_format_converter(&out->conv,
 					     stream->direction,
 					     stream_fmt,
 					     dev_fmt,
 					     max_frames);
 	} else {
-		max_frames = MAX(stream->buffer_frames,
-				 cras_frames_at_rate(dev_fmt->frame_rate,
-						     stream->buffer_frames,
-						     stream_fmt->frame_rate));
+		/*
+		 * For input, take into account the stream specific processing
+		 * like AEC. Use the post processing format to configure format
+		 * converter.
+		 */
+		ofmt = cras_rstream_post_processing_format(
+				stream, dev_ptr) ? : dev_fmt,
 		rc = config_format_converter(&out->conv,
 					     stream->direction,
-					     dev_fmt,
+					     ofmt,
 					     stream_fmt,
 					     max_frames);
 	}
@@ -117,8 +152,8 @@
 	cras_rstream_dev_detach(dev_stream->stream, dev_stream->dev_id);
 	if (dev_stream->conv) {
 		cras_audio_area_destroy(dev_stream->conv_area);
-		cras_fmt_conv_destroy(dev_stream->conv);
-		byte_buffer_destroy(dev_stream->conv_buffer);
+		cras_fmt_conv_destroy(&dev_stream->conv);
+		byte_buffer_destroy(&dev_stream->conv_buffer);
 	}
 	free(dev_stream);
 }
@@ -286,7 +321,7 @@
 			cras_rstream_get_cb_threshold(rstream),
 			&rstream->audio_area->frames);
 	num_frames = MIN(rstream->audio_area->frames - offset,
-			 buf_queued_bytes(dev_stream->conv_buffer) /
+			 buf_queued(dev_stream->conv_buffer) /
 							frame_bytes);
 
 	ATLOG(atlog, AUDIO_THREAD_CONV_COPY,
@@ -343,14 +378,18 @@
 
 	/* Check if format conversion is needed. */
 	if (cras_fmt_conversion_needed(dev_stream->conv)) {
-		unsigned int format_bytes;
+		unsigned int format_bytes, fr_to_capture;
+
+		fr_to_capture = dev_stream_capture_avail(dev_stream);
+		fr_to_capture = MIN(fr_to_capture, area->frames - area_offset);
 
 		format_bytes = cras_get_format_bytes(
 				cras_fmt_conv_in_format(dev_stream->conv));
 		nread = capture_with_fmt_conv(
 			dev_stream,
 			area->channels[0].buf + area_offset * format_bytes,
-			area->frames - area_offset);
+			fr_to_capture);
+
 		capture_copy_converted_to_stream(dev_stream, rstream,
 						 software_gain_scaler);
 	} else {
@@ -446,15 +485,15 @@
 
 	/* Sample rate conversion may cause some sample left in conv_buffer
 	 * take this buffer into account. */
-	conv_buf_level = buf_queued_bytes(dev_stream->conv_buffer) /
+	conv_buf_level = buf_queued(dev_stream->conv_buffer) /
 			format_bytes;
-	if (frames_avail < conv_buf_level)
+	if (frames_avail <= conv_buf_level)
 		return 0;
 	else
 		frames_avail -= conv_buf_level;
 
 	frames_avail = MIN(frames_avail,
-			   buf_available_bytes(dev_stream->conv_buffer) /
+			   buf_available(dev_stream->conv_buffer) /
 					format_bytes);
 
 	return cras_fmt_conv_out_frames_to_in(dev_stream->conv, frames_avail);
@@ -495,6 +534,9 @@
 	unsigned int frames_ready = cras_rstream_get_cb_threshold(rstream);
 	int rc;
 
+	if ((rstream->flags & TRIGGER_ONLY) && rstream->triggered)
+		return 0;
+
 	cras_rstream_update_input_write_pointer(rstream);
 
 	/*
@@ -523,6 +565,9 @@
 	if (rc < 0)
 		return rc;
 
+	if (rstream->flags & TRIGGER_ONLY)
+		rstream->triggered = 1;
+
 	/* Update next callback time according to perfect schedule. */
 	add_timespecs(&rstream->next_cb_ts,
 		      &rstream->sleep_interval_ts);
@@ -608,7 +653,7 @@
 	shm = cras_rstream_output_shm(rstream);
 
 	/* Don't fetch if the previous request hasn't got response. */
-	return !cras_shm_callback_pending(shm) &&
+	return !cras_rstream_is_pending_reply(rstream) &&
 	       cras_shm_is_buffer_available(shm);
 }
 
@@ -625,7 +670,6 @@
 	add_timespecs(&rstream->next_cb_ts,
 		      &rstream->sleep_interval_ts);
 	check_next_wake_time(dev_stream);
-	cras_shm_set_callback_pending(cras_rstream_output_shm(rstream), 1);
 
 	return 0;
 }
@@ -634,8 +678,14 @@
 {
 	const struct cras_rstream *stream = dev_stream->stream;
 
+	/* For streams which rely on dev level timing, we should
+	 * let client response wake audio thread up. */
+	if (stream_uses_input(stream) && (stream->flags & USE_DEV_TIMING) &&
+	    cras_rstream_is_pending_reply(stream))
+		return stream->fd;
+
 	if (!stream_uses_output(stream) ||
-	    !cras_shm_callback_pending(&stream->shm) ||
+	    !cras_rstream_is_pending_reply(stream) ||
 	    cras_rstream_get_is_draining(stream))
 		return -1;
 
@@ -643,54 +693,69 @@
 }
 
 /*
- * Needed frames from this device such that written frames in shm meets
- * cb_threshold.
- */
-static int get_input_needed_frames(struct dev_stream *dev_stream,
-				   unsigned int curr_level)
-{
-	struct cras_rstream *rstream = dev_stream->stream;
-	unsigned int rstream_level = cras_rstream_level(rstream);
-	unsigned int dev_offset = cras_rstream_dev_offset(
-			rstream, dev_stream->dev_id);
-	unsigned int needed_for_stream;
-
-	/*
-	 * rstream_level + def_offset is the number of frames written to shm
-	 * from this device.
-	 */
-	if (rstream_level + dev_offset > rstream->cb_threshold) {
-		/* Enough frames from this device for this stream. */
-		return 0;
-	}
-
-	/*
-	 * Needed number of frames in shm such that written frames in shm meets
-	 * cb_threshold.
-	 */
-	needed_for_stream = rstream->cb_threshold - rstream_level - dev_offset;
-
-	/* Convert the number of frames from stream format to device format. */
-	return cras_fmt_conv_out_frames_to_in(dev_stream->conv,
-					      needed_for_stream);
-
-}
-
-/*
  * Gets proper wake up time for an input stream. It considers both
  * time for samples to reach one callback level, and the time for next callback.
+ * Returns:
+ *   0 on success; negavite error code on failure. A positive value if
+ *   there is no need to set wake up time for this stream.
  */
 static int get_input_wake_time(struct dev_stream *dev_stream,
 			       unsigned int curr_level,
 			       struct timespec *level_tstamp,
+			       unsigned int cap_limit,
+			       int is_cap_limit_stream,
 			       struct timespec *wake_time_out)
 {
 	struct cras_rstream *rstream = dev_stream->stream;
 	struct timespec time_for_sample;
 	int needed_frames_from_device;
 
-	needed_frames_from_device = get_input_needed_frames(
-			dev_stream, curr_level);
+	needed_frames_from_device = dev_stream_capture_avail(dev_stream);
+
+	/*
+	 * If this stream is not cap_limit stream, and it needs more
+	 * frames than the capture limit from audio thread, don't bother
+	 * re-calculating the wake time for it because
+	 * |needed_frames_from_device| cannot be all copied to shm until
+	 * the cap_limit stream get its samples in shm read by client
+	 * and relieve the cap_limit.
+	 *
+	 * Note that we need to know whether this stream is cap_limit
+	 * stream here because the client of cap_limit stream may read
+	 * the data from shm during this time window, and cause
+	 * needed_frames_from_device to be greater than cap_limit which
+	 * was calculated before.
+	 */
+	if (!is_cap_limit_stream && needed_frames_from_device > cap_limit)
+		return 1;
+
+	/*
+	 * For capture stream using device timing, the flow would be:
+	 * 1. Device has less than one cb_threshold of data.
+	 * 2. Device has a large chunk of data that client needs to consume
+	 *    in multiple cycles.
+	 * 3. Audio thread sends one block to client and goes to sleep.
+	 * 4. Client sends reply to wake up audio thread.
+	 * 5. Repeat 3 and 4 until there is less than one cb_threshold of data.
+	 * 6. Goes to 1.
+	 *
+	 * In 1, we schedule the next wake up time based on the needed frames.
+	 * This is needed to poll the samples from device.
+	 *
+	 * In 3, we do not schedule a wake up time for this stream.
+	 * We let reply from client wakes up audio thread to send next
+	 * cb_threshold of data.
+	 *
+	 * TODO(cychiang) Do we want to actually block sending data to client
+	 * until client replies ? Or control the scheduling of wake up time
+	 * is enough ?
+	 *
+	 */
+	if ((rstream->flags & USE_DEV_TIMING) &&
+	     cras_rstream_is_pending_reply(rstream))
+		return 1;
+
+	*wake_time_out = rstream->next_cb_ts;
 
 	/*
 	 * If current frames in the device can provide needed amount for stream,
@@ -698,7 +763,6 @@
 	 */
 	if (curr_level >= needed_frames_from_device)
 		needed_frames_from_device = 0;
-
 	else
 		needed_frames_from_device -= curr_level;
 
@@ -710,11 +774,11 @@
 
 	/* Select the time that is later so both sample and time conditions
 	 * are met. */
-	if (timespec_after(&time_for_sample, &rstream->next_cb_ts)) {
+	if (timespec_after(&time_for_sample, &rstream->next_cb_ts))
 		*wake_time_out =  time_for_sample;
-	} else {
-		*wake_time_out =  rstream->next_cb_ts;
-	}
+	/* Using device timing means the stream neglects next callback time. */
+	if (rstream->flags & USE_DEV_TIMING)
+		*wake_time_out =  time_for_sample;
 
 	return 0;
 }
@@ -722,6 +786,8 @@
 int dev_stream_wake_time(struct dev_stream *dev_stream,
 			 unsigned int curr_level,
 			 struct timespec *level_tstamp,
+			 unsigned int cap_limit,
+			 int is_cap_limit_stream,
 			 struct timespec *wake_time_out)
 {
 	if (dev_stream->stream->direction == CRAS_STREAM_OUTPUT) {
@@ -734,5 +800,16 @@
 	}
 
 	return get_input_wake_time(dev_stream, curr_level, level_tstamp,
+				   cap_limit, is_cap_limit_stream,
 				   wake_time_out);
 }
+
+int dev_stream_is_pending_reply(const struct dev_stream *dev_stream)
+{
+	return cras_rstream_is_pending_reply(dev_stream->stream);
+}
+
+int dev_stream_flush_old_audio_messages(struct dev_stream *dev_stream)
+{
+	return cras_rstream_flush_old_audio_messages(dev_stream->stream);
+}
diff --git a/cras/src/server/dev_stream.h b/cras/src/server/dev_stream.h
index c4af825..52838f0 100644
--- a/cras/src/server/dev_stream.h
+++ b/cras/src/server/dev_stream.h
@@ -162,13 +162,20 @@
  * Args:
  *   dev_stream[in]: The dev_stream to check wake up time.
  *   curr_level[in]: The current level of device.
+ *   level_tstamp[in]: The time stamp when getting current level of device.
+ *   cap_limit[in]: The number of frames that can be captured across all
+ *                  streams.
+ *   is_cap_limit_stream[in]: 1 if this stream is causing cap_limit.
  *   wake_time_out[out]: A timespec for wake up time.
  * Returns:
  *   0 on success; negative error code on failure.
+ *   A positive value if there is no need to set wake up time for this stream.
  */
 int dev_stream_wake_time(struct dev_stream *dev_stream,
 			 unsigned int curr_level,
 			 struct timespec *level_tstamp,
+			 unsigned int cap_limit,
+			 int is_cap_limit_stream,
 			 struct timespec *wake_time_out);
 
 /*
@@ -192,4 +199,11 @@
 	return &dev_stream->stream->sleep_interval_ts;
 }
 
+int dev_stream_is_pending_reply(const struct dev_stream *dev_stream);
+
+/*
+ * Reads any pending audio message from the socket.
+ */
+int dev_stream_flush_old_audio_messages(struct dev_stream *dev_stream);
+
 #endif /* DEV_STREAM_H_ */
diff --git a/cras/src/server/float_buffer.h b/cras/src/server/float_buffer.h
new file mode 100644
index 0000000..55e176e
--- /dev/null
+++ b/cras/src/server/float_buffer.h
@@ -0,0 +1,128 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef FLOAT_BUFFER_H_
+#define FLOAT_BUFFER_H_
+
+#include "byte_buffer.h"
+
+/*
+ * Circular buffer storing deinterleaved floating point data.
+ * Members:
+ *    fp - Pointer to be filled wtih read/write position of the buffer.
+ *    num_channels - Number of channels.
+ */
+struct float_buffer {
+	struct byte_buffer *buf;
+	float **fp;
+	unsigned int num_channels;
+};
+
+/*
+ * Creates an float_buffer.
+ * Args:
+ *    max_size - The max number of frames this buffer may store.
+ *    num_channels - Number of channels of the deinterleaved data.
+ */
+static inline struct float_buffer *float_buffer_create(
+		unsigned int max_size,
+		unsigned int num_channels)
+{
+	struct float_buffer *b;
+
+	b = (struct float_buffer *)calloc(1, sizeof(*b));
+
+	b->num_channels = num_channels;
+	b->fp = (float **)malloc(num_channels * sizeof(float *));
+	b->buf = (struct byte_buffer *)
+		calloc(1, sizeof(struct byte_buffer) +
+			max_size * num_channels * sizeof(float));
+	b->buf->max_size = max_size;
+	b->buf->used_size = max_size;
+	return b;
+}
+
+/* Destroys the float buffer. */
+static inline void float_buffer_destroy(struct float_buffer **b)
+{
+	if (*b == NULL)
+		return;
+
+	byte_buffer_destroy(&(*b)->buf);
+	free((*b)->fp);
+	free(*b);
+	*b = NULL;
+}
+
+/* Gets the write pointer of given float_buffer. */
+static inline float *const *float_buffer_write_pointer(struct float_buffer *b)
+{
+	unsigned int i;
+	float *data = (float *)b->buf->bytes;
+
+	for (i = 0; i < b->num_channels; i++, data += b->buf->max_size)
+		b->fp[i] = data + b->buf->write_idx;
+	return b->fp;
+}
+
+/* Gets the number of frames can write to the float_buffer. */
+static inline unsigned int float_buffer_writable(struct float_buffer *b)
+{
+	return buf_writable(b->buf);
+}
+
+/* Marks |nwritten| of frames as written to float_buffer. */
+static inline void float_buffer_written(struct float_buffer *b,
+					unsigned int nwritten)
+{
+	buf_increment_write(b->buf, nwritten);
+}
+
+/* Gets the read pointer of given float_buffer. */
+static inline float *const *float_buffer_read_pointer(struct float_buffer *b,
+						      unsigned int offset,
+						      unsigned int *readable)
+{
+	unsigned int i;
+	float *data = (float *)b->buf->bytes;
+	unsigned int nread = buf_readable(b->buf);
+
+	if (offset >= buf_queued(b->buf)) {
+		*readable = 0;
+		offset = 0;
+	} else if (offset >= nread) {
+		/* wraps */
+		offset = offset + b->buf->read_idx - b->buf->max_size;
+		*readable = MIN(*readable, b->buf->write_idx - offset);
+	} else {
+		*readable = MIN(*readable, nread - offset);
+		offset += b->buf->read_idx;
+	}
+
+	for (i = 0; i < b->num_channels; i++, data += b->buf->max_size)
+		b->fp[i] = data + offset;
+	return b->fp;
+}
+
+/* Gets the buffer level in frames queued in float_buffer. */
+static inline unsigned int float_buffer_level(struct float_buffer *b)
+{
+	return buf_queued(b->buf);
+}
+
+/* Resets float_buffer to initial state. */
+static inline void float_buffer_reset(struct float_buffer *b)
+{
+	buf_reset(b->buf);
+}
+
+/* Marks |nread| frames as read in float_buffer. */
+static inline void float_buffer_read(struct float_buffer *b,
+				     unsigned int nread)
+{
+	buf_increment_read(b->buf, nread);
+}
+
+#endif /* FLOAT_BUFFER_H_ */
diff --git a/cras/src/server/iniparser_wrapper.h b/cras/src/server/iniparser_wrapper.h
new file mode 100644
index 0000000..b6d32f9
--- /dev/null
+++ b/cras/src/server/iniparser_wrapper.h
@@ -0,0 +1,22 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef INIPARSER_WRAPPER_H_
+#define INIPARSER_WRAPPER_H_
+
+#include <iniparser.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static inline dictionary *iniparser_load_wrapper(const char *ini_name)
+{
+	struct stat st;
+	int rc = stat(ini_name, &st);
+	if (rc < 0)
+		return NULL;
+	return iniparser_load(ini_name);
+}
+
+#endif /* INIPARSER_WRAPPER_H_ */
diff --git a/cras/src/server/input_data.c b/cras/src/server/input_data.c
new file mode 100644
index 0000000..a790e11
--- /dev/null
+++ b/cras/src/server/input_data.c
@@ -0,0 +1,139 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "buffer_share.h"
+#include "cras_audio_area.h"
+#include "cras_dsp_pipeline.h"
+#include "cras_mix.h"
+#include "cras_rstream.h"
+#include "cras_system_state.h"
+#include "dsp_util.h"
+#include "input_data.h"
+#include "utlist.h"
+
+void input_data_run(struct ext_dsp_module *ext,
+		    unsigned int nframes)
+{
+	struct input_data *data = (struct input_data *)ext;
+	float *const *wp;
+	int i;
+	unsigned int writable;
+	unsigned int offset = 0;
+
+	while (nframes) {
+		writable = float_buffer_writable(data->fbuffer);
+		writable = MIN(nframes, writable);
+		if (!writable) {
+			syslog(LOG_ERR, "Not enough space to process input data");
+			break;
+		}
+		wp = float_buffer_write_pointer(data->fbuffer);
+		for (i = 0; i < data->fbuffer->num_channels; i++)
+			memcpy(wp[i], ext->ports[i] + offset, writable * sizeof(float));
+
+		float_buffer_written(data->fbuffer, writable);
+		nframes -= writable;
+		offset += writable;
+	}
+}
+
+void input_data_configure(struct ext_dsp_module *ext, unsigned int buffer_size,
+			  unsigned int num_channels, unsigned int rate)
+{
+	struct input_data *data = (struct input_data *)ext;
+
+	if (data->fbuffer)
+		float_buffer_destroy(&data->fbuffer);
+	data->fbuffer = float_buffer_create(buffer_size, num_channels);
+}
+
+struct input_data *input_data_create(void *dev_ptr)
+{
+	struct input_data *data = (struct input_data *)calloc(1, sizeof(*data));
+
+	data->dev_ptr = dev_ptr;
+
+	data->ext.run = input_data_run;
+	data->ext.configure = input_data_configure;
+
+	return data;
+}
+
+void input_data_destroy(struct input_data **data)
+{
+	if ((*data)->fbuffer)
+		float_buffer_destroy(&(*data)->fbuffer);
+	free(*data);
+	*data = NULL;
+}
+
+void input_data_set_all_streams_read(struct input_data *data,
+				     unsigned int nframes)
+{
+	if (!data->fbuffer)
+		return;
+
+	if (float_buffer_level(data->fbuffer) < nframes) {
+		syslog(LOG_ERR, "All streams read %u frames exceeds %u"
+		       " in input_data's buffer",
+		       nframes, float_buffer_level(data->fbuffer));
+		float_buffer_reset(data->fbuffer);
+		return;
+	}
+	float_buffer_read(data->fbuffer, nframes);
+}
+
+int input_data_get_for_stream(struct input_data *data,
+			      struct cras_rstream *stream,
+			      struct buffer_share *offsets,
+			      struct cras_audio_area **area,
+			      unsigned int *offset)
+{
+	unsigned int apm_processed;
+	struct cras_apm *apm;
+
+	/*
+	 * It is possible that area buffer frames is smaller than the
+	 * offset of stream. In this case, just reset the offset value
+	 * to area->frames to prevent caller using these information get
+	 * bad access to data.
+	 */
+	*area = data->area;
+	*offset = MIN(buffer_share_id_offset(offsets, stream->stream_id),
+		      data->area->frames);
+
+	apm = cras_apm_list_get(stream->apm_list, data->dev_ptr);
+	if (apm == NULL)
+		return 0;
+
+	apm_processed = cras_apm_list_process(apm, data->fbuffer, *offset);
+	if (apm_processed < 0) {
+		cras_apm_list_remove(stream->apm_list, apm);
+		return 0;
+	}
+	buffer_share_offset_update(offsets, stream->stream_id, apm_processed);
+	*area = cras_apm_list_get_processed(apm);
+	*offset = 0;
+
+	return 0;
+}
+
+int input_data_put_for_stream(struct input_data *data,
+			      struct cras_rstream *stream,
+			      struct buffer_share *offsets,
+			      unsigned int frames)
+{
+	struct cras_apm *apm = cras_apm_list_get(
+			stream->apm_list, data->dev_ptr);
+
+	if (apm)
+		cras_apm_list_put_processed(apm, frames);
+	else
+		buffer_share_offset_update(offsets, stream->stream_id, frames);
+
+	return 0;
+}
diff --git a/cras/src/server/input_data.h b/cras/src/server/input_data.h
new file mode 100644
index 0000000..71ec1a6
--- /dev/null
+++ b/cras/src/server/input_data.h
@@ -0,0 +1,79 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef INPUT_DATA_H_
+#define INPUT_DATA_H_
+
+#include "cras_dsp_pipeline.h"
+#include "float_buffer.h"
+
+/*
+ * Structure holding the information used when a chunk of input buffer
+ * is accessed by multiple streams with different properties and
+ * processing requirements.
+ * Member:
+ *    ext - Provides interface to read and process buffer in dsp pipeline.
+ *    dev_ptr - Pointer to the associated input iodev.
+ *    area - The audio area used for deinterleaved data copy.
+ *    fbuffer - Floating point buffer from input device.
+ */
+struct input_data {
+	struct ext_dsp_module ext;
+	void *dev_ptr;
+	struct cras_audio_area *area;
+	struct float_buffer *fbuffer;
+};
+
+/*
+ * Creates an input_data instance for input iodev.
+ * Args:
+ *    dev_ptr - Pointer to the associated input device.
+ */
+struct input_data *input_data_create(void *dev_ptr);
+
+/* Destroys an input_data instance. */
+void input_data_destroy(struct input_data **data);
+
+/* Sets how many frames in buffer has been read by all input streams. */
+void input_data_set_all_streams_read(struct input_data *data,
+				     unsigned int nframes);
+
+/*
+ * Gets an audio area for |stream| to read data from. An input_data may be
+ * accessed by multiple streams while some requires processing, the
+ * |offsets| arguments helps track the offset value each stream has read
+ * into |data|.
+ * Args:
+ *    data - The input data to get audio area from.
+ *    stream - The stream that reads data.
+ *    offsets - Structure holding the mapping from stream to the offset value
+ *        of how many frames each stream has read into input buffer.
+ *    area - To be filled with a pointer to an audio area struct for stream to
+ *        read data.
+ *    offset - To be filled with the samples offset in |area| that |stream|
+ *        should start reading.
+ */
+int input_data_get_for_stream(
+		struct input_data *data,
+		struct cras_rstream *stream,
+		struct buffer_share *offsets,
+		struct cras_audio_area **area,
+		unsigned int *offset);
+
+/*
+ * Marks |frames| of audio data as read by |stream|.
+ * Args:
+ *    data - The input_data to mark frames has been read by |stream|.
+ *    stream - The stream that has read audio data.
+ *    offsets - Structure holding the mapping from stream to the offset value
+ *        of how many frames each stream has read into input buffer.
+ *    frames - Number of frames |stream| has read.
+ */
+int input_data_put_for_stream(struct input_data *data,
+			   struct cras_rstream *stream,
+			   struct buffer_share *offsets,
+			   unsigned int frames);
+
+#endif /* INPUT_DATA_H_ */
diff --git a/cras/src/server/polled_interval_checker.c b/cras/src/server/polled_interval_checker.c
new file mode 100644
index 0000000..f647455
--- /dev/null
+++ b/cras/src/server/polled_interval_checker.c
@@ -0,0 +1,47 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "cras_util.h"
+#include "polled_interval_checker.h"
+
+struct polled_interval {
+    struct timespec last_interval_start_ts;
+    int interval_sec;
+};
+
+static struct timespec now;
+
+static inline int get_sec_since_last_active(
+	const struct timespec *last_active_ts) {
+	struct timespec diff;
+	subtract_timespecs(&now, last_active_ts, &diff);
+	return diff.tv_sec;
+}
+
+void pic_update_current_time() {
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+}
+
+struct polled_interval *pic_polled_interval_create(int interval_sec) {
+	struct polled_interval *pi;
+	pi = malloc(sizeof(*pi));
+	pi->last_interval_start_ts = now;
+	pi->interval_sec = interval_sec;
+	return pi;
+}
+
+void pic_polled_interval_destroy(struct polled_interval **interval) {
+	free(*interval);
+	*interval = NULL;
+}
+
+int pic_interval_elapsed(const struct polled_interval *pi) {
+	return get_sec_since_last_active(&pi->last_interval_start_ts) >=
+		pi->interval_sec;
+}
+
+void pic_interval_reset(struct polled_interval *pi) {
+	pi->last_interval_start_ts = now;
+}
\ No newline at end of file
diff --git a/cras/src/server/polled_interval_checker.h b/cras/src/server/polled_interval_checker.h
new file mode 100644
index 0000000..fc2fec5
--- /dev/null
+++ b/cras/src/server/polled_interval_checker.h
@@ -0,0 +1,50 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef POLLED_ACTIVITY_CHECKER_H_
+#define POLLED_ACTIVITY_CHECKER_H_
+
+#include <time.h>
+
+/* Represents a time interval, in seconds, which can be checked periodically. */
+struct polled_interval;
+
+/*
+ * Creates a new polled_interval, of the specified duration. The interval will
+ * first elapse interval_sec after it was created.
+ *
+ * Call pic_update_current_time() shortly before this function.
+ */
+struct polled_interval *pic_polled_interval_create(int interval_sec);
+
+/*
+ * Destroys the specified polled_interval, and set's the pointer to it to NULL.
+ */
+void pic_polled_interval_destroy(struct polled_interval **interval);
+
+/*
+ * Whether the interval's duration has elapsed (since the interval was created
+ * or reset).
+ *
+ * Call pic_update_current_time() shortly before this function.
+ */
+int pic_interval_elapsed(const struct polled_interval *interval);
+
+/*
+ * Resets the interval; it will elapse it's specified duration from now.
+ *
+ * Call pic_update_current_time() shortly before this function.
+ */
+void pic_interval_reset(struct polled_interval *pi);
+
+/*
+ * Updates the current time, which is used in all other pic_* functions (which
+ * will never update the current time). This update is pulled out separately to
+ * allow the caller to control when and how often the time is updated.
+ */
+void pic_update_current_time();
+
+
+#endif /* POLLED_ACTIVITY_CHECKER_H_ */
\ No newline at end of file
diff --git a/cras/src/server/rate_estimator.c b/cras/src/server/rate_estimator.c
index b6b76ac..e12c0b7 100644
--- a/cras/src/server/rate_estimator.c
+++ b/cras/src/server/rate_estimator.c
@@ -81,12 +81,8 @@
 {
 	struct timespec td;
 
-	/* TODO(hychao) - is this the right thing to do if level is 0? */
-	if ((re->window_start_ts.tv_sec == 0) || (level == 0)) {
+	if (re->window_start_ts.tv_sec == 0) {
 		re->window_start_ts = *now;
-		re->window_frames = 0;
-		re->level_diff = 0;
-		re->last_level = level;
 		return 0;
 	}
 
diff --git a/cras/src/server/server_stream.c b/cras/src/server/server_stream.c
new file mode 100644
index 0000000..242a34c
--- /dev/null
+++ b/cras/src/server/server_stream.c
@@ -0,0 +1,99 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "cras_rstream.h"
+#include "cras_server.h"
+#include "cras_system_state.h"
+#include "cras_types.h"
+#include "server_stream.h"
+#include "stream_list.h"
+
+
+/* Parameters used for server stream. */
+static unsigned int server_stream_block_size = 480;
+
+/*
+ * Server stream doesn't care what format is used, because no
+ * client is reading data from stream. The main point is to
+ * make pinned device open and let data flow through its dsp
+ * pipeline.
+ */
+static struct cras_audio_format format =
+{
+	SND_PCM_FORMAT_S16_LE,
+	48000,
+	2,
+};
+
+/*
+ * Information of a stream created by server. Currently only
+ * one server stream is allowed, for echo reference use.
+ */
+static struct cras_rstream_config *stream_config;
+
+/* Actually create the server stream and add to stream list. */
+static void server_stream_add_cb(void *data)
+{
+	struct stream_list *stream_list = (struct stream_list *)data;
+	struct cras_rstream *stream;
+
+	if (!stream_config)
+		return;
+
+	stream_list_add(stream_list, stream_config, &stream);
+}
+
+void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx)
+{
+	if (stream_config) {
+		syslog(LOG_ERR, "server stream already exists, dev %u",
+		       stream_config->dev_idx);
+		return;
+	}
+
+	stream_config = (struct cras_rstream_config *)
+			calloc(1, sizeof(*stream_config));
+	stream_config->format = &format;
+	stream_config->stream_id =
+			cras_get_stream_id(SERVER_STREAM_CLIENT_ID, 0);
+	stream_config->stream_type = CRAS_STREAM_TYPE_DEFAULT;
+	stream_config->direction = CRAS_STREAM_INPUT;
+	stream_config->flags = SERVER_ONLY;
+	stream_config->buffer_frames = server_stream_block_size;
+	stream_config->cb_threshold = server_stream_block_size;
+	stream_config->dev_idx = dev_idx;
+	stream_config->audio_fd = -1;
+
+	/* Schedule add stream in next main thread loop. */
+	cras_system_add_task(server_stream_add_cb, stream_list);
+}
+
+static void server_stream_rm_cb(void *data)
+{
+	struct stream_list *stream_list = (struct stream_list *)data;
+
+	if (stream_config == NULL)
+		return;
+
+	if (stream_list_rm(stream_list, stream_config->stream_id))
+		syslog(LOG_ERR, "Server stream %x no longer exist",
+		       stream_config->stream_id);
+
+	free(stream_config);
+	stream_config = NULL;
+}
+
+void server_stream_destroy(struct stream_list *stream_list,
+			   unsigned int dev_idx)
+{
+	if (!stream_config || stream_config->dev_idx != dev_idx) {
+		syslog(LOG_ERR, "No server stream to destroy");
+		return;
+	}
+	/* Schedule remove stream in next main thread loop. */
+	cras_system_add_task(server_stream_rm_cb, stream_list);
+}
diff --git a/cras/src/server/server_stream.h b/cras/src/server/server_stream.h
new file mode 100644
index 0000000..595987c
--- /dev/null
+++ b/cras/src/server/server_stream.h
@@ -0,0 +1,29 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SERVER_STREAM_H_
+#define SERVER_STREAM_H_
+
+struct stream_list;
+
+/*
+ * Asynchronously creates a server stream pinned to device of given idx.
+ * Args:
+ *    stream_list - List of stream to add new server stream to.
+ *    dev_idx - The id of the device that new server stream will pin to.
+ */
+void server_stream_create(struct stream_list *stream_list,
+			  unsigned int dev_idx);
+
+/*
+ * Asynchronously destroys existing server stream pinned to device of given idx.
+ * Args:
+ *    stream_list - List of stream to look up server stream.
+ *    dev_idx - The device id that target server stream is pinned to.
+ **/
+void server_stream_destroy(struct stream_list *stream_list,
+			   unsigned int dev_idx);
+
+#endif /* SERVER_STREAM_H_ */
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index 4955726..37730d3 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -67,13 +67,13 @@
 {
 	struct test_iodev *testio = (struct test_iodev *)iodev;
 
-	byte_buffer_destroy(testio->audbuff);
+	byte_buffer_destroy(&testio->audbuff);
 	testio->audbuff = NULL;
 	cras_iodev_free_audio_area(iodev);
 	return 0;
 }
 
-static int open_dev(struct cras_iodev *iodev)
+static int configure_dev(struct cras_iodev *iodev)
 {
 	struct test_iodev *testio = (struct test_iodev *)iodev;
 
@@ -193,7 +193,7 @@
 	iodev->supported_formats = test_supported_formats;
 	iodev->buffer_size = TEST_BUFFER_SIZE;
 
-	iodev->open_dev = open_dev;
+	iodev->configure_dev = configure_dev;
 	iodev->close_dev = close_dev;
 	iodev->frames_queued = frames_queued;
 	iodev->delay_frames = delay_frames;
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 6e8e3c7..02d5068 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -99,14 +99,25 @@
 namespace {
 
 static struct timespec time_now;
+class A2dpIodev: public testing::Test {
+  protected:
+    virtual void SetUp() {
+      ResetStubData();
+      atlog = (audio_thread_event_log *)calloc(
+          1,
+          sizeof(audio_thread_event_log));
+    }
 
-TEST(A2dpIoInit, InitializeA2dpIodev) {
+    virtual void TearDown() {
+      free(dummy_audio_area);
+      dummy_audio_area = NULL;
+      free(atlog);
+    }
+};
+
+TEST_F(A2dpIodev, InitializeA2dpIodev) {
   struct cras_iodev *iodev;
 
-  atlog = (audio_thread_event_log *)calloc(1, sizeof(audio_thread_event_log));
-
-  ResetStubData();
-
   cras_bt_device_name_ret = NULL;
   iodev = a2dp_iodev_create(fake_transport);
 
@@ -137,11 +148,9 @@
   a2dp_iodev_destroy(iodev);
 }
 
-TEST(A2dpIoInit, InitializeFail) {
+TEST_F(A2dpIodev, InitializeFail) {
   struct cras_iodev *iodev;
 
-  ResetStubData();
-
   init_a2dp_return_val = -1;
   iodev = a2dp_iodev_create(fake_transport);
 
@@ -154,14 +163,13 @@
   ASSERT_EQ(0, cras_iodev_rm_node_called);
 }
 
-TEST(A2dpIoInit, OpenIodev) {
+TEST_F(A2dpIodev, OpenIodev) {
   struct cras_iodev *iodev;
 
-  ResetStubData();
   iodev = a2dp_iodev_create(fake_transport);
 
   iodev_set_format(iodev, &format);
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
 
   ASSERT_EQ(1, cras_bt_transport_acquire_called);
 
@@ -173,17 +181,16 @@
   a2dp_iodev_destroy(iodev);
 }
 
-TEST(A2dpIoInit, GetPutBuffer) {
+TEST_F(A2dpIodev, GetPutBuffer) {
   struct cras_iodev *iodev;
   struct cras_audio_area *area1, *area2, *area3;
   uint8_t *area1_buf;
   unsigned frames;
 
-  ResetStubData();
   iodev = a2dp_iodev_create(fake_transport);
 
   iodev_set_format(iodev, &format);
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   ASSERT_NE(write_callback, (void *)NULL);
 
   frames = 256;
@@ -233,22 +240,22 @@
   ASSERT_EQ(256, frames);
   EXPECT_EQ(800, area3->channels[0].buf - area1_buf);
 
+  iodev->close_dev(iodev);
   a2dp_iodev_destroy(iodev);
 }
 
-TEST(A2dpIoInif, FramesQueued) {
+TEST_F(A2dpIodev, FramesQueued) {
   struct cras_iodev *iodev;
   struct cras_audio_area *area;
   struct timespec tstamp;
   unsigned frames;
 
-  ResetStubData();
   iodev = a2dp_iodev_create(fake_transport);
 
   iodev_set_format(iodev, &format);
   time_now.tv_sec = 0;
   time_now.tv_nsec = 0;
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   ASSERT_NE(write_callback, (void *)NULL);
 
   frames = 256;
@@ -303,21 +310,22 @@
   EXPECT_EQ(200, iodev->frames_queued(iodev, &tstamp));
   EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
   EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
+  iodev->close_dev(iodev);
+  a2dp_iodev_destroy(iodev);
 }
 
-TEST(A2dpIo, FlushAtLowBufferLevel) {
+TEST_F(A2dpIodev, FlushAtLowBufferLevel) {
   struct cras_iodev *iodev;
   struct cras_audio_area *area;
   struct timespec tstamp;
   unsigned frames;
 
-  ResetStubData();
   iodev = a2dp_iodev_create(fake_transport);
 
   iodev_set_format(iodev, &format);
   time_now.tv_sec = 0;
   time_now.tv_nsec = 0;
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   ASSERT_NE(write_callback, (void *)NULL);
 
   ASSERT_EQ(iodev->min_buffer_level, 400);
@@ -351,6 +359,8 @@
   EXPECT_EQ(500, iodev->frames_queued(iodev, &tstamp));
   EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
   EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
+  iodev->close_dev(iodev);
+  a2dp_iodev_destroy(iodev);
 }
 
 } // namespace
diff --git a/cras/src/tests/alert_unittest.cc b/cras/src/tests/alert_unittest.cc
index f013c1b..5f547f5 100644
--- a/cras/src/tests/alert_unittest.cc
+++ b/cras/src/tests/alert_unittest.cc
@@ -15,8 +15,12 @@
 void callback2(void *arg, void *data);
 void prepare(struct cras_alert *alert);
 
+struct cb_data_struct {
+  int data;
+};
+
 static int cb1_called = 0;
-static void *cb1_data;
+static cb_data_struct cb1_data;
 static int cb2_called = 0;
 static int cb2_set_pending = 0;
 static int prepare_called = 0;
@@ -26,9 +30,20 @@
   cb2_called = 0;
   cb2_set_pending = 0;
   prepare_called = 0;
+  cb1_data.data = 0;
 }
 
-TEST(Alert, OneCallback) {
+class Alert: public testing::Test {
+  protected:
+    virtual void SetUp() {
+      cb1_data.data = 0;
+    }
+
+    virtual void TearDown() {
+    }
+};
+
+TEST_F(Alert, OneCallback) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
@@ -39,7 +54,7 @@
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, OneCallbackPost2Call1) {
+TEST_F(Alert, OneCallbackPost2Call1) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
@@ -52,53 +67,55 @@
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, OneCallbackWithData) {
+TEST_F(Alert, OneCallbackWithData) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
-  const char *data = "ThisIsMyData";
+  struct cb_data_struct data = {1};
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
-  cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
+  cras_alert_pending_data(alert, (void *)&data, sizeof(struct cb_data_struct));
   EXPECT_EQ(0, cb1_called);
   cras_alert_process_all_pending_alerts();
   EXPECT_EQ(1, cb1_called);
-  EXPECT_EQ(0, strcmp(data, (const char *)cb1_data));
+  EXPECT_EQ(1, cb1_data.data);
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, OneCallbackTwoDataCalledOnce) {
+TEST_F(Alert, OneCallbackTwoDataCalledOnce) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
-  const char *data = "ThisIsMyData";
-  const char *data2 = "ThisIsMyData2";
+  struct cb_data_struct data = {1};
+  struct cb_data_struct data2 = {2};
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
   // Callback called with last data only.
-  cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
-  cras_alert_pending_data(alert, (void *)data2, strlen(data2) + 1);
+  cras_alert_pending_data(
+      alert, (void *)&data, sizeof(struct cb_data_struct));
+  cras_alert_pending_data(
+      alert, (void *)&data2, sizeof(struct cb_data_struct));
   EXPECT_EQ(0, cb1_called);
   cras_alert_process_all_pending_alerts();
   EXPECT_EQ(1, cb1_called);
-  EXPECT_EQ(0, strcmp(data2, (const char *)cb1_data));
+  EXPECT_EQ(2, cb1_data.data);
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, OneCallbackTwoDataKeepAll) {
+TEST_F(Alert, OneCallbackTwoDataKeepAll) {
   struct cras_alert *alert = cras_alert_create(
                                  NULL, CRAS_ALERT_FLAG_KEEP_ALL_DATA);
-  const char *data = "ThisIsMyData";
-  const char *data2 = "ThisIsMyData2";
+  struct cb_data_struct data = {1};
+  struct cb_data_struct data2 = {2};
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
   // Callbacks with data should each be called.
-  cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
-  cras_alert_pending_data(alert, (void *)data2, strlen(data2) + 1);
+  cras_alert_pending_data(alert, (void *)&data, sizeof(cb_data_struct));
+  cras_alert_pending_data(alert, (void *)&data2, sizeof(cb_data_struct));
   EXPECT_EQ(0, cb1_called);
   cras_alert_process_all_pending_alerts();
   EXPECT_EQ(2, cb1_called);
-  EXPECT_EQ(0, strcmp(data2, (const char *)cb1_data));
+  EXPECT_EQ(2, cb1_data.data);
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, TwoCallbacks) {
+TEST_F(Alert, TwoCallbacks) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
   cras_alert_add_callback(alert, &callback1, NULL);
   cras_alert_add_callback(alert, &callback2, NULL);
@@ -112,7 +129,7 @@
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, NoPending) {
+TEST_F(Alert, NoPending) {
   struct cras_alert *alert = cras_alert_create(NULL, 0);
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
@@ -122,7 +139,7 @@
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, PendingInCallback) {
+TEST_F(Alert, PendingInCallback) {
   struct cras_alert *alert1 = cras_alert_create(NULL, 0);
   struct cras_alert *alert2 = cras_alert_create(NULL, 0);
   cras_alert_add_callback(alert1, &callback1, NULL);
@@ -139,7 +156,7 @@
   cras_alert_destroy(alert2);
 }
 
-TEST(Alert, Prepare) {
+TEST_F(Alert, Prepare) {
   struct cras_alert *alert = cras_alert_create(prepare, 0);
   cras_alert_add_callback(alert, &callback1, NULL);
   ResetStub();
@@ -151,7 +168,7 @@
   cras_alert_destroy(alert);
 }
 
-TEST(Alert, TwoAlerts) {
+TEST_F(Alert, TwoAlerts) {
   struct cras_alert *alert1 = cras_alert_create(prepare, 0);
   struct cras_alert *alert2 = cras_alert_create(prepare, 0);
   cras_alert_add_callback(alert1, &callback1, NULL);
@@ -191,7 +208,8 @@
 void callback1(void *arg, void *data)
 {
   cb1_called++;
-  cb1_data = data;
+  if(data)
+    cb1_data.data = ((struct cb_data_struct *)data)->data;
 }
 
 void callback2(void *arg, void *data)
diff --git a/cras/src/tests/alsa_card_unittest.cc b/cras/src/tests/alsa_card_unittest.cc
index 741c6b4..29c315b 100644
--- a/cras/src/tests/alsa_card_unittest.cc
+++ b/cras/src/tests/alsa_card_unittest.cc
@@ -14,6 +14,7 @@
 #include "cras_alsa_io.h"
 #include "cras_alsa_mixer.h"
 #include "cras_alsa_ucm.h"
+#include "cras_iodev.h"
 #include "cras_types.h"
 #include "cras_util.h"
 #include "utlist.h"
@@ -26,11 +27,12 @@
 static size_t cras_alsa_mixer_destroy_called;
 static size_t cras_alsa_iodev_create_called;
 static struct cras_iodev **cras_alsa_iodev_create_return;
+static struct cras_iodev fake_dev1, fake_dev2, fake_dev3, fake_dev4;
 static struct cras_iodev *cras_alsa_iodev_create_default_return[] = {
-  reinterpret_cast<struct cras_iodev *>(2),
-  reinterpret_cast<struct cras_iodev *>(3),
-  reinterpret_cast<struct cras_iodev *>(4),
-  reinterpret_cast<struct cras_iodev *>(5),
+  &fake_dev1,
+  &fake_dev2,
+  &fake_dev3,
+  &fake_dev4,
 };
 static size_t cras_alsa_iodev_create_return_size;
 static size_t cras_alsa_iodev_legacy_complete_init_called;
@@ -89,6 +91,8 @@
 static struct ucm_section *ucm_get_sections_return_value;
 static size_t cras_alsa_mixer_add_controls_in_section_called;
 static int cras_alsa_mixer_add_controls_in_section_return_value;
+static int ucm_get_echo_reference_dev_name_for_dev_called;
+static const char *ucm_get_echo_reference_dev_name_for_dev_return_value[4];
 
 static void ResetStubData() {
   cras_alsa_mixer_create_called = 0;
@@ -154,6 +158,7 @@
   ucm_get_sections_return_value = NULL;
   cras_alsa_mixer_add_controls_in_section_called = 0;
   cras_alsa_mixer_add_controls_in_section_return_value = 0;
+  ucm_get_echo_reference_dev_name_for_dev_called = 0;
 }
 
 TEST(AlsaCard, CreateFailInvalidCard) {
@@ -223,6 +228,7 @@
   EXPECT_EQ(0, snd_hctl_nonblock_called);
   EXPECT_EQ(0, snd_hctl_load_called);
   EXPECT_EQ(1, cras_alsa_mixer_create_called);
+  cras_alsa_card_destroy(c);
 }
 
 TEST(AlsaCard, CreateFailHctlLoad) {
@@ -632,6 +638,11 @@
   mixer_name_2 = (struct mixer_name*)malloc(sizeof(*mixer_name_2));
   mixer_name_1->name = name1;
   mixer_name_2->name = name2;
+  mixer_name_1->dir = CRAS_STREAM_OUTPUT;
+  mixer_name_2->dir = CRAS_STREAM_OUTPUT;
+  mixer_name_1->type = MIXER_NAME_VOLUME;
+  mixer_name_2->type = MIXER_NAME_VOLUME;
+
   DL_APPEND(ucm_get_coupled_mixer_names_return_value, mixer_name_1);
   DL_APPEND(ucm_get_coupled_mixer_names_return_value, mixer_name_2);
 
@@ -715,10 +726,6 @@
   ucm_section_add_coupled(section, "SPK-R", MIXER_NAME_VOLUME);
   DL_APPEND(sections, section);
 
-  section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT,
-                               "my-sound-card Headset Jack", "gpio");
-  ucm_section_set_mixer_name(section, "CAPTURE");
-
   section = ucm_section_create("Internal Mic", 0, CRAS_STREAM_INPUT,
                                NULL, NULL);
   ucm_section_add_coupled(section, "INT-MIC-L", MIXER_NAME_VOLUME);
@@ -810,6 +817,39 @@
   EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
 }
 
+TEST(AlsaCard, GG) {
+  struct cras_alsa_card *c;
+  cras_alsa_card_info card_info;
+  int info_rets[] = {0, 0, 0, 0, 0, -1};
+  struct cras_ionode nodes[4];
+  const char *echo_ref = "echo ref";
+
+  ResetStubData();
+  card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+  card_info.card_index = 0;
+  snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
+  snd_ctl_pcm_info_rets = info_rets;
+  ucm_has_fully_specified_ucm_flag_return_value = 1;
+  ucm_get_sections_return_value = GenerateUcmSections();
+
+  fake_dev1.nodes = nodes;
+  fake_dev2.nodes = nodes + 1;
+  fake_dev3.nodes = nodes + 2;
+  fake_dev4.nodes = nodes + 3;
+  snprintf(nodes[0].name, CRAS_NODE_NAME_BUFFER_SIZE, "dev1");
+  snprintf(nodes[1].name, CRAS_NODE_NAME_BUFFER_SIZE, "dev2");
+  snprintf(nodes[2].name, CRAS_NODE_NAME_BUFFER_SIZE, "dev3");
+  snprintf(nodes[3].name, CRAS_NODE_NAME_BUFFER_SIZE, "echo ref");
+
+  ucm_get_echo_reference_dev_name_for_dev_return_value[0] = strdup(echo_ref);
+
+  c = cras_alsa_card_create(&card_info, device_config_dir,
+                            fake_blacklist, NULL);
+
+  EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+  EXPECT_EQ(fake_dev1.echo_reference_dev, &fake_dev4);
+  cras_alsa_card_destroy(c);
+}
 
 /* Stubs */
 
@@ -929,6 +969,12 @@
 }
 void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val) {
 }
+const char *snd_pcm_info_get_name(const snd_pcm_info_t *obj) {
+  return "Fake device name";
+}
+const char *snd_pcm_info_get_id(const snd_pcm_info_t *obj) {
+  return "Fake device id";
+}
 int snd_ctl_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t *info) {
   int ret;
   snd_ctl_pcm_info_called++;
@@ -1063,6 +1109,12 @@
   ucm_get_sections_called++;
   return ucm_get_sections_return_value;
 }
+const char *ucm_get_echo_reference_dev_name_for_dev(
+    struct cras_use_case_mgr *mgr, const char *dev)
+{
+  int idx = ucm_get_echo_reference_dev_name_for_dev_called++;
+  return ucm_get_echo_reference_dev_name_for_dev_return_value[idx];
+}
 
 int cras_alsa_mixer_add_controls_in_section(
 		struct cras_alsa_mixer *cmix,
diff --git a/cras/src/tests/alsa_helpers_unittest.cc b/cras/src/tests/alsa_helpers_unittest.cc
index f774a50..a613929 100644
--- a/cras/src/tests/alsa_helpers_unittest.cc
+++ b/cras/src/tests/alsa_helpers_unittest.cc
@@ -168,7 +168,6 @@
   snd_pcm_uframes_t used;
   snd_pcm_uframes_t severe_underrun_frames = 480;
   struct timespec tstamp;
-  unsigned int underruns = 0;
   int htimestamp_enabled = 1;
   const char *dev_name = "dev_name";
 
@@ -201,7 +200,7 @@
   snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 10000;
 
   cras_alsa_get_avail_frames(dummy_handle, 48000, severe_underrun_frames,
-                             dev_name, &used, &tstamp, &underruns);
+                             dev_name, &used, &tstamp);
   EXPECT_EQ(used, snd_pcm_htimestamp_avail_ret_val);
   EXPECT_EQ(tstamp.tv_sec, snd_pcm_htimestamp_tstamp_ret_val.tv_sec);
   EXPECT_EQ(tstamp.tv_nsec, snd_pcm_htimestamp_tstamp_ret_val.tv_nsec);
@@ -213,7 +212,6 @@
   snd_pcm_uframes_t severe_underrun_frames = 480;
   snd_pcm_uframes_t buffer_size = 48000;
   struct timespec tstamp;
-  unsigned int underruns = 0;
   int rc;
   const char *dev_name = "dev_name";
 
@@ -221,41 +219,28 @@
   snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames + 1;
   rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
                                   severe_underrun_frames, dev_name,
-                                  &avail, &tstamp, &underruns);
+                                  &avail, &tstamp);
   // Returns -EPIPE when severe underrun happens.
   EXPECT_EQ(rc, -EPIPE);
-  EXPECT_EQ(1, underruns);
 
   ResetStubData();
   snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames;
   rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
                                   severe_underrun_frames, dev_name,
-                                  &avail, &tstamp, &underruns);
+                                  &avail, &tstamp);
   // Underrun which is not severe enough will be masked.
   // avail will be adjusted to buffer_size.
   EXPECT_EQ(avail, buffer_size);
   EXPECT_EQ(rc, 0);
-  EXPECT_EQ(2, underruns);
-
-  ResetStubData();
-  snd_pcm_htimestamp_avail_ret_val = buffer_size;
-  rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
-                                  severe_underrun_frames, dev_name,
-                                  &avail, &tstamp, &underruns);
-  // When avail == buffer_size, num_underruns will be increased.
-  EXPECT_EQ(avail, buffer_size);
-  EXPECT_EQ(rc, 0);
-  EXPECT_EQ(3, underruns);
 
   ResetStubData();
   snd_pcm_htimestamp_avail_ret_val = buffer_size - 1;
   rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
                                   severe_underrun_frames, dev_name,
-                                  &avail, &tstamp, &underruns);
+                                  &avail, &tstamp);
   // When avail < buffer_size, there is no underrun.
   EXPECT_EQ(avail, buffer_size - 1);
   EXPECT_EQ(rc, 0);
-  EXPECT_EQ(3, underruns);
 }
 } // namespace
 
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index dc58cd7..9a6b7f6 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -116,12 +116,15 @@
 static int ucm_enable_swap_mode_ret_value;
 static size_t ucm_enable_swap_mode_called;
 static int is_utf8_string_ret_value;
-static char *cras_alsa_jack_update_monitor_fake_name = 0;
+static const char *cras_alsa_jack_update_monitor_fake_name = 0;
 static int cras_alsa_jack_get_name_called;
 static const char *cras_alsa_jack_get_name_ret_value = 0;
 static char default_jack_name[] = "Something Jack";
 static int auto_unplug_input_node_ret = 0;
 static int auto_unplug_output_node_ret = 0;
+static int ucm_get_min_software_gain_called;
+static int ucm_get_min_software_gain_ret_value;
+static long ucm_get_min_software_gain_value;
 static int ucm_get_max_software_gain_called;
 static int ucm_get_max_software_gain_ret_value;
 static long ucm_get_max_software_gain_value;
@@ -147,6 +150,9 @@
 static const struct cras_volume_curve *fake_get_dBFS_volume_curve_val;
 static int cras_iodev_dsp_set_swap_mode_for_node_called;
 static std::map<std::string, long> ucm_get_default_node_gain_values;
+static thread_callback audio_thread_cb;
+static void *audio_thread_cb_data;
+static int hotword_send_triggered_msg_called;
 
 void ResetStubData() {
   cras_alsa_open_called = 0;
@@ -209,6 +215,9 @@
   cras_alsa_jack_get_name_called = 0;
   cras_alsa_jack_get_name_ret_value = default_jack_name;
   cras_alsa_jack_update_monitor_fake_name = 0;
+  ucm_get_min_software_gain_called = 0;
+  ucm_get_min_software_gain_ret_value = -1;
+  ucm_get_min_software_gain_value = 0;
   ucm_get_max_software_gain_called = 0;
   ucm_get_max_software_gain_ret_value = -1;
   ucm_get_max_software_gain_value = 0;
@@ -393,6 +402,7 @@
   EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
   aio = (struct alsa_io *)iodev;
   format.frame_rate = 48000;
+  format.num_channels = 1;
   cras_iodev_set_format(iodev, &format);
 
   // Test that these flags are cleared after open_dev.
@@ -400,6 +410,8 @@
   aio->filled_zeros_for_draining = 512;
   iodev->open_dev(iodev);
   EXPECT_EQ(1, cras_alsa_open_called);
+  iodev->configure_dev(iodev);
+  EXPECT_EQ(1, cras_alsa_open_called);
   EXPECT_EQ(1, sys_set_volume_limits_called);
   EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
   EXPECT_EQ(0, cras_alsa_start_called);
@@ -482,8 +494,10 @@
   struct cras_iodev *iodev;
   struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
 
-  /* Meet the requirements of using software gain. */
+  /* MaxSoftwareGain is specified in UCM */
   ResetStubData();
+  ucm_get_min_software_gain_ret_value = 1;
+  ucm_get_min_software_gain_value = 1;
   ucm_get_max_software_gain_ret_value = 0;
   ucm_get_max_software_gain_value = 2000;
   iodev = alsa_iodev_create_with_default_parameters(0, NULL,
@@ -493,14 +507,62 @@
                                                     CRAS_STREAM_INPUT);
   ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
   EXPECT_EQ(1, iodev->active_node->software_volume_needed);
+  EXPECT_EQ(DEFAULT_MIN_CAPTURE_GAIN, iodev->active_node->min_software_gain);
   EXPECT_EQ(2000, iodev->active_node->max_software_gain);
   ASSERT_EQ(1, sys_set_capture_gain_limits_called);
-  /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum softare gain]. */
+  /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum software gain]. */
   ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0],
       DEFAULT_MIN_CAPTURE_GAIN);
   ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000);
 
-  /* MaxSoftwareGain is not specified in UCM */
+  alsa_iodev_destroy(iodev);
+
+  /* MaxSoftwareGain and MinSoftwareGain are specified in UCM. */
+  ResetStubData();
+  ucm_get_min_software_gain_ret_value = 0;
+  ucm_get_min_software_gain_value = 1000;
+  ucm_get_max_software_gain_ret_value = 0;
+  ucm_get_max_software_gain_value = 2000;
+  iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+                                                    ALSA_CARD_TYPE_INTERNAL, 1,
+                                                    fake_mixer, fake_config,
+                                                    fake_ucm,
+                                                    CRAS_STREAM_INPUT);
+  ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+  EXPECT_EQ(1, iodev->active_node->software_volume_needed);
+  EXPECT_EQ(1000, iodev->active_node->min_software_gain);
+  EXPECT_EQ(2000, iodev->active_node->max_software_gain);
+  ASSERT_EQ(1, sys_set_capture_gain_limits_called);
+  /* The gain range is [minimum software gain, maximum software gain]. */
+  ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], 1000);
+  ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000);
+
+  alsa_iodev_destroy(iodev);
+
+  /* MinSoftwareGain is larger than MaxSoftwareGain in UCM. */
+  ResetStubData();
+  ucm_get_min_software_gain_ret_value = 0;
+  ucm_get_min_software_gain_value = 3000;
+  ucm_get_max_software_gain_ret_value = 0;
+  ucm_get_max_software_gain_value = 2000;
+  iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+                                                    ALSA_CARD_TYPE_INTERNAL, 1,
+                                                    fake_mixer, fake_config,
+                                                    fake_ucm,
+                                                    CRAS_STREAM_INPUT);
+  ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+  EXPECT_EQ(1, iodev->active_node->software_volume_needed);
+  EXPECT_EQ(DEFAULT_MIN_CAPTURE_GAIN, iodev->active_node->min_software_gain);
+  EXPECT_EQ(2000, iodev->active_node->max_software_gain);
+  ASSERT_EQ(1, sys_set_capture_gain_limits_called);
+  /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum software gain]. */
+  ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0],
+      DEFAULT_MIN_CAPTURE_GAIN);
+  ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000);
+
+  alsa_iodev_destroy(iodev);
+
+  /* MaxSoftwareGain is not specified in UCM. */
   ResetStubData();
   ucm_get_max_software_gain_ret_value = 1;
   ucm_get_max_software_gain_value = 1;
@@ -534,7 +596,7 @@
   ucm_get_max_software_gain_ret_value = 0;
   ucm_get_max_software_gain_value = 2000;
 
-  // Set default node gain to -1000 dBm.
+  // Set default node gain to -1000 * 0.01 dB.
   ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain;
 
   // Assume this is the first device so it gets internal mic node name.
@@ -545,7 +607,7 @@
                                                     CRAS_STREAM_INPUT);
   ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
 
-  // Gain on node is 300 dBm.
+  // Gain on node is 300 * 0.01 dB.
   iodev->active_node->capture_gain = default_node_gain;
 
   // cras_iodev will call cras_iodev_adjust_active_node_gain to get gain for
@@ -647,11 +709,14 @@
 
   aio = (struct alsa_io *)iodev;
   format.frame_rate = 48000;
+  format.num_channels = 1;
   cras_iodev_set_format(iodev, &format);
 
   ResetStubData();
   iodev->open_dev(iodev);
   EXPECT_EQ(1, cras_alsa_open_called);
+  iodev->configure_dev(iodev);
+  EXPECT_EQ(1, cras_alsa_open_called);
   EXPECT_EQ(1, cras_alsa_mixer_get_minimum_capture_gain_called);
   EXPECT_EQ(1, cras_alsa_mixer_get_maximum_capture_gain_called);
   EXPECT_EQ(1, sys_set_capture_gain_limits_called);
@@ -675,7 +740,7 @@
   long default_node_gain = -1000;
 
   ResetStubData();
-  // Set default node gain to -1000 dBm.
+  // Set default node gain to -1000 * 0.01 dB.
   ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain;
 
   // Assume this is the first device so it gets internal mic node name.
@@ -690,13 +755,14 @@
 
   // Check the default node gain is the same as what specified in UCM.
   EXPECT_EQ(default_node_gain, iodev->active_node->capture_gain);
-  // System gain is set to 2000 dBm.
+  // System gain is set to 2000 * 0.01 dB.
   sys_get_capture_gain_return_value = system_gain;
 
   iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   iodev->close_dev(iodev);
 
-  // Hardware gain is set to 2000 - 1000 dBm.
+  // Hardware gain is set to (2000 - 1000) * 0.01 dB.
   EXPECT_EQ(system_gain + default_node_gain, alsa_mixer_set_capture_dBFS_value);
 
   alsa_iodev_destroy(iodev);
@@ -720,12 +786,15 @@
                                                     CRAS_STREAM_INPUT);
   ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
 
+  format.frame_rate = 48000;
+  format.num_channels = 1;
   cras_iodev_set_format(iodev, &format);
 
-  /* System gain is set to 1000dBm */
+  /* System gain is set to 1000 * 0.01 dB */
   sys_get_capture_gain_return_value = 1000;
 
   iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   iodev->close_dev(iodev);
 
   /* Hardware gain is set to 0dB when software gain is used. */
@@ -734,9 +803,10 @@
   /* Test the case where software gain is not needed. */
   iodev->active_node->software_volume_needed = 0;
   iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
   iodev->close_dev(iodev);
 
-  /* Hardware gain is set to 1000dBm as got from system capture gain.*/
+  /* Hardware gain is set to 1000 * 0.01 dB as got from system capture gain.*/
   EXPECT_EQ(1000, alsa_mixer_set_capture_dBFS_value);
 
   alsa_iodev_destroy(iodev);
@@ -892,7 +962,8 @@
   struct alsa_io *aio;
   struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
   struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
-  struct cras_ionode * const fake_node = (cras_ionode *)4;
+  struct cras_ionode * const fake_node = (cras_ionode *)calloc(
+      1, sizeof(struct cras_ionode));
   ResetStubData();
   // Stub replies that swap mode does not exist.
   ucm_swap_mode_exists_ret_value = 0;
@@ -908,6 +979,7 @@
 
   // Stub replies that swap mode exists.
   ucm_swap_mode_exists_ret_value = 1;
+  alsa_iodev_destroy((struct cras_iodev *)aio);
 
   aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
       0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
@@ -921,6 +993,7 @@
   EXPECT_EQ(1, ucm_enable_swap_mode_called);
 
   alsa_iodev_destroy((struct cras_iodev *)aio);
+  free(fake_node);
 }
 
 // Test that system settins aren't touched if no streams active.
@@ -1641,6 +1714,19 @@
   ASSERT_EQ(0, node.plugged);
   ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
   ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
+  // Node name is changed to "Mic".
+  ASSERT_EQ(0, strcmp(node.name, "Mic"));
+
+  memset(&node, 0, sizeof(node));
+  node.dev = &dev;
+  dev.direction = CRAS_STREAM_OUTPUT;
+  strcpy(node.name, "DAISY-I2S Headphone Jack");
+  set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
+  ASSERT_EQ(0, node.plugged);
+  ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type);
+  ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
+  // Node name is changed to "Headphone".
+  ASSERT_EQ(0, strcmp(node.name, "Headphone"));
 
   memset(&node, 0, sizeof(node));
   node.dev = &dev;
@@ -1720,8 +1806,7 @@
   // HDMI jack, and thus the callback creates an HDMI node.
   cras_alsa_jack_get_name_ret_value = "HDMI Jack";
   // Set the jack name updated from monitor to be an invalid UTF8 string.
-  cras_alsa_jack_update_monitor_fake_name = strdup("Something");
-  cras_alsa_jack_update_monitor_fake_name[0] = 0xfe;
+  cras_alsa_jack_update_monitor_fake_name = "\xfeomething";
   is_utf8_string_ret_value = 0;
 
   // Add the jack node.
@@ -1785,12 +1870,13 @@
   aio_output_->base.format = fmt;
   aio_output_->handle = (snd_pcm_t *)0x24;
 
-  rc = aio_output_->base.open_dev(&aio_output_->base);
+  rc = aio_output_->base.configure_dev(&aio_output_->base);
   ASSERT_EQ(0, rc);
   EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val);
 
   aio_output_->base.set_volume(&aio_output_->base);
   EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val);
+  free(fmt);
 }
 
 TEST_F(AlsaVolumeMuteSuite, GetVolumeCurveFromNode)
@@ -1820,12 +1906,13 @@
   node = aio_output_->base.nodes->next;
   aio_output_->base.active_node = node;
 
-  rc = aio_output_->base.open_dev(&aio_output_->base);
+  rc = aio_output_->base.configure_dev(&aio_output_->base);
   ASSERT_EQ(0, rc);
   EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val);
 
   aio_output_->base.set_volume(&aio_output_->base);
   EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val);
+  free(fmt);
 }
 
 TEST_F(AlsaVolumeMuteSuite, SetVolume) {
@@ -1841,7 +1928,7 @@
 
   aio_output_->num_underruns = 3; //  Something non-zero.
   sys_get_volume_return_value = fake_system_volume;
-  rc = aio_output_->base.open_dev(&aio_output_->base);
+  rc = aio_output_->base.configure_dev(&aio_output_->base);
   ASSERT_EQ(0, rc);
   EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
   EXPECT_EQ(fake_system_volume_dB, alsa_mixer_set_dBFS_value);
@@ -1919,12 +2006,22 @@
       fmt_.format = SND_PCM_FORMAT_S16_LE;
       fmt_.frame_rate = 48000;
       fmt_.num_channels = 2;
+      aio.base.frames_queued = frames_queued;
+      aio.base.direction = CRAS_STREAM_OUTPUT;
       aio.base.format = &fmt_;
       aio.base.buffer_size = BUFFER_SIZE;
       aio.base.min_cb_level = 240;
+      aio.base.min_buffer_level = 0;
+      aio.filled_zeros_for_draining = 0;
+      cras_alsa_mmap_begin_buffer = (uint8_t *)calloc(
+        BUFFER_SIZE * 2 * 2,
+        sizeof(*cras_alsa_mmap_begin_buffer));
+      memset(cras_alsa_mmap_begin_buffer, 0xff,
+             sizeof(*cras_alsa_mmap_begin_buffer));
     }
 
     virtual void TearDown() {
+      free(cras_alsa_mmap_begin_buffer);
     }
 
   struct alsa_io aio;
@@ -1935,11 +2032,6 @@
   int rc;
   int16_t *zeros;
 
-  cras_alsa_mmap_begin_buffer = (uint8_t *)calloc(
-      BUFFER_SIZE * 2 * 2,
-      sizeof(*cras_alsa_mmap_begin_buffer));
-  memset(cras_alsa_mmap_begin_buffer, 0xff,
-         sizeof(*cras_alsa_mmap_begin_buffer));
 
   rc = fill_whole_buffer_with_zeros(&aio.base);
 
@@ -1948,7 +2040,6 @@
   EXPECT_EQ(0, memcmp(zeros, cras_alsa_mmap_begin_buffer, BUFFER_SIZE * 2 * 2));
 
   free(zeros);
-  free(cras_alsa_mmap_begin_buffer);
 }
 
 TEST_F(AlsaFreeRunTestSuite, EnterFreeRunAlreadyFreeRunning) {
@@ -1966,14 +2057,17 @@
 }
 
 TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNeedToFillZeros) {
-  int rc;
-
+  int rc, real_hw_level;
+  struct timespec hw_tstamp;
   // Device is not in free run state. There are still valid samples to play.
   // The number of valid samples is less than min_cb_level * 2.
   // Need to fill zeros targeting min_cb_level * 2 = 480.
   // The number of zeros to be filled is 480 - 200 = 280.
-  cras_iodev_frames_queued_ret = 200;
-  cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+  real_hw_level = 200;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
+
+  rc = aio.base.frames_queued(&aio.base, &hw_tstamp);
+  EXPECT_EQ(200, rc);
 
   rc = no_stream(&aio.base, 1);
 
@@ -1986,13 +2080,13 @@
 }
 
 TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNoNeedToFillZeros) {
-  int rc;
+  int rc, real_hw_level;
 
   // Device is not in free run state. There are still valid samples to play.
   // The number of valid samples is more than min_cb_level * 2.
   // No need to fill zeros.
-  cras_iodev_frames_queued_ret = 500;
-  cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+  real_hw_level = 500;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
 
   rc = no_stream(&aio.base, 1);
 
@@ -2003,13 +2097,13 @@
 }
 
 TEST_F(AlsaFreeRunTestSuite, EnterFreeRunDrained) {
-  int rc;
+  int rc, real_hw_level;
 
   // Device is not in free run state. There are still valid samples to play.
   // The number of valid samples is less than filled zeros.
   // Should enter free run state and fill whole buffer with zeros.
-  cras_iodev_frames_queued_ret = 40;
-  cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+  real_hw_level = 40;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
   aio.filled_zeros_for_draining = 100;
 
   rc = no_stream(&aio.base, 1);
@@ -2021,12 +2115,12 @@
 }
 
 TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNoSamples) {
-  int rc;
+  int rc, real_hw_level;
 
   // Device is not in free run state. There is no sample to play.
   // Should enter free run state and fill whole buffer with zeros.
-  cras_iodev_frames_queued_ret = 0;
-  cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+  real_hw_level = 0;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
 
   rc = no_stream(&aio.base, 1);
 
@@ -2053,13 +2147,50 @@
   EXPECT_EQ(0, output_should_wake(&aio.base));
 }
 
-TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRun) {
-  int rc;
+TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunMoreRemain) {
+  int rc, real_hw_level;
+
+  // Compare min_buffer_level + min_cb_level with valid samples left.
+  // 240 + 512 < 900 - 100, so we will get 900 - 100 in appl_ptr_ahead.
+
+  aio.is_free_running = 0;
+  aio.filled_zeros_for_draining = 100;
+  aio.base.min_buffer_level = 512;
+  real_hw_level = 900;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
 
   rc = no_stream(&aio.base, 0);
 
   EXPECT_EQ(0, rc);
-  EXPECT_EQ(0, cras_alsa_resume_appl_ptr_called);
+  EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called);
+  EXPECT_EQ(800, cras_alsa_resume_appl_ptr_ahead);
+  EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames);
+  EXPECT_EQ(0, aio.is_free_running);
+  EXPECT_EQ(0, aio.filled_zeros_for_draining);
+}
+
+TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunLessRemain) {
+  int rc, real_hw_level;
+
+  // Compare min_buffer_level + min_cb_level with valid samples left.
+  // 240 + 256 > 400 - 500, so we will get 240 + 256 in appl_ptr_ahead.
+  // And it will fill 240 + 256 - 400 = 96 zeros frames into device.
+
+  aio.is_free_running = 0;
+  aio.filled_zeros_for_draining = 500;
+  aio.base.min_buffer_level = 256;
+  real_hw_level = 400;
+  cras_alsa_get_avail_frames_avail = BUFFER_SIZE - real_hw_level;
+
+  rc = no_stream(&aio.base, 0);
+
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called);
+  EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level,
+            cras_alsa_resume_appl_ptr_ahead);
+  EXPECT_EQ(96, cras_iodev_fill_odev_zeros_frames);
+  EXPECT_EQ(0, aio.is_free_running);
+  EXPECT_EQ(0, aio.filled_zeros_for_draining);
 }
 
 TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) {
@@ -2085,15 +2216,12 @@
   int rc;
   int16_t *zeros;
 
-  cras_alsa_mmap_begin_buffer = (uint8_t *)calloc(
-      BUFFER_SIZE * 2 * 2,
-      sizeof(*cras_alsa_mmap_begin_buffer));
-  memset(cras_alsa_mmap_begin_buffer, 0xff,
-         sizeof(*cras_alsa_mmap_begin_buffer));
+  aio.num_underruns = 0;
 
   // Ask alsa_io to handle output underrun.
   rc = alsa_output_underrun(&aio.base);
   EXPECT_EQ(0, rc);
+  EXPECT_EQ(1, aio.num_underruns);
 
   // mmap buffer should be filled with zeros.
   zeros = (int16_t *)calloc(BUFFER_SIZE * 2, sizeof(*zeros));
@@ -2106,9 +2234,40 @@
             cras_alsa_resume_appl_ptr_ahead);
 
   free(zeros);
-  free(cras_alsa_mmap_begin_buffer);
 }
 
+TEST(AlsaHotwordNode, HotwordTriggeredSendMessage) {
+  struct cras_iodev *iodev;
+  struct cras_audio_format format;
+  struct alsa_input_node alsa_node;
+  struct cras_ionode *node = &alsa_node.base;
+  int rc;
+
+  ResetStubData();
+  iodev = alsa_iodev_create_with_default_parameters(
+      0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
+      CRAS_STREAM_INPUT);
+  format.frame_rate = 16000;
+  format.num_channels = 1;
+  cras_iodev_set_format(iodev, &format);
+
+  memset(&alsa_node, 0, sizeof(alsa_node));
+  node->dev = iodev;
+  strcpy(node->name, "Wake on Voice");
+  set_node_initial_state(node, ALSA_CARD_TYPE_INTERNAL);
+  EXPECT_EQ(CRAS_NODE_TYPE_HOTWORD, node->type);
+
+  iodev->active_node = node;
+  iodev->open_dev(iodev);
+  rc = iodev->configure_dev(iodev);
+  free(fake_format);
+  ASSERT_EQ(0, rc);
+
+  ASSERT_NE(reinterpret_cast<thread_callback>(NULL), audio_thread_cb);
+  audio_thread_cb(audio_thread_cb_data);
+  EXPECT_EQ(1, hotword_send_triggered_msg_called);
+  alsa_iodev_destroy(iodev);
+}
 
 }  //  namespace
 
@@ -2152,6 +2311,16 @@
 	return 0;
 }
 
+int cras_iodev_list_suspend_hotword_streams()
+{
+  return 0;
+}
+
+int cras_iodev_list_resume_hotword_stream()
+{
+  return 0;
+}
+
 struct audio_thread *cras_iodev_list_get_audio_thread()
 {
   return NULL;
@@ -2188,8 +2357,7 @@
 {
   return 0;
 }
-int cras_alsa_fill_properties(const char *dev,
-			      snd_pcm_stream_t stream,
+int cras_alsa_fill_properties(snd_pcm_t *handle,
 			      size_t **rates,
 			      size_t **channel_counts,
 			      snd_pcm_format_t **formats)
@@ -2222,8 +2390,7 @@
                                snd_pcm_uframes_t severe_underrun_frames,
                                const char* dev_name,
                                snd_pcm_uframes_t *used,
-                               struct timespec *tstamp,
-                               unsigned int *num_underruns)
+                               struct timespec *tstamp)
 {
   *used = cras_alsa_get_avail_frames_avail;
   clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
@@ -2237,14 +2404,14 @@
 }
 int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes,
 			 uint8_t **dst, snd_pcm_uframes_t *offset,
-			 snd_pcm_uframes_t *frames, unsigned int *underruns)
+			 snd_pcm_uframes_t *frames)
 {
   *dst = cras_alsa_mmap_begin_buffer;
   *frames = cras_alsa_mmap_begin_frames;
   return 0;
 }
 int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
-			  snd_pcm_uframes_t frames, unsigned int *underruns)
+			  snd_pcm_uframes_t frames)
 {
   return 0;
 }
@@ -2530,11 +2697,11 @@
 }
 
 char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name) {
-  char *ret = (char *)malloc(8);
   if ((!strcmp(flag_name, "AutoUnplugInputNode") &&
        auto_unplug_input_node_ret) ||
       (!strcmp(flag_name, "AutoUnplugOutputNode") &&
        auto_unplug_output_node_ret)) {
+    char *ret = (char *)malloc(8);
     snprintf(ret, 8, "%s", "1");
     return ret;
   }
@@ -2558,8 +2725,10 @@
   return ucm_enable_swap_mode_ret_value;
 }
 
-unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr)
+int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr,
+			     unsigned int *level)
 {
+  *level = 0;
   return 0;
 }
 
@@ -2573,6 +2742,14 @@
   return 0;
 }
 
+int ucm_get_min_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+    long *gain)
+{
+  ucm_get_min_software_gain_called++;
+  *gain = ucm_get_min_software_gain_value;
+  return ucm_get_min_software_gain_ret_value;
+}
+
 int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
     long *gain)
 {
@@ -2611,6 +2788,11 @@
   return -EINVAL;
 }
 
+int ucm_get_preempt_hotword(struct cras_use_case_mgr *mgr, const char *dev)
+{
+  return 0;
+}
+
 struct cras_volume_curve *cras_volume_curve_create_default()
 {
   return &default_curve;
@@ -2637,9 +2819,11 @@
 int cras_iodev_set_format(struct cras_iodev *iodev,
 			  const struct cras_audio_format *fmt)
 {
-  fake_format = (struct cras_audio_format *)calloc(1, sizeof(*fake_format));
+  fake_format = (struct cras_audio_format *)calloc(
+        1,
+        sizeof(cras_audio_format));
   // Copy the content of format from fmt into format of iodev.
-  memcpy(fake_format, fmt, sizeof(*fake_format));
+  memcpy(fake_format, fmt, sizeof(cras_audio_format));
   iodev->format = fake_format;
   return 0;
 }
@@ -2761,6 +2945,8 @@
 
 void audio_thread_add_callback(int fd, thread_callback cb, void *data)
 {
+  audio_thread_cb = cb;
+  audio_thread_cb_data = data;
 }
 
 void audio_thread_rm_callback(int fd)
@@ -2771,18 +2957,38 @@
   return 0;
 }
 
+int cras_hotword_send_triggered_msg()
+{
+  hotword_send_triggered_msg_called++;
+  return 0;
+}
+
+int snd_pcm_poll_descriptors_count(snd_pcm_t *pcm)
+{
+  return 1;
+}
+
+int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds,
+                             unsigned int space)
+{
+  if (space >= 1) {
+    pfds[0].events = POLLIN;
+    pfds[0].fd = 99;
+  }
+  return 0;
+}
+
 int is_utf8_string(const char* string)
 {
   return is_utf8_string_ret_value;
 }
 
-int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
-				    unsigned int *underruns)
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst)
 {
   snd_pcm_uframes_t offset, frames;
 
   cras_alsa_mmap_get_whole_buffer_called++;
-  return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames, underruns);
+  return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames);
 }
 
 int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead)
diff --git a/cras/src/tests/alsa_jack_unittest.cc b/cras/src/tests/alsa_jack_unittest.cc
index e188f87..a739c70 100644
--- a/cras/src/tests/alsa_jack_unittest.cc
+++ b/cras/src/tests/alsa_jack_unittest.cc
@@ -239,10 +239,12 @@
   EXPECT_GE(snd_hctl_elem_next_called, nelems + nhdmi_jacks);
   EXPECT_GE(snd_hctl_elem_get_name_called, nelems + njacks);
 
-  if (direction == CRAS_STREAM_OUTPUT)
+  if (direction == CRAS_STREAM_OUTPUT) {
     EXPECT_EQ(njacks, cras_alsa_mixer_get_output_matching_name_called);
-  if (direction == CRAS_STREAM_INPUT && ucm_get_dev_for_jack_return)
+  }
+  if (direction == CRAS_STREAM_INPUT && ucm_get_dev_for_jack_return) {
     EXPECT_EQ(njacks, ucm_get_cap_control_called);
+  }
 
   return jack_list;
 }
@@ -357,7 +359,6 @@
   snd_hctl_first_elem_return_val = NULL;
   ucm_get_cap_control_value = reinterpret_cast<char *>(0x1);
 
-  // Freed in destroy.
   cras_alsa_mixer_get_input_matching_name_return_value =
       reinterpret_cast<struct mixer_control *>(malloc(1));
 
@@ -377,6 +378,8 @@
   EXPECT_EQ(ucm_get_cap_control_called, 1);
   EXPECT_EQ(cras_alsa_mixer_get_input_matching_name_called, 1);
   cras_alsa_jack_list_destroy(jack_list);
+  // Mixer will be free by alsa_card_destroy, we should free it explicitly here
+  free(cras_alsa_mixer_get_input_matching_name_return_value);
 }
 
 TEST(AlsaJacks, CreateGPIOHdmi) {
diff --git a/cras/src/tests/alsa_mixer_unittest.cc b/cras/src/tests/alsa_mixer_unittest.cc
index c689835..2deef7f 100644
--- a/cras/src/tests/alsa_mixer_unittest.cc
+++ b/cras/src/tests/alsa_mixer_unittest.cc
@@ -167,6 +167,7 @@
   c = cras_alsa_mixer_create("hw:0");
   EXPECT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
   EXPECT_EQ(1, snd_mixer_open_called);
+  cras_alsa_mixer_destroy(c);
 }
 
 TEST(AlsaMixer, CreateFailAttach) {
@@ -180,6 +181,7 @@
   EXPECT_EQ(1, snd_mixer_attach_called);
   EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
   EXPECT_EQ(1, snd_mixer_close_called);
+  cras_alsa_mixer_destroy(c);
 }
 
 TEST(AlsaMixer, CreateFailSelemRegister) {
@@ -194,6 +196,7 @@
   EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
   EXPECT_EQ(1, snd_mixer_selem_register_called);
   EXPECT_EQ(1, snd_mixer_close_called);
+  cras_alsa_mixer_destroy(c);
 }
 
 TEST(AlsaMixer, CreateFailLoad) {
@@ -209,6 +212,7 @@
   EXPECT_EQ(1, snd_mixer_selem_register_called);
   EXPECT_EQ(1, snd_mixer_load_called);
   EXPECT_EQ(1, snd_mixer_close_called);
+  cras_alsa_mixer_destroy(c);
 }
 
 TEST(AlsaMixer, CreateNoElements) {
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index 822ef9e..3199573 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -396,6 +396,8 @@
   ASSERT_EQ(2, snd_use_case_get_called);
   EXPECT_EQ(snd_use_case_get_id[0], id_1);
   EXPECT_EQ(snd_use_case_get_id[1], id_2);
+  free((void *)input_dev_name);
+  free((void *)output_dev_name);
 }
 
 TEST(AlsaUcm, GetDeviceRateForDevice) {
@@ -456,6 +458,24 @@
   EXPECT_EQ(channel_layout[10], -1);
 }
 
+TEST(AlsaUcm, GetEchoReferenceDev) {
+  struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+  const char *echo_ref_dev;
+
+  ResetStubData();
+
+  std::string id_1 = "=EchoReferenceDev/Dev1/HiFi";
+  std::string value_1 = "Echo Ref";
+
+  snd_use_case_get_value[id_1] = value_1;
+  echo_ref_dev = ucm_get_echo_reference_dev_name_for_dev(mgr, "Dev1");
+
+  ASSERT_EQ(1, snd_use_case_get_called);
+  EXPECT_EQ(snd_use_case_get_id[0], id_1);
+  EXPECT_EQ(0, strcmp(echo_ref_dev, value_1.c_str()));
+  free((void *)echo_ref_dev);
+}
+
 TEST(AlsaUcm, GetHotwordModels) {
   struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
   const char *models;
@@ -477,6 +497,7 @@
   models = ucm_get_hotword_models(mgr);
   ASSERT_TRUE(models);
   EXPECT_EQ(0, strcmp(models, "en,jp,de"));
+  free((void *)models);
 }
 
 TEST(AlsaUcm, SetHotwordModel) {
@@ -784,6 +805,31 @@
   ASSERT_TRUE(ret);
 }
 
+TEST(AlsaUcm, MinSoftwareGain) {
+  struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+  long min_software_gain;
+  int ret;
+  std::string id = "=MinSoftwareGain/Internal Mic/HiFi";
+  std::string value = "2000";
+
+  ResetStubData();
+
+  /* Value can be found in UCM. */
+  snd_use_case_get_value[id] = value;
+
+  ret = ucm_get_min_software_gain(mgr, "Internal Mic", &min_software_gain);
+
+  EXPECT_EQ(0, ret);
+  EXPECT_EQ(2000, min_software_gain);
+
+  ResetStubData();
+
+  /* Value can not be found in UCM. */
+  ret = ucm_get_min_software_gain(mgr, "Internal Mic", &min_software_gain);
+
+  ASSERT_TRUE(ret);
+}
+
 TEST(AlsaUcm, DefaultNodeGain) {
   struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
   long default_node_gain;
@@ -875,6 +921,8 @@
 
   EXPECT_EQ(0, strcmp(mixer_name_1, value_1.c_str()));
   EXPECT_EQ(0, strcmp(mixer_name_2, value_2.c_str()));
+  free((void *)mixer_name_1);
+  free((void *)mixer_name_2);
 }
 
 TEST(AlsaUcm, GetMainVolumeMixerName) {
@@ -978,6 +1026,9 @@
 
   EXPECT_EQ(0, strcmp(jack_name_1, value_1.c_str()));
   EXPECT_EQ(NULL, jack_name_2);
+
+  free((void *)jack_name_1);
+  free((void *)jack_name_2);
 }
 
 TEST(AlsaUcm, GetJackTypeForDevice) {
@@ -1013,6 +1064,11 @@
   EXPECT_EQ(0, strcmp(jack_type_2, value_2.c_str()));
   EXPECT_EQ(NULL, jack_type_3);
   EXPECT_EQ(NULL, jack_type_4);
+
+  free((void *)jack_type_1);
+  free((void *)jack_type_2);
+  free((void *)jack_type_3);
+  free((void *)jack_type_4);
 }
 
 TEST(AlsaUcm, GetPeriodFramesForDevice) {
diff --git a/cras/src/tests/apm_list_unittest.cc b/cras/src/tests/apm_list_unittest.cc
new file mode 100644
index 0000000..b7d01c5
--- /dev/null
+++ b/cras/src/tests/apm_list_unittest.cc
@@ -0,0 +1,271 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_apm_list.h"
+#include "cras_audio_area.h"
+#include "cras_dsp_pipeline.h"
+#include "cras_iodev.h"
+#include "cras_iodev_list.h"
+#include "cras_types.h"
+#include "float_buffer.h"
+#include "webrtc_apm.h"
+}
+
+namespace {
+
+static void *stream_ptr = reinterpret_cast<void *>(0x123);
+static void *dev_ptr = reinterpret_cast<void *>(0x345);
+static void *dev_ptr2 = reinterpret_cast<void *>(0x678);
+static struct cras_apm_list *list;
+static struct cras_audio_area fake_audio_area;
+static unsigned int dsp_util_interleave_frames;
+static unsigned int webrtc_apm_process_stream_f_called;
+static unsigned int webrtc_apm_process_reverse_stream_f_called;
+static device_enabled_callback_t device_enabled_callback_val;
+static struct ext_dsp_module *ext_dsp_module_value;
+static struct cras_iodev fake_iodev;
+
+
+TEST(ApmList, ApmListCreate) {
+  list = cras_apm_list_create(stream_ptr, 0);
+  EXPECT_EQ((void *)NULL, list);
+
+  list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
+  EXPECT_NE((void *)NULL, list);
+  EXPECT_EQ(APM_ECHO_CANCELLATION, cras_apm_list_get_effects(list));
+
+  cras_apm_list_destroy(list);
+}
+
+TEST(ApmList, AddRemoveApm) {
+  struct cras_audio_format fmt;
+
+  fmt.num_channels = 2;
+  fmt.frame_rate = 48000;
+  fmt.format = SND_PCM_FORMAT_S16_LE;
+
+  list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
+  EXPECT_NE((void *)NULL, list);
+
+  EXPECT_NE((void *)NULL, cras_apm_list_add(list, dev_ptr, &fmt));
+  EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr2));
+
+  EXPECT_NE((void *)NULL, cras_apm_list_add(list, dev_ptr2, &fmt));
+  EXPECT_NE((void *)NULL, cras_apm_list_get(list, dev_ptr));
+
+  cras_apm_list_remove(list, dev_ptr);
+  EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr));
+  EXPECT_NE((void *)NULL, cras_apm_list_get(list, dev_ptr2));
+
+  cras_apm_list_remove(list, dev_ptr2);
+  EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr2));
+
+  cras_apm_list_destroy(list);
+}
+
+TEST(ApmList, ApmProcessForwardBuffer) {
+  struct cras_apm *apm;
+  struct cras_audio_format fmt;
+  struct cras_audio_area *area;
+  struct float_buffer *buf;
+
+  fmt.num_channels = 2;
+  fmt.frame_rate = 48000;
+  fmt.format = SND_PCM_FORMAT_S16_LE;
+
+  list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
+  EXPECT_NE((void *)NULL, list);
+
+  apm = cras_apm_list_add(list, dev_ptr, &fmt);
+
+  buf = float_buffer_create(500, 2);
+  float_buffer_written(buf, 300);
+  webrtc_apm_process_stream_f_called = 0;
+  cras_apm_list_process(apm, buf, 0);
+  EXPECT_EQ(0, webrtc_apm_process_stream_f_called);
+
+  area = cras_apm_list_get_processed(apm);
+  EXPECT_EQ(0, area->frames);
+
+  float_buffer_reset(buf);
+  float_buffer_written(buf, 200);
+  cras_apm_list_process(apm, buf, 0);
+  area = cras_apm_list_get_processed(apm);
+  EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
+  EXPECT_EQ(480, dsp_util_interleave_frames);
+  EXPECT_EQ(480, area->frames);
+
+  /* Put some processed frames. Another apm_list process will not call
+   * into webrtc_apm because the processed buffer is not yet empty.
+   */
+  cras_apm_list_put_processed(apm, 200);
+  float_buffer_reset(buf);
+  float_buffer_written(buf, 500);
+  cras_apm_list_process(apm, buf, 0);
+  EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
+
+  /* Put another 280 processed frames, so it's now ready for webrtc_apm
+   * to process another chunk of 480 frames (10ms) data.
+   */
+  cras_apm_list_put_processed(apm, 280);
+  cras_apm_list_process(apm, buf, 0);
+  EXPECT_EQ(2, webrtc_apm_process_stream_f_called);
+
+  float_buffer_destroy(&buf);
+  cras_apm_list_destroy(list);
+}
+
+TEST(ApmList, ApmProcessReverseData) {
+  struct cras_apm *apm;
+  struct cras_audio_format fmt;
+  struct float_buffer *buf;
+  float *const *rp;
+  unsigned int nread;
+  struct cras_iodev fake_iodev;
+
+  fmt.num_channels = 2;
+  fmt.frame_rate = 48000;
+  fmt.format = SND_PCM_FORMAT_S16_LE;
+
+  fake_iodev.direction = CRAS_STREAM_OUTPUT;
+  device_enabled_callback_val = NULL;
+  ext_dsp_module_value = NULL;
+  webrtc_apm_process_reverse_stream_f_called = 0;
+
+  cras_apm_list_init("");
+  EXPECT_NE((void *)NULL, device_enabled_callback_val);
+
+  device_enabled_callback_val(&fake_iodev, NULL);
+  EXPECT_NE((void *)NULL, ext_dsp_module_value);
+  EXPECT_NE((void *)NULL, ext_dsp_module_value->ports);
+
+  buf = float_buffer_create(500, 2);
+  float_buffer_written(buf, 500);
+  nread = 500;
+  rp = float_buffer_read_pointer(buf, 0, &nread);
+
+  for (int i = 0; i < buf->num_channels; i++)
+    ext_dsp_module_value->ports[i] = rp[i];
+
+  ext_dsp_module_value->configure(ext_dsp_module_value,
+                                  800, 2, 48000);
+  ext_dsp_module_value->run(ext_dsp_module_value, 500);
+  EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
+
+  list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
+  EXPECT_NE((void *)NULL, list);
+
+  apm = cras_apm_list_add(list, dev_ptr, &fmt);
+
+  ext_dsp_module_value->run(ext_dsp_module_value, 250);
+  EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
+
+  ext_dsp_module_value->run(ext_dsp_module_value, 250);
+  EXPECT_EQ(1, webrtc_apm_process_reverse_stream_f_called);
+
+  float_buffer_destroy(&buf);
+  cras_apm_list_deinit();
+}
+
+extern "C" {
+int cras_iodev_list_set_device_enabled_callback(
+		device_enabled_callback_t enabled_cb,
+		device_disabled_callback_t disabled_cb,
+		void *cb_data)
+{
+  device_enabled_callback_val = enabled_cb;
+  return 0;
+}
+struct cras_iodev *cras_iodev_list_get_first_enabled_iodev(
+	enum CRAS_STREAM_DIRECTION direction)
+{
+  return &fake_iodev;
+}
+void cras_iodev_set_ext_dsp_module(struct cras_iodev *iodev,
+				   struct ext_dsp_module *ext)
+{
+  ext_dsp_module_value = ext;
+}
+struct cras_audio_area *cras_audio_area_create(int num_channels)
+{
+  return &fake_audio_area;
+}
+
+void cras_audio_area_destroy(struct cras_audio_area *area)
+{
+}
+void cras_audio_area_config_channels(struct cras_audio_area *area,
+				     const struct cras_audio_format *fmt)
+{
+}
+void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
+					 const struct cras_audio_format *fmt,
+					 uint8_t *base_buffer)
+{
+}
+void dsp_util_interleave(float *const *input, int16_t *output, int channels,
+			 snd_pcm_format_t format, int frames)
+{
+  dsp_util_interleave_frames = frames;
+}
+struct aec_config *aec_config_get(const char *device_config_dir)
+{
+  return NULL;
+}
+void aec_config_dump(struct aec_config *config)
+{
+}
+struct apm_config *apm_config_get(const char *device_config_dir)
+{
+  return NULL;
+}
+void apm_config_dump(struct apm_config *config)
+{
+}
+webrtc_apm webrtc_apm_create(unsigned int num_channels,
+			     unsigned int frame_rate,
+			     struct aec_config *aec_config,
+                             struct apm_config *apm_config)
+{
+  return reinterpret_cast<webrtc_apm>(0x11);
+}
+void webrtc_apm_destroy(webrtc_apm apm)
+{
+  return;
+}
+int webrtc_apm_process_stream_f(webrtc_apm ptr,
+				int num_channels,
+				int rate,
+				float *const *data)
+{
+  webrtc_apm_process_stream_f_called++;
+  return 0;
+}
+
+int webrtc_apm_process_reverse_stream_f(
+		webrtc_apm ptr,
+		int num_channels, int rate,
+		float *const *data)
+{
+  webrtc_apm_process_reverse_stream_f_called++;
+  return 0;
+}
+int webrtc_apm_aec_dump(webrtc_apm ptr, void** work_queue,
+                        int start, FILE *handle)
+{
+  return 0;
+}
+
+} // extern "C"
+} // namespace
+
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/audio_thread_monitor_unittest.cc b/cras/src/tests/audio_thread_monitor_unittest.cc
new file mode 100644
index 0000000..d62c572
--- /dev/null
+++ b/cras/src/tests/audio_thread_monitor_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_audio_thread_monitor.c"
+#include "cras_main_message.h"
+}
+
+// Function call counters
+static int cras_system_state_add_snapshot_called;
+static int audio_thread_dump_thread_info_called;
+
+// Stub data
+static enum CRAS_MAIN_MESSAGE_TYPE type_set;
+struct cras_audio_thread_event_message message;
+
+void ResetStubData() {
+  cras_system_state_add_snapshot_called = 0;
+  audio_thread_dump_thread_info_called = 0;
+  type_set = (enum CRAS_MAIN_MESSAGE_TYPE) 999;
+  message.event_type = (enum CRAS_AUDIO_THREAD_EVENT_TYPE)999;
+}
+
+namespace {
+
+class AudioThreadMonitorTestSuite: public testing::Test {
+  protected:
+    virtual void SetUp() {
+      ResetStubData();
+    }
+
+    virtual void TearDown() {
+    }
+};
+
+TEST_F(AudioThreadMonitorTestSuite, Init) {
+  cras_audio_thread_monitor_init();
+  EXPECT_EQ(type_set, CRAS_MAIN_AUDIO_THREAD_EVENT);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, Busyloop) {
+  cras_audio_thread_busyloop();
+  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_BUSYLOOP);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, Debug) {
+  cras_audio_thread_debug();
+  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_DEBUG);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, Underrun) {
+  cras_audio_thread_underrun();
+  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_UNDERRUN);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, SevereUnderrun) {
+  cras_audio_thread_severe_underrun();
+  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_SEVERE_UNDERRUN);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, TakeSnapshot) {
+  take_snapshot(AUDIO_THREAD_EVENT_DEBUG);
+  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
+  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, EventHandlerDoubleCall) {
+  struct cras_audio_thread_event_message msg;
+  msg.event_type = AUDIO_THREAD_EVENT_DEBUG;
+  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
+  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
+  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);
+
+  // take_snapshot shouldn't be called since the time interval is short
+  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
+  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
+  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);
+}
+
+TEST_F(AudioThreadMonitorTestSuite, EventHandlerIgnoreInvalidEvent) {
+  struct cras_audio_thread_event_message msg;
+  msg.event_type = (enum CRAS_AUDIO_THREAD_EVENT_TYPE)999;
+  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
+  EXPECT_EQ(cras_system_state_add_snapshot_called, 0);
+  EXPECT_EQ(audio_thread_dump_thread_info_called, 0);
+}
+
+extern "C" {
+
+void cras_system_state_add_snapshot(
+  struct cras_audio_thread_snapshot *snapshot) {
+  cras_system_state_add_snapshot_called ++;
+}
+
+struct audio_thread* cras_iodev_list_get_audio_thread() {
+  return reinterpret_cast <struct audio_thread*>(0xff);
+}
+
+int audio_thread_dump_thread_info(struct audio_thread *thread,
+                                  struct audio_debug_info *info) {
+  audio_thread_dump_thread_info_called ++;
+  return 0;
+}
+
+int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
+                                  cras_message_callback callback,
+                                  void *callback_data) {
+  type_set = type;
+  return 0;
+}
+
+int cras_main_message_send(struct cras_main_message *msg) {
+  message = *(struct cras_audio_thread_event_message*)msg;
+  return 0;
+}
+
+}  // extern "C"
+}  // namespace
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int rc = RUN_ALL_TESTS();
+
+  return rc;
+}
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index e150126..083dab1 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -14,6 +14,7 @@
 #define BUFFER_SIZE 8192
 #define FIRST_CB_LEVEL 480
 
+static int cras_audio_thread_busyloop_called;
 static unsigned int cras_rstream_dev_offset_called;
 static unsigned int cras_rstream_dev_offset_ret[MAX_CALLS];
 static const struct cras_rstream *cras_rstream_dev_offset_rstream_val[MAX_CALLS];
@@ -31,6 +32,7 @@
 static unsigned int cras_iodev_prepare_output_before_write_samples_called;
 static enum CRAS_IODEV_STATE cras_iodev_prepare_output_before_write_samples_state;
 static unsigned int cras_iodev_get_output_buffer_called;
+static unsigned int cras_iodev_frames_to_play_in_sleep_called;
 static int cras_iodev_prepare_output_before_write_samples_ret;
 static int cras_iodev_reset_request_called;
 static struct cras_iodev *cras_iodev_reset_request_iodev;
@@ -54,12 +56,14 @@
   }
   cras_iodev_all_streams_written_ret = 0;
   if (cras_iodev_get_output_buffer_area) {
+    free(cras_iodev_get_output_buffer_area->channels[0].buf);
     free(cras_iodev_get_output_buffer_area);
     cras_iodev_get_output_buffer_area = NULL;
   }
   cras_iodev_put_output_buffer_called = 0;
   cras_iodev_put_output_buffer_nframes = 0;
   cras_iodev_fill_odev_zeros_frames = 0;
+  cras_iodev_frames_to_play_in_sleep_called = 0;
   dev_stream_playback_frames_ret = 0;
   cras_iodev_prepare_output_before_write_samples_called = 0;
   cras_iodev_prepare_output_before_write_samples_state = CRAS_IODEV_STATE_OPEN;
@@ -84,6 +88,8 @@
     }
 
     virtual void TearDown() {
+      audio_thread_destroy(thread_);
+      ResetGlobalStubData();
     }
 
     virtual void SetupDevice(cras_iodev *iodev,
@@ -91,7 +97,7 @@
       memset(iodev, 0, sizeof(*iodev));
       iodev->info.idx = ++device_id_;
       iodev->direction = direction;
-      iodev->open_dev = open_dev;
+      iodev->configure_dev = configure_dev;
       iodev->close_dev = close_dev;
       iodev->frames_queued = frames_queued;
       iodev->delay_frames = delay_frames;
@@ -118,7 +124,7 @@
       rstream->direction = direction;
       rstream->cb_threshold = 480;
       rstream->shm.area = static_cast<cras_audio_shm_area*>(
-          calloc(1, sizeof(rstream->shm.area)));
+          calloc(1, sizeof(*rstream->shm.area)));
     }
 
     void TearDownRstream(struct cras_rstream *rstream) {
@@ -133,7 +139,7 @@
       rstream->pinned_dev_idx = pin_to_dev->info.idx;
     }
 
-    static int open_dev(cras_iodev* iodev) {
+    static int configure_dev(cras_iodev* iodev) {
       open_dev_called_++;
       return 0;
     }
@@ -251,6 +257,7 @@
   EXPECT_EQ(0, rc);
   EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
   EXPECT_EQ(req, cras_iodev_start_ramp_request);
+  thread_rm_open_dev(thread_, &iodev);
 }
 
 TEST_F(StreamDeviceSuite, AddRemoveOpenInputDevice) {
@@ -320,6 +327,9 @@
   thread_rm_open_dev(thread_, &idev3);
   adev = thread_->open_devs[CRAS_STREAM_INPUT];
   EXPECT_EQ(adev->dev, &idev2);
+  thread_rm_open_dev(thread_, &idev2);
+  thread_rm_open_dev(thread_, &odev2);
+  thread_rm_open_dev(thread_, &odev3);
 }
 
 TEST_F(StreamDeviceSuite, MultipleInputStreamsCopyFirstStreamOffset) {
@@ -363,6 +373,8 @@
   EXPECT_EQ(&rstream2, cras_rstream_dev_offset_update_rstream_val[1]);
   EXPECT_EQ(0, cras_rstream_dev_offset_update_frames_val[1]);
 
+  thread_rm_open_dev(thread_, &iodev);
+  thread_rm_open_dev(thread_, &iodev2);
   TearDownRstream(&rstream);
   TearDownRstream(&rstream2);
   TearDownRstream(&rstream3);
@@ -395,7 +407,7 @@
   // Send captured samples to client.
   // This will also update wake time for this device based on
   // dev_stream_wake_time of each stream of this device.
-  send_captured_samples(thread_);
+  dev_io_send_captured_samples(thread_->open_devs[CRAS_STREAM_INPUT]);
 
   // wake_ts is maintained in open_dev.
   adev = thread_->open_devs[CRAS_STREAM_INPUT];
@@ -405,6 +417,7 @@
   EXPECT_EQ(ts_wake_1.tv_sec, adev->wake_ts.tv_sec);
   EXPECT_EQ(ts_wake_1.tv_nsec, adev->wake_ts.tv_nsec);
 
+  thread_rm_open_dev(thread_, &iodev);
   TearDownRstream(&rstream1);
   TearDownRstream(&rstream2);
 }
@@ -458,8 +471,10 @@
   EXPECT_EQ(NULL, dev_stream->next);
 
   // Remove 2 streams, check the streams are removed from both open devices.
-  thread_remove_stream(thread_, &rstream, &iodev);
-  thread_remove_stream(thread_, &rstream3, &iodev2);
+  dev_io_remove_stream(&thread_->open_devs[rstream.direction],
+		       &rstream, &iodev);
+  dev_io_remove_stream(&thread_->open_devs[rstream3.direction],
+		       &rstream3, &iodev2);
   dev_stream = iodev2.streams;
   EXPECT_EQ(NULL, dev_stream);
 
@@ -471,6 +486,7 @@
   dev_stream = iodev.streams;
   EXPECT_EQ(NULL, dev_stream);
 
+  thread_rm_open_dev(thread_, &iodev);
   TearDownRstream(&rstream);
   TearDownRstream(&rstream2);
   TearDownRstream(&rstream3);
@@ -498,7 +514,9 @@
   cras_iodev_prepare_output_before_write_samples_ret = -EINVAL;
 
   // cras_iodev should handle no stream playback.
-  EXPECT_EQ(-EINVAL, write_output_samples(thread_, adev));
+  EXPECT_EQ(-EINVAL,
+	    write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT],
+				 adev, nullptr));
 
   // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
   // called.
@@ -526,7 +544,7 @@
       CRAS_IODEV_STATE_NO_STREAM_RUN;
 
   // cras_iodev should handle no stream playback.
-  write_output_samples(thread_, adev);
+  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
   EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
   // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
   // called.
@@ -558,7 +576,7 @@
       CRAS_IODEV_STATE_NO_STREAM_RUN;
 
   // cras_iodev should NOT leave no stream state;
-  write_output_samples(thread_, adev);
+  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
   EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
   // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
   // called.
@@ -569,16 +587,48 @@
       CRAS_IODEV_STATE_NORMAL_RUN;
 
   // cras_iodev should write samples from streams.
-  write_output_samples(thread_, adev);
+  write_output_samples(&thread_->open_devs[CRAS_STREAM_OUTPUT], adev, nullptr);
   EXPECT_EQ(2, cras_iodev_prepare_output_before_write_samples_called);
   EXPECT_EQ(1, cras_iodev_get_output_buffer_called);
 
   thread_rm_open_dev(thread_, &iodev);
 }
 
-TEST_F(StreamDeviceSuite, WriteOutputSamplesUnderrun) {
+TEST_F(StreamDeviceSuite, DoPlaybackNoStream) {
+  struct cras_iodev iodev;
+
+  ResetGlobalStubData();
+
+  SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+  // Add the device.
+  thread_add_open_dev(thread_, &iodev);
+
+  // Assume device is started.
+  iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+  // Assume device remains in no stream state;
+  cras_iodev_prepare_output_before_write_samples_state = \
+      CRAS_IODEV_STATE_NO_STREAM_RUN;
+  // Add 10 frames in queue to prevent underrun
+  frames_queued_ = 10;
+
+  // cras_iodev should handle no stream playback.
+  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);
+  EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
+  // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
+  // called.
+  EXPECT_EQ(0, cras_iodev_get_output_buffer_called);
+
+  EXPECT_EQ(0, cras_iodev_output_underrun_called);
+  // cras_iodev_frames_to_play_in_sleep should be called from
+  // update_dev_wakeup_time.
+  EXPECT_EQ(1, cras_iodev_frames_to_play_in_sleep_called);
+
+  thread_rm_open_dev(thread_, &iodev);
+}
+
+TEST_F(StreamDeviceSuite, DoPlaybackUnderrun) {
   struct cras_iodev iodev, *piodev = &iodev;
-  struct open_dev *adev;
   struct cras_rstream rstream;
 
   ResetGlobalStubData();
@@ -591,28 +641,29 @@
 
   // Add the device and add the stream.
   thread_add_open_dev(thread_, &iodev);
-  adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
   thread_add_stream(thread_, &rstream, &piodev, 1);
 
-  // Assume device is running and there is an underrun. There is no frame
-  // queued and there is no sample written in this cycle.
+  // Assume device is running and there is an underrun.
+  // It wrote 11 frames into device but new hw_level is only 10.
+  // It means underrun may happened because 10 - 11 < 0.
   // Audio thread should ask iodev to handle output underrun.
   iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
-  frames_queued_ = 0;
-  cras_iodev_all_streams_written_ret = 0;
+  frames_queued_ = 10;
+  cras_iodev_all_streams_written_ret = 11;
 
   // Assume device in normal run stream state;
   cras_iodev_prepare_output_before_write_samples_state = \
       CRAS_IODEV_STATE_NORMAL_RUN;
 
-  write_output_samples(thread_, adev);
+  EXPECT_EQ(0, cras_iodev_output_underrun_called);
+  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);
   EXPECT_EQ(1, cras_iodev_output_underrun_called);
 
   thread_rm_open_dev(thread_, &iodev);
   TearDownRstream(&rstream);
 }
 
-TEST_F(StreamDeviceSuite, DoPlaybackUnderrun) {
+TEST_F(StreamDeviceSuite, DoPlaybackSevereUnderrun) {
   struct cras_iodev iodev, *piodev = &iodev;
   struct cras_rstream rstream;
 
@@ -636,7 +687,7 @@
   cras_iodev_prepare_output_before_write_samples_state = \
       CRAS_IODEV_STATE_NORMAL_RUN;
 
-  do_playback(thread_);
+  dev_io_playback_write(&thread_->open_devs[CRAS_STREAM_OUTPUT], nullptr);
 
   // Audio thread should ask main thread to reset device.
   EXPECT_EQ(1, cras_iodev_reset_request_called);
@@ -675,6 +726,28 @@
   EXPECT_EQ(0, thread_drain_stream_ms_remaining(&thread, &rstream));
 }
 
+TEST(BusyloopDetectSuite, CheckerTest) {
+  continuous_zero_sleep_count = 0;
+  cras_audio_thread_busyloop_called = 0;
+  timespec wait_ts;
+  wait_ts.tv_sec = 0;
+  wait_ts.tv_nsec = 0;
+
+  check_busyloop(&wait_ts);
+  EXPECT_EQ(continuous_zero_sleep_count, 1);
+  EXPECT_EQ(cras_audio_thread_busyloop_called, 0);
+  check_busyloop(&wait_ts);
+  EXPECT_EQ(continuous_zero_sleep_count, 2);
+  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);
+  check_busyloop(&wait_ts);
+  EXPECT_EQ(continuous_zero_sleep_count, 3);
+  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);
+
+  wait_ts.tv_sec = 1;
+  check_busyloop(&wait_ts);
+  EXPECT_EQ(continuous_zero_sleep_count, 0);
+  EXPECT_EQ(cras_audio_thread_busyloop_called, 1);
+}
 
 extern "C" {
 
@@ -709,7 +782,8 @@
   return 0;
 }
 
-int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level)
+int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level,
+                    const struct cras_audio_format *fmt)
 {
   return 0;
 }
@@ -761,21 +835,20 @@
   return 0;
 }
 
-int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes)
+int cras_iodev_put_input_buffer(struct cras_iodev *iodev)
 {
   return 0;
 }
 
 int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
-				 unsigned int nframes)
-{
+                                 unsigned int nframes, int* non_empty,
+                                 struct cras_fmt_conv *output_converter) {
   cras_iodev_put_output_buffer_called++;
   cras_iodev_put_output_buffer_nframes = nframes;
   return 0;
 }
 
 int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
-				struct cras_audio_area **area,
 				unsigned *frames)
 {
   return 0;
@@ -795,7 +868,7 @@
   return 0;
 }
 
-void cras_fmt_conv_destroy(struct cras_fmt_conv *conv)
+void cras_fmt_conv_destroy(struct cras_fmt_conv **conv)
 {
 }
 
@@ -957,6 +1030,8 @@
 int dev_stream_wake_time(struct dev_stream *dev_stream,
                          unsigned int curr_level,
                          struct timespec *level_tstamp,
+                         unsigned int cap_limit,
+                         int is_cap_limit_stream,
                          struct timespec *wake_time)
 {
   if (dev_stream_wake_time_val.find(dev_stream) !=
@@ -967,6 +1042,16 @@
   return 0;
 }
 
+int dev_stream_is_pending_reply(const struct dev_stream *dev_stream)
+{
+  return 0;
+}
+
+int dev_stream_flush_old_audio_messages(struct dev_stream *dev_stream)
+{
+  return 0;
+}
+
 int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
 {
 	return iodev->frames_queued(iodev, tstamp);
@@ -997,11 +1082,22 @@
   return cras_iodev_prepare_output_before_write_samples_ret;
 }
 
+int cras_server_metrics_highest_hw_level(unsigned hw_level,
+		enum CRAS_STREAM_DIRECTION direction)
+{
+  return 0;
+}
+
 int cras_server_metrics_longest_fetch_delay(int delay_msec)
 {
   return 0;
 }
 
+int cras_server_metrics_num_underruns(unsigned num_underruns)
+{
+  return 0;
+}
+
 float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev)
 {
   return 1.0f;
@@ -1011,8 +1107,8 @@
                                                 unsigned int *hw_level,
                                                 struct timespec *hw_tstamp)
 {
-  clock_gettime(CLOCK_MONOTONIC_RAW, hw_tstamp);
-  *hw_level = 0;
+  *hw_level = cras_iodev_frames_queued(odev, hw_tstamp);
+  cras_iodev_frames_to_play_in_sleep_called++;
   return 0;
 }
 
@@ -1056,6 +1152,11 @@
   return 0;
 }
 
+void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
+		unsigned int hw_level)
+{
+}
+
 int cras_iodev_start_ramp(struct cras_iodev *odev,
                           enum CRAS_IODEV_RAMP_REQUEST request)
 {
@@ -1064,6 +1165,50 @@
   return 0;
 }
 
+int input_data_get_for_stream(
+		struct input_data *data,
+		struct cras_rstream *stream,
+		struct buffer_share *offsets,
+		struct cras_audio_area **area,
+		unsigned int *offset)
+{
+  return 0;
+}
+
+int input_data_put_for_stream(struct input_data *data,
+			   struct cras_rstream *stream,
+			   struct buffer_share *offsets,
+			   unsigned int frames)
+{
+  return 0;
+}
+
+#ifdef HAVE_WEBRTC_APM
+
+uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
+{
+  return 0;
+}
+
+void cras_apm_list_set_debug_recording(struct cras_apm *apm,
+    unsigned int stream_id, int start, const char *file_name_base)
+{
+}
+void cras_apm_list_set_aec_dump(struct cras_apm_list *list,
+				void *dev_ptr,
+			        int start,
+			        int fd)
+{
+}
+
+#endif
+
+int cras_audio_thread_busyloop()
+{
+  cras_audio_thread_busyloop_called ++;
+  return 0;
+}
+
 }  // extern "C"
 
 int main(int argc, char **argv) {
diff --git a/cras/src/tests/bt_device_unittest.cc b/cras/src/tests/bt_device_unittest.cc
index 4f7cd86..e0f340e 100644
--- a/cras/src/tests/bt_device_unittest.cc
+++ b/cras/src/tests/bt_device_unittest.cc
@@ -35,6 +35,7 @@
   cras_bt_io_remove_called = 0;
   cras_bt_io_destroy_called = 0;
   cras_bt_io_try_remove_ret = 0;
+  cras_main_message_send_msg = NULL;
 }
 
 namespace {
@@ -55,6 +56,11 @@
       d3_.update_active_node = update_active_node;
     }
 
+    virtual void TearDown() {
+      if(cras_main_message_send_msg)
+        free(cras_main_message_send_msg);
+    }
+
     static void update_active_node(struct cras_iodev *iodev,
                                    unsigned node_idx,
                                    unsigned dev_enabled) {
@@ -117,6 +123,7 @@
   EXPECT_EQ(1, cras_bt_io_remove_called);
   EXPECT_EQ(1, cras_bt_io_destroy_called);
   EXPECT_EQ(0, cras_bt_device_get_active_profile(device));
+  cras_bt_device_destroy(device);
 }
 
 TEST_F(BtDeviceTestSuite, SwitchProfile) {
@@ -150,6 +157,7 @@
   cras_main_message_add_handler_callback(
       cras_main_message_send_msg,
       cras_main_message_add_handler_callback_data);
+  cras_bt_device_destroy(device);
 }
 
 /* Stubs */
@@ -260,7 +268,9 @@
 
 /* From iodev_list */
 
-int cras_iodev_open(struct cras_iodev *dev, unsigned int cb_level) {
+int cras_iodev_open(struct cras_iodev *dev, unsigned int cb_level,
+                    const struct cras_audio_format *fmt)
+{
   return 0;
 }
 
@@ -287,7 +297,13 @@
 
 int cras_main_message_send(struct cras_main_message *msg)
 {
-  cras_main_message_send_msg = msg;
+  // cras_main_message is a local variable from caller, we should allocate
+  // memory from heap and copy its data
+  if(cras_main_message_send_msg)
+    free(cras_main_message_send_msg);
+  cras_main_message_send_msg =
+    (struct cras_main_message *)calloc(1, msg->length);
+  memcpy((void *)cras_main_message_send_msg, (void *)msg, msg->length);
   return 0;
 }
 
diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc
index 979404a..aa37cc9 100644
--- a/cras/src/tests/bt_io_unittest.cc
+++ b/cras/src/tests/bt_io_unittest.cc
@@ -8,6 +8,7 @@
 
 // To test static functions.
 #include "cras_bt_io.c"
+#include "utlist.h"
 }
 
 static struct cras_bt_device *fake_device =
@@ -15,6 +16,7 @@
 static unsigned int cras_iodev_add_node_called;
 static unsigned int cras_iodev_rm_node_called;
 static unsigned int cras_iodev_free_format_called;
+static unsigned int cras_iodev_free_resources_called;
 static unsigned int cras_iodev_set_active_node_called;
 static unsigned int cras_iodev_list_add_output_called;
 static unsigned int cras_iodev_list_rm_output_called;
@@ -33,6 +35,7 @@
   cras_iodev_add_node_called = 0;
   cras_iodev_rm_node_called = 0;
   cras_iodev_free_format_called = 0;
+  cras_iodev_free_resources_called = 0;
   cras_iodev_set_active_node_called = 0;
   cras_iodev_list_add_output_called = 0;
   cras_iodev_list_rm_output_called = 0;
@@ -62,7 +65,7 @@
       delay_frames_called_ = 0;
       get_buffer_called_ = 0;
       put_buffer_called_ = 0;
-      open_dev_called_ = 0;
+      configure_dev_called_ = 0;
       close_dev_called_ = 0;
     }
 
@@ -77,12 +80,18 @@
       d->delay_frames = delay_frames;
       d->get_buffer = get_buffer;
       d->put_buffer = put_buffer;
-      d->open_dev = open_dev;
+      d->configure_dev = configure_dev;
       d->close_dev = close_dev;
+      d->supported_rates = NULL;
+      d->supported_channel_counts = NULL;
+      d->supported_formats = NULL;
     }
 
     // Stub functions for the iodev structure.
     static int update_supported_formats(struct cras_iodev *iodev) {
+      free(iodev->supported_rates);
+      free(iodev->supported_channel_counts);
+      free(iodev->supported_formats);
       iodev->supported_rates = (size_t *)calloc(
           2, sizeof(*iodev->supported_rates));
       iodev->supported_rates[0] = 48000;
@@ -118,11 +127,15 @@
       put_buffer_called_++;
       return 0;
     }
-    static int open_dev(cras_iodev* iodev) {
-      open_dev_called_++;
+    static int configure_dev(cras_iodev* iodev) {
+      configure_dev_called_++;
       return 0;
     }
     static int close_dev(cras_iodev* iodev) {
+      free(iodev->format);
+      free(iodev->ext_format);
+      iodev->format = NULL;
+      iodev->ext_format = NULL;
       close_dev_called_++;
       return 0;
     }
@@ -135,7 +148,7 @@
   static unsigned int delay_frames_called_;
   static unsigned int get_buffer_called_;
   static unsigned int put_buffer_called_;
-  static unsigned int open_dev_called_;
+  static unsigned int configure_dev_called_;
   static unsigned int close_dev_called_;
 };
 
@@ -147,7 +160,7 @@
 unsigned int BtIoBasicSuite::delay_frames_called_;
 unsigned int BtIoBasicSuite::get_buffer_called_;
 unsigned int BtIoBasicSuite::put_buffer_called_;
-unsigned int BtIoBasicSuite::open_dev_called_;
+unsigned int BtIoBasicSuite::configure_dev_called_;
 unsigned int BtIoBasicSuite::close_dev_called_;
 
 TEST_F(BtIoBasicSuite, CreateBtIo) {
@@ -160,12 +173,13 @@
   EXPECT_NE((void *)NULL, bt_iodev);
   EXPECT_EQ(&iodev_, active_profile_dev(bt_iodev));
   EXPECT_EQ(1, cras_iodev_list_add_output_called);
+  bt_iodev->open_dev(bt_iodev);
   bt_iodev->format = &fake_fmt;
   bt_iodev->update_supported_formats(bt_iodev);
   EXPECT_EQ(1, update_supported_formats_called_);
 
-  bt_iodev->open_dev(bt_iodev);
-  EXPECT_EQ(1, open_dev_called_);
+  bt_iodev->configure_dev(bt_iodev);
+  EXPECT_EQ(1, configure_dev_called_);
   bt_iodev->frames_queued(bt_iodev, &tstamp);
   EXPECT_EQ(1, frames_queued_called_);
   bt_iodev->get_buffer(bt_iodev, &fake_area, &fr);
@@ -176,25 +190,31 @@
   EXPECT_EQ(1, close_dev_called_);
   EXPECT_EQ(1, cras_iodev_free_format_called);
   cras_bt_io_destroy(bt_iodev);
+  EXPECT_EQ(1, cras_iodev_free_resources_called);
   EXPECT_EQ(1, cras_iodev_list_rm_output_called);
+
+  free(iodev_.supported_rates);
+  free(iodev_.supported_channel_counts);
+  free(iodev_.supported_formats);
 }
 
-TEST_F(BtIoBasicSuite, SwitchProfileOnUpdateFormatForInputDev) {
+TEST_F(BtIoBasicSuite, SwitchProfileOnOpenDevForInputDev) {
   ResetStubData();
   iodev_.direction = CRAS_STREAM_INPUT;
   bt_iodev = cras_bt_io_create(fake_device, &iodev_,
       CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
 
   cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE;
-  bt_iodev->update_supported_formats(bt_iodev);
+  bt_iodev->open_dev(bt_iodev);
 
   EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY |
             CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
             cras_bt_device_set_active_profile_val);
   EXPECT_EQ(1, cras_bt_device_switch_profile_enable_dev_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
-TEST_F(BtIoBasicSuite, NoSwitchProfileOnUpdateFormatForInputDevAlreadyOnHfp) {
+TEST_F(BtIoBasicSuite, NoSwitchProfileOnOpenDevForInputDevAlreadyOnHfp) {
   ResetStubData();
   iodev_.direction = CRAS_STREAM_INPUT;
   bt_iodev = cras_bt_io_create(fake_device, &iodev_,
@@ -203,9 +223,10 @@
   /* No need to switch profile if already on HFP. */
   cras_bt_device_get_active_profile_ret =
       CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY;
-  bt_iodev->update_supported_formats(bt_iodev);
+  bt_iodev->open_dev(bt_iodev);
 
   EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 TEST_F(BtIoBasicSuite, SwitchProfileOnCloseInputDev) {
@@ -223,6 +244,7 @@
   EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE,
             cras_bt_device_set_active_profile_val);
   EXPECT_EQ(1, cras_bt_device_switch_profile_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 TEST_F(BtIoBasicSuite, NoSwitchProfileOnCloseInputDevNoSupportA2dp) {
@@ -238,6 +260,7 @@
   bt_iodev->close_dev(bt_iodev);
 
   EXPECT_EQ(0, cras_bt_device_switch_profile_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 TEST_F(BtIoBasicSuite, SwitchProfileOnAppendA2dpDev) {
@@ -253,6 +276,7 @@
             cras_bt_device_set_active_profile_val);
   EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
   EXPECT_EQ(1, cras_bt_device_switch_profile_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 TEST_F(BtIoBasicSuite, NoSwitchProfileOnAppendHfpDev) {
@@ -265,6 +289,7 @@
       CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
 
   EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 TEST_F(BtIoBasicSuite, CreateSetDeviceActiveProfileToA2DP) {
@@ -315,6 +340,7 @@
       CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
 
   ASSERT_STREQ("BLUETOOTH", bt_iodev->active_node->name);
+  cras_bt_io_destroy(bt_iodev);
 }
 
 } // namespace
@@ -330,13 +356,13 @@
 void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node)
 {
   cras_iodev_add_node_called++;
-  iodev->nodes = node;
+  DL_APPEND(iodev->nodes, node);
 }
 
 void cras_iodev_rm_node(struct cras_iodev *iodev, struct cras_ionode *node)
 {
   cras_iodev_rm_node_called++;
-  iodev->nodes = NULL;
+  DL_DELETE(iodev->nodes, node);
 }
 
 void cras_iodev_free_format(struct cras_iodev *iodev)
@@ -357,6 +383,11 @@
   return 0;
 }
 
+void cras_iodev_free_resources(struct cras_iodev *iodev)
+{
+    cras_iodev_free_resources_called++;
+}
+
 //  From iodev list.
 int cras_iodev_list_add_output(struct cras_iodev *output)
 {
@@ -424,6 +455,11 @@
   return "/fake/object/path";
 }
 
+int cras_bt_device_get_use_hardware_volume(struct cras_bt_device *device)
+{
+  return 1;
+}
+
 int is_utf8_string(const char* string)
 {
   return is_utf8_string_ret_value;
diff --git a/cras/src/tests/byte_buffer_unittest.cc b/cras/src/tests/byte_buffer_unittest.cc
index 5acfb1a..e0e7bbf 100644
--- a/cras/src/tests/byte_buffer_unittest.cc
+++ b/cras/src/tests/byte_buffer_unittest.cc
@@ -14,8 +14,8 @@
   unsigned int data_size;
 
   b = byte_buffer_create(100);
-  EXPECT_EQ(100, buf_available_bytes(b));
-  EXPECT_EQ(0, buf_queued_bytes(b));
+  EXPECT_EQ(100, buf_available(b));
+  EXPECT_EQ(0, buf_queued(b));
 
   data = buf_read_pointer_size(b, &data_size);
   EXPECT_NE((void *)NULL, data);
@@ -31,8 +31,8 @@
   EXPECT_EQ(50, data_size);
 
   buf_increment_read(b, 40);
-  EXPECT_EQ(10, buf_queued_bytes(b));
-  EXPECT_EQ(90, buf_available_bytes(b));
+  EXPECT_EQ(10, buf_queued(b));
+  EXPECT_EQ(90, buf_available(b));
 
   /* Test write to the end of ring buffer. */
   data = buf_write_pointer_size(b, &data_size);
@@ -44,7 +44,7 @@
   EXPECT_NE((void *)NULL, data);
   EXPECT_EQ(40, data_size);
 
-  byte_buffer_destroy(b);
+  byte_buffer_destroy(&b);
 }
 
 TEST(ByteBuffer, SetUsedSizeReadWrite) {
@@ -53,12 +53,12 @@
   unsigned int data_size;
 
   b = byte_buffer_create(100);
-  EXPECT_EQ(100, buf_available_bytes(b));
-  EXPECT_EQ(0, buf_queued_bytes(b));
+  EXPECT_EQ(100, buf_available(b));
+  EXPECT_EQ(0, buf_queued(b));
 
   /* Test set used_size to limit the initial allocated max size. */
   byte_buffer_set_used_size(b, 90);
-  EXPECT_EQ(90, buf_available_bytes(b));
+  EXPECT_EQ(90, buf_available(b));
 
   data = buf_write_pointer_size(b, &data_size);
   EXPECT_NE((void *)NULL, data);
@@ -70,8 +70,8 @@
   EXPECT_EQ(90, data_size);
 
   buf_increment_read(b, 50);
-  EXPECT_EQ(50, buf_available_bytes(b));
-  EXPECT_EQ(40, buf_queued_bytes(b));
+  EXPECT_EQ(50, buf_available(b));
+  EXPECT_EQ(40, buf_queued(b));
 
   data = buf_write_pointer_size(b, &data_size);
   EXPECT_NE((void *)NULL, data);
@@ -92,7 +92,7 @@
   EXPECT_NE((void *)NULL, data);
   EXPECT_EQ(50, data_size);
 
-  byte_buffer_destroy(b);
+  byte_buffer_destroy(&b);
 }
 
 } // namespace
diff --git a/cras/src/tests/cras_client_unittest.cc b/cras/src/tests/cras_client_unittest.cc
index 1853ae4..4980609 100644
--- a/cras/src/tests/cras_client_unittest.cc
+++ b/cras/src/tests/cras_client_unittest.cc
@@ -164,6 +164,7 @@
   handle_capture_data_ready(&stream_, 480);
   EXPECT_EQ(1, samples_ready_called);
   EXPECT_EQ(0, shm->area->read_buf_idx);
+  FreeShm(shm);
 }
 
 void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
@@ -171,6 +172,7 @@
   int shm_fds[2] = {0, 1};
   int shm_max_size = 600;
   size_t format_bytes;
+  size_t effects = 123;
   struct cras_audio_shm_area area;
 
   stream_.direction = direction;
@@ -192,7 +194,8 @@
       0,
       stream_.id,
       &server_format,
-      shm_max_size);
+      shm_max_size,
+      effects);
 
   stream_connected(&stream_, &msg, shm_fds, 2);
 
@@ -222,6 +225,7 @@
   int shm_fds[2] = {0, 1};
   int shm_max_size = 600;
   size_t format_bytes;
+  size_t effects = 123;
   struct cras_audio_shm_area area;
   int rc;
 
@@ -250,7 +254,8 @@
       1,
       stream_.id,
       &server_format,
-      shm_max_size);
+      shm_max_size,
+      effects);
 
   stream_connected(&stream_, &msg, shm_fds, 2);
 
@@ -367,4 +372,16 @@
 {
   return mmap_return_value;
 }
+
+struct cras_audio_format *cras_audio_format_create(snd_pcm_format_t format,
+                                                   size_t frame_rate,
+                                                   size_t num_channels)
+{
+  return reinterpret_cast<struct cras_audio_format*>(0x123);
+}
+
+void cras_audio_format_destroy(struct cras_audio_format *fmt)
+{
+}
+
 }
diff --git a/cras/src/tests/cras_dsp_pipeline_unittest.cc b/cras/src/tests/cras_dsp_pipeline_unittest.cc
index ef04cb6..21cbc10 100644
--- a/cras/src/tests/cras_dsp_pipeline_unittest.cc
+++ b/cras/src/tests/cras_dsp_pipeline_unittest.cc
@@ -188,6 +188,7 @@
 }
 
 static struct dsp_module *modules[MAX_MODULES];
+static struct dsp_module *cras_dsp_module_set_sink_ext_module_val;
 static int num_modules;
 static struct dsp_module *find_module(const char *name)
 {
@@ -210,6 +211,11 @@
   modules[num_modules++] = module;
   return module;
 }
+void cras_dsp_module_set_sink_ext_module(struct dsp_module *module,
+					 struct ext_dsp_module *ext_module)
+{
+  cras_dsp_module_set_sink_ext_module_val = module;
+}
 }
 
 namespace {
@@ -237,6 +243,7 @@
 
   char filename[sizeof(FILENAME_TEMPLATE) + 1];
   FILE *fp;
+  struct ext_dsp_module ext_mod;
 };
 
 TEST_F(DspPipelineTestSuite, Simple) {
@@ -335,6 +342,12 @@
   ASSERT_EQ(3, d2->input[0]);
   ASSERT_EQ(1000, d2->input[1]);
 
+  /* Expect the sink module "m2" is set. */
+  cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod);
+  struct data *d = (struct data *)
+      cras_dsp_module_set_sink_ext_module_val->data;
+  ASSERT_STREQ("m2", d->title);
+
   cras_dsp_pipeline_deinstantiate(p);
   ASSERT_EQ(1, d1->deinstantiate_called);
   ASSERT_EQ(1, d2->deinstantiate_called);
@@ -465,7 +478,7 @@
 
   int16_t *samples = new int16_t[DSP_BUFFER_SIZE];
   fill_test_data(samples, DSP_BUFFER_SIZE);
-  cras_dsp_pipeline_apply(p, (uint8_t*)samples, 100);
+  cras_dsp_pipeline_apply(p, (uint8_t*)samples, SND_PCM_FORMAT_S16_LE, 100);
   /* the data flow through 2 plugins because m4 is disabled. */
   verify_processed_data(samples, 100, 2);
   delete[] samples;
@@ -477,6 +490,12 @@
   ASSERT_EQ(1, d5->run_called);
   ASSERT_EQ(100, d5->sample_count);
 
+  /* Expect the sink module "m5" is set. */
+  cras_dsp_pipeline_set_sink_ext_module(p, &ext_mod);
+  struct data *d = (struct data *)
+      cras_dsp_module_set_sink_ext_module_val->data;
+  ASSERT_STREQ("m5", d->title);
+
   /* re-instantiate */
   ASSERT_EQ(1, d5->instantiate_called);
   ASSERT_EQ(1, d5->get_delay_called);
diff --git a/cras/src/tests/cras_selinux_helper_unittest.c b/cras/src/tests/cras_selinux_helper_unittest.c
new file mode 100644
index 0000000..0e7efad
--- /dev/null
+++ b/cras/src/tests/cras_selinux_helper_unittest.c
@@ -0,0 +1,10 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cras_shm.h"
+
+/* Define a stub cras_selinux_restorecon() which doesn't do anything */
+int cras_selinux_restorecon(const char* pathname) {
+	return 0;
+}
diff --git a/cras/src/tests/cras_test_client.c b/cras/src/tests/cras_test_client.c
index 6db3e11..c7c4565 100644
--- a/cras/src/tests/cras_test_client.c
+++ b/cras/src/tests/cras_test_client.c
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <inttypes.h>
 #include <math.h>
 #include <pthread.h>
 #include <stdio.h>
@@ -56,6 +57,12 @@
 static int play_short_sound_periods = 0;
 static int play_short_sound_periods_left = 0;
 
+static int effect_aec = 0;
+static int effect_ns = 0;
+static int effect_agc = 0;
+static int effect_vad = 0;
+static char *aecdump_file = NULL;
+
 /* Conditional so the client thread can signal that main should exit. */
 static pthread_mutex_t done_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t done_cond = PTHREAD_COND_INITIALIZER;
@@ -152,21 +159,6 @@
 }
 
 /* Run from callback thread. */
-static int got_hotword(struct cras_client *client,
-		       cras_stream_id_t stream_id,
-		       uint8_t *captured_samples,
-		       uint8_t *playback_samples,
-		       unsigned int frames,
-		       const struct timespec *captured_time,
-		       const struct timespec *playback_time,
-		       void *user_arg)
-{
-	printf("got hotword %u frames\n", frames);
-
-	return frames;
-}
-
-/* Run from callback thread. */
 static int put_samples(struct cras_client *client,
 		       cras_stream_id_t stream_id,
 		       uint8_t *captured_samples,
@@ -536,6 +528,10 @@
 		printf("%-30s dev:%u hw_level:%u target:%u\n",
 		       "DEFAULT_NO_STREAMS", data1, data2, data3);
 		break;
+	case AUDIO_THREAD_UNDERRUN:
+		printf("%-30s dev:%u hw_level:%u total_written:%u\n",
+		       "UNDERRUN", data1, data2, data3);
+		break;
 	case AUDIO_THREAD_SEVERE_UNDERRUN:
 		printf("%-30s dev:%u\n", "SEVERE_UNDERRUN", data1);
 		break;
@@ -545,15 +541,9 @@
 	}
 }
 
-static void audio_debug_info(struct cras_client *client)
+static void print_audio_debug_info(const struct audio_debug_info *info)
 {
-	const struct audio_debug_info *info;
 	int i, j;
-
-	info = cras_client_get_audio_debug_info(client);
-	if (!info)
-		return;
-
 	printf("Audio Debug Stats:\n");
 	printf("-------------devices------------\n");
 	if (info->num_devs > MAX_DEBUG_DEVS)
@@ -572,7 +562,8 @@
 		       "num_channels: %u\n"
 		       "est_rate_ratio: %lf\n"
 		       "num_underruns: %u\n"
-		       "num_severe_underruns: %u\n",
+		       "num_severe_underruns: %u\n"
+		       "highest_hw_level: %u\n",
 		       (unsigned int)info->devs[i].buffer_size,
 		       (unsigned int)info->devs[i].min_buffer_level,
 		       (unsigned int)info->devs[i].min_cb_level,
@@ -581,7 +572,8 @@
 		       (unsigned int)info->devs[i].num_channels,
 		       info->devs[i].est_rate_ratio,
 		       (unsigned int)info->devs[i].num_underruns,
-		       (unsigned int)info->devs[i].num_severe_underruns);
+		       (unsigned int)info->devs[i].num_severe_underruns,
+		       (unsigned int)info->devs[i].highest_hw_level);
 		printf("\n");
 	}
 
@@ -591,7 +583,7 @@
 
 	for (i = 0; i < info->num_streams; i++) {
 		int channel;
-		printf("stream: %llx dev: %u\n",
+		printf("stream: %llu dev: %u\n",
 		       (unsigned long long)info->streams[i].stream_id,
 		       (unsigned int)info->streams[i].dev_idx);
 		printf("direction: %s\n",
@@ -601,12 +593,14 @@
 		       cras_stream_type_str(info->streams[i].stream_type));
 		printf("buffer_frames: %u\n"
 		       "cb_threshold: %u\n"
+		       "effects: 0x%.4x\n"
 		       "frame_rate: %u\n"
 		       "num_channels: %u\n"
 		       "longest_fetch_sec: %u.%09u\n"
 		       "num_overruns: %u\n",
 		       (unsigned int)info->streams[i].buffer_frames,
 		       (unsigned int)info->streams[i].cb_threshold,
+		       (unsigned int)info->streams[i].effects,
 		       (unsigned int)info->streams[i].frame_rate,
 		       (unsigned int)info->streams[i].num_channels,
 		       (unsigned int)info->streams[i].longest_fetch_sec,
@@ -628,6 +622,72 @@
 		j++;
 		j %= info->log.len;
 	}
+}
+
+static void audio_debug_info(struct cras_client *client)
+{
+	const struct audio_debug_info *info;
+	info = cras_client_get_audio_debug_info(client);
+	if (!info)
+		return;
+	print_audio_debug_info(info);
+
+	/* Signal main thread we are done after the last chunk. */
+	pthread_mutex_lock(&done_mutex);
+	pthread_cond_signal(&done_cond);
+	pthread_mutex_unlock(&done_mutex);
+}
+
+static void print_cras_audio_thread_snapshot(
+	const struct cras_audio_thread_snapshot *snapshot)
+{
+	printf("-------------snapshot------------\n");
+	printf("Event time: %" PRId64 ".%ld\n",
+	       (int64_t)snapshot->timestamp.tv_sec,
+	       snapshot->timestamp.tv_nsec);
+
+	printf("Event type: ");
+	switch(snapshot->event_type) {
+	case AUDIO_THREAD_EVENT_BUSYLOOP:
+		printf("busyloop\n");
+		break;
+	case AUDIO_THREAD_EVENT_UNDERRUN:
+		printf("underrun\n");
+		break;
+	case AUDIO_THREAD_EVENT_SEVERE_UNDERRUN:
+		printf("severe underrun\n");
+		break;
+	case AUDIO_THREAD_EVENT_DEBUG:
+		printf("debug\n");
+		break;
+	default:
+		printf("no such type\n");
+	}
+	print_audio_debug_info(&snapshot->audio_debug_info);
+}
+
+static void audio_thread_snapshots(struct cras_client *client)
+{
+	const struct cras_audio_thread_snapshot_buffer *snapshot_buffer;
+	uint32_t i;
+	int j;
+	int count = 0;
+
+	snapshot_buffer = cras_client_get_audio_thread_snapshot_buffer(client);
+	i = snapshot_buffer->pos;
+	for(j = 0; j < CRAS_MAX_AUDIO_THREAD_SNAPSHOTS; j++)
+	{
+		if(snapshot_buffer->snapshots[i].timestamp.tv_sec ||
+		   snapshot_buffer->snapshots[i].timestamp.tv_nsec)
+		{
+			print_cras_audio_thread_snapshot(
+				&snapshot_buffer->snapshots[i]);
+			count++;
+		}
+		i++;
+		i %= CRAS_MAX_AUDIO_THREAD_SNAPSHOTS;
+	}
+	printf("There are %d, snapshots.\n", count);
 
 	/* Signal main thread we are done after the last chunk. */
 	pthread_mutex_lock(&done_mutex);
@@ -669,6 +729,27 @@
 	return 0;
 }
 
+static void run_aecdump(struct cras_client *client, uint64_t stream_id,
+			int start)
+{
+	int aecdump_fd;
+	if (start) {
+		aecdump_fd = open(aecdump_file, O_CREAT | O_RDWR | O_TRUNC,
+				  0666);
+		if (aecdump_fd == -1) {
+			printf("Fail to open file %s", aecdump_file);
+			return;
+		}
+
+		printf("Dumping AEC info to %s, stream %" PRId64 ", fd %d\n",
+		       aecdump_file, stream_id, aecdump_fd);
+		cras_client_set_aec_dump(client, stream_id, 1, aecdump_fd);
+	} else {
+		cras_client_set_aec_dump(client, stream_id, 0, -1);
+		printf("Close AEC dump file %s\n", aecdump_file);
+	}
+}
+
 static int run_file_io_stream(struct cras_client *client,
 			      int fd,
 			      enum CRAS_STREAM_DIRECTION direction,
@@ -677,7 +758,8 @@
 			      size_t rate,
 			      size_t num_channels,
 			      uint32_t flags,
-			      int is_loopback)
+			      int is_loopback,
+			      int is_post_dsp)
 {
 	int rc, tty;
 	struct cras_stream_params *params;
@@ -709,14 +791,10 @@
 	total_rms_sqr_sum = 0;
 	total_rms_size = 0;
 
-	if (direction == CRAS_STREAM_INPUT) {
-		if (flags == HOTWORD_STREAM)
-			aud_cb = got_hotword;
-		else
-			aud_cb = got_samples;
-	} else {
+	if (direction == CRAS_STREAM_INPUT)
+		aud_cb = got_samples;
+	else
 		aud_cb = put_samples;
-	}
 
 	if (fd == 0) {
 		if (direction != CRAS_STREAM_OUTPUT)
@@ -746,12 +824,24 @@
 	if (params == NULL)
 		return -ENOMEM;
 
+	if (effect_aec)
+		cras_client_stream_params_enable_aec(params);
+	if (effect_ns)
+		cras_client_stream_params_enable_ns(params);
+	if (effect_agc)
+		cras_client_stream_params_enable_agc(params);
+	if (effect_vad)
+		cras_client_stream_params_enable_vad(params);
+
 	cras_client_run_thread(client);
 	if (is_loopback) {
+		enum CRAS_NODE_TYPE type = (is_post_dsp ?
+					    CRAS_NODE_TYPE_POST_DSP :
+					    CRAS_NODE_TYPE_POST_MIX_PRE_DSP);
+
 		cras_client_connected_wait(client);
-		pin_device_id = cras_client_get_first_dev_type_idx(client,
-				CRAS_NODE_TYPE_POST_MIX_PRE_DSP,
-				CRAS_STREAM_INPUT);
+		pin_device_id = cras_client_get_first_dev_type_idx(
+				client, type, CRAS_STREAM_INPUT);
 	}
 
 	stream_playing =
@@ -904,7 +994,9 @@
 		       enum CRAS_STREAM_TYPE stream_type,
 		       size_t rate,
 		       size_t num_channels,
-		       int is_loopback)
+		       uint32_t flags,
+		       int is_loopback,
+		       int is_post_dsp)
 {
 	int fd = open(file, O_CREAT | O_RDWR | O_TRUNC, 0666);
 	if (fd == -1) {
@@ -913,7 +1005,8 @@
 	}
 
 	run_file_io_stream(client, fd, CRAS_STREAM_INPUT, block_size,
-			   stream_type, rate, num_channels, 0, is_loopback);
+			   stream_type, rate, num_channels, flags, is_loopback,
+			   is_post_dsp);
 
 	close(fd);
 	return 0;
@@ -935,21 +1028,12 @@
 	}
 
 	run_file_io_stream(client, fd, CRAS_STREAM_OUTPUT, block_size,
-			   stream_type, rate, num_channels, 0, 0);
+			   stream_type, rate, num_channels, 0, 0, 0);
 
 	close(fd);
 	return 0;
 }
 
-static int run_hotword(struct cras_client *client,
-		       size_t block_size,
-		       size_t rate)
-{
-	run_file_io_stream(client, -1, CRAS_STREAM_INPUT, block_size,
-			   CRAS_STREAM_TYPE_DEFAULT, rate, 1, HOTWORD_STREAM,
-			   0);
-	return 0;
-}
 static void print_server_info(struct cras_client *client)
 {
 	cras_client_run_thread(client);
@@ -961,7 +1045,24 @@
 	print_active_stream_info(client);
 }
 
-static void print_audio_debug_info(struct cras_client *client)
+static void show_audio_thread_snapshots(struct cras_client *client)
+{
+	struct timespec wait_time;
+
+	cras_client_run_thread(client);
+	cras_client_connected_wait(client); /* To synchronize data. */
+	cras_client_update_audio_thread_snapshots(client,
+						  audio_thread_snapshots);
+
+	clock_gettime(CLOCK_REALTIME, &wait_time);
+	wait_time.tv_sec += 2;
+
+	pthread_mutex_lock(&done_mutex);
+	pthread_cond_timedwait(&done_cond, &done_mutex, &wait_time);
+	pthread_mutex_unlock(&done_mutex);
+}
+
+static void show_audio_debug_info(struct cras_client *client)
 {
 	struct timespec wait_time;
 
@@ -1035,6 +1136,7 @@
 	{"block_size",		required_argument,	0, 'b'},
 	{"capture_file",	required_argument,	0, 'c'},
 	{"duration_seconds",	required_argument,	0, 'd'},
+	{"dump_events",	        no_argument,            0, 'e'},
 	{"dump_dsp",            no_argument,            0, 'f'},
 	{"capture_gain",        required_argument,      0, 'g'},
 	{"help",                no_argument,            0, 'h'},
@@ -1063,7 +1165,7 @@
 	{"version",             no_argument,            0, '4'},
 	{"add_test_dev",        required_argument,      0, '5'},
 	{"test_hotword_file",   required_argument,      0, '6'},
-	{"listen_for_hotword",  no_argument,            0, '7'},
+	{"listen_for_hotword",  required_argument,      0, '7'},
 	{"pin_device",		required_argument,	0, '8'},
 	{"suspend",		required_argument,	0, '9'},
 	{"set_node_gain",	required_argument,	0, ':'},
@@ -1071,6 +1173,12 @@
 	{"config_global_remix", required_argument,	0, ';'},
 	{"set_hotword_model",	required_argument,	0, '<'},
 	{"get_hotword_models",	required_argument,	0, '>'},
+	{"post_dsp",            required_argument,	0, 'A'},
+	{"stream_id",		required_argument,	0, 'B'},
+	{"aecdump",		required_argument,	0, 'C'},
+	{"reload_aec_config",	no_argument,		0, 'D'},
+	{"effects",		required_argument,	0, 'E'},
+	{"get_aec_supported",	no_argument,		0, 'F'},
 	{"syslog_mask",		required_argument,	0, 'L'},
 	{"mute_loop_test",	required_argument,	0, 'M'},
 	{"stream_type",		required_argument,	0, 'T'},
@@ -1096,8 +1204,8 @@
 	printf("--duration_seconds <N> - Seconds to record or playback.\n");
 	printf("--get_hotword_models <N>:<M> - Get the supported hotword models of node\n");
 	printf("--help - Print this message.\n");
-	printf("--listen_for_hotword - Listen for a hotword if supported\n");
-	printf("--loopback_file <name> - Name of file to record loopback to.\n");
+	printf("--listen_for_hotword <name> - Listen and capture hotword stream if supported\n");
+	printf("--loopback_file <name> - Name of file to record from loopback device.\n");
 	printf("--mute <0|1> - Set system mute state.\n");
 	printf("--mute_loop_test <0|1> - Continuously loop mute/umute. Argument: 0 - stop on error.\n"
 	       "                         1 - automatically reconnect to CRAS.\n");
@@ -1119,6 +1227,9 @@
 	printf("--select_output <N>:<M> - Select the ionode with the given id as preferred output\n");
 	printf("--set_hotword_model <N>:<M>:<model> - Set the model to node\n");
 	printf("--playback_delay_us <N> - Set the time in us to delay a reply for playback when i is pressed\n");
+	printf("--post_dsp <0|1> - Use this flag with --loopback_file. The default value is 0.\n"
+	       "                   Argument: 0 - Record from post-mix, pre-DSP loopback device.\n"
+	       "                             1 - Record from post-DSP loopback device.\n");
 	printf("--set_node_volume <N>:<M>:<0-100> - Set the volume of the ionode with the given id\n");
 	printf("--show_latency - Display latency while playing or recording.\n");
 	printf("--show_rms - Display RMS value of loopback stream.\n");
@@ -1146,8 +1257,11 @@
 	const char *capture_file = NULL;
 	const char *playback_file = NULL;
 	const char *loopback_file = NULL;
+	int post_dsp = 0;
 	enum CRAS_STREAM_TYPE stream_type = CRAS_STREAM_TYPE_DEFAULT;
 	int rc = 0;
+	uint32_t stream_flags = 0;
+	cras_stream_id_t stream_id = 0;
 
 	option_index = 0;
 	openlog("cras_test_client", LOG_PERROR, LOG_USER);
@@ -1180,6 +1294,9 @@
 		case 'l':
 			loopback_file = optarg;
 			break;
+		case 'A':
+			post_dsp = atoi(optarg);
+			break;
 		case 'b':
 			block_size = atoi(optarg);
 			break;
@@ -1337,8 +1454,11 @@
 			}
 			break;
 		}
+		case 'e':
+			show_audio_thread_snapshots(client);
+			break;
 		case 'm':
-			print_audio_debug_info(client);
+			show_audio_debug_info(client);
 			break;
 		case 'o':
 			channel_layout = optarg;
@@ -1369,7 +1489,8 @@
 			break;
 		}
 		case '7': {
-			run_hotword(client, 4096, 16000);
+			stream_flags = HOTWORD_STREAM;
+			capture_file = optarg;
 			break;
 		}
 		case '8':
@@ -1451,6 +1572,37 @@
 		case 'T':
 			stream_type = atoi(optarg);
 			break;
+		case 'E': {
+			char *s;
+
+			s = strtok(optarg, ",");
+			while (s) {
+				if (strcmp("aec", s) == 0)
+					effect_aec = 1;
+				else if (strcmp("ns", s) == 0)
+					effect_ns = 1;
+				else if (strcmp("agc", s) == 0)
+					effect_agc = 1;
+				else if (strcmp("vad", s) == 0)
+					effect_vad = 1;
+				else
+					printf("Unknown effect %s\n", s);
+				s = strtok(NULL, ",");
+			}
+			break;
+		}
+		case 'B':
+			stream_id = atoi(optarg);
+			break;
+		case 'C':
+			aecdump_file = optarg;
+			break;
+		case 'D':
+			cras_client_reload_aec_config(client);
+			break;
+		case 'F':
+			printf("AEC supported %d\n",
+			       !!cras_client_get_aec_supported(client));
 		default:
 			break;
 		}
@@ -1464,21 +1616,27 @@
 		if (strcmp(capture_file, "-") == 0)
 			rc = run_file_io_stream(client, 1, CRAS_STREAM_INPUT,
 					block_size, stream_type, rate,
-					num_channels, 0, 0);
+					num_channels, stream_flags, 0, 0);
 		else
 			rc = run_capture(client, capture_file, block_size,
-					 stream_type, rate, num_channels, 0);
+					 stream_type, rate, num_channels,
+					 stream_flags, 0, 0);
 	} else if (playback_file != NULL) {
 		if (strcmp(playback_file, "-") == 0)
 			rc = run_file_io_stream(client, 0, CRAS_STREAM_OUTPUT,
 					block_size, stream_type, rate,
-					num_channels, 0, 0);
+					num_channels, stream_flags, 0, 0);
 		else
 			rc = run_playback(client, playback_file, block_size,
 					  stream_type, rate, num_channels);
 	} else if (loopback_file != NULL) {
 		rc = run_capture(client, loopback_file, block_size,
-				 stream_type, rate, num_channels, 1);
+				 stream_type, rate, num_channels,
+				 stream_flags, 1, post_dsp);
+	} else if (aecdump_file != NULL) {
+		run_aecdump(client, stream_id, 1);
+		sleep(duration_seconds);
+		run_aecdump(client, stream_id, 0);
 	}
 
 destroy_exit:
diff --git a/cras/src/tests/dev_io_stubs.cc b/cras/src/tests/dev_io_stubs.cc
new file mode 100644
index 0000000..fc6c353
--- /dev/null
+++ b/cras/src/tests/dev_io_stubs.cc
@@ -0,0 +1,144 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <memory>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+extern "C" {
+#include "dev_stream.h"
+#include "cras_rstream.h"
+#include "cras_iodev.h"
+#include "cras_shm.h"
+#include "cras_types.h"
+#include "utlist.h"
+}
+
+#include "dev_io_stubs.h"
+
+ShmPtr create_shm(size_t cb_threshold) {
+  uint32_t frame_bytes = 4;
+  uint32_t used_size = cb_threshold * 2 * frame_bytes;
+  uint32_t shm_size = sizeof(cras_audio_shm_area) + used_size * 2;
+  ShmPtr shm(reinterpret_cast<cras_audio_shm_area*>(calloc(1, shm_size)),
+              free);
+  shm->config.used_size = used_size;
+  shm->config.frame_bytes = frame_bytes;
+  shm->volume_scaler = 1.0;
+  return shm;
+}
+
+RstreamPtr create_rstream(cras_stream_id_t id,
+                          CRAS_STREAM_DIRECTION direction,
+                          size_t cb_threshold,
+                          const cras_audio_format* format,
+                          cras_audio_shm_area* shm) {
+  RstreamPtr rstream(
+      reinterpret_cast<cras_rstream*>(calloc(1, sizeof(cras_rstream))), free);
+  rstream->stream_id = id;
+  rstream->direction = direction;
+  rstream->fd = RSTREAM_FAKE_POLL_FD;
+  rstream->buffer_frames = cb_threshold * 2;
+  rstream->cb_threshold = cb_threshold;
+  rstream->shm.area = shm;
+  rstream->shm.config = shm->config;
+  rstream->format = *format;
+  cras_frames_to_time(cb_threshold,
+                      rstream->format.frame_rate,
+                      &rstream->sleep_interval_ts);
+  return rstream;
+}
+
+DevStreamPtr create_dev_stream(unsigned int dev_id, cras_rstream* rstream) {
+  DevStreamPtr dstream(
+      reinterpret_cast<dev_stream*>(calloc(1, sizeof(dev_stream))),
+      free);
+  dstream->dev_id = dev_id;
+  dstream->stream = rstream;
+  dstream->dev_rate = rstream->format.frame_rate;
+  return dstream;
+}
+
+StreamPtr create_stream(cras_stream_id_t id,
+                        unsigned int dev_id,
+                        CRAS_STREAM_DIRECTION direction,
+                        size_t cb_threshold,
+                        const cras_audio_format* format) {
+  ShmPtr shm = create_shm(cb_threshold);
+  RstreamPtr rstream = create_rstream(1, CRAS_STREAM_INPUT, cb_threshold,
+                                      format, shm.get());
+  DevStreamPtr dstream = create_dev_stream(1, rstream.get());
+  StreamPtr s(new Stream(std::move(shm),
+                         std::move(rstream),
+                         std::move(dstream)));
+  return s;
+}
+
+void AddFakeDataToStream(Stream* stream, unsigned int frames) {
+  cras_shm_check_write_overrun(&stream->rstream->shm);
+  cras_shm_buffer_written(&stream->rstream->shm, frames);
+}
+
+int delay_frames_stub(const struct cras_iodev* iodev) {
+  return 0;
+}
+
+IonodePtr create_ionode(CRAS_NODE_TYPE type) {
+  IonodePtr ionode(
+      reinterpret_cast<cras_ionode*>(calloc(1, sizeof(cras_ionode))), free);
+  ionode->type = type;
+  return ionode;
+}
+
+IodevPtr create_open_iodev(CRAS_STREAM_DIRECTION direction,
+                           size_t cb_threshold,
+                           cras_audio_format* format,
+                           cras_ionode* active_node) {
+  IodevPtr iodev(reinterpret_cast<cras_iodev*>(calloc(1, sizeof(cras_iodev))),
+                  free);
+  iodev->is_enabled = 1;
+  iodev->direction = direction;
+  iodev->format = format;
+  iodev->state = CRAS_IODEV_STATE_OPEN;
+  iodev->delay_frames = delay_frames_stub;
+  iodev->active_node = active_node;
+  iodev->buffer_size = cb_threshold * 2;
+  iodev->min_cb_level = UINT_MAX;
+  iodev->max_cb_level = 0;
+  return iodev;
+}
+
+DevicePtr create_device(CRAS_STREAM_DIRECTION direction,
+                        size_t cb_threshold,
+                        cras_audio_format* format,
+                        CRAS_NODE_TYPE active_node_type) {
+  IonodePtr node = create_ionode(active_node_type);
+  IodevPtr dev = create_open_iodev(direction, cb_threshold, format, node.get());
+  OpendevPtr odev(
+      reinterpret_cast<open_dev*>(calloc(1, sizeof(open_dev))), free);
+  odev->dev = dev.get();
+
+  DevicePtr d(new Device(std::move(dev), std::move(node), std::move(odev)));
+  return d;
+}
+
+void add_stream_to_dev(IodevPtr& dev, const StreamPtr& stream) {
+  DL_APPEND(dev->streams, stream->dstream.get());
+  dev->min_cb_level = std::min(stream->rstream->cb_threshold,
+                               static_cast<size_t>(dev->min_cb_level));
+  dev->max_cb_level = std::max(stream->rstream->cb_threshold,
+                               static_cast<size_t>(dev->max_cb_level));
+}
+
+void fill_audio_format(cras_audio_format* format, unsigned int rate) {
+  format->format = SND_PCM_FORMAT_S16_LE;
+  format->frame_rate = rate;
+  format->num_channels = 2;
+  format->channel_layout[0] = 0;
+  format->channel_layout[1] = 1;
+  for (int i = 2; i < CRAS_CH_MAX; i++)
+    format->channel_layout[i] = -1;
+}
diff --git a/cras/src/tests/dev_io_stubs.h b/cras/src/tests/dev_io_stubs.h
new file mode 100644
index 0000000..4c54ff8
--- /dev/null
+++ b/cras/src/tests/dev_io_stubs.h
@@ -0,0 +1,82 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+extern "C" {
+#include "dev_io.h"
+#include "dev_stream.h"
+#include "cras_rstream.h"
+#include "cras_iodev.h"
+#include "cras_shm.h"
+#include "cras_types.h"
+#include "utlist.h"
+}
+
+#include "iodev_stub.h"
+#include "rstream_stub.h"
+
+#define RSTREAM_FAKE_POLL_FD 33
+
+using DevStreamPtr = std::unique_ptr<dev_stream, decltype(free)*>;
+using IodevPtr = std::unique_ptr<cras_iodev, decltype(free)*>;
+using IonodePtr = std::unique_ptr<cras_ionode, decltype(free)*>;
+using OpendevPtr = std::unique_ptr<open_dev, decltype(free)*>;
+using RstreamPtr = std::unique_ptr<cras_rstream, decltype(free)*>;
+using ShmPtr = std::unique_ptr<cras_audio_shm_area, decltype(free)*>;
+
+// Holds the rstream and devstream pointers for an attached stream.
+struct Stream {
+  Stream(ShmPtr shm, RstreamPtr rstream, DevStreamPtr dstream) :
+    shm(std::move(shm)),
+    rstream(std::move(rstream)),
+    dstream(std::move(dstream)) {
+  }
+  ShmPtr shm;
+  RstreamPtr rstream;
+  DevStreamPtr dstream;
+};
+using StreamPtr = std::unique_ptr<Stream>;
+
+// Holds the iodev and ionode pointers for an attached device.
+struct Device {
+  Device(IodevPtr dev, IonodePtr node, OpendevPtr odev) :
+    dev(std::move(dev)),
+    node(std::move(node)),
+    odev(std::move(odev)) {
+  }
+  IodevPtr dev;
+  IonodePtr node;
+  OpendevPtr odev;
+};
+using DevicePtr = std::unique_ptr<Device>;
+
+ShmPtr create_shm(size_t cb_threshold);
+RstreamPtr create_rstream(cras_stream_id_t id,
+                          CRAS_STREAM_DIRECTION direction,
+                          size_t cb_threshold,
+                          const cras_audio_format* format,
+                          cras_audio_shm_area* shm);
+DevStreamPtr create_dev_stream(unsigned int dev_id, cras_rstream* rstream);
+StreamPtr create_stream(cras_stream_id_t id,
+                        unsigned int dev_id,
+                        CRAS_STREAM_DIRECTION direction,
+                        size_t cb_threshold,
+                        const cras_audio_format* format);
+void AddFakeDataToStream(Stream* stream, unsigned int frames);
+int delay_frames_stub(const struct cras_iodev* iodev);
+IonodePtr create_ionode(CRAS_NODE_TYPE type);
+IodevPtr create_open_iodev(CRAS_STREAM_DIRECTION direction,
+                           size_t cb_threshold,
+                           cras_audio_format* format,
+                           cras_ionode* active_node);
+DevicePtr create_device(CRAS_STREAM_DIRECTION direction,
+                        size_t cb_threshold,
+                        cras_audio_format* format,
+                        CRAS_NODE_TYPE active_node_type);
+void add_stream_to_dev(IodevPtr& dev, const StreamPtr& stream);
+void fill_audio_format(cras_audio_format* format, unsigned int rate);
diff --git a/cras/src/tests/dev_io_unittest.cc b/cras/src/tests/dev_io_unittest.cc
new file mode 100644
index 0000000..c49fb45
--- /dev/null
+++ b/cras/src/tests/dev_io_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "dev_io.h" // tested
+#include "dev_stream.h" // tested
+#include "cras_rstream.h" // stubbed
+#include "cras_iodev.h" // stubbed
+#include "cras_shm.h"
+#include "cras_types.h"
+#include "utlist.h"
+
+struct audio_thread_event_log* atlog;
+}
+
+#include "dev_io_stubs.h"
+#include "iodev_stub.h"
+#include "rstream_stub.h"
+
+namespace {
+
+
+class DevIoSuite : public testing::Test{
+ protected:
+  virtual void SetUp() {
+    atlog = static_cast<audio_thread_event_log*>(calloc(1, sizeof(*atlog)));
+    iodev_stub_reset();
+    rstream_stub_reset();
+  }
+
+  virtual void TearDown() {
+    free(atlog);
+  }
+};
+
+TEST_F(DevIoSuite, SendCapturedFails) {
+  const size_t cb_threshold = 480;
+
+  cras_audio_format format;
+  fill_audio_format(&format, 48000);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  // rstream's next callback is now and there is enough data to fill.
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream.get(), 480);
+
+  struct open_dev* dev_list = NULL;
+  DevicePtr dev = create_device(CRAS_STREAM_INPUT, cb_threshold,
+                                &format, CRAS_NODE_TYPE_MIC);
+  DL_APPEND(dev_list, dev->odev.get());
+  add_stream_to_dev(dev->dev, stream);
+
+  // Set failure response from frames_queued.
+  iodev_stub_frames_queued(dev->dev.get(), -3, start);
+
+  EXPECT_EQ(-3, dev_io_send_captured_samples(dev_list));
+}
+
+/* Stubs */
+extern "C" {
+
+int cras_server_metrics_highest_hw_level(unsigned hw_level,
+		enum CRAS_STREAM_DIRECTION direction)
+{
+  return 0;
+}
+
+int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
+{
+  return 0;
+}
+
+int cras_server_metrics_num_underruns(unsigned num_underruns)
+{
+  return 0;
+}
+
+int input_data_get_for_stream(
+		struct input_data *data,
+		struct cras_rstream *stream,
+		struct buffer_share *offsets,
+		struct cras_audio_area **area,
+		unsigned int *offset)
+{
+  return 0;
+}
+
+int input_data_put_for_stream(struct input_data *data,
+			   struct cras_rstream *stream,
+			   struct buffer_share *offsets,
+			   unsigned int frames)
+{
+  return 0;
+}
+struct cras_audio_format *cras_rstream_post_processing_format(
+    const struct cras_rstream *stream, void *dev_ptr)
+{
+  return NULL;
+}
+}  // extern "C"
+
+}  //  namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc
index 8f90ed6..450f5c7 100644
--- a/cras/src/tests/dev_stream_unittest.cc
+++ b/cras/src/tests/dev_stream_unittest.cc
@@ -19,6 +19,10 @@
 
 extern "C" {
 struct audio_thread_event_log *atlog;
+unsigned int max_frames_for_conversion(
+    unsigned int stream_frames,
+    unsigned int stream_rate,
+    unsigned int device_rate);
 };
 
 static struct timespec clock_gettime_retspec;
@@ -40,6 +44,11 @@
   48000,
   1,
 };
+static const struct cras_audio_format fmt_s16le_8 = {
+  SND_PCM_FORMAT_S16_LE,
+  8000,
+  2,
+};
 
 struct cras_audio_area_copy_call {
   const struct cras_audio_area *dst;
@@ -74,14 +83,14 @@
 };
 
 static int config_format_converter_called;
+static const struct cras_audio_format *config_format_converter_from_fmt;
+static int config_format_converter_frames;
 static struct cras_fmt_conv *config_format_converter_conv;
 static struct cras_audio_format in_fmt;
 static struct cras_audio_format out_fmt;
 static struct cras_audio_area_copy_call copy_area_call;
 static struct fmt_conv_call conv_frames_call;
-static size_t conv_frames_ret;
 static int cras_audio_area_create_num_channels_val;
-static int cras_fmt_conv_convert_frames_in_frames_val;
 static int cras_fmt_conversion_needed_val;
 static int cras_fmt_conv_set_linear_resample_rates_called;
 static float cras_fmt_conv_set_linear_resample_rates_from;
@@ -93,8 +102,11 @@
 static unsigned int rstream_get_readable_num;
 static uint8_t *rstream_get_readable_ptr;
 
+static struct cras_audio_format *cras_rstream_post_processing_format_val;
 static int cras_rstream_audio_ready_called;
 static int cras_rstream_audio_ready_count;
+static int cras_rstream_is_pending_reply_ret;
+static int cras_rstream_flush_old_audio_messages_called;
 
 class CreateSuite : public testing::Test{
   protected:
@@ -117,20 +129,51 @@
       rstream_.format = fmt_s16le_44_1;
       rstream_.flags = 0;
 
+      config_format_converter_from_fmt = NULL;
       config_format_converter_called = 0;
       cras_fmt_conversion_needed_val = 0;
       cras_fmt_conv_set_linear_resample_rates_called = 0;
 
       cras_rstream_audio_ready_called = 0;
       cras_rstream_audio_ready_count = 0;
+      cras_rstream_is_pending_reply_ret = 0;
+      cras_rstream_flush_old_audio_messages_called = 0;
 
       memset(&copy_area_call, 0xff, sizeof(copy_area_call));
       memset(&conv_frames_call, 0xff, sizeof(conv_frames_call));
 
       atlog = audio_thread_event_log_init();
+
+      devstr.stream = &rstream_;
+      devstr.conv = NULL;
+      devstr.conv_buffer = NULL;
+      devstr.conv_buffer_size_frames = 0;
+
+      area = (struct cras_audio_area*)calloc(1,
+          sizeof(*area) + 2 * sizeof(struct cras_channel_area));
+      area->num_channels = 2;
+      channel_area_set_channel(&area->channels[0], CRAS_CH_FL);
+      channel_area_set_channel(&area->channels[1], CRAS_CH_FR);
+      area->channels[0].step_bytes = 4;
+      area->channels[0].buf = (uint8_t *)(cap_buf);
+      area->channels[1].step_bytes = 4;
+      area->channels[1].buf = (uint8_t *)(cap_buf + 1);
+      area->frames = kBufferFrames;
+
+      stream_area = (struct cras_audio_area*)calloc(1,
+          sizeof(*area) + 2 * sizeof(struct cras_channel_area));
+      stream_area->num_channels = 2;
+      rstream_.audio_area = stream_area;
+      int16_t *shm_samples = (int16_t *)rstream_.shm.area->samples;
+      stream_area->channels[0].step_bytes = 4;
+      stream_area->channels[0].buf = (uint8_t *)(shm_samples);
+      stream_area->channels[1].step_bytes = 4;
+      stream_area->channels[1].buf = (uint8_t *)(shm_samples + 1);
     }
 
     virtual void TearDown() {
+      free(area);
+      free(stream_area);
       free(rstream_.shm.area);
       audio_thread_event_log_deinit(atlog);
     }
@@ -151,42 +194,37 @@
       cras_shm_set_volume_scaler(shm, 1.0);
     }
 
-  int16_t *compare_buffer_;
-  struct cras_rstream rstream_;
-};
+    void SetUpFmtConv(unsigned int in_rate, unsigned int out_rate,
+                      unsigned int conv_buf_size) {
+      in_fmt.frame_rate = in_rate;
+      out_fmt.frame_rate = out_rate;
+      cras_fmt_conversion_needed_val = 1;
 
-TEST_F(CreateSuite, CaptureNoSRC) {
+      devstr.conv = (struct cras_fmt_conv *)0xdead;
+      devstr.conv_buffer =
+          (struct byte_buffer *)byte_buffer_create(conv_buf_size * 4);
+      devstr.conv_buffer_size_frames = kBufferFrames * 2;
+
+      devstr.conv_area = (struct cras_audio_area*)calloc(1,
+          sizeof(*area) + 2 * sizeof(*area->channels));
+      devstr.conv_area->num_channels = 2;
+      devstr.conv_area->channels[0].step_bytes = 4;
+      devstr.conv_area->channels[0].buf = (uint8_t *)(devstr.conv_buffer->bytes);
+      devstr.conv_area->channels[1].step_bytes = 4;
+      devstr.conv_area->channels[1].buf =
+          (uint8_t *)(devstr.conv_buffer->bytes + 1);
+    }
+
   struct dev_stream devstr;
   struct cras_audio_area *area;
   struct cras_audio_area *stream_area;
   int16_t cap_buf[kBufferFrames * 2];
+  struct cras_rstream rstream_;
+};
+
+TEST_F(CreateSuite, CaptureNoSRC) {
   float software_gain_scaler = 10;
 
-  devstr.stream = &rstream_;
-  devstr.conv = NULL;
-  devstr.conv_buffer = NULL;
-  devstr.conv_buffer_size_frames = 0;
-
-  area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
-                                               2 * sizeof(*area->channels));
-  area->num_channels = 2;
-  channel_area_set_channel(&area->channels[0], CRAS_CH_FL);
-  channel_area_set_channel(&area->channels[1], CRAS_CH_FR);
-  area->channels[0].step_bytes = 4;
-  area->channels[0].buf = (uint8_t *)(cap_buf);
-  area->channels[1].step_bytes = 4;
-  area->channels[1].buf = (uint8_t *)(cap_buf + 1);
-
-  stream_area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
-                                                  2 * sizeof(*area->channels));
-  stream_area->num_channels = 2;
-  rstream_.audio_area = stream_area;
-  int16_t *shm_samples = (int16_t *)rstream_.shm.area->samples;
-  stream_area->channels[0].step_bytes = 4;
-  stream_area->channels[0].buf = (uint8_t *)(shm_samples);
-  stream_area->channels[1].step_bytes = 4;
-  stream_area->channels[1].buf = (uint8_t *)(shm_samples + 1);
-
   dev_stream_capture(&devstr, area, 0, software_gain_scaler);
 
   EXPECT_EQ(stream_area, copy_area_call.dst);
@@ -195,109 +233,123 @@
   EXPECT_EQ(area, copy_area_call.src);
   EXPECT_EQ(software_gain_scaler, copy_area_call.software_gain_scaler);
 
-  free(area);
-  free(stream_area);
 }
 
-TEST_F(CreateSuite, CaptureSRC) {
-  struct dev_stream devstr;
-  struct cras_audio_area *area;
-  struct cras_audio_area *stream_area;
-  int16_t cap_buf[kBufferFrames * 2];
+TEST_F(CreateSuite, CaptureSRCSmallConverterBuffer) {
   float software_gain_scaler = 10;
+  unsigned int conv_buf_avail_at_input_rate;
+  int nread;
 
-  devstr.stream = &rstream_;
-  devstr.conv = (struct cras_fmt_conv *)0xdead;
-  devstr.conv_buffer =
-      (struct byte_buffer *)byte_buffer_create(kBufferFrames * 2 * 4);
-  devstr.conv_buffer_size_frames = kBufferFrames * 2;
+  SetUpFmtConv(44100, 32000, kBufferFrames / 4);
+  nread = dev_stream_capture(&devstr, area, 0, software_gain_scaler);
 
-  area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
-                                               2 * sizeof(*area->channels));
-  area->num_channels = 2;
-  channel_area_set_channel(&area->channels[0], CRAS_CH_FL);
-  channel_area_set_channel(&area->channels[1], CRAS_CH_FR);
-  area->channels[0].step_bytes = 4;
-  area->channels[0].buf = (uint8_t *)(cap_buf);
-  area->channels[1].step_bytes = 4;
-  area->channels[1].buf = (uint8_t *)(cap_buf + 1);
-  area->frames = kBufferFrames;
+  // |nread| is bound by small converter buffer size (kBufferFrames / 4)
+  conv_buf_avail_at_input_rate =
+      cras_frames_at_rate(out_fmt.frame_rate,
+                          (kBufferFrames / 4),
+                          in_fmt.frame_rate);
 
-  stream_area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
-                                                  2 * sizeof(*area->channels));
-  stream_area->num_channels = 2;
-  rstream_.audio_area = stream_area;
-  int16_t *shm_samples = (int16_t *)rstream_.shm.area->samples;
-  stream_area->channels[0].step_bytes = 4;
-  stream_area->channels[0].buf = (uint8_t *)(shm_samples);
-  stream_area->channels[1].step_bytes = 4;
-  stream_area->channels[1].buf = (uint8_t *)(shm_samples + 1);
-  rstream_.audio_area = stream_area;
-
-  devstr.conv_area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
-                                                  2 * sizeof(*area->channels));
-  devstr.conv_area->num_channels = 2;
-  devstr.conv_area->channels[0].step_bytes = 4;
-  devstr.conv_area->channels[0].buf = (uint8_t *)(devstr.conv_buffer->bytes);
-  devstr.conv_area->channels[1].step_bytes = 4;
-  devstr.conv_area->channels[1].buf =
-      (uint8_t *)(devstr.conv_buffer->bytes + 1);
-
-  conv_frames_ret = kBufferFrames / 2;
-  cras_fmt_conv_convert_frames_in_frames_val = kBufferFrames;
-  cras_fmt_conversion_needed_val = 1;
-  dev_stream_capture(&devstr, area, 0, software_gain_scaler);
-
+  EXPECT_EQ(conv_buf_avail_at_input_rate, nread);
   EXPECT_EQ((struct cras_fmt_conv *)0xdead, conv_frames_call.conv);
   EXPECT_EQ((uint8_t *)cap_buf, conv_frames_call.in_buf);
   EXPECT_EQ(devstr.conv_buffer->bytes, conv_frames_call.out_buf);
-  EXPECT_EQ(kBufferFrames, conv_frames_call.in_frames);
+
+  EXPECT_EQ(conv_buf_avail_at_input_rate, conv_frames_call.in_frames);
+
+  // Expect number of output frames is limited by the size of converter buffer.
+  EXPECT_EQ(kBufferFrames / 4, conv_frames_call.out_frames);
+
+  EXPECT_EQ(stream_area, copy_area_call.dst);
+  EXPECT_EQ(0, copy_area_call.dst_offset);
+  EXPECT_EQ(4, copy_area_call.dst_format_bytes);
+  EXPECT_EQ(devstr.conv_area, copy_area_call.src);
+  EXPECT_EQ(software_gain_scaler, copy_area_call.software_gain_scaler);
+
+  free(devstr.conv_area);
+  byte_buffer_destroy(&devstr.conv_buffer);
+}
+
+TEST_F(CreateSuite, CaptureSRCLargeConverterBuffer) {
+  float software_gain_scaler = 10;
+  unsigned int stream_avail_at_input_rate;
+  int nread;
+
+  SetUpFmtConv(44100, 32000, kBufferFrames * 2);
+  nread = dev_stream_capture(&devstr, area, 0, software_gain_scaler);
+
+  // Available frames at stream side is bound by cb_threshold, which
+  // equals to kBufferFrames / 2.
+  stream_avail_at_input_rate =
+      cras_frames_at_rate(out_fmt.frame_rate,
+                          (kBufferFrames / 2),
+                          in_fmt.frame_rate);
+
+  EXPECT_EQ(stream_avail_at_input_rate, nread);
+  EXPECT_EQ((struct cras_fmt_conv *)0xdead, conv_frames_call.conv);
+  EXPECT_EQ((uint8_t *)cap_buf, conv_frames_call.in_buf);
+  EXPECT_EQ(devstr.conv_buffer->bytes, conv_frames_call.out_buf);
+
+  // Expect number of input frames is limited by |stream_avail_at_input_rate|
+  // at format conversion.
+  EXPECT_EQ(stream_avail_at_input_rate, conv_frames_call.in_frames);
+
+  // Expect number of output frames is limited by the size of converter buffer.
   EXPECT_EQ(kBufferFrames * 2, conv_frames_call.out_frames);
 
   EXPECT_EQ(stream_area, copy_area_call.dst);
   EXPECT_EQ(0, copy_area_call.dst_offset);
   EXPECT_EQ(4, copy_area_call.dst_format_bytes);
   EXPECT_EQ(devstr.conv_area, copy_area_call.src);
-  EXPECT_EQ(conv_frames_ret, devstr.conv_area->frames);
   EXPECT_EQ(software_gain_scaler, copy_area_call.software_gain_scaler);
 
-  free(area);
-  free(stream_area);
   free(devstr.conv_area);
+  byte_buffer_destroy(&devstr.conv_buffer);
 }
 
 TEST_F(CreateSuite, CreateSRC44to48) {
   struct dev_stream *dev_stream;
 
   rstream_.format = fmt_s16le_44_1;
-  in_fmt.frame_rate = 44100;
-  out_fmt.frame_rate = 48000;
+  in_fmt.frame_rate = 44100;  // Input to converter is stream rate.
+  out_fmt.frame_rate = 48000;  // Output from converter is device rate.
   config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
   dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
                                  &cb_ts);
   EXPECT_EQ(1, config_format_converter_called);
   EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
-  EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
-                                out_fmt.frame_rate),
-            dev_stream->conv_buffer_size_frames);
+  // Converter tmp and output buffers are large enough for device output.
+  unsigned int device_frames =
+      cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
+                          out_fmt.frame_rate);
+  EXPECT_LE(kBufferFrames, device_frames); // Sanity check.
+  EXPECT_LE(device_frames, config_format_converter_frames);
+  EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
 }
 
-TEST_F(CreateSuite, CreateSRC44to48Input) {
+TEST_F(CreateSuite, CreateSRC44from48Input) {
   struct dev_stream *dev_stream;
+  struct cras_audio_format processed_fmt = fmt_s16le_48;
 
+  processed_fmt.num_channels = 1;
   rstream_.format = fmt_s16le_44_1;
   rstream_.direction = CRAS_STREAM_INPUT;
-  in_fmt.frame_rate = 48000;
-  out_fmt.frame_rate = 44100;
+  in_fmt.frame_rate = 48000;  // Input to converter is device rate.
+  out_fmt.frame_rate = 44100;  // Output from converter is stream rate.
   config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
+  cras_rstream_post_processing_format_val = &processed_fmt;
   dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
                                  &cb_ts);
   EXPECT_EQ(1, config_format_converter_called);
   EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
-  EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
-                                out_fmt.frame_rate),
-            dev_stream->conv_buffer_size_frames);
+  // Converter tmp and output buffers are large enough for device input.
+  unsigned int device_frames =
+      cras_frames_at_rate(out_fmt.frame_rate, kBufferFrames,
+                          in_fmt.frame_rate);
+  EXPECT_LE(kBufferFrames, device_frames); // Sanity check.
+  EXPECT_LE(device_frames, config_format_converter_frames);
+  EXPECT_EQ(&processed_fmt, config_format_converter_from_fmt);
+  EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
 }
 
@@ -305,52 +357,130 @@
   struct dev_stream *dev_stream;
 
   rstream_.format = fmt_s16le_48;
-  in_fmt.frame_rate = 48000;
-  out_fmt.frame_rate = 44100;
+  in_fmt.frame_rate = 48000;  // Stream rate.
+  out_fmt.frame_rate = 44100;  // Device rate.
   config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
   dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
                                  &cb_ts);
   EXPECT_EQ(1, config_format_converter_called);
   EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
-  EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
-                                out_fmt.frame_rate),
-            dev_stream->conv_buffer_size_frames);
+  // Converter tmp and output buffers are large enough for stream input.
+  EXPECT_LE(kBufferFrames, config_format_converter_frames);
+  EXPECT_LE(kBufferFrames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
 }
 
-TEST_F(CreateSuite, CreateSRC48to44Input) {
+TEST_F(CreateSuite, CreateSRC48from44Input) {
   struct dev_stream *dev_stream;
 
   rstream_.format = fmt_s16le_48;
   rstream_.direction = CRAS_STREAM_INPUT;
-  in_fmt.frame_rate = 44100;
-  out_fmt.frame_rate = 48000;
+  in_fmt.frame_rate = 44100;  // Device rate.
+  out_fmt.frame_rate = 48000;  // Stream rate.
   config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
   dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
                                  &cb_ts);
   EXPECT_EQ(1, config_format_converter_called);
   EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
-  EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
-                                out_fmt.frame_rate),
-            dev_stream->conv_buffer_size_frames);
+  // Converter tmp and output buffers are large enough for stream output.
+  EXPECT_LE(kBufferFrames, config_format_converter_frames);
+  EXPECT_LE(kBufferFrames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
 }
 
-TEST_F(CreateSuite, CreateSRC48to44StereoToMono) {
+TEST_F(CreateSuite, CreateSRC8to48) {
+  struct dev_stream *dev_stream;
+
+  rstream_.format = fmt_s16le_8;
+  in_fmt.frame_rate = 8000;  // Stream rate.
+  out_fmt.frame_rate = 48000;  // Device rate.
+  config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
+  dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
+                                 &cb_ts);
+  EXPECT_EQ(1, config_format_converter_called);
+  EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
+  // Converter tmp and output buffers are large enough for device output.
+  unsigned int device_frames =
+      cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
+                          out_fmt.frame_rate);
+  EXPECT_LE(kBufferFrames, device_frames); // Sanity check.
+  EXPECT_LE(device_frames, config_format_converter_frames);
+  EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
+  dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, CreateSRC8from48Input) {
+  struct dev_stream *dev_stream;
+
+  rstream_.format = fmt_s16le_8;
+  rstream_.direction = CRAS_STREAM_INPUT;
+  in_fmt.frame_rate = 48000;  // Device rate.
+  out_fmt.frame_rate = 8000;  // Stream rate.
+  config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
+  dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
+                                 &cb_ts);
+  EXPECT_EQ(1, config_format_converter_called);
+  EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
+  // Converter tmp and output buffers are large enough for device input.
+  unsigned int device_frames =
+      cras_frames_at_rate(out_fmt.frame_rate, kBufferFrames,
+                          in_fmt.frame_rate);
+  EXPECT_LE(kBufferFrames, device_frames); // Sanity check.
+  EXPECT_LE(device_frames, config_format_converter_frames);
+  EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
+  dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, CreateSRC48to8) {
+  struct dev_stream *dev_stream;
+
+  rstream_.format = fmt_s16le_48;
+  in_fmt.frame_rate = 48000;  // Stream rate.
+  out_fmt.frame_rate = 8000;  // Device rate.
+  config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
+  dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void *)0x55,
+                                 &cb_ts);
+  EXPECT_EQ(1, config_format_converter_called);
+  EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
+  // Converter tmp and output buffers are large enough for stream input.
+  EXPECT_LE(kBufferFrames, config_format_converter_frames);
+  EXPECT_LE(kBufferFrames, dev_stream->conv_buffer_size_frames);
+  dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, CreateSRC48from8Input) {
+  struct dev_stream *dev_stream;
+
+  rstream_.format = fmt_s16le_48;
+  rstream_.direction = CRAS_STREAM_INPUT;
+  in_fmt.frame_rate = 8000;  // Device rate.
+  out_fmt.frame_rate = 48000;  // Stream rate.
+  config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
+  dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void *)0x55,
+                                 &cb_ts);
+  EXPECT_EQ(1, config_format_converter_called);
+  EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
+  // Converter tmp and output buffers are large enough for stream output.
+  EXPECT_LE(kBufferFrames, config_format_converter_frames);
+  EXPECT_LE(kBufferFrames, dev_stream->conv_buffer_size_frames);
+  dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, CreateSRC48MonoFrom44StereoInput) {
   struct dev_stream *dev_stream;
 
   rstream_.format = fmt_s16le_48_mono;
   rstream_.direction = CRAS_STREAM_INPUT;
-  in_fmt.frame_rate = 44100;
-  out_fmt.frame_rate = 48000;
+  in_fmt.frame_rate = 44100;  // Device rate.
+  out_fmt.frame_rate = 48000;  // Stream rate.
   config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
   dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
                                  &cb_ts);
   EXPECT_EQ(1, config_format_converter_called);
   EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
-  EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
-                                out_fmt.frame_rate),
-            dev_stream->conv_buffer_size_frames);
+  // Converter tmp and output buffers are large enough for stream output.
+  EXPECT_LE(kBufferFrames, config_format_converter_frames);
+  EXPECT_LE(kBufferFrames, dev_stream->conv_buffer_size_frames);
   EXPECT_EQ(dev_stream->conv_buffer_size_frames * 4,
             dev_stream->conv_buffer->max_size);
   EXPECT_EQ(2, cras_audio_area_create_num_channels_val);
@@ -512,6 +642,35 @@
   EXPECT_EQ(2, rstream_get_readable_call.num_called);
 }
 
+TEST_F(CreateSuite, DevStreamFlushAudioMessages) {
+  struct dev_stream *dev_stream;
+  unsigned int dev_id = 9;
+
+  dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+                                 (void *)0x55, &cb_ts);
+
+  dev_stream_flush_old_audio_messages(dev_stream);
+  EXPECT_EQ(1, cras_rstream_flush_old_audio_messages_called);
+  dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, DevStreamIsPending) {
+  struct dev_stream *dev_stream;
+  unsigned int dev_id = 9;
+
+  dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+                                 (void *)0x55, &cb_ts);
+
+  // dev_stream_is_pending_reply is only a wrapper.
+  cras_rstream_is_pending_reply_ret = 0;
+  EXPECT_EQ(0, dev_stream_is_pending_reply(dev_stream));
+
+  cras_rstream_is_pending_reply_ret = 1;
+  EXPECT_EQ(1, dev_stream_is_pending_reply(dev_stream));
+
+  dev_stream_destroy(dev_stream);
+}
+
 TEST_F(CreateSuite, StreamCanFetch) {
   struct dev_stream *dev_stream;
   unsigned int dev_id = 9;
@@ -520,16 +679,17 @@
                                  (void *)0x55, &cb_ts);
 
   /* Verify stream cannot fetch when it's still pending. */
-  cras_shm_set_callback_pending(&rstream_.shm, 1);
+  cras_rstream_is_pending_reply_ret = 1;
   EXPECT_EQ(0, dev_stream_can_fetch(dev_stream));
 
   /* Verify stream can fetch when buffer available. */
-  cras_shm_set_callback_pending(&rstream_.shm, 0);
+  cras_rstream_is_pending_reply_ret = 0;
   rstream_.shm.area->write_offset[0] = 0;
   rstream_.shm.area->write_offset[1] = 0;
   EXPECT_EQ(1, dev_stream_can_fetch(dev_stream));
 
   /* Verify stream cannot fetch when there's still buffer. */
+  cras_rstream_is_pending_reply_ret = 0;
   rstream_.shm.area->write_offset[0] = kBufferFrames;
   rstream_.shm.area->write_offset[1] = kBufferFrames;
   EXPECT_EQ(0, dev_stream_can_fetch(dev_stream));
@@ -707,6 +867,33 @@
   dev_stream_destroy(dev_stream);
 }
 
+TEST_F(CreateSuite, TriggerOnlyStreamSendOnlyOnce) {
+  struct dev_stream *dev_stream;
+  unsigned int dev_id = 9;
+
+  rstream_.direction = CRAS_STREAM_INPUT;
+  dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+                                 (void *) 0x55, &cb_ts);
+  dev_stream->stream->flags = TRIGGER_ONLY;
+  dev_stream->stream->triggered = 0;
+
+  // Check first trigger callback called.
+  cras_shm_buffer_written(&rstream_.shm, rstream_.cb_threshold);
+  clock_gettime_retspec.tv_sec = 1;
+  clock_gettime_retspec.tv_nsec = 0;
+  dev_stream_capture_update_rstream(dev_stream);
+  EXPECT_EQ(1, cras_rstream_audio_ready_called);
+  EXPECT_EQ(1, dev_stream->stream->triggered);
+
+  // No future callback will be called for TRIGGER_ONLY streams.
+  cras_shm_buffer_written(&rstream_.shm, rstream_.cb_threshold);
+  clock_gettime_retspec.tv_sec = 2;
+  clock_gettime_retspec.tv_nsec = 0;
+  dev_stream_capture_update_rstream(dev_stream);
+  EXPECT_EQ(1, cras_rstream_audio_ready_called);
+  dev_stream_destroy(dev_stream);
+}
+
 TEST_F(CreateSuite, InputDevStreamWakeTimeByNextCbTs) {
   struct dev_stream *dev_stream;
   unsigned int dev_id = 9;
@@ -729,12 +916,14 @@
   cras_shm_buffer_written(&rstream_.shm, written_frames);
 
   rc = dev_stream_wake_time(dev_stream, curr_level,
-                            &level_tstamp, &wake_time_out);
+                            &level_tstamp, rstream_.cb_threshold, 0,
+                            &wake_time_out);
 
   // The next wake up time is determined by next_cb_ts on dev_stream.
   EXPECT_EQ(rstream_.next_cb_ts.tv_sec, wake_time_out.tv_sec);
   EXPECT_EQ(rstream_.next_cb_ts.tv_nsec, wake_time_out.tv_nsec);
   EXPECT_EQ(0, rc);
+  dev_stream_destroy(dev_stream);
 }
 
 TEST_F(CreateSuite, InputDevStreamWakeTimeByDevice) {
@@ -789,7 +978,8 @@
   in_fmt.frame_rate = 48000;
 
   rc = dev_stream_wake_time(dev_stream, curr_level,
-                            &level_tstamp, &wake_time_out);
+                            &level_tstamp, rstream_.cb_threshold, 0,
+                            &wake_time_out);
 
   // The next wake up time is determined by needed time for device level
   // to reach enough samples for one cb_threshold.
@@ -801,10 +991,12 @@
   // The wake up time is determined by next_cb_ts.
   curr_level += rstream_.cb_threshold;
   rc = dev_stream_wake_time(dev_stream, curr_level,
-                            &level_tstamp, &wake_time_out);
+                            &level_tstamp, rstream_.cb_threshold, 0,
+                            &wake_time_out);
   EXPECT_EQ(rstream_.next_cb_ts.tv_sec, wake_time_out.tv_sec);
   EXPECT_EQ(rstream_.next_cb_ts.tv_nsec, wake_time_out.tv_nsec);
   EXPECT_EQ(0, rc);
+  dev_stream_destroy(dev_stream);
 }
 
 //  Test set_playback_timestamp.
@@ -875,6 +1067,18 @@
   EXPECT_LE(ts.tv_nsec, 250100000);
 }
 
+TEST(MaxFramesForConverter, 8to48) {
+  EXPECT_EQ(481, max_frames_for_conversion(80,  // Stream frames.
+                                           8000,  // Stream rate.
+                                           48000));  // Device rate.
+}
+
+TEST(MaxFramesForConverter, 48to8) {
+  EXPECT_EQ(81, max_frames_for_conversion(80,  // Stream frames.
+                                          48000,  // Stream rate.
+                                          8000));  // Device rate.
+}
+
 /* Stubs */
 extern "C" {
 
@@ -940,17 +1144,24 @@
 int cras_rstream_get_mute(const struct cras_rstream *rstream) {
   return 0;
 }
-
 void cras_rstream_update_queued_frames(struct cras_rstream *rstream)
 {
 }
 
+struct cras_audio_format *cras_rstream_post_processing_format(
+    const struct cras_rstream *stream, void *dev_ptr)
+{
+  return cras_rstream_post_processing_format_val;
+}
+
 int config_format_converter(struct cras_fmt_conv **conv,
 			    enum CRAS_STREAM_DIRECTION dir,
 			    const struct cras_audio_format *from,
 			    const struct cras_audio_format *to,
 			    unsigned int frames) {
   config_format_converter_called++;
+  config_format_converter_from_fmt = from;
+  config_format_converter_frames = frames;
   *conv = config_format_converter_conv;
   return 0;
 }
@@ -963,14 +1174,23 @@
 				    uint8_t *out_buf,
 				    unsigned int *in_frames,
 				    unsigned int out_frames) {
+  unsigned int ret;
   conv_frames_call.conv = conv;
   conv_frames_call.in_buf = in_buf;
   conv_frames_call.out_buf = out_buf;
   conv_frames_call.in_frames = *in_frames;
-  *in_frames = cras_fmt_conv_convert_frames_in_frames_val;
+  ret = cras_frames_at_rate(in_fmt.frame_rate,
+                            *in_frames,
+                            out_fmt.frame_rate);
   conv_frames_call.out_frames = out_frames;
+  if (ret > out_frames) {
+    ret = out_frames;
+    *in_frames = cras_frames_at_rate(
+      out_fmt.frame_rate,
+      ret, in_fmt.frame_rate);
+  }
 
-  return conv_frames_ret;
+  return ret;
 }
 
 void cras_mix_add(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
@@ -1055,6 +1275,17 @@
   cras_fmt_conv_set_linear_resample_rates_called++;
 }
 
+int cras_rstream_is_pending_reply(const struct cras_rstream *stream)
+{
+  return cras_rstream_is_pending_reply_ret;
+}
+
+int cras_rstream_flush_old_audio_messages(struct  cras_rstream *stream)
+{
+  cras_rstream_flush_old_audio_messages_called++;
+  return 0;
+}
+
 //  From librt.
 int clock_gettime(clockid_t clk_id, struct timespec *tp) {
   tp->tv_sec = clock_gettime_retspec.tv_sec;
diff --git a/cras/src/tests/device_monitor_unittest.cc b/cras/src/tests/device_monitor_unittest.cc
index 31913be..cc9e0d1 100644
--- a/cras/src/tests/device_monitor_unittest.cc
+++ b/cras/src/tests/device_monitor_unittest.cc
@@ -132,7 +132,7 @@
   enable_dev = dev;
 }
 
-void cras_iodev_list_disable_dev(struct cras_iodev *dev) {
+void cras_iodev_list_disable_dev(struct cras_iodev *dev, bool force) {
   disable_dev_called++;
   disable_dev = dev;
 }
diff --git a/cras/src/tests/dsp_core_unittest.cc b/cras/src/tests/dsp_core_unittest.cc
index 71b92eb..0957795 100644
--- a/cras/src/tests/dsp_core_unittest.cc
+++ b/cras/src/tests/dsp_core_unittest.cc
@@ -55,7 +55,8 @@
   float output[SAMPLES];
   float *out_ptr[] = {output, output + FRAMES};
 
-  dsp_util_deinterleave(input, out_ptr, 2, FRAMES);
+  dsp_util_deinterleave((uint8_t *)input, out_ptr, 2,
+			SND_PCM_FORMAT_S16_LE, FRAMES);
 
   for (int i = 0 ; i < SAMPLES; i++) {
     EXPECT_EQ(answer[i], output[i]);
@@ -68,7 +69,8 @@
   }
 
   int16_t output2[SAMPLES];
-  dsp_util_interleave(out_ptr, output2, 2, FRAMES);
+  dsp_util_interleave(out_ptr, (uint8_t *)output2, 2,
+		      SND_PCM_FORMAT_S16_LE, FRAMES);
   for (int i = 0 ; i < SAMPLES; i++) {
     EXPECT_EQ(input[i], output2[i]);
   }
diff --git a/cras/src/tests/dsp_unittest.cc b/cras/src/tests/dsp_unittest.cc
index 40fd9da..2202990 100644
--- a/cras/src/tests/dsp_unittest.cc
+++ b/cras/src/tests/dsp_unittest.cc
@@ -150,6 +150,10 @@
   empty_init_module(module);
   return module;
 }
+void cras_dsp_module_set_sink_ext_module(struct dsp_module *module,
+					 struct ext_dsp_module *ext_module)
+{
+}
 } // extern "C"
 
 int main(int argc, char **argv) {
diff --git a/cras/src/tests/empty_audio_stub.cc b/cras/src/tests/empty_audio_stub.cc
new file mode 100644
index 0000000..e15af00
--- /dev/null
+++ b/cras/src/tests/empty_audio_stub.cc
@@ -0,0 +1,24 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+extern "C" {
+#include "polled_interval_checker.h"
+
+struct polled_interval *pic_polled_interval_create(int interval_sec) {
+	return NULL;
+}
+
+int pic_interval_elapsed(const struct polled_interval *interval) {
+	return 0;
+}
+
+void pic_interval_reset(struct polled_interval *interval) {}
+
+void pic_polled_interval_destroy(struct polled_interval **interval) {}
+
+void pic_update_current_time() {}
+
+void cras_non_empty_audio_send_msg(int non_empty) {}
+}
diff --git a/cras/src/tests/float_buffer_unittest.cc b/cras/src/tests/float_buffer_unittest.cc
new file mode 100644
index 0000000..20d392e
--- /dev/null
+++ b/cras/src/tests/float_buffer_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+
+#include "float_buffer.h"
+
+namespace {
+TEST(FloatBuffer, ReadWrite) {
+  unsigned int readable = 10;
+  struct float_buffer *b = float_buffer_create(10, 2);
+  EXPECT_EQ(10, float_buffer_writable(b));
+
+  // (w, r)=(8, 0)
+  float_buffer_written(b, 8);
+  EXPECT_EQ(8, float_buffer_level(b));
+
+  float_buffer_read_pointer(b, 0, &readable);
+  EXPECT_EQ(8, readable);
+  EXPECT_EQ(2, float_buffer_writable(b));
+
+  readable = 10;
+  float_buffer_read_pointer(b, 3, &readable);
+  EXPECT_EQ(5, readable);
+
+  // (w, r)=(8, 6)
+  float_buffer_read(b, 6);
+  EXPECT_EQ(2, float_buffer_writable(b));
+
+  // (w, r)=(0, 6)
+  float_buffer_written(b, 2);
+  EXPECT_EQ(6, float_buffer_writable(b));
+
+  // (w, r)=(3, 6)
+  readable = 10;
+  float_buffer_written(b, 3);
+  float_buffer_read_pointer(b, 0, &readable);
+  EXPECT_EQ(4, readable);
+
+  readable = 10;
+  float_buffer_read_pointer(b, 1, &readable);
+  EXPECT_EQ(3, readable);
+
+  float_buffer_destroy(&b);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/fmt_conv_unittest.cc b/cras/src/tests/fmt_conv_unittest.cc
index a0b6194..08bb243 100644
--- a/cras/src/tests/fmt_conv_unittest.cc
+++ b/cras/src/tests/fmt_conv_unittest.cc
@@ -16,8 +16,12 @@
   {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
 static int surround_channel_layout[CRAS_CH_MAX] =
 	{0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1};
+static int quad_channel_layout[CRAS_CH_MAX] =
+	{0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1};
 static int linear_resampler_needed_val;
 static double linear_resampler_ratio = 1.0;
+static unsigned int linear_resampler_num_channels;
+static unsigned int linear_resampler_format_bytes;
 
 void ResetStub() {
   linear_resampler_needed_val = 0;
@@ -40,6 +44,58 @@
 	layout[b] = tmp;
 }
 
+TEST(FormatConverterTest, SmallFramesSRCWithLinearResampler) {
+  struct cras_audio_format in_fmt;
+  struct cras_audio_format out_fmt;
+  struct cras_fmt_conv *c;
+  int16_t *in_buf;
+  int16_t *out_buf;
+  unsigned int in_frames = 1;
+  unsigned int out_frames = 2;
+
+  ResetStub();
+  in_fmt.format = out_fmt.format = SND_PCM_FORMAT_S16_LE;
+  in_fmt.num_channels = out_fmt.num_channels = 1;
+  in_fmt.frame_rate = 16000;
+  out_fmt.frame_rate = 48000;
+  linear_resampler_needed_val = 1;
+
+  in_buf = (int16_t *)malloc(10 * 2 * 2);
+  out_buf = (int16_t *)malloc(10 * 2 * 2);
+
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, 10, 1);
+  EXPECT_NE((void *)NULL, c);
+
+  /* When process on small buffers doing SRC 16KHz -> 48KHz,
+   * speex does the work in two steps:
+   *
+   * (1) 0 -> 2 frames in output
+   * (2) 1 -> 1 frame in output
+   *
+   * Total result is 1 frame consumed in input and generated
+   * 3 frames in output.
+   */
+  out_frames = cras_fmt_conv_convert_frames(c, (uint8_t *)in_buf,
+                               (uint8_t *)out_buf,
+                               &in_frames,
+                               out_frames);
+  EXPECT_EQ(2, out_frames);
+  EXPECT_EQ(0, in_frames);
+
+  in_frames = 1;
+  out_frames = 2;
+  out_frames = cras_fmt_conv_convert_frames(c, (uint8_t *)in_buf,
+                               (uint8_t *)out_buf,
+                               &in_frames,
+                               out_frames);
+  EXPECT_EQ(1, out_frames);
+  EXPECT_EQ(1, in_frames);
+
+  cras_fmt_conv_destroy(&c);
+  free(in_buf);
+  free(out_buf);
+}
+
 // Only support LE, BE should fail.
 TEST(FormatConverterTest,  InvalidParamsOnlyLE) {
   struct cras_audio_format in_fmt;
@@ -98,7 +154,7 @@
     }
   }
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -149,7 +205,7 @@
     EXPECT_EQ(1, out_buff[i]);
   }
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -208,7 +264,7 @@
 	    EXPECT_EQ(0x10000, out_buff[i]);
     }
 
-    cras_fmt_conv_destroy(c);
+    cras_fmt_conv_destroy(&c);
     free(in_buff);
     free(out_buff);
   }
@@ -268,6 +324,7 @@
   EXPECT_EQ(buf_size, out_frames);
   for (i = 0; i < buf_size; i++)
     EXPECT_LT(0, out_buff[i * 2]);
+  cras_fmt_conv_destroy(&c);
 
   /* Swap channel to FR = 13450, RR = -100.
    * Assert left channel is silent.
@@ -283,6 +340,7 @@
   EXPECT_EQ(buf_size, out_frames);
   for (i = 0; i < buf_size; i++)
     EXPECT_LT(0, out_buff[i * 2 + 1]);
+  cras_fmt_conv_destroy(&c);
 
   /* Swap channel to FC = 13450, LFE = -100.
    * Assert output left and right has equal magnitude.
@@ -300,6 +358,7 @@
     EXPECT_NE(0, out_buff[i * 2]);
     EXPECT_EQ(out_buff[i * 2], out_buff[i * 2 + 1]);
   }
+  cras_fmt_conv_destroy(&c);
 
   /* Swap channel to FR = 13450, FL = -100.
    * Assert output left is positive and right is negative. */
@@ -317,7 +376,123 @@
     EXPECT_GT(0, out_buff[i * 2 + 1]);
   }
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
+  free(in_buff);
+  free(out_buff);
+}
+
+// Test Quad to Stereo mix.
+TEST(FormatConverterTest, QuadToStereo) {
+  struct cras_fmt_conv *c;
+  struct cras_audio_format in_fmt;
+  struct cras_audio_format out_fmt;
+
+  size_t out_frames;
+  int16_t *in_buff;
+  int16_t *out_buff;
+  unsigned int i;
+  const size_t buf_size = 4096;
+  unsigned int in_buf_size = 4096;
+
+  ResetStub();
+  in_fmt.format = SND_PCM_FORMAT_S16_LE;
+  out_fmt.format = SND_PCM_FORMAT_S16_LE;
+  in_fmt.num_channels = 4;
+  out_fmt.num_channels = 2;
+  in_fmt.frame_rate = 48000;
+  out_fmt.frame_rate = 48000;
+  for (i = 0; i < CRAS_CH_MAX; i++)
+    in_fmt.channel_layout[i] = quad_channel_layout[i];
+
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  ASSERT_NE(c, (void *)NULL);
+
+  out_frames = cras_fmt_conv_out_frames_to_in(c, buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+
+  out_frames = cras_fmt_conv_in_frames_to_out(c, buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+
+  in_buff = (int16_t *)malloc(buf_size * 2 * cras_get_format_bytes(&in_fmt));
+
+  /*
+   * Set left channel positive, right channel negative, assert values are
+   * copied and scaled as expected.
+   */
+  for (i = 0; i < buf_size; i++) {
+    in_buff[i * 4] = 800;
+    in_buff[i * 4 + 1] = -800;
+    in_buff[i * 4 + 2] = 80;
+    in_buff[i * 4 + 3] = -80;
+  }
+  out_buff = (int16_t *)malloc(buf_size * 2 * cras_get_format_bytes(&out_fmt));
+
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (i = 0; i < buf_size; i++) {
+    EXPECT_EQ(820, out_buff[i * 2]);
+    EXPECT_EQ(-820, out_buff[i * 2 + 1]);
+  }
+  cras_fmt_conv_destroy(&c);
+
+  /*
+   * Swap left and right channels, check channel map is respected.
+   */
+  swap_channel_layout(in_fmt.channel_layout, CRAS_CH_FL, CRAS_CH_FR);
+  swap_channel_layout(in_fmt.channel_layout, CRAS_CH_RL, CRAS_CH_RR);
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (i = 0; i < buf_size; i++) {
+    EXPECT_EQ(-820, out_buff[i * 2]);
+    EXPECT_EQ(820, out_buff[i * 2 + 1]);
+  }
+  cras_fmt_conv_destroy(&c);
+
+  /*
+   * Swap front and rear, check channel map is respected.
+   */
+  swap_channel_layout(in_fmt.channel_layout, CRAS_CH_FR, CRAS_CH_RR);
+  swap_channel_layout(in_fmt.channel_layout, CRAS_CH_FL, CRAS_CH_RL);
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (i = 0; i < buf_size; i++) {
+    EXPECT_EQ(-280, out_buff[i * 2]);
+    EXPECT_EQ(280, out_buff[i * 2 + 1]);
+  }
+  cras_fmt_conv_destroy(&c);
+
+  /*
+   * Empty channel map, check default behavior is applied.
+   */
+  for (i = 0; i < CRAS_CH_MAX; i++)
+    in_fmt.channel_layout[i] = -1;
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (i = 0; i < buf_size; i++) {
+    EXPECT_EQ(820, out_buff[i * 2]);
+    EXPECT_EQ(-820, out_buff[i * 2 + 1]);
+  }
+  cras_fmt_conv_destroy(&c);
+
   free(in_buff);
   free(out_buff);
 }
@@ -353,7 +528,7 @@
                                             (uint8_t *)out_buff,
                                             &in_buf_size,
                                             buf_size / 2);
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -388,7 +563,7 @@
                                             (uint8_t *)out_buff,
                                             &in_buf_size,
                                             buf_size * 2);
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -427,7 +602,7 @@
                                             (uint8_t *)out_buff,
                                             &in_buf_size,
                                             buf_size * 2);
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -468,7 +643,7 @@
   for (unsigned int i = 0; i < buf_size; i++)
     EXPECT_EQ((int16_t)(in_buff[i] >> 16), out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -509,7 +684,7 @@
   for (unsigned int i = 0; i < buf_size; i++)
     EXPECT_EQ((int16_t)(in_buff[i] >> 8), out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -549,9 +724,10 @@
                                             buf_size);
   EXPECT_EQ(buf_size, out_frames);
   for (unsigned int i = 0; i < buf_size; i++)
-    EXPECT_EQ(((int16_t)(in_buff[i] - 128) << 8), out_buff[i]);
+    EXPECT_EQ((int16_t)((uint16_t)((int16_t)(in_buff[i]) - 128) << 8),
+              out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -590,9 +766,9 @@
                                             buf_size);
   EXPECT_EQ(buf_size, out_frames);
   for (unsigned int i = 0; i < buf_size; i++)
-    EXPECT_EQ(((int32_t)in_buff[i] << 16), out_buff[i]);
+    EXPECT_EQ((int32_t)((uint32_t)(int32_t)in_buff[i] << 16), out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -631,9 +807,9 @@
                                             buf_size);
   EXPECT_EQ(buf_size, out_frames);
   for (unsigned int i = 0; i < buf_size; i++)
-    EXPECT_EQ(((int32_t)in_buff[i] << 8), out_buff[i]);
+    EXPECT_EQ((int32_t)((uint32_t)(int32_t)in_buff[i] << 8), out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -675,7 +851,7 @@
   for (unsigned int i = 0; i < buf_size; i++)
     EXPECT_EQ((in_buff[i] >> 8) + 128, out_buff[i]);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -718,7 +894,7 @@
                                             buf_size);
   EXPECT_EQ(buf_size, out_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -770,7 +946,7 @@
     EXPECT_EQ(0, out_buff[6 * i + 5]);
   }
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -822,7 +998,79 @@
     EXPECT_EQ(0, out_buff[6 * i + 5]);
   }
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
+  free(in_buff);
+  free(out_buff);
+}
+
+
+// Test 16 bit stereo to quad conversion.
+TEST(FormatConverterTest, ConvertS16LEToS16LEStereoToQuad) {
+  struct cras_fmt_conv *c;
+  struct cras_audio_format in_fmt;
+  struct cras_audio_format out_fmt;
+
+  size_t out_frames;
+  int16_t *in_buff;
+  int16_t *out_buff;
+  const size_t buf_size = 4096;
+  unsigned int in_buf_size = 4096;
+
+  ResetStub();
+  in_fmt.format = SND_PCM_FORMAT_S16_LE;
+  out_fmt.format = SND_PCM_FORMAT_S16_LE;
+  in_fmt.num_channels = 2;
+  out_fmt.num_channels = 4;
+  in_fmt.frame_rate = 48000;
+  out_fmt.frame_rate = 48000;
+  for (unsigned int i = 0; i < CRAS_CH_MAX; i++)
+    out_fmt.channel_layout[i] = quad_channel_layout[i];
+
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  ASSERT_NE(c, (void *)NULL);
+
+  out_frames = cras_fmt_conv_in_frames_to_out(c, buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+
+  in_buff = (int16_t *)malloc(buf_size * cras_get_format_bytes(&in_fmt));
+  for (unsigned int i = 0; i < in_buf_size; i++) {
+    in_buff[i * 2] = 40;
+    in_buff[i * 2 + 1] = 80;
+  }
+
+  out_buff = (int16_t *)malloc(buf_size * cras_get_format_bytes(&out_fmt));
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (unsigned int i = 0; i < buf_size; i++) {
+    EXPECT_EQ(40, out_buff[4 * i]);
+    EXPECT_EQ(80, out_buff[4 * i + 1]);
+    EXPECT_EQ(40, out_buff[4 * i + 2]);
+    EXPECT_EQ(80, out_buff[4 * i + 3]);
+  }
+  cras_fmt_conv_destroy(&c);
+
+  // Swap channels and check channel layout is respected.
+  swap_channel_layout(out_fmt.channel_layout, CRAS_CH_FL, CRAS_CH_RR);
+  swap_channel_layout(out_fmt.channel_layout, CRAS_CH_RL, CRAS_CH_FR);
+  c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0);
+  out_frames = cras_fmt_conv_convert_frames(c,
+                                            (uint8_t *)in_buff,
+                                            (uint8_t *)out_buff,
+                                            &in_buf_size,
+                                            buf_size);
+  EXPECT_EQ(buf_size, out_frames);
+  for (unsigned int i = 0; i < buf_size; i++) {
+    EXPECT_EQ(80, out_buff[4 * i]);
+    EXPECT_EQ(40, out_buff[4 * i + 1]);
+    EXPECT_EQ(80, out_buff[4 * i + 2]);
+    EXPECT_EQ(40, out_buff[4 * i + 3]);
+  }
+
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -865,7 +1113,7 @@
                                             buf_size * 2);
   EXPECT_EQ(buf_size * 2, out_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -908,7 +1156,7 @@
                                             buf_size / 2);
   EXPECT_EQ(buf_size / 2, out_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -952,7 +1200,7 @@
                                             out_frames);
   EXPECT_EQ(out_frames, ret_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -997,7 +1245,7 @@
                                             out_frames - 1);
   EXPECT_EQ(out_frames - 1, ret_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -1042,7 +1290,7 @@
                                             out_frames - 2);
   EXPECT_EQ(out_frames - 2, ret_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -1091,7 +1339,7 @@
                                             out_frames);
   EXPECT_EQ(expected_fr, out_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -1140,7 +1388,7 @@
                                             buf_size);
   EXPECT_EQ(expected_fr, out_frames);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
   free(in_buff);
   free(out_buff);
 }
@@ -1167,7 +1415,7 @@
   config_format_converter(&c, CRAS_STREAM_OUTPUT, &in_fmt, &out_fmt, 4096);
   ASSERT_NE(c, (void *)NULL);
 
-  cras_fmt_conv_destroy(c);
+  cras_fmt_conv_destroy(&c);
 }
 
 // Test format converter not created when in/out format conversion is not
@@ -1193,6 +1441,7 @@
   config_format_converter(&c, CRAS_STREAM_OUTPUT, &in_fmt, &out_fmt, 4096);
   EXPECT_NE(c, (void *)NULL);
   EXPECT_EQ(0, cras_fmt_conversion_needed(c));
+  cras_fmt_conv_destroy(&c);
 }
 
 // Test format converter not created for input when in/out format differs
@@ -1220,6 +1469,7 @@
   config_format_converter(&c, CRAS_STREAM_INPUT, &in_fmt, &out_fmt, 4096);
   EXPECT_NE(c, (void *)NULL);
   EXPECT_EQ(0, cras_fmt_conversion_needed(c));
+  cras_fmt_conv_destroy(&c);
 }
 
 TEST(ChannelRemixTest, ChannelRemixAppliedOrNot) {
@@ -1252,7 +1502,9 @@
   for (i = 0; i < 100; i++)
     EXPECT_EQ(res[i],  buf[i]);
 
-  cras_fmt_conv_destroy(conv);
+  cras_fmt_conv_destroy(&conv);
+  free(buf);
+  free(res);
 }
 
 int main(int argc, char **argv) {
@@ -1288,6 +1540,8 @@
              float src_rate,
              float dst_rate)
 {
+  linear_resampler_format_bytes = format_bytes;
+  linear_resampler_num_channels = num_channels;
   return reinterpret_cast<struct linear_resampler*>(0x33);;
 }
 
@@ -1327,6 +1581,10 @@
     resampled_fr = dst_frames;
     *src_frames = dst_frames / linear_resampler_ratio;
   }
+  unsigned int resampled_bytes = resampled_fr * linear_resampler_format_bytes *
+                                 linear_resampler_num_channels;
+  for(size_t i = 0; i < resampled_bytes; i++)
+    dst[i] = (uint8_t)rand() & 0xff;
 
   return resampled_fr;
 }
diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc
index 202bb13..3e1dc2d 100644
--- a/cras/src/tests/hfp_iodev_unittest.cc
+++ b/cras/src/tests/hfp_iodev_unittest.cc
@@ -23,6 +23,7 @@
 static size_t cras_iodev_rm_node_called;
 static size_t cras_iodev_set_active_node_called;
 static size_t cras_iodev_free_format_called;
+static size_t cras_iodev_free_resources_called;
 static size_t cras_bt_device_sco_connect_called;
 static int cras_bt_transport_sco_connect_return_val;
 static size_t hfp_info_add_iodev_called;
@@ -46,6 +47,7 @@
   cras_iodev_rm_node_called = 0;
   cras_iodev_set_active_node_called = 0;
   cras_iodev_free_format_called = 0;
+  cras_iodev_free_resources_called = 0;
   cras_bt_device_sco_connect_called = 0;
   cras_bt_transport_sco_connect_return_val = 0;
   hfp_info_add_iodev_called = 0;
@@ -71,8 +73,19 @@
 
 namespace {
 
-TEST(HfpIodev, CreateHfpOutputIodev) {
-  ResetStubData();
+class HfpIodev: public testing::Test {
+  protected:
+    virtual void SetUp() {
+      ResetStubData();
+    }
+
+    virtual void TearDown() {
+      free(dummy_audio_area);
+      dummy_audio_area = NULL;
+    }
+};
+
+TEST_F(HfpIodev, CreateHfpOutputIodev) {
   iodev = hfp_iodev_create(CRAS_STREAM_OUTPUT, fake_device, fake_slc,
                            CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
                 		  	   fake_info);
@@ -86,10 +99,10 @@
 
   ASSERT_EQ(1, cras_bt_device_rm_iodev_called);
   ASSERT_EQ(1, cras_iodev_rm_node_called);
+  ASSERT_EQ(1, cras_iodev_free_resources_called);
 }
 
-TEST(HfpIodev, CreateHfpInputIodev) {
-  ResetStubData();
+TEST_F(HfpIodev, CreateHfpInputIodev) {
   iodev = hfp_iodev_create(CRAS_STREAM_INPUT, fake_device, fake_slc,
                            CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY, fake_info);
 
@@ -104,11 +117,10 @@
 
   ASSERT_EQ(1, cras_bt_device_rm_iodev_called);
   ASSERT_EQ(1, cras_iodev_rm_node_called);
+  ASSERT_EQ(1, cras_iodev_free_resources_called);
 }
 
-TEST(HfpIodev, OpenHfpIodev) {
-  ResetStubData();
-
+TEST_F(HfpIodev, OpenHfpIodev) {
   iodev = hfp_iodev_create(CRAS_STREAM_OUTPUT, fake_device, fake_slc,
                            CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
                            fake_info);
@@ -116,7 +128,7 @@
 
   /* hfp_info not start yet */
   hfp_info_running_return_val = 0;
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
 
   ASSERT_EQ(1, cras_bt_device_sco_connect_called);
   ASSERT_EQ(1, hfp_info_start_called);
@@ -126,14 +138,14 @@
   hfp_info_running_return_val = 1;
 
   iodev->close_dev(iodev);
+  hfp_iodev_destroy(iodev);
   ASSERT_EQ(1, hfp_info_rm_iodev_called);
   ASSERT_EQ(1, hfp_info_stop_called);
   ASSERT_EQ(1, cras_iodev_free_format_called);
+  ASSERT_EQ(1, cras_iodev_free_resources_called);
 }
 
-TEST(HfpIodev, OpenIodevWithHfpInfoAlreadyRunning) {
-  ResetStubData();
-
+TEST_F(HfpIodev, OpenIodevWithHfpInfoAlreadyRunning) {
   iodev = hfp_iodev_create(CRAS_STREAM_INPUT, fake_device, fake_slc,
                            CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
                            fake_info);
@@ -142,7 +154,7 @@
 
   /* hfp_info already started by another device */
   hfp_info_running_return_val = 1;
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
 
   ASSERT_EQ(0, cras_bt_device_sco_connect_called);
   ASSERT_EQ(0, hfp_info_start_called);
@@ -150,12 +162,14 @@
 
   hfp_info_has_iodev_return_val = 1;
   iodev->close_dev(iodev);
+  hfp_iodev_destroy(iodev);
   ASSERT_EQ(1, hfp_info_rm_iodev_called);
   ASSERT_EQ(0, hfp_info_stop_called);
   ASSERT_EQ(1, cras_iodev_free_format_called);
+  ASSERT_EQ(1, cras_iodev_free_resources_called);
 }
 
-TEST(HfpIodev, PutGetBuffer) {
+TEST_F(HfpIodev, PutGetBuffer) {
   cras_audio_area *area;
   unsigned frames;
 
@@ -164,7 +178,7 @@
                            CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
                   			   fake_info);
   iodev->format = &fake_format;
-  iodev->open_dev(iodev);
+  iodev->configure_dev(iodev);
 
   hfp_buf_acquire_return_val = 100;
   iodev->get_buffer(iodev, &area, &frames);
@@ -175,6 +189,8 @@
   iodev->put_buffer(iodev, 40);
   ASSERT_EQ(1, hfp_buf_release_called);
   ASSERT_EQ(40, hfp_buf_release_nwritten_val);
+  hfp_iodev_destroy(iodev);
+  ASSERT_EQ(1, cras_iodev_free_resources_called);
 }
 
 } // namespace
@@ -332,6 +348,10 @@
 void cras_iodev_free_audio_area(struct cras_iodev *iodev) {
 }
 
+void cras_iodev_free_resources(struct cras_iodev *iodev) {
+    cras_iodev_free_resources_called++;
+}
+
 void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
 					 const struct cras_audio_format *fmt,
 					 uint8_t *base_buffer)
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index 35b515d..26ebc8d 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -27,7 +27,6 @@
 
 /* Data for stubs. */
 static struct cras_observer_ops *observer_ops;
-static unsigned int cras_system_get_suspended_val;
 static int add_stream_called;
 static int rm_stream_called;
 static unsigned int set_node_attr_called;
@@ -37,14 +36,18 @@
 static cras_iodev *audio_thread_add_open_dev_dev;
 static int audio_thread_add_open_dev_called;
 static int audio_thread_rm_open_dev_called;
+static int audio_thread_is_dev_open_ret;
 static struct audio_thread thread;
 static struct cras_iodev loopback_input;
 static int cras_iodev_close_called;
 static struct cras_iodev *cras_iodev_close_dev;
+static struct cras_iodev dummy_hotword_iodev;
 static struct cras_iodev dummy_empty_iodev[2];
 static stream_callback *stream_add_cb;
 static stream_callback *stream_rm_cb;
 static struct cras_rstream *stream_list_get_ret;
+static int server_stream_create_called;
+static int server_stream_destroy_called;
 static int audio_thread_drain_stream_return;
 static int audio_thread_drain_stream_called;
 static int cras_tm_create_timer_called;
@@ -57,8 +60,10 @@
 static struct cras_iodev *device_disabled_dev;
 static int device_disabled_count;
 static void *device_enabled_cb_data;
+static void *device_disabled_cb_data;
 static struct cras_rstream *audio_thread_add_stream_stream;
 static struct cras_iodev *audio_thread_add_stream_dev;
+static struct cras_iodev *audio_thread_disconnect_stream_dev;
 static int audio_thread_add_stream_called;
 static unsigned update_active_node_called;
 static struct cras_iodev *update_active_node_iodev_val[5];
@@ -79,6 +84,9 @@
 static int audio_thread_dev_start_ramp_called;
 static enum CRAS_IODEV_RAMP_REQUEST audio_thread_dev_start_ramp_req ;
 static std::map<const struct cras_iodev*, enum CRAS_IODEV_STATE> cras_iodev_state_ret;
+static std::map<const struct cras_iodev*, int> cras_iodev_has_pinned_stream_ret;
+static struct cras_rstream *audio_thread_disconnect_stream_stream;
+static int audio_thread_disconnect_stream_called;
 static int cras_iodev_is_zero_volume_ret;
 
 void dummy_update_active_node(struct cras_iodev *iodev,
@@ -98,11 +106,18 @@
 
       cras_iodev_close_called = 0;
       stream_list_get_ret = 0;
+      server_stream_create_called = 0;
+      server_stream_destroy_called = 0;
       audio_thread_drain_stream_return = 0;
       audio_thread_drain_stream_called = 0;
       cras_tm_create_timer_called = 0;
       cras_tm_cancel_timer_called = 0;
 
+      audio_thread_disconnect_stream_called = 0;
+      audio_thread_disconnect_stream_stream = NULL;
+      audio_thread_is_dev_open_ret = 0;
+      cras_iodev_has_pinned_stream_ret.clear();
+
       sample_rates_[0] = 44100;
       sample_rates_[1] = 48000;
       sample_rates_[2] = 0;
@@ -199,6 +214,13 @@
       audio_thread_dev_start_ramp_req =
           CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
       cras_iodev_is_zero_volume_ret = 0;
+
+      dummy_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE;
+      dummy_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE;
+    }
+
+    virtual void TearDown() {
+      cras_iodev_list_reset();
     }
 
     static void set_volume_1(struct cras_iodev* iodev) {
@@ -274,14 +296,13 @@
   stream_add_cb(&rstream2);
   EXPECT_EQ(2, audio_thread_add_stream_called);
 
-  cras_system_get_suspended_val = 1;
   audio_thread_rm_open_dev_called = 0;
   observer_ops->suspend_changed(NULL, 1);
   EXPECT_EQ(1, audio_thread_rm_open_dev_called);
 
   /* Test disable/enable dev won't cause add_stream to audio_thread. */
   audio_thread_add_stream_called = 0;
-  cras_iodev_list_disable_dev(&d1_);
+  cras_iodev_list_disable_dev(&d1_, false);
   cras_iodev_list_enable_dev(&d1_);
   EXPECT_EQ(0, audio_thread_add_stream_called);
 
@@ -298,7 +319,6 @@
 
   audio_thread_add_open_dev_called = 0;
   audio_thread_add_stream_called = 0;
-  cras_system_get_suspended_val = 0;
   stream_list_get_ret = stream_list;
   observer_ops->suspend_changed(NULL, 0);
   EXPECT_EQ(1, audio_thread_add_open_dev_called);
@@ -333,6 +353,54 @@
   /* open dev called twice, one for fallback device. */
   EXPECT_EQ(2, cras_iodev_open_called);
   EXPECT_EQ(1, audio_thread_add_stream_called);
+  cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, InitDevWithEchoRef) {
+  int rc;
+  struct cras_rstream rstream;
+  struct cras_rstream *stream_list = NULL;
+
+  memset(&rstream, 0, sizeof(rstream));
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  d1_.echo_reference_dev = &d2_;
+  rc = cras_iodev_list_add_output(&d1_);
+  ASSERT_EQ(0, rc);
+
+  d2_.direction = CRAS_STREAM_INPUT;
+  snprintf(d2_.active_node->name, CRAS_NODE_NAME_BUFFER_SIZE, "echo ref");
+  rc = cras_iodev_list_add_input(&d2_);
+  ASSERT_EQ(0, rc);
+
+  cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+      cras_make_node_id(d1_.info.idx, 0));
+  /* No close call happened, because no stream exists. */
+  EXPECT_EQ(0, cras_iodev_close_called);
+
+  cras_iodev_open_ret[1] = 0;
+
+  DL_APPEND(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+  stream_add_cb(&rstream);
+
+  EXPECT_EQ(1, cras_iodev_open_called);
+  EXPECT_EQ(1, server_stream_create_called);
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+
+  DL_DELETE(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+  stream_rm_cb(&rstream);
+
+  clock_gettime_retspec.tv_sec = 11;
+  clock_gettime_retspec.tv_nsec = 0;
+  cras_tm_timer_cb(NULL, NULL);
+
+  EXPECT_EQ(1, cras_iodev_close_called);
+  EXPECT_EQ(1, server_stream_destroy_called);
+
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, SelectNodeOpenFailShouldScheduleRetry) {
@@ -411,6 +479,7 @@
   cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
       cras_make_node_id(d2_.info.idx, 1));
   EXPECT_EQ(1, cras_tm_cancel_timer_called);
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, InitDevFailShouldScheduleRetry) {
@@ -455,21 +524,59 @@
 
   cras_iodev_list_rm_output(&d1_);
   EXPECT_EQ(1, cras_tm_cancel_timer_called);
+  cras_iodev_list_deinit();
 }
 
-static void device_enabled_cb(struct cras_iodev *dev, int enabled,
-                              void *cb_data)
+TEST_F(IoDevTestSuite, PinnedStreamInitFailShouldScheduleRetry) {
+  int rc;
+  struct cras_rstream rstream;
+  struct cras_rstream *stream_list = NULL;
+
+  memset(&rstream, 0, sizeof(rstream));
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  rc = cras_iodev_list_add_output(&d1_);
+  ASSERT_EQ(0, rc);
+
+  rstream.is_pinned = 1;
+  rstream.pinned_dev_idx = d1_.info.idx;
+
+  cras_iodev_open_ret[0] = -5;
+  cras_iodev_open_ret[1] = 0;
+  cras_tm_timer_cb = NULL;
+  DL_APPEND(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+  stream_add_cb(&rstream);
+  /* Init pinned dev fail, not proceed to add stream. */
+  EXPECT_EQ(1, cras_iodev_open_called);
+  EXPECT_EQ(0, audio_thread_add_stream_called);
+
+  EXPECT_NE((void *)NULL, cras_tm_timer_cb);
+  EXPECT_EQ(1, cras_tm_create_timer_called);
+
+  cras_tm_timer_cb(NULL, cras_tm_timer_cb_data);
+  EXPECT_EQ(2, cras_iodev_open_called);
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+
+  cras_iodev_list_rm_output(&d1_);
+  cras_iodev_list_deinit();
+}
+
+static void device_enabled_cb(struct cras_iodev *dev, void *cb_data)
 {
-  if (enabled) {
-    device_enabled_dev = dev;
-    device_enabled_count++;
-  } else {
-    device_disabled_dev = dev;
-    device_disabled_count++;
-  }
+  device_enabled_dev = dev;
+  device_enabled_count++;
   device_enabled_cb_data = cb_data;
 }
 
+static void device_disabled_cb(struct cras_iodev *dev, void *cb_data)
+{
+  device_disabled_dev = dev;
+  device_disabled_count++;
+  device_disabled_cb_data = cb_data;
+}
+
 TEST_F(IoDevTestSuite, SelectNode) {
   struct cras_rstream rstream, rstream2;
   int rc;
@@ -496,7 +603,7 @@
   device_disabled_count = 0;
 
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
-      device_enabled_cb, (void *)0xABCD));
+      device_enabled_cb, device_disabled_cb, (void *)0xABCD));
 
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
       cras_make_node_id(d1_.info.idx, 1));
@@ -506,7 +613,8 @@
   EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
 
   // There should be a disable device call for the fallback device.
-  EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  // But no close call actually happened, because no stream exists.
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
   EXPECT_EQ(1, device_disabled_count);
   EXPECT_NE(&d1_, device_disabled_dev);
 
@@ -529,14 +637,15 @@
   EXPECT_EQ(3, device_enabled_count);
   // Additional disabled devices: d1_, fallback device.
   EXPECT_EQ(3, device_disabled_count);
-  EXPECT_EQ(3, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(2, audio_thread_rm_open_dev_called);
   EXPECT_EQ(2, cras_observer_notify_active_node_called);
   EXPECT_EQ(&d2_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
 
   // For each stream, the stream is added for fallback device and d2_.
   EXPECT_EQ(6, audio_thread_add_stream_called);
 
-  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
+  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL));
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, SelectPreviouslyEnabledNode) {
@@ -563,7 +672,7 @@
   device_disabled_count = 0;
 
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
-      device_enabled_cb, (void *)0xABCD));
+      device_enabled_cb, device_disabled_cb, (void *)0xABCD));
 
   // Add an active node.
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
@@ -613,7 +722,8 @@
   EXPECT_EQ(2, audio_thread_add_open_dev_called);
   EXPECT_EQ(1, audio_thread_rm_open_dev_called);
 
-  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
+  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL));
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, UpdateActiveNode) {
@@ -651,6 +761,7 @@
   EXPECT_EQ(0, update_active_node_dev_enabled_val[1]);
   EXPECT_EQ(1, update_active_node_dev_enabled_val[2]);
   EXPECT_EQ(2, cras_observer_notify_active_node_called);
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, SelectNonExistingNode) {
@@ -670,6 +781,7 @@
       cras_make_node_id(2, 1));
   EXPECT_EQ(0, d1_.is_enabled);
   EXPECT_EQ(2, cras_observer_notify_active_node_called);
+  cras_iodev_list_deinit();
 }
 
 // Devices with the wrong direction should be rejected.
@@ -801,6 +913,7 @@
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+  cras_iodev_list_deinit();
 }
 
 // Test output_mute_changed callback.
@@ -885,6 +998,7 @@
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
   ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+  cras_iodev_list_deinit();
 }
 
 // Test enable/disable an iodev.
@@ -895,7 +1009,7 @@
   EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
 
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
-      device_enabled_cb, (void *)0xABCD));
+      device_enabled_cb, device_disabled_cb, (void *)0xABCD));
 
   // Enable a device.
   cras_iodev_list_enable_dev(&d1_);
@@ -905,16 +1019,16 @@
   EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
 
   // Disable a device.
-  cras_iodev_list_disable_dev(&d1_);
+  cras_iodev_list_disable_dev(&d1_, false);
   EXPECT_EQ(&d1_, device_disabled_dev);
   EXPECT_EQ(1, device_disabled_count);
-  EXPECT_EQ((void *)0xABCD, device_enabled_cb_data);
+  EXPECT_EQ((void *)0xABCD, device_disabled_cb_data);
 
-  EXPECT_EQ(-EEXIST, cras_iodev_list_set_device_enabled_callback(
-      device_enabled_cb, (void *)0xABCD));
+  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
+      device_enabled_cb, device_disabled_cb, (void *)0xCDEF));
   EXPECT_EQ(2, cras_observer_notify_active_node_called);
 
-  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
+  EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL));
 }
 
 // Test adding/removing an input dev to the list.
@@ -1121,6 +1235,7 @@
                                      IONODE_ATTR_PLUGGED, 1);
   EXPECT_EQ(rc, 0);
   EXPECT_EQ(1, set_node_attr_called);
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, AddActiveNode) {
@@ -1174,6 +1289,7 @@
   /* Assert active devices was set to default one, when selected device
    * removed. */
   cras_iodev_list_rm_output(&d1_);
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, DrainTimerCancel) {
@@ -1225,6 +1341,7 @@
   clock_gettime_retspec.tv_sec += 30;
   cras_tm_timer_cb(NULL, NULL);
   EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, RemoveThenSelectActiveNode) {
@@ -1248,6 +1365,7 @@
   cras_iodev_list_rm_active_node(CRAS_STREAM_OUTPUT, id);
   ASSERT_EQ(audio_thread_rm_open_dev_called, 0);
 
+  cras_iodev_list_deinit();
 }
 
 TEST_F(IoDevTestSuite, AddRemovePinnedStream) {
@@ -1257,6 +1375,65 @@
 
   // Add 2 output devices.
   d1_.direction = CRAS_STREAM_OUTPUT;
+  d1_.info.idx = 1;
+  EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
+  cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+      cras_make_node_id(d1_.info.idx, 0));
+  EXPECT_EQ(1, update_active_node_called);
+  EXPECT_EQ(&d1_, update_active_node_iodev_val[0]);
+
+  d2_.direction = CRAS_STREAM_OUTPUT;
+  d2_.info.idx = 2;
+  EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
+
+  // Setup pinned stream.
+  memset(&rstream, 0, sizeof(rstream));
+  rstream.is_pinned = 1;
+  rstream.pinned_dev_idx = d1_.info.idx;
+
+  // Add pinned stream to d1.
+  EXPECT_EQ(0, stream_add_cb(&rstream));
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+  EXPECT_EQ(2, update_active_node_called);
+  // Init d1_ because of pinned stream
+  EXPECT_EQ(&d1_, update_active_node_iodev_val[1]);
+
+  // Select d2, check pinned stream is not added to d2.
+  cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+      cras_make_node_id(d2_.info.idx, 0));
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(4, update_active_node_called);
+  // Unselect d1_ and select to d2_
+  EXPECT_EQ(&d1_, update_active_node_iodev_val[2]);
+  EXPECT_EQ(&d2_, update_active_node_iodev_val[3]);
+
+  // Remove pinned stream from d1, check d1 is closed after stream removed.
+  EXPECT_EQ(0, stream_rm_cb(&rstream));
+  EXPECT_EQ(1, cras_iodev_close_called);
+  EXPECT_EQ(&d1_, cras_iodev_close_dev);
+  EXPECT_EQ(5, update_active_node_called);
+  // close pinned device
+  EXPECT_EQ(&d1_, update_active_node_iodev_val[4]);
+
+  // Assume dev is already opened, add pin stream should not trigger another
+  // update_active_node call, but will trigger audio_thread_add_stream.
+  audio_thread_is_dev_open_ret = 1;
+  EXPECT_EQ(0, stream_add_cb(&rstream));
+  EXPECT_EQ(5, update_active_node_called);
+  EXPECT_EQ(2, audio_thread_add_stream_called);
+
+  cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, SuspendResumePinnedStream) {
+  struct cras_rstream rstream;
+
+  cras_iodev_list_init();
+
+  // Add 2 output devices.
+  d1_.direction = CRAS_STREAM_OUTPUT;
   EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
   d2_.direction = CRAS_STREAM_OUTPUT;
   EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
@@ -1271,22 +1448,124 @@
   EXPECT_EQ(1, audio_thread_add_stream_called);
   EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
   EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
-  EXPECT_EQ(1, update_active_node_called);
-  EXPECT_EQ(&d1_, update_active_node_iodev_val[0]);
 
-  // Select d2, check pinned stream is not added to d2.
-  cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
-      cras_make_node_id(d2_.info.idx, 0));
-  EXPECT_EQ(1, audio_thread_add_stream_called);
-  EXPECT_EQ(2, update_active_node_called);
-  EXPECT_EQ(&d2_, update_active_node_iodev_val[1]);
+  DL_APPEND(stream_list_get_ret, &rstream);
 
-  // Remove pinned stream from d1, check d1 is closed after stream removed.
-  EXPECT_EQ(0, stream_rm_cb(&rstream));
+  // Test for suspend
+
+  // Device state enters no_stream after stream is disconnected.
+  d1_.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+  // Device has no pinned stream now. But this pinned stream remains in stream_list.
+  cras_iodev_has_pinned_stream_ret[&d1_] = 0;
+
+  // Suspend
+  observer_ops->suspend_changed(NULL, 1);
+
+  // Verify that stream is disconnected and d1 is closed.
+  EXPECT_EQ(1, audio_thread_disconnect_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
   EXPECT_EQ(1, cras_iodev_close_called);
   EXPECT_EQ(&d1_, cras_iodev_close_dev);
-  EXPECT_EQ(3, update_active_node_called);
-  EXPECT_EQ(&d1_, update_active_node_iodev_val[2]);
+
+  // Test for resume
+  cras_iodev_open_called = 0;
+  audio_thread_add_stream_called = 0;
+  audio_thread_add_stream_stream = NULL;
+  d1_.state = CRAS_IODEV_STATE_CLOSE;
+
+  // Resume
+  observer_ops->suspend_changed(NULL, 0);
+
+  // Verify that device is opened and stream is attached to the device.
+  EXPECT_EQ(1, cras_iodev_open_called);
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+  cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, HotwordStreamsAddedThenSuspendResume) {
+  struct cras_rstream rstream;
+  struct cras_rstream *stream_list = NULL;
+  cras_iodev_list_init();
+
+  node1.type = CRAS_NODE_TYPE_HOTWORD;
+  d1_.direction = CRAS_STREAM_INPUT;
+  EXPECT_EQ(0, cras_iodev_list_add_input(&d1_));
+
+  memset(&rstream, 0, sizeof(rstream));
+  rstream.is_pinned = 1;
+  rstream.pinned_dev_idx = d1_.info.idx;
+  rstream.flags = HOTWORD_STREAM;
+
+  /* Add a hotword stream. */
+  EXPECT_EQ(0, stream_add_cb(&rstream));
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+
+  DL_APPEND(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+
+  /* Suspend hotword streams, verify the existing stream disconnects
+   * from the hotword device and connects to the empty iodev. */
+  EXPECT_EQ(0, cras_iodev_list_suspend_hotword_streams());
+  EXPECT_EQ(1, audio_thread_disconnect_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
+  EXPECT_EQ(&d1_, audio_thread_disconnect_stream_dev);
+  EXPECT_EQ(2, audio_thread_add_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+  EXPECT_EQ(&dummy_hotword_iodev, audio_thread_add_stream_dev);
+
+  /* Resume hotword streams, verify the stream disconnects from
+   * the empty iodev and connects back to the real hotword iodev. */
+  EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream());
+  EXPECT_EQ(2, audio_thread_disconnect_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
+  EXPECT_EQ(&dummy_hotword_iodev, audio_thread_disconnect_stream_dev);
+  EXPECT_EQ(3, audio_thread_add_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+  EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+  cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, HotwordStreamsAddedAfterSuspend) {
+  struct cras_rstream rstream;
+  struct cras_rstream *stream_list = NULL;
+  cras_iodev_list_init();
+
+  node1.type = CRAS_NODE_TYPE_HOTWORD;
+  d1_.direction = CRAS_STREAM_INPUT;
+  EXPECT_EQ(0, cras_iodev_list_add_input(&d1_));
+
+  memset(&rstream, 0, sizeof(rstream));
+  rstream.is_pinned = 1;
+  rstream.pinned_dev_idx = d1_.info.idx;
+  rstream.flags = HOTWORD_STREAM;
+
+  /* Suspends hotword streams before a stream connected. */
+  EXPECT_EQ(0, cras_iodev_list_suspend_hotword_streams());
+  EXPECT_EQ(0, audio_thread_disconnect_stream_called);
+  EXPECT_EQ(0, audio_thread_add_stream_called);
+
+  DL_APPEND(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+
+  /* Hotword stream connected, verify it is added to the empty iodev. */
+  EXPECT_EQ(0, stream_add_cb(&rstream));
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(&dummy_hotword_iodev, audio_thread_add_stream_dev);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+
+  /* Resume hotword streams, now the existing hotword stream should disconnect
+   * from the empty iodev and connect to the real hotword iodev. */
+  EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream());
+  EXPECT_EQ(1, audio_thread_disconnect_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
+  EXPECT_EQ(&dummy_hotword_iodev, audio_thread_disconnect_stream_dev);
+  EXPECT_EQ(2, audio_thread_add_stream_called);
+  EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+  EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+  cras_iodev_list_deinit();
 }
 
 }  //  namespace
@@ -1307,11 +1586,6 @@
 void cras_system_state_update_complete() {
 }
 
-int cras_system_get_suspended()
-{
-  return cras_system_get_suspended_val;
-}
-
 struct audio_thread *audio_thread_create() {
   return &thread;
 }
@@ -1350,6 +1624,12 @@
   return 0;
 }
 
+int audio_thread_is_dev_open(struct audio_thread *thread,
+			     struct cras_iodev *dev)
+{
+  return audio_thread_is_dev_open_ret;
+}
+
 int audio_thread_add_stream(struct audio_thread *thread,
                             struct cras_rstream *stream,
                             struct cras_iodev **devs,
@@ -1365,6 +1645,9 @@
                                    struct cras_rstream *stream,
                                    struct cras_iodev *iodev)
 {
+  audio_thread_disconnect_stream_called++;
+  audio_thread_disconnect_stream_stream = stream;
+  audio_thread_disconnect_stream_dev = iodev;
   return 0;
 }
 
@@ -1413,14 +1696,29 @@
   return 0;
 }
 
-struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction) {
-  dummy_empty_iodev[direction].direction = direction;
-  dummy_empty_iodev[direction].update_active_node = dummy_update_active_node;
-  if (dummy_empty_iodev[direction].active_node == NULL) {
-    struct cras_ionode *node = (struct cras_ionode *)calloc(1, sizeof(*node));
-    dummy_empty_iodev[direction].active_node = node;
+struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction,
+                                      enum CRAS_NODE_TYPE node_type) {
+  struct cras_iodev *dev;
+  if (node_type == CRAS_NODE_TYPE_HOTWORD) {
+    dev = &dummy_hotword_iodev;
+  } else {
+    dev = &dummy_empty_iodev[direction];
   }
-  return &dummy_empty_iodev[direction];
+  dev->direction = direction;
+  dev->update_active_node = dummy_update_active_node;
+  if (dev->active_node == NULL) {
+    struct cras_ionode *node = (struct cras_ionode *)calloc(1, sizeof(*node));
+    node->type = node_type;
+    dev->active_node = node;
+  }
+  return dev;
+}
+
+void empty_iodev_destroy(struct cras_iodev *iodev) {
+  if (iodev->active_node) {
+    free(iodev->active_node);
+    iodev->active_node = NULL;
+  }
 }
 
 struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction,
@@ -1441,7 +1739,8 @@
 void loopback_iodev_destroy(struct cras_iodev *iodev) {
 }
 
-int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level)
+int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level,
+                    const struct cras_audio_format *fmt)
 {
   if (cras_iodev_open_ret[cras_iodev_open_called] == 0)
     iodev->state = CRAS_IODEV_STATE_OPEN;
@@ -1476,6 +1775,11 @@
 	return cras_iodev_state_ret[iodev];
 }
 
+int cras_iodev_has_pinned_stream(const struct cras_iodev *dev)
+{
+  return cras_iodev_has_pinned_stream_ret[dev];
+}
+
 struct stream_list *stream_list_create(stream_callback *add_cb,
                                        stream_callback *rm_cb,
                                        stream_create_func *create_cb,
@@ -1492,6 +1796,16 @@
 struct cras_rstream *stream_list_get(struct stream_list *list) {
   return stream_list_get_ret;
 }
+void server_stream_create(struct stream_list *stream_list,
+			  unsigned int dev_idx)
+{
+  server_stream_create_called++;
+}
+void server_stream_destroy(struct stream_list *stream_list,
+			   unsigned int dev_idx)
+{
+  server_stream_destroy_called++;
+}
 
 int cras_rstream_create(struct cras_rstream_config *config,
                         struct cras_rstream **stream_out) {
@@ -1590,6 +1904,22 @@
   return 0;
 }
 
+#ifdef HAVE_WEBRTC_APM
+struct cras_apm *cras_apm_list_add(struct cras_apm_list *list,
+				   void *dev_ptr,
+				   const struct cras_audio_format *fmt)
+{
+  return NULL;
+}
+void cras_apm_list_remove(struct cras_apm_list *list, void *dev_ptr)
+{
+}
+int cras_apm_list_init(const char *device_config_dir)
+{
+  return 0;
+}
+#endif
+
 //  From librt.
 int clock_gettime(clockid_t clk_id, struct timespec *tp) {
   tp->tv_sec = clock_gettime_retspec.tv_sec;
diff --git a/cras/src/tests/iodev_stub.cc b/cras/src/tests/iodev_stub.cc
new file mode 100644
index 0000000..519c3f0
--- /dev/null
+++ b/cras/src/tests/iodev_stub.cc
@@ -0,0 +1,148 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <time.h>
+#include <unordered_map>
+
+extern "C" {
+#include "cras_iodev.h"
+}
+
+namespace {
+  struct cb_data {
+    int frames_queued_ret;
+    timespec frames_queued_ts;
+  };
+  std::unordered_map<cras_iodev*, cb_data> data_map;
+} // namespace
+
+void iodev_stub_reset() {
+  data_map.clear();
+}
+
+void iodev_stub_frames_queued(cras_iodev* iodev, int ret, timespec ts) {
+  cb_data data = { ret, ts };
+  data_map.insert({iodev, data});
+}
+
+extern "C" {
+
+double cras_iodev_get_est_rate_ratio(const struct cras_iodev *iodev) {
+  return 48000.0;
+}
+
+int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev) {
+  return 0;
+}
+
+int cras_iodev_frames_queued(struct cras_iodev *iodev,
+                             struct timespec *tstamp) {
+  auto elem = data_map.find(iodev);
+  if (elem != data_map.end()) {
+    *tstamp = elem->second.frames_queued_ts;
+    return elem->second.frames_queued_ret;
+  }
+  clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
+  return 0;
+}
+
+struct dev_stream *cras_iodev_rm_stream(struct cras_iodev *iodev,
+                                        const struct cras_rstream *stream) {
+  return NULL;
+}
+
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+                           struct timespec *level_tstamp) {
+  return 0;
+}
+
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev) {
+  return CRAS_IODEV_STATE_OPEN;
+}
+
+unsigned int cras_iodev_all_streams_written(struct cras_iodev *iodev) {
+  return 0;
+}
+
+int cras_iodev_put_input_buffer(struct cras_iodev *iodev) {
+  return 0;
+}
+
+int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
+                                 unsigned int nframes, int* non_empty,
+                                 struct cras_fmt_conv *output_converter) {
+  return 0;
+}
+
+int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
+                                unsigned *frames) {
+  return 0;
+}
+
+int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
+                                 struct cras_audio_area **area,
+                                 unsigned *frames) {
+  return 0;
+}
+
+float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev) {
+  return 0.0;
+}
+
+void cras_iodev_stream_written(struct cras_iodev *iodev,
+                               struct dev_stream *stream,
+                               unsigned int nwritten) {
+}
+
+int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev)
+{ return 0;
+}
+
+int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level) {
+  return 0;
+}
+
+unsigned int cras_iodev_max_stream_offset(const struct cras_iodev *iodev) {
+  return 0;
+}
+
+int cras_iodev_odev_should_wake(const struct cras_iodev *odev)
+{
+  return 1;
+}
+
+int cras_iodev_output_underrun(struct cras_iodev *odev) {
+  return 0;
+}
+
+int cras_iodev_reset_request(struct cras_iodev* iodev) {
+  return 0;
+}
+
+unsigned int cras_iodev_stream_offset(struct cras_iodev *iodev,
+                                      struct dev_stream *stream) {
+  return 0;
+}
+
+unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev)
+{
+  return 0;
+}
+
+unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
+                                                unsigned int *hw_level,
+                                                struct timespec *hw_tstamp)
+{
+  clock_gettime(CLOCK_MONOTONIC_RAW, hw_tstamp);
+  *hw_level = 0;
+  return 0;
+}
+
+void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
+		unsigned int hw_level)
+{
+}
+
+} // extern "C"
diff --git a/cras/src/tests/iodev_stub.h b/cras/src/tests/iodev_stub.h
new file mode 100644
index 0000000..b1c4745
--- /dev/null
+++ b/cras/src/tests/iodev_stub.h
@@ -0,0 +1,15 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef IODEV_STUB_H_
+#define IODEV_STUB_H_
+
+#include <time.h>
+
+void iodev_stub_reset();
+
+void iodev_stub_frames_queued(cras_iodev* iodev, int ret, timespec ts);
+
+#endif // IODEV_STUB_H_
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index 29e6e22..f022164 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -13,6 +13,7 @@
 #include "utlist.h"
 #include "cras_audio_area.h"
 #include "audio_thread_log.h"
+#include "input_data.h"
 
 // Mock software volume scalers.
 float softvol_scalers[101];
@@ -52,11 +53,13 @@
 static float cras_dsp_pipeline_sink_buffer[2][DSP_BUFFER_SIZE];
 static int cras_dsp_pipeline_get_delay_called;
 static int cras_dsp_pipeline_apply_called;
+static int cras_dsp_pipeline_set_sink_ext_module_called;
 static int cras_dsp_pipeline_apply_sample_count;
 static unsigned int cras_mix_mute_count;
 static unsigned int cras_dsp_num_input_channels_return;
 static unsigned int cras_dsp_num_output_channels_return;
 struct cras_dsp_context *cras_dsp_context_new_return;
+static unsigned int cras_dsp_load_dummy_pipeline_called;
 static unsigned int rate_estimator_add_frames_num_frames;
 static unsigned int rate_estimator_add_frames_called;
 static int cras_system_get_mute_return;
@@ -103,6 +106,11 @@
 static float cras_scale_buffer_increment_scaler;
 static float cras_scale_buffer_increment_increment;
 static int cras_scale_buffer_increment_channel;
+static struct cras_audio_format audio_fmt;
+static int buffer_share_add_id_called;
+static int buffer_share_get_new_write_point_ret;
+static int ext_mod_configure_called;
+static struct input_data *input_data_create_ret;
 
 // Iodev callback
 int update_channel_layout(struct cras_iodev *iodev) {
@@ -144,10 +152,12 @@
          sizeof(cras_dsp_pipeline_sink_buffer));
   cras_dsp_pipeline_get_delay_called = 0;
   cras_dsp_pipeline_apply_called = 0;
+  cras_dsp_pipeline_set_sink_ext_module_called = 0;
   cras_dsp_pipeline_apply_sample_count = 0;
   cras_dsp_num_input_channels_return = 2;
   cras_dsp_num_output_channels_return = 2;
   cras_dsp_context_new_return = NULL;
+  cras_dsp_load_dummy_pipeline_called = 0;
   rate_estimator_add_frames_num_frames = 0;
   rate_estimator_add_frames_called = 0;
   cras_system_get_mute_return = 0;
@@ -194,6 +204,11 @@
   cras_scale_buffer_increment_scaler = 0;
   cras_scale_buffer_increment_increment = 0;
   cras_scale_buffer_increment_channel = 0;
+  audio_fmt.format = SND_PCM_FORMAT_S16_LE;
+  audio_fmt.frame_rate = 48000;
+  audio_fmt.num_channels = 2;
+  buffer_share_add_id_called = 0;
+  ext_mod_configure_called = 0;
 }
 
 namespace {
@@ -479,10 +494,11 @@
   EXPECT_EQ(SND_PCM_FORMAT_S16_LE, iodev_.ext_format->format);
   EXPECT_EQ(48000, iodev_.ext_format->frame_rate);
   EXPECT_EQ(2, iodev_.ext_format->num_channels);
-  EXPECT_EQ(2, cras_audio_format_set_channel_layout_called);
   EXPECT_EQ(0, dsp_context_free_called);
-  for (i = 0; i < CRAS_CH_MAX; i++)
+  for (i = 0; i < CRAS_CH_MAX; i++) {
     EXPECT_EQ(iodev_.format->channel_layout[i], stereo_layout[i]);
+    EXPECT_EQ(iodev_.ext_format->channel_layout[i], stereo_layout[i]);
+  }
 }
 
 TEST_F(IoDevSetFormatTestSuite, UpdateChannelLayoutFail6ch) {
@@ -506,10 +522,11 @@
   EXPECT_EQ(SND_PCM_FORMAT_S16_LE, iodev_.ext_format->format);
   EXPECT_EQ(48000, iodev_.ext_format->frame_rate);
   EXPECT_EQ(6, iodev_.ext_format->num_channels);
-  EXPECT_EQ(2, cras_audio_format_set_channel_layout_called);
   EXPECT_EQ(0, dsp_context_free_called);
-  for (i = 0; i < CRAS_CH_MAX; i++)
+  for (i = 0; i < CRAS_CH_MAX; i++) {
     EXPECT_EQ(iodev_.format->channel_layout[i], default_6ch_layout[i]);
+    EXPECT_EQ(iodev_.ext_format->channel_layout[i], default_6ch_layout[i]);
+  }
 }
 
 // Put buffer tests
@@ -589,7 +606,7 @@
   iodev.format = &fmt;
   iodev.put_buffer = put_buffer;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(20, cras_mix_mute_count);
   EXPECT_EQ(20, put_buffer_nframes);
@@ -651,7 +668,7 @@
   iodev.format = &fmt;
   iodev.put_buffer = put_buffer;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(20, cras_mix_mute_count);
   EXPECT_EQ(20, put_buffer_nframes);
@@ -680,7 +697,7 @@
   // Assume ramping is done.
   cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
   // Output should be muted.
   EXPECT_EQ(0, rc);
   EXPECT_EQ(20, cras_mix_mute_count);
@@ -690,7 +707,7 @@
   // Test for the case where ramping is not done yet.
   ResetStubData();
   cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
 
   // Output should not be muted.
   EXPECT_EQ(0, rc);
@@ -729,7 +746,7 @@
   // Assume ramping is done.
   cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(20, cras_mix_mute_count);
   EXPECT_EQ(20, put_buffer_nframes);
@@ -738,7 +755,7 @@
   // Test for the case where ramping is not done yet.
   ResetStubData();
   cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 20, NULL, nullptr);
 
   // Output should not be muted.
   EXPECT_EQ(0, rc);
@@ -770,7 +787,7 @@
   iodev.format = &fmt;
   iodev.put_buffer = put_buffer;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 22);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 22, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   EXPECT_EQ(22, put_buffer_nframes);
@@ -796,7 +813,7 @@
   cras_iodev_register_pre_dsp_hook(&iodev, pre_dsp_hook, (void *)0x1234);
   cras_iodev_register_post_dsp_hook(&iodev, post_dsp_hook, (void *)0x5678);
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 32);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 32, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   EXPECT_EQ(1, pre_dsp_hook_called);
@@ -829,7 +846,7 @@
   cras_system_get_volume_return = 13;
   softvol_scalers[13] = 0.435;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 53);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 53, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   EXPECT_EQ(53, put_buffer_nframes);
@@ -867,7 +884,7 @@
   cras_system_get_volume_return = volume;
   softvol_scalers[volume] = volume_scaler;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   EXPECT_EQ(n_frames, put_buffer_nframes);
@@ -884,7 +901,7 @@
   cras_system_get_volume_return = volume;
   softvol_scalers[volume] = volume_scaler;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   // cras_scale_buffer is not called.
@@ -932,7 +949,7 @@
   // Assume ramping is done.
   cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   // cras_scale_buffer is not called.
@@ -946,7 +963,7 @@
   cras_ramp_get_current_action_ret.scaler = ramp_scaler;
   cras_ramp_get_current_action_ret.increment = increment;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   // cras_scale_buffer is not called.
@@ -983,7 +1000,7 @@
   iodev.format = &fmt;
   iodev.put_buffer = put_buffer;
 
-  rc = cras_iodev_put_output_buffer(&iodev, frames, 53);
+  rc = cras_iodev_put_output_buffer(&iodev, frames, 53, NULL, nullptr);
   EXPECT_EQ(0, rc);
   EXPECT_EQ(0, cras_mix_mute_count);
   EXPECT_EQ(53, put_buffer_nframes);
@@ -1227,7 +1244,7 @@
   // Check that system volume changes software volume if needed.
   cras_system_get_capture_gain_ret_value = 2000;
   // system_gain + node_gain = 2000 + 400  = 2400
-  // 2400 dBm is 15.848931
+  // 2400 * 0.01 dB is 15.848931
   EXPECT_FLOAT_EQ(15.848931, cras_iodev_get_software_gain_scaler(&iodev));
   EXPECT_FLOAT_EQ(3000, cras_iodev_maximum_software_gain(&iodev));
 
@@ -1269,10 +1286,10 @@
   iodev.get_buffer = bad_get_buffer;
 
   EXPECT_EQ(-EINVAL, cras_iodev_get_output_buffer(&iodev, area, &frames));
-  EXPECT_EQ(-EINVAL, cras_iodev_get_input_buffer(&iodev, area, &frames));
+  EXPECT_EQ(-EINVAL, cras_iodev_get_input_buffer(&iodev, &frames));
 }
 
-static int open_dev(struct cras_iodev *iodev) {
+static int configure_dev(struct cras_iodev *iodev) {
   iodev->buffer_size = iodev_buffer_size;
   return 0;
 }
@@ -1281,14 +1298,15 @@
   struct cras_iodev iodev;
 
   memset(&iodev, 0, sizeof(iodev));
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.direction = CRAS_STREAM_OUTPUT;
+  iodev.ext_format = &audio_fmt;
   ResetStubData();
 
   iodev.state = CRAS_IODEV_STATE_CLOSE;
 
   iodev_buffer_size = 1024;
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
   EXPECT_EQ(0, iodev.max_cb_level);
   EXPECT_EQ(240, iodev.min_cb_level);
 
@@ -1296,6 +1314,28 @@
   EXPECT_EQ(CRAS_IODEV_STATE_NO_STREAM_RUN, iodev.state);
 }
 
+TEST(IoDev, OpenOutputDeviceWithLowRateFmt) {
+  struct cras_iodev iodev;
+
+  memset(&iodev, 0, sizeof(iodev));
+  iodev.configure_dev = configure_dev;
+  iodev.direction = CRAS_STREAM_OUTPUT;
+  iodev.ext_format = &audio_fmt;
+  ResetStubData();
+
+  cras_audio_format low_rate_fmt = audio_fmt;
+  low_rate_fmt.frame_rate = 8000;
+  iodev.state = CRAS_IODEV_STATE_CLOSE;
+
+  iodev_buffer_size = 1024;
+  cras_iodev_open(&iodev, 40, &low_rate_fmt);
+  EXPECT_EQ(0, iodev.max_cb_level);
+
+  // Test that iodev min_cb_level should be set to
+  // 40 * 48000 / 8000 = 240
+  EXPECT_EQ(240, iodev.min_cb_level);
+}
+
 int fake_start(const struct cras_iodev *iodev) {
   return 0;
 }
@@ -1304,15 +1344,16 @@
   struct cras_iodev iodev;
 
   memset(&iodev, 0, sizeof(iodev));
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.direction = CRAS_STREAM_OUTPUT;
+  iodev.ext_format = &audio_fmt;
   ResetStubData();
 
   iodev.state = CRAS_IODEV_STATE_CLOSE;
   iodev.start = fake_start;
 
   iodev_buffer_size = 1024;
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
   EXPECT_EQ(0, iodev.max_cb_level);
   EXPECT_EQ(240, iodev.min_cb_level);
 
@@ -1324,14 +1365,15 @@
   struct cras_iodev iodev;
 
   memset(&iodev, 0, sizeof(iodev));
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.direction = CRAS_STREAM_INPUT;
+  iodev.ext_format = &audio_fmt;
   ResetStubData();
 
   iodev.state = CRAS_IODEV_STATE_CLOSE;
 
   iodev_buffer_size = 1024;
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
   EXPECT_EQ(0, iodev.max_cb_level);
   EXPECT_EQ(240, iodev.min_cb_level);
 
@@ -1343,15 +1385,16 @@
   struct cras_iodev iodev;
 
   memset(&iodev, 0, sizeof(iodev));
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.direction = CRAS_STREAM_INPUT;
+  iodev.ext_format = &audio_fmt;
   ResetStubData();
 
   iodev.state = CRAS_IODEV_STATE_CLOSE;
   iodev.start = fake_start;
 
   iodev_buffer_size = 1024;
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
   EXPECT_EQ(0, iodev.max_cb_level);
   EXPECT_EQ(240, iodev.min_cb_level);
 
@@ -1359,6 +1402,28 @@
   EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
 }
 
+TEST(IoDev, OpenInputDeviceWithLowRateFmt) {
+  struct cras_iodev iodev;
+
+  memset(&iodev, 0, sizeof(iodev));
+  iodev.configure_dev = configure_dev;
+  iodev.direction = CRAS_STREAM_INPUT;
+  iodev.ext_format = &audio_fmt;
+  ResetStubData();
+
+  cras_audio_format low_rate_fmt = audio_fmt;
+  low_rate_fmt.frame_rate = 8000;
+  iodev.state = CRAS_IODEV_STATE_CLOSE;
+
+  iodev_buffer_size = 1024;
+  cras_iodev_open(&iodev, 40, &low_rate_fmt);
+  EXPECT_EQ(0, iodev.max_cb_level);
+
+  // Test that iodev min_cb_level should be set to
+  // 40 * 48000 / 8000 = 240
+  EXPECT_EQ(240, iodev.min_cb_level);
+}
+
 static int simple_no_stream(struct cras_iodev *dev, int enable)
 {
   simple_no_stream_enable = enable;
@@ -1372,8 +1437,11 @@
   struct dev_stream stream1, stream2;
 
   memset(&iodev, 0, sizeof(iodev));
-  iodev.open_dev = open_dev;
+  memset(&rstream1, 0, sizeof(rstream1));
+  memset(&rstream2, 0, sizeof(rstream2));
+  iodev.configure_dev = configure_dev;
   iodev.no_stream = simple_no_stream;
+  iodev.ext_format = &audio_fmt;
   iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
   rstream1.cb_threshold = 800;
   stream1.stream = &rstream1;
@@ -1382,7 +1450,7 @@
   ResetStubData();
 
   iodev_buffer_size = 1024;
-  cras_iodev_open(&iodev, rstream1.cb_threshold);
+  cras_iodev_open(&iodev, rstream1.cb_threshold, &audio_fmt);
   EXPECT_EQ(0, iodev.max_cb_level);
   EXPECT_EQ(512, iodev.min_cb_level);
 
@@ -1390,10 +1458,12 @@
   cras_iodev_add_stream(&iodev, &stream1);
   EXPECT_EQ(800, iodev.max_cb_level);
   EXPECT_EQ(512, iodev.min_cb_level);
+  EXPECT_EQ(1, buffer_share_add_id_called);
 
   cras_iodev_add_stream(&iodev, &stream2);
   EXPECT_EQ(800, iodev.max_cb_level);
   EXPECT_EQ(400, iodev.min_cb_level);
+  EXPECT_EQ(2, buffer_share_add_id_called);
 
   cras_iodev_rm_stream(&iodev, &rstream1);
   EXPECT_EQ(400, iodev.max_cb_level);
@@ -1403,7 +1473,28 @@
   /* When all streams are removed, keep the last min_cb_level for draining. */
   cras_iodev_rm_stream(&iodev, &rstream2);
   EXPECT_EQ(0, iodev.max_cb_level);
-  EXPECT_EQ(400, iodev.min_cb_level);
+  EXPECT_EQ(512, iodev.min_cb_level);
+}
+
+TEST(IoDev, TriggerOnlyStreamNoBufferShare) {
+  struct cras_iodev iodev;
+  struct cras_rstream rstream;
+  struct dev_stream stream;
+
+  memset(&iodev, 0, sizeof(iodev));
+  memset(&rstream, 0, sizeof(rstream));
+  iodev.configure_dev = configure_dev;
+  iodev.ext_format = &audio_fmt;
+  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+  rstream.cb_threshold = 800;
+  rstream.flags = TRIGGER_ONLY;
+  stream.stream = &rstream;
+  ResetStubData();
+
+  cras_iodev_open(&iodev, rstream.cb_threshold, &audio_fmt);
+  /* TRIGGER_ONLY streams shall not be added to buffer_share. */
+  cras_iodev_add_stream(&iodev, &stream);
+  EXPECT_EQ(0, buffer_share_add_id_called);
 }
 
 TEST(IoDev, FillZeros) {
@@ -1503,6 +1594,8 @@
   struct dev_stream stream1;
   struct cras_iodev_info info;
 
+  memset(&info, 0, sizeof(info));
+
   ResetStubData();
 
   rstream1.cb_threshold = min_cb_level;
@@ -1523,13 +1616,13 @@
   iodev.direction = CRAS_STREAM_OUTPUT;
   iodev.buffer_size = BUFFER_SIZE;
   iodev.no_stream = no_stream;
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.start = fake_start;
   iodev.info = info;
   iodev_buffer_size = BUFFER_SIZE;
 
   // Open device.
-  cras_iodev_open(&iodev, rstream1.cb_threshold);
+  cras_iodev_open(&iodev, rstream1.cb_threshold, &fmt);
 
   // Add one stream to device.
   cras_iodev_add_stream(&iodev, &stream1);
@@ -1825,30 +1918,51 @@
 
 TEST(IoDev, FramesToPlayInSleep) {
   struct cras_iodev iodev;
-  unsigned int min_cb_level = 240, hw_level;
+  struct cras_audio_format fmt;
+  unsigned int min_cb_level = 512, hw_level;
   unsigned int got_hw_level, got_frames;
   struct timespec hw_tstamp;
 
   memset(&iodev, 0, sizeof(iodev));
+  memset(&fmt, 0, sizeof(fmt));
   iodev.frames_queued = frames_queued;
   iodev.min_buffer_level = 0;
   iodev.direction = CRAS_STREAM_OUTPUT;
   iodev.buffer_size = BUFFER_SIZE;
   iodev.min_cb_level = min_cb_level;
+  iodev.streams = reinterpret_cast<struct dev_stream *>(0x1);
+  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+  iodev.format = &fmt;
+  fmt.frame_rate = 48000;
 
   ResetStubData();
 
   // Device is running. There is at least one stream for this device.
   // hw_level is greater than min_cb_level.
-  iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
   hw_level = min_cb_level + 50;
   fr_queued = hw_level;
-  iodev.streams = reinterpret_cast<struct dev_stream *>(0x1);
-
   got_frames = cras_iodev_frames_to_play_in_sleep(
                    &iodev, &got_hw_level, &hw_tstamp);
-  EXPECT_EQ(hw_level, got_hw_level);
-  EXPECT_EQ(hw_level, got_frames);
+  EXPECT_EQ(got_hw_level, hw_level);
+  EXPECT_EQ(got_frames, 514);
+
+  // Device is running. There is at least one stream for this device.
+  // hw_level is 2x greater than min_cb_level.
+  hw_level = 2 * min_cb_level + 50;
+  fr_queued = hw_level;
+  got_frames = cras_iodev_frames_to_play_in_sleep(
+                   &iodev, &got_hw_level, &hw_tstamp);
+  EXPECT_EQ(got_hw_level, hw_level);
+  EXPECT_EQ(got_frames, 1026);
+
+  // Device is running. There is at least one stream for this device.
+  // hw_level is less than min_cb_level.
+  hw_level = min_cb_level / 2;
+  fr_queued = hw_level;
+  got_frames = cras_iodev_frames_to_play_in_sleep(
+                   &iodev, &got_hw_level, &hw_tstamp);
+  EXPECT_EQ(got_hw_level, hw_level);
+  EXPECT_EQ(got_frames, 208);
 
   // Device is running. There is no stream for this device.
   // hw_level is greater than min_cb_level.
@@ -1856,8 +1970,8 @@
 
   got_frames = cras_iodev_frames_to_play_in_sleep(
                    &iodev, &got_hw_level, &hw_tstamp);
-  EXPECT_EQ(hw_level, got_hw_level);
-  EXPECT_EQ(hw_level - min_cb_level, got_frames);
+  EXPECT_EQ(got_hw_level, hw_level);
+  EXPECT_EQ(got_frames, 0);
 
   // Device is running. There is no stream for this device.
   // hw_level is less than min_cb_level.
@@ -1867,8 +1981,8 @@
 
   got_frames = cras_iodev_frames_to_play_in_sleep(
                    &iodev, &got_hw_level, &hw_tstamp);
-  EXPECT_EQ(hw_level, got_hw_level);
-  EXPECT_EQ(0, got_frames);
+  EXPECT_EQ(got_hw_level, hw_level);
+  EXPECT_EQ(got_frames, 0);
 }
 
 static unsigned int get_num_underruns(const struct cras_iodev *iodev) {
@@ -1892,14 +2006,15 @@
 
   ResetStubData();
 
-  iodev.open_dev = open_dev;
+  iodev.configure_dev = configure_dev;
   iodev.direction = CRAS_STREAM_OUTPUT;
+  iodev.ext_format = &audio_fmt;
 
   iodev.state = CRAS_IODEV_STATE_CLOSE;
   iodev_buffer_size = 1024;
 
   // Open device.
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
 
   // The first reset request works.
   EXPECT_EQ(0, cras_iodev_reset_request(&iodev));
@@ -1910,7 +2025,7 @@
   EXPECT_EQ(1, device_monitor_reset_device_called);
 
   // Assume device is opened again.
-  cras_iodev_open(&iodev, 240);
+  cras_iodev_open(&iodev, 240, &audio_fmt);
 
   // The reset request works.
   EXPECT_EQ(0, cras_iodev_reset_request(&iodev));
@@ -1957,6 +2072,109 @@
   EXPECT_EQ(1, output_underrun_called);
 }
 
+static void ext_mod_configure(
+    struct ext_dsp_module *ext,
+    unsigned int buffer_size,
+    unsigned int num_channels,
+    unsigned int rate)
+{
+  ext_mod_configure_called++;
+}
+
+TEST(IoDev, SetExtDspMod) {
+  struct cras_iodev iodev;
+  struct cras_audio_format fmt;
+  struct ext_dsp_module ext;
+
+  ResetStubData();
+
+  memset(&iodev, 0, sizeof(iodev));
+  fmt.format = SND_PCM_FORMAT_S16_LE;
+  fmt.frame_rate = 48000;
+  fmt.num_channels = 2;
+  iodev.configure_dev = configure_dev;
+  iodev.ext_format = &fmt;
+  iodev.format = &fmt;
+  iodev.state = CRAS_IODEV_STATE_CLOSE;
+  ext.configure = ext_mod_configure;
+
+  iodev.dsp_context = reinterpret_cast<cras_dsp_context *>(0xf0f);
+  cras_dsp_get_pipeline_ret = 0x25;
+
+  cras_iodev_set_ext_dsp_module(&iodev, &ext);
+  EXPECT_EQ(0, ext_mod_configure_called);
+
+  cras_iodev_open(&iodev, 240, &fmt);
+  EXPECT_EQ(1, ext_mod_configure_called);
+  EXPECT_EQ(1, cras_dsp_get_pipeline_called);
+  EXPECT_EQ(1, cras_dsp_pipeline_set_sink_ext_module_called);
+
+  cras_iodev_set_ext_dsp_module(&iodev, NULL);
+  EXPECT_EQ(1, ext_mod_configure_called);
+
+  cras_iodev_set_ext_dsp_module(&iodev, &ext);
+  EXPECT_EQ(2, ext_mod_configure_called);
+  EXPECT_EQ(2, cras_dsp_get_pipeline_called);
+  EXPECT_EQ(2, cras_dsp_pipeline_set_sink_ext_module_called);
+
+  /* If pipeline doesn't exist, dummy pipeline should be loaded. */
+  cras_dsp_get_pipeline_ret = 0x0;
+  cras_iodev_set_ext_dsp_module(&iodev, &ext);
+  EXPECT_EQ(3, ext_mod_configure_called);
+  EXPECT_EQ(4, cras_dsp_get_pipeline_called);
+  EXPECT_EQ(1, cras_dsp_load_dummy_pipeline_called);
+  EXPECT_EQ(3, cras_dsp_pipeline_set_sink_ext_module_called);
+}
+
+TEST(IoDev, InputDspOffset) {
+  struct cras_iodev iodev;
+  struct cras_audio_format fmt;
+  struct cras_rstream rstream1;
+  struct dev_stream stream1;
+  struct input_data data;
+  unsigned int frames = 240;
+
+  ResetStubData();
+
+  rstream1.cb_threshold = 240;
+  rstream1.stream_id = 123;
+  stream1.stream = &rstream1;
+
+  memset(&iodev, 0, sizeof(iodev));
+  fmt.format = SND_PCM_FORMAT_S16_LE;
+  fmt.frame_rate = 48000;
+  fmt.num_channels = 2;
+  iodev.configure_dev = configure_dev;
+  iodev.ext_format = &fmt;
+  iodev.format = &fmt;
+  iodev.state = CRAS_IODEV_STATE_CLOSE;
+  iodev.get_buffer = get_buffer;
+  iodev.put_buffer = put_buffer;
+  iodev.direction = CRAS_STREAM_INPUT;
+  iodev.buffer_size = 480;
+
+  iodev.dsp_context = reinterpret_cast<cras_dsp_context *>(0xf0f);
+  cras_dsp_get_pipeline_ret = 0x25;
+  input_data_create_ret = &data;
+
+  cras_iodev_open(&iodev, 240, &fmt);
+
+  cras_iodev_add_stream(&iodev, &stream1);
+  cras_iodev_get_input_buffer(&iodev, &frames);
+
+  buffer_share_get_new_write_point_ret = 100;
+  cras_iodev_put_input_buffer(&iodev);
+  EXPECT_EQ(140, iodev.input_dsp_offset);
+
+  frames = 130;
+  cras_iodev_get_input_buffer(&iodev, &frames);
+  EXPECT_EQ(130, iodev.input_frames_read);
+
+  buffer_share_get_new_write_point_ret = 80;
+  cras_iodev_put_input_buffer(&iodev);
+  EXPECT_EQ(60, iodev.input_dsp_offset);
+}
+
 extern "C" {
 
 //  From libpthread.
@@ -1969,12 +2187,6 @@
   return 0;
 }
 
-// From audio_thread
-struct cras_fmt_conv *audio_thread_get_global_remix_converter()
-{
-  return NULL;
-}
-
 // Fromt fmt_conv
 void cras_channel_remix_convert(struct cras_fmt_conv *conv,
     uint8_t *in_buf,
@@ -1982,6 +2194,12 @@
 {
 }
 
+size_t cras_fmt_conv_in_frames_to_out(struct cras_fmt_conv *conv,
+				      size_t in_frames)
+{
+	return in_frames;
+}
+
 // From buffer_share
 struct buffer_share *buffer_share_create(unsigned int buf_sz) {
   return NULL;
@@ -1997,10 +2215,11 @@
 }
 
 unsigned int buffer_share_get_new_write_point(struct buffer_share *mix) {
-  return 0;
+  return buffer_share_get_new_write_point_ret;
 }
 
 int buffer_share_add_id(struct buffer_share *mix, unsigned int id) {
+  buffer_share_add_id_called++;
   return 0;
 }
 
@@ -2038,6 +2257,11 @@
 void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
 {
 }
+void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx,
+				  unsigned int num_channels)
+{
+  cras_dsp_load_dummy_pipeline_called++;
+}
 
 void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
                                   const char *value)
@@ -2080,11 +2304,13 @@
   return 0;
 }
 
-void cras_dsp_pipeline_apply(struct pipeline *pipeline,
-			     uint8_t *buf, unsigned int frames)
+int cras_dsp_pipeline_apply(struct pipeline *pipeline,
+			    uint8_t *buf, snd_pcm_format_t format,
+			    unsigned int frames)
 {
   cras_dsp_pipeline_apply_called++;
   cras_dsp_pipeline_apply_sample_count = frames;
+  return 0;
 }
 
 void cras_dsp_pipeline_add_statistic(struct pipeline *pipeline,
@@ -2092,6 +2318,11 @@
                                      int samples)
 {
 }
+void cras_dsp_pipeline_set_sink_ext_module(struct pipeline *pipeline,
+					   struct ext_dsp_module *ext_module)
+{
+  cras_dsp_pipeline_set_sink_ext_module_called++;
+}
 
 unsigned int cras_dsp_num_output_channels(const struct cras_dsp_context *ctx)
 {
@@ -2307,6 +2538,45 @@
   return 0;
 }
 
+static void mod_run(struct ext_dsp_module *ext,
+                    unsigned int nframes)
+{
+}
+
+static void mod_configure(struct ext_dsp_module *ext,
+                          unsigned int buffer_size,
+                          unsigned int num_channels,
+                          unsigned int rate)
+{
+}
+
+struct input_data *input_data_create(void *dev_ptr)
+{
+  if (input_data_create_ret) {
+    input_data_create_ret->ext.run = mod_run;
+    input_data_create_ret->ext.configure = mod_configure;
+  }
+  return input_data_create_ret;
+}
+
+void input_data_destroy(struct input_data **data)
+{
+}
+void input_data_set_all_streams_read(struct input_data *data,
+				     unsigned int nframes)
+{
+}
+
+int cras_audio_thread_severe_underrun()
+{
+  return 0;
+}
+
+int cras_audio_thread_underrun()
+{
+  return 0;
+}
+
 }  // extern "C"
 }  //  namespace
 
diff --git a/cras/src/tests/linear_resampler_unittest.cc b/cras/src/tests/linear_resampler_unittest.cc
index e299648..6dbab94 100644
--- a/cras/src/tests/linear_resampler_unittest.cc
+++ b/cras/src/tests/linear_resampler_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium OS Autholr. All rights reserved.
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -54,6 +54,7 @@
 		EXPECT_LE(*(int16_t *)(in_buf + 4 * i),
 			  *(int16_t *)(out_buf + 4 * (i + 1)));
 	}
+	linear_resampler_destroy(lr);
 }
 
 TEST(LinearResampler, ResampleIntegerFractionToLarger) {
@@ -104,6 +105,7 @@
 		EXPECT_LE(*(int16_t *)(in_buf + 4 * i + 2),
 			  *(int16_t *)(out_buf + 4 * i + 2));
 	}
+	linear_resampler_destroy(lr);
 }
 
 TEST(LinearResampler, ResampleIntegerFractionToLess) {
@@ -152,6 +154,7 @@
 		EXPECT_LE(*(int16_t *)(in_buf + 4 * i + 2),
 			  *(int16_t *)(out_buf + 4 * i + 2));
 	}
+	linear_resampler_destroy(lr);
 }
 
 TEST(LinearResampler, ResampleIntegerNoSrcBuffer) {
@@ -170,6 +173,7 @@
 				     out_buf, BUF_SIZE);
 	EXPECT_EQ(0, rc);
 	EXPECT_EQ(0, count);
+	linear_resampler_destroy(lr);
 }
 
 TEST(LinearResampler, ResampleIntegerNoDstBuffer) {
@@ -188,6 +192,7 @@
 				     out_buf, 0);
 	EXPECT_EQ(0, rc);
 	EXPECT_EQ(0, count);
+	linear_resampler_destroy(lr);
 }
 
 extern "C" {
diff --git a/cras/src/tests/loopback_iodev_unittest.cc b/cras/src/tests/loopback_iodev_unittest.cc
index b0387bf..b5acb1a 100644
--- a/cras/src/tests/loopback_iodev_unittest.cc
+++ b/cras/src/tests/loopback_iodev_unittest.cc
@@ -14,6 +14,7 @@
 #include "cras_shm.h"
 #include "cras_types.h"
 #include "dev_stream.h"
+#include "utlist.h"
 }
 
 namespace {
@@ -30,8 +31,9 @@
 static unsigned int cras_iodev_list_add_input_called;
 static unsigned int cras_iodev_list_rm_input_called;
 static unsigned int cras_iodev_list_set_device_enabled_callback_called;
-static device_enabled_callback_t cras_iodev_list_set_device_enabled_callback_cb;
-static void *cras_iodev_list_set_device_enabled_callback_cb_data;
+static device_enabled_callback_t device_enabled_callback_cb;
+static device_disabled_callback_t device_disabled_callback_cb;
+static void *device_enabled_callback_cb_data;
 
 class LoopBackTestSuite : public testing::Test{
   protected:
@@ -58,7 +60,9 @@
     virtual void TearDown() {
       loopback_iodev_destroy(loop_in_);
       EXPECT_EQ(1, cras_iodev_list_rm_input_called);
-      EXPECT_EQ(NULL, cras_iodev_list_set_device_enabled_callback_cb);
+      EXPECT_EQ(NULL, device_enabled_callback_cb);
+      EXPECT_EQ(NULL, device_disabled_callback_cb);
+      free(dummy_audio_area);
     }
 
     uint8_t buf_[kBufferSize];
@@ -73,15 +77,15 @@
   iodev.direction = CRAS_STREAM_OUTPUT;
   iodev.format = &fmt_;
   iodev.ext_format = &fmt_;
+  iodev.streams = NULL;
   enabled_dev = &iodev;
 
   // Open loopback devices.
-  EXPECT_EQ(0, loop_in_->open_dev(loop_in_));
+  EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
   EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
 
   // Signal an output device is enabled.
-  cras_iodev_list_set_device_enabled_callback_cb(&iodev, 1,
-      cras_iodev_list_set_device_enabled_callback_cb_data);
+  device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
 
   // Expect that a hook was added to the iodev
   ASSERT_NE(reinterpret_cast<loopback_hook_t>(NULL), loop_hook);
@@ -106,7 +110,7 @@
   time_now.tv_sec = 100;
   time_now.tv_nsec = 0;
 
-  EXPECT_EQ(0, loop_in_->open_dev(loop_in_));
+  EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
   EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
 
   // Should be 480 samples after 480/frame rate seconds
@@ -139,7 +143,7 @@
   iodev.streams = &stream;
   enabled_dev = &iodev;
 
-  loop_in_->open_dev(loop_in_);
+  loop_in_->configure_dev(loop_in_);
   ASSERT_NE(reinterpret_cast<void *>(NULL), loop_hook);
 
   // Loopback callback for the hook.
@@ -185,6 +189,7 @@
 
 void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node)
 {
+  DL_APPEND(iodev->nodes, node);
 }
 
 void cras_iodev_set_active_node(struct cras_iodev *iodev,
@@ -220,12 +225,15 @@
   return 0;
 }
 
-int cras_iodev_list_set_device_enabled_callback(device_enabled_callback_t cb,
-                                                void *cb_data)
+int cras_iodev_list_set_device_enabled_callback(
+    device_enabled_callback_t enabled_cb,
+    device_disabled_callback_t disabled_cb,
+    void *cb_data)
 {
   cras_iodev_list_set_device_enabled_callback_called++;
-  cras_iodev_list_set_device_enabled_callback_cb = cb;
-  cras_iodev_list_set_device_enabled_callback_cb_data = cb_data;
+  device_enabled_callback_cb = enabled_cb;
+  device_disabled_callback_cb = disabled_cb;
+  device_enabled_callback_cb_data = cb_data;
   return 0;
 }
 
diff --git a/cras/src/tests/observer_unittest.cc b/cras/src/tests/observer_unittest.cc
index d45a6a4..899590f 100644
--- a/cras/src/tests/observer_unittest.cc
+++ b/cras/src/tests/observer_unittest.cc
@@ -196,7 +196,7 @@
     ResetStubData();
     rc = cras_observer_server_init();
     ASSERT_EQ(0, rc);
-    EXPECT_EQ(13, cras_alert_create_called);
+    EXPECT_EQ(15, cras_alert_create_called);
     EXPECT_EQ(reinterpret_cast<void *>(output_volume_alert),
               cras_alert_add_callback_map[g_observer->alerts.output_volume]);
     EXPECT_EQ(reinterpret_cast<void *>(output_mute_alert),
@@ -230,6 +230,11 @@
                                                CRAS_STREAM_POST_MIX_PRE_DSP]]);
     EXPECT_EQ(reinterpret_cast<void *>(suspend_changed_alert),
        cras_alert_add_callback_map[g_observer->alerts.suspend_changed]);
+    EXPECT_EQ(reinterpret_cast<void *>(hotword_triggered_alert),
+        cras_alert_add_callback_map[g_observer->alerts.hotword_triggered]);
+    EXPECT_EQ(reinterpret_cast<void *>(non_empty_audio_state_changed_alert),
+        cras_alert_add_callback_map[
+                g_observer->alerts.non_empty_audio_state_changed]);
 
     cras_observer_get_ops(NULL, &ops1_);
     EXPECT_NE(0, cras_observer_ops_are_empty(&ops1_));
@@ -243,7 +248,7 @@
 
   virtual void TearDown() {
     cras_observer_server_free();
-    EXPECT_EQ(13, cras_alert_destroy_called);
+    EXPECT_EQ(15, cras_alert_destroy_called);
     ResetStubData();
   }
 
@@ -285,6 +290,7 @@
     EXPECT_NE(0, cras_observer_ops_are_empty(&ops2_));
     cras_observer_set_ops(client2_, &ops2_);
 
+    cras_observer_remove(client2_);
     cb_context.clear();
     alert(NULL, data);
     // No callbacks executed.
@@ -567,6 +573,33 @@
   DoObserverRemoveClear(num_active_streams_alert, data);
 };
 
+TEST_F(ObserverTest, NotifyHotwordTriggered) {
+  struct cras_observer_alert_data_hotword_triggered *data;
+
+  cras_observer_notify_hotword_triggered(100, 200);
+  EXPECT_EQ(cras_alert_pending_alert_value,
+      g_observer->alerts.hotword_triggered);
+  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+  data = reinterpret_cast<struct cras_observer_alert_data_hotword_triggered *>(
+          cras_alert_pending_data_value);
+  EXPECT_EQ(data->tv_sec, 100);
+  EXPECT_EQ(data->tv_nsec, 200);
+}
+
+TEST_F(ObserverTest, NonEmpyAudioStateChanged) {
+  struct cras_observer_non_empty_audio_state *data;
+
+  cras_observer_notify_non_empty_audio_state_changed(1);
+  EXPECT_EQ(cras_alert_pending_alert_value,
+    g_observer->alerts.non_empty_audio_state_changed);
+  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+  data = reinterpret_cast<struct cras_observer_non_empty_audio_state *>(
+    cras_alert_pending_data_value);
+  EXPECT_EQ(data->non_empty, 1);
+}
+
 // Stubs
 extern "C" {
 
diff --git a/cras/src/tests/polled_interval_checker_unittest.cc b/cras/src/tests/polled_interval_checker_unittest.cc
new file mode 100644
index 0000000..c799387
--- /dev/null
+++ b/cras/src/tests/polled_interval_checker_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "polled_interval_checker.h"
+}
+
+static const int INTERVAL_DURATION = 5;
+
+static struct timespec time_now;
+
+static struct polled_interval* create_interval() {
+  struct polled_interval *interval =
+    pic_polled_interval_create(INTERVAL_DURATION);
+  EXPECT_NE(interval, static_cast<struct polled_interval*>(NULL));
+  return interval;
+}
+
+TEST(PolledIntervalCheckerTest, CreateDestroy) {
+  // Create an interval, checks it is non-null.
+  struct polled_interval *interval = create_interval();
+
+  pic_polled_interval_destroy(&interval);
+
+  // Check it's been set to null.
+  EXPECT_EQ(interval, static_cast<struct polled_interval*>(NULL));
+}
+
+TEST(PolledIntervalCheckerTest, BasicFlow) {
+  // Set initial time.
+  time_now.tv_sec = 1000;
+  time_now.tv_nsec = 0;
+  pic_update_current_time();
+
+  // Create interval starting at initial time.
+  struct polled_interval *interval = create_interval();
+
+  // Check it hasn't elapsed.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time by less than the interval duration.
+  time_now.tv_sec += INTERVAL_DURATION / 2;
+  pic_update_current_time();
+
+  // Check the interval hasn't elapsed yet.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time past the duration of the interval.
+  time_now.tv_sec += INTERVAL_DURATION;
+
+  // We haven't updated the current time, check the interval hasn't
+  // elapsed (that it isn't calling clock_gettime without us asking it to).
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Update time, check the interval has elapsed.
+  pic_update_current_time();
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+  pic_polled_interval_destroy(&interval);
+}
+
+TEST(PolledIntervalCheckerTest, DoesNotResetAutomatically) {
+  // Set initial time.
+  time_now.tv_sec = 1000;
+  time_now.tv_nsec = 0;
+  pic_update_current_time();
+
+  struct polled_interval *interval = create_interval();
+
+  // Sanity check.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time so the interval elapses.
+  time_now.tv_sec += INTERVAL_DURATION;
+  pic_update_current_time();
+
+  // Check the interval has elapsed.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+
+  // Increment time further.
+  time_now.tv_sec += INTERVAL_DURATION * 2;
+  pic_update_current_time();
+
+  // Check the interval has still elapsed.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+
+  // Check repeated calls return true.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+  pic_polled_interval_destroy(&interval);
+}
+
+TEST(PolledIntervalCheckerTest, Reset) {
+  // Set initial time.
+  time_now.tv_sec = 1000;
+  time_now.tv_nsec = 0;
+  pic_update_current_time();
+
+  struct polled_interval *interval = create_interval();
+
+  // Sanity check.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time so the interval elapses.
+  time_now.tv_sec += INTERVAL_DURATION;
+  pic_update_current_time();
+
+  // Check the interval has elapsed.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+
+  // Increment time further.
+  time_now.tv_sec += INTERVAL_DURATION * 2;
+  pic_update_current_time();
+
+  // Check the interval has still elapsed.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+
+  // Reset the interval.
+  pic_interval_reset(interval);
+
+  // Check it's been reset.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time to just before it should elapse again.
+  time_now.tv_sec += INTERVAL_DURATION - 1;
+  pic_update_current_time();
+
+  // Check it still has not elapsed.
+  EXPECT_FALSE(pic_interval_elapsed(interval));
+
+  // Increment time to one duration after we reset it.
+  time_now.tv_sec += 1;
+  pic_update_current_time();
+
+  // Check the interval has elapsed now.
+  EXPECT_TRUE(pic_interval_elapsed(interval));
+  pic_polled_interval_destroy(&interval);
+}
+
+/* Stubs */
+extern "C" {
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+  *tp = time_now;
+  return 0;
+}
+
+}  // extern "C"
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/rclient_unittest.cc b/cras/src/tests/rclient_unittest.cc
index e699c7e..0d32c2a 100644
--- a/cras/src/tests/rclient_unittest.cc
+++ b/cras/src/tests/rclient_unittest.cc
@@ -35,6 +35,7 @@
 static int cras_system_set_capture_mute_called;
 static size_t cras_system_set_capture_mute_locked_value;
 static int cras_system_set_capture_mute_locked_called;
+static int cras_system_state_dump_snapshots_called;
 static size_t cras_make_fd_nonblocking_called;
 static audio_thread* iodev_get_thread_return;
 static int stream_list_add_stream_return;
@@ -42,6 +43,7 @@
 static unsigned int stream_list_disconnect_stream_called;
 static unsigned int cras_iodev_list_rm_input_called;
 static unsigned int cras_iodev_list_rm_output_called;
+static unsigned int cras_server_metrics_stream_config_called;
 static struct cras_rstream dummy_rstream;
 static size_t cras_observer_num_ops_registered;
 static size_t cras_observer_register_notify_called;
@@ -59,6 +61,7 @@
   cras_rstream_create_return = 0;
   cras_rstream_create_stream_out = (struct cras_rstream *)NULL;
   cras_iodev_attach_stream_retval = 0;
+  cras_server_metrics_stream_config_called = 0;
   cras_system_set_volume_value = 0;
   cras_system_set_volume_called = 0;
   cras_system_set_capture_gain_value = 0;
@@ -73,6 +76,7 @@
   cras_system_set_capture_mute_called = 0;
   cras_system_set_capture_mute_locked_value = 0;
   cras_system_set_capture_mute_locked_called = 0;
+  cras_system_state_dump_snapshots_called = 0;
   cras_make_fd_nonblocking_called = 0;
   iodev_get_thread_return = reinterpret_cast<audio_thread*>(0xad);
   stream_list_add_stream_return = 0;
@@ -139,6 +143,7 @@
       stream_id_ = 0x10002;
       connect_msg_.header.id = CRAS_SERVER_CONNECT_STREAM;
       connect_msg_.header.length = sizeof(connect_msg_);
+      connect_msg_.proto_version = CRAS_PROTO_VER;
       connect_msg_.stream_type = CRAS_STREAM_TYPE_DEFAULT;
       connect_msg_.direction = CRAS_STREAM_OUTPUT;
       connect_msg_.stream_id = stream_id_;
@@ -187,6 +192,7 @@
   EXPECT_EQ(0, cras_iodev_list_rm_output_called);
   EXPECT_EQ(1, stream_list_add_stream_called);
   EXPECT_EQ(0, stream_list_disconnect_stream_called);
+  EXPECT_EQ(0, cras_server_metrics_stream_config_called);
 }
 
 TEST_F(RClientMessagesSuite, ConnectMsgWithBadFd) {
@@ -202,6 +208,30 @@
   EXPECT_NE(0, out_msg.err);
   EXPECT_EQ(stream_list_add_stream_called,
             stream_list_disconnect_stream_called);
+  EXPECT_EQ(0, cras_server_metrics_stream_config_called);
+}
+
+TEST_F(RClientMessagesSuite, ConnectMsgFromOldClient) {
+  struct cras_client_stream_connected_old out_msg;
+  int rc;
+
+  cras_rstream_create_stream_out = rstream_;
+  cras_iodev_attach_stream_retval = 0;
+
+  connect_msg_.header.length = sizeof(struct cras_connect_message_old);
+  connect_msg_.proto_version = CRAS_PROTO_VER - 1;
+
+  rc = cras_rclient_message_from_client(rclient_, &connect_msg_.header, 100);
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ(1, cras_make_fd_nonblocking_called);
+
+  rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
+  EXPECT_EQ(sizeof(out_msg), rc);
+  EXPECT_EQ(stream_id_, out_msg.stream_id);
+  EXPECT_EQ(0, out_msg.err);
+  EXPECT_EQ(1, stream_list_add_stream_called);
+  EXPECT_EQ(0, stream_list_disconnect_stream_called);
+  EXPECT_EQ(1, cras_server_metrics_stream_config_called);
 }
 
 TEST_F(RClientMessagesSuite, SuccessReply) {
@@ -221,6 +251,7 @@
   EXPECT_EQ(0, out_msg.err);
   EXPECT_EQ(1, stream_list_add_stream_called);
   EXPECT_EQ(0, stream_list_disconnect_stream_called);
+  EXPECT_EQ(1, cras_server_metrics_stream_config_called);
 }
 
 TEST_F(RClientMessagesSuite, SuccessCreateThreadReply) {
@@ -240,6 +271,7 @@
   EXPECT_EQ(0, out_msg.err);
   EXPECT_EQ(1, stream_list_add_stream_called);
   EXPECT_EQ(0, stream_list_disconnect_stream_called);
+  EXPECT_EQ(1, cras_server_metrics_stream_config_called);
 }
 
 TEST_F(RClientMessagesSuite, SetVolume) {
@@ -324,6 +356,15 @@
   EXPECT_EQ(1, cras_system_set_capture_mute_locked_value);
 }
 
+TEST_F(RClientMessagesSuite, DumpSnapshots) {
+  struct cras_dump_snapshots msg;
+  int rc;
+  cras_fill_dump_snapshots(&msg);
+  rc = cras_rclient_message_from_client(rclient_, &msg.header, -1);
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ(1, cras_system_state_dump_snapshots_called);
+}
+
 void RClientMessagesSuite::RegisterNotification(
     enum CRAS_CLIENT_MESSAGE_ID msg_id,
     void *callback, void **ops_address) {
@@ -361,8 +402,9 @@
   }
   if (!msg.do_register)
     cras_observer_num_ops_registered--;
-  if (cras_observer_num_ops_registered)
+  if (cras_observer_num_ops_registered) {
     EXPECT_EQ(callback, *ops_address);
+  }
 }
 
 TEST_F(RClientMessagesSuite, RegisterStatusNotification) {
@@ -548,7 +590,7 @@
   ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
   EXPECT_EQ(msg->header.id, CRAS_CLIENT_ACTIVE_NODE_CHANGED);
   EXPECT_EQ(msg->direction, (int32_t)dir);
-  EXPECT_EQ(msg->node_id, node_id);
+  EXPECT_EQ((uint64_t)msg->node_id, node_id);
 }
 
 TEST_F(RClientMessagesSuite, SendOutputNodeVolumeChanged) {
@@ -676,6 +718,20 @@
   return 0;
 }
 
+int audio_thread_set_aec_dump(struct audio_thread *thread,
+			      cras_stream_id_t stream_id,
+			      unsigned int start,
+			      int fd)
+{
+  return 0;
+}
+
+#ifdef HAVE_WEBRTC_APM
+void cras_apm_list_reload_aec_config()
+{
+}
+#endif
+
 const char *cras_config_get_socket_file_dir()
 {
   return CRAS_UT_TMPDIR;
@@ -688,6 +744,11 @@
   return cras_rstream_create_return;
 }
 
+unsigned int cras_rstream_get_effects(const struct cras_rstream *stream)
+{
+  return 0;
+}
+
 int cras_iodev_move_stream_type(uint32_t type, uint32_t index)
 {
   return 0;
@@ -713,6 +774,11 @@
   return 0;
 }
 
+void cras_system_state_dump_snapshots()
+{
+  cras_system_state_dump_snapshots_called ++;
+}
+
 void cras_system_set_volume(size_t volume)
 {
   cras_system_set_volume_value = volume;
@@ -894,4 +960,10 @@
   cras_observer_remove_called++;
 }
 
+int cras_server_metrics_stream_config(struct cras_rstream_config *config)
+{
+  cras_server_metrics_stream_config_called++;
+  return 0;
+}
+
 }  // extern "C"
diff --git a/cras/src/tests/rstream_stub.cc b/cras/src/tests/rstream_stub.cc
new file mode 100644
index 0000000..d0b7a0a
--- /dev/null
+++ b/cras/src/tests/rstream_stub.cc
@@ -0,0 +1,130 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <unordered_map>
+
+extern "C" {
+#include "cras_rstream.h"
+}
+
+namespace {
+
+struct cb_data {
+	std::unordered_map<unsigned int, unsigned int> dev_offset;
+	int pending_reply;
+};
+std::unordered_map<const cras_rstream*, cb_data> data_map;
+
+};
+
+void rstream_stub_reset() {
+  data_map.clear();
+}
+
+void rstream_stub_dev_offset(const cras_rstream* rstream,
+                             unsigned int dev_id,
+                             unsigned int offset) {
+  auto data = data_map.find(rstream);
+  if (data == data_map.end()) {
+    cb_data new_data;
+    new_data.dev_offset[dev_id] = offset;
+    data_map.insert({rstream, new_data});
+  } else {
+    data->second.dev_offset[dev_id] = offset;
+  }
+}
+
+void rstream_stub_pending_reply(const cras_rstream* rstream,
+                                int ret_value) {
+  auto data = data_map.find(rstream);
+  if (data == data_map.end()) {
+    cb_data new_data;
+    new_data.pending_reply = ret_value;
+    data_map.insert({rstream, new_data});
+  } else {
+    data->second.pending_reply = ret_value;
+  }
+}
+
+extern "C" {
+
+void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
+                                        const struct timespec *now) {
+}
+
+void cras_rstream_dev_attach(struct cras_rstream *rstream,
+                             unsigned int dev_id,
+                             void *dev_ptr) {
+}
+
+void cras_rstream_dev_detach(struct cras_rstream *rstream, unsigned int dev_id) {
+}
+
+unsigned int cras_rstream_dev_offset(const struct cras_rstream *rstream,
+                                     unsigned int dev_id) {
+  auto elem = data_map.find(rstream);
+  if (elem != data_map.end())
+    return elem->second.dev_offset[dev_id];
+  return 0;
+}
+
+void cras_rstream_dev_offset_update(struct cras_rstream *rstream,
+                                    unsigned int frames,
+                                    unsigned int dev_id) {
+}
+
+unsigned int cras_rstream_playable_frames(struct cras_rstream *rstream,
+                                          unsigned int dev_id) {
+  return 0;
+}
+
+float cras_rstream_get_volume_scaler(struct cras_rstream *rstream) {
+  return 0.0;
+}
+
+int cras_rstream_get_mute(const struct cras_rstream *rstream) {
+  return 0;
+}
+
+uint8_t *cras_rstream_get_readable_frames(struct cras_rstream *rstream,
+                                          unsigned int offset,
+                                          size_t *frames) {
+  return NULL;
+}
+
+void cras_rstream_update_input_write_pointer(struct cras_rstream *rstream) {
+}
+
+void cras_rstream_update_output_read_pointer(struct cras_rstream *rstream) {
+}
+
+int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count) {
+  cras_shm_buffer_write_complete(&stream->shm);
+  return 0;
+}
+
+int cras_rstream_request_audio(struct cras_rstream *stream,
+                               const struct timespec *now) {
+  return 0;
+}
+
+void cras_rstream_update_queued_frames(struct cras_rstream *rstream)
+{
+}
+
+int cras_rstream_is_pending_reply(const struct cras_rstream *rstream)
+{
+  auto elem = data_map.find(rstream);
+  if (elem != data_map.end())
+    return elem->second.pending_reply;
+  return 0;
+}
+
+int cras_rstream_flush_old_audio_messages(struct cras_rstream *rstream)
+{
+  return 0;
+}
+
+} // extern "C"
diff --git a/cras/src/tests/rstream_stub.h b/cras/src/tests/rstream_stub.h
new file mode 100644
index 0000000..39a48b2
--- /dev/null
+++ b/cras/src/tests/rstream_stub.h
@@ -0,0 +1,21 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef RSTREAM_STUB_H_
+#define RSTREAM_STUB_H_
+
+#include <time.h>
+
+void rstream_stub_reset();
+
+void rstream_stub_dev_offset(const cras_rstream* rstream,
+			     unsigned int dev_id,
+			     unsigned int offset);
+
+// Stub that rstream is pending the reply from client or not.
+void rstream_stub_pending_reply(const cras_rstream* rstream,
+                                int ret_value);
+
+#endif // RSTREAM_STUB_H_
diff --git a/cras/src/tests/rstream_unittest.cc b/cras/src/tests/rstream_unittest.cc
index 8034bfb..2261d47 100644
--- a/cras/src/tests/rstream_unittest.cc
+++ b/cras/src/tests/rstream_unittest.cc
@@ -5,6 +5,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/mman.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <gtest/gtest.h>
 
@@ -20,6 +21,9 @@
 class RstreamTestSuite : public testing::Test {
   protected:
     virtual void SetUp() {
+      int rc;
+      int sock[2] = {-1, -1};
+
       fmt_.format = SND_PCM_FORMAT_S16_LE;
       fmt_.frame_rate = 48000;
       fmt_.num_channels = 2;
@@ -32,18 +36,44 @@
       config_.format = &fmt_;
       config_.buffer_frames = 4096;
       config_.cb_threshold = 2048;
-      config_.audio_fd = 1;
+
+      // Create a socket pair because it will be used in rstream.
+      rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+      ASSERT_EQ(0, rc);
+      config_.audio_fd = sock[1];
+      client_fd_ = sock[0];
+
       config_.client = NULL;
     }
 
+    virtual void TearDown() {
+      close(config_.audio_fd);
+      close(client_fd_);
+    }
+
     static bool format_equal(cras_audio_format *fmt1, cras_audio_format *fmt2) {
       return fmt1->format == fmt2->format &&
           fmt1->frame_rate == fmt2->frame_rate &&
           fmt1->num_channels == fmt2->num_channels;
     }
 
+    void stub_client_reply(enum CRAS_AUDIO_MESSAGE_ID id, int frames, int err) {
+      int rc;
+      struct audio_message aud_msg;
+      // Create a message.
+      aud_msg.id = id;
+      aud_msg.frames = frames;
+      aud_msg.error = err;
+
+      // Use socket fd to stub message from client.
+      rc = write(client_fd_, &aud_msg, sizeof(aud_msg));
+      EXPECT_EQ(sizeof(aud_msg), rc);
+      return;
+    }
+
     struct cras_audio_format fmt_;
     struct cras_rstream_config config_;
+    int client_fd_;
 };
 
 TEST_F(RstreamTestSuite, InvalidDirection) {
@@ -193,6 +223,120 @@
   cras_rstream_destroy(s);
 }
 
+TEST_F(RstreamTestSuite, OutputStreamIsPendingReply) {
+  struct cras_rstream *s;
+  int rc;
+  struct timespec ts;
+
+  rc = cras_rstream_create(&config_, &s);
+  EXPECT_EQ(0, rc);
+
+  // Not pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  // Request some data from client.
+  rc = cras_rstream_request_audio(s, &ts);
+  EXPECT_GT(rc, 0);
+
+  // Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(1, rc);
+
+  cras_rstream_destroy(s);
+}
+
+TEST_F(RstreamTestSuite, OutputStreamFlushMessages) {
+  struct cras_rstream *s;
+  int rc;
+  struct timespec ts;
+
+  rc = cras_rstream_create(&config_, &s);
+  EXPECT_EQ(0, rc);
+
+  // Not pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  // Request some data from client.
+  rc = cras_rstream_request_audio(s, &ts);
+  EXPECT_GT(rc, 0);
+
+  // Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(1, rc);
+
+  // Client replies that data is ready.
+  stub_client_reply(AUDIO_MESSAGE_DATA_READY, 10, 0);
+
+  // Read messages.
+  cras_rstream_flush_old_audio_messages(s);
+
+  // NOT Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  cras_rstream_destroy(s);
+}
+
+TEST_F(RstreamTestSuite, InputStreamIsPendingReply) {
+  struct cras_rstream *s;
+  int rc;
+
+  config_.direction = CRAS_STREAM_INPUT;
+
+  rc = cras_rstream_create(&config_, &s);
+  EXPECT_EQ(0, rc);
+
+  // Not pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  // Some data is ready. Sends it to client.
+  rc = cras_rstream_audio_ready(s, 10);
+  EXPECT_GT(rc, 0);
+
+  // Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(1, rc);
+
+  cras_rstream_destroy(s);
+}
+
+TEST_F(RstreamTestSuite, InputStreamFlushMessages) {
+  struct cras_rstream *s;
+  int rc;
+
+  config_.direction = CRAS_STREAM_INPUT;
+
+  rc = cras_rstream_create(&config_, &s);
+  EXPECT_EQ(0, rc);
+
+  // Not pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  // Some data is ready. Sends it to client.
+  rc = cras_rstream_audio_ready(s, 10);
+  EXPECT_GT(rc, 0);
+
+  // Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(1, rc);
+
+  // Client replies that data is captured.
+  stub_client_reply(AUDIO_MESSAGE_DATA_CAPTURED, 10, 0);
+
+  // Read messages.
+  cras_rstream_flush_old_audio_messages(s);
+
+  // NOT Pending reply.
+  rc = cras_rstream_is_pending_reply(s);
+  EXPECT_EQ(0, rc);
+
+  cras_rstream_destroy(s);
+}
+
 }  //  namespace
 
 int main(int argc, char **argv) {
@@ -249,5 +393,28 @@
 
 void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) {
 }
-
+#ifdef HAVE_WEBRTC_APM
+struct cras_apm_list *cras_apm_list_create(void *stream_ptr,
+					   uint64_t effects)
+{
+  return NULL;
+}
+int cras_apm_list_destroy(struct cras_apm_list *list)
+{
+  return 0;
+}
+uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
+{
+  return APM_ECHO_CANCELLATION;
+}
+struct cras_apm *cras_apm_list_get(struct cras_apm_list *list,
+           void *dev_ptr)
+{
+  return NULL;
+}
+struct cras_audio_format *cras_apm_list_get_format(struct cras_apm *apm)
+{
+  return NULL;
+}
+#endif
 }
diff --git a/cras/src/tests/server_metrics_unittest.cc b/cras/src/tests/server_metrics_unittest.cc
index db1b69f..c7d2339 100644
--- a/cras/src/tests/server_metrics_unittest.cc
+++ b/cras/src/tests/server_metrics_unittest.cc
@@ -8,6 +8,7 @@
 extern "C" {
 #include "cras_server_metrics.c"
 #include "cras_main_message.h"
+#include "cras_rstream.h"
 }
 
 static enum CRAS_MAIN_MESSAGE_TYPE type_set;
@@ -27,7 +28,33 @@
   EXPECT_EQ(type_set, CRAS_MAIN_METRICS);
 }
 
-TEST(ServerMetricsTestSuite, SetMetrics) {
+TEST(ServerMetricsTestSuite, SetMetricHighestHardwareLevel) {
+  ResetStubData();
+  unsigned int hw_level = 1000;
+  sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
+
+  cras_server_metrics_highest_hw_level(hw_level, CRAS_STREAM_INPUT);
+
+  EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
+  EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+  EXPECT_EQ(sent_msg->metrics_type, HIGHEST_INPUT_HW_LEVEL);
+  EXPECT_EQ(sent_msg->data.value, hw_level);
+
+  free(sent_msg);
+
+  sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
+
+  cras_server_metrics_highest_hw_level(hw_level, CRAS_STREAM_OUTPUT);
+
+  EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
+  EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+  EXPECT_EQ(sent_msg->metrics_type, HIGHEST_OUTPUT_HW_LEVEL);
+  EXPECT_EQ(sent_msg->data.value, hw_level);
+
+  free(sent_msg);
+}
+
+TEST(ServerMetricsTestSuite, SetMetricsLongestFetchDelay) {
   ResetStubData();
   unsigned int delay = 100;
   sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
@@ -37,7 +64,47 @@
   EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
   EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
   EXPECT_EQ(sent_msg->metrics_type, LONGEST_FETCH_DELAY);
-  EXPECT_EQ(sent_msg->data, delay);
+  EXPECT_EQ(sent_msg->data.value, delay);
+
+  free(sent_msg);
+}
+
+TEST(ServerMetricsTestSuite, SetMetricsNumUnderruns) {
+  ResetStubData();
+  unsigned int underrun = 10;
+  sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
+
+  cras_server_metrics_num_underruns(underrun);
+
+  EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
+  EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+  EXPECT_EQ(sent_msg->metrics_type, NUM_UNDERRUNS);
+  EXPECT_EQ(sent_msg->data.value, underrun);
+
+  free(sent_msg);
+}
+
+TEST(ServerMetricsTestSuite, SetMetricsStreamConfig) {
+  ResetStubData();
+  struct cras_rstream_config config;
+  struct cras_audio_format format;
+  sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
+
+  config.cb_threshold = 1024;
+  config.flags = BULK_AUDIO_OK;
+  format.format = SND_PCM_FORMAT_S16_LE;
+  format.frame_rate = 48000;
+
+  config.format = &format;
+  cras_server_metrics_stream_config(&config);
+
+  EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
+  EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+  EXPECT_EQ(sent_msg->metrics_type, STREAM_CONFIG);
+  EXPECT_EQ(sent_msg->data.stream_config.cb_threshold, 1024);
+  EXPECT_EQ(sent_msg->data.stream_config.flags, BULK_AUDIO_OK);
+  EXPECT_EQ(sent_msg->data.stream_config.format, SND_PCM_FORMAT_S16_LE);
+  EXPECT_EQ(sent_msg->data.stream_config.rate, 48000);
 
   free(sent_msg);
 }
@@ -55,6 +122,10 @@
                                 int max, int nbuckets) {
 }
 
+void cras_metrics_log_sparse_histogram(const char *name, int sample)
+{
+}
+
 int cras_main_message_send(struct cras_main_message *msg) {
   // Copy the sent message so we can examine it in the test later.
   memcpy(sent_msg, msg, sizeof(*sent_msg));
diff --git a/cras/src/tests/shm_unittest.cc b/cras/src/tests/shm_unittest.cc
index c0bec89..9c36432 100644
--- a/cras/src/tests/shm_unittest.cc
+++ b/cras/src/tests/shm_unittest.cc
@@ -251,6 +251,32 @@
   EXPECT_EQ(1, cras_shm_num_overruns(&shm_));
 }
 
+TEST_F(ShmTestSuite, GetWritableFramesNeedToWrite) {
+  unsigned buffer_size = 480;
+  unsigned limit = 480;
+  unsigned written = 200;
+  unsigned frames;
+  shm_.area->write_buf_idx = 0;
+  shm_.config.used_size = buffer_size * shm_.config.frame_bytes;
+  shm_.area->write_offset[0] = written * shm_.config.frame_bytes;
+  buf_ = cras_shm_get_writeable_frames(&shm_, limit, &frames);
+  EXPECT_EQ(limit - written, frames);
+  EXPECT_EQ(shm_.area->samples + shm_.area->write_offset[0], buf_);
+}
+
+TEST_F(ShmTestSuite, GetWritableFramesNoNeedToWrite) {
+  unsigned buffer_size = 480;
+  unsigned limit = 240;
+  unsigned written = 300;
+  unsigned frames;
+  shm_.area->write_buf_idx = 0;
+  shm_.config.used_size = buffer_size * shm_.config.frame_bytes;
+  shm_.area->write_offset[0] = written * shm_.config.frame_bytes;
+  buf_ = cras_shm_get_writeable_frames(&shm_, limit, &frames);
+  EXPECT_EQ(0, frames);
+  EXPECT_EQ(shm_.area->samples + shm_.area->write_offset[0], buf_);
+}
+
 }  //  namespace
 
 int main(int argc, char **argv) {
diff --git a/cras/src/tests/system_state_unittest.cc b/cras/src/tests/system_state_unittest.cc
index 5816b77..d71d5f5 100644
--- a/cras/src/tests/system_state_unittest.cc
+++ b/cras/src/tests/system_state_unittest.cc
@@ -7,6 +7,7 @@
 
 extern "C" {
 #include "cras_alert.h"
+#include "cras_shm.h"
 #include "cras_system_state.h"
 #include "cras_types.h"
 }
@@ -17,8 +18,10 @@
 size_t cras_alsa_card_destroy_called;
 static size_t add_stub_called;
 static size_t rm_stub_called;
+static size_t add_task_stub_called;
 static size_t callback_stub_called;
 static void *select_data_value;
+static void *task_data_value;
 static size_t add_callback_called;
 static cras_alert_cb add_callback_cb;
 static void *add_callback_arg;
@@ -41,11 +44,12 @@
   kFakeAlsaCard = reinterpret_cast<struct cras_alsa_card*>(0x33);
   add_stub_called = 0;
   rm_stub_called = 0;
+  add_task_stub_called = 0;
   callback_stub_called = 0;
   add_callback_called = 0;
   rm_callback_called = 0;
   alert_pending_called = 0;
-  device_config_dir = reinterpret_cast<char *>(3);
+  device_config_dir = NULL;
   cras_alsa_card_config_dir = NULL;
   cras_observer_notify_output_volume_called = 0;
   cras_observer_notify_output_mute_called = 0;
@@ -67,12 +71,42 @@
   select_data_value = select_data;
 }
 
+static int add_task_stub(void (*cb)(void *data),
+                         void *callback_data,
+                         void *task_data)
+{
+  add_task_stub_called++;
+  task_data_value = task_data;
+  return 0;
+}
+
 static void callback_stub(void *data) {
   callback_stub_called++;
 }
 
+static void do_sys_init() {
+  char *shm_name;
+  ASSERT_GT(asprintf(&shm_name, "/cras-%d", getpid()), 0);
+  int rw_shm_fd;
+  int ro_shm_fd;
+  struct cras_server_state *exp_state = (struct cras_server_state *)
+    cras_shm_setup(shm_name,
+                   sizeof(*exp_state),
+                   &rw_shm_fd,
+                   &ro_shm_fd);
+  if (!exp_state)
+    exit(-1);
+  cras_system_state_init(device_config_dir,
+                         shm_name,
+                         rw_shm_fd,
+                         ro_shm_fd,
+                         exp_state,
+                         sizeof(*exp_state));
+  free(shm_name);
+}
+
 TEST(SystemStateSuite, DefaultVolume) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   EXPECT_EQ(100, cras_system_get_volume());
   EXPECT_EQ(2000, cras_system_get_capture_gain());
   EXPECT_EQ(0, cras_system_get_mute());
@@ -81,7 +115,7 @@
 }
 
 TEST(SystemStateSuite, SetVolume) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   cras_system_set_volume(0);
   EXPECT_EQ(0, cras_system_get_volume());
   cras_system_set_volume(50);
@@ -95,7 +129,7 @@
 }
 
 TEST(SystemStateSuite, SetMinMaxVolume) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   cras_system_set_volume_limits(-10000, -600);
   EXPECT_EQ(-10000, cras_system_get_min_volume());
   EXPECT_EQ(-600, cras_system_get_max_volume());
@@ -103,7 +137,7 @@
 }
 
 TEST(SystemStateSuite, SetCaptureVolume) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   cras_system_set_capture_gain(0);
   EXPECT_EQ(0, cras_system_get_capture_gain());
   cras_system_set_capture_gain(3000);
@@ -116,7 +150,7 @@
 }
 
 TEST(SystemStateSuite, SetCaptureVolumeStoreTarget) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   cras_system_set_capture_gain_limits(-2000, 2000);
   cras_system_set_capture_gain(3000);
   // Gain is within the limit.
@@ -132,7 +166,7 @@
 }
 
 TEST(SystemStateSuite, SetMinMaxCaptureGain) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   cras_system_set_capture_gain(3000);
   cras_system_set_capture_gain_limits(-2000, 2000);
   EXPECT_EQ(-2000, cras_system_get_min_capture_gain());
@@ -144,7 +178,7 @@
 
 TEST(SystemStateSuite, SetUserMute) {
   ResetStubData();
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
 
   EXPECT_EQ(0, cras_system_get_mute());
 
@@ -165,7 +199,7 @@
 
 TEST(SystemStateSuite, SetMute) {
   ResetStubData();
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
 
   EXPECT_EQ(0, cras_system_get_mute());
 
@@ -184,8 +218,54 @@
   cras_system_state_deinit();
 }
 
+TEST(SystemStateSuite, SetSystemMuteThenSwitchUserMute) {
+  ResetStubData();
+  do_sys_init();
+
+  EXPECT_EQ(0, cras_system_get_mute());
+
+  // Set system mute.
+  cras_system_set_mute(1);
+
+  // Switching user mute will not notify observer.
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+  cras_system_set_user_mute(1);
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+  cras_system_set_user_mute(0);
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+
+  // Unset system mute.
+  cras_system_set_mute(0);
+  EXPECT_EQ(2, cras_observer_notify_output_mute_called);
+
+  cras_system_state_deinit();
+}
+
+TEST(SystemStateSuite, SetUserMuteThenSwitchSystemMute) {
+  ResetStubData();
+  do_sys_init();
+
+  EXPECT_EQ(0, cras_system_get_mute());
+
+  // Set user mute.
+  cras_system_set_user_mute(1);
+
+  // Switching system mute will not notify observer.
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+  cras_system_set_mute(1);
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+  cras_system_set_mute(0);
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+
+  // Unset user mute.
+  cras_system_set_user_mute(0);
+  EXPECT_EQ(2, cras_observer_notify_output_mute_called);
+
+  cras_system_state_deinit();
+}
+
 TEST(SystemStateSuite, CaptureMuteChangedCallbackMultiple) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   ResetStubData();
 
   cras_system_set_capture_mute(1);
@@ -199,7 +279,7 @@
 }
 
 TEST(SystemStateSuite, MuteLocked) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   ResetStubData();
 
   cras_system_set_mute(1);
@@ -211,7 +291,7 @@
   cras_system_set_mute(0);
   EXPECT_EQ(1, cras_system_get_mute());
   EXPECT_EQ(1, cras_system_get_mute_locked());
-  EXPECT_EQ(2, cras_observer_notify_output_mute_called);
+  EXPECT_EQ(1, cras_observer_notify_output_mute_called);
 
   cras_system_set_capture_mute(1);
   EXPECT_EQ(1, cras_system_get_capture_mute());
@@ -227,7 +307,7 @@
 }
 
 TEST(SystemStateSuite, Suspend) {
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   ResetStubData();
 
   cras_system_set_suspended(1);
@@ -248,7 +328,7 @@
 
   info.card_type = ALSA_CARD_TYPE_INTERNAL;
   info.card_index = 0;
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   EXPECT_EQ(-ENOMEM, cras_system_add_alsa_card(&info));
   EXPECT_EQ(1, cras_alsa_card_create_called);
   EXPECT_EQ(cras_alsa_card_config_dir, device_config_dir);
@@ -261,7 +341,7 @@
 
   info.card_type = ALSA_CARD_TYPE_INTERNAL;
   info.card_index = 0;
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   EXPECT_EQ(0, cras_system_add_alsa_card(&info));
   EXPECT_EQ(1, cras_alsa_card_create_called);
   EXPECT_EQ(cras_alsa_card_config_dir, device_config_dir);
@@ -281,7 +361,7 @@
   int rc;
 
   ResetStubData();
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
   rc = cras_system_add_select_fd(7, callback_stub, stub_data);
   EXPECT_NE(0, rc);
   EXPECT_EQ(0, add_stub_called);
@@ -305,9 +385,29 @@
   cras_system_state_deinit();
 }
 
+TEST(SystemSettingsAddTask, AddTask) {
+  void *stub_data = reinterpret_cast<void *>(44);
+  void *task_data = reinterpret_cast<void *>(33);
+  int rc;
+
+  do_sys_init();
+  rc = cras_system_add_task(callback_stub, stub_data);
+  EXPECT_NE(0, rc);
+  EXPECT_EQ(0, add_task_stub_called);
+  rc = cras_system_set_add_task_handler(add_task_stub, task_data);
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ(0, add_task_stub_called);
+  rc = cras_system_add_task(callback_stub, stub_data);
+  EXPECT_EQ(0, rc);
+  EXPECT_EQ(1, add_task_stub_called);
+  EXPECT_EQ(task_data, task_data_value);
+
+  cras_system_state_deinit();
+}
+
 TEST(SystemSettingsStreamCount, StreamCount) {
   ResetStubData();
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
 
   EXPECT_EQ(0, cras_system_state_get_active_streams());
   cras_system_state_stream_added(CRAS_STREAM_OUTPUT);
@@ -324,7 +424,7 @@
 
 TEST(SystemSettingsStreamCount, StreamCountByDirection) {
   ResetStubData();
-  cras_system_state_init(device_config_dir);
+  do_sys_init();
 
   EXPECT_EQ(0, cras_system_state_get_active_streams());
   cras_system_state_stream_added(CRAS_STREAM_OUTPUT);
diff --git a/cras/src/tests/timing_unittest.cc b/cras/src/tests/timing_unittest.cc
new file mode 100644
index 0000000..bae0fee
--- /dev/null
+++ b/cras/src/tests/timing_unittest.cc
@@ -0,0 +1,459 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "dev_io.h" // tested
+#include "dev_stream.h" // tested
+#include "cras_rstream.h" // stubbed
+#include "cras_iodev.h" // stubbed
+#include "cras_shm.h"
+#include "cras_types.h"
+#include "utlist.h"
+
+struct audio_thread_event_log* atlog;
+}
+
+#include "dev_io_stubs.h"
+#include "iodev_stub.h"
+#include "rstream_stub.h"
+
+#define FAKE_POLL_FD 33
+
+namespace {
+
+class TimingSuite : public testing::Test{
+ protected:
+  virtual void SetUp() {
+    atlog = static_cast<audio_thread_event_log*>(calloc(1, sizeof(*atlog)));
+    iodev_stub_reset();
+    rstream_stub_reset();
+  }
+
+  virtual void TearDown() {
+    free(atlog);
+  }
+
+  timespec SingleInputDevNextWake(
+      size_t dev_cb_threshold,
+      size_t dev_level,
+      const timespec* level_timestamp,
+      cras_audio_format* dev_format,
+      const std::vector<StreamPtr>& streams,
+      CRAS_NODE_TYPE active_node_type = CRAS_NODE_TYPE_MIC) {
+    struct open_dev* dev_list_ = NULL;
+
+    DevicePtr dev = create_device(CRAS_STREAM_INPUT, dev_cb_threshold,
+                                  dev_format, active_node_type);
+    dev->dev->input_streaming = true;
+    DL_APPEND(dev_list_, dev->odev.get());
+
+    for (auto const& stream : streams) {
+      add_stream_to_dev(dev->dev, stream);
+    }
+
+    // Set response for frames_queued.
+    iodev_stub_frames_queued(dev->dev.get(), dev_level, *level_timestamp);
+
+    dev_io_send_captured_samples(dev_list_);
+
+    struct timespec dev_time;
+    dev_time.tv_sec = level_timestamp->tv_sec + 500; // Far in the future.
+    dev_io_next_input_wake(&dev_list_, &dev_time);
+    return dev_time;
+  }
+};
+
+// One device, one stream, write a callback of data and check the sleep time is
+// one more wakeup interval.
+TEST_F(TimingSuite, WaitAfterFill) {
+  const size_t cb_threshold = 480;
+
+  cras_audio_format format;
+  fill_audio_format(&format, 48000);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  // rstream's next callback is now and there is enough data to fill.
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream.get(), 480);
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  timespec dev_time = SingleInputDevNextWake(cb_threshold, 0, &start,
+                                             &format, streams);
+
+  // The next callback should be scheduled 10ms in the future.
+  // And the next wake up should reflect the only attached stream.
+  EXPECT_EQ(dev_time.tv_sec, streams[0]->rstream->next_cb_ts.tv_sec);
+  EXPECT_EQ(dev_time.tv_nsec, streams[0]->rstream->next_cb_ts.tv_nsec);
+}
+
+// One device with one stream which has block_size larger than the device buffer
+// level. If the device buffer level = 0, the input device wake time should be
+// set to (buffer_size / 2) / device_rate secs.
+TEST_F(TimingSuite, LargeCallbackStreamWithEmptyBuffer) {
+  const size_t cb_threshold = 3000;
+  const size_t dev_cb_threshold = 1200;
+  const size_t dev_level = 0;
+
+  cras_audio_format format;
+  fill_audio_format(&format, 48000);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream->rstream->next_cb_ts = start;
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  timespec dev_time = SingleInputDevNextWake(
+      dev_cb_threshold, dev_level, &start, &format, streams);
+
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  // The next dev wake ts should be 25ms since the buffer level is empty and
+  // 1200 / 48000 = 0.025.
+  EXPECT_EQ(delta.tv_sec, 0);
+  EXPECT_LT(delta.tv_nsec, 25000000 + 5000 * 1000);
+  EXPECT_GT(delta.tv_nsec, 25000000 - 5000 * 1000);
+}
+
+// One device with one stream which has block_size larger than the device buffer
+// level. If the device buffer level = buffer_size / 2, the input device wake
+// time should be set to max(0, 5ms) = 5ms to prevent busy loop occurs.
+TEST_F(TimingSuite, LargeCallbackStreamWithHalfFullBuffer) {
+  const size_t cb_threshold = 3000;
+  const size_t dev_cb_threshold = 1200;
+  const size_t dev_level = 1200;
+
+  cras_audio_format format;
+  fill_audio_format(&format, 48000);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream->rstream->next_cb_ts = start;
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  timespec dev_time = SingleInputDevNextWake(
+      dev_cb_threshold, dev_level, &start, &format, streams);
+
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  // The next dev wake ts should be 5ms since the buffer level is half full.
+  EXPECT_EQ(delta.tv_sec, 0);
+  EXPECT_LT(delta.tv_nsec, 5000000 + 5000 * 1000);
+  EXPECT_GT(delta.tv_nsec, 5000000 - 5000 * 1000);
+}
+
+// One device(48k), one stream(44.1k), write a callback of data and check that
+// the sleep time is correct when doing SRC.
+TEST_F(TimingSuite, WaitAfterFillSRC) {
+  cras_audio_format dev_format;
+  fill_audio_format(&dev_format, 48000);
+  cras_audio_format stream_format;
+  fill_audio_format(&stream_format, 44100);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &stream_format);
+  // rstream's next callback is now and there is enough data to fill.
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream.get(), 441);
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  timespec dev_time = SingleInputDevNextWake(480, 0, &start,
+                                             &dev_format, streams);
+
+  // The next callback should be scheduled 10ms in the future.
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  EXPECT_LT(9900 * 1000, delta.tv_nsec);
+  EXPECT_GT(10100 * 1000, delta.tv_nsec);
+}
+
+// One device, two streams. One stream is ready the other still needs data.
+// Checks that the sleep interval is based on the time the device will take to
+// supply the needed samples for stream2.
+TEST_F(TimingSuite, WaitTwoStreamsSameFormat) {
+  const size_t cb_threshold = 480;
+
+  cras_audio_format format;
+  fill_audio_format(&format, 48000);
+
+  // stream1's next callback is now and there is enough data to fill.
+  StreamPtr stream1 =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream1->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream1.get(), cb_threshold);
+
+  // stream2 is only half full.
+  StreamPtr stream2  =
+      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
+  stream2->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream2.get(), 240);
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream1));
+  streams.emplace_back(std::move(stream2));
+  timespec dev_time = SingleInputDevNextWake(cb_threshold, 0, &start,
+                                             &format, streams);
+
+  // Should wait for approximately 5 milliseconds for 240 samples at 48k.
+  struct timespec delta2;
+  subtract_timespecs(&dev_time, &start, &delta2);
+  EXPECT_LT(4900 * 1000, delta2.tv_nsec);
+  EXPECT_GT(5100 * 1000, delta2.tv_nsec);
+}
+
+// One device(44.1), two streams(44.1, 48). One stream is ready the other still
+// needs data. Checks that the sleep interval is based on the time the device
+// will take to supply the needed samples for stream2, stream2 is sample rate
+// converted from the 44.1k device to the 48k stream.
+TEST_F(TimingSuite, WaitTwoStreamsDifferentRates) {
+  cras_audio_format s1_format, s2_format;
+  fill_audio_format(&s1_format, 44100);
+  fill_audio_format(&s2_format, 48000);
+
+  // stream1's next callback is now and there is enough data to fill.
+  StreamPtr stream1 =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &s1_format);
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  stream1->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream1.get(), 441);
+  // stream2's next callback is now but there is only half a callback of data.
+  StreamPtr stream2  =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 480, &s2_format);
+  stream2->rstream->next_cb_ts = start;
+  AddFakeDataToStream(stream2.get(), 240);
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream1));
+  streams.emplace_back(std::move(stream2));
+  timespec dev_time = SingleInputDevNextWake(441, 0, &start,
+                                             &s1_format, streams);
+
+  // Should wait for approximately 5 milliseconds for 240 48k samples from the
+  // 44.1k device.
+  struct timespec delta2;
+  subtract_timespecs(&dev_time, &start, &delta2);
+  EXPECT_LT(4900 * 1000, delta2.tv_nsec);
+  EXPECT_GT(5100 * 1000, delta2.tv_nsec);
+}
+
+// One device, two streams. Both streams get a full callback of data and the
+// device has enough samples for the next callback already. Checks that the
+// shorter of the two streams times is used for the next sleep interval.
+TEST_F(TimingSuite, WaitTwoStreamsDifferentWakeupTimes) {
+  cras_audio_format s1_format, s2_format;
+  fill_audio_format(&s1_format, 44100);
+  fill_audio_format(&s2_format, 48000);
+
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+
+  // stream1's next callback is in 3ms.
+  StreamPtr stream1 =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &s1_format);
+  stream1->rstream->next_cb_ts = start;
+  const timespec three_millis = { 0, 3 * 1000 * 1000 };
+  add_timespecs(&stream1->rstream->next_cb_ts, &three_millis);
+  AddFakeDataToStream(stream1.get(), 441);
+  // stream2 is also ready next cb in 5ms..
+  StreamPtr stream2  =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 480, &s2_format);
+  stream2->rstream->next_cb_ts = start;
+  const timespec five_millis = { 0, 5 * 1000 * 1000 };
+  add_timespecs(&stream2->rstream->next_cb_ts, &five_millis);
+  AddFakeDataToStream(stream1.get(), 480);
+
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream1));
+  streams.emplace_back(std::move(stream2));
+  timespec dev_time = SingleInputDevNextWake(441, 441, &start,
+                                             &s1_format, streams);
+
+  // Should wait for approximately 3 milliseconds for stream 1 first.
+  struct timespec delta2;
+  subtract_timespecs(&dev_time, &start, &delta2);
+  EXPECT_LT(2900 * 1000, delta2.tv_nsec);
+  EXPECT_GT(3100 * 1000, delta2.tv_nsec);
+}
+
+// One hotword stream attaches to hotword device. Input data has copied from
+// device to stream but total number is less than cb_threshold. Hotword stream
+// should be scheduled wake base on the samples needed to fill full shm.
+TEST_F(TimingSuite, HotwordStreamUseDevTiming) {
+  cras_audio_format fmt;
+  fill_audio_format(&fmt, 48000);
+
+  struct timespec start, delay;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
+  stream->rstream->flags = HOTWORD_STREAM;
+  stream->rstream->next_cb_ts = start;
+  delay.tv_sec = 0;
+  delay.tv_nsec = 3 * 1000 * 1000;
+  add_timespecs(&stream->rstream->next_cb_ts, &delay);
+
+  // Add fake data to stream and device so its slightly less than cb_threshold.
+  // Expect to wait for samples to fill the full buffer (480 - 192) frames
+  // instead of using the next_cb_ts.
+  AddFakeDataToStream(stream.get(), 192);
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  timespec dev_time = SingleInputDevNextWake(4096, 0, &start,
+                                             &fmt, streams);
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  // 288 frames worth of time = 6 ms.
+  EXPECT_EQ(6 * 1000 * 1000, delta.tv_nsec);
+}
+
+// One hotword stream attaches to hotword device. Input data burst to a number
+// larger than cb_threshold. Also, stream is pending client reply.
+// In this case stream fd is used to poll for next wake.
+// And the dev wake time is unchanged from the default 20 seconds limit.
+TEST_F(TimingSuite, HotwordStreamBulkDataIsPending) {
+  int poll_fd = 0;
+  cras_audio_format fmt;
+  fill_audio_format(&fmt, 48000);
+
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
+  stream->rstream->flags = HOTWORD_STREAM;
+  stream->rstream->next_cb_ts = start;
+
+  AddFakeDataToStream(stream.get(), 480);
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  // Stream is pending the reply from client.
+  rstream_stub_pending_reply(streams[0]->rstream.get(), 1);
+
+  // There is more than 1 cb_threshold of data in device.
+  timespec dev_time = SingleInputDevNextWake(
+      4096, 7000, &start, &fmt, streams, CRAS_NODE_TYPE_HOTWORD);
+
+  // Need to wait for stream fd in the next ppoll.
+  poll_fd = dev_stream_poll_stream_fd(streams[0]->dstream.get());
+  EXPECT_EQ(FAKE_POLL_FD, poll_fd);
+
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  // Wake up time should be default 20 seconds because audio thread
+  // depends on reply from client to wake it up.
+  EXPECT_LT(19, delta.tv_sec);
+  EXPECT_GT(21, delta.tv_sec);
+}
+
+// One hotword stream attaches to hotword device. Input data burst to a number
+// larger than cb_threshold. However, stream is not pending client reply.
+// This happens if there was no data during capture_to_stream.
+// In this case stream fd is NOT used to poll for next wake.
+// And the dev wake time is changed to a 0 instead of default 20 seconds.
+TEST_F(TimingSuite, HotwordStreamBulkDataIsNotPending) {
+  int poll_fd = 0;
+  cras_audio_format fmt;
+  fill_audio_format(&fmt, 48000);
+
+  struct timespec start;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+
+  StreamPtr stream =
+      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
+  stream->rstream->flags = HOTWORD_STREAM;
+  stream->rstream->next_cb_ts = start;
+
+  AddFakeDataToStream(stream.get(), 480);
+  std::vector<StreamPtr> streams;
+  streams.emplace_back(std::move(stream));
+  // Stream is not pending the reply from client.
+  rstream_stub_pending_reply(streams[0]->rstream.get(), 0);
+
+  // There is more than 1 cb_threshold of data in device.
+  timespec dev_time = SingleInputDevNextWake(4096, 7000, &start,
+                                             &fmt, streams);
+
+  // Does not need to wait for stream fd in the next ppoll.
+  poll_fd = dev_stream_poll_stream_fd(streams[0]->dstream.get());
+  EXPECT_EQ(-1, poll_fd);
+
+  struct timespec delta;
+  subtract_timespecs(&dev_time, &start, &delta);
+  // Wake up time should be very small because there is enough
+  // data to be send to client.
+  EXPECT_LT(delta.tv_sec, 0.1);
+}
+
+/* Stubs */
+extern "C" {
+
+int cras_server_metrics_highest_hw_level(unsigned hw_level,
+		enum CRAS_STREAM_DIRECTION direction)
+{
+  return 0;
+}
+
+int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
+{
+  return 0;
+}
+
+int cras_server_metrics_num_underruns(unsigned num_underruns)
+{
+  return 0;
+}
+
+int input_data_get_for_stream(
+		struct input_data *data,
+		struct cras_rstream *stream,
+		struct buffer_share *offsets,
+		struct cras_audio_area **area,
+		unsigned int *offset)
+{
+  return 0;
+}
+
+int input_data_put_for_stream(struct input_data *data,
+			   struct cras_rstream *stream,
+			   struct buffer_share *offsets,
+			   unsigned int frames)
+{
+  return 0;
+}
+struct cras_audio_format *cras_rstream_post_processing_format(
+    const struct cras_rstream *stream, void *dev_ptr)
+{
+  return NULL;
+}
+}  // extern "C"
+
+}  //  namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/init/cras.conf b/init/cras.conf
index eb1e62c..e7df799 100644
--- a/init/cras.conf
+++ b/init/cras.conf
@@ -5,7 +5,7 @@
 # Installed by ADHD package.
 # cras upstart job.
 
-description     "ChromeOS audio server"
+description     "Chrome OS audio server"
 author          "chromium-os-dev@chromium.org"
 
 env CRAS_SOCKET_DIR=/run/cras
diff --git a/init/cras.sh b/init/cras.sh
index 2284243..cebf3a9 100644
--- a/init/cras.sh
+++ b/init/cras.sh
@@ -2,24 +2,53 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Disable HSP/HFP on Google WiFi (Gale) with UART-HCI Bluetooth
-# which is incapable to handle SCO audio.
-platform_name="$(mosys platform name)"
-if [ "$platform_name" = "Gale" ]; then
-     DISABLE_PROFILE="--disable_profile=hfp,hsp"
+# Unified build config.
+device_config_dir="$(cros_config --abspath /audio/main cras-config-dir)"
+internal_ucm_suffix="$(cros_config /audio/main ucm-suffix)"
+
+# Handle legacy config.
+if [ -z "${device_config_dir}" ]; then
+  # Disable HSP/HFP on Google WiFi (Gale) with UART-HCI Bluetooth
+  # which is incapable of handling SCO audio.
+  platform_name="$(mosys platform name)"
+  if [ "$platform_name" = "Gale" ]; then
+      DISABLE_PROFILE="--disable_profile=hfp,hsp"
+  fi
+  # For boards that need a different device config, check which config
+  # directory to use. Use that directory for both volume curves
+  # and DSP config.
+  if [ -f /etc/cras/get_device_config_dir ]; then
+    device_config_dir="$(sh /etc/cras/get_device_config_dir)"
+  fi
+  if [ -f /etc/cras/get_internal_ucm_suffix ]; then
+    internal_ucm_suffix="$(sh /etc/cras/get_internal_ucm_suffix)"
+  fi
 fi
-# For board needs different device configs, check which config
-# directory to use. Use that directory for both volume curves
-# and dsp config.
-if [ -f /etc/cras/get_device_config_dir ]; then
-  device_config_dir="$(sh /etc/cras/get_device_config_dir)"
+
+if [ -n "${device_config_dir}" ]; then
   DEVICE_CONFIG_DIR="--device_config_dir=${device_config_dir}"
   DSP_CONFIG="--dsp_config=${device_config_dir}/dsp.ini"
 fi
-if [ -f /etc/cras/get_internal_ucm_suffix ]; then
-  internal_ucm_suffix="$(sh /etc/cras/get_internal_ucm_suffix)"
+if [ -n "${internal_ucm_suffix}" ]; then
   INTERNAL_UCM_SUFFIX="--internal_ucm_suffix=${internal_ucm_suffix}"
 fi
-exec minijail0 -u cras -g cras -G -- /usr/bin/cras \
-    ${DSP_CONFIG} ${DEVICE_CONFIG_DIR} ${DISABLE_PROFILE} \
-    ${INTERNAL_UCM_SUFFIX}
+
+# Leave cras in the init pid namespace as it uses its PID as an IPC identifier.
+exec minijail0 -u cras -g cras -G -n --uts -v -l \
+        -P /var/empty \
+        -b /,/ \
+        -k 'tmpfs,/run,tmpfs,MS_NODEV|MS_NOEXEC|MS_NOSUID,mode=755,size=10M' \
+        -b /run/cras,/run/cras,1 \
+        -b /run/dbus,/run/dbus,1 \
+        -b /run/udev,/run/udev \
+        -b /dev,/dev \
+        -b /dev/shm,/dev/shm,1 \
+        -k proc,/proc,proc \
+        -b /sys,/sys \
+        -k 'tmpfs,/var,tmpfs,MS_NODEV|MS_NOEXEC|MS_NOSUID,mode=755,size=10M' \
+        -b /var/lib/metrics/,/var/lib/metrics/,1 \
+	-S /usr/share/policy/cras-seccomp.policy \
+        -- \
+        /usr/bin/cras \
+        ${DSP_CONFIG} ${DEVICE_CONFIG_DIR} ${DISABLE_PROFILE} \
+        ${INTERNAL_UCM_SUFFIX}
diff --git a/scripts/audio_diagnostics b/scripts/audio_diagnostics
index 5ec96a9..83a380c 100755
--- a/scripts/audio_diagnostics
+++ b/scripts/audio_diagnostics
@@ -22,6 +22,9 @@
 echo '=== cras_test_client --dump_audio_thread ==='
 cras_test_client --dump_audio_thread
 
+echo '=== cras_test_client --dump_events ==='
+cras_test_client --dump_events
+
 echo '=== aplay -l ==='
 aplay -l
 echo '=== arecord -l ==='
diff --git a/scripts/audio_thread_log_viewer/viewer_c3.py b/scripts/audio_thread_log_viewer/viewer_c3.py
index a6b11be..2f23dd5 100755
--- a/scripts/audio_thread_log_viewer/viewer_c3.py
+++ b/scripts/audio_thread_log_viewer/viewer_c3.py
@@ -18,7 +18,7 @@
   <!-- Load c3.css -->
   <link href="https://rawgit.com/masayuki0812/c3/master/c3.css" rel="stylesheet" type="text/css">
   <!-- Load d3.js and c3.js -->
-  <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+  <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
   <script src="https://rawgit.com/masayuki0812/c3/master/c3.js" charset="utf-8"></script>
   <style type="text/css">
     .c3-grid text {
diff --git a/seccomp/cras-seccomp-amd64.policy b/seccomp/cras-seccomp-amd64.policy
new file mode 100644
index 0000000..41b5d2a
--- /dev/null
+++ b/seccomp/cras-seccomp-amd64.policy
@@ -0,0 +1,84 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+clock_gettime: 1
+getuid: 1
+lstat: 1
+read: 1
+recvfrom: 1
+poll: 1
+ppoll: 1
+fstat: 1
+write: 1
+open: 1
+openat: 1
+close: 1
+readlinkat: 1
+getrandom: 1
+access: 1
+fcntl: 1
+getdents: 1
+sendmsg: 1
+stat: 1
+statfs: 1
+recvmsg: 1
+brk: 1
+mmap: 1
+getsockopt: 1
+accept: 1
+munmap: 1
+sendto: 1
+readlink: 1
+futex: 1
+lseek: 1
+rt_sigaction: 1
+socket: arg0 == AF_UNIX || arg0 == AF_BLUETOOTH || arg0 == AF_NETLINK
+unlink: 1
+nanosleep: 1
+pipe: 1
+ftruncate: 1
+mprotect: 1
+connect: 1
+getsockname: 1
+setsockopt: 1
+bind: 1
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+chmod: 1
+geteuid: 1
+uname: 1
+clock_getres: 1
+gettid: 1
+rt_sigprocmask: 1
+fchmod: 1
+getrlimit: 1
+setrlimit: 1
+listen: 1
+clone: 1
+set_robust_list: 1
+getresuid: 1
+getresgid: 1
+sched_setscheduler: 1
+epoll_wait: 1
+getegid: 1
+getgid: 1
+prctl: arg0 == PR_SET_NAME
+epoll_create1: 1
+sched_get_priority_min: 1
+pipe2: 1
+epoll_ctl: 1
+sched_get_priority_max: 1
+lgetxattr: 1
+lsetxattr: 1
+madvise: 1
+sysinfo: 1
+flock: 1
+
+# Allow ioctl command of type 'A' and 'U' for SNDRV_PCM_IOCTL_* and
+# SNDRV_CTL_IOCTL_*, and EVIOCGSW(8), EVIOCGNAME(256), EVIOCGBIT(0x05, 8),
+# HCIGETDEVINFO
+ioctl: arg1 in 0xffff41ff && arg1 & 0x00004100 || arg1 in 0xffff55ff && arg1 & 0x00005500 || arg1 == 0x8008451b || arg1 == 0x81004506 || arg1 == 0x80084525 || arg1 == 0x800448d3
+getpid: 1
diff --git a/seccomp/cras-seccomp-arm.policy b/seccomp/cras-seccomp-arm.policy
new file mode 100644
index 0000000..74676a0
--- /dev/null
+++ b/seccomp/cras-seccomp-arm.policy
@@ -0,0 +1,90 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+clock_gettime: 1
+poll: 1
+read: 1
+ppoll: 1
+write: 1
+recv: 1
+send: 1
+recvmsg: 1
+lstat64: 1
+fstat64: 1
+open: 1
+openat: 1
+close: 1
+fcntl64: 1
+readlinkat: 1
+sendmsg: 1
+access: 1
+getrandom: 1
+mmap2: 1
+epoll_wait: 1
+getsockopt: 1
+accept: 1
+stat64: 1
+mprotect: 1
+gettimeofday: 1
+getdents64: 1
+brk: 1
+statfs: 1
+readlink: 1
+munmap: 1
+rt_sigaction: 1
+lgetxattr: 1
+unlink: 1
+lsetxattr: 1
+rt_sigprocmask: 1
+ftruncate: 1
+futex: 1
+execve: 1
+set_robust_list: 1
+socket: arg0 == AF_UNIX || arg0 == AF_BLUETOOTH || arg0 == AF_NETLINK
+clone: 1
+setsockopt: 1
+geteuid32: 1
+ugetrlimit: 1
+uname: 1
+connect: 1
+bind: 1
+_llseek: 1
+getuid32: 1
+getgid32: 1
+getegid32: 1
+pipe: 1
+flock: 1
+# set_tls: 1
+set_tid_address: 1
+exit_group: 1
+getsockname: 1
+getdents: 1
+nanosleep: 1
+epoll_ctl: 1
+sched_setscheduler: 1
+restart_syscall: 1
+rt_sigreturn: 1
+getresuid32: 1
+exit: 1
+prctl: arg0 == PR_SET_NAME
+clock_getres: 1
+epoll_create1: 1
+fchmod: 1
+setrlimit: 1
+listen: 1
+gettid: 1
+sched_get_priority_min: 1
+chmod: 1
+madvise: 1
+getresgid32: 1
+pipe2: 1
+sched_get_priority_max: 1
+sysinfo: 1
+flock: 1
+
+# Allow ioctl command of type 'A' and 'U' for SNDRV_PCM_IOCTL_* and
+# SNDRV_CTL_IOCTL_*, and EVIOCGSW(8), EVIOCGNAME(256), EVIOCGBIT(0x05, 8),
+# HCIGETDEVINFO
+ioctl: arg1 in 0xffff41ff && arg1 & 0x00004100 || arg1 in 0xffff55ff && arg1 & 0x00005500 || arg1 == 0x8008451b || arg1 == 0x81004506 || arg1 == 0x80084525 || arg1 == 0x800448d3
+getpid: 1
diff --git a/seccomp/cras-seccomp-arm64.policy b/seccomp/cras-seccomp-arm64.policy
new file mode 100644
index 0000000..569c10c
--- /dev/null
+++ b/seccomp/cras-seccomp-arm64.policy
@@ -0,0 +1,82 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+clock_gettime: 1
+# Allow ioctl command of type 'A' and 'U' for SNDRV_PCM_IOCTL_* and
+# SNDRV_CTL_IOCTL_*, and EVIOCGSW(8), EVIOCGNAME(256), EVIOCGBIT(0x05, 8),
+# HCIGETDEVINFO
+ioctl: arg1 in 0xffff41ff && arg1 & 0x00004100 || arg1 in 0xffff55ff && arg1 & 0x00005500 || arg1 == 0x8008451b || arg1 == 0x81004506 || arg1 == 0x80084525 || arg1 == 0x800448d3
+ppoll: 1
+read: 1
+write: 1
+newfstatat: 1
+fstat: 1
+openat: 1
+close: 1
+readlinkat: 1
+getrandom: 1
+faccessat: 1
+# Don't allow mmap or mprotect with both PROT_WRITE and PROT_EXEC
+mmap: arg2 in 0xfffffffb || arg2 in 0xfffffffd
+mprotect: arg2 in 0xfffffffb || arg2 in 0xfffffffd
+sendmsg: 1
+rt_sigaction: 1
+lseek: 1
+recvmsg: 1
+fcntl: 1
+getdents64: 1
+sendto: 1
+brk: 1
+munmap: 1
+socket: arg0 == AF_UNIX || arg0 == AF_BLUETOOTH || arg0 == AF_NETLINK
+statfs: 1
+getsockopt: 1
+accept: 1
+pipe2: 1
+prctl: arg0 == PR_SET_NAME
+futex: 1
+ftruncate: 1
+connect: 1
+bind: 1
+clock_getres: 1
+clone: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_pwait: 1
+execve: 1
+exit: 1
+exit_group: 1
+fchmod: 1
+fchmodat: 1
+flock: 1
+flock: 1
+getegid: 1
+geteuid: 1
+getgid: 1
+getresgid: 1
+getresuid: 1
+getrlimit: 1
+getsockname: 1
+gettid: 1
+gettimeofday: 1
+getuid: 1
+lgetxattr: 1
+listen: 1
+lsetxattr: 1
+madvise: 1
+nanosleep: 1
+restart_syscall: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+sched_get_priority_max: 1
+sched_get_priority_min: 1
+sched_setscheduler: 1
+setrlimit: 1
+set_robust_list: 1
+setsockopt: 1
+set_tid_address: 1
+sysinfo: 1
+uname: 1
+unlinkat: 1
+getpid: 1
diff --git a/ucm-config/cyan/sof-chtmax98090/HiFi.conf b/ucm-config/cyan/sof-chtmax98090/HiFi.conf
new file mode 100644
index 0000000..8b7aacd
--- /dev/null
+++ b/ucm-config/cyan/sof-chtmax98090/HiFi.conf
@@ -0,0 +1,185 @@
+# command-line sequence to switch playback/capture
+# alsaucm -c sof-chtmax98090 set _verb HiFi set _enadev Headphone
+# alsaucm -c sof-chtmax98090 set _verb HiFi set _enadev Speakers
+# alsaucm -c sof-chtmax98090 set _verb HiFi set _enadev HeadsetMic
+# alsaucm -c sof-chtmax98090 set _verb HiFi set _enadev InternalMic
+
+
+SectionVerb {
+
+	EnableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Left Speaker Mixer Left DAC Switch' on"
+		cset "name='Right Speaker Mixer Right DAC Switch' on"
+		cset "name='Digital EQ 3 Band Switch' off"
+		cset "name='Digital EQ 5 Band Switch' off"
+		cset "name='Digital EQ 7 Band Switch' off"
+		cset "name='Biquad Switch' off"
+		cset "name='Filter Mode' Music"
+		cset "name='ADC Oversampling Rate' 0"
+
+		cset "name='DMIC Mux' DMIC"
+		cset "name='MIC2 Mux' IN34"
+		cset "name='MIC2 Volume' 10"
+		cset "name='MIC2 Boost Volume' 0"
+
+		cset "name='ADCR Boost Volume' 4"
+		cset "name='ADCL Boost Volume' 4"
+		cset "name='ADCR Volume' 11"
+		cset "name='ADCL Volume' 11"
+
+		cset "name='Headphone Volume' 10"
+		cset "name='Speaker Volume' 10"
+
+		cset "name='Speaker Left Mixer Volume' 3"
+		cset "name='Speaker Right Mixer Volume' 3"
+		cset "name='Record Path DC Blocking' on"
+		cset "name='Playback Path DC Blocking' on"
+
+		cset "name='Headphone Left Switch' off"
+		cset "name='Headphone Right Switch' off"
+		cset "name='Headphone Switch' off"
+
+		cset "name='Speaker Left Switch' off"
+		cset "name='Speaker Right Switch' off"
+		cset "name='Ext Spk Switch' off"
+
+		cset "name='Headset Mic Switch' off"
+		cset "name='Int Mic Switch' off"
+	]
+
+	DisableSequence [
+	]
+
+	# ALSA PCM
+	Value {
+		# ALSA PCM device for HiFi
+		PlaybackPCM "hw:sofchtmax98090"
+		CapturePCM  "hw:sofchtmax98090"
+	}
+}
+
+SectionDevice."Headphone" {
+	Comment "Headphone"
+
+	ConflictingDevice [
+		"Speakers"
+	]
+
+	Value {
+		JackControl "Headphone Jack"
+		JackHWMute "Speakers"
+	}
+
+	EnableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Headphone Left Switch' on"
+		cset "name='Headphone Right Switch' on"
+		cset "name='Headphone Switch' on"
+	]
+	DisableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Headphone Left Switch' off"
+		cset "name='Headphone Right Switch' off"
+		cset "name='Headphone Switch' off"
+	]
+
+	Value {
+		PlaybackChannels 2
+	}
+}
+
+SectionDevice."Speakers" {
+        Comment "Speakers"
+
+	ConflictingDevice [
+		"Headphone"
+	]
+
+	EnableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Speaker Left Switch' on"
+		cset "name='Speaker Right Switch' on"
+		cset "name='Ext Spk Switch' on"
+	]
+	DisableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Speaker Left Switch' off"
+		cset "name='Speaker Right Switch' off"
+		cset "name='Ext Spk Switch' off"
+	]
+
+	Value {
+		PlaybackChannels 2
+	}
+}
+
+SectionDevice."HeadsetMic" {
+         Comment "Headset Mic"
+
+	 Value {
+		JackControl "Headset Mic Jack"
+		#FIXME CaptureControl "MIC2"
+	}
+
+	ConflictingDevice [
+		"InternalMic"
+	]
+
+	EnableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Headset Mic Switch' on"
+		cset "name='DMIC Mux' ADC"
+		cset "name='Record Path DC Blocking' on"
+	]
+
+	DisableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Headset Mic Switch' off"
+		cset "name='DMIC Mux' DMIC"
+		cset "name='Record Path DC Blocking' off"
+	]
+
+	Value {
+		CaptureChannels 2
+	}
+}
+
+SectionDevice."InternalMic" {
+	Comment "Internal Mic"
+
+	Value {
+		#FIXME CaptureControl "MIC2"
+	}
+
+	ConflictingDevice [
+		"HeadsetMic"
+	]
+
+	EnableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Int Mic Switch' on"
+		cset "name='DMIC Mux' DMIC"
+		cset "name='Record Path DC Blocking' off"
+	]
+
+	DisableSequence [
+		cdev "hw:sofchtmax98090"
+
+		cset "name='Int Mic Switch' off"
+		cset "name='DMIC Mux' ADC"
+		cset "name='Record Path DC Blocking' on"
+	]
+
+	Value {
+		CaptureChannels 2
+	}
+}
diff --git a/ucm-config/cyan/sof-chtmax98090/sof-chtmax98090.conf b/ucm-config/cyan/sof-chtmax98090/sof-chtmax98090.conf
new file mode 100644
index 0000000..6f73533
--- /dev/null
+++ b/ucm-config/cyan/sof-chtmax98090/sof-chtmax98090.conf
@@ -0,0 +1,6 @@
+Comment "sof-chtmax98090 internal card"
+
+SectionUseCase."HiFi" {
+	File "HiFi.conf"
+	Comment "Default"
+}
diff --git a/ucm-config/daisy/DAISY-I2S-98090/HiFi.conf b/ucm-config/daisy/DAISY-I2S-98090/HiFi.conf
index b583e31..bddfce6 100644
--- a/ucm-config/daisy/DAISY-I2S-98090/HiFi.conf
+++ b/ucm-config/daisy/DAISY-I2S-98090/HiFi.conf
@@ -42,7 +42,7 @@
 	Value {
 		JackName "DAISY-I2S-98090 HDMI Jack"
 		OutputDspName ""
-		EDIDFile "/sys/devices/platform/exynos-drm/drm/card0/card0-HDMI-A-1/edid"
+		EDIDFile "/sys/devices/platform/exynos-drm/drm/card1/card1-HDMI-A-1/edid"
 	}
 	EnableSequence [
 		cdev "hw:DAISYI2S98090"
@@ -82,6 +82,7 @@
 	Value {
 		JackName "DAISY-I2S-98090 Mic Jack"
 		CaptureControl "MIC2"
+		DefaultNodeGain "-500"
 	}
 
 	EnableSequence [
diff --git a/ucm-config/daisy/DAISY-I2S/HiFi.conf b/ucm-config/daisy/DAISY-I2S/HiFi.conf
index 54d08fc..9031816 100644
--- a/ucm-config/daisy/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy/DAISY-I2S/HiFi.conf
@@ -90,7 +90,7 @@
 SectionDevice."HDMI".0 {
 	Value {
 		JackName "DAISY-I2S HDMI Jack"
-		EDIDFile "/sys/devices/platform/exynos-drm/drm/card0/card0-HDMI-A-1/edid"
+		EDIDFile "/sys/devices/platform/exynos-drm/drm/card1/card1-HDMI-A-1/edid"
 	}
 }
 
@@ -119,6 +119,7 @@
 	Value {
 		JackName "DAISY-I2S Mic Jack"
 		CaptureControl "MIC2"
+		DefaultNodeGain "-500"
 	}
 
 	EnableSequence [
diff --git a/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf b/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
index 2516a96..ce1e45d 100644
--- a/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
@@ -23,7 +23,7 @@
 	Value {
 		JackName "DAISY-I2S HDMI Jack"
 		OutputDspName ""
-		EDIDFile "/sys/devices/platform/exynos-drm/drm/card0/card0-HDMI-A-1/edid"
+		EDIDFile "/sys/devices/platform/exynos-drm/drm/card1/card1-HDMI-A-1/edid"
 	}
 }
 
diff --git a/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf b/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
index 85fbc56..f6a1d31 100644
--- a/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
@@ -48,7 +48,7 @@
 	Value {
 		JackName "DAISY-I2S HDMI Jack"
 		OutputDspName ""
-		EDIDFile "/sys/devices/platform/exynos-drm/drm/card0/card0-HDMI-A-1/edid"
+		EDIDFile "/sys/devices/platform/exynos-drm/drm/card1/card1-HDMI-A-1/edid"
 	}
 }
 
diff --git a/ucm-config/for_all_boards/Hangouts Meet speakermic/Hangouts Meet speakermic.conf b/ucm-config/for_all_boards/Hangouts Meet speakermic/Hangouts Meet speakermic.conf
new file mode 100644
index 0000000..c114430
--- /dev/null
+++ b/ucm-config/for_all_boards/Hangouts Meet speakermic/Hangouts Meet speakermic.conf
@@ -0,0 +1,6 @@
+Comment "Speakermic for Chrome for meeting"
+
+SectionUseCase."HiFi" {
+	File "HiFi.conf"
+	Comment "Default"
+}
diff --git a/ucm-config/for_all_boards/Hangouts Meet speakermic/HiFi.conf b/ucm-config/for_all_boards/Hangouts Meet speakermic/HiFi.conf
new file mode 100644
index 0000000..b84e696
--- /dev/null
+++ b/ucm-config/for_all_boards/Hangouts Meet speakermic/HiFi.conf
@@ -0,0 +1,8 @@
+SectionVerb {
+	Value {
+		MinBufferLevel "0"
+	}
+}
+
+SectionDevice."Headset".0 {
+}
diff --git a/ucm-config/for_all_boards/Plankton Captured HDMI Audio/HiFi.conf b/ucm-config/for_all_boards/Plankton Captured HDMI Audio/HiFi.conf
new file mode 100644
index 0000000..13db891
--- /dev/null
+++ b/ucm-config/for_all_boards/Plankton Captured HDMI Audio/HiFi.conf
@@ -0,0 +1,23 @@
+SectionVerb {
+        Value {
+                FullySpecifiedUCM "1"
+        }
+
+        EnableSequence [
+        ]
+
+        DisableSequence [
+        ]
+}
+
+SectionDevice."Plankton Captured HDMI Audio".0 {
+        Value {
+                CapturePCM "hw:Plankton Captured HDMI Audio,0"
+        }
+
+        EnableSequence [
+        ]
+
+        DisableSequence [
+        ]
+}
diff --git a/ucm-config/for_all_boards/Plankton Captured HDMI Audio/Plankton Captured HDMI Audio.conf b/ucm-config/for_all_boards/Plankton Captured HDMI Audio/Plankton Captured HDMI Audio.conf
new file mode 100644
index 0000000..6514aa1
--- /dev/null
+++ b/ucm-config/for_all_boards/Plankton Captured HDMI Audio/Plankton Captured HDMI Audio.conf
@@ -0,0 +1,6 @@
+Comment "Plankton Captured HDMI Audio"
+
+SectionUseCase."HiFi" {
+        File "HiFi.conf"
+        Comment "Default"
+}
diff --git a/ucm-config/glados/sklnau8825adi/HiFi.conf b/ucm-config/glados/sklnau8825adi/HiFi.conf
index 376e704..6a53caf 100644
--- a/ucm-config/glados/sklnau8825adi/HiFi.conf
+++ b/ucm-config/glados/sklnau8825adi/HiFi.conf
@@ -1,14 +1,17 @@
 SectionVerb {
 	Value {
 		OutputDspName "speaker_eq"
+		InputDspName "extmic_eq"
 	}
 
 	EnableSequence [
 		cdev "hw:sklnau8825adi"
 		cset "name='codec1_out mo media0_in mi Switch' off"
 		cset "name='codec0_out mo media0_in mi Switch' on"
-		cset "name='DAC Oversampling Rate' 128"
+		cset "name='DAC Oversampling Rate' 64"
 		cset "name='Headset Mic Switch' off"
+		cset "name='BIQ Path Select' ADC"
+		cset "name='BIQ Coefficients' 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
 		cset "name='codec0_iv_in Switch' 1"
 		cset "name='media0_out mo codec0_in mi Switch' off"
 		cset "name='media0_out mo dmic01_hifi_in mi Switch' on"
@@ -24,7 +27,8 @@
 
 SectionDevice."Internal Mic".0 {
 	Value {
-		MaxSoftwareGain "2000"
+		MaxSoftwareGain "2400"
+		InputDspName ""
 	}
 
 	EnableSequence [
@@ -99,11 +103,13 @@
 	Value {
 		JackName "sklnau8825adi Headset Jack"
 		CaptureControl "Mic"
+		DefaultNodeGain "800"
 	}
 
 	EnableSequence [
 		cdev "hw:sklnau8825adi"
 		cset "name='Headset Mic Switch' on"
+		cset "name='BIQ Coefficients' 0,155,0,6,255,102,0,0,255,179,0,0,0,154,0,6,255,179,128,0"
 		cset "name='media0_out mo codec0_in mi Switch' on"
 		cset "name='media0_out mo dmic01_hifi_in mi Switch' off"
 	]
@@ -111,6 +117,7 @@
 	DisableSequence [
 		cdev "hw:sklnau8825adi"
 		cset "name='Headset Mic Switch' off"
+		cset "name='BIQ Coefficients' 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
 		cset "name='media0_out mo codec0_in mi Switch' off"
 		cset "name='media0_out mo dmic01_hifi_in mi Switch' on"
 	]