Merge "Revert^2 "Merge branch 'upstream-master'"" am: 0aaab56eba am: 1a33fa0434 am: 6552c7ad82

Original change: https://android-review.googlesource.com/c/platform/external/adhd/+/1613721

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I3b73f8ff54dc52938ca7c787479130b235bcc747
diff --git a/audio_streams/README.md b/audio_streams/README.md
index b62c294..d3a02e8 100644
--- a/audio_streams/README.md
+++ b/audio_streams/README.md
@@ -2,5 +2,5 @@
 
 The `audio_streams` crate provides a basic interface for playing audio.
 This will be used to enable playback to various audio subsystems such as
-Alsa and cras. To start, an empty playback example `DummyStreamSource`
+Alsa and cras. To start, an empty playback example `NoopStreamSource`
 is provided.
diff --git a/audio_streams/src/audio_streams.rs b/audio_streams/src/audio_streams.rs
index 3cb1173..5290357 100644
--- a/audio_streams/src/audio_streams.rs
+++ b/audio_streams/src/audio_streams.rs
@@ -13,14 +13,14 @@
 //! the samples written to it are committed to the `PlaybackBufferStream` it came from.
 //!
 //! ```
-//! use audio_streams::{BoxError, SampleFormat, StreamSource, DummyStreamSource};
+//! use audio_streams::{BoxError, SampleFormat, StreamSource, NoopStreamSource};
 //! use std::io::Write;
 //!
 //! const buffer_size: usize = 120;
 //! const num_channels: usize = 2;
 //!
 //! # fn main() -> std::result::Result<(), BoxError> {
-//! let mut stream_source = DummyStreamSource::new();
+//! let mut stream_source = NoopStreamSource::new();
 //! let sample_format = SampleFormat::S16LE;
 //! let frame_size = num_channels * sample_format.sample_bytes();
 //!
@@ -55,7 +55,7 @@
 }
 
 impl SampleFormat {
-    pub fn sample_bytes(&self) -> usize {
+    pub fn sample_bytes(self) -> usize {
         use SampleFormat::*;
         match self {
             U8 => 1,
@@ -145,7 +145,7 @@
 
     /// Returns a stream control and buffer generator object. These are separate as the buffer
     /// generator might want to be passed to the audio stream.
-    /// Default implementation returns `DummyStreamControl` and `DummyCaptureStream`.
+    /// Default implementation returns `NoopStreamControl` and `NoopCaptureStream`.
     #[allow(clippy::type_complexity)]
     fn new_capture_stream(
         &mut self,
@@ -161,8 +161,8 @@
         BoxError,
     > {
         Ok((
-            Box::new(DummyStreamControl::new()),
-            Box::new(capture::DummyCaptureStream::new(
+            Box::new(NoopStreamControl::new()),
+            Box::new(capture::NoopCaptureStream::new(
                 num_channels,
                 format,
                 frame_rate,
@@ -295,28 +295,28 @@
 }
 
 /// Stream that accepts playback samples but drops them.
-pub struct DummyStream {
+pub struct NoopStream {
     buffer: Vec<u8>,
     frame_size: usize,
     interval: Duration,
     next_frame: Duration,
     start_time: Option<Instant>,
-    buffer_drop: DummyBufferDrop,
+    buffer_drop: NoopBufferDrop,
 }
 
-/// DummyStream data that is needed from the buffer complete callback.
-struct DummyBufferDrop {
+/// NoopStream data that is needed from the buffer complete callback.
+struct NoopBufferDrop {
     which_buffer: bool,
 }
 
-impl BufferDrop for DummyBufferDrop {
+impl BufferDrop for NoopBufferDrop {
     fn trigger(&mut self, _nwritten: usize) {
         // When a buffer completes, switch to the other one.
         self.which_buffer ^= true;
     }
 }
 
-impl DummyStream {
+impl NoopStream {
     pub fn new(
         num_channels: usize,
         format: SampleFormat,
@@ -325,20 +325,20 @@
     ) -> Self {
         let frame_size = format.sample_bytes() * num_channels;
         let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
-        DummyStream {
+        NoopStream {
             buffer: vec![0; buffer_size * frame_size],
             frame_size,
             interval,
             next_frame: interval,
             start_time: None,
-            buffer_drop: DummyBufferDrop {
+            buffer_drop: NoopBufferDrop {
                 which_buffer: false,
             },
         }
     }
 }
 
-impl PlaybackBufferStream for DummyStream {
+impl PlaybackBufferStream for NoopStream {
     fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError> {
         if let Some(start_time) = self.start_time {
             if start_time.elapsed() < self.next_frame {
@@ -357,7 +357,8 @@
     }
 }
 
-/// No-op control for `DummyStream`s.
+/// No-op control for `NoopStream`s.
+/// Should be deprecated once all existing use of DummyStreamControl removed.
 #[derive(Default)]
 pub struct DummyStreamControl;
 
@@ -369,17 +370,29 @@
 
 impl StreamControl for DummyStreamControl {}
 
-/// Source of `DummyStream` and `DummyStreamControl` objects.
+/// No-op control for `NoopStream`s.
 #[derive(Default)]
-pub struct DummyStreamSource;
+pub struct NoopStreamControl;
 
-impl DummyStreamSource {
+impl NoopStreamControl {
     pub fn new() -> Self {
-        DummyStreamSource {}
+        NoopStreamControl {}
     }
 }
 
-impl StreamSource for DummyStreamSource {
+impl StreamControl for NoopStreamControl {}
+
+/// Source of `NoopStream` and `NoopStreamControl` objects.
+#[derive(Default)]
+pub struct NoopStreamSource;
+
+impl NoopStreamSource {
+    pub fn new() -> Self {
+        NoopStreamSource {}
+    }
+}
+
+impl StreamSource for NoopStreamSource {
     #[allow(clippy::type_complexity)]
     fn new_playback_stream(
         &mut self,
@@ -389,8 +402,8 @@
         buffer_size: usize,
     ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
         Ok((
-            Box::new(DummyStreamControl::new()),
-            Box::new(DummyStream::new(
+            Box::new(NoopStreamControl::new()),
+            Box::new(NoopStream::new(
                 num_channels,
                 format,
                 frame_rate,
@@ -408,7 +421,7 @@
     fn invalid_buffer_length() {
         // Playback buffers can't be created with a size that isn't divisible by the frame size.
         let mut pb_buf = [0xa5u8; 480 * 2 * 2 + 1];
-        let mut buffer_drop = DummyBufferDrop {
+        let mut buffer_drop = NoopBufferDrop {
             which_buffer: false,
         };
         assert!(PlaybackBuffer::new(2, &mut pb_buf, &mut buffer_drop).is_err());
@@ -436,7 +449,7 @@
 
     #[test]
     fn sixteen_bit_stereo() {
-        let mut server = DummyStreamSource::new();
+        let mut server = NoopStreamSource::new();
         let (_, mut stream) = server
             .new_playback_stream(2, SampleFormat::S16LE, 48000, 480)
             .unwrap();
@@ -448,7 +461,7 @@
 
     #[test]
     fn consumption_rate() {
-        let mut server = DummyStreamSource::new();
+        let mut server = NoopStreamSource::new();
         let (_, mut stream) = server
             .new_playback_stream(2, SampleFormat::S16LE, 48000, 480)
             .unwrap();
diff --git a/audio_streams/src/capture.rs b/audio_streams/src/capture.rs
index 3f2de3f..930f182 100644
--- a/audio_streams/src/capture.rs
+++ b/audio_streams/src/capture.rs
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //! ```
-//! use audio_streams::{BoxError, SampleFormat, StreamSource, DummyStreamSource};
+//! use audio_streams::{BoxError, SampleFormat, StreamSource, NoopStreamSource};
 //! use std::io::Read;
 //!
 //! const buffer_size: usize = 120;
 //! const num_channels: usize = 2;
 //!
 //! # fn main() -> std::result::Result<(),BoxError> {
-//! let mut stream_source = DummyStreamSource::new();
+//! let mut stream_source = NoopStreamSource::new();
 //! let sample_format = SampleFormat::S16LE;
 //! let frame_size = num_channels * sample_format.sample_bytes();
 //!
@@ -34,7 +34,7 @@
     time::{Duration, Instant},
 };
 
-use super::{AudioBuffer, BoxError, BufferDrop, DummyBufferDrop, SampleFormat};
+use super::{AudioBuffer, BoxError, BufferDrop, NoopBufferDrop, SampleFormat};
 
 /// `CaptureBufferStream` provides `CaptureBuffer`s to read with audio samples from capture.
 pub trait CaptureBufferStream: Send {
@@ -123,16 +123,16 @@
 }
 
 /// Stream that provides null capture samples.
-pub struct DummyCaptureStream {
+pub struct NoopCaptureStream {
     buffer: Vec<u8>,
     frame_size: usize,
     interval: Duration,
     next_frame: Duration,
     start_time: Option<Instant>,
-    buffer_drop: DummyBufferDrop,
+    buffer_drop: NoopBufferDrop,
 }
 
-impl DummyCaptureStream {
+impl NoopCaptureStream {
     pub fn new(
         num_channels: usize,
         format: SampleFormat,
@@ -141,20 +141,20 @@
     ) -> Self {
         let frame_size = format.sample_bytes() * num_channels;
         let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
-        DummyCaptureStream {
+        NoopCaptureStream {
             buffer: vec![0; buffer_size * frame_size],
             frame_size,
             interval,
             next_frame: interval,
             start_time: None,
-            buffer_drop: DummyBufferDrop {
+            buffer_drop: NoopBufferDrop {
                 which_buffer: false,
             },
         }
     }
 }
 
-impl CaptureBufferStream for DummyCaptureStream {
+impl CaptureBufferStream for NoopCaptureStream {
     fn next_capture_buffer(&mut self) -> Result<CaptureBuffer, BoxError> {
         if let Some(start_time) = self.start_time {
             if start_time.elapsed() < self.next_frame {
@@ -182,7 +182,7 @@
     fn invalid_buffer_length() {
         // Capture buffers can't be created with a size that isn't divisible by the frame size.
         let mut cp_buf = [0xa5u8; 480 * 2 * 2 + 1];
-        let mut buffer_drop = DummyBufferDrop {
+        let mut buffer_drop = NoopBufferDrop {
             which_buffer: false,
         };
         assert!(CaptureBuffer::new(2, &mut cp_buf, &mut buffer_drop).is_err());
@@ -212,7 +212,7 @@
 
     #[test]
     fn sixteen_bit_stereo() {
-        let mut server = DummyStreamSource::new();
+        let mut server = NoopStreamSource::new();
         let (_, mut stream) = server
             .new_capture_stream(2, SampleFormat::S16LE, 48000, 480)
             .unwrap();
@@ -224,7 +224,7 @@
 
     #[test]
     fn consumption_rate() {
-        let mut server = DummyStreamSource::new();
+        let mut server = NoopStreamSource::new();
         let (_, mut stream) = server
             .new_capture_stream(2, SampleFormat::S16LE, 48000, 480)
             .unwrap();
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index 243107a..c55a8df 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -151,9 +151,9 @@
 		int32 IsAudioOutputActive()
 
 			Returns 1 if there are currently any active output streams,
-			excluding 'dummy' streams that are not actually outputting any
+			excluding 'fake' streams that are not actually outputting any
 			audio. Returns 0 if there are no active streams, or all active
-			streams are 'dummy' streams.
+			streams are 'fake' streams.
 
 		void SetGlobalOutputChannelRemix(int32 num_channels,
 						 array:double coefficient)
diff --git a/cras/client/cras-sys/generator/src/main.rs b/cras/client/cras-sys/generator/src/main.rs
index 1a02245..e562691 100644
--- a/cras/client/cras-sys/generator/src/main.rs
+++ b/cras/client/cras-sys/generator/src/main.rs
@@ -36,6 +36,7 @@
         "cras_shm.h",
         "cras_types.h",
         "cras_util.h",
+        "packet_status_logger.h",
     ];
 
     for header in &header_files {
@@ -133,7 +134,11 @@
  * cras_shm.h
  * cras_types.h
  * cras_util.h
+ * packet_status_logger.h
  */
+
+#![allow(clippy::unreadable_literal)]
+#![allow(clippy::cognitive_complexity)]
 ";
 
     let mut output_file = File::create(output_path)?;
diff --git a/cras/client/cras-sys/src/gen.rs b/cras/client/cras-sys/src/gen.rs
index 129a91a..0375a0b 100644
--- a/cras/client/cras-sys/src/gen.rs
+++ b/cras/client/cras-sys/src/gen.rs
@@ -10,7 +10,11 @@
  * cras_shm.h
  * cras_types.h
  * cras_util.h
+ * packet_status_logger.h
  */
+
+#![allow(clippy::unreadable_literal)]
+#![allow(clippy::cognitive_complexity)]
 /* automatically generated by rust-bindgen */
 
 pub const CRAS_IODEV_NAME_BUFFER_SIZE: u32 = 64;
@@ -609,6 +613,78 @@
         )
     );
 }
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct packet_status_logger {
+    pub data: [u8; 64usize],
+    pub size: ::std::os::raw::c_int,
+    pub wp: ::std::os::raw::c_int,
+    pub num_wraps: ::std::os::raw::c_int,
+    pub ts: timespec,
+}
+#[test]
+fn bindgen_test_layout_packet_status_logger() {
+    assert_eq!(
+        ::std::mem::size_of::<packet_status_logger>(),
+        96usize,
+        concat!("Size of: ", stringify!(packet_status_logger))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<packet_status_logger>(),
+        8usize,
+        concat!("Alignment of ", stringify!(packet_status_logger))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<packet_status_logger>())).data as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(packet_status_logger),
+            "::",
+            stringify!(data)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<packet_status_logger>())).size as *const _ as usize },
+        64usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(packet_status_logger),
+            "::",
+            stringify!(size)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<packet_status_logger>())).wp as *const _ as usize },
+        68usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(packet_status_logger),
+            "::",
+            stringify!(wp)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<packet_status_logger>())).num_wraps as *const _ as usize },
+        72usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(packet_status_logger),
+            "::",
+            stringify!(num_wraps)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<packet_status_logger>())).ts as *const _ as usize },
+        80usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(packet_status_logger),
+            "::",
+            stringify!(ts)
+        )
+    );
+}
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
 pub struct cras_timespec {
@@ -845,13 +921,17 @@
     BT_HFP_REQUEST_DISCONNECT = 13,
     BT_HFP_SUPPORTED_FEATURES = 14,
     BT_HFP_HF_INDICATOR = 15,
-    BT_HSP_NEW_CONNECTION = 16,
-    BT_HSP_REQUEST_DISCONNECT = 17,
-    BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 18,
-    BT_RESET = 19,
-    BT_SCO_CONNECT = 20,
-    BT_TRANSPORT_ACQUIRE = 21,
-    BT_TRANSPORT_RELEASE = 22,
+    BT_HFP_SET_SPEAKER_GAIN = 16,
+    BT_HFP_UPDATE_SPEAKER_GAIN = 17,
+    BT_HSP_NEW_CONNECTION = 18,
+    BT_HSP_REQUEST_DISCONNECT = 19,
+    BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 20,
+    BT_RESET = 21,
+    BT_SCO_CONNECT = 22,
+    BT_TRANSPORT_ACQUIRE = 23,
+    BT_TRANSPORT_RELEASE = 24,
+    BT_TRANSPORT_SET_VOLUME = 25,
+    BT_TRANSPORT_UPDATE_VOLUME = 26,
 }
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
@@ -1589,12 +1669,13 @@
     pub nsec: u32,
     pub data1: u32,
     pub data2: u32,
+    pub data3: u32,
 }
 #[test]
 fn bindgen_test_layout_main_thread_event() {
     assert_eq!(
         ::std::mem::size_of::<main_thread_event>(),
-        16usize,
+        20usize,
         concat!("Size of: ", stringify!(main_thread_event))
     );
     assert_eq!(
@@ -1642,6 +1723,16 @@
             stringify!(data2)
         )
     );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event>())).data3 as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event),
+            "::",
+            stringify!(data3)
+        )
+    );
 }
 #[repr(C, packed)]
 #[derive(Copy, Clone)]
@@ -1654,7 +1745,7 @@
 fn bindgen_test_layout_main_thread_event_log() {
     assert_eq!(
         ::std::mem::size_of::<main_thread_event_log>(),
-        16392usize,
+        20488usize,
         concat!("Size of: ", stringify!(main_thread_event_log))
     );
     assert_eq!(
@@ -1702,7 +1793,7 @@
 fn bindgen_test_layout_main_thread_debug_info() {
     assert_eq!(
         ::std::mem::size_of::<main_thread_debug_info>(),
-        16392usize,
+        20488usize,
         concat!("Size of: ", stringify!(main_thread_debug_info))
     );
     assert_eq!(
@@ -1836,12 +1927,13 @@
 #[derive(Copy, Clone)]
 pub struct cras_bt_debug_info {
     pub bt_log: cras_bt_event_log,
+    pub wbs_logger: packet_status_logger,
 }
 #[test]
 fn bindgen_test_layout_cras_bt_debug_info() {
     assert_eq!(
         ::std::mem::size_of::<cras_bt_debug_info>(),
-        16392usize,
+        16488usize,
         concat!("Size of: ", stringify!(cras_bt_debug_info))
     );
     assert_eq!(
@@ -1859,6 +1951,16 @@
             stringify!(bt_log)
         )
     );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<cras_bt_debug_info>())).wbs_logger as *const _ as usize },
+        16392usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(cras_bt_debug_info),
+            "::",
+            stringify!(wbs_logger)
+        )
+    );
 }
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -2013,13 +2115,14 @@
     pub snapshot_buffer: cras_audio_thread_snapshot_buffer,
     pub bt_debug_info: cras_bt_debug_info,
     pub bt_wbs_enabled: i32,
+    pub deprioritize_bt_wbs_mic: i32,
     pub main_thread_debug_info: main_thread_debug_info,
 }
 #[test]
 fn bindgen_test_layout_cras_server_state() {
     assert_eq!(
         ::std::mem::size_of::<cras_server_state>(),
-        1410096usize,
+        1414292usize,
         concat!("Size of: ", stringify!(cras_server_state))
     );
     assert_eq!(
@@ -2383,7 +2486,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).bt_wbs_enabled as *const _ as usize
         },
-        1393700usize,
+        1393796usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2393,10 +2496,23 @@
     );
     assert_eq!(
         unsafe {
+            &(*(::std::ptr::null::<cras_server_state>())).deprioritize_bt_wbs_mic as *const _
+                as usize
+        },
+        1393800usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(cras_server_state),
+            "::",
+            stringify!(deprioritize_bt_wbs_mic)
+        )
+    );
+    assert_eq!(
+        unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).main_thread_debug_info as *const _
                 as usize
         },
-        1393704usize,
+        1393804usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2857,187 +2973,6 @@
 }
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
-pub struct cras_connect_message_old {
-    pub header: cras_server_message,
-    pub proto_version: u32,
-    pub direction: CRAS_STREAM_DIRECTION,
-    pub stream_id: cras_stream_id_t,
-    pub stream_type: CRAS_STREAM_TYPE,
-    pub buffer_frames: u32,
-    pub cb_threshold: u32,
-    pub flags: u32,
-    pub format: cras_audio_format_packed,
-    pub dev_idx: u32,
-    pub effects: u64,
-    pub client_type: CRAS_CLIENT_TYPE,
-    pub client_shm_size: u32,
-}
-#[test]
-fn bindgen_test_layout_cras_connect_message_old() {
-    assert_eq!(
-        ::std::mem::size_of::<cras_connect_message_old>(),
-        79usize,
-        concat!("Size of: ", stringify!(cras_connect_message_old))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<cras_connect_message_old>(),
-        1usize,
-        concat!("Alignment of ", stringify!(cras_connect_message_old))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).header as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(header)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).proto_version as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(proto_version)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).direction as *const _ as usize
-        },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(direction)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).stream_id as *const _ as usize
-        },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(stream_id)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).stream_type as *const _ as usize
-        },
-        20usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(stream_type)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).buffer_frames as *const _ as usize
-        },
-        24usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(buffer_frames)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).cb_threshold as *const _ as usize
-        },
-        28usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(cb_threshold)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).flags as *const _ as usize },
-        32usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(flags)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).format as *const _ as usize },
-        36usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(format)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).dev_idx as *const _ as usize
-        },
-        59usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(dev_idx)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).effects as *const _ as usize
-        },
-        63usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(effects)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).client_type as *const _ as usize
-        },
-        71usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(client_type)
-        )
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<cras_connect_message_old>())).client_shm_size as *const _
-                as usize
-        },
-        75usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_connect_message_old),
-            "::",
-            stringify!(client_shm_size)
-        )
-    );
-}
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone)]
 pub struct cras_disconnect_stream_message {
     pub header: cras_server_message,
     pub stream_id: cras_stream_id_t,
diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs
index 261ad63..5ab2247 100644
--- a/cras/client/cras_tests/src/audio.rs
+++ b/cras/client/cras_tests/src/audio.rs
@@ -317,9 +317,11 @@
                         samples[sample_bytes * i + 3],
                     ]);
 
-                    // Upsample to 32 bit since CRAS doesn't support S24_3LE,
-                    // even though the wav encoder does.
-                    // TODO(fletcherw): add S24_LE support to hound.
+                    // Upsample to 32 bit since CRAS doesn't support S24_3LE.
+                    // Our wav encoder/decoder, hound, does have support for
+                    // S24_LE, but it hasn't released a new version since the
+                    // support was added. If getting that support is an issue,
+                    // push upstream to cut a new a release.
                     if self.format == SampleFormat::S24LE {
                         sample <<= 8;
                     }
diff --git a/cras/client/libcras/src/cras_client_message.rs b/cras/client/libcras/src/cras_client_message.rs
index d42b6f9..c1c5ec5 100644
--- a/cras/client/libcras/src/cras_client_message.rs
+++ b/cras/client/libcras/src/cras_client_message.rs
@@ -195,6 +195,6 @@
         if self.len != mem::size_of::<T>() {
             return Err(Error::InvalidSize);
         }
-        T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or_else(|| Error::MessageFromSliceError)
+        T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or(Error::MessageFromSliceError)
     }
 }
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs
index 62e8fdd..80d2cff 100644
--- a/cras/client/libcras/src/libcras.rs
+++ b/cras/client/libcras/src/libcras.rs
@@ -126,9 +126,9 @@
 
 pub use audio_streams::BoxError;
 use audio_streams::{
-    capture::{CaptureBufferStream, DummyCaptureStream},
+    capture::{CaptureBufferStream, NoopCaptureStream},
     shm_streams::{NullShmStream, ShmStream, ShmStreamSource},
-    BufferDrop, DummyStreamControl, PlaybackBufferStream, SampleFormat, StreamControl,
+    BufferDrop, NoopStreamControl, PlaybackBufferStream, SampleFormat, StreamControl,
     StreamDirection, StreamEffect, StreamSource,
 };
 use cras_sys::gen::*;
@@ -475,7 +475,7 @@
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
     {
         Ok((
-            Box::new(DummyStreamControl::new()),
+            Box::new(NoopStreamControl::new()),
             Box::new(self.create_stream::<CrasPlaybackData>(
                 Some(device_index),
                 buffer_size as u32,
@@ -509,7 +509,7 @@
         buffer_size: usize,
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
         Ok((
-            Box::new(DummyStreamControl::new()),
+            Box::new(NoopStreamControl::new()),
             Box::new(self.create_stream::<CrasCaptureData>(
                 Some(device_index),
                 buffer_size as u32,
@@ -535,7 +535,7 @@
         let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect();
         tokens
             .get(0)
-            .ok_or_else(|| Error::UnexpectedExit)
+            .ok_or(Error::UnexpectedExit)
             .and_then(|ref token| {
                 match token {
                     Token::ServerMsg => ServerResult::handle_server_message(socket),
@@ -562,7 +562,7 @@
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
     {
         Ok((
-            Box::new(DummyStreamControl::new()),
+            Box::new(NoopStreamControl::new()),
             Box::new(self.create_stream::<CrasPlaybackData>(
                 None,
                 buffer_size as u32,
@@ -584,7 +584,7 @@
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
         if self.cras_capture {
             Ok((
-                Box::new(DummyStreamControl::new()),
+                Box::new(NoopStreamControl::new()),
                 Box::new(self.create_stream::<CrasCaptureData>(
                     None,
                     buffer_size as u32,
@@ -596,8 +596,8 @@
             ))
         } else {
             Ok((
-                Box::new(DummyStreamControl::new()),
-                Box::new(DummyCaptureStream::new(
+                Box::new(NoopStreamControl::new()),
+                Box::new(NoopCaptureStream::new(
                     num_channels,
                     format,
                     frame_rate,
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index 874389c..69fea5f 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -46,6 +46,7 @@
 if HAVE_DBUS
 CRAS_DBUS_SOURCES = \
 	common/cras_sbc_codec.c \
+	common/packet_status_logger.c \
 	server/cras_bt_manager.c \
 	server/cras_bt_adapter.c \
 	server/cras_bt_device.c \
@@ -155,6 +156,7 @@
 	server/cras_volume_curve.c \
 	server/dev_io.c \
 	server/dev_stream.c \
+	server/ewma_power.c \
 	server/input_data.c \
 	server/linear_resampler.c \
 	server/polled_interval_checker.c \
@@ -280,6 +282,7 @@
 	common/cras_types.h \
 	common/cras_util.h \
 	common/edid_utils.h \
+	common/packet_status_logger.h \
 	common/utlist.h \
 	libcras/cras_client.h \
 	libcras/cras_helpers.h
@@ -437,6 +440,7 @@
 	edid_utils_unittest \
 	empty_iodev_unittest \
 	expr_unittest \
+	ewma_power_unittest \
 	file_wait_unittest \
 	float_buffer_unittest \
 	fmt_conv_unittest \
@@ -880,6 +884,14 @@
 	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server
 buffer_share_unittest_LDADD = -lgtest -liniparser -lpthread
 
+ewma_power_unittest_SOURCES = tests/ewma_power_unittest.cc \
+	common/cras_audio_format.c server/cras_audio_area.c \
+	server/ewma_power.c
+
+ewma_power_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
+	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server
+ewma_power_unittest_LDADD = -lgtest
+
 iodev_list_unittest_SOURCES = tests/iodev_list_unittest.cc \
 	server/cras_iodev_list.c
 iodev_list_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
@@ -949,8 +961,9 @@
 				   server/cras_rstream_config.c
 control_rclient_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
 	-I$(top_srcdir)/src/common \
-	-I$(top_srcdir)/src/server $(CRAS_UT_TMPDIR_CFLAGS)
-control_rclient_unittest_LDADD = -lgtest -lpthread
+	-I$(top_srcdir)/src/server $(CRAS_UT_TMPDIR_CFLAGS) \
+	$(DBUS_CFLAGS)
+control_rclient_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
 
 playback_rclient_unittest_SOURCES = tests/playback_rclient_unittest.cc \
 				    server/cras_rstream_config.c
diff --git a/cras/src/alsa_plugin/pcm_cras.c b/cras/src/alsa_plugin/pcm_cras.c
index 715db2c..7bc960b 100644
--- a/cras/src/alsa_plugin/pcm_cras.c
+++ b/cras/src/alsa_plugin/pcm_cras.c
@@ -124,7 +124,7 @@
 	struct snd_pcm_cras *pcm_cras;
 	const snd_pcm_channel_area_t *areas;
 	snd_pcm_uframes_t copied_frames;
-	char dummy_byte;
+	char empty_byte;
 	size_t chan, frame_bytes, sample_bytes;
 	int rc;
 	uint8_t *samples;
@@ -196,7 +196,7 @@
 		copied_frames += frames;
 	}
 
-	rc = write(pcm_cras->fd, &dummy_byte, 1); /* Wake up polling clients. */
+	rc = write(pcm_cras->fd, &empty_byte, 1); /* Wake up polling clients. */
 	if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
 		fprintf(stderr, "%s write failed %d\n", __func__, errno);
 
diff --git a/cras/src/common/cras_config.c b/cras/src/common/cras_config.c
index 55caee2..75fa24e 100644
--- a/cras/src/common/cras_config.c
+++ b/cras/src/common/cras_config.c
@@ -44,6 +44,12 @@
 	case CRAS_VMS_UNIFIED:
 		sock_file = CRAS_VMS_UNIFIED_SOCKET_FILE;
 		break;
+	case CRAS_PLUGIN_PLAYBACK:
+		sock_file = CRAS_PLUGIN_PLAYBACK_SOCKET_FILE;
+		break;
+	case CRAS_PLUGIN_UNIFIED:
+		sock_file = CRAS_PLUGIN_UNIFIED_SOCKET_FILE;
+		break;
 	default:
 		return -EINVAL;
 	}
diff --git a/cras/src/common/cras_config.h b/cras/src/common/cras_config.h
index 7f0aa23..1c8e55f 100644
--- a/cras/src/common/cras_config.h
+++ b/cras/src/common/cras_config.h
@@ -20,6 +20,9 @@
 /* Socket file paths for VMs. */
 #define CRAS_VMS_LEGACY_SOCKET_FILE "vms/.cras_socket"
 #define CRAS_VMS_UNIFIED_SOCKET_FILE "vms/.cras_unified"
+/* Socket file paths for pluginVM. */
+#define CRAS_PLUGIN_PLAYBACK_SOCKET_FILE "vms/plugin/playback/.cras_socket"
+#define CRAS_PLUGIN_UNIFIED_SOCKET_FILE "vms/plugin/unified/.cras_socket"
 
 /* Maximum socket_path size, which is equals to sizeof(sun_path) in sockaddr_un
  * structure.
diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h
index ee09d93..50cbe7c 100644
--- a/cras/src/common/cras_messages.h
+++ b/cras/src/common/cras_messages.h
@@ -120,28 +120,6 @@
 	uint64_t buffer_offsets[2];
 };
 
-/*
- * Old version of connect message without 'buffer_offsets'.
- * Used to check against when receiving invalid size of connect message.
- * Expected to have proto_version set to 5.
- * TODO(fletcherw): 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 */
-	uint64_t effects; /* Bit map of requested effects. */
-	enum CRAS_CLIENT_TYPE client_type; /* chrome, or arc, etc. */
-	uint32_t client_shm_size; /* Size of client-provided samples shm, if any */
-};
-
 static inline void cras_fill_connect_message(
 	struct cras_connect_message *m, enum CRAS_STREAM_DIRECTION direction,
 	cras_stream_id_t stream_id, enum CRAS_STREAM_TYPE stream_type,
diff --git a/cras/src/common/cras_observer_ops.h b/cras/src/common/cras_observer_ops.h
index a54d044..e73845c 100644
--- a/cras/src/common/cras_observer_ops.h
+++ b/cras/src/common/cras_observer_ops.h
@@ -47,6 +47,10 @@
 	void (*num_active_streams_changed)(void *context,
 					   enum CRAS_STREAM_DIRECTION dir,
 					   uint32_t num_active_streams);
+	/* Number of input streams with permission changed. */
+	void (*num_input_streams_with_permission_changed)(
+		void *context,
+		uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]);
 	/* Hotword triggered. */
 	void (*hotword_triggered)(void *context, int64_t tv_sec,
 				  int64_t tv_nsec);
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index c3482e8..90a0474 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -15,6 +15,7 @@
 
 #include "cras_audio_format.h"
 #include "cras_iodev_info.h"
+#include "packet_status_logger.h"
 
 /* Architecture independent timespec */
 struct __attribute__((__packed__)) cras_timespec {
@@ -50,6 +51,8 @@
 	CRAS_CAPTURE, // For capture client.
 	CRAS_VMS_LEGACY, // For legacy client in vms.
 	CRAS_VMS_UNIFIED, // For unified client in vms.
+	CRAS_PLUGIN_PLAYBACK, // For playback client in vms/plugin.
+	CRAS_PLUGIN_UNIFIED, // For unified client in vms/plugin.
 	CRAS_NUM_CONN_TYPE,
 };
 
@@ -165,8 +168,15 @@
 	CRAS_CLIENT_TYPE_CROSVM, /* CROSVM */
 	CRAS_CLIENT_TYPE_SERVER_STREAM, /* Server stream */
 	CRAS_CLIENT_TYPE_LACROS, /* LaCrOS */
+	CRAS_CLIENT_TYPE_PLUGIN, /* PluginVM */
+	CRAS_NUM_CLIENT_TYPE, /* numbers of CRAS_CLIENT_TYPE */
 };
 
+static inline bool cras_validate_client_type(enum CRAS_CLIENT_TYPE client_type)
+{
+	return 0 <= client_type && client_type < CRAS_NUM_CLIENT_TYPE;
+}
+
 #define ENUM_STR(x)                                                            \
 	case x:                                                                \
 		return #x;
@@ -202,6 +212,7 @@
 	ENUM_STR(CRAS_CLIENT_TYPE_CROSVM)
 	ENUM_STR(CRAS_CLIENT_TYPE_SERVER_STREAM)
 	ENUM_STR(CRAS_CLIENT_TYPE_LACROS)
+	ENUM_STR(CRAS_CLIENT_TYPE_PLUGIN)
 	default:
 		return "INVALID_CLIENT_TYPE";
 	}
@@ -479,6 +490,7 @@
 
 struct __attribute__((__packed__)) cras_bt_debug_info {
 	struct cras_bt_event_log bt_log;
+	struct packet_status_logger wbs_logger;
 };
 
 /*
@@ -556,7 +568,11 @@
  *    snapshot_buffer - ring buffer for storing audio thread snapshots.
  *    bt_debug_info - ring buffer for storing bluetooth event logs.
  *    bt_wbs_enabled - Whether or not bluetooth wideband speech is enabled.
+ *    deprioritize_bt_wbs_mic - Whether Bluetooth wideband speech mic
+ *        should be deprioritized for selecting as default audio input.
  *    main_thread_debug_info - ring buffer for storing main thread event logs.
+ *    num_input_streams_with_permission - An array containing numbers of input
+ *        streams with permission in each client type.
  */
 #define CRAS_SERVER_STATE_VERSION 2
 struct __attribute__((packed, aligned(4))) cras_server_state {
@@ -593,7 +609,9 @@
 	struct cras_audio_thread_snapshot_buffer snapshot_buffer;
 	struct cras_bt_debug_info bt_debug_info;
 	int32_t bt_wbs_enabled;
+	int32_t deprioritize_bt_wbs_mic;
 	struct main_thread_debug_info main_thread_debug_info;
+	uint32_t num_input_streams_with_permission[CRAS_NUM_CLIENT_TYPE];
 };
 
 /* Actions for card add/remove/change. */
diff --git a/cras/src/common/packet_status_logger.c b/cras/src/common/packet_status_logger.c
new file mode 100644
index 0000000..f1be696
--- /dev/null
+++ b/cras/src/common/packet_status_logger.c
@@ -0,0 +1,35 @@
+/* Copyright 2020 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 <time.h>
+
+#include "cras_util.h"
+#include "packet_status_logger.h"
+
+void packet_status_logger_init(struct packet_status_logger *logger)
+{
+	memset(logger->data, 0, PACKET_STATUS_LEN_BYTES);
+	logger->size = PACKET_STATUS_LEN_BYTES * 8;
+	logger->wp = 0;
+	logger->num_wraps = 0;
+	clock_gettime(CLOCK_MONOTONIC_RAW, &logger->ts);
+}
+
+void packet_status_logger_update(struct packet_status_logger *logger, bool val)
+{
+	if (val) {
+		logger->data[logger->wp / 8] |= 1UL << (logger->wp % 8);
+	} else {
+		logger->data[logger->wp / 8] &= ~(1UL << (logger->wp % 8));
+	}
+	logger->wp++;
+	if (logger->wp >= logger->size) {
+		logger->wp %= logger->size;
+		logger->num_wraps += 1;
+	}
+	if (logger->wp == 0 || (logger->num_wraps == 0 && logger->wp == 1))
+		clock_gettime(CLOCK_MONOTONIC_RAW, &logger->ts);
+}
diff --git a/cras/src/common/packet_status_logger.h b/cras/src/common/packet_status_logger.h
new file mode 100644
index 0000000..3bc9004
--- /dev/null
+++ b/cras/src/common/packet_status_logger.h
@@ -0,0 +1,127 @@
+/* Copyright 2020 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 PACKET_STATUS_LOGGER_
+#define PACKET_STATUS_LOGGER_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define PACKET_STATUS_LEN_BYTES 64
+#define WBS_FRAME_NS 7500000
+
+/* Avoid 32, 40, 64 consecutive hex characters so CrOS feedback redact
+ * tool doesn't trim our dump. */
+#define PACKET_STATUS_LOG_LINE_WRAP 50
+
+/*
+ * Object to log consecutive packets' status.
+ * Members:
+ *    data - Bytes to store packets' status.
+ *    size - Total number of bits in |data|.
+ *    wp - Position of the next bit to log packet status.
+ *    num_wraps - Number of times the ring buffer has wrapped.
+ *    ts - The timestamp of the last time when the first bit of |data| updated.
+ */
+struct packet_status_logger {
+	uint8_t data[PACKET_STATUS_LEN_BYTES];
+	int size;
+	int wp;
+	int num_wraps;
+	struct timespec ts;
+};
+
+/* Initializes the packet status logger. */
+void packet_status_logger_init(struct packet_status_logger *logger);
+
+/* Updates the next packet status to logger. */
+void packet_status_logger_update(struct packet_status_logger *logger, bool val);
+
+/* Rewinds logger's time stamp to calculate the beginning.
+ * If logger's ring buffer hasn't wrapped, simply return logger_ts.
+ * Otherwise beginning_ts = logger_ts - WBS_FRAME_NS * (size - wp)
+ */
+static inline void
+packet_status_logger_begin_ts(const struct packet_status_logger *logger,
+			      struct timespec *ts)
+{
+	long nsec = WBS_FRAME_NS * (logger->size - logger->wp);
+
+	*ts = logger->ts;
+	if (logger->num_wraps == 0)
+		return;
+	while (nsec > 1000000000L) {
+		ts->tv_sec--;
+		nsec -= 1000000000L;
+	}
+	ts->tv_nsec -= nsec;
+	if (ts->tv_nsec < 0) {
+		ts->tv_sec--;
+		ts->tv_nsec += 1000000000L;
+	}
+}
+
+/* Fast-forwards the logger's time stamp to calculate the end.
+ * In other words, end_ts = logger_ts + WBS_FRAME_NS * wp
+ */
+static inline void
+packet_status_logger_end_ts(const struct packet_status_logger *logger,
+			    struct timespec *ts)
+{
+	*ts = logger->ts;
+	ts->tv_nsec += WBS_FRAME_NS * logger->wp;
+	while (ts->tv_nsec > 1000000000L) {
+		ts->tv_sec++;
+		ts->tv_nsec -= 1000000000L;
+	}
+}
+
+/* Prints the logger data in hex format */
+static inline void
+packet_status_logger_dump_hex(const struct packet_status_logger *logger)
+{
+	int i = logger->wp / 8;
+
+	/* Print the bits after wp only if buffer has wrapped. */
+	if (logger->num_wraps) {
+		if (logger->wp % 8)
+			printf("%.2x",
+			       logger->data[i] & (0xff << (logger->wp % 8)));
+		for (; i < PACKET_STATUS_LEN_BYTES; i++)
+			printf("%.2x", logger->data[i]);
+	}
+	for (i = 0; i < logger->wp / 8; i++)
+		printf("%.2x", logger->data[i]);
+	if (logger->wp % 8)
+		printf("%.2x", logger->data[i] & (~(0xff << (logger->wp % 8))));
+	printf("\n");
+}
+
+/* Prints the logger data in binary format */
+static inline void
+packet_status_logger_dump_binary(const struct packet_status_logger *logger)
+{
+	/* Don't print the bits after wp if buffer hasn't wrapped. */
+	int head = logger->num_wraps ? logger->wp : 0;
+	int len = logger->num_wraps ? logger->size : logger->wp;
+	int i, j;
+
+	for (i = 0; i < len; ++i) {
+		j = (head + i) % logger->size;
+		printf("%d", (logger->data[j / 8] >> (j % 8)) & 1U);
+		if ((i + 1) % PACKET_STATUS_LOG_LINE_WRAP == 0)
+			printf("\n");
+	}
+	/* Fill indicator digit 'D' until the last line wraps. */
+	if (len % PACKET_STATUS_LOG_LINE_WRAP) {
+		while (len % PACKET_STATUS_LOG_LINE_WRAP) {
+			printf("D");
+			++len;
+		}
+		printf("\n");
+	}
+}
+
+#endif /* PACKET_STATUS_LOGGER_ */
diff --git a/cras/src/plc/cras_plc.c b/cras/src/plc/cras_plc.c
index 4bc9fb7..74c3568 100644
--- a/cras/src/plc/cras_plc.c
+++ b/cras/src/plc/cras_plc.c
@@ -22,6 +22,9 @@
 #define PLC_SBCRL 36 /* SBC Reconvergence sample Length */
 #define PLC_OLAL 16 /* OverLap-Add Length */
 
+#define PLC_WINDOW_SIZE 5
+#define PLC_PL_THRESHOLD 2
+
 /* The pre-computed zero input bit stream of mSBC codec, per HFP 1.7 spec.
  * This mSBC frame will be decoded into all-zero input PCM. */
 static const uint8_t msbc_zero_frame[] = {
@@ -40,6 +43,18 @@
 				      0.13049554f, 0.07489143f, 0.03376389f,
 				      0.00851345f };
 
+/* This structure tracks the packet loss information for last PLC_WINDOW_SIZE
+ * of packets:
+ *    loss_hist - The packet loss history of receiving packets. 1 means lost.
+ *    ptr - The index of the to be updated packet loss status.
+ *    count - The count of lost packets in the window.
+ */
+struct packet_window {
+	uint8_t loss_hist[PLC_WINDOW_SIZE];
+	unsigned int ptr;
+	unsigned int count;
+};
+
 /* The PLC is specifically designed for mSBC. The algorithm searches the
  * history of receiving samples to find the best match samples and constructs
  * substitutions for the lost samples. The selection is based on pattern
@@ -57,23 +72,30 @@
  *                         frame.
  *    zero_frame - A buffer used for storing the samples from decoding the
  *                 mSBC zero frame packet.
+ *    pl_window - A window monitoring how many packets are bad within the recent
+ *                PLC_WINDOW_SIZE of packets. This is used to determine if we
+ *                want to disable the PLC temporarily.
  */
 struct cras_msbc_plc {
 	int16_t hist[PLC_HL + MSBC_FS + PLC_SBCRL + PLC_OLAL];
 	unsigned int best_lag;
 	int handled_bad_frames;
 	int16_t zero_frame[MSBC_FS];
+	struct packet_window *pl_window;
 };
 
 struct cras_msbc_plc *cras_msbc_plc_create()
 {
 	struct cras_msbc_plc *plc =
 		(struct cras_msbc_plc *)calloc(1, sizeof(*plc));
+	plc->pl_window =
+		(struct packet_window *)calloc(1, sizeof(*plc->pl_window));
 	return plc;
 }
 
 void cras_msbc_plc_destroy(struct cras_msbc_plc *plc)
 {
+	free(plc->pl_window);
 	free(plc);
 }
 
@@ -94,14 +116,33 @@
 	}
 }
 
+void update_plc_state(struct packet_window *w, uint8_t is_packet_loss)
+{
+	uint8_t *curr = &w->loss_hist[w->ptr];
+	if (is_packet_loss != *curr) {
+		w->count += (is_packet_loss - *curr);
+		*curr = is_packet_loss;
+	}
+	w->ptr = (w->ptr + 1) % PLC_WINDOW_SIZE;
+}
+
+int possibly_pause_plc(struct packet_window *w)
+{
+	/* The packet loss count comes from a time window and we use it as an
+	 * indicator of our confidence of the PLC algorithm. It is known to
+	 * generate poorer and robotic feeling sounds, when the majority of
+	 * samples in the PLC history buffer are from the concealment results.
+	 */
+	return w->count >= PLC_PL_THRESHOLD;
+}
+
 int cras_msbc_plc_handle_good_frames(struct cras_msbc_plc *state,
 				     const uint8_t *input, uint8_t *output)
 {
 	int16_t *frame_head, *input_samples, *output_samples;
 	if (state->handled_bad_frames == 0) {
-		/* If there was no packet loss before this good frame, there
-		 * is nothing we need to do to the frame so we'll just pass
-		 * the input to output.
+		/* If there was no packet concealment before this good frame,
+		 * we just simply copy the input to output without reconverge.
 		 */
 		memmove(output, input, MSBC_FS * MSBC_SAMPLE_SIZE);
 	} else {
@@ -129,6 +170,7 @@
 		(PLC_HL - MSBC_FS) * MSBC_SAMPLE_SIZE);
 	memcpy(&state->hist[PLC_HL - MSBC_FS], output,
 	       MSBC_FS * MSBC_SAMPLE_SIZE);
+	update_plc_state(state->pl_window, 0);
 	return MSBC_CODE_SIZE;
 }
 
@@ -184,37 +226,60 @@
 	int16_t *frame_head = &state->hist[PLC_HL];
 	size_t pcm_decoded = 0;
 
+	/* mSBC codec is stateful, the history of signal would contribute to the
+	 * decode result state->zero_frame.
+	 */
 	codec->decode(codec, msbc_zero_frame, MSBC_PKT_LEN, state->zero_frame,
 		      MSBC_FS, &pcm_decoded);
 
-	if (state->handled_bad_frames == 0) {
-		/* Finds the best matching samples and amplitude */
-		state->best_lag = pattern_match(state->hist) + PLC_TL;
-		best_match_hist = &state->hist[state->best_lag];
-		scaler = amplitude_match(&state->hist[PLC_HL - MSBC_FS],
-					 best_match_hist);
+	/* The PLC algorithm is more likely to generate bad results that sound
+	 * robotic after severe packet losses happened. Only applying it when
+	 * we are confident.
+	 */
+	if (!possibly_pause_plc(state->pl_window)) {
+		if (state->handled_bad_frames == 0) {
+			/* Finds the best matching samples and amplitude */
+			state->best_lag = pattern_match(state->hist) + PLC_TL;
+			best_match_hist = &state->hist[state->best_lag];
+			scaler = amplitude_match(&state->hist[PLC_HL - MSBC_FS],
+						 best_match_hist);
 
-		/* Constructs the substitution samples */
-		overlap_add(frame_head, 1.0, state->zero_frame, scaler,
-			    best_match_hist);
-		for (int i = PLC_OLAL; i < MSBC_FS; i++)
-			state->hist[PLC_HL + i] =
-				f_to_s16(scaler * best_match_hist[i]);
-		overlap_add(&frame_head[MSBC_FS], scaler,
-			    &best_match_hist[MSBC_FS], 1.0,
-			    &best_match_hist[MSBC_FS]);
+			/* Constructs the substitution samples */
+			overlap_add(frame_head, 1.0, state->zero_frame, scaler,
+				    best_match_hist);
+			for (int i = PLC_OLAL; i < MSBC_FS; i++)
+				state->hist[PLC_HL + i] =
+					f_to_s16(scaler * best_match_hist[i]);
+			overlap_add(&frame_head[MSBC_FS], scaler,
+				    &best_match_hist[MSBC_FS], 1.0,
+				    &best_match_hist[MSBC_FS]);
 
-		memmove(&frame_head[MSBC_FS + PLC_OLAL],
-			&best_match_hist[MSBC_FS + PLC_OLAL],
-			PLC_SBCRL * MSBC_SAMPLE_SIZE);
+			memmove(&frame_head[MSBC_FS + PLC_OLAL],
+				&best_match_hist[MSBC_FS + PLC_OLAL],
+				PLC_SBCRL * MSBC_SAMPLE_SIZE);
+		} else {
+			memmove(frame_head, &state->hist[state->best_lag],
+				(MSBC_FS + PLC_SBCRL + PLC_OLAL) *
+					MSBC_SAMPLE_SIZE);
+		}
+		state->handled_bad_frames++;
 	} else {
-		memmove(frame_head, &state->hist[state->best_lag],
-			(MSBC_FS + PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE);
+		/* This is a case similar to receiving a good frame with all
+		 * zeros, we set handled_bad_frames to zero to prevent the
+		 * following good frame from being concealed to reconverge with
+		 * the zero frames we fill in. The concealment result sounds
+		 * more artificial and weird than simply writing zeros and
+		 * following samples.
+		 */
+		memmove(frame_head, state->zero_frame, MSBC_CODE_SIZE);
+		memset(frame_head + MSBC_CODE_SIZE, 0,
+		       (PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE);
+		state->handled_bad_frames = 0;
 	}
-	state->handled_bad_frames++;
 
 	memcpy(output, frame_head, MSBC_CODE_SIZE);
 	memmove(state->hist, &state->hist[MSBC_FS],
 		(PLC_HL + PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE);
+	update_plc_state(state->pl_window, 1);
 	return MSBC_CODE_SIZE;
 }
diff --git a/cras/src/plc/cras_plc_test.c b/cras/src/plc/cras_plc_test.c
index 458f125..4b7a6a7 100644
--- a/cras/src/plc/cras_plc_test.c
+++ b/cras/src/plc/cras_plc_test.c
@@ -4,11 +4,13 @@
  */
 
 #include <errno.h>
+#include <getopt.h>
 #include <math.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/param.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -29,32 +31,65 @@
 	0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c
 };
 
-bool *generate_pl_seq(unsigned pk_count, unsigned loss_count)
+bool *generate_pl_seq(int input_file_size, float pl_percent)
 {
-	bool *seq = (bool *)calloc(pk_count, sizeof(*seq));
+	unsigned pk_count, pl_count;
+	bool *seq;
+
+	pk_count = input_file_size / MSBC_CODE_SIZE;
+	pl_count = pk_count * (pl_percent / 100.0);
+	seq = (bool *)calloc(pk_count, sizeof(*seq));
 	srand(RND_SEED);
-	while (loss_count > 0) {
+	while (pl_count > 0) {
 		bool *missed = &seq[rand() % pk_count];
 		if (!*missed) {
 			*missed = true;
-			loss_count--;
+			pl_count--;
 		}
 	}
 	return seq;
 }
 
-void plc_experiment(char *input_filename, float pl_percent, bool with_plc)
+/* pl_hex is expected to be consecutive bytes(two chars) in hex format.*/
+bool *parse_pl_hex(int input_file_size, const char *pl_hex)
+{
+	char tmp[3];
+	uint8_t val = 0;
+	int i, pl_hex_len, seq_len;
+	bool *seq;
+
+	pl_hex_len = strlen(pl_hex);
+	seq_len = MAX(1 + input_file_size / MSBC_CODE_SIZE, pl_hex_len * 4);
+	seq = (bool *)calloc(seq_len, sizeof(*seq));
+
+	for (i = 0; i < seq_len; i++) {
+		/* If sequence is longer then the provided pl_hex, leave the
+		 * rest to all zeros. */
+		if (i > pl_hex_len * 4)
+			break;
+		if (i % 8 == 0) {
+			memcpy(tmp, pl_hex + i / 4, 2);
+			tmp[2] = '\0';
+			val = strtol(tmp, NULL, 16);
+		}
+		seq[i] = val & 1U;
+		val >>= 1;
+	}
+	printf("pl_hex string maps to %ld ms, total sequence size %f ms\n",
+	       strlen(pl_hex) * 30, seq_len * 7.5f);
+	return seq;
+}
+
+void plc_experiment(const char *input_filename, bool *pl_seq, bool with_plc)
 {
 	char output_filename[255];
 	int input_fd, output_fd, rc;
-	struct stat st;
-	bool *pl_seq;
 	struct cras_audio_codec *msbc_input = cras_msbc_codec_create();
 	struct cras_audio_codec *msbc_output = cras_msbc_codec_create();
 	struct cras_msbc_plc *plc = cras_msbc_plc_create();
 	uint8_t buffer[MSBC_CODE_SIZE], packet_buffer[MSBC_PKT_FRAME_LEN];
 	size_t encoded, decoded;
-	unsigned pk_count, pl_count, count = 0;
+	unsigned count = 0;
 
 	input_fd = open(input_filename, O_RDONLY);
 	if (input_fd == -1) {
@@ -63,9 +98,9 @@
 	}
 
 	if (with_plc)
-		sprintf(output_filename, "output_%2.2f_plc.raw", pl_percent);
+		sprintf(output_filename, "output_with_plc.raw");
 	else
-		sprintf(output_filename, "output_%2.2f_zero.raw", pl_percent);
+		sprintf(output_filename, "output_with_zero.raw");
 
 	output_fd = open(output_filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
 	if (output_fd == -1) {
@@ -74,11 +109,6 @@
 		return;
 	}
 
-	fstat(input_fd, &st);
-	pk_count = st.st_size / MSBC_CODE_SIZE;
-	pl_count = pk_count * (pl_percent / 100.0);
-	pl_seq = generate_pl_seq(pk_count, pl_count);
-
 	while (1) {
 		rc = read(input_fd, buffer, MSBC_CODE_SIZE);
 		if (rc < 0) {
@@ -117,19 +147,77 @@
 	}
 }
 
+static void show_usage()
+{
+	printf("This test only supports reading/writing raw audio with format:\n"
+	       "\t16000 sample rate, mono channel, S16_LE\n");
+	printf("--help - Print this usage.\n");
+	printf("--input_file - path to an audio file.\n");
+	printf("--pattern - Hex string representing consecutive packets'"
+	       "status.\n");
+	printf("--random - Percentage of packet loss.\n");
+}
+
 int main(int argc, char **argv)
 {
-	if (argc != 3) {
-		printf("Usage: cras_plc_test input.raw pl_percentage\n"
-		       "This test only supports reading/writing files with "
-		       "format:\n"
-		       "- raw pcm\n"
-		       "- 16000 sample rate\n"
-		       "- mono channel\n"
-		       "- S16_LE sample format\n");
+	int fd;
+	struct stat st;
+	float pl_percent;
+	int pl_percent_set = 0;
+	int option_character;
+	int option_index = 0;
+	const char *input_file = NULL;
+	const char *pl_hex = NULL;
+	bool *pl_seq = NULL;
+	static struct option long_options[] = {
+		{ "help", no_argument, NULL, 'h' },
+		{ "input", required_argument, NULL, 'i' },
+		{ "pattern", required_argument, NULL, 'p' },
+		{ "random", required_argument, NULL, 'r' },
+		{ NULL, 0, NULL, 0 },
+	};
+
+	while (true) {
+		option_character = getopt_long(argc, argv, "i:r:p:h",
+					       long_options, &option_index);
+		if (option_character == -1)
+			break;
+		switch (option_character) {
+		case 'h':
+			show_usage();
+			break;
+		case 'i':
+			input_file = optarg;
+			break;
+		case 'p':
+			pl_hex = optarg;
+			break;
+		case 'r':
+			pl_percent = atof(optarg);
+			pl_percent_set = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if ((!pl_percent_set && !pl_hex) || !input_file) {
+		show_usage();
 		return 1;
 	}
 
-	plc_experiment(argv[1], atof(argv[2]), true);
-	plc_experiment(argv[1], atof(argv[2]), false);
+	fd = open(input_file, O_RDONLY);
+	if (fd == -1) {
+		fprintf(stderr, "Cannout open input file %s\n", input_file);
+		return 1;
+	}
+	fstat(fd, &st);
+	close(fd);
+	if (pl_percent_set)
+		pl_seq = generate_pl_seq(st.st_size, pl_percent);
+	else if (pl_hex)
+		pl_seq = parse_pl_hex(st.st_size, pl_hex);
+
+	plc_experiment(input_file, pl_seq, true);
+	plc_experiment(input_file, pl_seq, false);
 }
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index df713ca..cd155e8 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -555,8 +555,11 @@
 	si->runtime_nsec = time_since.tv_nsec;
 }
 
-/* Handle a message sent to the playback thread */
-static int handle_playback_thread_message(struct audio_thread *thread)
+/* Handle a message sent from main thread to the audio thread.
+ * Returns:
+ *    Error code when reading or sending message fails.
+ */
+static int handle_audio_thread_message(struct audio_thread *thread)
 {
 	uint8_t buf[256];
 	struct audio_thread_msg *msg = (struct audio_thread_msg *)buf;
@@ -711,7 +714,7 @@
 	err = audio_thread_send_response(thread, ret);
 	if (err < 0)
 		return err;
-	return ret;
+	return 0;
 }
 
 /* Returns the number of active streams plus the number of active devices. */
@@ -912,7 +915,7 @@
 			continue;
 
 		if (thread->pollfds[0].revents & POLLIN) {
-			rc = handle_playback_thread_message(thread);
+			rc = handle_audio_thread_message(thread);
 			if (rc < 0)
 				syslog(LOG_ERR, "handle message %d", rc);
 		}
diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c
index d04d626..14d3fa0 100644
--- a/cras/src/server/config/cras_board_config.c
+++ b/cras/src/server/config/cras_board_config.c
@@ -13,12 +13,14 @@
 static const int32_t AEC_SUPPORTED_DEFAULT = 0;
 static const int32_t AEC_GROUP_ID_DEFAULT = -1;
 static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1;
+static const int32_t BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_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"
 #define AEC_GROUP_ID_INI_KEY "processing:group_id"
 #define BLUETOOTH_WBS_ENABLED_INI_KEY "bluetooth:wbs_enabled"
+#define BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY "bluetooth:deprioritize_wbs_mic"
 #define UCM_IGNORE_SUFFIX_KEY "ucm:ignore_suffix"
 
 void cras_board_config_get(const char *config_path,
@@ -34,6 +36,8 @@
 	board_config->aec_group_id = AEC_GROUP_ID_DEFAULT;
 	board_config->ucm_ignore_suffix = NULL;
 	board_config->bt_wbs_enabled = BLUETOOTH_WBS_ENABLED_INI_DEFAULT;
+	board_config->deprioritize_bt_wbs_mic =
+		BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT;
 	if (config_path == NULL)
 		return;
 
@@ -66,6 +70,12 @@
 	board_config->bt_wbs_enabled = iniparser_getint(
 		ini, ini_key, BLUETOOTH_WBS_ENABLED_INI_DEFAULT);
 
+	snprintf(ini_key, MAX_INI_KEY_LENGTH,
+		 BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY);
+	ini_key[MAX_INI_KEY_LENGTH] = 0;
+	board_config->deprioritize_bt_wbs_mic = iniparser_getint(
+		ini, ini_key, BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT);
+
 	snprintf(ini_key, MAX_INI_KEY_LENGTH, UCM_IGNORE_SUFFIX_KEY);
 	ini_key[MAX_INI_KEY_LENGTH] = 0;
 	ptr = iniparser_getstring(ini, ini_key, "");
diff --git a/cras/src/server/config/cras_board_config.h b/cras/src/server/config/cras_board_config.h
index ed80bec..2ecde26 100644
--- a/cras/src/server/config/cras_board_config.h
+++ b/cras/src/server/config/cras_board_config.h
@@ -13,6 +13,7 @@
 	int32_t aec_supported;
 	int32_t aec_group_id;
 	int32_t bt_wbs_enabled;
+	int32_t deprioritize_bt_wbs_mic;
 	char *ucm_ignore_suffix;
 };
 
diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c
index 683fa31..6c43475 100644
--- a/cras/src/server/cras_a2dp_iodev.c
+++ b/cras/src/server/cras_a2dp_iodev.c
@@ -664,7 +664,7 @@
 	iodev->start = start;
 	iodev->frames_to_play_in_sleep = frames_to_play_in_sleep;
 
-	/* Create a dummy ionode */
+	/* Create an empty ionode */
 	node = (struct cras_ionode *)calloc(1, sizeof(*node));
 	node->dev = iodev;
 	strcpy(node->name, iodev->info.name);
@@ -684,6 +684,8 @@
 	iodev->info.max_supported_channels =
 		(a2dp.channel_mode == SBC_CHANNEL_MODE_MONO) ? 1 : 2;
 
+	ewma_power_disable(&iodev->ewma);
+
 	return iodev;
 error:
 	if (a2dpio) {
diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c
index 4f40249..6cdc165 100644
--- a/cras/src/server/cras_alsa_helpers.c
+++ b/cras/src/server/cras_alsa_helpers.c
@@ -556,7 +556,7 @@
 	return 0;
 }
 
-int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
+int cras_alsa_set_swparams(snd_pcm_t *handle)
 {
 	int err;
 	snd_pcm_sw_params_t *swparams;
@@ -593,50 +593,7 @@
 		return err;
 	}
 
-	if (*enable_htimestamp) {
-		/* Use MONOTONIC_RAW time-stamps. */
-		err = snd_pcm_sw_params_set_tstamp_type(
-			handle, swparams, SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW);
-		if (err < 0) {
-			syslog(LOG_ERR, "set_tstamp_type: %s\n",
-			       snd_strerror(err));
-			return err;
-		}
-		err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams,
-							SND_PCM_TSTAMP_ENABLE);
-		if (err < 0) {
-			syslog(LOG_ERR, "set_tstamp_mode: %s\n",
-			       snd_strerror(err));
-			return err;
-		}
-	}
-
-	/* This hack is required because ALSA-LIB does not provide any way to
-	 * detect whether MONOTONIC_RAW timestamps are supported by the kernel.
-	 * In ALSA-LIB, the code checks the hardware protocol version. */
 	err = snd_pcm_sw_params(handle, swparams);
-	if (err == -EINVAL && *enable_htimestamp) {
-		*enable_htimestamp = 0;
-		syslog(LOG_WARNING,
-		       "MONOTONIC_RAW timestamps are not supported.");
-
-		err = snd_pcm_sw_params_set_tstamp_type(
-			handle, swparams, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY);
-		if (err < 0) {
-			syslog(LOG_ERR, "set_tstamp_type: %s\n",
-			       snd_strerror(err));
-			return err;
-		}
-		err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams,
-							SND_PCM_TSTAMP_NONE);
-		if (err < 0) {
-			syslog(LOG_ERR, "set_tstamp_mode: %s\n",
-			       snd_strerror(err));
-			return err;
-		}
-
-		err = snd_pcm_sw_params(handle, swparams);
-	}
 
 	if (err < 0) {
 		syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err));
diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h
index 3897674..01a42ae 100644
--- a/cras/src/server/cras_alsa_helpers.h
+++ b/cras/src/server/cras_alsa_helpers.h
@@ -135,13 +135,10 @@
 /* Sets up the swparams to alsa.
  * Args:
  *    handle - The open PCM to configure.
- *    enable_htimestamp - If non-zero, enable and configure hardware timestamps,
- *                        updated to reflect whether MONOTONIC RAW htimestamps
- *                        are supported by the kernel implementation.
  * Returns:
  *    0 on success, negative error on failure.
  */
-int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp);
+int cras_alsa_set_swparams(snd_pcm_t *handle);
 
 /* Get the number of used frames in the alsa buffer.
  *
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index 792305b..da4ef63 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -113,7 +113,6 @@
  * is_first - true if this is the first iodev on the card.
  * fully_specified - true if this device and it's nodes were fully specified.
  *     That is, don't automatically create nodes for it.
- * enable_htimestamp - True when the device's htimestamp is used.
  * handle - Handle to the opened ALSA device.
  * num_severe_underruns - Number of times we have run out of data badly.
                           Unlike num_underruns which records for the duration
@@ -148,7 +147,6 @@
 	enum CRAS_ALSA_CARD_TYPE card_type;
 	int is_first;
 	int fully_specified;
-	int enable_htimestamp;
 	snd_pcm_t *handle;
 	unsigned int num_severe_underruns;
 	snd_pcm_stream_t alsa_stream;
@@ -331,8 +329,7 @@
 			aio->num_severe_underruns++;
 		return rc;
 	}
-	if (!aio->enable_htimestamp)
-		clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
+	clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
 	if (iodev->direction == CRAS_STREAM_INPUT)
 		return (int)frames;
 
@@ -374,7 +371,7 @@
 	return 0;
 }
 
-static int dummy_hotword_cb(void *arg, int revents)
+static int empty_hotword_cb(void *arg, int revents)
 {
 	/* Only need this once. */
 	struct alsa_io *aio = (struct alsa_io *)arg;
@@ -449,7 +446,7 @@
 		return rc;
 
 	/* Configure software params. */
-	rc = cras_alsa_set_swparams(aio->handle, &aio->enable_htimestamp);
+	rc = cras_alsa_set_swparams(aio->handle);
 	if (rc < 0)
 		return rc;
 
@@ -490,7 +487,7 @@
 
 		if (aio->poll_fd >= 0)
 			audio_thread_add_events_callback(
-				aio->poll_fd, dummy_hotword_cb, aio, POLLIN);
+				aio->poll_fd, empty_hotword_cb, aio, POLLIN);
 	}
 
 	/* Capture starts right away, playback will wait for samples. */
@@ -1549,7 +1546,7 @@
 	 * For HDMI plug event cases, update max supported channels according
 	 * to the current active node.
 	 */
-	if (node->base.type == CRAS_NODE_TYPE_HDMI)
+	if (node->base.type == CRAS_NODE_TYPE_HDMI && plugged)
 		update_max_supported_channels(&aio->base);
 }
 
@@ -2134,8 +2131,6 @@
 		rc = ucm_get_min_buffer_level(ucm, &level);
 		if (!rc && direction == CRAS_STREAM_OUTPUT)
 			iodev->min_buffer_level = level;
-
-		aio->enable_htimestamp = ucm_get_enable_htimestamp_flag(ucm);
 	}
 
 	set_iodev_name(iodev, card_name, dev_name, card_index, device_index,
@@ -2355,8 +2350,11 @@
 
 	set_default_hotword_model(iodev);
 
+	node = iodev->active_node;
+
 	/* Record max supported channels into cras_iodev_info. */
-	update_max_supported_channels(iodev);
+	if (node && node->plugged)
+		update_max_supported_channels(iodev);
 }
 
 void alsa_iodev_destroy(struct cras_iodev *iodev)
diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c
index 52a227e..6d4d7bf 100644
--- a/cras/src/server/cras_alsa_jack.c
+++ b/cras/src/server/cras_alsa_jack.c
@@ -734,6 +734,16 @@
 	return snd_hctl_find_elem(hctl, elem_id);
 }
 
+/* For non-gpio jack, check if it's of type hdmi/dp by
+ * matching jack name. */
+static int is_jack_hdmi_dp(const char *jack_name)
+{
+	// TODO(hychao): Use the information provided in UCM instead of
+	// name matching.
+	static const char *hdmi_dp = "HDMI";
+	return !!strstr(jack_name, hdmi_dp);
+}
+
 /* Find GPIO jacks for this jack_list.
  * Args:
  *    jack_list - Jack list to add to.
@@ -772,8 +782,9 @@
 	if (result_jack) {
 		*result_jack = data.result_jack;
 
-		/* Find ELD control for HDMI/DP gpio jack. */
-		if (*result_jack)
+		/* Find ELD control only for HDMI/DP gpio jack. */
+		if (*result_jack &&
+		    is_jack_hdmi_dp((*result_jack)->gpio.device_name))
 			(*result_jack)->eld_control =
 				find_eld_control_by_dev_index(
 					jack_list->hctl,
@@ -833,14 +844,6 @@
 	return (unsigned int)device_index;
 }
 
-/* For non-gpio jack, check if it's of type hdmi/dp by
- * matching jack name. */
-static int is_jack_hdmi_dp(const char *jack_name)
-{
-	static const char *hdmi_dp = "HDMI/DP";
-	return strncmp(jack_name, hdmi_dp, strlen(hdmi_dp)) == 0;
-}
-
 /* Checks if the given control name is in the supplied list of possible jack
  * control base names. */
 static int is_jack_control_in_list(const char *const *list,
diff --git a/cras/src/server/cras_alsa_plugin_io.c b/cras/src/server/cras_alsa_plugin_io.c
index 9c557a4..32c1ae1 100644
--- a/cras/src/server/cras_alsa_plugin_io.c
+++ b/cras/src/server/cras_alsa_plugin_io.c
@@ -24,9 +24,9 @@
 #define PLUGIN_KEY_PCM "pcm"
 #define PLUGIN_KEY_CARD "card"
 
-#define DUMMY_USB_VID 0x00
-#define DUMMY_USB_PID 0x00
-#define DUMMY_USB_SERIAL_NUMBER "serial-number-not-used"
+#define NULL_USB_VID 0x00
+#define NULL_USB_PID 0x00
+#define NULL_USB_SERIAL_NUMBER "serial-number-not-used"
 
 struct hctl_poll_fd {
 	int fd;
@@ -159,12 +159,11 @@
 			       "section %s mixer_name %s",
 			       section->name, section->mixer_name);
 	}
-	plugin->iodev =
-		alsa_iodev_create(0, card_name, 0, pcm_name, "", "",
-				  ALSA_CARD_TYPE_USB, 1, /* is first */
-				  plugin->mixer, NULL, plugin->ucm,
-				  plugin->hctl, direction, DUMMY_USB_VID,
-				  DUMMY_USB_PID, DUMMY_USB_SERIAL_NUMBER);
+	plugin->iodev = alsa_iodev_create(0, card_name, 0, pcm_name, "", "",
+					  ALSA_CARD_TYPE_USB, 1, /* is first */
+					  plugin->mixer, NULL, plugin->ucm,
+					  plugin->hctl, direction, NULL_USB_VID,
+					  NULL_USB_PID, NULL_USB_SERIAL_NUMBER);
 
 	DL_FOREACH (ucm_sections, section) {
 		if (section->dir != plugin->iodev->direction)
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index 3782cb2..9759a50 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -57,7 +57,6 @@
 static const char hotword_model_prefix[] = "Hotword Model";
 static const char fully_specified_ucm_var[] = "FullySpecifiedUCM";
 static const char main_volume_names[] = "MainVolumeNames";
-static const char enable_htimestamp_var[] = "EnableHtimestamp";
 
 /* Use case verbs corresponding to CRAS_STREAM_TYPE. */
 static const char *use_case_verbs[] = {
@@ -1121,15 +1120,3 @@
 		return 0;
 	return value;
 }
-
-unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr)
-{
-	char *flag;
-	int ret = 0;
-	flag = ucm_get_flag(mgr, enable_htimestamp_var);
-	if (!flag)
-		return 0;
-	ret = !strcmp(flag, "1");
-	free(flag);
-	return ret;
-}
diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h
index 48dc655..99a8b44 100644
--- a/cras/src/server/cras_alsa_ucm.h
+++ b/cras/src/server/cras_alsa_ucm.h
@@ -472,12 +472,4 @@
  */
 unsigned int ucm_get_optimize_no_stream_flag(struct cras_use_case_mgr *mgr);
 
-/* Retrieve the flag that enables use of htimestamp.
- * Args:
- *    mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
- * Returns:
- *    1 if the flag is enabled. 0 otherwise.
- */
-unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr);
-
 #endif /* _CRAS_ALSA_UCM_H */
diff --git a/cras/src/server/cras_apm_list.h b/cras/src/server/cras_apm_list.h
index b9a7fe2..7a36cea 100644
--- a/cras/src/server/cras_apm_list.h
+++ b/cras/src/server/cras_apm_list.h
@@ -162,7 +162,7 @@
 
 /*
  * 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
+ * cras_apm_list functions as empty. 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)
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 0607ac8..70c8747 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -61,9 +61,7 @@
 static const unsigned int SCO_SUSPEND_DELAY_MS = 5000;
 
 static const unsigned int CRAS_SUPPORTED_PROFILES =
-	CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
-	CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE |
-	CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY;
+	CRAS_BT_DEVICE_PROFILE_A2DP_SINK | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE;
 
 /* Object to represent a general bluetooth device, and used to
  * associate with some CRAS modules if it supports audio.
@@ -79,6 +77,8 @@
  *    connected - If this devices is connected.
  *    connected_profiles - OR'ed all connected audio profiles.
  *    profiles - OR'ed by all audio profiles this device supports.
+ *    hidden_profiles - OR'ed by all audio profiles this device actually
+ *        supports but is not scanned by BlueZ.
  *    bt_iodevs - The pointer to the cras_iodevs of this device.
  *    active_profile - The flag to indicate the active audio profile this
  *        device is currently using.
@@ -102,8 +102,9 @@
 	int paired;
 	int trusted;
 	int connected;
-	enum cras_bt_device_profile connected_profiles;
-	enum cras_bt_device_profile profiles;
+	unsigned int connected_profiles;
+	unsigned int profiles;
+	unsigned int hidden_profiles;
 	struct cras_iodev *bt_iodevs[CRAS_NUM_DIRECTIONS];
 	unsigned int active_profile;
 	int use_hardware_volume;
@@ -512,14 +513,18 @@
 	 * behavior on qualification test software. */
 	if (!cras_bt_device_supports_profile(
 		    device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE)) {
-		cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+		unsigned int profiles =
+			device->profiles | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE;
+		cras_bt_device_set_supported_profiles(device, profiles);
+		device->hidden_profiles |= CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE;
 		bt_device_conn_watch_cb(NULL, (void *)device);
 	}
 
 	return 0;
 }
 
-int cras_bt_device_get_active_profile(const struct cras_bt_device *device)
+unsigned int
+cras_bt_device_get_active_profile(const struct cras_bt_device *device)
 {
 	return device->active_profile;
 }
@@ -569,6 +574,19 @@
 	}
 }
 
+static void cras_bt_device_log_profiles(const struct cras_bt_device *device,
+					unsigned int profiles)
+{
+	unsigned int profile;
+
+	while (profiles) {
+		/* Get the LSB of profiles */
+		profile = profiles & -profiles;
+		cras_bt_device_log_profile(device, profile);
+		profiles ^= profile;
+	}
+}
+
 static int
 cras_bt_device_is_profile_connected(const struct cras_bt_device *device,
 				    enum cras_bt_device_profile profile)
@@ -708,7 +726,7 @@
 void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device,
 					   enum cras_bt_device_profile profile)
 {
-	device->connected_profiles &= !profile;
+	device->connected_profiles &= ~profile;
 
 	/* Do nothing if device already disconnected. */
 	if (!device->connected)
@@ -723,37 +741,33 @@
 				   UNEXPECTED_PROFILE_DROP);
 }
 
-/*
- * Check if the uuid is of a new audio profile that isn't listed
- * as supported by device.
+/* Refresh the list of known supported profiles.
  * Args:
- *    device - The BT device holding supported profiles bitmap.
- *    uuid - UUID string from the device properties notified by BlueZ.
+ *    device - The BT device holding scanned profiles bitmap.
+ *    profiles - The OR'ed profiles the device claims to support as is notified
+ *               by BlueZ.
  * Returns:
- *    True if uuid is a new audio profiles not already supported by device.
+ *    The OR'ed profiles that are both supported by Cras and isn't previously
+ *    supported by the device.
  */
-int cras_bt_device_add_supported_profiles(struct cras_bt_device *device,
-					  const char *uuid)
+int cras_bt_device_set_supported_profiles(struct cras_bt_device *device,
+					  unsigned int profiles)
 {
-	enum cras_bt_device_profile profile =
-		cras_bt_device_profile_from_uuid(uuid);
-
-	if (profile == 0)
+	/* Do nothing if no new profiles. */
+	if ((device->profiles & profiles) == profiles)
 		return 0;
 
-	/* Do nothing if this profile is not new. */
-	if (device->profiles & profile)
-		return 0;
+	unsigned int new_profiles = profiles & ~device->profiles;
 
 	/* Log this event as we might need to re-intialize the BT audio nodes
 	 * if new audio profile is reported for already connected device. */
-	if (device->connected && (profile & CRAS_SUPPORTED_PROFILES))
+	if (device->connected && (new_profiles & CRAS_SUPPORTED_PROFILES))
 		BTLOG(btlog, BT_NEW_AUDIO_PROFILE_AFTER_CONNECT,
-		      device->profiles, profile);
-	device->profiles |= profile;
-	cras_bt_device_log_profile(device, profile);
+		      device->profiles, new_profiles);
+	cras_bt_device_log_profiles(device, new_profiles);
+	device->profiles = profiles | device->hidden_profiles;
 
-	return (profile & CRAS_SUPPORTED_PROFILES);
+	return (new_profiles & CRAS_SUPPORTED_PROFILES);
 }
 
 void cras_bt_device_update_properties(struct cras_bt_device *device,
@@ -821,6 +835,7 @@
 				  "as") == 0 &&
 			   strcmp(key, "UUIDs") == 0) {
 			DBusMessageIter uuid_array_iter;
+			unsigned int profiles = 0;
 
 			dbus_message_iter_recurse(&variant_iter,
 						  &uuid_array_iter);
@@ -830,22 +845,21 @@
 
 				dbus_message_iter_get_basic(&uuid_array_iter,
 							    &uuid);
-
-				/*
-				 * If updated properties includes new audio
-				 * profile, and device is connected, we need
-				 * to start connection watcher. This is needed
-				 * because on some bluetooth device, supported
-				 * profiles do not present when device
-				 * interface is added and they are updated
-				 * later.
-				 */
-				if (cras_bt_device_add_supported_profiles(
-					    device, uuid))
-					watch_needed = device->connected;
+				profiles |=
+					cras_bt_device_profile_from_uuid(uuid);
 
 				dbus_message_iter_next(&uuid_array_iter);
 			}
+
+			/* If updated properties includes new audio profile and
+			 * device is connected, we need to start connection
+			 * watcher. This is needed because on some bluetooth
+			 * devices, supported profiles do not present when
+			 * device interface is added and they are updated later.
+			 */
+			if (cras_bt_device_set_supported_profiles(device,
+								  profiles))
+				watch_needed = device->connected;
 		}
 
 		dbus_message_iter_next(properties_array_iter);
@@ -876,7 +890,7 @@
 		} else if (strcmp(key, "Connected") == 0) {
 			device->connected = 0;
 		} else if (strcmp(key, "UUIDs") == 0) {
-			device->profiles = 0;
+			device->profiles = device->hidden_profiles;
 		}
 
 		dbus_message_iter_next(invalidated_array_iter);
diff --git a/cras/src/server/cras_bt_device.h b/cras/src/server/cras_bt_device.h
index 3800927..4202bc9 100644
--- a/cras/src/server/cras_bt_device.h
+++ b/cras/src/server/cras_bt_device.h
@@ -63,8 +63,8 @@
 				      DBusMessageIter *invalidated_array_iter);
 
 /* Updates the supported profiles on dev. Expose for unit test. */
-int cras_bt_device_add_supported_profiles(struct cras_bt_device *device,
-					  const char *uuid);
+int cras_bt_device_set_supported_profiles(struct cras_bt_device *device,
+					  unsigned int profiles);
 
 /* Checks if profile is claimed supported by the device. */
 int cras_bt_device_supports_profile(const struct cras_bt_device *device,
@@ -133,7 +133,8 @@
 			     struct cras_iodev *iodev);
 
 /* Gets the active profile of the bt device. */
-int cras_bt_device_get_active_profile(const struct cras_bt_device *device);
+unsigned int
+cras_bt_device_get_active_profile(const struct cras_bt_device *device);
 
 /* Sets the active profile of the bt device. */
 void cras_bt_device_set_active_profile(struct cras_bt_device *device,
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 3cffe14..9f5c2f7 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -518,7 +518,7 @@
 		iodev->set_volume = set_bt_volume;
 	}
 
-	/* Create the dummy node so it's the only node exposed to UI, and
+	/* Create the fake node 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));
 	if (!active)
diff --git a/cras/src/server/cras_control_rclient.c b/cras/src/server/cras_control_rclient.c
index 3906a23..cd0c4d3 100644
--- a/cras/src/server/cras_control_rclient.c
+++ b/cras/src/server/cras_control_rclient.c
@@ -15,6 +15,7 @@
 #include "cras_dsp.h"
 #include "cras_iodev.h"
 #include "cras_iodev_list.h"
+#include "cras_hfp_ag_profile.h"
 #include "cras_main_thread_log.h"
 #include "cras_messages.h"
 #include "cras_observer.h"
@@ -298,15 +299,11 @@
 	switch (msg->id) {
 	case CRAS_SERVER_CONNECT_STREAM: {
 		int client_shm_fd = num_fds > 1 ? fds[1] : -1;
-		struct cras_connect_message cmsg;
 		if (MSG_LEN_VALID(msg, struct cras_connect_message)) {
 			rclient_handle_client_stream_connect(
 				client,
 				(const struct cras_connect_message *)msg, fd,
 				client_shm_fd);
-		} else if (!convert_connect_message_old(msg, &cmsg)) {
-			rclient_handle_client_stream_connect(client, &cmsg, fd,
-							     client_shm_fd);
 		} else {
 			return -EINVAL;
 		}
@@ -422,10 +419,15 @@
 		state = cras_system_state_get_no_lock();
 #ifdef CRAS_DBUS
 		memcpy(&state->bt_debug_info.bt_log, btlog,
-		       sizeof(struct cras_bt_debug_info));
+		       sizeof(struct cras_bt_event_log));
+		memcpy(&state->bt_debug_info.wbs_logger,
+		       cras_hfp_ag_get_wbs_logger(),
+		       sizeof(struct packet_status_logger));
 #else
 		memset(&state->bt_debug_info.bt_log, 0,
 		       sizeof(struct cras_bt_debug_info));
+		memset(&state->bt_debug_info.wbs_logger, 0,
+		       sizeof(struct packet_status_logger));
 #endif
 
 		cras_fill_client_audio_debug_info_ready(&msg);
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 628ec22..3479c3c 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -75,6 +75,9 @@
 	"    <method name=\"GetSystemAecGroupId\">\n"                           \
 	"      <arg name=\"group_id\" type=\"i\" direction=\"out\"/>\n"         \
 	"    </method>\n"                                                       \
+	"    <method name=\"GetDeprioritizeBtWbsMic\">\n"                       \
+	"      <arg name=\"deprioritized\" type=\"b\" direction=\"out\"/>\n"    \
+	"    </method>\n"                                                       \
 	"    <method name=\"SetActiveOutputNode\">\n"                           \
 	"      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"           \
 	"    </method>\n"                                                       \
@@ -105,6 +108,9 @@
 	"    <method name=\"GetNumberOfActiveInputStreams\">\n"                 \
 	"      <arg name=\"num\" type=\"i\" direction=\"out\"/>\n"              \
 	"    </method>\n"                                                       \
+	"    <method name=\"GetNumberOfInputStreamsWithPermission\">\n"         \
+	"      <arg name=\"num\" type=\"a{sv}\" direction=\"out\"/>\n"          \
+	"    </method>\n"                                                       \
 	"    <method name=\"SetGlobalOutputChannelRemix\">\n"                   \
 	"      <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n"      \
 	"      <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n"      \
@@ -671,6 +677,27 @@
 }
 
 static DBusHandlerResult
+handle_get_deprioritize_bt_wbs_mic(DBusConnection *conn, DBusMessage *message,
+				   void *arg)
+{
+	DBusMessage *reply;
+	dbus_uint32_t serial = 0;
+	dbus_bool_t deprioritized;
+
+	reply = dbus_message_new_method_return(message);
+
+	deprioritized = cras_system_get_deprioritize_bt_wbs_mic();
+	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &deprioritized,
+				 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, void *arg,
 		       enum CRAS_STREAM_DIRECTION direction)
 {
@@ -784,6 +811,65 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static bool append_num_input_streams_with_permission(
+	DBusMessage *message, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
+{
+	DBusMessageIter array;
+	DBusMessageIter dict;
+	unsigned type;
+
+	dbus_message_iter_init_append(message, &array);
+	for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) {
+		const char *client_type_str = cras_client_type_str(type);
+		if (!is_utf8_string(client_type_str)) {
+			syslog(LOG_ERR,
+			       "Non-utf8 clinet_type_str '%s' cannot be sent "
+			       "via dbus",
+			       client_type_str);
+			client_type_str = "";
+		}
+
+		if (!dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
+						      "{sv}", &dict))
+			return false;
+		if (!append_key_value(&dict, "ClientType", DBUS_TYPE_STRING,
+				      DBUS_TYPE_STRING_AS_STRING,
+				      &client_type_str))
+			return false;
+		if (!append_key_value(&dict, "NumStreamsWithPermission",
+				      DBUS_TYPE_UINT32,
+				      DBUS_TYPE_UINT32_AS_STRING,
+				      &num_input_streams[type]))
+			return false;
+		if (!dbus_message_iter_close_container(&array, &dict))
+			return false;
+	}
+	return true;
+}
+
+static DBusHandlerResult
+handle_get_num_input_streams_with_permission(DBusConnection *conn,
+					     DBusMessage *message, void *arg)
+{
+	DBusMessage *reply;
+	dbus_uint32_t serial = 0;
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {};
+
+	reply = dbus_message_new_method_return(message);
+
+	cras_system_state_get_input_streams_with_permission(num_input_streams);
+	if (!append_num_input_streams_with_permission(reply, num_input_streams))
+		goto error;
+
+	dbus_connection_send(conn, reply, &serial);
+	dbus_message_unref(reply);
+	return DBUS_HANDLER_RESULT_HANDLED;
+
+error:
+	dbus_message_unref(reply);
+	return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
 static DBusHandlerResult
 handle_set_global_output_channel_remix(DBusConnection *conn,
 				       DBusMessage *message, void *arg)
@@ -1052,6 +1138,9 @@
 					       "GetSystemAecGroupId")) {
 		return handle_get_system_aec_group_id(conn, message, arg);
 	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
+					       "GetDeprioritizeBtWbsMic")) {
+		return handle_get_deprioritize_bt_wbs_mic(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);
@@ -1088,6 +1177,11 @@
 								  arg);
 	} else if (dbus_message_is_method_call(
 			   message, CRAS_CONTROL_INTERFACE,
+			   "GetNumberOfInputStreamsWithPermission")) {
+		return handle_get_num_input_streams_with_permission(
+			conn, message, arg);
+	} else if (dbus_message_is_method_call(
+			   message, CRAS_CONTROL_INTERFACE,
 			   "GetNumberOfActiveOutputStreams")) {
 		return handle_get_num_active_streams_use_output_hw(
 			conn, message, arg);
@@ -1315,6 +1409,25 @@
 	dbus_message_unref(msg);
 }
 
+static void signal_num_input_streams_with_permission_changed(
+	void *context, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
+{
+	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
+	dbus_uint32_t serial = 0;
+	DBusMessage *msg;
+
+	msg = create_dbus_message("NumberOfInputStreamsWithPermissionChanged");
+	if (!msg)
+		return;
+
+	if (!append_num_input_streams_with_permission(msg, num_input_streams))
+		goto error;
+
+	dbus_connection_send(control->conn, msg, &serial);
+error:
+	dbus_message_unref(msg);
+}
+
 static void signal_hotword_triggered(void *context, int64_t tv_sec,
 				     int64_t tv_nsec)
 {
@@ -1399,6 +1512,8 @@
 	observer_ops.capture_mute_changed = signal_capture_mute;
 	observer_ops.num_active_streams_changed =
 		signal_num_active_streams_changed;
+	observer_ops.num_input_streams_with_permission_changed =
+		signal_num_input_streams_with_permission_changed;
 	observer_ops.nodes_changed = signal_nodes_changed;
 	observer_ops.active_node_changed = signal_active_node_changed;
 	observer_ops.input_node_gain_changed = signal_node_capture_gain_changed;
diff --git a/cras/src/server/cras_dsp.c b/cras/src/server/cras_dsp.c
index d0b2626..9c4cc7b 100644
--- a/cras/src/server/cras_dsp.c
+++ b/cras/src/server/cras_dsp.c
@@ -208,15 +208,15 @@
 	cmd_load_pipeline(ctx, global_ini);
 }
 
-void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx,
-				  unsigned int num_channels)
+void cras_dsp_load_mock_pipeline(struct cras_dsp_context *ctx,
+				 unsigned int num_channels)
 {
-	struct ini *dummy_ini;
-	dummy_ini = create_dummy_ini(ctx->purpose, num_channels);
-	if (dummy_ini == NULL)
-		syslog(LOG_ERR, "Failed to create dummy ini");
+	struct ini *mock_ini;
+	mock_ini = create_mock_ini(ctx->purpose, num_channels);
+	if (mock_ini == NULL)
+		syslog(LOG_ERR, "Failed to create mock ini");
 	else
-		cmd_load_pipeline(ctx, dummy_ini);
+		cmd_load_pipeline(ctx, mock_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 9a72f42..366e2e6 100644
--- a/cras/src/server/cras_dsp.h
+++ b/cras/src/server/cras_dsp.h
@@ -54,11 +54,11 @@
  * 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
+/* Loads a mock 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);
+void cras_dsp_load_mock_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. */
diff --git a/cras/src/server/cras_dsp_ini.c b/cras/src/server/cras_dsp_ini.c
index 0b844d1..a331acf 100644
--- a/cras/src/server/cras_dsp_ini.c
+++ b/cras/src/server/cras_dsp_ini.c
@@ -11,7 +11,7 @@
 
 #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 20 /* Max number of channels to create dummy ini */
+#define MAX_MOCK_INI_CH 20 /* Max number of channels to create mock ini */
 
 /* Format of the ini file (See dsp.ini.sample for an example).
 
@@ -305,22 +305,21 @@
 	return 0;
 }
 
-struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels)
+struct ini *create_mock_ini(const char *purpose, unsigned int num_channels)
 {
-	static char dummy_flow_names[MAX_DUMMY_INI_CH][9] = {
-		"{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}",
-		"{tmp:4}", "{tmp:5}", "{tmp:6}", "{tmp:7}",
-		"{tmp:8}", "{tmp:9}", "{tmp:10}", "{tmp:11}",
-		"{tmp:12}", "{tmp:13}", "{tmp:14}", "{tmp:15}",
-		"{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}",
+	static char mock_flow_names[MAX_MOCK_INI_CH][9] = {
+		"{tmp:0}",  "{tmp:1}",	"{tmp:2}",  "{tmp:3}",	"{tmp:4}",
+		"{tmp:5}",  "{tmp:6}",	"{tmp:7}",  "{tmp:8}",	"{tmp:9}",
+		"{tmp:10}", "{tmp:11}", "{tmp:12}", "{tmp:13}", "{tmp:14}",
+		"{tmp:15}", "{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}",
 	};
 	struct ini *ini;
 	struct plugin *source, *sink;
-	int tmp_flow_ids[MAX_DUMMY_INI_CH];
+	int tmp_flow_ids[MAX_MOCK_INI_CH];
 	int i;
 
-	if (num_channels > MAX_DUMMY_INI_CH) {
-		syslog(LOG_ERR, "Unable to create %u channels of dummy ini",
+	if (num_channels > MAX_MOCK_INI_CH) {
+		syslog(LOG_ERR, "Unable to create %u channels of mock ini",
 		       num_channels);
 		return NULL;
 	}
@@ -332,7 +331,7 @@
 	}
 
 	for (i = 0; i < num_channels; i++)
-		tmp_flow_ids[i] = add_new_flow(ini, dummy_flow_names[i]);
+		tmp_flow_ids[i] = add_new_flow(ini, mock_flow_names[i]);
 
 	source = ARRAY_APPEND_ZERO(&ini->plugins);
 	source->title = "source";
diff --git a/cras/src/server/cras_dsp_ini.h b/cras/src/server/cras_dsp_ini.h
index 51deefd..c839d4b 100644
--- a/cras/src/server/cras_dsp_ini.h
+++ b/cras/src/server/cras_dsp_ini.h
@@ -71,7 +71,7 @@
 };
 
 /*
- * Creates a dummy ini structure equivalent to:
+ * Creates a mock ini structure equivalent to:
  *
  * [src]
  * out0={tmp:0}
@@ -86,7 +86,7 @@
  * 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);
+struct ini *create_mock_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);
diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c
index 76eab6c..3471c75 100644
--- a/cras/src/server/cras_empty_iodev.c
+++ b/cras/src/server/cras_empty_iodev.c
@@ -199,7 +199,7 @@
 	iodev->update_active_node = update_active_node;
 	iodev->no_stream = cras_iodev_default_no_stream_playback;
 
-	/* Create a dummy ionode */
+	/* Create an empty ionode */
 	node = (struct cras_ionode *)calloc(1, sizeof(*node));
 	node->dev = iodev;
 	node->type = node_type;
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index 478452d..509db1e 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -9,6 +9,7 @@
 #include <syslog.h>
 #include <endian.h>
 #include <limits.h>
+#include <math.h>
 
 #include "cras_fmt_conv.h"
 #include "cras_fmt_conv_ops.h"
@@ -58,21 +59,35 @@
 	return 1;
 }
 
-static void normalize_buf(float *buf, size_t size)
+/*
+ * Calculates the normalize_factor abs_sum(ci) from given coefficients.
+ * Since sum(ci / abs_sum(ci)) <= 1, this could prevent sample overflow while
+ * upmixing or downmixing.
+ */
+static float normalize_factor(float *buf, size_t n)
 {
 	int i;
-	float squre_sum = 0.0;
-	for (i = 0; i < size; i++)
-		squre_sum += buf[i] * buf[i];
+	float abs_sum = 0.0;
+	for (i = 0; i < n; i++)
+		abs_sum += fabs(buf[i]);
 
-	if (squre_sum == 0.0)
-		return;
-
-	for (i = 0; i < size; i++)
-		buf[i] /= squre_sum;
+	return 1.0 / abs_sum;
 }
 
-/* Populates the down mix matrix by rules:
+/*
+ * Normalize all channels with the same factor to maintain
+ * the energy ratio between original channels.
+ */
+static void normalize(float **mtx, size_t m, size_t n, float factor)
+{
+	int i, j;
+	for (i = 0; i < m; i++)
+		for (j = 0; j < n; j++)
+			mtx[i][j] *= factor;
+}
+
+/*
+ * Populates the down mix matrix by rules:
  * 1. Front/side left(right) channel will mix to left(right) of
  *    full scale.
  * 2. Center and LFE will be split equally to left and right.
@@ -106,9 +121,46 @@
 		mtx[STEREO_L][layout[CRAS_CH_LFE]] = 0.707;
 		mtx[STEREO_R][layout[CRAS_CH_LFE]] = 0.707;
 	}
+	normalize(mtx, 2, 6, normalize_factor(mtx[STEREO_L], 6));
+}
 
-	normalize_buf(mtx[STEREO_L], 6);
-	normalize_buf(mtx[STEREO_R], 6);
+/* Populates the down mix matrix by rules:
+ * 1. Front left(right) channel will mix to the front left(right) of
+ *    full scale.
+ * 2. Rear and side left(right) channel will mix to the rear left(right) of
+ *    full scale.
+ * 3. Center will be split equally to the front left and right.
+ * 4. LFE will be split equally to the other channels.
+ */
+static void surround51_to_quad_downmix_mtx(float **mtx,
+					   int8_t layout[CRAS_CH_MAX])
+{
+	if (layout[CRAS_CH_FL] != -1 && layout[CRAS_CH_FR] != -1) {
+		mtx[CRAS_CH_FL][layout[CRAS_CH_FL]] = 1.0;
+		mtx[CRAS_CH_FR][layout[CRAS_CH_FR]] = 1.0;
+	}
+	if (layout[CRAS_CH_RL] != -1 && layout[CRAS_CH_RR] != -1) {
+		mtx[CRAS_CH_RL][layout[CRAS_CH_RL]] = 1.0;
+		mtx[CRAS_CH_RR][layout[CRAS_CH_RR]] = 1.0;
+	}
+	if (layout[CRAS_CH_SL] != -1 && layout[CRAS_CH_SR] != -1) {
+		mtx[CRAS_CH_RL][layout[CRAS_CH_SL]] = 1.0;
+		mtx[CRAS_CH_RR][layout[CRAS_CH_SR]] = 1.0;
+	}
+	if (layout[CRAS_CH_FC] != -1) {
+		/* Split 1/2 power to the front L/R */
+		mtx[CRAS_CH_FL][layout[CRAS_CH_FC]] = 0.707;
+		mtx[CRAS_CH_FR][layout[CRAS_CH_FC]] = 0.707;
+	}
+	if (layout[CRAS_CH_LFE] != -1) {
+		/* Split 1/4 power to the other channel */
+		mtx[CRAS_CH_FL][layout[CRAS_CH_LFE]] = 0.5;
+		mtx[CRAS_CH_FR][layout[CRAS_CH_LFE]] = 0.5;
+		mtx[CRAS_CH_RL][layout[CRAS_CH_LFE]] = 0.5;
+		mtx[CRAS_CH_RR][layout[CRAS_CH_LFE]] = 0.5;
+	}
+
+	normalize(mtx, 4, 6, normalize_factor(mtx[CRAS_CH_FL], 6));
 }
 
 static int is_supported_format(const struct cras_audio_format *fmt)
@@ -170,6 +222,12 @@
 	return s16_51_to_stereo(in, in_frames, out);
 }
 
+static size_t _51_to_quad(struct cras_fmt_conv *conv, const uint8_t *in,
+			  size_t in_frames, uint8_t *out)
+{
+	return s16_51_to_quad(in, in_frames, out);
+}
+
 static size_t stereo_to_quad(struct cras_fmt_conv *conv, const uint8_t *in,
 			     size_t in_frames, uint8_t *out)
 {
@@ -340,7 +398,8 @@
 			conv->channel_converter = quad_to_stereo;
 		} else if (in->num_channels == 2 && out->num_channels == 6) {
 			conv->channel_converter = stereo_to_51;
-		} else if (in->num_channels == 6 && out->num_channels == 2) {
+		} else if (in->num_channels == 6 &&
+			   (out->num_channels == 2 || out->num_channels == 4)) {
 			int in_channel_layout_set = 0;
 
 			/* Checks if channel_layout is set in the incoming format */
@@ -361,11 +420,20 @@
 					return NULL;
 				}
 				conv->channel_converter = convert_channels;
-				surround51_to_stereo_downmix_mtx(
-					conv->ch_conv_mtx,
-					conv->in_fmt.channel_layout);
+				if (out->num_channels == 4) {
+					surround51_to_quad_downmix_mtx(
+						conv->ch_conv_mtx,
+						conv->in_fmt.channel_layout);
+				} else {
+					surround51_to_stereo_downmix_mtx(
+						conv->ch_conv_mtx,
+						conv->in_fmt.channel_layout);
+				}
 			} else {
-				conv->channel_converter = _51_to_stereo;
+				if (out->num_channels == 4)
+					conv->channel_converter = _51_to_quad;
+				else
+					conv->channel_converter = _51_to_stereo;
 			}
 		} else if (in->num_channels <= 8 && out->num_channels <= 8) {
 			// For average channel counts mix from all to all.
diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c
index 87358af..a306d21 100644
--- a/cras/src/server/cras_fmt_conv_ops.c
+++ b/cras/src/server/cras_fmt_conv_ops.c
@@ -235,20 +235,69 @@
 	int16_t *out = (int16_t *)_out;
 	static const unsigned int left_idx = 0;
 	static const unsigned int right_idx = 1;
-	/* static const unsigned int left_surround_idx = 2; */
-	/* static const unsigned int right_surround_idx = 3; */
-	static const unsigned int center_idx = 4;
-	/* static const unsigned int lfe_idx = 5; */
+	static const unsigned int center_idx = 2;
+	/* static const unsigned int lfe_idx = 3; */
+	/* static const unsigned int left_surround_idx = 4; */
+	/* static const unsigned int right_surround_idx = 5; */
+
 	size_t i;
-
+	int16_t half_center;
+	/* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|)
+	 * to prevent mixing overflow.
+	 */
+	const float normalized_factor = 0.585;
 	for (i = 0; i < in_frames; i++) {
-		unsigned int half_center;
-
-		half_center = in[6 * i + center_idx] / 2;
+		half_center =
+			in[6 * i + center_idx] * 0.707 * normalized_factor;
 		out[2 * i + left_idx] =
-			s16_add_and_clip(in[6 * i + left_idx], half_center);
+			in[6 * i + left_idx] * normalized_factor + half_center;
 		out[2 * i + right_idx] =
-			s16_add_and_clip(in[6 * i + right_idx], half_center);
+			in[6 * i + right_idx] * normalized_factor + half_center;
+	}
+	return in_frames;
+}
+
+/*
+ * Channel converter: 5.1 surround to quad (front L/R, rear L/R).
+ *
+ * The out buffer can have room for just quad samples. This convert function
+ * is used as the default behavior when channel layout is not set from the
+ * client side.
+ */
+size_t s16_51_to_quad(const uint8_t *_in, size_t in_frames, uint8_t *_out)
+{
+	const int16_t *in = (const int16_t *)_in;
+	int16_t *out = (int16_t *)_out;
+	static const unsigned int l_quad = 0;
+	static const unsigned int r_quad = 1;
+	static const unsigned int rl_quad = 2;
+	static const unsigned int rr_quad = 3;
+
+	static const unsigned int l_51 = 0;
+	static const unsigned int r_51 = 1;
+	static const unsigned int center_51 = 2;
+	static const unsigned int lfe_51 = 3;
+	static const unsigned int rl_51 = 4;
+	static const unsigned int rr_51 = 5;
+
+	/* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|)
+	 * to prevent overflow. */
+	const float normalized_factor = 0.453;
+	size_t i;
+	for (i = 0; i < in_frames; i++) {
+		int16_t half_center;
+		int16_t lfe;
+
+		half_center = in[6 * i + center_51] * 0.707 * normalized_factor;
+		lfe = in[6 * i + lfe_51] * 0.5 * normalized_factor;
+		out[4 * i + l_quad] = normalized_factor * in[6 * i + l_51] +
+				      half_center + lfe;
+		out[4 * i + r_quad] = normalized_factor * in[6 * i + r_51] +
+				      half_center + lfe;
+		out[4 * i + rl_quad] =
+			normalized_factor * in[6 * i + rl_51] + lfe;
+		out[4 * i + rr_quad] =
+			normalized_factor * in[6 * i + rr_51] + lfe;
 	}
 	return in_frames;
 }
diff --git a/cras/src/server/cras_fmt_conv_ops.h b/cras/src/server/cras_fmt_conv_ops.h
index 8042ad5..a1a5748 100644
--- a/cras/src/server/cras_fmt_conv_ops.h
+++ b/cras/src/server/cras_fmt_conv_ops.h
@@ -51,6 +51,11 @@
 size_t s16_51_to_stereo(const uint8_t *in, size_t in_frames, uint8_t *out);
 
 /*
+ * Channel converter: 5.1 surround to quad.
+ */
+size_t s16_51_to_quad(const uint8_t *in, size_t in_frames, uint8_t *out);
+
+/*
  * Channel converter: stereo to quad (front L/R, rear L/R).
  */
 size_t s16_stereo_to_quad(size_t front_left, size_t front_right,
diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c
index 8cf4a43..9d59d40 100644
--- a/cras/src/server/cras_hfp_ag_profile.c
+++ b/cras/src/server/cras_hfp_ag_profile.c
@@ -22,6 +22,7 @@
 #include "cras_iodev_list.h"
 #include "cras_observer.h"
 #include "utlist.h"
+#include "packet_status_logger.h"
 
 #define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway"
 #define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG"
@@ -103,6 +104,7 @@
 };
 
 static struct audio_gateway *connected_ags;
+static struct packet_status_logger wbs_logger;
 
 static int need_go_sco_pcm(struct cras_bt_device *device)
 {
@@ -411,6 +413,7 @@
 						 ag->slc_handle, ag->profile);
 	} else {
 		ag->info = hfp_info_create();
+		hfp_info_set_wbs_logger(ag->info, &wbs_logger);
 		ag->idev =
 			hfp_iodev_create(CRAS_STREAM_INPUT, ag->device,
 					 ag->slc_handle, ag->profile, ag->info);
@@ -453,6 +456,11 @@
 	return NULL;
 }
 
+struct packet_status_logger *cras_hfp_ag_get_wbs_logger()
+{
+	return &wbs_logger;
+}
+
 void cras_hfp_ag_resend_device_battery_level()
 {
 	struct audio_gateway *ag;
diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h
index fb58efb..50d27e0 100644
--- a/cras/src/server/cras_hfp_ag_profile.h
+++ b/cras/src/server/cras_hfp_ag_profile.h
@@ -53,6 +53,9 @@
 /* Gets the SLC handle for given cras_bt_device. */
 struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device);
 
+/* Gets the logger for WBS packet status. */
+struct packet_status_logger *cras_hfp_ag_get_wbs_logger();
+
 /* Iterate all possible AGs (theoratically only one) and signal its battery
  * level */
 void cras_hfp_ag_resend_device_battery_level();
diff --git a/cras/src/server/cras_hfp_alsa_iodev.c b/cras/src/server/cras_hfp_alsa_iodev.c
index 532b6c4..b80a88c 100644
--- a/cras/src/server/cras_hfp_alsa_iodev.c
+++ b/cras/src/server/cras_hfp_alsa_iodev.c
@@ -309,6 +309,10 @@
 	/* Record max supported channels into cras_iodev_info. */
 	iodev->info.max_supported_channels = 1;
 
+	/* Specifically disable EWMA calculation on this and the child iodev. */
+	ewma_power_disable(&iodev->ewma);
+	ewma_power_disable(&aio->ewma);
+
 	return iodev;
 }
 
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index 550de58..fc407b2 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -19,6 +19,7 @@
 #include "cras_sbc_codec.h"
 #include "cras_server_metrics.h"
 #include "utlist.h"
+#include "packet_status_logger.h"
 
 /* The max buffer size. Note that the actual used size must set to multiple
  * of SCO packet size, and the packet size does not necessarily be equal to
@@ -93,6 +94,7 @@
  *     read_align_cb - Callback used to align mSBC frame reading with read buf.
  *     msbc_read_current_corrupted - Flag to mark if the current mSBC frame
  *         read is corrupted.
+ *     wbs_logger - The logger for packet status in WBS.
  */
 struct hfp_info {
 	int fd;
@@ -119,6 +121,7 @@
 	size_t read_rp;
 	int (*read_align_cb)(uint8_t *buf);
 	bool msbc_read_current_corrupted;
+	struct packet_status_logger *wbs_logger;
 };
 
 int hfp_info_add_iodev(struct hfp_info *info,
@@ -403,6 +406,20 @@
 	return NULL;
 }
 
+/* Log value 0 when packet is received. */
+static void log_wbs_packet_received(struct hfp_info *info)
+{
+	if (info->wbs_logger)
+		packet_status_logger_update(info->wbs_logger, 0);
+}
+
+/* Log value 1 when packet is lost. */
+static void log_wbs_packet_lost(struct hfp_info *info)
+{
+	if (info->wbs_logger)
+		packet_status_logger_update(info->wbs_logger, 1);
+}
+
 /*
  * Handle the case when mSBC frame is considered lost.
  * Args:
@@ -419,6 +436,8 @@
 	info->msbc_num_in_frames++;
 	info->msbc_num_lost_frames++;
 
+	log_wbs_packet_lost(info);
+
 	in_bytes = buf_write_pointer_size(info->capture_buf, &pcm_avail);
 	if (pcm_avail < MSBC_CODE_SIZE)
 		return 0;
@@ -580,6 +599,7 @@
 		pcm_read += err;
 	} else {
 		/* Good mSBC frame decoded. */
+		log_wbs_packet_received(info);
 		buf_increment_write(info->capture_buf, pcm_decoded);
 		info->msbc_num_in_frames++;
 		cras_msbc_plc_handle_good_frames(info->msbc_plc, capture_buf,
@@ -723,6 +743,12 @@
 	return NULL;
 }
 
+void hfp_info_set_wbs_logger(struct hfp_info *info,
+			     struct packet_status_logger *wbs_logger)
+{
+	info->wbs_logger = wbs_logger;
+}
+
 int hfp_info_running(struct hfp_info *info)
 {
 	return info->started;
@@ -760,6 +786,8 @@
 		info->msbc_read = cras_msbc_codec_create();
 		info->msbc_write = cras_msbc_codec_create();
 		info->msbc_plc = cras_msbc_plc_create();
+
+		packet_status_logger_init(info->wbs_logger);
 	} else {
 		info->write_cb = hfp_write;
 		info->read_cb = hfp_read;
diff --git a/cras/src/server/cras_hfp_info.h b/cras/src/server/cras_hfp_info.h
index 96110e2..3472aea 100644
--- a/cras/src/server/cras_hfp_info.h
+++ b/cras/src/server/cras_hfp_info.h
@@ -30,6 +30,10 @@
 /* Destroys given hfp_info instance. */
 void hfp_info_destroy(struct hfp_info *info);
 
+/* Sets the wbs_logger to hfp_info instance. */
+void hfp_info_set_wbs_logger(struct hfp_info *info,
+			     struct packet_status_logger *wbs_logger);
+
 /* Checks if given hfp_info is running. */
 int hfp_info_running(struct hfp_info *info);
 
diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c
index bf609cf..7cce373 100644
--- a/cras/src/server/cras_hfp_iodev.c
+++ b/cras/src/server/cras_hfp_iodev.c
@@ -350,6 +350,8 @@
 	/* Record max supported channels into cras_iodev_info. */
 	iodev->info.max_supported_channels = 1;
 
+	ewma_power_disable(&iodev->ewma);
+
 	return iodev;
 
 error:
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index 1cf003a..e4f0127 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -708,7 +708,14 @@
 			 * check the  Bluetooth SIG Assigned Numbers web page.
 			 */
 			BTLOG(btlog, BT_HFP_HF_INDICATOR, 1, 0);
-			err = hfp_send(handle, AT_CMD("+BIND: (2)"));
+			/* "2" is for HF Battery Level that we support. We don't
+			 * support "1" but this is a workaround for Pixel Buds 2
+			 * which expects this exact combination for battery
+			 * reporting (HFP 1.7 standard) to work. This workaround
+			 * is fine since we don't enable Safety Drive with
+			 * +BIND: 1,1 (b/172680041).
+			 */
+			err = hfp_send(handle, AT_CMD("+BIND: (1,2)"));
 			if (err < 0)
 				return err;
 		}
@@ -738,6 +745,14 @@
 		 * indicator
 		 * 1 = enabled, value changes may be sent for this indicator
 		 */
+
+		/* We don't support Enhanced Driver Status, so explicitly
+		 * disable it (b/172680041).
+		 */
+		err = hfp_send(handle, AT_CMD("+BIND: 1,0"));
+		if (err < 0)
+			return err;
+
 		BTLOG(btlog, BT_HFP_HF_INDICATOR, 0, 0);
 
 		err = hfp_send(handle, AT_CMD("+BIND: 2,1"));
@@ -927,7 +942,7 @@
  *
  * An initialized service level connection is the pre-condition for all
  * call related procedures. Note that for the call related commands,
- * we are good to just respond with a dummy "OK".
+ * we are good to just respond with a meaningless "OK".
  *
  * The procedure to establish a service level connection is described below:
  *
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index f426eb5..fd1ce80 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -239,7 +239,7 @@
  *  |                        ----------------              | device from
  *  |                        | S1  Open     |              | audio_thread and
  *  |                        ----------------              | closes device
- *  | Device with dummy start       |                      |
+ *  | Device with empty start       |                      |
  *  | ops transits into             | Sample is ready      |
  *  | no stream state right         V                      |
  *  | after open.            ----------------              |
@@ -527,8 +527,8 @@
 
 	if (!pipeline) {
 		cras_iodev_alloc_dsp(iodev);
-		cras_dsp_load_dummy_pipeline(iodev->dsp_context,
-					     iodev->format->num_channels);
+		cras_dsp_load_mock_pipeline(iodev->dsp_context,
+					    iodev->format->num_channels);
 		pipeline = cras_dsp_get_pipeline(iodev->dsp_context);
 	}
 	/* dsp_context mutex locked. Now it's safe to modify dsp
@@ -953,6 +953,8 @@
 	iodev->highest_hw_level = 0;
 	iodev->input_dsp_offset = 0;
 
+	ewma_power_init(&iodev->ewma, iodev->format->frame_rate);
+
 	if (iodev->direction == CRAS_STREAM_OUTPUT) {
 		/* If device supports start ops, device can be in open state.
 		 * Otherwise, device starts running right after opening. */
@@ -1097,6 +1099,9 @@
 					    loopback->cb_data);
 	}
 
+	ewma_power_calculate(&iodev->ewma, (int16_t *)frames,
+			     iodev->format->num_channels, nframes);
+
 	rc = apply_dsp(iodev, frames, nframes);
 	if (rc)
 		return rc;
@@ -1200,6 +1205,11 @@
 			       *frames - iodev->input_dsp_offset);
 		if (rc)
 			return rc;
+		ewma_power_calculate_area(
+			&iodev->ewma,
+			(int16_t *)(hw_buffer +
+				    iodev->input_dsp_offset * frame_bytes),
+			data->area, *frames - iodev->input_dsp_offset);
 	}
 
 	if (cras_system_get_capture_mute())
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index 553cb80..db16a0f 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -19,6 +19,7 @@
 #include "cras_dsp.h"
 #include "cras_iodev_info.h"
 #include "cras_messages.h"
+#include "ewma_power.h"
 
 struct buffer_share;
 struct cras_fmt_conv;
@@ -237,7 +238,7 @@
  *              stream side processing.
  * initial_ramp_request - The value indicates which type of ramp the device
  * should perform when some samples are ready for playback.
- *
+ * ewma - The ewma instance to calculate iodev volume.
  */
 struct cras_iodev {
 	void (*set_volume)(struct cras_iodev *iodev);
@@ -312,6 +313,7 @@
 	unsigned int input_dsp_offset;
 	unsigned int initial_ramp_request;
 	struct input_data *input_data;
+	struct ewma_power ewma;
 	struct cras_iodev *prev, *next;
 };
 
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index bba793d..ada2971 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -361,7 +361,8 @@
 	if (dev->echo_reference_dev == NULL)
 		return;
 
-	server_stream_create(stream_list, dev->echo_reference_dev->info.idx);
+	server_stream_create(stream_list, dev->echo_reference_dev->info.idx,
+			     dev->format);
 }
 
 /*
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index 0947313..cf3ba4a 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -331,7 +331,7 @@
 	if (iodev == NULL)
 		return NULL;
 
-	/* Create a dummy ionode */
+	/* Create an empty ionode */
 	node = (struct cras_ionode *)calloc(1, sizeof(*node));
 	node->dev = iodev;
 	node->type = node_type;
diff --git a/cras/src/server/cras_observer.c b/cras/src/server/cras_observer.c
index a73bccc..0f17dc9 100644
--- a/cras/src/server/cras_observer.c
+++ b/cras/src/server/cras_observer.c
@@ -7,6 +7,7 @@
 
 #include "cras_alert.h"
 #include "cras_iodev_list.h"
+#include "cras_types.h"
 #include "utlist.h"
 
 struct cras_observer_client {
@@ -35,6 +36,7 @@
 	struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS];
 	struct cras_alert *non_empty_audio_state_changed;
 	struct cras_alert *bt_battery_changed;
+	struct cras_alert *num_input_streams_with_permission;
 };
 
 struct cras_observer_server {
@@ -76,6 +78,10 @@
 	uint32_t num_active_streams;
 };
 
+struct cras_observer_alert_data_input_streams {
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE];
+};
+
 struct cras_observer_alert_data_hotword_triggered {
 	int64_t tv_sec;
 	int64_t tv_nsec;
@@ -253,6 +259,20 @@
 	}
 }
 
+static void num_input_streams_with_permission_alert(void *arg, void *data)
+{
+	struct cras_observer_client *client;
+	struct cras_observer_alert_data_input_streams *input_streams_data =
+		(struct cras_observer_alert_data_input_streams *)data;
+
+	DL_FOREACH (g_observer->clients, client) {
+		if (client->ops.num_input_streams_with_permission_changed)
+			client->ops.num_input_streams_with_permission_changed(
+				client->context,
+				input_streams_data->num_input_streams);
+	}
+}
+
 static void hotword_triggered_alert(void *arg, void *data)
 {
 	struct cras_observer_client *client;
@@ -353,6 +373,7 @@
 	CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0);
 	CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0);
 	CRAS_OBSERVER_SET_ALERT(bt_battery_changed, NULL, 0);
+	CRAS_OBSERVER_SET_ALERT(num_input_streams_with_permission, NULL, 0);
 
 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
 					       CRAS_STREAM_OUTPUT);
@@ -386,6 +407,8 @@
 	cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed);
 	cras_alert_destroy(g_observer->alerts.bt_battery_changed);
 	cras_alert_destroy(
+		g_observer->alerts.num_input_streams_with_permission);
+	cras_alert_destroy(
 		g_observer->alerts.num_active_streams[CRAS_STREAM_OUTPUT]);
 	cras_alert_destroy(
 		g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]);
@@ -562,6 +585,21 @@
 	cras_alert_pending_data(alert, &data, sizeof(data));
 }
 
+void cras_observer_notify_input_streams_with_permission(
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
+{
+	struct cras_observer_alert_data_input_streams data;
+	struct cras_alert *alert;
+
+	memcpy(&data.num_input_streams, num_input_streams,
+	       sizeof(*num_input_streams) * CRAS_NUM_CLIENT_TYPE);
+	alert = g_observer->alerts.num_input_streams_with_permission;
+	if (!alert)
+		return;
+
+	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;
diff --git a/cras/src/server/cras_observer.h b/cras/src/server/cras_observer.h
index 1da5eea..2dd013b 100644
--- a/cras/src/server/cras_observer.h
+++ b/cras/src/server/cras_observer.h
@@ -92,6 +92,10 @@
 void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
 					     uint32_t num_active_streams);
 
+/* Notify observers of the number of input streams with permission. */
+void cras_observer_notify_input_streams_with_permission(
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]);
+
 /* Notify observers of the timestamp when hotword triggered. */
 void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec);
 
diff --git a/cras/src/server/cras_rclient.c b/cras/src/server/cras_rclient.c
index 22fbb05..d2646e7 100644
--- a/cras/src/server/cras_rclient.c
+++ b/cras/src/server/cras_rclient.c
@@ -59,9 +59,16 @@
 	return client->ops->send_message_to_client(client, msg, fds, num_fds);
 }
 
+static void cras_rclient_set_client_type(struct cras_rclient *client,
+					 enum CRAS_CLIENT_TYPE client_type)
+{
+	client->client_type = client_type;
+}
+
 struct cras_rclient *cras_rclient_create(int fd, size_t id,
 					 enum CRAS_CONNECTION_TYPE conn_type)
 {
+	struct cras_rclient *client;
 	if (!cras_validate_connection_type(conn_type))
 		goto error;
 
@@ -76,6 +83,14 @@
 		return cras_playback_rclient_create(fd, id);
 	case CRAS_VMS_UNIFIED:
 		return cras_unified_rclient_create(fd, id);
+	case CRAS_PLUGIN_PLAYBACK:
+		client = cras_playback_rclient_create(fd, id);
+		cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN);
+		return client;
+	case CRAS_PLUGIN_UNIFIED:
+		client = cras_unified_rclient_create(fd, id);
+		cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN);
+		return client;
 	default:
 		goto error;
 	}
diff --git a/cras/src/server/cras_rclient.h b/cras/src/server/cras_rclient.h
index 6cffb7d..3a3988c 100644
--- a/cras/src/server/cras_rclient.h
+++ b/cras/src/server/cras_rclient.h
@@ -20,6 +20,9 @@
  *  fd - Connection for client communication.
  *  ops - cras_rclient_ops for the cras_rclient.
  *  supported_directions - Bit mask for supported stream directions.
+ *  client_type - Client type of this rclient. If this is set to value other
+ *                than CRAS_CLIENT_TYPE_UNKNOWN, rclient will overwrite incoming
+ *                messages' client type.
  */
 struct cras_rclient {
 	struct cras_observer_client *observer;
@@ -27,6 +30,7 @@
 	int fd;
 	const struct cras_rclient_ops *ops;
 	int supported_directions;
+	enum CRAS_CLIENT_TYPE client_type;
 };
 
 /* Operations for cras_rclient.
diff --git a/cras/src/server/cras_rclient_util.c b/cras/src/server/cras_rclient_util.c
index da99128..def645e 100644
--- a/cras/src/server/cras_rclient_util.c
+++ b/cras/src/server/cras_rclient_util.c
@@ -80,6 +80,13 @@
 		       msg->direction, client->id);
 		return -EINVAL;
 	}
+
+	if (!cras_validate_client_type(msg->client_type)) {
+		syslog(LOG_ERR,
+		       "stream_connect: invalid stream client_type: %x for "
+		       "client: %zx.\n",
+		       msg->client_type, client->id);
+	}
 	return 0;
 }
 
@@ -155,6 +162,9 @@
 
 	stream_config = cras_rstream_config_init_with_message(
 		client, msg, &aud_fd, &client_shm_fd, &remote_fmt);
+	/* Overwrite client_type if client->client_type is set. */
+	if (client->client_type != CRAS_CLIENT_TYPE_UNKNOWN)
+		stream_config.client_type = client->client_type;
 	rc = stream_list_add(cras_iodev_list_get_stream_list(), &stream_config,
 			     &stream);
 	if (rc)
@@ -279,15 +289,11 @@
 	switch (msg->id) {
 	case CRAS_SERVER_CONNECT_STREAM: {
 		int client_shm_fd = num_fds > 1 ? fds[1] : -1;
-		struct cras_connect_message cmsg;
 		if (MSG_LEN_VALID(msg, struct cras_connect_message)) {
 			rclient_handle_client_stream_connect(
 				client,
 				(const struct cras_connect_message *)msg, fd,
 				client_shm_fd);
-		} else if (!convert_connect_message_old(msg, &cmsg)) {
-			rclient_handle_client_stream_connect(client, &cmsg, fd,
-							     client_shm_fd);
 		} else {
 			return -EINVAL;
 		}
diff --git a/cras/src/server/cras_rclient_util.h b/cras/src/server/cras_rclient_util.h
index e00f87c..089c2ec 100644
--- a/cras/src/server/cras_rclient_util.h
+++ b/cras/src/server/cras_rclient_util.h
@@ -122,33 +122,4 @@
 				       const struct cras_server_message *msg,
 				       int *fds, unsigned int num_fds);
 
-/*
- * Converts an old version of connect message to the correct
- * cras_connect_message. Returns zero on success, negative on failure.
- * Note that this is special check only for libcras transition in
- * clients, from CRAS_PROTO_VER 5 to 7.
- * TODO(fletcherw): clean up the function once transition is done.
- */
-static inline int
-convert_connect_message_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 -EINVAL;
-
-	old = (struct cras_connect_message_old *)msg;
-	if (old->proto_version != 5 || CRAS_PROTO_VER != 7)
-		return -EINVAL;
-
-	// We want to copy everything except the client_shm_size field, since
-	// that overlaps slightly with the now larger client_shm_size.
-	memcpy(cmsg, old, sizeof(*old) - sizeof(old->client_shm_size));
-	cmsg->client_shm_size = old->client_shm_size;
-	cmsg->buffer_offsets[0] = 0;
-	cmsg->buffer_offsets[1] = 0;
-	return 0;
-}
-
 #endif /* CRAS_RCLIENT_UTIL_H_ */
diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c
index b5a6490..94adcea 100644
--- a/cras/src/server/cras_rstream.c
+++ b/cras/src/server/cras_rstream.c
@@ -292,6 +292,7 @@
 	stream->num_missed_cb = 0;
 	stream->is_pinned = (config->dev_idx != NO_DEVICE);
 	stream->pinned_dev_idx = config->dev_idx;
+	ewma_power_init(&stream->ewma, stream->format.frame_rate);
 
 	rc = setup_shm_area(stream, config);
 	if (rc < 0) {
@@ -312,7 +313,7 @@
 	       config->stream_id, config->buffer_frames, config->cb_threshold);
 	*stream_out = stream;
 
-	cras_system_state_stream_added(stream->direction);
+	cras_system_state_stream_added(stream->direction, stream->client_type);
 
 	clock_gettime(CLOCK_MONOTONIC_RAW, &stream->start_ts);
 
@@ -324,7 +325,8 @@
 void cras_rstream_destroy(struct cras_rstream *stream)
 {
 	cras_server_metrics_stream_destroy(stream);
-	cras_system_state_stream_removed(stream->direction);
+	cras_system_state_stream_removed(stream->direction,
+					 stream->client_type);
 	close(stream->fd);
 	cras_audio_shm_destroy(stream->shm);
 	cras_audio_area_destroy(stream->audio_area);
@@ -472,9 +474,16 @@
 
 void cras_rstream_update_output_read_pointer(struct cras_rstream *rstream)
 {
+	size_t nfr = 0;
+	uint8_t *src;
 	unsigned int nwritten =
 		buffer_share_get_new_write_point(rstream->buf_state);
 
+	/* Retrieve the read pointer |src| start from which to calculate
+	 * the EWMA power. */
+	src = cras_shm_get_readable_frames(rstream->shm, 0, &nfr);
+	ewma_power_calculate(&rstream->ewma, (int16_t *)src,
+			     rstream->format.num_channels, nwritten);
 	cras_shm_buffer_read(rstream->shm, nwritten);
 }
 
diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h
index 059f2bb..3bf7df0 100644
--- a/cras/src/server/cras_rstream.h
+++ b/cras/src/server/cras_rstream.h
@@ -14,6 +14,7 @@
 #include "cras_shm.h"
 #include "cras_types.h"
 #include "cras_rstream_config.h"
+#include "ewma_power.h"
 
 struct cras_connect_message;
 struct cras_rclient;
@@ -55,6 +56,7 @@
  *    first_missed_cb_ts - The time when the first missed callback happens.
  *    buf_state - State of the buffer from all devices for this stream.
  *    apm_list - List of audio processing module instances.
+ *    ewma - The ewma instance to calculate stream volume.
  *    num_attached_devs - Number of iodevs this stream has attached to.
  *    num_missed_cb - Number of callback schedules have been missed.
  *    queued_frames - Cached value of the number of queued frames in shm.
@@ -85,6 +87,7 @@
 	struct timespec first_missed_cb_ts;
 	struct buffer_share *buf_state;
 	struct cras_apm_list *apm_list;
+	struct ewma_power ewma;
 	int num_attached_devs;
 	int num_missed_cb;
 	int queued_frames;
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index 91556d8..ef4011b 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -268,7 +268,7 @@
 		return "NoDevice";
 	case CRAS_METRICS_DEVICE_ALSA_LOOPBACK:
 		return "AlsaLoopback";
-	/* Other dummy devices. */
+	/* Other fallback devices. */
 	case CRAS_METRICS_DEVICE_NORMAL_FALLBACK:
 		return "NormalFallback";
 	case CRAS_METRICS_DEVICE_ABNORMAL_FALLBACK:
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index a583419..331ecb1 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -156,6 +156,8 @@
 	exp_state->aec_supported = board_config.aec_supported;
 	exp_state->aec_group_id = board_config.aec_group_id;
 	exp_state->bt_wbs_enabled = board_config.bt_wbs_enabled;
+	exp_state->deprioritize_bt_wbs_mic =
+		board_config.deprioritize_bt_wbs_mic;
 
 	if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) {
 		syslog(LOG_ERR, "Fatal: system state mutex init");
@@ -382,6 +384,11 @@
 	return !!state.exp_state->bt_wbs_enabled;
 }
 
+bool cras_system_get_deprioritize_bt_wbs_mic()
+{
+	return !!state.exp_state->deprioritize_bt_wbs_mic;
+}
+
 void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled)
 {
 	state.bt_fix_a2dp_packet_size = enabled;
@@ -511,7 +518,8 @@
 		state.fd_rm(fd, state.select_data);
 }
 
-void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction)
+void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction,
+				    enum CRAS_CLIENT_TYPE client_type)
 {
 	struct cras_server_state *s;
 
@@ -521,13 +529,19 @@
 
 	s->num_active_streams[direction]++;
 	s->num_streams_attached++;
+	if (direction == CRAS_STREAM_INPUT) {
+		s->num_input_streams_with_permission[client_type]++;
+		cras_observer_notify_input_streams_with_permission(
+			s->num_input_streams_with_permission);
+	}
 
 	cras_system_state_update_complete();
 	cras_observer_notify_num_active_streams(
 		direction, s->num_active_streams[direction]);
 }
 
-void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction)
+void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction,
+				      enum CRAS_CLIENT_TYPE client_type)
 {
 	struct cras_server_state *s;
 	unsigned i, sum;
@@ -545,6 +559,11 @@
 		cras_clock_gettime(CLOCK_MONOTONIC_RAW,
 				   &s->last_active_stream_time);
 	s->num_active_streams[direction]--;
+	if (direction == CRAS_STREAM_INPUT) {
+		s->num_input_streams_with_permission[client_type]--;
+		cras_observer_notify_input_streams_with_permission(
+			s->num_input_streams_with_permission);
+	}
 
 	cras_system_state_update_complete();
 	cras_observer_notify_num_active_streams(
@@ -566,6 +585,15 @@
 	return state.exp_state->num_active_streams[direction];
 }
 
+void cras_system_state_get_input_streams_with_permission(
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
+{
+	unsigned type;
+	for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type)
+		num_input_streams[type] =
+			state.exp_state->num_input_streams_with_permission[type];
+}
+
 void cras_system_state_get_last_stream_active_time(struct cras_timespec *ts)
 {
 	*ts = state.exp_state->last_active_stream_time;
diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h
index 7f92625..ff04606 100644
--- a/cras/src/server/cras_system_state.h
+++ b/cras/src/server/cras_system_state.h
@@ -122,6 +122,12 @@
 /* Gets the elable flag of bluetooth wideband speech feature. */
 bool cras_system_get_bt_wbs_enabled();
 
+/*
+ * Returns if Bluetooth WBS mic should be deprioritized for selecting
+ * as default audio input option.
+ */
+bool cras_system_get_deprioritize_bt_wbs_mic();
+
 /* Sets the flag to enable or disable Bluetooth fixed A2DP packet size. */
 void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled);
 
@@ -226,16 +232,20 @@
  * subsystem is idle.
  * Args:
  *   direction - Directions of audio streams.
+ *   client_type - CRAS_CLIENT_TYPE of the audio stream.
  */
-void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction);
+void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction,
+				    enum CRAS_CLIENT_TYPE client_type);
 
 /* Signals that an audio input or output stream has been removed from the
  * system.  This allows the count of active streams can be used to notice when
  * the audio subsystem is idle.
  * Args:
  *   direction - Directions of audio stream.
+ *   client_type - CRAS_CLIENT_TYPE of the audio stream.
  */
-void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction);
+void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction,
+				      enum CRAS_CLIENT_TYPE client_type);
 
 /* Returns the number of active playback and capture streams. */
 unsigned cras_system_state_get_active_streams();
@@ -247,6 +257,16 @@
 unsigned cras_system_state_get_active_streams_by_direction(
 	enum CRAS_STREAM_DIRECTION direction);
 
+/* Returns the number of input streams with permission per CRAS_CLIENT_TYPE.
+ *
+ * Returns:
+ *   num_input_streams - An array with length = CRAS_NUM_CLIENT_TYPE and each
+ *                        element is the number of the current input
+ *                        streams with permission in each client type.
+ */
+void cras_system_state_get_input_streams_with_permission(
+	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]);
+
 /* Fills ts with the time the last stream was removed from the system, the time
  * the stream count went to zero.
  */
diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c
index 20b25f7..42fe955 100644
--- a/cras/src/server/dev_io.c
+++ b/cras/src/server/dev_io.c
@@ -126,6 +126,19 @@
 	return 0;
 }
 
+/* The log only accepts uint32 arguments, so the float power
+ * must be written as bits and assumed to have a float when
+ * parsing the log.
+ */
+static uint32_t get_ewma_power_as_int(struct ewma_power *ewma)
+{
+	uint32_t pow_as_int = 0;
+
+	if (sizeof(uint32_t) == sizeof(float))
+		memcpy(&pow_as_int, &ewma->power, sizeof(uint32_t));
+	return pow_as_int;
+}
+
 /* Asks any stream with room for more data. Sets the time stamp for all streams.
  * Args:
  *    adev - The output device streams are attached to.
@@ -194,7 +207,8 @@
 		dev_stream_set_delay(dev_stream, delay);
 
 		ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id,
-		      cras_rstream_get_cb_threshold(rstream), delay);
+		      cras_rstream_get_cb_threshold(rstream),
+		      get_ewma_power_as_int(&rstream->ewma));
 
 		rc = dev_stream_request_playback_samples(dev_stream, &now);
 		if (rc < 0) {
@@ -550,7 +564,8 @@
 			break;
 	}
 
-	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0);
+	ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder,
+	      get_ewma_power_as_int(&idev->ewma), 0);
 
 	return 0;
 }
@@ -733,7 +748,8 @@
 		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);
+	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level,
+	      odev->min_cb_level);
 
 	/* 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
@@ -797,7 +813,7 @@
 	}
 
 	ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level, total_written,
-	      odev->min_cb_level);
+	      get_ewma_power_as_int(&odev->ewma));
 
 	return total_written;
 }
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index f0fcb71..025aedd 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -87,9 +87,12 @@
 	} else {
 		/*
 		 * For input, take into account the stream specific processing
-		 * like AEC. Use the post processing format to configure format
-		 * converter.
+		 * like AEC. APM exists only in input path, and has no dependency
+		 * to dev_stream. Starts APM in dev_stream's constructor just to
+		 * align with its life cycle, and then gets the post processing
+		 * format to configure format converter.
 		 */
+		cras_apm_list_start_apm(stream->apm_list, dev_ptr);
 		ofmt = cras_rstream_post_processing_format(stream, dev_ptr) ?:
 			       dev_fmt,
 		rc = config_format_converter(&out->conv, stream->direction,
@@ -123,9 +126,8 @@
 			    stream_fmt->frame_rate, &stream->sleep_interval_ts);
 	stream->next_cb_ts = *cb_ts;
 
-	/* Sets up the stream & dev pair and then start APM. */
+	/* Sets up the stream & dev pair. */
 	cras_rstream_dev_attach(stream, dev_id, dev_ptr);
-	cras_apm_list_start_apm(stream->apm_list, dev_ptr);
 
 	return out;
 }
diff --git a/cras/src/server/ewma_power.c b/cras/src/server/ewma_power.c
new file mode 100644
index 0000000..5270ef0
--- /dev/null
+++ b/cras/src/server/ewma_power.c
@@ -0,0 +1,81 @@
+/* Copyright 2020 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 "ewma_power.h"
+#include "math.h"
+
+/* One sample per 1ms. */
+#define EWMA_SAMPLE_RATE 1000
+
+/* Smooth factor for EWMA, 1 - expf(-1.0/(rate * 0.01))
+ * where the 0.01 corresponds to 10ms interval that is chosen and
+ * being used in Chrome for a long time.
+ * Here the |rate| is set to the down sampled EWMA_SAMPLE_RATE and
+ * whenever it changes the calculated |smooth_factor| should be updated
+ * accordingly.
+ */
+const static float smooth_factor = 0.095;
+
+void ewma_power_disable(struct ewma_power *ewma)
+{
+	ewma->enabled = 0;
+}
+
+void ewma_power_init(struct ewma_power *ewma, unsigned int rate)
+{
+	ewma->enabled = 1;
+	ewma->power_set = 0;
+	ewma->step_fr = rate / EWMA_SAMPLE_RATE;
+}
+
+void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf,
+			  unsigned int channels, unsigned int size)
+{
+	int i, ch;
+	float power, f;
+
+	if (!ewma->enabled)
+		return;
+	for (i = 0; i < size; i += ewma->step_fr * channels) {
+		power = 0.0f;
+		for (ch = 0; ch < channels; ch++) {
+			f = buf[i + ch] / 32768.0f;
+			power += f * f / channels;
+		}
+		if (!ewma->power_set) {
+			ewma->power = power;
+			ewma->power_set = 1;
+		} else {
+			ewma->power = smooth_factor * power +
+				      (1 - smooth_factor) * ewma->power;
+		}
+	}
+}
+
+void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf,
+			       struct cras_audio_area *area, unsigned int size)
+{
+	int i, ch;
+	float power, f;
+
+	if (!ewma->enabled)
+		return;
+	for (i = 0; i < size; i += ewma->step_fr * area->num_channels) {
+		power = 0.0f;
+		for (ch = 0; ch < area->num_channels; ch++) {
+			if (area->channels[ch].ch_set == 0)
+				continue;
+			f = buf[i + ch] / 32768.0f;
+			power += f * f / area->num_channels;
+		}
+		if (!ewma->power_set) {
+			ewma->power = power;
+			ewma->power_set = 1;
+		} else {
+			ewma->power = smooth_factor * power +
+				      (1 - smooth_factor) * ewma->power;
+		}
+	}
+}
diff --git a/cras/src/server/ewma_power.h b/cras/src/server/ewma_power.h
new file mode 100644
index 0000000..78d2e50
--- /dev/null
+++ b/cras/src/server/ewma_power.h
@@ -0,0 +1,65 @@
+/* Copyright 2020 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 EWMA_POWER_H_
+#define EWMA_POWER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "cras_audio_area.h"
+
+/*
+ * The exponentially weighted moving average power module used to
+ * calculate the energe level in audio stream.
+ * Members:
+ *    power_set - Flag to note if the first power value has set.
+ *    enabled - Flag to enable ewma calculation. Set to false to
+ *        make all calculations no-ops.
+ *    power - The power value.
+ *    step_fr - How many frames to sample one for EWMA calculation.
+ */
+struct ewma_power {
+	bool power_set;
+	bool enabled;
+	float power;
+	unsigned int step_fr;
+};
+
+/*
+ * Disables the ewma instance.
+ */
+void ewma_power_disable(struct ewma_power *ewma);
+
+/*
+ * Initializes the ewma_power object.
+ * Args:
+ *    ewma - The ewma_power object to initialize.
+ *    rate - The sample rate of the audio data that the ewma object
+ *        will calculate power from.
+ */
+void ewma_power_init(struct ewma_power *ewma, unsigned int rate);
+
+/*
+ * Feeds an audio buffer to ewma_power object to calculate the
+ * latest power value.
+ * Args:
+ *    ewma - The ewma_power object to calculate power.
+ *    buf - Pointer to the audio data.
+ *    channels - Number of channels of the audio data.
+ *    size - Length in frames of the audio data.
+ */
+void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf,
+			  unsigned int channels, unsigned int size);
+
+/*
+ * Feeds non-interleaved audio data to ewma_power to calculate the
+ * latest power value. This is similar to ewma_power_calculate but
+ * accepts cras_audio_area.
+ */
+void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf,
+			       struct cras_audio_area *area, unsigned int size);
+
+#endif /* EWMA_POWER_H_ */
diff --git a/cras/src/server/server_stream.c b/cras/src/server/server_stream.c
index 48dd6a3..6644c46 100644
--- a/cras/src/server/server_stream.c
+++ b/cras/src/server/server_stream.c
@@ -16,18 +16,6 @@
 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.
  */
@@ -45,7 +33,8 @@
 	stream_list_add(stream_list, stream_config, &stream);
 }
 
-void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx)
+void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx,
+			  struct cras_audio_format *format)
 {
 	int audio_fd = -1;
 	int client_shm_fd = -1;
@@ -64,7 +53,7 @@
 		CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_SERVER_STREAM,
 		CRAS_STREAM_INPUT, dev_idx,
 		/*flags=*/SERVER_ONLY,
-		/*effects=*/0, &format, server_stream_block_size,
+		/*effects=*/0, format, server_stream_block_size,
 		server_stream_block_size, &audio_fd, &client_shm_fd,
 		/*client_shm_size=*/0, buffer_offsets, stream_config);
 
diff --git a/cras/src/server/server_stream.h b/cras/src/server/server_stream.h
index 595987c..e1eb8e1 100644
--- a/cras/src/server/server_stream.h
+++ b/cras/src/server/server_stream.h
@@ -14,8 +14,8 @@
  *    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);
+void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx,
+			  struct cras_audio_format *format);
 
 /*
  * Asynchronously destroys existing server stream pinned to device of given idx.
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index 266b62a..cb7d5f3 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -201,7 +201,7 @@
 	 */
 	iodev->info.max_supported_channels = 1;
 
-	/* Create a dummy ionode */
+	/* Create an empty ionode */
 	node = (struct cras_ionode *)calloc(1, sizeof(*node));
 	node->dev = iodev;
 	node->plugged = 1;
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index f03a614..523a62e 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -44,7 +44,7 @@
 static int a2dp_write_return_val[MAX_A2DP_WRITE_CALLS];
 static unsigned int a2dp_write_index;
 static int a2dp_encode_called;
-static cras_audio_area* dummy_audio_area;
+static cras_audio_area* mock_audio_area;
 static thread_callback write_callback;
 static void* write_callback_data;
 static const char* fake_device_name = "fake device name";
@@ -79,9 +79,9 @@
 
   fake_transport = reinterpret_cast<struct cras_bt_transport*>(0x123);
 
-  if (!dummy_audio_area) {
-    dummy_audio_area = (cras_audio_area*)calloc(
-        1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
+  if (!mock_audio_area) {
+    mock_audio_area = (cras_audio_area*)calloc(
+        1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2);
   }
 
   write_callback = NULL;
@@ -108,8 +108,8 @@
   }
 
   virtual void TearDown() {
-    free(dummy_audio_area);
-    dummy_audio_area = NULL;
+    free(mock_audio_area);
+    mock_audio_area = NULL;
     free(atlog);
   }
 };
@@ -899,7 +899,7 @@
 }
 
 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
-  iodev->area = dummy_audio_area;
+  iodev->area = mock_audio_area;
 }
 
 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
@@ -917,12 +917,15 @@
 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
                                          const struct cras_audio_format* fmt,
                                          uint8_t* base_buffer) {
-  dummy_audio_area->channels[0].buf = base_buffer;
+  mock_audio_area->channels[0].buf = base_buffer;
 }
 
 struct audio_thread* cras_iodev_list_get_audio_thread() {
   return NULL;
 }
+// From ewma_power
+void ewma_power_disable(struct ewma_power* ewma) {}
+
 // From audio_thread
 struct audio_thread_event_log* atlog;
 
diff --git a/cras/src/tests/alsa_helpers_unittest.cc b/cras/src/tests/alsa_helpers_unittest.cc
index 0e8112c..32df30a 100644
--- a/cras/src/tests/alsa_helpers_unittest.cc
+++ b/cras/src/tests/alsa_helpers_unittest.cc
@@ -163,34 +163,12 @@
 }
 
 TEST(AlsaHelper, Htimestamp) {
-  snd_pcm_t* dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1);
+  snd_pcm_t* mock_handle = reinterpret_cast<snd_pcm_t*>(0x1);
   snd_pcm_uframes_t used;
   snd_pcm_uframes_t severe_underrun_frames = 480;
   struct timespec tstamp;
-  int htimestamp_enabled = 1;
   const char* dev_name = "dev_name";
 
-  // Enable htimestamp use.
-  ResetStubData();
-  EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 1);
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 1);
-  EXPECT_EQ(1, htimestamp_enabled);
-
-  // Try to enable htimestamp use: not supported.
-  ResetStubData();
-  snd_pcm_sw_params_ret_vals.push_back(-EINVAL);
-  EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 2);
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 2);
-  EXPECT_EQ(0, htimestamp_enabled);
-
-  // Disable htimestamp use.
-  ResetStubData();
-  EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 0);
-  EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 0);
-
   ResetStubData();
   tstamp.tv_sec = 0;
   tstamp.tv_nsec = 0;
@@ -198,7 +176,7 @@
   snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 10;
   snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 10000;
 
-  cras_alsa_get_avail_frames(dummy_handle, 48000, severe_underrun_frames,
+  cras_alsa_get_avail_frames(mock_handle, 48000, severe_underrun_frames,
                              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);
@@ -206,7 +184,7 @@
 }
 
 TEST(AlsaHelper, GetAvailFramesSevereUnderrun) {
-  snd_pcm_t* dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1);
+  snd_pcm_t* mock_handle = reinterpret_cast<snd_pcm_t*>(0x1);
   snd_pcm_uframes_t avail;
   snd_pcm_uframes_t severe_underrun_frames = 480;
   snd_pcm_uframes_t buffer_size = 48000;
@@ -216,7 +194,7 @@
 
   ResetStubData();
   snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames + 1;
-  rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+  rc = cras_alsa_get_avail_frames(mock_handle, buffer_size,
                                   severe_underrun_frames, dev_name, &avail,
                                   &tstamp);
   // Returns -EPIPE when severe underrun happens.
@@ -224,7 +202,7 @@
 
   ResetStubData();
   snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames;
-  rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+  rc = cras_alsa_get_avail_frames(mock_handle, buffer_size,
                                   severe_underrun_frames, dev_name, &avail,
                                   &tstamp);
   // Underrun which is not severe enough will be masked.
@@ -234,7 +212,7 @@
 
   ResetStubData();
   snd_pcm_htimestamp_avail_ret_val = buffer_size - 1;
-  rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+  rc = cras_alsa_get_avail_frames(mock_handle, buffer_size,
                                   severe_underrun_frames, dev_name, &avail,
                                   &tstamp);
   // When avail < buffer_size, there is no underrun.
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index ba650a2..b3059a2 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -137,7 +137,6 @@
 static int cras_iodev_buffer_avail_ret;
 static int cras_alsa_resume_appl_ptr_called;
 static int cras_alsa_resume_appl_ptr_ahead;
-static int ucm_get_enable_htimestamp_flag_ret;
 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;
@@ -220,7 +219,6 @@
   cras_iodev_buffer_avail_ret = 0;
   cras_alsa_resume_appl_ptr_called = 0;
   cras_alsa_resume_appl_ptr_ahead = 0;
-  ucm_get_enable_htimestamp_flag_ret = 0;
   fake_get_dBFS_volume_curve_val = NULL;
   cras_iodev_dsp_set_swap_mode_for_node_called = 0;
   ucm_get_default_node_gain_values.clear();
@@ -2473,7 +2471,7 @@
                            unsigned int dma_period_time) {
   return 0;
 }
-int cras_alsa_set_swparams(snd_pcm_t* handle, int* enable_htimestamp) {
+int cras_alsa_set_swparams(snd_pcm_t* handle) {
   return 0;
 }
 int cras_alsa_get_avail_frames(snd_pcm_t* handle,
@@ -2768,10 +2766,6 @@
   return 0;
 }
 
-unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr* mgr) {
-  return ucm_get_enable_htimestamp_flag_ret;
-}
-
 unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr* mgr) {
   return 0;
 }
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index efb8265..44c3587 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -862,28 +862,6 @@
   ASSERT_FALSE(fully_specified_flag);
 }
 
-TEST(AlsaUcm, EnableHtimestampFlag) {
-  struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
-  unsigned int enable_htimestamp_flag;
-
-  std::string id = "=EnableHtimestamp//HiFi";
-  ResetStubData();
-
-  /* Flag is not set */
-  enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
-  ASSERT_FALSE(enable_htimestamp_flag);
-
-  /* Flag is set to "1". */
-  snd_use_case_get_value[id] = std::string("1");
-  enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
-  ASSERT_TRUE(enable_htimestamp_flag);
-
-  /* Flag is set to "0". */
-  snd_use_case_get_value[id] = std::string("0");
-  enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
-  ASSERT_FALSE(enable_htimestamp_flag);
-}
-
 TEST(AlsaUcm, GetMixerNameForDevice) {
   struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
   const char *mixer_name_1, *mixer_name_2;
diff --git a/cras/src/tests/audio_thread_unittest_obsolete.cc b/cras/src/tests/audio_thread_unittest_obsolete.cc
index 0baeb38..ae9f5ef 100644
--- a/cras/src/tests/audio_thread_unittest_obsolete.cc
+++ b/cras/src/tests/audio_thread_unittest_obsolete.cc
@@ -52,8 +52,8 @@
 
 static struct timespec time_now;
 static int cras_fmt_conversion_needed_return_val;
-static struct cras_audio_area* dummy_audio_area1;
-static struct cras_audio_area* dummy_audio_area2;
+static struct cras_audio_area* mock_audio_area1;
+static struct cras_audio_area* mock_audio_area2;
 static struct cras_audio_format cras_iodev_set_format_val;
 
 static struct dev_stream_capture_call dev_stream_capture_call;
@@ -95,18 +95,18 @@
     SetupRstream(&rstream2_, 2);
     shm2_ = cras_rstream_input_shm(rstream2_);
 
-    dummy_audio_area1 = (cras_audio_area*)calloc(
+    mock_audio_area1 = (cras_audio_area*)calloc(
         1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area));
-    dummy_audio_area1->num_channels = 2;
-    channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL);
-    channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR);
-    rstream_->input_audio_area = dummy_audio_area1;
-    dummy_audio_area2 = (cras_audio_area*)calloc(
+    mock_audio_area1->num_channels = 2;
+    channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL);
+    channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR);
+    rstream_->input_audio_area = mock_audio_area1;
+    mock_audio_area2 = (cras_audio_area*)calloc(
         1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area));
-    dummy_audio_area2->num_channels = 2;
-    channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL);
-    channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR);
-    rstream2_->input_audio_area = dummy_audio_area2;
+    mock_audio_area2->num_channels = 2;
+    channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL);
+    channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR);
+    rstream2_->input_audio_area = mock_audio_area2;
 
     dev_stream_mix_dont_fill_next = 0;
     dev_stream_mix_count = 0;
@@ -123,8 +123,8 @@
     free(rstream_);
     free(shm2_->area);
     free(rstream2_);
-    free(dummy_audio_area1);
-    free(dummy_audio_area2);
+    free(mock_audio_area1);
+    free(mock_audio_area2);
   }
 
   void SetupRstream(struct cras_rstream** rstream, int fd) {
@@ -1444,18 +1444,18 @@
     rstream2_->buffer_frames -= 50;
     rstream2_->cb_threshold -= 50;
 
-    dummy_audio_area1 = (cras_audio_area*)calloc(
+    mock_audio_area1 = (cras_audio_area*)calloc(
         1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area));
-    dummy_audio_area1->num_channels = 2;
-    channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL);
-    channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR);
-    rstream_->input_audio_area = dummy_audio_area1;
-    dummy_audio_area2 = (cras_audio_area*)calloc(
+    mock_audio_area1->num_channels = 2;
+    channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL);
+    channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR);
+    rstream_->input_audio_area = mock_audio_area1;
+    mock_audio_area2 = (cras_audio_area*)calloc(
         1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area));
-    dummy_audio_area2->num_channels = 2;
-    channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL);
-    channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR);
-    rstream2_->input_audio_area = dummy_audio_area2;
+    mock_audio_area2->num_channels = 2;
+    channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL);
+    channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR);
+    rstream2_->input_audio_area = mock_audio_area2;
 
     cras_iodev_set_format_called = 0;
     close_dev_called_ = 0;
@@ -1483,8 +1483,8 @@
     shm = cras_rstream_output_shm(rstream_);
     free(shm->header);
     free(rstream_);
-    free(dummy_audio_area1);
-    free(dummy_audio_area2);
+    free(mock_audio_area1);
+    free(mock_audio_area2);
   }
 
   void SetupRstream(struct cras_rstream** rstream) {
diff --git a/cras/src/tests/bt_device_unittest.cc b/cras/src/tests/bt_device_unittest.cc
index a9213f3..ccb581c 100644
--- a/cras/src/tests/bt_device_unittest.cc
+++ b/cras/src/tests/bt_device_unittest.cc
@@ -228,7 +228,8 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
+  cras_bt_device_set_supported_profiles(device,
+                                        CRAS_BT_DEVICE_PROFILE_A2DP_SINK);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -268,8 +269,9 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -310,9 +312,10 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
+                  CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -360,9 +363,10 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
+                  CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -398,9 +402,10 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
+                  CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -433,9 +438,10 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
+                  CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
@@ -500,9 +506,10 @@
   device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
   EXPECT_NE((void*)NULL, device);
 
-  cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID);
-  cras_bt_device_add_supported_profiles(device, HSP_HS_UUID);
-  cras_bt_device_add_supported_profiles(device, HFP_HF_UUID);
+  cras_bt_device_set_supported_profiles(
+      device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK |
+                  CRAS_BT_DEVICE_PROFILE_HSP_HEADSET |
+                  CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
 
   cur = msg_root = NewMockDBusConnectedMessage(1);
   cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL);
diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc
index 7ccb669..ee013cf 100644
--- a/cras/src/tests/bt_io_unittest.cc
+++ b/cras/src/tests/bt_io_unittest.cc
@@ -423,7 +423,8 @@
 }
 
 // From bt device
-int cras_bt_device_get_active_profile(const struct cras_bt_device* device) {
+unsigned int cras_bt_device_get_active_profile(
+    const struct cras_bt_device* device) {
   return cras_bt_device_get_active_profile_ret;
 }
 
diff --git a/cras/src/tests/capture_rclient_unittest.cc b/cras/src/tests/capture_rclient_unittest.cc
index 4c1ab07..b749f1a 100644
--- a/cras/src/tests/capture_rclient_unittest.cc
+++ b/cras/src/tests/capture_rclient_unittest.cc
@@ -23,8 +23,8 @@
 static int stream_list_add_called;
 static int stream_list_add_return;
 static unsigned int stream_list_rm_called;
-static struct cras_audio_shm dummy_shm;
-static struct cras_rstream dummy_rstream;
+static struct cras_audio_shm mock_shm;
+static struct cras_rstream mock_rstream;
 
 void ResetStubData() {
   cras_make_fd_nonblocking_called = 0;
@@ -177,44 +177,6 @@
   EXPECT_EQ(stream_id, out_msg.stream_id);
 }
 
-/*
- * TODO(yuhsaun): Remove this test when there are no client uses the old
- * craslib. (CRAS_PROTO_VER = 5)
- */
-TEST_F(CCRMessageSuite, StreamConnectMessageOldProtocal) {
-  struct cras_client_stream_connected out_msg;
-  int rc;
-
-  struct cras_connect_message_old msg;
-  cras_stream_id_t stream_id = 0x10002;
-
-  msg.proto_version = 5;
-  msg.direction = CRAS_STREAM_INPUT;
-  msg.stream_id = stream_id;
-  msg.stream_type = CRAS_STREAM_TYPE_DEFAULT;
-  msg.buffer_frames = 480;
-  msg.cb_threshold = 240;
-  msg.flags = 0;
-  msg.effects = 0;
-  pack_cras_audio_format(&msg.format, &fmt);
-  msg.dev_idx = NO_DEVICE;
-  msg.client_shm_size = 0;
-  msg.client_type = CRAS_CLIENT_TYPE_TEST;
-  msg.header.id = CRAS_SERVER_CONNECT_STREAM;
-  msg.header.length = sizeof(struct cras_connect_message_old);
-
-  fd_ = 100;
-  rc =
-      rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
-  EXPECT_EQ(1, cras_make_fd_nonblocking_called);
-  EXPECT_EQ(1, stream_list_add_called);
-  EXPECT_EQ(0, stream_list_rm_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);
-}
-
 TEST_F(CCRMessageSuite, StreamDisconnectMessage) {
   struct cras_disconnect_stream_message msg;
   cras_stream_id_t stream_id = 0x10002;
@@ -290,16 +252,16 @@
                     struct cras_rstream** stream) {
   int ret;
 
-  *stream = &dummy_rstream;
+  *stream = &mock_rstream;
 
   stream_list_add_called++;
   ret = stream_list_add_return;
   if (ret)
     stream_list_add_return = -EINVAL;
 
-  dummy_rstream.shm = &dummy_shm;
-  dummy_rstream.direction = config->direction;
-  dummy_rstream.stream_id = config->stream_id;
+  mock_rstream.shm = &mock_shm;
+  mock_rstream.direction = config->direction;
+  mock_rstream.stream_id = config->stream_id;
 
   return ret;
 }
diff --git a/cras/src/tests/control_rclient_unittest.cc b/cras/src/tests/control_rclient_unittest.cc
index b0d01fc..d6b63aa 100644
--- a/cras/src/tests/control_rclient_unittest.cc
+++ b/cras/src/tests/control_rclient_unittest.cc
@@ -47,8 +47,8 @@
 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 struct cras_audio_shm dummy_shm;
-static struct cras_rstream dummy_rstream;
+static struct cras_audio_shm mock_shm;
+static struct cras_rstream mock_rstream;
 static size_t cras_observer_num_ops_registered;
 static size_t cras_observer_register_notify_called;
 static size_t cras_observer_add_called;
@@ -222,30 +222,6 @@
             stream_list_disconnect_stream_called);
 }
 
-TEST_F(RClientMessagesSuite, ConnectMsgFromOldClient) {
-  struct cras_client_stream_connected 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 = 5;
-
-  fd_ = 100;
-  rc = rclient_->ops->handle_message_from_client(rclient_, &connect_msg_.header,
-                                                 &fd_, 1);
-  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);
-}
-
 TEST_F(RClientMessagesSuite, StreamConnectMessageValidDirection) {
   struct cras_client_stream_connected out_msg;
   int rc;
@@ -911,16 +887,16 @@
                     struct cras_rstream** stream) {
   int ret;
 
-  *stream = &dummy_rstream;
+  *stream = &mock_rstream;
 
   stream_list_add_stream_called++;
   ret = stream_list_add_stream_return;
   if (ret)
     stream_list_add_stream_return = -EINVAL;
 
-  dummy_rstream.shm = &dummy_shm;
-  dummy_rstream.direction = config->direction;
-  dummy_rstream.stream_id = config->stream_id;
+  mock_rstream.shm = &mock_shm;
+  mock_rstream.direction = config->direction;
+  mock_rstream.stream_id = config->stream_id;
 
   return ret;
 }
@@ -987,4 +963,8 @@
   return true;
 }
 
+struct packet_status_logger* cras_hfp_ag_get_wbs_logger() {
+  return NULL;
+}
+
 }  // extern "C"
diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc
index 9210021..640ca93 100644
--- a/cras/src/tests/dev_stream_unittest.cc
+++ b/cras/src/tests/dev_stream_unittest.cc
@@ -340,7 +340,7 @@
   // 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(kBufferFrames, device_frames);  // Soundness check.
   EXPECT_LE(device_frames, config_format_converter_frames);
   EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
@@ -364,7 +364,7 @@
   // 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(kBufferFrames, device_frames);  // Soundness 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);
@@ -420,7 +420,7 @@
   // 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(kBufferFrames, device_frames);  // Soundness check.
   EXPECT_LE(device_frames, config_format_converter_frames);
   EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
@@ -441,7 +441,7 @@
   // 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(kBufferFrames, device_frames);  // Soundness check.
   EXPECT_LE(device_frames, config_format_converter_frames);
   EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames);
   dev_stream_destroy(dev_stream);
diff --git a/cras/src/tests/empty_iodev_unittest.cc b/cras/src/tests/empty_iodev_unittest.cc
index 585fba3..148d530 100644
--- a/cras/src/tests/empty_iodev_unittest.cc
+++ b/cras/src/tests/empty_iodev_unittest.cc
@@ -13,7 +13,7 @@
 
 static struct timespec clock_gettime_retspec;
 static struct cras_audio_format fake_format;
-static cras_audio_area dummy_audio_area;
+static cras_audio_area mock_audio_area;
 
 namespace {
 
@@ -57,7 +57,7 @@
 }
 
 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
-  iodev->area = &dummy_audio_area;
+  iodev->area = &mock_audio_area;
 }
 
 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
diff --git a/cras/src/tests/ewma_power_unittest.cc b/cras/src/tests/ewma_power_unittest.cc
new file mode 100644
index 0000000..10f0318
--- /dev/null
+++ b/cras/src/tests/ewma_power_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 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>
+
+extern "C" {
+#include "ewma_power.h"
+}
+
+namespace {
+
+TEST(EWMAPower, RelativePowerValue) {
+  struct ewma_power ewma;
+  int16_t buf[480];
+  float f;
+  int i;
+
+  for (i = 0; i < 480; i++)
+    buf[i] = 0x00fe;
+
+  ewma_power_init(&ewma, 48000);
+  EXPECT_EQ(48, ewma.step_fr);
+
+  ewma_power_calculate(&ewma, buf, 1, 480);
+  EXPECT_LT(0.0f, ewma.power);
+
+  // After 10ms of silence the power value decreases.
+  f = ewma.power;
+  for (i = 0; i < 480; i++)
+    buf[i] = 0x00;
+  ewma_power_calculate(&ewma, buf, 1, 480);
+  EXPECT_LT(ewma.power, f);
+
+  // After 300ms of silence the power value decreases to insignificant low.
+  for (i = 0; i < 30; i++)
+    ewma_power_calculate(&ewma, buf, 1, 480);
+  EXPECT_LT(ewma.power, 1.0e-10);
+}
+
+TEST(EWMAPower, PowerInStereoData) {
+  struct ewma_power ewma;
+  int16_t buf[960];
+  int i;
+  float f;
+
+  ewma_power_init(&ewma, 48000);
+
+  for (i = 0; i < 960; i += 2) {
+    buf[i] = 0x0;
+    buf[i + 1] = 0x00fe;
+  }
+  ewma_power_calculate(&ewma, buf, 2, 480);
+  EXPECT_LT(0.0f, ewma.power);
+
+  // After 10ms of silence the power value decreases.
+  f = ewma.power;
+  for (i = 0; i < 960; i++)
+    buf[i] = 0x0;
+  ewma_power_calculate(&ewma, buf, 2, 480);
+  EXPECT_LT(ewma.power, f);
+
+  // After 300ms of silence the power value decreases to insignificant low.
+  for (i = 0; i < 30; i++)
+    ewma_power_calculate(&ewma, buf, 2, 480);
+  EXPECT_LT(ewma.power, 1.0e-10);
+
+  // Assume the data is silent in the other channel.
+  ewma_power_init(&ewma, 48000);
+
+  for (i = 0; i < 960; i += 2) {
+    buf[i] = 0x0ffe;
+    buf[i + 1] = 0x0;
+  }
+  ewma_power_calculate(&ewma, buf, 2, 480);
+  EXPECT_LT(0.0f, ewma.power);
+}
+
+TEST(EWMAPower, PowerInAudioArea) {
+  struct ewma_power ewma;
+  struct cras_audio_area* area = cras_audio_area_create(4);
+  struct cras_audio_format* fmt =
+      cras_audio_format_create(SND_PCM_FORMAT_S16_LE, 48000, 4);
+  int8_t layout[CRAS_CH_MAX] = {0, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+  int16_t buf[1920];
+  int i;
+  float f;
+
+  cras_audio_format_set_channel_layout(fmt, layout);
+  cras_audio_area_config_channels(area, fmt);
+
+  for (i = 0; i < 1920; i += 4) {
+    buf[i] = 0x0ffe;
+    buf[i + 1] = 0x0;
+    buf[i + 2] = 0x0;
+    buf[i + 3] = 0x0ffe;
+  }
+  ewma_power_init(&ewma, 48000);
+  ewma_power_calculate_area(&ewma, buf, area, 480);
+  f = ewma.power;
+  EXPECT_LT(0.0f, f);
+
+  /* Change the layout in the same audio area. Expect the power be lower because
+   * one of the channel is now silent. */
+  layout[CRAS_CH_FR] = 2;
+  cras_audio_format_set_channel_layout(fmt, layout);
+  cras_audio_area_config_channels(area, fmt);
+  ewma_power_init(&ewma, 48000);
+  ewma_power_calculate_area(&ewma, buf, area, 480);
+  EXPECT_GT(f, ewma.power);
+
+  /* Change layout to the two silent channels. Expect power is 0.0f. */
+  layout[CRAS_CH_FL] = 1;
+  cras_audio_format_set_channel_layout(fmt, layout);
+  cras_audio_area_config_channels(area, fmt);
+  ewma_power_init(&ewma, 48000);
+  ewma_power_calculate_area(&ewma, buf, area, 480);
+  EXPECT_EQ(0.0f, ewma.power);
+
+  cras_audio_format_destroy(fmt);
+  cras_audio_area_destroy(area);
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/fmt_conv_ops_unittest.cc b/cras/src/tests/fmt_conv_ops_unittest.cc
index d8feeab..ebe8b65 100644
--- a/cras/src/tests/fmt_conv_ops_unittest.cc
+++ b/cras/src/tests/fmt_conv_ops_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <gtest/gtest.h>
 #include <limits.h>
+#include <math.h>
+#include <stdint.h>
 #include <sys/param.h>
 
 #include <memory>
@@ -477,7 +479,7 @@
   const size_t out_ch = 2;
   const size_t left = 0;
   const size_t right = 1;
-  const size_t center = 4;
+  const size_t center = 2;
 
   S16LEPtr src = CreateS16LE(frames * in_ch);
   S16LEPtr dst = CreateS16LE(frames * out_ch);
@@ -486,11 +488,58 @@
       s16_51_to_stereo((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
   EXPECT_EQ(ret, frames);
 
+  /* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|)
+   * to prevent mixing overflow.
+   */
+  const float normalized_factor = 0.585;
+
   for (size_t i = 0; i < frames; ++i) {
-    int16_t half_center = src[i * 6 + center] / 2;
-    EXPECT_EQ(S16AddAndClip(src[i * 6 + left], half_center), dst[i * 2 + left]);
-    EXPECT_EQ(S16AddAndClip(src[i * 6 + right], half_center),
-              dst[i * 2 + right]);
+    int16_t half_center = src[i * 6 + center] * 0.707 * normalized_factor;
+    int16_t l = normalized_factor * src[i * 6 + left] + half_center;
+    int16_t r = normalized_factor * src[i * 6 + right] + half_center;
+
+    EXPECT_EQ(l, dst[i * 2 + left]);
+    EXPECT_EQ(r, dst[i * 2 + right]);
+  }
+}
+
+// Test 5.1 to Quad conversion.  S16_LE.
+TEST(FormatConverterOpsTest, _51ToQuadS16LE) {
+  const size_t frames = 4096;
+  const size_t in_ch = 6;
+  const size_t out_ch = 4;
+  const unsigned int fl_quad = 0;
+  const unsigned int fr_quad = 1;
+  const unsigned int rl_quad = 2;
+  const unsigned int rr_quad = 3;
+
+  const unsigned int fl_51 = 0;
+  const unsigned int fr_51 = 1;
+  const unsigned int center_51 = 2;
+  const unsigned int lfe_51 = 3;
+  const unsigned int rl_51 = 4;
+  const unsigned int rr_51 = 5;
+
+  S16LEPtr src = CreateS16LE(frames * in_ch);
+  S16LEPtr dst = CreateS16LE(frames * out_ch);
+
+  size_t ret = s16_51_to_quad((uint8_t*)src.get(), frames, (uint8_t*)dst.get());
+  EXPECT_EQ(ret, frames);
+
+  /* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|)
+   * to prevent overflow. */
+  const float normalized_factor = 0.453;
+  for (size_t i = 0; i < frames; ++i) {
+    int16_t half_center = src[i * 6 + center_51] * 0.707 * normalized_factor;
+    int16_t lfe = src[6 * i + lfe_51] * 0.5 * normalized_factor;
+    int16_t fl = normalized_factor * src[6 * i + fl_51] + half_center + lfe;
+    int16_t fr = normalized_factor * src[6 * i + fr_51] + half_center + lfe;
+    int16_t rl = normalized_factor * src[6 * i + rl_51] + lfe;
+    int16_t rr = normalized_factor * src[6 * i + rr_51] + lfe;
+    EXPECT_EQ(fl, dst[4 * i + fl_quad]);
+    EXPECT_EQ(fr, dst[4 * i + fr_quad]);
+    EXPECT_EQ(rl, dst[4 * i + rl_quad]);
+    EXPECT_EQ(rr, dst[4 * i + rr_quad]);
   }
 }
 
diff --git a/cras/src/tests/fmt_conv_unittest.cc b/cras/src/tests/fmt_conv_unittest.cc
index 5474f17..c66984e 100644
--- a/cras/src/tests/fmt_conv_unittest.cc
+++ b/cras/src/tests/fmt_conv_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <gtest/gtest.h>
+#include <math.h>
 #include <sys/param.h>
 
 extern "C" {
@@ -459,6 +460,81 @@
   free(out_buff);
 }
 
+// Test 5.1 to Quad mix.
+TEST(FormatConverterTest, SurroundToQuad) {
+  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 = 6;
+  out_fmt.num_channels = 4;
+  in_fmt.frame_rate = 48000;
+  out_fmt.frame_rate = 48000;
+  for (i = 0; i < CRAS_CH_MAX; i++)
+    in_fmt.channel_layout[i] = surround_channel_center_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));
+
+  const int16_t in_fl = 100;
+  const int16_t in_fr = 200;
+  const int16_t in_rl = 200;
+  const int16_t in_rr = 300;
+  const int16_t in_fc = 60;
+  const int16_t in_lfe = 90;
+
+  for (i = 0; i < buf_size; i++) {
+    in_buff[i * 6 + CRAS_CH_FL] = in_fl;
+    in_buff[i * 6 + CRAS_CH_FR] = in_fr;
+    in_buff[i * 6 + CRAS_CH_RL] = in_rl;
+    in_buff[i * 6 + CRAS_CH_RR] = in_rr;
+    in_buff[i * 6 + CRAS_CH_FC] = in_fc;
+    in_buff[i * 6 + CRAS_CH_LFE] = in_lfe;
+  }
+  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);
+
+  // This is the sum of mtx[CRAS_CH_FL] coefficients.
+  const float normalize_factor = 1.0 / (1 + 0.707 + 0.5);
+
+  for (i = 0; i < buf_size; i++) {
+    int16_t lfe = 0.5 * normalize_factor * in_lfe;
+    int16_t center = 0.707 * normalize_factor * in_fc;
+    int16_t fl = normalize_factor * in_fl + center + lfe;
+    int16_t fr = normalize_factor * in_fr + center + lfe;
+    int16_t rl = normalize_factor * in_rl + lfe;
+    int16_t rr = normalize_factor * in_rr + lfe;
+
+    EXPECT_EQ(fl, out_buff[i * 4 + CRAS_CH_FL]);
+    EXPECT_EQ(fr, out_buff[i * 4 + CRAS_CH_FR]);
+    EXPECT_EQ(rl, out_buff[i * 4 + CRAS_CH_RL]);
+    EXPECT_EQ(rr, out_buff[i * 4 + CRAS_CH_RR]);
+  }
+  cras_fmt_conv_destroy(&c);
+  free(in_buff);
+  free(out_buff);
+}
+
 // Test Quad to Stereo mix.
 TEST(FormatConverterTest, QuadToStereo) {
   struct cras_fmt_conv* c;
diff --git a/cras/src/tests/hfp_ag_profile_unittest.cc b/cras/src/tests/hfp_ag_profile_unittest.cc
index 1a115f0..3ecd240 100644
--- a/cras/src/tests/hfp_ag_profile_unittest.cc
+++ b/cras/src/tests/hfp_ag_profile_unittest.cc
@@ -286,6 +286,9 @@
   cras_bt_device_notify_profile_dropped_profile = profile;
 }
 
+void hfp_info_set_wbs_logger(struct hfp_info* info,
+                             struct packet_status_logger* wbs_logger) {}
+
 void cras_observer_notify_bt_battery_changed(const char* address,
                                              uint32_t level) {
   return;
diff --git a/cras/src/tests/hfp_alsa_iodev_unittest.cc b/cras/src/tests/hfp_alsa_iodev_unittest.cc
index 8975694..c5bd4e9 100644
--- a/cras/src/tests/hfp_alsa_iodev_unittest.cc
+++ b/cras/src/tests/hfp_alsa_iodev_unittest.cc
@@ -477,6 +477,9 @@
   iodev->active_node = node;
 }
 
+// From ewma_power
+void ewma_power_disable(struct ewma_power* ewma) {}
+
 size_t cras_system_get_volume() {
   return 0;
 }
diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc
index 48a1c89..24f536a 100644
--- a/cras/src/tests/hfp_info_unittest.cc
+++ b/cras/src/tests/hfp_info_unittest.cc
@@ -3,10 +3,15 @@
  * found in the LICENSE file.
  */
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdint.h>
 #include <time.h>
 
+using testing::MatchesRegex;
+using testing::internal::CaptureStdout;
+using testing::internal::GetCapturedStdout;
+
 extern "C" {
 #include "cras_hfp_info.c"
 #include "sbc_codec_stub.h"
@@ -504,6 +509,26 @@
   hfp_info_destroy(info);
 }
 
+TEST(HfpInfo, WBSLoggerPacketStatusDumpBinary) {
+  struct packet_status_logger logger;
+  char log_regex[64];
+  int num_wraps[5] = {0, 0, 0, 1, 1};
+  int wp[5] = {40, 150, 162, 100, 32};
+
+  /* Expect the log line wraps at correct length to avoid feedback redact. */
+  snprintf(log_regex, 64, "([01D]{%d}\n)*", PACKET_STATUS_LOG_LINE_WRAP);
+
+  packet_status_logger_init(&logger);
+  logger.size = PACKET_STATUS_LEN_BYTES * 8;
+  for (int i = 0; i < 5; i++) {
+    CaptureStdout();
+    logger.num_wraps = num_wraps[i];
+    logger.wp = wp[i];
+    packet_status_logger_dump_binary(&logger);
+    EXPECT_THAT(GetCapturedStdout(), MatchesRegex(log_regex));
+  }
+}
+
 }  // namespace
 
 extern "C" {
@@ -549,6 +574,10 @@
   cras_msbc_plc_handle_good_frames_called++;
   return MSBC_CODE_SIZE;
 }
+void packet_status_logger_init(struct packet_status_logger* logger) {}
+
+void packet_status_logger_update(struct packet_status_logger* logger,
+                                 bool val) {}
 }
 
 int main(int argc, char** argv) {
diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc
index 2a61140..18262bf 100644
--- a/cras/src/tests/hfp_iodev_unittest.cc
+++ b/cras/src/tests/hfp_iodev_unittest.cc
@@ -43,7 +43,7 @@
 static size_t hfp_force_output_level_called;
 static size_t hfp_force_output_level_target;
 static size_t fake_buffer_size = 500;
-static cras_audio_area* dummy_audio_area;
+static cras_audio_area* mock_audio_area;
 
 void ResetStubData() {
   cras_bt_device_append_iodev_called = 0;
@@ -73,9 +73,9 @@
 
   fake_info = reinterpret_cast<struct hfp_info*>(0x123);
 
-  if (!dummy_audio_area) {
-    dummy_audio_area = (cras_audio_area*)calloc(
-        1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
+  if (!mock_audio_area) {
+    mock_audio_area = (cras_audio_area*)calloc(
+        1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2);
   }
 }
 
@@ -86,8 +86,8 @@
   virtual void SetUp() { ResetStubData(); }
 
   virtual void TearDown() {
-    free(dummy_audio_area);
-    dummy_audio_area = NULL;
+    free(mock_audio_area);
+    mock_audio_area = NULL;
   }
 };
 
@@ -243,6 +243,9 @@
   iodev->active_node = node;
 }
 
+// From ewma_power
+void ewma_power_disable(struct ewma_power* ewma) {}
+
 //  From system_state.
 size_t cras_system_get_volume() {
   return 0;
@@ -351,7 +354,7 @@
 }
 
 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
-  iodev->area = dummy_audio_area;
+  iodev->area = mock_audio_area;
 }
 
 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
@@ -367,7 +370,7 @@
 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
                                          const struct cras_audio_format* fmt,
                                          uint8_t* base_buffer) {
-  dummy_audio_area->channels[0].buf = base_buffer;
+  mock_audio_area->channels[0].buf = base_buffer;
 }
 
 int hfp_set_call_status(struct hfp_slc_handle* handle, int call) {
diff --git a/cras/src/tests/hfp_slc_unittest.cc b/cras/src/tests/hfp_slc_unittest.cc
index 4501b07..966278f 100644
--- a/cras/src/tests/hfp_slc_unittest.cc
+++ b/cras/src/tests/hfp_slc_unittest.cc
@@ -202,7 +202,7 @@
   /* Assert "\r\n+BIND: (2)\r\n" response is received */
   err = read(sock[1], buf, 256);
 
-  chp = strstr(buf, "\r\n+BIND: (2)\r\n");
+  chp = strstr(buf, "\r\n+BIND: (1,2)\r\n");
   ASSERT_NE((void*)NULL, (void*)chp);
   chp = strstr(buf, "\r\nOK\r\n");
   ASSERT_NE((void*)NULL, (void*)chp);
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index d40faca..272537f 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -44,8 +44,8 @@
 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 struct cras_iodev mock_hotword_iodev;
+static struct cras_iodev mock_empty_iodev[2];
 static stream_callback* stream_add_cb;
 static stream_callback* stream_rm_cb;
 static struct cras_rstream* stream_list_get_ret;
@@ -233,11 +233,11 @@
     DL_APPEND(fake_sco_out_dev.nodes, &fake_sco_out_node);
     fake_sco_in_node.is_sco_pcm = 0;
     fake_sco_out_node.is_sco_pcm = 0;
-    dummy_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE;
-    dummy_empty_iodev[0].update_active_node = update_active_node;
-    dummy_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE;
-    dummy_empty_iodev[1].update_active_node = update_active_node;
-    dummy_hotword_iodev.update_active_node = update_active_node;
+    mock_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE;
+    mock_empty_iodev[0].update_active_node = update_active_node;
+    mock_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE;
+    mock_empty_iodev[1].update_active_node = update_active_node;
+    mock_hotword_iodev.update_active_node = update_active_node;
   }
 
   virtual void TearDown() {
@@ -681,8 +681,7 @@
   EXPECT_EQ(2, cras_iodev_open_called);
   EXPECT_EQ(1, audio_thread_add_stream_called);
   EXPECT_EQ(0, update_active_node_called);
-  EXPECT_EQ(&dummy_empty_iodev[CRAS_STREAM_OUTPUT],
-            audio_thread_add_stream_dev);
+  EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT], audio_thread_add_stream_dev);
 
   EXPECT_NE((void*)NULL, cras_tm_timer_cb);
   EXPECT_EQ(1, cras_tm_create_timer_called);
@@ -693,7 +692,7 @@
   EXPECT_EQ(1, cras_tm_create_timer_called);
   EXPECT_EQ(1, audio_thread_add_stream_called);
 
-  dummy_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_;
+  mock_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_;
   cras_tm_timer_cb = NULL;
   cras_iodev_open_ret[3] = -5;
   stream_add_cb(&rstream);
@@ -1752,7 +1751,7 @@
   EXPECT_EQ(2, update_active_node_called);
   // Unselect d1_ and select to d2_
   EXPECT_EQ(&d2_, update_active_node_iodev_val[0]);
-  EXPECT_EQ(&dummy_empty_iodev[CRAS_STREAM_OUTPUT],
+  EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT],
             update_active_node_iodev_val[1]);
 
   // Remove pinned stream from d1, check d1 is closed after stream removed.
@@ -1869,14 +1868,14 @@
   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);
+  EXPECT_EQ(&mock_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(&mock_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);
@@ -1910,7 +1909,7 @@
   /* 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(&mock_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
@@ -1918,7 +1917,7 @@
   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(&mock_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);
@@ -2035,9 +2034,9 @@
                                       enum CRAS_NODE_TYPE node_type) {
   struct cras_iodev* dev;
   if (node_type == CRAS_NODE_TYPE_HOTWORD) {
-    dev = &dummy_hotword_iodev;
+    dev = &mock_hotword_iodev;
   } else {
-    dev = &dummy_empty_iodev[direction];
+    dev = &mock_empty_iodev[direction];
   }
   dev->direction = direction;
   if (dev->active_node == NULL) {
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index 03cfee6..21dc4d5 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -60,7 +60,7 @@
 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 cras_dsp_load_mock_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;
@@ -148,7 +148,7 @@
   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;
+  cras_dsp_load_mock_pipeline_called = 0;
   rate_estimator_add_frames_num_frames = 0;
   rate_estimator_add_frames_called = 0;
   cras_system_get_mute_return = 0;
@@ -2240,12 +2240,12 @@
   EXPECT_EQ(3, cras_dsp_get_pipeline_called);
   EXPECT_EQ(3, cras_dsp_pipeline_set_sink_ext_module_called);
 
-  /* If pipeline doesn't exist, dummy pipeline should be loaded. */
+  /* If pipeline doesn't exist, mock 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(5, cras_dsp_get_pipeline_called);
-  EXPECT_EQ(1, cras_dsp_load_dummy_pipeline_called);
+  EXPECT_EQ(1, cras_dsp_load_mock_pipeline_called);
   EXPECT_EQ(4, cras_dsp_pipeline_set_sink_ext_module_called);
 }
 
@@ -2462,9 +2462,11 @@
 }
 
 // From cras_system_state.
-void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) {}
+void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction,
+                                    enum CRAS_CLIENT_TYPE client_type) {}
 
-void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) {}
+void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction,
+                                      enum CRAS_CLIENT_TYPE client_type) {}
 
 // From cras_dsp
 struct cras_dsp_context* cras_dsp_context_new(int sample_rate,
@@ -2479,9 +2481,9 @@
 }
 
 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_load_mock_pipeline(struct cras_dsp_context* ctx,
+                                 unsigned int num_channels) {
+  cras_dsp_load_mock_pipeline_called++;
 }
 
 void cras_dsp_set_variable_string(struct cras_dsp_context* ctx,
@@ -2760,6 +2762,18 @@
   return 0;
 }
 
+void ewma_power_init(struct ewma_power* ewma, unsigned int rate){};
+
+void ewma_power_calculate(struct ewma_power* ewma,
+                          const int16_t* buf,
+                          unsigned int channels,
+                          unsigned int size){};
+
+void ewma_power_calculate_area(struct ewma_power* ewma,
+                               const int16_t* buf,
+                               struct cras_audio_area* area,
+                               unsigned int size){};
+
 }  // extern "C"
 }  //  namespace
 
diff --git a/cras/src/tests/loopback_iodev_unittest.cc b/cras/src/tests/loopback_iodev_unittest.cc
index 1c69ba5..fde5037 100644
--- a/cras/src/tests/loopback_iodev_unittest.cc
+++ b/cras/src/tests/loopback_iodev_unittest.cc
@@ -29,7 +29,7 @@
 static const unsigned int kBufferSize = kBufferFrames * kFrameBytes;
 
 static struct timespec time_now;
-static cras_audio_area* dummy_audio_area;
+static cras_audio_area* mock_audio_area;
 static loopback_hook_data_t loop_hook;
 static struct cras_iodev* enabled_dev;
 static unsigned int cras_iodev_list_add_input_called;
@@ -46,8 +46,8 @@
 class LoopBackTestSuite : public testing::Test {
  protected:
   virtual void SetUp() {
-    dummy_audio_area = (cras_audio_area*)calloc(
-        1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
+    mock_audio_area = (cras_audio_area*)calloc(
+        1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2);
     for (unsigned int i = 0; i < kBufferSize; i++) {
       buf_[i] = rand();
     }
@@ -77,7 +77,7 @@
     EXPECT_EQ(1, cras_iodev_list_rm_input_called);
     EXPECT_EQ(NULL, device_enabled_callback_cb);
     EXPECT_EQ(NULL, device_disabled_callback_cb);
-    free(dummy_audio_area);
+    free(mock_audio_area);
     audio_thread_event_log_deinit(atlog, atlog_name);
     free(atlog_name);
   }
@@ -224,7 +224,7 @@
 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
                                          const struct cras_audio_format* fmt,
                                          uint8_t* base_buffer) {
-  dummy_audio_area->channels[0].buf = base_buffer;
+  mock_audio_area->channels[0].buf = base_buffer;
 }
 
 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
@@ -232,7 +232,7 @@
 void cras_iodev_free_format(struct cras_iodev* iodev) {}
 
 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
-  iodev->area = dummy_audio_area;
+  iodev->area = mock_audio_area;
 }
 
 void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) {
diff --git a/cras/src/tests/observer_unittest.cc b/cras/src/tests/observer_unittest.cc
index 2053e94..2a8fae2 100644
--- a/cras/src/tests/observer_unittest.cc
+++ b/cras/src/tests/observer_unittest.cc
@@ -11,6 +11,8 @@
 
 extern "C" {
 #include "cras_observer.c"
+#include "cras_observer.h"
+#include "cras_types.h"
 }
 
 namespace {
@@ -56,6 +58,9 @@
 static std::vector<enum CRAS_STREAM_DIRECTION>
     cb_num_active_streams_changed_dir;
 static std::vector<uint32_t> cb_num_active_streams_changed_num;
+static size_t cb_num_input_streams_with_permission_called;
+static std::vector<std::vector<uint32_t>>
+    cb_num_input_streams_with_permission_array;
 
 static void ResetStubData() {
   cras_alert_destroy_called = 0;
@@ -99,6 +104,8 @@
   cb_num_active_streams_changed_called = 0;
   cb_num_active_streams_changed_dir.clear();
   cb_num_active_streams_changed_num.clear();
+  cb_num_input_streams_with_permission_called = 0;
+  cb_num_input_streams_with_permission_array.clear();
 }
 
 /* System output volume changed. */
@@ -190,6 +197,15 @@
   cb_num_active_streams_changed_num.push_back(num_active_streams);
 }
 
+void cb_num_input_streams_with_permission_changed(
+    void* context,
+    uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) {
+  cb_num_input_streams_with_permission_called++;
+  cb_context.push_back(context);
+  cb_num_input_streams_with_permission_array.push_back(std::vector<uint32_t>(
+      num_input_streams, num_input_streams + CRAS_NUM_CLIENT_TYPE));
+}
+
 class ObserverTest : public testing::Test {
  protected:
   virtual void SetUp() {
@@ -198,7 +214,7 @@
     ResetStubData();
     rc = cras_observer_server_init();
     ASSERT_EQ(0, rc);
-    EXPECT_EQ(16, cras_alert_create_called);
+    EXPECT_EQ(17, 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),
@@ -256,7 +272,7 @@
 
   virtual void TearDown() {
     cras_observer_server_free();
-    EXPECT_EQ(16, cras_alert_destroy_called);
+    EXPECT_EQ(17, cras_alert_destroy_called);
     ResetStubData();
   }
 
@@ -578,6 +594,37 @@
   DoObserverRemoveClear(num_active_streams_alert, data);
 };
 
+TEST_F(ObserverTest, NotifyNumInputStreamsWithPermission) {
+  struct cras_observer_alert_data_input_streams* data;
+  uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {};
+  for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) {
+    num_input_streams[type] = (uint32_t)type;
+  }
+
+  cras_observer_notify_input_streams_with_permission(num_input_streams);
+  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_input_streams*>(
+      cras_alert_pending_data_value);
+  for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) {
+    EXPECT_EQ(data->num_input_streams[type], num_input_streams[type]);
+  }
+
+  ops1_.num_input_streams_with_permission_changed =
+      cb_num_input_streams_with_permission_changed;
+  ops2_.num_input_streams_with_permission_changed =
+      cb_num_input_streams_with_permission_changed;
+  DoObserverAlert(num_input_streams_with_permission_alert, data);
+  ASSERT_EQ(2, cb_num_input_streams_with_permission_called);
+  for (auto cb_num_input_streams : cb_num_input_streams_with_permission_array) {
+    ASSERT_EQ(cb_num_input_streams.size(), (size_t)CRAS_NUM_CLIENT_TYPE);
+    for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) {
+      EXPECT_EQ(cb_num_input_streams[type], num_input_streams[type]);
+    }
+  }
+  DoObserverRemoveClear(num_input_streams_with_permission_alert, data);
+}
+
 TEST_F(ObserverTest, NotifyHotwordTriggered) {
   struct cras_observer_alert_data_hotword_triggered* data;
 
diff --git a/cras/src/tests/playback_rclient_unittest.cc b/cras/src/tests/playback_rclient_unittest.cc
index 7056d2f..75cbe55 100644
--- a/cras/src/tests/playback_rclient_unittest.cc
+++ b/cras/src/tests/playback_rclient_unittest.cc
@@ -24,8 +24,8 @@
 static int stream_list_add_called;
 static int stream_list_add_return;
 static unsigned int stream_list_rm_called;
-static struct cras_audio_shm dummy_shm;
-static struct cras_rstream dummy_rstream;
+static struct cras_audio_shm mock_shm;
+static struct cras_rstream mock_rstream;
 
 void ResetStubData() {
   audio_format_valid = true;
@@ -207,44 +207,6 @@
   EXPECT_EQ(stream_id, out_msg.stream_id);
 }
 
-/*
- * TODO(yuhsaun): Remove this test when there are no client uses the old
- * craslib. (CRAS_PROTO_VER = 5)
- */
-TEST_F(CPRMessageSuite, StreamConnectMessageOldProtocal) {
-  struct cras_client_stream_connected out_msg;
-  int rc;
-
-  struct cras_connect_message_old msg;
-  cras_stream_id_t stream_id = 0x10002;
-
-  msg.proto_version = 5;
-  msg.direction = CRAS_STREAM_OUTPUT;
-  msg.stream_id = stream_id;
-  msg.stream_type = CRAS_STREAM_TYPE_DEFAULT;
-  msg.buffer_frames = 480;
-  msg.cb_threshold = 240;
-  msg.flags = 0;
-  msg.effects = 0;
-  pack_cras_audio_format(&msg.format, &fmt);
-  msg.dev_idx = NO_DEVICE;
-  msg.client_shm_size = 0;
-  msg.client_type = CRAS_CLIENT_TYPE_TEST;
-  msg.header.id = CRAS_SERVER_CONNECT_STREAM;
-  msg.header.length = sizeof(struct cras_connect_message_old);
-
-  fd_ = 100;
-  rc =
-      rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
-  EXPECT_EQ(1, cras_make_fd_nonblocking_called);
-  EXPECT_EQ(1, stream_list_add_called);
-  EXPECT_EQ(0, stream_list_rm_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);
-}
-
 TEST_F(CPRMessageSuite, StreamDisconnectMessage) {
   struct cras_disconnect_stream_message msg;
   cras_stream_id_t stream_id = 0x10002;
@@ -320,16 +282,16 @@
                     struct cras_rstream** stream) {
   int ret;
 
-  *stream = &dummy_rstream;
+  *stream = &mock_rstream;
 
   stream_list_add_called++;
   ret = stream_list_add_return;
   if (ret)
     stream_list_add_return = -EINVAL;
 
-  dummy_rstream.shm = &dummy_shm;
-  dummy_rstream.direction = config->direction;
-  dummy_rstream.stream_id = config->stream_id;
+  mock_rstream.shm = &mock_shm;
+  mock_rstream.direction = config->direction;
+  mock_rstream.stream_id = config->stream_id;
 
   return ret;
 }
diff --git a/cras/src/tests/polled_interval_checker_unittest.cc b/cras/src/tests/polled_interval_checker_unittest.cc
index c18fdf7..a4aff09 100644
--- a/cras/src/tests/polled_interval_checker_unittest.cc
+++ b/cras/src/tests/polled_interval_checker_unittest.cc
@@ -70,7 +70,7 @@
 
   struct polled_interval* interval = create_interval();
 
-  // Sanity check.
+  // Initial check.
   EXPECT_FALSE(pic_interval_elapsed(interval));
 
   // Increment time so the interval elapses.
@@ -100,7 +100,7 @@
 
   struct polled_interval* interval = create_interval();
 
-  // Sanity check.
+  // Initial check.
   EXPECT_FALSE(pic_interval_elapsed(interval));
 
   // Increment time so the interval elapses.
diff --git a/cras/src/tests/rstream_unittest.cc b/cras/src/tests/rstream_unittest.cc
index 77dd6ad..593c805 100644
--- a/cras/src/tests/rstream_unittest.cc
+++ b/cras/src/tests/rstream_unittest.cc
@@ -390,10 +390,18 @@
                                     unsigned int id) {
   return 0;
 }
+void ewma_power_init(struct ewma_power* ewma, unsigned int rate) {}
 
-void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) {}
+void ewma_power_calculate(struct ewma_power* ewma,
+                          const int16_t* buf,
+                          unsigned int channels,
+                          unsigned int size) {}
 
-void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) {}
+void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction,
+                                    enum CRAS_CLIENT_TYPE client_type) {}
+
+void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction,
+                                      enum CRAS_CLIENT_TYPE client_type) {}
 
 int cras_server_metrics_stream_create(
     const struct cras_rstream_config* config) {
@@ -405,9 +413,13 @@
 }
 
 #ifdef HAVE_WEBRTC_APM
+#define FAKE_CRAS_APM_PTR reinterpret_cast<struct cras_apm*>(0x99)
 struct cras_apm_list* cras_apm_list_create(void* stream_ptr, uint64_t effects) {
   return NULL;
 }
+struct cras_apm* cras_apm_list_get_active_apm(void* stream_ptr, void* dev_ptr) {
+  return FAKE_CRAS_APM_PTR;
+}
 int cras_apm_list_destroy(struct cras_apm_list* list) {
   return 0;
 }
diff --git a/cras/src/tests/system_state_unittest.cc b/cras/src/tests/system_state_unittest.cc
index e61a43e..0450df3 100644
--- a/cras/src/tests/system_state_unittest.cc
+++ b/cras/src/tests/system_state_unittest.cc
@@ -38,6 +38,7 @@
 static size_t cras_observer_notify_capture_mute_called;
 static size_t cras_observer_notify_suspend_changed_called;
 static size_t cras_observer_notify_num_active_streams_called;
+static size_t cras_observer_notify_input_streams_with_permission_called;
 static struct cras_board_config fake_board_config;
 
 static void ResetStubData() {
@@ -58,6 +59,7 @@
   cras_observer_notify_capture_mute_called = 0;
   cras_observer_notify_suspend_changed_called = 0;
   cras_observer_notify_num_active_streams_called = 0;
+  cras_observer_notify_input_streams_with_permission_called = 0;
   memset(&fake_board_config, 0, sizeof(fake_board_config));
 }
 
@@ -371,11 +373,11 @@
   do_sys_init();
 
   EXPECT_EQ(0, cras_system_state_get_active_streams());
-  cras_system_state_stream_added(CRAS_STREAM_OUTPUT);
+  cras_system_state_stream_added(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME);
   EXPECT_EQ(1, cras_system_state_get_active_streams());
   struct cras_timespec ts1;
   cras_system_state_get_last_stream_active_time(&ts1);
-  cras_system_state_stream_removed(CRAS_STREAM_OUTPUT);
+  cras_system_state_stream_removed(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME);
   EXPECT_EQ(0, cras_system_state_get_active_streams());
   struct cras_timespec ts2;
   cras_system_state_get_last_stream_active_time(&ts2);
@@ -388,9 +390,11 @@
   do_sys_init();
 
   EXPECT_EQ(0, cras_system_state_get_active_streams());
-  cras_system_state_stream_added(CRAS_STREAM_OUTPUT);
-  cras_system_state_stream_added(CRAS_STREAM_INPUT);
-  cras_system_state_stream_added(CRAS_STREAM_POST_MIX_PRE_DSP);
+  cras_system_state_stream_added(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME);
+  cras_system_state_stream_added(CRAS_STREAM_INPUT, CRAS_CLIENT_TYPE_CHROME);
+  cras_system_state_stream_added(CRAS_STREAM_POST_MIX_PRE_DSP,
+                                 CRAS_CLIENT_TYPE_CHROME);
+  EXPECT_EQ(1, cras_observer_notify_input_streams_with_permission_called);
   EXPECT_EQ(
       1, cras_system_state_get_active_streams_by_direction(CRAS_STREAM_OUTPUT));
   EXPECT_EQ(
@@ -399,9 +403,11 @@
                    CRAS_STREAM_POST_MIX_PRE_DSP));
   EXPECT_EQ(3, cras_system_state_get_active_streams());
   EXPECT_EQ(3, cras_observer_notify_num_active_streams_called);
-  cras_system_state_stream_removed(CRAS_STREAM_OUTPUT);
-  cras_system_state_stream_removed(CRAS_STREAM_INPUT);
-  cras_system_state_stream_removed(CRAS_STREAM_POST_MIX_PRE_DSP);
+  cras_system_state_stream_removed(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME);
+  cras_system_state_stream_removed(CRAS_STREAM_INPUT, CRAS_CLIENT_TYPE_CHROME);
+  cras_system_state_stream_removed(CRAS_STREAM_POST_MIX_PRE_DSP,
+                                   CRAS_CLIENT_TYPE_CHROME);
+  EXPECT_EQ(2, cras_observer_notify_input_streams_with_permission_called);
   EXPECT_EQ(
       0, cras_system_state_get_active_streams_by_direction(CRAS_STREAM_OUTPUT));
   EXPECT_EQ(
@@ -511,6 +517,11 @@
   cras_observer_notify_num_active_streams_called++;
 }
 
+void cras_observer_notify_input_streams_with_permission(
+    uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) {
+  cras_observer_notify_input_streams_with_permission_called++;
+}
+
 void cras_board_config_get(const char* config_path,
                            struct cras_board_config* board_config) {
   *board_config = fake_board_config;
diff --git a/cras/src/tools/cras_test_client/cras_test_client.c b/cras/src/tools/cras_test_client/cras_test_client.c
index 947b945..5a7b3e0 100644
--- a/cras/src/tools/cras_test_client/cras_test_client.c
+++ b/cras/src/tools/cras_test_client/cras_test_client.c
@@ -493,6 +493,26 @@
 	*nsec = nsec_offset;
 }
 
+static float get_ewma_power_as_float(uint32_t data)
+{
+	float f = 0.0f;
+
+	/* Convert from the uint32_t log type back to float.
+	 * If data cannot be assigned to float, default value will
+	 * be printed as -inf to hint the problem.
+	 */
+	if (sizeof(uint32_t) == sizeof(float))
+		memcpy(&f, &data, sizeof(float));
+	else
+		printf("%-30s float to uint32_t\n", "MEMORY_NOT_ALIGNED");
+
+	/* Convert to dBFS and set to zero if it's
+	 * insignificantly low.  Picking the same threshold
+	 * 1.0e-10f as in Chrome.
+	 */
+	return (f < 1.0e-10f) ? -INFINITY : 10.0f * log10f(f);
+}
+
 static void show_alog_tag(const struct audio_thread_event_log *log,
 			  unsigned int tag_idx, int32_t sec_offset,
 			  int32_t nsec_offset)
@@ -552,25 +572,30 @@
 		printf("%-30s dev:%u tstamp:%s.%09u\n", "READ_AUDIO_TSTAMP",
 		       data1, time_str, nsec);
 		break;
-	case AUDIO_THREAD_READ_AUDIO_DONE:
-		printf("%-30s read_remainder:%u\n", "READ_AUDIO_DONE", data1);
+	case AUDIO_THREAD_READ_AUDIO_DONE: {
+		float f = get_ewma_power_as_float(data2);
+		printf("%-30s read_remainder:%u power:%f dBFS\n",
+		       "READ_AUDIO_DONE", data1, f);
 		break;
+	}
 	case AUDIO_THREAD_READ_OVERRUN:
 		printf("%-30s dev:%u stream:%x num_overruns:%u\n",
 		       "READ_AUDIO_OVERRUN", data1, data2, data3);
 		break;
 	case AUDIO_THREAD_FILL_AUDIO:
-		printf("%-30s dev:%u hw_level:%u\n", "FILL_AUDIO", data1,
-		       data2);
+		printf("%-30s dev:%u hw_level:%u min_cb_level:%u\n",
+		       "FILL_AUDIO", data1, data2, data3);
 		break;
 	case AUDIO_THREAD_FILL_AUDIO_TSTAMP:
 		printf("%-30s dev:%u tstamp:%s.%09u\n", "FILL_AUDIO_TSTAMP",
 		       data1, time_str, nsec);
 		break;
-	case AUDIO_THREAD_FILL_AUDIO_DONE:
-		printf("%-30s hw_level:%u total_written:%u min_cb_level:%u\n",
-		       "FILL_AUDIO_DONE", data1, data2, data3);
+	case AUDIO_THREAD_FILL_AUDIO_DONE: {
+		float f = get_ewma_power_as_float(data3);
+		printf("%-30s hw_level:%u total_written:%u power:%f dBFS\n",
+		       "FILL_AUDIO_DONE", data1, data2, f);
 		break;
+	}
 	case AUDIO_THREAD_WRITE_STREAMS_WAIT:
 		printf("%-30s stream:%x\n", "WRITE_STREAMS_WAIT", data1);
 		break;
@@ -588,10 +613,12 @@
 		printf("%-30s id:%x shm_frames:%u cb_pending:%u\n",
 		       "WRITE_STREAMS_STREAM", data1, data2, data3);
 		break;
-	case AUDIO_THREAD_FETCH_STREAM:
-		printf("%-30s id:%x cbth:%u delay:%u\n",
-		       "WRITE_STREAMS_FETCH_STREAM", data1, data2, data3);
+	case AUDIO_THREAD_FETCH_STREAM: {
+		float f = get_ewma_power_as_float(data3);
+		printf("%-30s id:%x cbth:%u power:%f dBFS\n",
+		       "WRITE_STREAMS_FETCH_STREAM", data1, data2, f);
 		break;
+	}
 	case AUDIO_THREAD_STREAM_ADDED:
 		printf("%-30s id:%x dev:%u\n", "STREAM_ADDED", data1, data2);
 		break;
@@ -966,7 +993,7 @@
 		printf("%-30s\n", "ADAPTER_REMOVED");
 		break;
 	case BT_A2DP_CONFIGURED:
-		printf("%-30s connected profiles %u\n", "A2DP_CONFIGURED",
+		printf("%-30s connected profiles 0x%.2x\n", "A2DP_CONFIGURED",
 		       data1);
 		break;
 	case BT_A2DP_START:
@@ -976,8 +1003,8 @@
 		printf("%-30s\n", "A2DP_SUSPENDED");
 		break;
 	case BT_AUDIO_GATEWAY_INIT:
-		printf("%-30s supported profiles %u\n", "AUDIO_GATEWAY_INIT",
-		       data1);
+		printf("%-30s supported profiles 0x%.2x\n",
+		       "AUDIO_GATEWAY_INIT", data1);
 		break;
 	case BT_AUDIO_GATEWAY_START:
 		printf("%-30s \n", "AUDIO_GATEWAY_START");
@@ -991,11 +1018,12 @@
 		       data2);
 		break;
 	case BT_DEV_CONNECTED_CHANGE:
-		printf("%-30s profiles %u now %u\n", "DEV_CONNECTED_CHANGE",
-		       data1, data2);
+		printf("%-30s supported profiles 0x%.2x now %s\n",
+		       "DEV_CONNECTED_CHANGE", data1,
+		       data2 ? "connected" : "disconnected");
 		break;
 	case BT_DEV_CONN_WATCH_CB:
-		printf("%-30s %u retries left, supported profiles %u\n",
+		printf("%-30s %u retries left, supported profiles 0x%.2x\n",
 		       "DEV_CONN_WATCH_CB", data1, data2);
 		break;
 	case BT_DEV_SUSPEND_CB:
@@ -1021,8 +1049,8 @@
 		printf("%-30s\n", "HFP_REQUEST_DISCONNECT");
 		break;
 	case BT_HFP_SUPPORTED_FEATURES:
-		printf("%-30s role %s features %u\n", "HFP_SUPPORTED_FEATURES",
-		       data1 ? "AG" : "HF", data2);
+		printf("%-30s role %s features 0x%.4x\n",
+		       "HFP_SUPPORTED_FEATURES", data1 ? "AG" : "HF", data2);
 		break;
 	case BT_HSP_NEW_CONNECTION:
 		printf("%-30s\n", "HSP_NEW_CONNECTION");
@@ -1031,7 +1059,7 @@
 		printf("%-30s\n", "HSP_REQUEST_DISCONNECT");
 		break;
 	case BT_NEW_AUDIO_PROFILE_AFTER_CONNECT:
-		printf("%-30s old %u, new %u\n",
+		printf("%-30s old 0x%.2x, new 0x%.2x\n",
 		       "NEW_AUDIO_PROFILE_AFTER_CONNECT", data1, data2);
 		break;
 	case BT_RESET:
@@ -1060,12 +1088,30 @@
 	}
 }
 
+static void convert_to_time_str(const struct timespec *ts, time_t sec_offset,
+				int32_t nsec_offset)
+{
+	time_t lt = ts->tv_sec;
+	struct tm t;
+	unsigned int time_nsec;
+
+	/* Assuming tv_nsec doesn't exceed 10^9 */
+	time_nsec = ts->tv_nsec;
+	convert_time((unsigned int *)&lt, &time_nsec, sec_offset, nsec_offset);
+	localtime_r(&lt, &t);
+	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
+	snprintf(time_str + strlen(time_str), 128 - strlen(time_str), ".%09u",
+		 time_nsec);
+}
+
 static void cras_bt_debug_info(struct cras_client *client)
 {
 	const struct cras_bt_debug_info *info;
 	time_t sec_offset;
 	int32_t nsec_offset;
 	int i, j;
+	struct timespec ts;
+	struct packet_status_logger wbs_logger;
 
 	info = cras_client_get_bt_debug_info(client);
 	fill_time_offset(&sec_offset, &nsec_offset);
@@ -1078,6 +1124,23 @@
 		j %= info->bt_log.len;
 	}
 
+	printf("-------------WBS packet loss------------\n");
+	wbs_logger = info->wbs_logger;
+
+	packet_status_logger_begin_ts(&wbs_logger, &ts);
+	convert_to_time_str(&ts, sec_offset, nsec_offset);
+	printf("%s [begin]\n", time_str);
+
+	packet_status_logger_end_ts(&wbs_logger, &ts);
+	convert_to_time_str(&ts, sec_offset, nsec_offset);
+	printf("%s [end]\n", time_str);
+
+	printf("In hex format:\n");
+	packet_status_logger_dump_hex(&wbs_logger);
+
+	printf("In binary format:\n");
+	packet_status_logger_dump_binary(&wbs_logger);
+
 	/* Signal main thread we are done after the last chunk. */
 	pthread_mutex_lock(&done_mutex);
 	pthread_cond_signal(&done_cond);
diff --git a/init/cras.conf b/init/cras.conf
index c61d789..2084749 100644
--- a/init/cras.conf
+++ b/init/cras.conf
@@ -10,6 +10,8 @@
 
 env CRAS_SOCKET_DIR=/run/cras
 env CRAS_VMS_SOCKET_DIR=/run/cras/vms
+env CRAS_PLUGIN_DIR=/run/cras/vms/plugin
+env CRAS_ARGS=
 
 start on starting system-services
 stop on stopping system-services
@@ -23,6 +25,18 @@
   chown -R cras:cras "${CRAS_SOCKET_DIR}"
   mkdir -p -m 1770 "${CRAS_VMS_SOCKET_DIR}"
   chown -R cras:cras "${CRAS_VMS_SOCKET_DIR}"
+  for socket_dir in playback unified; do
+    mkdir -p -m 1770 "${CRAS_PLUGIN_DIR}/${socket_dir}"
+    chown -R cras:cras "${CRAS_PLUGIN_DIR}/${socket_dir}"
+  done
+  mkdir -m 0755 -p /var/lib/cras
+  chown -R cras:cras /var/lib/cras
 end script
 
 exec /bin/sh /usr/share/cros/init/cras.sh
+
+# sound_card_init uses CRAS stop timestamp as a criterion to skip boot time
+# calibration for DSM.
+post-stop script
+  echo "$(date +---%\nsecs:\ %s%\nnanos:\ %N)" > /var/lib/cras/stop
+end script
diff --git a/init/cras.sh b/init/cras.sh
index b0ca430..ca2d6b9 100644
--- a/init/cras.sh
+++ b/init/cras.sh
@@ -62,4 +62,4 @@
         -- \
         /usr/bin/cras \
         ${DSP_CONFIG} ${DEVICE_CONFIG_DIR} ${DISABLE_PROFILE} \
-        ${INTERNAL_UCM_SUFFIX}
+        ${INTERNAL_UCM_SUFFIX} ${CRAS_ARGS}
diff --git a/scripts/audio_tuning/frontend/audio.js b/scripts/audio_tuning/frontend/audio.js
index 2476f1a..b90be95 100644
--- a/scripts/audio_tuning/frontend/audio.js
+++ b/scripts/audio_tuning/frontend/audio.js
@@ -1105,7 +1105,7 @@
   this.update = update;
 }
 
-function dummy() {
+function empty() {
 }
 
 /* Changes the opacity of a div. */
@@ -1185,7 +1185,7 @@
   var f_slider;
   if (lower_freq == 0) {  /* Special case for the lowest band */
     f_slider = new slider_input_log(table, freq_label, lower_freq, 0, 1,
-                                    'Hz', 0, dummy);
+                                    'Hz', 0, empty);
     f_slider.hide(true);
   } else {
     f_slider = new slider_input_log(table, freq_label, lower_freq, 1,
diff --git a/scripts/volume_tuning/volume.js b/scripts/volume_tuning/volume.js
index c5f0526..88f3998 100644
--- a/scripts/volume_tuning/volume.js
+++ b/scripts/volume_tuning/volume.js
@@ -116,7 +116,7 @@
   var max = parseFloat(minmax_boxes[1].value);
   var step = parseFloat(minmax_boxes[2].value);
 
-  // Sanity checks
+  // Soundness checks
   if (isNaN(min) || isNaN(max) || isNaN(step)) return;
   if (min >= max || step <= 0 || (max - min) / step > 10000) return;
 
diff --git a/seccomp/cras-seccomp-arm.policy b/seccomp/cras-seccomp-arm.policy
index 16b6ca3..e823244 100644
--- a/seccomp/cras-seccomp-arm.policy
+++ b/seccomp/cras-seccomp-arm.policy
@@ -85,7 +85,6 @@
 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),
diff --git a/seccomp/cras-seccomp-arm64.policy b/seccomp/cras-seccomp-arm64.policy
index d505d17..d06a741 100644
--- a/seccomp/cras-seccomp-arm64.policy
+++ b/seccomp/cras-seccomp-arm64.policy
@@ -53,7 +53,6 @@
 fchmod: 1
 fchmodat: 1
 flock: 1
-flock: 1
 getegid: 1
 geteuid: 1
 getgid: 1
diff --git a/sound_card_init/.gitignore b/sound_card_init/.gitignore
index f2e972d..b6e8700 100644
--- a/sound_card_init/.gitignore
+++ b/sound_card_init/.gitignore
@@ -1,6 +1,6 @@
 # Generated by Cargo
 # will have compiled files and executables
-/target/
+**/target/
 
 # These are backup files generated by rustfmt
 **/*.rs.bk
diff --git a/sound_card_init/Cargo.lock b/sound_card_init/Cargo.lock
index 8d0eeea..06448e9 100644
--- a/sound_card_init/Cargo.lock
+++ b/sound_card_init/Cargo.lock
@@ -106,6 +106,7 @@
  "serde",
  "serde_yaml",
  "sys_util",
+ "utils",
 ]
 
 [[package]]
@@ -197,6 +198,7 @@
  "serde",
  "serde_yaml",
  "sys_util",
+ "utils",
 ]
 
 [[package]]
@@ -250,6 +252,15 @@
 checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
 
 [[package]]
+name = "utils"
+version = "0.1.0"
+dependencies = [
+ "remain",
+ "serde",
+ "serde_yaml",
+]
+
+[[package]]
 name = "yaml-rust"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/sound_card_init/Cargo.toml b/sound_card_init/Cargo.toml
index d6ed9e2..d4a2b37 100644
--- a/sound_card_init/Cargo.toml
+++ b/sound_card_init/Cargo.toml
@@ -12,6 +12,7 @@
 libcras = "*"
 remain = "0.2.1"
 max98390d = { path = "max98390d" }
+utils = { path = "utils" }
 serde = { version = "1.0", features = ["derive"] }
 serde_yaml = "0.8.11"
 sys_util = "*"
diff --git a/sound_card_init/max98390d/Cargo.toml b/sound_card_init/max98390d/Cargo.toml
index 1566058..f3bbae6 100644
--- a/sound_card_init/max98390d/Cargo.toml
+++ b/sound_card_init/max98390d/Cargo.toml
@@ -13,3 +13,4 @@
 serde = { version = "1.0", features = ["derive"]}
 serde_yaml = "0.8.11"
 sys_util = "*"
+utils = { path = "../utils" }
\ No newline at end of file
diff --git a/sound_card_init/max98390d/src/amp_calibration.rs b/sound_card_init/max98390d/src/amp_calibration.rs
index 50f4227..3534022 100644
--- a/sound_card_init/max98390d/src/amp_calibration.rs
+++ b/sound_card_init/max98390d/src/amp_calibration.rs
@@ -123,24 +123,10 @@
         };
 
         if !self.validate_temperature(temp_cali) {
-            match datastore {
-                None => return Err(Error::InvalidTemperature(temp_cali)),
-                Some(d) => match d {
-                    Datastore::UseVPD => {
-                        info!("invalid temperature: {}, use VPD values.", temp_cali);
-                        return Ok(());
-                    }
-                    Datastore::DSM { rdc, ambient_temp } => {
-                        info!("invalid temperature: {}, use datastore values.", temp_cali);
-                        self.card
-                            .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)?
-                            .set(rdc)?;
-                        self.card
-                            .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)?
-                            .set(ambient_temp)?;
-                        return Ok(());
-                    }
-                },
+            info!("invalid temperature: {}.", temp_cali);
+            return match datastore {
+                None => Err(Error::InvalidTemperature(temp_cali)),
+                Some(d) => self.apply_datastore(d),
             };
         }
 
@@ -149,22 +135,10 @@
         } else if diff < CALI_ERROR_LOWER_LIMIT {
             match datastore {
                 None => Datastore::UseVPD.save(self.card.name(), &self.setting.calib_file)?,
-                Some(d) => match d {
-                    Datastore::UseVPD => {
-                        info!("rdc diff: {}, use VPD values.", diff);
-                    }
-                    Datastore::DSM { rdc, ambient_temp } => {
-                        info!("rdc diff: {}, use datastore values.", diff);
-                        self.card
-                            .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)?
-                            .set(rdc)?;
-                        self.card
-                            .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)?
-                            .set(ambient_temp)?;
-                    }
-                },
+                Some(d) => self.apply_datastore(d)?,
             }
         } else {
+            info!("apply boot time calibration values.");
             self.card
                 .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)?
                 .set(rdc_cali)?;
@@ -180,6 +154,22 @@
         Ok(())
     }
 
+    fn apply_datastore(&mut self, d: Datastore) -> Result<()> {
+        info!("apply datastore values.");
+        match d {
+            Datastore::UseVPD => Ok(()),
+            Datastore::DSM { rdc, ambient_temp } => {
+                self.card
+                    .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)?
+                    .set(rdc)?;
+                self.card
+                    .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)?
+                    .set(ambient_temp)?;
+                Ok(())
+            }
+        }
+    }
+
     fn validate_temperature(&self, temp: i32) -> bool {
         temp < self.setting.amp.temp_upper_limit && temp > self.setting.amp.temp_lower_limit
     }
@@ -213,7 +203,7 @@
                     return Err(Error::StartPlaybackTimeout);
                 } else {
                     // Spurious wakes. Decrements the sleep duration by the amount slept.
-                    timeout = timeout - start_time.elapsed();
+                    timeout -= start_time.elapsed();
                 }
             }
         }
@@ -312,4 +302,18 @@
 
         Ok(handle)
     }
+
+    /// Skips max98390d boot time calibration when the speaker may be hot.
+    ///
+    /// If datastore exists, applies the stored value and sets volume to high.
+    /// If datastore does not exist, sets volume to low.
+    pub fn hot_speaker_workflow(&mut self) -> Result<()> {
+        if let Ok(sci_calib) = Datastore::from_file(self.card.name(), &self.setting.calib_file) {
+            self.apply_datastore(sci_calib)?;
+            self.set_volume(VolumeMode::High)?;
+            return Ok(());
+        }
+        info!("no datastore, set volume low");
+        self.set_volume(VolumeMode::Low)
+    }
 }
diff --git a/sound_card_init/max98390d/src/datastore.rs b/sound_card_init/max98390d/src/datastore.rs
index 6e22163..12ebff7 100644
--- a/sound_card_init/max98390d/src/datastore.rs
+++ b/sound_card_init/max98390d/src/datastore.rs
@@ -7,11 +7,10 @@
 
 use serde::{Deserialize, Serialize};
 use sys_util::info;
+use utils::DATASTORE_DIR;
 
 use crate::error::{Error, Result};
 
-const DATASTORE_DIR: &str = "/var/lib/sound_card_init";
-
 /// `Datastore`, which stores and reads calibration values in yaml format.
 #[derive(Debug, Deserialize, Serialize, Copy, Clone)]
 pub enum Datastore {
diff --git a/sound_card_init/max98390d/src/error.rs b/sound_card_init/max98390d/src/error.rs
index 572340d..778ff96 100644
--- a/sound_card_init/max98390d/src/error.rs
+++ b/sound_card_init/max98390d/src/error.rs
@@ -6,6 +6,7 @@
 use std::io;
 use std::num::ParseIntError;
 use std::sync::PoisonError;
+use std::time;
 
 use remain::sorted;
 
@@ -21,8 +22,10 @@
     CrasClientFailed(libcras::Error),
     DeserializationFailed(String, serde_yaml::Error),
     FileIOFailed(String, io::Error),
+    HotSpeaker,
     InternalSpeakerNotFound,
     InvalidDatastore,
+    InvalidShutDownTime,
     InvalidTemperature(i32),
     LargeCalibrationDiff(i32, i32),
     MissingDSMParam,
@@ -30,8 +33,10 @@
     NewPlayStreamFailed(libcras::BoxError),
     NextPlaybackBufferFailed(libcras::BoxError),
     PlaybackFailed(io::Error),
+    ReadTimestampFailed(utils::error::Error),
     SerializationFailed(serde_yaml::Error),
     StartPlaybackTimeout,
+    SystemTimeError(time::SystemTimeError),
     VPDParseFailed(String, ParseIntError),
     WorkerPanics,
 }
@@ -67,6 +72,7 @@
             CrasClientFailed(e) => write!(f, "failed to create cras client: {}", e),
             DeserializationFailed(file, e) => write!(f, "failed to parse {}: {}", file, e),
             FileIOFailed(file, e) => write!(f, "{}: {}", file, e),
+            InvalidShutDownTime => write!(f, "invalid shutdown time"),
             InternalSpeakerNotFound => write!(f, "internal speaker is not found in cras"),
             InvalidTemperature(temp) => write!(
                 f,
@@ -74,18 +80,21 @@
                 temp
             ),
             InvalidDatastore => write!(f, "invalid datastore format"),
+            HotSpeaker => write!(f, "skip boot time calibration as the speakers may be hot"),
             LargeCalibrationDiff(rdc, temp) => write!(
                 f,
                 "calibration difference is too large, rdc: {}, temp: {}",
                 rdc, temp
             ),
-            MutexPoisonError => write!(f, "mutex is poisoned"),
             MissingDSMParam => write!(f, "missing dsm_param.bin"),
+            MutexPoisonError => write!(f, "mutex is poisoned"),
             NewPlayStreamFailed(e) => write!(f, "{}", e),
             NextPlaybackBufferFailed(e) => write!(f, "{}", e),
             PlaybackFailed(e) => write!(f, "{}", e),
+            ReadTimestampFailed(e) => write!(f, "{}", e),
             SerializationFailed(e) => write!(f, "failed to serialize yaml: {}", e),
             StartPlaybackTimeout => write!(f, "playback is not started in time"),
+            SystemTimeError(e) => write!(f, "{}", e),
             VPDParseFailed(file, e) => write!(f, "failed to parse vpd {}: {}", file, e),
             WorkerPanics => write!(f, "run_play_zero_worker panics"),
         }
diff --git a/sound_card_init/max98390d/src/lib.rs b/sound_card_init/max98390d/src/lib.rs
index 7e445c5..44b1c61 100644
--- a/sound_card_init/max98390d/src/lib.rs
+++ b/sound_card_init/max98390d/src/lib.rs
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //! `max98390d` crate implements the required initialization workflows.
-//! It currently supports boot time calibration for amplifiers.
+//! It currently supports boot time calibration for max98390d.
 #![deny(missing_docs)]
 mod amp_calibration;
 mod datastore;
@@ -10,15 +10,20 @@
 mod settings;
 mod vpd;
 
-use std::path::Path;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
 
 use cros_alsa::Card;
 use sys_util::error;
+use utils::{run_time, shutdown_time, DATASTORE_DIR};
 
 use crate::amp_calibration::{AmpCalibration, VolumeMode};
 use crate::error::{Error, Result};
 use crate::settings::DeviceSettings;
 
+const SPEAKER_COOL_DOWN_TIME: Duration = Duration::from_secs(180);
+
 /// Performs max98390d boot time calibration.
 ///
 ///
@@ -35,20 +40,27 @@
     let mut card = Card::new(snd_card)?;
 
     if !Path::new(&settings.dsm_param).exists() {
-        for s in &settings.amp_calibrations {
-            let mut amp_calib = match AmpCalibration::new(&mut card, s.clone()) {
-                Ok(amp) => amp,
-                Err(e) => {
-                    error!("{}.", e);
-                    continue;
-                }
-            };
-            if let Err(e) = amp_calib.set_volume(VolumeMode::Low) {
-                error!("failed to set volume to low: {}.", e);
-            }
-        }
+        set_all_volume_low(&mut card, &settings);
         return Err(Error::MissingDSMParam);
     }
+
+    // Needs to check whether the speakers are over heated if it is not the first time boot.
+    if run_time::exists(snd_card) {
+        if let Err(err) = check_speaker_over_heated(snd_card, SPEAKER_COOL_DOWN_TIME) {
+            match err {
+                Error::HotSpeaker => run_all_hot_speaker_workflow(&mut card, &settings),
+                _ => {
+                    // We cannot assume the speakers are not replaced or not over heated
+                    // when the shutdown time file is invalid; therefore we can not use the datastore
+                    // value anymore and we can not trigger boot time calibration.
+                    del_all_datastore(snd_card, &settings);
+                    set_all_volume_low(&mut card, &settings);
+                }
+            };
+            return Err(err);
+        };
+    }
+
     // If some error occurs during the calibration, the iteration will continue running the
     // calibration for the next amp.
     let results: Vec<Result<()>> = settings
@@ -74,3 +86,68 @@
 
     Ok(())
 }
+
+fn del_all_datastore(snd_card: &str, settings: &DeviceSettings) {
+    for s in &settings.amp_calibrations {
+        if let Err(e) = fs::remove_file(
+            PathBuf::from(DATASTORE_DIR)
+                .join(snd_card)
+                .join(&s.calib_file),
+        ) {
+            error!("failed to remove datastore: {}.", e);
+        }
+    }
+}
+
+fn run_all_hot_speaker_workflow(card: &mut Card, settings: &DeviceSettings) {
+    for s in &settings.amp_calibrations {
+        let mut amp_calib = match AmpCalibration::new(card, s.clone()) {
+            Ok(amp) => amp,
+            Err(e) => {
+                error!("{}.", e);
+                continue;
+            }
+        };
+        if let Err(e) = amp_calib.hot_speaker_workflow() {
+            error!("failed to run hot_speaker_workflow: {}.", e);
+        }
+    }
+}
+
+fn set_all_volume_low(card: &mut Card, settings: &DeviceSettings) {
+    for s in &settings.amp_calibrations {
+        let mut amp_calib = match AmpCalibration::new(card, s.clone()) {
+            Ok(amp) => amp,
+            Err(e) => {
+                error!("{}.", e);
+                continue;
+            }
+        };
+        if let Err(e) = amp_calib.set_volume(VolumeMode::Low) {
+            error!("failed to set volume to low: {}.", e);
+        }
+    }
+}
+
+// If (Current time - the latest CRAS shutdown time) < cool_down_time, we assume that
+// the speakers may be over heated.
+fn check_speaker_over_heated(snd_card: &str, cool_down_time: Duration) -> Result<()> {
+    let last_run = run_time::from_file(snd_card).map_err(Error::ReadTimestampFailed)?;
+    let last_shutdown = shutdown_time::from_file().map_err(Error::ReadTimestampFailed)?;
+    if last_shutdown < last_run {
+        return Err(Error::InvalidShutDownTime);
+    }
+
+    let now = SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .map_err(Error::SystemTimeError)?;
+
+    let elapsed = now
+        .checked_sub(last_shutdown)
+        .ok_or(Error::InvalidShutDownTime)?;
+
+    if elapsed < cool_down_time {
+        return Err(Error::HotSpeaker);
+    }
+    Ok(())
+}
diff --git a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
index bddf6ea..b1bcd6b 100644
--- a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
+++ b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
@@ -34,6 +34,7 @@
 recvfrom: 1
 set_robust_list: 1
 umask: 1
+unlink: 1
 sendto: 1
 setgroups: 1
 connect: 1
diff --git a/sound_card_init/sound_card_init.conf b/sound_card_init/sound_card_init.conf
index 7ad1a80..7ab0211 100644
--- a/sound_card_init/sound_card_init.conf
+++ b/sound_card_init/sound_card_init.conf
@@ -48,8 +48,8 @@
 # -k: empty /sys tmpfs path.
 # -b: need /sys/firmware/vpd/ro/ access to read the default calibration value in vpd.
 # -k: get a writeable and empty /var tmpfs path.
-# -b: need /var/lib/sound_card_init/$SOUND_CARD_ID writable
-#     access for datastore update.
+# -b: need /var/lib/sound_card_init/$SOUND_CARD_ID writable access for datastore update.
+# -b: need /var/lib/cras readable
 exec minijail0 \
     --uts \
     -e \
@@ -68,6 +68,7 @@
     -b /sys/firmware/vpd/ro/ \
     -k 'tmpfs,/var,tmpfs,MS_NODEV|MS_NOEXEC|MS_NOSUID,mode=755,size=10M' \
     -b /var/lib/sound_card_init/"${SOUND_CARD_ID}"/,,1 \
+    -b /var/lib/cras/ \
     -u sound_card_init -g sound_card_init -G \
     -S /usr/share/policy/sound_card_init-seccomp.policy \
     /usr/bin/sound_card_init "--id=${SOUND_CARD_ID}"
diff --git a/sound_card_init/src/main.rs b/sound_card_init/src/main.rs
index addde2d..3f49ed9 100644
--- a/sound_card_init/src/main.rs
+++ b/sound_card_init/src/main.rs
@@ -25,6 +25,7 @@
 use sys_util::{error, info, syslog};
 
 use max98390d::run_max98390d;
+use utils::run_time;
 
 type Result<T> = std::result::Result<T, Error>;
 const CONF_DIR: &str = "/etc/sound_card_init";
@@ -99,24 +100,35 @@
 }
 
 /// Parses the CONF_DIR/<sound_card_id>.yaml and starts sound card initialization.
-fn sound_card_init() -> std::result::Result<(), Box<dyn error::Error>> {
-    let args = parse_args()?;
+fn sound_card_init(args: &Args) -> std::result::Result<(), Box<dyn error::Error>> {
     info!("sound_card_id: {}", args.sound_card_id);
-    let conf = get_config(&args)?;
+    let conf = get_config(args)?;
 
     match args.sound_card_id.as_str() {
         "sofcmlmax98390d" => {
             run_max98390d(&args.sound_card_id, &conf)?;
             info!("run_max98390d() finished successfully.");
+            Ok(())
         }
-        _ => return Err(Error::UnsupportedSoundCard(args.sound_card_id).into()),
-    };
-    Ok(())
+        _ => Err(Error::UnsupportedSoundCard(args.sound_card_id.clone()).into()),
+    }
 }
 
 fn main() {
     syslog::init().expect("failed to initialize syslog");
-    if let Err(e) = sound_card_init() {
+    let args = match parse_args() {
+        Ok(args) => args,
+        Err(e) => {
+            error!("failed to parse arguments: {}", e);
+            return;
+        }
+    };
+
+    if let Err(e) = sound_card_init(&args) {
         error!("sound_card_init: {}", e);
     }
+
+    if let Err(e) = run_time::now_to_file(&args.sound_card_id) {
+        error!("failed to create sound_card_init run time file: {}", e);
+    }
 }
diff --git a/sound_card_init/utils/Cargo.lock b/sound_card_init/utils/Cargo.lock
new file mode 100644
index 0000000..fd53fb0
--- /dev/null
+++ b/sound_card_init/utils/Cargo.lock
@@ -0,0 +1,109 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "dtoa"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "remain"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.116"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.116"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.8.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
+dependencies = [
+ "dtoa",
+ "linked-hash-map",
+ "serde",
+ "yaml-rust",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+
+[[package]]
+name = "utils"
+version = "0.1.0"
+dependencies = [
+ "remain",
+ "serde",
+ "serde_yaml",
+]
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
+dependencies = [
+ "linked-hash-map",
+]
diff --git a/sound_card_init/utils/Cargo.toml b/sound_card_init/utils/Cargo.toml
new file mode 100644
index 0000000..8c688de
--- /dev/null
+++ b/sound_card_init/utils/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "utils"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+edition = "2018"
+description = "Utils for sound_card_init"
+
+[dependencies]
+remain = "0.2.1"
+serde = { version = "1.0", features = ["derive"]}
+serde_yaml = "0.8.11"
diff --git a/sound_card_init/utils/src/error.rs b/sound_card_init/utils/src/error.rs
new file mode 100644
index 0000000..93d53b9
--- /dev/null
+++ b/sound_card_init/utils/src/error.rs
@@ -0,0 +1,39 @@
+// Copyright 2020 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.
+
+//! This mod contains all possible errors that can occur within utils.
+use std::fmt;
+use std::io;
+use std::time;
+use std::{error, path::PathBuf};
+
+use remain::sorted;
+
+/// Alias for a `Result` with the error type `utils::Error`.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// This type represents all possible errors that can occur within utils.
+#[sorted]
+#[derive(Debug)]
+pub enum Error {
+    /// It wraps file path with the io::Error.
+    FileIOFailed(PathBuf, io::Error),
+    /// It wraps file path with the serde_yaml::Error.
+    SerdeError(PathBuf, serde_yaml::Error),
+    /// It wraps time::SystemTimeError.
+    SystemTimeError(time::SystemTimeError),
+}
+
+impl error::Error for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use Error::*;
+        match self {
+            FileIOFailed(file, e) => write!(f, "{:?}: {}", file, e),
+            SerdeError(file, e) => write!(f, "{:?}: {}", file, e),
+            SystemTimeError(e) => write!(f, "{}", e),
+        }
+    }
+}
diff --git a/sound_card_init/utils/src/lib.rs b/sound_card_init/utils/src/lib.rs
new file mode 100644
index 0000000..e8edce7
--- /dev/null
+++ b/sound_card_init/utils/src/lib.rs
@@ -0,0 +1,87 @@
+// Copyright 2020 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.
+//! It contains common utils shared within sound_card_init.
+#![deny(missing_docs)]
+
+//! The error definitions for utils.
+pub mod error;
+
+use std::fs::File;
+use std::io::{prelude::*, BufReader, BufWriter};
+use std::path::PathBuf;
+use std::time::Duration;
+
+use crate::error::{Error, Result};
+
+/// The path of datastore.
+pub const DATASTORE_DIR: &str = "/var/lib/sound_card_init";
+
+fn duration_from_file(path: &PathBuf) -> Result<Duration> {
+    let reader =
+        BufReader::new(File::open(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?);
+    serde_yaml::from_reader(reader).map_err(|e| Error::SerdeError(path.clone(), e))
+}
+
+/// The utils to parse CRAS shutdown time file.
+pub mod shutdown_time {
+    use super::*;
+    // The path of CRAS shutdown time file.
+    const SHUTDOWN_TIME_FILE: &str = "/var/lib/cras/stop";
+
+    /// Reads the unix time from CRAS shutdown time file.
+    pub fn from_file() -> Result<Duration> {
+        duration_from_file(&PathBuf::from(SHUTDOWN_TIME_FILE))
+    }
+}
+
+/// The utils to create and parse sound_card_init run time file.
+pub mod run_time {
+    use std::time::SystemTime;
+
+    use super::*;
+    // The filename of sound_card_init run time file.
+    const RUN_TIME_FILE: &str = "run";
+
+    /// Returns the sound_card_init run time file existence.
+    pub fn exists(snd_card: &str) -> bool {
+        run_time_file(snd_card).exists()
+    }
+
+    /// Reads the unix time from sound_card_init run time file.
+    pub fn from_file(snd_card: &str) -> Result<Duration> {
+        duration_from_file(&run_time_file(snd_card))
+    }
+
+    /// Saves the current unix time to sound_card_init run time file.
+    pub fn now_to_file(snd_card: &str) -> Result<()> {
+        match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
+            Ok(t) => to_file(snd_card, t),
+            Err(e) => Err(Error::SystemTimeError(e)),
+        }
+    }
+
+    /// Saves the unix time to sound_card_init run time file.
+    pub fn to_file(snd_card: &str, duration: Duration) -> Result<()> {
+        let path = run_time_file(snd_card);
+        let mut writer =
+            BufWriter::new(File::create(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?);
+        writer
+            .write_all(
+                serde_yaml::to_string(&duration)
+                    .map_err(|e| Error::SerdeError(path.clone(), e))?
+                    .as_bytes(),
+            )
+            .map_err(|e| Error::FileIOFailed(path.clone(), e))?;
+        writer
+            .flush()
+            .map_err(|e| Error::FileIOFailed(path.clone(), e))?;
+        Ok(())
+    }
+
+    fn run_time_file(snd_card: &str) -> PathBuf {
+        PathBuf::from(DATASTORE_DIR)
+            .join(snd_card)
+            .join(RUN_TIME_FILE)
+    }
+}
diff --git a/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf b/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf
new file mode 100644
index 0000000..126ac33
--- /dev/null
+++ b/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf
@@ -0,0 +1,6 @@
+Comment "Logitech Webcam C505"
+
+SectionUseCase."HiFi" {
+	File "HiFi.conf"
+	Comment "Default"
+}
diff --git a/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf b/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf
new file mode 100644
index 0000000..c7e1342
--- /dev/null
+++ b/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf
@@ -0,0 +1,25 @@
+SectionVerb {
+	Value {
+		FullySpecifiedUCM "1"
+	}
+
+	EnableSequence [
+		cdev "hw:Webcam"
+		cset "name='Mic Capture Volume' 0"
+	]
+
+	DisableSequence [
+	]
+}
+
+SectionDevice."Webcam" {
+	Value {
+		CapturePCM "hw:Webcam,0"
+	}
+
+	EnableSequence [
+	]
+
+	DisableSequence [
+	]
+}
diff --git a/ucm-config/for_all_boards/Chat 150 C/HiFi.conf b/ucm-config/for_all_boards/Chat 150 C/HiFi.conf
index 5a73702..368796d 100644
--- a/ucm-config/for_all_boards/Chat 150 C/HiFi.conf
+++ b/ucm-config/for_all_boards/Chat 150 C/HiFi.conf
@@ -10,7 +10,7 @@
 	]
 }
 
-SectionDevice."Dummy".0 {
+SectionDevice."Chat 150 C".0 {
 	EnableSequence [
 	]
 
diff --git a/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf b/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf
index 5a73702..313bffe 100644
--- a/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf
+++ b/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf
@@ -10,7 +10,7 @@
 	]
 }
 
-SectionDevice."Dummy".0 {
+SectionDevice."Jabra Speak 810".0 {
 	EnableSequence [
 	]
 
diff --git a/unblocked_terms.txt b/unblocked_terms.txt
new file mode 100644
index 0000000..674e3d3
--- /dev/null
+++ b/unblocked_terms.txt
@@ -0,0 +1,11 @@
+# KEEP THIS COMMENT IN YOUR COPY.
+#
+# Don't delete this file if you want to keep keyword_check enabled even if it's
+# empty.
+#
+# See repohooks/README.md for more details.
+
+dummy
+master
+\bnative
+white.?list