Merge remote-tracking branch 'aosp/upstream-master' into HEAD am: 4e8152bdc5

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

Change-Id: Ifcf05716bae476431b7e7e451bee424399e7beda
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..f8818dc
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,65 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+name: "CodeQL"
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [master]
+  schedule:
+    - cron: '0 11 * * 1'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+      matrix:
+        # Override automatic language detection by changing the below list
+        # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
+        language: ['cpp']
+        # Learn more...
+        # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+      with:
+        # We must fetch at least the immediate parents so that if this is
+        # a pull request then we can checkout the head.
+        fetch-depth: 2
+
+    # If this run was triggered by a pull request event, then checkout
+    # the head of the pull request instead of the merge commit.
+    - run: git checkout HEAD^2
+      if: ${{ github.event_name == 'pull_request' }}
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      with:
+        languages: ${{ matrix.language }}
+
+    # Command-line programs to run using the OS shell.
+    # https://git.io/JvXDl
+
+    - name: Install cras deps
+      working-directory: ./cras
+      run: sudo ./install_deps.sh
+
+    - name: Build
+      working-directory: ./cras
+      run: |
+        ./git_prepare.sh
+        ./configure
+        make
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
index c0e6ae9..e85e5dc 100644
--- a/PRESUBMIT.cfg
+++ b/PRESUBMIT.cfg
@@ -1,6 +1,7 @@
 [Hook Overrides]
 tab_check: false
 clang_format_check: true
+cargo_clippy_check: true
 
 # On by default, but required for options below.
 cros_license_check: true
@@ -10,3 +11,9 @@
 cros_license_check: --exclude_regex=HiFi\.conf$
 clang_format_check:
   cras/
+cargo_clippy_check:
+  --project=audio_streams
+  --project=cras/client/cras-sys
+  --project=cras/client/cras_tests
+  --project=cras/client/libcras
+  --project=cras/src/server/rust
diff --git a/audio_streams/src/audio_streams.rs b/audio_streams/src/audio_streams.rs
index b83cc8c..3cb1173 100644
--- a/audio_streams/src/audio_streams.rs
+++ b/audio_streams/src/audio_streams.rs
@@ -134,22 +134,24 @@
 pub trait StreamSource: Send {
     /// Returns a stream control and buffer generator object. These are separate as the buffer
     /// generator might want to be passed to the audio stream.
+    #[allow(clippy::type_complexity)]
     fn new_playback_stream(
         &mut self,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>;
 
     /// 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`.
+    #[allow(clippy::type_complexity)]
     fn new_capture_stream(
         &mut self,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> Result<
         (
@@ -318,7 +320,7 @@
     pub fn new(
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> Self {
         let frame_size = format.sample_bytes() * num_channels;
@@ -378,11 +380,12 @@
 }
 
 impl StreamSource for DummyStreamSource {
+    #[allow(clippy::type_complexity)]
     fn new_playback_stream(
         &mut self,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
         Ok((
@@ -426,7 +429,7 @@
             const FRAME_SIZE: usize = 4;
             let mut buf = [0u8; 480 * FRAME_SIZE];
             let mut pb_buf = PlaybackBuffer::new(FRAME_SIZE, &mut buf, &mut test_drop).unwrap();
-            pb_buf.write(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();
+            pb_buf.write_all(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();
         }
         assert_eq!(test_drop.frame_count, 480);
     }
diff --git a/audio_streams/src/capture.rs b/audio_streams/src/capture.rs
index a1313b1..3f2de3f 100644
--- a/audio_streams/src/capture.rs
+++ b/audio_streams/src/capture.rs
@@ -136,7 +136,7 @@
     pub fn new(
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> Self {
         let frame_size = format.sample_bytes() * num_channels;
@@ -233,8 +233,8 @@
             let mut stream_buffer = stream.next_capture_buffer().unwrap();
             let mut cp_buf = [0xa5u8; 480 * 2 * 2];
             assert_eq!(stream_buffer.read(&mut cp_buf).unwrap(), 480 * 2 * 2);
-            for i in 0..cp_buf.len() {
-                assert_eq!(cp_buf[i], 0, "Read samples should all be zeros.");
+            for buf in cp_buf.iter() {
+                assert_eq!(*buf, 0, "Read samples should all be zeros.");
             }
         }
         // The second call should block until the first buffer is consumed.
diff --git a/audio_streams/src/shm_streams.rs b/audio_streams/src/shm_streams.rs
index 8597896..13475cd 100644
--- a/audio_streams/src/shm_streams.rs
+++ b/audio_streams/src/shm_streams.rs
@@ -129,6 +129,9 @@
     /// Get the number of channels of audio data for this stream.
     fn num_channels(&self) -> usize;
 
+    /// Get the frame rate of audio data for this stream.
+    fn frame_rate(&self) -> u32;
+
     /// Waits until the next server message indicating action is required.
     ///
     /// For playback streams, this will be `AUDIO_MESSAGE_REQUEST_DATA`, meaning
@@ -189,12 +192,13 @@
     /// # Errors
     ///
     /// * If sending the connect stream message to the server fails.
+    #[allow(clippy::too_many_arguments)]
     fn new_stream(
         &mut self,
         direction: StreamDirection,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
         effects: &[StreamEffect],
         client_shm: &SharedMemory,
@@ -214,6 +218,7 @@
 /// Class that implements ShmStream trait but does nothing with the samples
 pub struct NullShmStream {
     num_channels: usize,
+    frame_rate: u32,
     buffer_size: usize,
     frame_size: usize,
     interval: Duration,
@@ -223,16 +228,17 @@
 
 impl NullShmStream {
     /// Attempt to create a new NullShmStream with the given number of channels,
-    /// format, and buffer_size.
+    /// format, frame_rate, and buffer_size.
     pub fn new(
         buffer_size: usize,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
     ) -> Self {
         let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
         Self {
             num_channels,
+            frame_rate,
             buffer_size,
             frame_size: format.sample_bytes() * num_channels,
             interval,
@@ -261,6 +267,10 @@
         self.num_channels
     }
 
+    fn frame_rate(&self) -> u32 {
+        self.frame_rate
+    }
+
     fn wait_for_next_action_with_timeout<'a>(
         &'a mut self,
         timeout: Duration,
@@ -295,7 +305,7 @@
         _direction: StreamDirection,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
         _effects: &[StreamEffect],
         _client_shm: &SharedMemory,
@@ -309,6 +319,7 @@
 #[derive(Clone)]
 pub struct MockShmStream {
     num_channels: usize,
+    frame_rate: u32,
     request_size: usize,
     frame_size: usize,
     request_notifier: Arc<(Mutex<bool>, Condvar)>,
@@ -316,10 +327,16 @@
 
 impl MockShmStream {
     /// Attempt to create a new MockShmStream with the given number of
-    /// channels, format, and buffer_size.
-    pub fn new(num_channels: usize, format: SampleFormat, buffer_size: usize) -> Self {
+    /// channels, frame_rate, format, and buffer_size.
+    pub fn new(
+        num_channels: usize,
+        frame_rate: u32,
+        format: SampleFormat,
+        buffer_size: usize,
+    ) -> Self {
         Self {
             num_channels,
+            frame_rate,
             request_size: buffer_size,
             frame_size: format.sample_bytes() * num_channels,
             request_notifier: Arc::new((Mutex::new(false), Condvar::new())),
@@ -346,7 +363,7 @@
             }
         }
 
-        return true;
+        true
     }
 
     fn notify_request(&mut self) {
@@ -378,6 +395,10 @@
         self.num_channels
     }
 
+    fn frame_rate(&self) -> u32 {
+        self.frame_rate
+    }
+
     fn wait_for_next_action_with_timeout<'a>(
         &'a mut self,
         timeout: Duration,
@@ -429,7 +450,7 @@
         _direction: StreamDirection,
         num_channels: usize,
         format: SampleFormat,
-        _frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
         _effects: &[StreamEffect],
         _client_shm: &SharedMemory,
@@ -438,7 +459,7 @@
         let &(ref last_stream, ref cvar) = &*self.last_stream;
         let mut stream = last_stream.lock();
 
-        let new_stream = MockShmStream::new(num_channels, format, buffer_size);
+        let new_stream = MockShmStream::new(num_channels, frame_rate, format, buffer_size);
         *stream = Some(new_stream.clone());
         cvar.notify_one();
         Ok(Box::new(new_stream))
@@ -476,7 +497,7 @@
             let request = stream
                 .wait_for_next_action_with_timeout(Duration::from_secs(5))
                 .expect("Failed to wait for next action");
-            let result = match request {
+            match request {
                 Some(r) => {
                     let requested = r.requested_frames();
                     r.set_buffer_offset_and_frames(872, requested)
@@ -484,8 +505,7 @@
                     requested
                 }
                 None => 0,
-            };
-            result
+            }
         });
 
         let mut stream = stream_source.get_last_stream();
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index 26cfcad..243107a 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -33,7 +33,8 @@
 		void SetInputNodeGain(uint64 node_id, int32 gain)
 
 			Sets the capture gain of the node. gain is a 0-100
-			integer which linearly maps to range [-40dB, 40dB].
+			integer which linearly maps [0, 50] to range [-40dB, 0dB]
+			and [50, 100] to [0dB, 20dB],
 			Default gain value is 50, which is 0dB.
 
 		void SetInputMute(boolean mute_on)
@@ -104,9 +105,6 @@
 				uint64 PluggedTime
 					The time that this device was plugged
 					in. This value is in microseconds.
-				string MicPositions
-					The string formed by floating numbers
-					describing the position of mic array.
 				unit64 NodeVolume
 					The node volume indexed from 0 to 100.
 				unit64 NodeCaptureGain
diff --git a/cras/client/cras-sys/src/gen.rs b/cras/client/cras-sys/src/gen.rs
index 611f8b6..129a91a 100644
--- a/cras/client/cras-sys/src/gen.rs
+++ b/cras/client/cras-sys/src/gen.rs
@@ -31,7 +31,7 @@
 pub const CRAS_SERV_MAX_MSG_SIZE: u32 = 256;
 pub const CRAS_CLIENT_MAX_MSG_SIZE: u32 = 256;
 pub const CRAS_MAX_HOTWORD_MODELS: u32 = 243;
-pub const CRAS_MAX_REMIX_CHANNELS: u32 = 32;
+pub const CRAS_MAX_REMIX_CHANNELS: u32 = 8;
 pub const CRAS_MAX_TEST_DATA_LEN: u32 = 224;
 pub const CRAS_AEC_DUMP_FILE_NAME_LEN: u32 = 128;
 pub const CRAS_NUM_SHM_BUFFERS: u32 = 2;
@@ -50,12 +50,13 @@
     pub idx: u32,
     pub name: [::std::os::raw::c_char; 64usize],
     pub stable_id: u32,
+    pub max_supported_channels: u32,
 }
 #[test]
 fn bindgen_test_layout_cras_iodev_info() {
     assert_eq!(
         ::std::mem::size_of::<cras_iodev_info>(),
-        72usize,
+        76usize,
         concat!("Size of: ", stringify!(cras_iodev_info))
     );
     assert_eq!(
@@ -93,6 +94,18 @@
             stringify!(stable_id)
         )
     );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<cras_iodev_info>())).max_supported_channels as *const _ as usize
+        },
+        72usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(cras_iodev_info),
+            "::",
+            stringify!(max_supported_channels)
+        )
+    );
 }
 #[repr(C, packed)]
 #[derive(Copy, Clone)]
@@ -108,7 +121,6 @@
     pub left_right_swapped: i32,
     pub type_enum: u32,
     pub stable_id: u32,
-    pub mic_positions: [::std::os::raw::c_char; 128usize],
     pub type_: [::std::os::raw::c_char; 32usize],
     pub name: [::std::os::raw::c_char; 64usize],
     pub active_hotword_model: [::std::os::raw::c_char; 16usize],
@@ -160,7 +172,7 @@
 fn bindgen_test_layout_cras_ionode_info() {
     assert_eq!(
         ::std::mem::size_of::<cras_ionode_info>(),
-        296usize,
+        168usize,
         concat!("Size of: ", stringify!(cras_ionode_info))
     );
     assert_eq!(
@@ -281,18 +293,8 @@
         )
     );
     assert_eq!(
-        unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).mic_positions as *const _ as usize },
-        56usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(cras_ionode_info),
-            "::",
-            stringify!(mic_positions)
-        )
-    );
-    assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).type_ as *const _ as usize },
-        184usize,
+        56usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_ionode_info),
@@ -302,7 +304,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).name as *const _ as usize },
-        216usize,
+        88usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_ionode_info),
@@ -314,7 +316,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_ionode_info>())).active_hotword_model as *const _ as usize
         },
-        280usize,
+        152usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_ionode_info),
@@ -719,6 +721,7 @@
     CRAS_CLIENT_TYPE_ARC = 5,
     CRAS_CLIENT_TYPE_CROSVM = 6,
     CRAS_CLIENT_TYPE_SERVER_STREAM = 7,
+    CRAS_CLIENT_TYPE_LACROS = 8,
 }
 impl CRAS_STREAM_EFFECT {
     pub const APM_ECHO_CANCELLATION: CRAS_STREAM_EFFECT = CRAS_STREAM_EFFECT(1);
@@ -841,13 +844,14 @@
     BT_HFP_NEW_CONNECTION = 12,
     BT_HFP_REQUEST_DISCONNECT = 13,
     BT_HFP_SUPPORTED_FEATURES = 14,
-    BT_HSP_NEW_CONNECTION = 15,
-    BT_HSP_REQUEST_DISCONNECT = 16,
-    BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 17,
-    BT_RESET = 18,
-    BT_SCO_CONNECT = 19,
-    BT_TRANSPORT_ACQUIRE = 20,
-    BT_TRANSPORT_RELEASE = 21,
+    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,
 }
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
@@ -1580,6 +1584,145 @@
 }
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
+pub struct main_thread_event {
+    pub tag_sec: u32,
+    pub nsec: u32,
+    pub data1: u32,
+    pub data2: u32,
+}
+#[test]
+fn bindgen_test_layout_main_thread_event() {
+    assert_eq!(
+        ::std::mem::size_of::<main_thread_event>(),
+        16usize,
+        concat!("Size of: ", stringify!(main_thread_event))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<main_thread_event>(),
+        1usize,
+        concat!("Alignment of ", stringify!(main_thread_event))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event>())).tag_sec as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event),
+            "::",
+            stringify!(tag_sec)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event>())).nsec as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event),
+            "::",
+            stringify!(nsec)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event>())).data1 as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event),
+            "::",
+            stringify!(data1)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event>())).data2 as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event),
+            "::",
+            stringify!(data2)
+        )
+    );
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct main_thread_event_log {
+    pub write_pos: u32,
+    pub len: u32,
+    pub log: [main_thread_event; 1024usize],
+}
+#[test]
+fn bindgen_test_layout_main_thread_event_log() {
+    assert_eq!(
+        ::std::mem::size_of::<main_thread_event_log>(),
+        16392usize,
+        concat!("Size of: ", stringify!(main_thread_event_log))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<main_thread_event_log>(),
+        1usize,
+        concat!("Alignment of ", stringify!(main_thread_event_log))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).write_pos as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event_log),
+            "::",
+            stringify!(write_pos)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).len as *const _ as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event_log),
+            "::",
+            stringify!(len)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).log as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_event_log),
+            "::",
+            stringify!(log)
+        )
+    );
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct main_thread_debug_info {
+    pub main_log: main_thread_event_log,
+}
+#[test]
+fn bindgen_test_layout_main_thread_debug_info() {
+    assert_eq!(
+        ::std::mem::size_of::<main_thread_debug_info>(),
+        16392usize,
+        concat!("Size of: ", stringify!(main_thread_debug_info))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<main_thread_debug_info>(),
+        1usize,
+        concat!("Alignment of ", stringify!(main_thread_debug_info))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<main_thread_debug_info>())).main_log as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(main_thread_debug_info),
+            "::",
+            stringify!(main_log)
+        )
+    );
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy, Clone)]
 pub struct cras_bt_event {
     pub tag_sec: u32,
     pub nsec: u32,
@@ -1720,12 +1863,15 @@
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum CRAS_AUDIO_THREAD_EVENT_TYPE {
-    AUDIO_THREAD_EVENT_BUSYLOOP = 0,
-    AUDIO_THREAD_EVENT_DEBUG = 1,
-    AUDIO_THREAD_EVENT_SEVERE_UNDERRUN = 2,
-    AUDIO_THREAD_EVENT_UNDERRUN = 3,
-    AUDIO_THREAD_EVENT_DROP_SAMPLES = 4,
-    AUDIO_THREAD_EVENT_TYPE_COUNT = 5,
+    AUDIO_THREAD_EVENT_A2DP_OVERRUN = 0,
+    AUDIO_THREAD_EVENT_A2DP_THROTTLE = 1,
+    AUDIO_THREAD_EVENT_BUSYLOOP = 2,
+    AUDIO_THREAD_EVENT_DEBUG = 3,
+    AUDIO_THREAD_EVENT_SEVERE_UNDERRUN = 4,
+    AUDIO_THREAD_EVENT_UNDERRUN = 5,
+    AUDIO_THREAD_EVENT_DROP_SAMPLES = 6,
+    AUDIO_THREAD_EVENT_DEV_OVERRUN = 7,
+    AUDIO_THREAD_EVENT_TYPE_COUNT = 8,
 }
 #[repr(C, packed)]
 #[derive(Copy, Clone)]
@@ -1867,12 +2013,13 @@
     pub snapshot_buffer: cras_audio_thread_snapshot_buffer,
     pub bt_debug_info: cras_bt_debug_info,
     pub bt_wbs_enabled: 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>(),
-        1398664usize,
+        1410096usize,
         concat!("Size of: ", stringify!(cras_server_state))
     );
     assert_eq!(
@@ -2044,7 +2191,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).input_devs as *const _ as usize },
-        1496usize,
+        1576usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2056,7 +2203,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).num_output_nodes as *const _ as usize
         },
-        2936usize,
+        3096usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2068,7 +2215,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).num_input_nodes as *const _ as usize
         },
-        2940usize,
+        3100usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2078,7 +2225,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).output_nodes as *const _ as usize },
-        2944usize,
+        3104usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2088,7 +2235,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).input_nodes as *const _ as usize },
-        8864usize,
+        6464usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2100,7 +2247,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).num_attached_clients as *const _ as usize
         },
-        14784usize,
+        9824usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2110,7 +2257,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).client_info as *const _ as usize },
-        14788usize,
+        9828usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2120,7 +2267,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).update_count as *const _ as usize },
-        15108usize,
+        10148usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2132,7 +2279,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).num_active_streams as *const _ as usize
         },
-        15112usize,
+        10152usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2145,7 +2292,7 @@
             &(*(::std::ptr::null::<cras_server_state>())).last_active_stream_time as *const _
                 as usize
         },
-        15128usize,
+        10168usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2157,7 +2304,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).audio_debug_info as *const _ as usize
         },
-        15144usize,
+        10184usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2170,7 +2317,7 @@
             &(*(::std::ptr::null::<cras_server_state>())).default_output_buffer_size as *const _
                 as usize
         },
-        139408usize,
+        134448usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2182,7 +2329,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).non_empty_status as *const _ as usize
         },
-        139412usize,
+        134452usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2192,7 +2339,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).aec_supported as *const _ as usize },
-        139416usize,
+        134456usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2202,7 +2349,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).aec_group_id as *const _ as usize },
-        139420usize,
+        134460usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2214,7 +2361,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).snapshot_buffer as *const _ as usize
         },
-        139424usize,
+        134464usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2224,7 +2371,7 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<cras_server_state>())).bt_debug_info as *const _ as usize },
-        1382268usize,
+        1377308usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2236,7 +2383,7 @@
         unsafe {
             &(*(::std::ptr::null::<cras_server_state>())).bt_wbs_enabled as *const _ as usize
         },
-        1398660usize,
+        1393700usize,
         concat!(
             "Offset of field: ",
             stringify!(cras_server_state),
@@ -2244,6 +2391,19 @@
             stringify!(bt_wbs_enabled)
         )
     );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<cras_server_state>())).main_thread_debug_info as *const _
+                as usize
+        },
+        1393704usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(cras_server_state),
+            "::",
+            stringify!(main_thread_debug_info)
+        )
+    );
 }
 pub const cras_notify_device_action_CRAS_DEVICE_ACTION_ADD: cras_notify_device_action = 0;
 pub const cras_notify_device_action_CRAS_DEVICE_ACTION_REMOVE: cras_notify_device_action = 1;
@@ -2366,6 +2526,7 @@
     CRAS_NODE_TYPE_FALLBACK_ABNORMAL = 13,
     CRAS_NODE_TYPE_UNKNOWN = 14,
     CRAS_NODE_TYPE_ECHO_REFERENCE = 15,
+    CRAS_NODE_TYPE_ALSA_LOOPBACK = 16,
 }
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -2410,6 +2571,7 @@
     CRAS_SERVER_DUMP_BT = 28,
     CRAS_SERVER_SET_BT_WBS_ENABLED = 29,
     CRAS_SERVER_GET_ATLOG_FD = 30,
+    CRAS_SERVER_DUMP_MAIN = 31,
 }
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -3378,6 +3540,34 @@
 }
 #[repr(C, packed)]
 #[derive(Debug, Copy, Clone)]
+pub struct cras_dump_main {
+    pub header: cras_server_message,
+}
+#[test]
+fn bindgen_test_layout_cras_dump_main() {
+    assert_eq!(
+        ::std::mem::size_of::<cras_dump_main>(),
+        8usize,
+        concat!("Size of: ", stringify!(cras_dump_main))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<cras_dump_main>(),
+        1usize,
+        concat!("Alignment of ", stringify!(cras_dump_main))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<cras_dump_main>())).header as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(cras_dump_main),
+            "::",
+            stringify!(header)
+        )
+    );
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy, Clone)]
 pub struct cras_dump_bt {
     pub header: cras_server_message,
 }
@@ -3544,17 +3734,17 @@
     );
 }
 #[repr(C, packed)]
-#[derive(Debug, Copy, Clone)]
+#[derive(Copy, Clone)]
 pub struct cras_config_global_remix {
     pub header: cras_server_message,
     pub num_channels: ::std::os::raw::c_uint,
-    pub coefficient: [f32; 32usize],
+    pub coefficient: [f32; 64usize],
 }
 #[test]
 fn bindgen_test_layout_cras_config_global_remix() {
     assert_eq!(
         ::std::mem::size_of::<cras_config_global_remix>(),
-        140usize,
+        268usize,
         concat!("Size of: ", stringify!(cras_config_global_remix))
     );
     assert_eq!(
diff --git a/cras/client/cras-sys/src/lib.rs b/cras/client/cras-sys/src/lib.rs
index 44709ce..8128575 100644
--- a/cras/client/cras-sys/src/lib.rs
+++ b/cras/client/cras-sys/src/lib.rs
@@ -4,6 +4,7 @@
 extern crate audio_streams;
 extern crate data_model;
 
+use std::cmp::min;
 use std::convert::{TryFrom, TryInto};
 use std::error;
 use std::fmt;
@@ -79,29 +80,86 @@
 
 impl cras_audio_format_packed {
     /// Initializes `cras_audio_format_packed` from input parameters.
+    /// Field `channel_layout` will be assigned with default channel layout defined in
+    /// `Self::default_channel_layout`.
     ///
     /// # Arguments
     /// * `format` - Format in used.
     /// * `rate` - Rate in used.
     /// * `num_channels` - Number of channels in used.
+    /// * `direction` - Stream direction enumeration.
     ///
     /// # Returns
     /// Structure `cras_audio_format_packed`
-    pub fn new(format: _snd_pcm_format, rate: usize, num_channels: usize) -> Self {
-        let mut audio_format = Self {
+    pub fn new(
+        format: _snd_pcm_format,
+        rate: u32,
+        num_channels: usize,
+        direction: CRAS_STREAM_DIRECTION,
+    ) -> Self {
+        Self {
             format: format as i32,
-            frame_rate: rate as u32,
+            frame_rate: rate,
             num_channels: num_channels as u32,
-            channel_layout: [-1; CRAS_CHANNEL::CRAS_CH_MAX as usize],
-        };
-        for i in 0..CRAS_CHANNEL::CRAS_CH_MAX as usize {
-            if i < num_channels {
-                audio_format.channel_layout[i] = i as i8;
-            } else {
-                break;
+            channel_layout: Self::default_channel_layout(num_channels, direction),
+        }
+    }
+
+    /// Generates default channel layout by given number of channels and stream direction.
+    /// ```
+    /// use cras_sys::gen::{
+    ///     _snd_pcm_format,
+    ///     cras_audio_format_packed,
+    ///     CRAS_STREAM_DIRECTION::*
+    /// };
+    /// let test_one = | num_channels, direction, expected_results | {
+    ///     let default_channel_fmt = cras_audio_format_packed::new(
+    ///         _snd_pcm_format::SND_PCM_FORMAT_S16,
+    ///         48000,
+    ///         num_channels,
+    ///         direction
+    ///     );
+    ///     assert_eq!(default_channel_fmt.channel_layout, expected_results);
+    /// };
+    /// test_one(2, CRAS_STREAM_OUTPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
+    /// test_one(4, CRAS_STREAM_OUTPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
+    /// test_one(6, CRAS_STREAM_OUTPUT, [0, 1, 4, 5, 2, 3, -1, -1, -1, -1, -1]);
+    /// test_one(2, CRAS_STREAM_INPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
+    /// test_one(4, CRAS_STREAM_INPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
+    /// test_one(6, CRAS_STREAM_INPUT, [0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1]);
+    /// ```
+    fn default_channel_layout(
+        num_channels: usize,
+        direction: CRAS_STREAM_DIRECTION,
+    ) -> [i8; CRAS_CHANNEL::CRAS_CH_MAX as usize] {
+        use {CRAS_CHANNEL::*, CRAS_STREAM_DIRECTION::*};
+
+        let mut channel_layout = [-1; CRAS_CH_MAX as usize];
+        match (num_channels, direction) {
+            (6, CRAS_STREAM_OUTPUT) => {
+                [
+                    CRAS_CH_FL,
+                    CRAS_CH_FR,
+                    CRAS_CH_FC,
+                    CRAS_CH_LFE,
+                    CRAS_CH_RL,
+                    CRAS_CH_RR,
+                ]
+                .iter()
+                .enumerate()
+                .for_each(|(idx, &channel)| channel_layout[channel as usize] = idx as i8);
+            }
+            _ => {
+                for (i, channel) in channel_layout
+                    .iter_mut()
+                    .enumerate()
+                    .take(min(num_channels, CRAS_CH_MAX as usize))
+                {
+                    *channel = i as i8;
+                }
             }
         }
-        audio_format
+        channel_layout
     }
 }
 
@@ -121,6 +179,7 @@
             idx: 0,
             name: [0; 64usize],
             stable_id: 0,
+            max_supported_channels: 0,
         }
     }
 }
@@ -168,7 +227,6 @@
             left_right_swapped: 0,
             type_enum: 0,
             stable_id: 0,
-            mic_positions: [0; 128usize],
             type_: [0; 32usize],
             name: [0; 64usize],
             active_hotword_model: [0; 16usize],
@@ -362,6 +420,7 @@
             5 => Ok(CRAS_CLIENT_TYPE_ARC),
             6 => Ok(CRAS_CLIENT_TYPE_CROSVM),
             7 => Ok(CRAS_CLIENT_TYPE_SERVER_STREAM),
+            8 => Ok(CRAS_CLIENT_TYPE_LACROS),
             _ => Err(Error::InvalidClientType(client_type)),
         }
     }
diff --git a/cras/client/cras_tests/src/arguments.rs b/cras/client/cras_tests/src/arguments.rs
index b051edf..59e9ec2 100644
--- a/cras/client/cras_tests/src/arguments.rs
+++ b/cras/client/cras_tests/src/arguments.rs
@@ -132,7 +132,14 @@
     pub buffer_size: Option<usize>,
     pub num_channels: Option<usize>,
     pub format: Option<SampleFormat>,
-    pub frame_rate: Option<usize>,
+    pub frame_rate: Option<u32>,
+}
+
+fn get_u32_param(matches: &Matches, option_name: &str) -> Result<Option<u32>> {
+    matches.opt_get::<u32>(option_name).map_err(|e| {
+        let argument = matches.opt_str(option_name).unwrap_or_default();
+        Error::InvalidArgument(option_name.to_string(), argument, e.to_string())
+    })
 }
 
 fn get_usize_param(matches: &Matches, option_name: &str) -> Result<Option<usize>> {
@@ -183,7 +190,7 @@
         }
 
         let loopback_type = if matches.opt_defined("loopback") {
-            match matches.opt_str("loopback").as_ref().map(|s| s.as_str()) {
+            match matches.opt_str("loopback").as_deref() {
                 Some("pre_dsp") => Some(LoopbackType::PreDsp),
                 Some("post_dsp") => Some(LoopbackType::PostDsp),
                 Some(s) => {
@@ -210,7 +217,7 @@
         let extension = file_name
             .extension()
             .map(|s| s.to_string_lossy().into_owned());
-        let file_type = match extension.as_ref().map(String::as_str) {
+        let file_type = match extension.as_deref() {
             Some("wav") | Some("wave") => FileType::Wav,
             Some("raw") | None => FileType::Raw,
             Some(extension) => return Err(Error::InvalidFiletype(extension.to_string())),
@@ -218,8 +225,8 @@
 
         let buffer_size = get_usize_param(&matches, "buffer_size")?;
         let num_channels = get_usize_param(&matches, "channels")?;
-        let frame_rate = get_usize_param(&matches, "rate")?;
-        let format = match matches.opt_str("format").as_ref().map(|s| s.as_str()) {
+        let frame_rate = get_u32_param(&matches, "rate")?;
+        let format = match matches.opt_str("format").as_deref() {
             Some("U8") => Some(SampleFormat::U8),
             Some("S16_LE") => Some(SampleFormat::S16LE),
             Some("S24_LE") => Some(SampleFormat::S24LE),
diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs
index 3ef90c2..261ad63 100644
--- a/cras/client/cras_tests/src/audio.rs
+++ b/cras/client/cras_tests/src/audio.rs
@@ -95,7 +95,7 @@
     wav_reader: WavReader<BufReader<File>>,
     format: SampleFormat,
     num_channels: usize,
-    frame_rate: usize,
+    frame_rate: u32,
 }
 
 impl WavSource {
@@ -122,7 +122,7 @@
             eprintln!("Warning: number of channels changed to {}", num_channels);
         }
 
-        let frame_rate = spec.sample_rate as usize;
+        let frame_rate = spec.sample_rate;
         if opts.frame_rate.is_some() && Some(frame_rate) != opts.frame_rate {
             eprintln!("Warning: frame rate changed to {}", frame_rate);
         }
@@ -143,7 +143,7 @@
         self.num_channels
     }
 
-    fn frame_rate(&self) -> usize {
+    fn frame_rate(&self) -> u32 {
         self.frame_rate
     }
 }
@@ -248,11 +248,11 @@
         path: P,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
     ) -> Result<Self> {
         let spec = WavSpec {
             channels: num_channels as u16,
-            sample_rate: frame_rate as u32,
+            sample_rate: frame_rate,
             bits_per_sample: (format.sample_bytes() * 8) as u16,
             sample_format: hound::SampleFormat::Int,
         };
@@ -278,11 +278,11 @@
         match self.format {
             SampleFormat::U8 => {
                 for sample in samples {
-                    self.wav_writer.write_sample(*sample as i8).or_else(|e| {
-                        Err(io::Error::new(
+                    self.wav_writer.write_sample(*sample as i8).map_err(|e| {
+                        io::Error::new(
                             io::ErrorKind::Other,
                             format!("Failed to write sample: {}", e),
-                        ))
+                        )
                     })?;
                 }
             }
@@ -301,11 +301,11 @@
                 // on drop.
                 // The flush method only writes data from the i16_writer to the underlying
                 // WavWriter, it does not actually guarantee a flush to disk.
-                writer.flush().or_else(|e| {
-                    Err(io::Error::new(
+                writer.flush().map_err(|e| {
+                    io::Error::new(
                         io::ErrorKind::Other,
                         format!("Failed to flush SampleWriter: {}", e),
-                    ))
+                    )
                 })?;
             }
             SampleFormat::S24LE | SampleFormat::S32LE => {
@@ -321,14 +321,14 @@
                     // even though the wav encoder does.
                     // TODO(fletcherw): add S24_LE support to hound.
                     if self.format == SampleFormat::S24LE {
-                        sample = sample << 8;
+                        sample <<= 8;
                     }
 
-                    self.wav_writer.write_sample(sample).or_else(|e| {
-                        Err(io::Error::new(
+                    self.wav_writer.write_sample(sample).map_err(|e| {
+                        io::Error::new(
                             io::ErrorKind::Other,
                             format!("Failed to write sample: {}", e),
-                        ))
+                        )
                     })?;
                 }
             }
@@ -338,11 +338,11 @@
     }
 
     fn flush(&mut self) -> io::Result<()> {
-        self.wav_writer.flush().or_else(|e| {
-            Err(io::Error::new(
+        self.wav_writer.flush().map_err(|e| {
+            io::Error::new(
                 io::ErrorKind::Other,
                 format!("Failed to flush WavWriter: {}", e),
-            ))
+            )
         })
     }
 }
@@ -385,7 +385,6 @@
 
             let loopback_node = cras_client
                 .input_nodes()
-                .into_iter()
                 .find(|node| node.node_type == node_type)
                 .ok_or(Error::NoLoopbackNode(node_type))?;
 
diff --git a/cras/client/libcras/src/cras_shm_stream.rs b/cras/client/libcras/src/cras_shm_stream.rs
index a04f20e..f72cc07 100644
--- a/cras/client/libcras/src/cras_shm_stream.rs
+++ b/cras/client/libcras/src/cras_shm_stream.rs
@@ -46,6 +46,7 @@
     header: CrasAudioHeader<'a>,
     frame_size: usize,
     num_channels: usize,
+    frame_rate: u32,
     // The index of the next buffer within SHM to set the buffer offset for.
     next_buffer_idx: usize,
 }
@@ -71,12 +72,14 @@
     /// # Errors
     ///
     /// * If `header_fd` could not be successfully mmapped.
+    #[allow(clippy::too_many_arguments)]
     pub fn try_new(
         stream_id: u32,
         server_socket: CrasServerSocket,
         audio_socket: AudioSocket,
         direction: StreamDirection,
         num_channels: usize,
+        frame_rate: u32,
         format: SampleFormat,
         header_fd: CrasAudioShmHeaderFd,
         samples_len: usize,
@@ -90,6 +93,7 @@
             header,
             frame_size: format.sample_bytes() * num_channels,
             num_channels,
+            frame_rate,
             // We have either sent zero or two offsets to the server, so we will
             // need to update index 0 next.
             next_buffer_idx: 0,
@@ -115,6 +119,10 @@
         self.num_channels
     }
 
+    fn frame_rate(&self) -> u32 {
+        self.frame_rate
+    }
+
     fn wait_for_next_action_with_timeout(
         &mut self,
         timeout: Duration,
@@ -129,7 +137,7 @@
             .read_audio_message_with_timeout(Some(timeout))?
         {
             Some(AudioMessage::Success { id, frames }) if id == expected_id => {
-                return Ok(Some(ServerRequest::new(frames as usize, self)));
+                Ok(Some(ServerRequest::new(frames as usize, self)))
             }
             None => Ok(None),
             _ => Err(Box::new(Error::MessageTypeError)),
diff --git a/cras/client/libcras/src/cras_stream.rs b/cras/client/libcras/src/cras_stream.rs
index f46649f..5914bfd 100644
--- a/cras/client/libcras/src/cras_stream.rs
+++ b/cras/client/libcras/src/cras_stream.rs
@@ -119,7 +119,7 @@
     server_socket: CrasServerSocket,
     block_size: u32,
     direction: CRAS_STREAM_DIRECTION,
-    rate: usize,
+    rate: u32,
     num_channels: usize,
     format: snd_pcm_format_t,
     /// A structure for stream to interact with server audio thread.
@@ -134,12 +134,13 @@
     ///
     /// # Returns
     /// `CrasStream` - CRAS client stream.
+    #[allow(clippy::too_many_arguments)]
     pub fn try_new(
         stream_id: u32,
         server_socket: CrasServerSocket,
         block_size: u32,
         direction: CRAS_STREAM_DIRECTION,
-        rate: usize,
+        rate: u32,
         num_channels: usize,
         format: snd_pcm_format_t,
         audio_sock: AudioSocket,
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs
index 1246b7b..62e8fdd 100644
--- a/cras/client/libcras/src/libcras.rs
+++ b/cras/client/libcras/src/libcras.rs
@@ -29,7 +29,7 @@
 //! use audio_streams::{SampleFormat, StreamSource};
 //!
 //! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: usize = 44100;
+//! const FRAME_RATE: u32 = 44100;
 //! const NUM_CHANNELS: usize = 2;
 //! const FORMAT: SampleFormat = SampleFormat::S16LE;
 //!
@@ -82,7 +82,7 @@
 //! use audio_streams::{SampleFormat, StreamSource};
 //!
 //! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: usize = 44100;
+//! const FRAME_RATE: u32 = 44100;
 //! const NUM_CHANNELS: usize = 2;
 //! const FORMAT: SampleFormat = SampleFormat::S16LE;
 //!
@@ -396,14 +396,15 @@
         device_index: Option<u32>,
         block_size: u32,
         direction: CRAS_STREAM_DIRECTION,
-        rate: usize,
+        rate: u32,
         channel_num: usize,
         format: SampleFormat,
     ) -> Result<CrasStream<'b, T>> {
         let stream_id = self.next_server_stream_id();
 
         // Prepares server message
-        let audio_format = cras_audio_format_packed::new(format.into(), rate, channel_num);
+        let audio_format =
+            cras_audio_format_packed::new(format.into(), rate, channel_num, direction);
         let msg_header = cras_server_message {
             length: mem::size_of::<cras_connect_message>() as u32,
             id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
@@ -463,12 +464,13 @@
     /// * `format` - The format to use for stream audio samples.
     /// * `frame_rate` - The sample rate of the stream.
     /// * `buffer_size` - The transfer size granularity in frames.
+    #[allow(clippy::type_complexity)]
     pub fn new_pinned_playback_stream(
         &mut self,
         device_index: u32,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
     {
@@ -497,12 +499,13 @@
     /// * `format` - The format to use for stream audio samples.
     /// * `frame_rate` - The sample rate of the stream.
     /// * `buffer_size` - The transfer size granularity in frames.
+    #[allow(clippy::type_complexity)]
     pub fn new_pinned_capture_stream(
         &mut self,
         device_index: u32,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
         Ok((
@@ -549,11 +552,12 @@
 }
 
 impl<'a> StreamSource for CrasClient<'a> {
+    #[allow(clippy::type_complexity)]
     fn new_playback_stream(
         &mut self,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
     {
@@ -570,11 +574,12 @@
         ))
     }
 
+    #[allow(clippy::type_complexity)]
     fn new_capture_stream(
         &mut self,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
     ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
         if self.cras_capture {
@@ -613,7 +618,7 @@
         direction: StreamDirection,
         num_channels: usize,
         format: SampleFormat,
-        frame_rate: usize,
+        frame_rate: u32,
         buffer_size: usize,
         effects: &[StreamEffect],
         client_shm: &SharedMemory,
@@ -632,7 +637,12 @@
 
         // Prepares server message
         let stream_id = self.next_server_stream_id();
-        let audio_format = cras_audio_format_packed::new(format.into(), frame_rate, num_channels);
+        let audio_format = cras_audio_format_packed::new(
+            format.into(),
+            frame_rate,
+            num_channels,
+            direction.into(),
+        );
         let msg_header = cras_server_message {
             length: mem::size_of::<cras_connect_message>() as u32,
             id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
@@ -649,7 +659,7 @@
             flags: 0,
             format: audio_format,
             dev_idx: CRAS_SPECIAL_DEVICE::NO_DEVICE as u32,
-            effects: effects.into_iter().collect::<CrasStreamEffect>().into(),
+            effects: effects.iter().collect::<CrasStreamEffect>().into(),
             client_type: self.client_type,
             client_shm_size: client_shm.size(),
             buffer_offsets,
@@ -673,6 +683,7 @@
                     audio_socket,
                     direction,
                     num_channels,
+                    frame_rate,
                     format,
                     header_fd,
                     client_shm.size() as usize,
diff --git a/cras/configure.ac b/cras/configure.ac
index 57030a5..f39a14a 100644
--- a/cras/configure.ac
+++ b/cras/configure.ac
@@ -71,6 +71,15 @@
 fi
 
 PKG_CHECK_MODULES([SBC], [ sbc >= 1.0 ])
+AC_CHECK_HEADERS([iniparser/iniparser.h iniparser.h], [FOUND_INIPARSER=1;break])
+test [$FOUND_INIPARSER] || AC_MSG_ERROR([Missing iniparser, please install.])
+AC_SEARCH_LIBS([LADSPA], [ladspa-sdk], [], [
+	AC_CHECK_HEADERS([ladspa.h], [], [
+		AC_MSG_ERROR([Missing ladspa-sdk, please install.])
+	])
+])
+PKG_CHECK_MODULES([UDEV], [ libudev >= 1.0 ])
+PKG_CHECK_MODULES([GTEST], [ gtest >= 1.0 ])
 AC_CHECK_LIB(asound, snd_pcm_ioplug_create,,
 	     AC_ERROR([*** libasound has no external plugin SDK]), -ldl)
 
@@ -80,7 +89,7 @@
 AC_ARG_ENABLE([metrics], AS_HELP_STRING([--enable-metrics], [Enable metrics uses]), have_metrics=$enableval, have_metrics=no)
 if test "$have_metrics" = "yes"; then
     AC_DEFINE(HAVE_LIB_METRICS, 1, [Define to use libmetrics])
-    METRICS_LIBS=-lmetrics-${BASE_VER}
+    METRICS_LIBS=-lmetrics
 else
     METRICS_LIBS=
 fi
diff --git a/cras/install_deps.sh b/cras/install_deps.sh
new file mode 100755
index 0000000..6eac01a
--- /dev/null
+++ b/cras/install_deps.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+apt-get install -y \
+  automake \
+  build-essential \
+  cmake \
+  g++ \
+  gdb \
+  git \
+  ladspa-sdk \
+  libasound-dev \
+  libdbus-1-dev \
+  libncurses5-dev \
+  libsbc-dev \
+  libsndfile-dev \
+  libspeexdsp-dev \
+  libtool \
+  libudev-dev \
+  wget \
+  zip
+cd /tmp
+git clone https://github.com/ndevilla/iniparser.git
+cd iniparser
+make
+cp libiniparser.* /usr/local/lib
+cp src/dictionary.h src/iniparser.h /usr/local/include
+chmod 644 /usr/local/include/dictionary.h /usr/local/include/iniparser.h
+chmod 644 /usr/local/lib/libiniparser.a
+chmod 755 /usr/local/lib/libiniparser.so.*
+
+cd /tmp
+git clone https://github.com/google/googletest.git -b v1.8.x
+cd googletest
+mkdir build
+cd build
+cmake .. -DBUILD_SHARED_LIBS=ON \
+  -DINSTALL_GTEST=ON \
+  -DCMAKE_INSTALL_PREFIX:PATH=/usr
+make
+make install
+
+# Need to build and install alsa so there is a static lib.
+mkdir -p /tmp/alsa-build &&
+  cd /tmp/alsa-build && \
+  wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.4.1.tar.bz2 && \
+  bzip2 -f -d alsa-lib-* && \
+  tar xf alsa-lib-* && \
+  cd alsa-lib-* && \
+  ./configure --enable-static --disable-shared && \
+  make clean && \
+  make -j$(nproc) all && \
+  make install
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index 2f98075..874389c 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -163,12 +163,14 @@
 	server/test_iodev.c \
 	server/softvol_curve.c
 
+SERVER_RUST_SRCDIR = $(top_srcdir)/src/server/rust
+
 libcrasserver_la_SOURCES = \
 	$(cras_server_SOURCES)
 libcrasserver_la_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
 	-I$(top_srcdir)/src/server/config -I$(top_srcdir)/src/plc \
-	-I$(top_srcdir)/src/server/rust/src/headers \
+	-I$(SERVER_RUST_SRCDIR)/src/headers \
 	$(DBUS_CFLAGS) $(SBC_CFLAGS) $(SELINUX_CFLAGS)
 libcrasserver_la_LIBADD = \
 	$(CRAS_RUST) \
@@ -306,14 +308,22 @@
 
 if !WITH_SYSTEM_RUST
 RUST_FILES = \
-	server/rust/Cargo.toml \
-	server/rust/src/rate_estimator_bindings.rs \
-	server/rust/src/rate_estimator.rs
+	$(SERVER_RUST_SRCDIR)/Cargo.toml \
+	$(SERVER_RUST_SRCDIR)/src/rate_estimator_bindings.rs \
+	$(SERVER_RUST_SRCDIR)/src/rate_estimator.rs
 
-CRAS_RUST = libcras_rust.a
+CRAS_RUST_TARGET_DIR = $(top_builddir)/src/server/rust/target
+CRAS_RUST = $(CRAS_RUST_TARGET_DIR)/release/libcras_rust.a
 $(CRAS_RUST): $(RUST_FILES)
-	cargo build --release --manifest-path server/rust/Cargo.toml
-	cp server/rust/target/release/libcras_rust.a .
+	cargo build --release \
+		--manifest-path $(SERVER_RUST_SRCDIR)/Cargo.toml \
+		--target-dir $(CRAS_RUST_TARGET_DIR)
+
+clean-local:
+	cargo clean --release \
+		--manifest-path $(SERVER_RUST_SRCDIR)/Cargo.toml \
+		--target-dir $(CRAS_RUST_TARGET_DIR)
+
 else
 CRAS_RUST = -lcras_rust
 endif
@@ -661,7 +671,7 @@
 	common/cras_shm.c
 audio_thread_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
 	-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
-	-I$(top_srcdir)/src/server/rust/src/headers
+	-I$(SERVER_RUST_SRCDIR)/src/headers
 audio_thread_unittest_LDADD = -lgtest -lpthread -lrt
 
 audio_thread_monitor_unittest_SOURCES = tests/audio_thread_monitor_unittest.cc
@@ -733,7 +743,7 @@
 	-I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/server \
 	-I$(top_srcdir)/src/server/config \
-	-I$(top_srcdir)/src/server/rust/src/headers \
+	-I$(SERVER_RUST_SRCDIR)/src/headers \
 	$(SELINUX_CFLAGS)
 dev_io_unittest_LDADD = \
 	libcrasmix.la \
@@ -893,7 +903,7 @@
 	server/cras_iodev.c common/cras_shm.c
 iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/server \
-	-I$(top_srcdir)/src/server/rust/src/headers
+	-I$(SERVER_RUST_SRCDIR)/src/headers
 iodev_unittest_LDADD = -lgtest -lpthread -lrt
 
 mix_unittest_SOURCES = tests/mix_unittest.cc server/cras_mix.c
@@ -932,7 +942,7 @@
 rate_estimator_unittest_SOURCES = tests/rate_estimator_unittest.cc
 rate_estimator_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
 	 -I$(top_srcdir)/src/server \
-	 -I$(top_srcdir)/src/server/rust/src/headers
+	 -I$(SERVER_RUST_SRCDIR)/src/headers
 rate_estimator_unittest_LDADD = $(CRAS_RUST) -lgtest -ldl -lpthread
 
 control_rclient_unittest_SOURCES = tests/control_rclient_unittest.cc \
@@ -1018,7 +1028,7 @@
 	-I$(top_srcdir)/src/common \
 	-I$(top_srcdir)/src/server \
 	-I$(top_srcdir)/src/server/config \
-	-I$(top_srcdir)/src/server/rust/src/headers \
+	-I$(SERVER_RUST_SRCDIR)/src/headers \
 	$(SELINUX_CFLAGS)
 timing_unittest_LDADD = \
 	libcrasmix.la \
diff --git a/cras/src/common/bluetooth.h b/cras/src/common/bluetooth.h
index f9a9b5e..66beada 100644
--- a/cras/src/common/bluetooth.h
+++ b/cras/src/common/bluetooth.h
@@ -75,6 +75,10 @@
 
 #define BT_VOICE_TRANSPARENT 0x0003
 
-#define BT_PKT_STATUS		16
+#define BT_SNDMTU 12
 
-#define BT_SCM_PKT_STATUS	0x03
\ No newline at end of file
+#define BT_RCVMTU 13
+
+#define BT_PKT_STATUS 16
+
+#define BT_SCM_PKT_STATUS 0x03
diff --git a/cras/src/common/cras_iodev_info.h b/cras/src/common/cras_iodev_info.h
index 3a6af91..85d20f9 100644
--- a/cras/src/common/cras_iodev_info.h
+++ b/cras/src/common/cras_iodev_info.h
@@ -39,7 +39,6 @@
  *    ui_gain_scaler - Adjustable gain scaler set by Chrome.
  *    left_right_swapped - Set true if left and right channels are swapped.
  *    stable_id - ID that does not change due to device plug/unplug or reboot.
- *    mic_positions - Positions of the mic array.
  *    type - Type displayed to the user.
  *    name - Name displayed to the user.
  *    active_hotword_model - name of the currently selected hotword model.
@@ -59,7 +58,6 @@
 	int32_t left_right_swapped;
 	uint32_t type_enum;
 	uint32_t stable_id;
-	char mic_positions[CRAS_NODE_MIC_POS_BUFFER_SIZE];
 	char type[CRAS_NODE_TYPE_BUFFER_SIZE];
 	char name[CRAS_NODE_NAME_BUFFER_SIZE];
 	char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h
index 7021808..ee09d93 100644
--- a/cras/src/common/cras_messages.h
+++ b/cras/src/common/cras_messages.h
@@ -58,6 +58,7 @@
 	CRAS_SERVER_DUMP_BT,
 	CRAS_SERVER_SET_BT_WBS_ENABLED,
 	CRAS_SERVER_GET_ATLOG_FD,
+	CRAS_SERVER_DUMP_MAIN,
 };
 
 enum CRAS_CLIENT_MESSAGE_ID {
@@ -352,6 +353,17 @@
 	m->header.length = sizeof(*m);
 }
 
+/* Dump events in CRAS main thread. */
+struct __attribute__((__packed__)) cras_dump_main {
+	struct cras_server_message header;
+};
+
+static inline void cras_fill_dump_main(struct cras_dump_main *m)
+{
+	m->header.id = CRAS_SERVER_DUMP_MAIN;
+	m->header.length = sizeof(*m);
+}
+
 /* Dump bluetooth events and state changes. */
 struct __attribute__((__packed__)) cras_dump_bt {
 	struct cras_server_message header;
diff --git a/cras/src/common/cras_shm.h b/cras/src/common/cras_shm.h
index 6e954e2..47786c3 100644
--- a/cras/src/common/cras_shm.h
+++ b/cras/src/common/cras_shm.h
@@ -564,11 +564,12 @@
 	uint32_t i;
 
 	shm->config.used_size = used_size;
-	if (shm->header)
+	if (shm->header) {
 		shm->header->config.used_size = used_size;
 
-	for (i = 0; i < CRAS_NUM_SHM_BUFFERS; i++)
-		cras_shm_set_buffer_offset(shm, i, i * used_size);
+		for (i = 0; i < CRAS_NUM_SHM_BUFFERS; i++)
+			cras_shm_set_buffer_offset(shm, i, i * used_size);
+	}
 }
 
 /* Returns the used size of the shm region in bytes. */
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index dc0b188..c3482e8 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -164,6 +164,7 @@
 	CRAS_CLIENT_TYPE_ARC, /* ARC++ */
 	CRAS_CLIENT_TYPE_CROSVM, /* CROSVM */
 	CRAS_CLIENT_TYPE_SERVER_STREAM, /* Server stream */
+	CRAS_CLIENT_TYPE_LACROS, /* LaCrOS */
 };
 
 #define ENUM_STR(x)                                                            \
@@ -200,6 +201,7 @@
 	ENUM_STR(CRAS_CLIENT_TYPE_ARC)
 	ENUM_STR(CRAS_CLIENT_TYPE_CROSVM)
 	ENUM_STR(CRAS_CLIENT_TYPE_SERVER_STREAM)
+	ENUM_STR(CRAS_CLIENT_TYPE_LACROS)
 	default:
 		return "INVALID_CLIENT_TYPE";
 	}
@@ -252,6 +254,7 @@
 #define MAX_DEBUG_STREAMS 8
 #define AUDIO_THREAD_EVENT_LOG_SIZE (1024 * 6)
 #define CRAS_BT_EVENT_LOG_SIZE 1024
+#define MAIN_THREAD_EVENT_LOG_SIZE 1024
 
 /* There are 8 bits of space for events. */
 enum AUDIO_THREAD_LOG_EVENTS {
@@ -305,6 +308,44 @@
 	AUDIO_THREAD_DEV_OVERRUN,
 };
 
+/* Important events in main thread.
+ * MAIN_THREAD_DEV_CLOSE - When an iodev closes at stream removal.
+ * MAIN_THREAD_DEV_DISABLE - When an iodev is removed from active dev list.
+ * MAIN_THREAD_DEV_INIT - When an iodev opens when stream attachs.
+ * MAIN_THREAD_DEV_REOPEN - When an iodev reopens for format change.
+ * MAIN_THREAD_ADD_ACTIVE_NODE - When an iodev is set as an additional
+ *    active device.
+ * MAIN_THREAD_SELECT_NODE - When UI selects an iodev as active.
+ * MAIN_THREAD_NODE_PLUGGED - When a jack of iodev is plugged/unplugged.
+ * MAIN_THREAD_ADD_TO_DEV_LIST - When iodev is added to list.
+ * MAIN_THREAD_INPUT_NODE_GAIN - When input node gain changes.
+ * MAIN_THREAD_OUTPUT_NODE_VOLUME - When output node volume changes.
+ * MAIN_THREAD_SET_OUTPUT_USER_MUTE - When output mute state is set.
+ * MAIN_THREAD_RESUME_DEVS - When system resumes and notifies CRAS.
+ * MAIN_THREAD_SUSPEND_DEVS - When system suspends and notifies CRAS.
+ * MAIN_THREAD_STREAM_ADDED - When an audio stream is added.
+ * MAIN_THREAD_STREAM_REMOVED - When an audio stream is removed.
+ */
+enum MAIN_THREAD_LOG_EVENTS {
+	/* iodev related */
+	MAIN_THREAD_DEV_CLOSE,
+	MAIN_THREAD_DEV_DISABLE,
+	MAIN_THREAD_DEV_INIT,
+	MAIN_THREAD_DEV_REOPEN,
+	MAIN_THREAD_ADD_ACTIVE_NODE,
+	MAIN_THREAD_SELECT_NODE,
+	MAIN_THREAD_NODE_PLUGGED,
+	MAIN_THREAD_ADD_TO_DEV_LIST,
+	MAIN_THREAD_INPUT_NODE_GAIN,
+	MAIN_THREAD_OUTPUT_NODE_VOLUME,
+	MAIN_THREAD_SET_OUTPUT_USER_MUTE,
+	MAIN_THREAD_RESUME_DEVS,
+	MAIN_THREAD_SUSPEND_DEVS,
+	/* stream related */
+	MAIN_THREAD_STREAM_ADDED,
+	MAIN_THREAD_STREAM_REMOVED,
+};
+
 /* There are 8 bits of space for events. */
 enum CRAS_BT_LOG_EVENTS {
 	BT_ADAPTER_ADDED,
@@ -323,6 +364,8 @@
 	BT_HFP_REQUEST_DISCONNECT,
 	BT_HFP_SUPPORTED_FEATURES,
 	BT_HFP_HF_INDICATOR,
+	BT_HFP_SET_SPEAKER_GAIN,
+	BT_HFP_UPDATE_SPEAKER_GAIN,
 	BT_HSP_NEW_CONNECTION,
 	BT_HSP_REQUEST_DISCONNECT,
 	BT_NEW_AUDIO_PROFILE_AFTER_CONNECT,
@@ -330,6 +373,8 @@
 	BT_SCO_CONNECT,
 	BT_TRANSPORT_ACQUIRE,
 	BT_TRANSPORT_RELEASE,
+	BT_TRANSPORT_SET_VOLUME,
+	BT_TRANSPORT_UPDATE_VOLUME,
 };
 
 struct __attribute__((__packed__)) audio_thread_event {
@@ -401,6 +446,24 @@
 	struct audio_thread_event_log log;
 };
 
+struct __attribute__((__packed__)) main_thread_event {
+	uint32_t tag_sec;
+	uint32_t nsec;
+	uint32_t data1;
+	uint32_t data2;
+	uint32_t data3;
+};
+
+struct __attribute__((__packed__)) main_thread_event_log {
+	uint32_t write_pos;
+	uint32_t len;
+	struct main_thread_event log[MAIN_THREAD_EVENT_LOG_SIZE];
+};
+
+struct __attribute__((__packed__)) main_thread_debug_info {
+	struct main_thread_event_log main_log;
+};
+
 struct __attribute__((__packed__)) cras_bt_event {
 	uint32_t tag_sec;
 	uint32_t nsec;
@@ -493,6 +556,7 @@
  *    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.
+ *    main_thread_debug_info - ring buffer for storing main thread event logs.
  */
 #define CRAS_SERVER_STATE_VERSION 2
 struct __attribute__((packed, aligned(4))) cras_server_state {
@@ -529,6 +593,7 @@
 	struct cras_audio_thread_snapshot_buffer snapshot_buffer;
 	struct cras_bt_debug_info bt_debug_info;
 	int32_t bt_wbs_enabled;
+	struct main_thread_debug_info main_thread_debug_info;
 };
 
 /* Actions for card add/remove/change. */
diff --git a/cras/src/common/dumper.c b/cras/src/common/dumper.c
index 50789e8..5da16df 100644
--- a/cras/src/common/dumper.c
+++ b/cras/src/common/dumper.c
@@ -108,18 +108,23 @@
 	struct dumper *dumper = calloc(1, sizeof(struct dumper));
 	struct mem_data *data = calloc(1, sizeof(struct mem_data));
 	if (!dumper || !data)
-		return NULL;
+		goto error;
 	data->size = 0;
 	data->capacity = 80;
 	data->buf = malloc(data->capacity);
-	if (!data->buf) {
-		free(data);
-		return NULL;
-	}
+	if (!data->buf)
+		goto error;
 	data->buf[0] = '\0';
 	dumper->data = data;
 	dumper->vprintf = &mem_vprintf;
 	return dumper;
+
+error:
+	if (dumper)
+		free(dumper);
+	if (data)
+		free(data);
+	return NULL;
 }
 
 void mem_dumper_free(struct dumper *dumper)
diff --git a/cras/src/common/utlist.h b/cras/src/common/utlist.h
index e3ee630..6c7f1e3 100644
--- a/cras/src/common/utlist.h
+++ b/cras/src/common/utlist.h
@@ -194,6 +194,7 @@
 
 #define DL_DELETE(head, del)                                                   \
 	do {                                                                   \
+		assert((head) != NULL);                                        \
 		assert((del)->prev != NULL);                                   \
 		if ((del)->prev == (del)) {                                    \
 			(head) = NULL;                                         \
diff --git a/cras/src/fuzz/Dockerfile b/cras/src/fuzz/Dockerfile
index f8e9e58..caffa99 100644
--- a/cras/src/fuzz/Dockerfile
+++ b/cras/src/fuzz/Dockerfile
@@ -7,52 +7,7 @@
 FROM gcr.io/oss-fuzz-base/base-builder
 LABEL maintainer="dgreid@chromium.org"
 
-RUN apt-get -y update && \
-      apt-get install -y \
-      automake \
-      build-essential \
-      cmake \
-      g++ \
-      gdb \
-      git \
-      ladspa-sdk \
-      libasound-dev \
-      libdbus-1-dev \
-      libgtest-dev \
-      libncurses5-dev \
-      libsbc-dev \
-      libsndfile-dev \
-      libspeexdsp-dev \
-      libtool \
-      libudev-dev \
-      wget \
-      zip
-RUN apt-get clean
-RUN cd /tmp && git clone https://github.com/ndevilla/iniparser.git && \
-      cd iniparser && \
-      make && \
-      cp libiniparser.* /usr/local/lib && \
-      cp src/dictionary.h src/iniparser.h /usr/local/include && \
-      chmod 644 /usr/local/include/dictionary.h /usr/local/include/iniparser.h && \
-      chmod 644 /usr/local/lib/libiniparser.a && \
-      chmod 755 /usr/local/lib/libiniparser.so.*
-RUN cd /usr/src/gtest && \
-      cmake . && \
-      make && \
-      chmod 644 *.a && \
-      cp *.a /usr/local/lib
-
-# Need to build and install alsa so there is a static lib.
-RUN mkdir -p /tmp/alsa-build && cd /tmp/alsa-build && \
-      wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.4.1.tar.bz2 && \
-      bzip2 -f -d alsa-lib-* && \
-      tar xf alsa-lib-* && \
-      cd alsa-lib-* && \
-      ./configure --enable-static --disable-shared && \
-      make clean && \
-      make -j$(nproc) all && \
-      make install
-
 COPY . "${SRC}/adhd"
 COPY cras/src/fuzz/build.sh "${SRC}/build.sh"
+RUN "${SRC}/adhd/cras/install_deps.sh"
 RUN mkdir -p /etc/cras && cp "${SRC}/adhd/cras-config/dsp.ini.sample" /etc/cras
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index e5a1380..3fe631d 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -1403,7 +1403,6 @@
 		goto err_ret;
 	}
 
-	samples_prot = 0;
 	if (stream->direction == CRAS_STREAM_OUTPUT)
 		samples_prot = PROT_WRITE;
 	else
@@ -2138,7 +2137,7 @@
 
 	rc = fill_socket_file((*client), conn_type);
 	if (rc < 0) {
-		goto free_error;
+		goto free_server_event_fd;
 	}
 
 	rc = cras_file_wait_create((*client)->sock_file,
@@ -2171,10 +2170,11 @@
 
 	return 0;
 free_error:
-	if ((*client)->server_event_fd >= 0)
-		close((*client)->server_event_fd);
 	cras_file_wait_destroy((*client)->sock_file_wait);
 	free((void *)(*client)->sock_file);
+free_server_event_fd:
+	if ((*client)->server_event_fd >= 0)
+		close((*client)->server_event_fd);
 free_cond:
 	pthread_cond_destroy(&(*client)->stream_start_cond);
 free_lock:
@@ -2632,6 +2632,21 @@
 	return debug_info;
 }
 
+const struct main_thread_debug_info *
+cras_client_get_main_thread_debug_info(const struct cras_client *client)
+{
+	const struct main_thread_debug_info *debug_info;
+	int lock_rc;
+
+	lock_rc = server_state_rdlock(client);
+	if (lock_rc)
+		return 0;
+
+	debug_info = &client->server_state->main_thread_debug_info;
+	server_state_unlock(client, lock_rc);
+	return debug_info;
+}
+
 const struct cras_bt_debug_info *
 cras_client_get_bt_debug_info(const struct cras_client *client)
 {
@@ -3168,6 +3183,20 @@
 	return len;
 }
 
+int cras_client_update_main_thread_debug_info(
+	struct cras_client *client, void (*debug_info_cb)(struct cras_client *))
+{
+	struct cras_dump_main msg;
+
+	if (client == NULL)
+		return -EINVAL;
+	if (client->debug_info_callback != NULL)
+		return -EINVAL;
+	client->debug_info_callback = debug_info_cb;
+	cras_fill_dump_main(&msg);
+	return write_message_to_server(client, &msg.header);
+}
+
 int cras_client_update_bt_debug_info(
 	struct cras_client *client, void (*debug_info_cb)(struct cras_client *))
 {
@@ -3333,10 +3362,10 @@
 {
 	struct cras_config_global_remix *msg;
 	int rc;
+	size_t nchan = (size_t)num_channels;
 
 	msg = (struct cras_config_global_remix *)malloc(
-		sizeof(*msg) +
-		num_channels * num_channels * sizeof(*coefficient));
+		sizeof(*msg) + nchan * nchan * sizeof(*coefficient));
 	cras_fill_config_global_remix_command(msg, num_channels, coefficient,
 					      num_channels * num_channels);
 	rc = write_message_to_server(client, &msg->header);
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index c91ebe2..f7a18b5 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -477,6 +477,16 @@
 int cras_client_update_audio_debug_info(struct cras_client *client,
 					void (*cb)(struct cras_client *));
 
+/* Asks the server to dump current main thread information.
+ * Args:
+ *    client - The client from cras_client_create.
+ *    cb - A function to call when the data is received.
+ * Returns:
+ *    0 on success, -EINVAL if the client isn't valid or isn't running.
+ */
+int cras_client_update_main_thread_debug_info(struct cras_client *client,
+					      void (*cb)(struct cras_client *));
+
 /* Asks the server to dump bluetooth debug information.
  * Args:
  *    client - The client from cras_client_create.
@@ -856,6 +866,16 @@
 const struct cras_bt_debug_info *
 cras_client_get_bt_debug_info(const struct cras_client *client);
 
+/* Gets main thread debug info.
+ * Args:
+ *    client - The client from cras_client_create.
+ * Returns:
+ *    A pointer to the debug info. This info is updated and requested by
+ *    calling cras_client_update_main_thread_debug_info.
+ */
+const struct main_thread_debug_info *
+cras_client_get_main_thread_debug_info(const struct cras_client *client);
+
 /* Gets audio thread snapshot buffer.
  *
  * Requires that the connection to the server has been established.
diff --git a/cras/src/plc/cras_plc.c b/cras/src/plc/cras_plc.c
index ed42ae9..4bc9fb7 100644
--- a/cras/src/plc/cras_plc.c
+++ b/cras/src/plc/cras_plc.c
@@ -141,7 +141,7 @@
 		x2 += ((float)x[i]) * x[i];
 		y2 += ((float)y[i]) * y[i];
 	}
-	return sum / sqrt(x2 * y2);
+	return sum / sqrtf(x2 * y2);
 }
 
 int pattern_match(int16_t *hist)
diff --git a/cras/src/plc/parse_sco.py b/cras/src/plc/parse_sco.py
new file mode 100755
index 0000000..c50df15
--- /dev/null
+++ b/cras/src/plc/parse_sco.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+# 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.
+"""
+A script to extract raw SCO RX packets from btsnoop.
+Use 'btmon -S' to dump SCO traffic from btsnoop file.
+Trim the btsnoop output to just the SCO traffic period.
+Then execute 'python parse-sco.py <btsnoop-output>'
+"""
+
+import atexit
+import binascii
+import os
+import re
+import sys
+
+
+class SCOParser:
+  """
+  Parser for grepping SCO packets
+  """
+
+  def __init__(self):
+    # On old releases, +CIEV: 4,1 indicates the start point of call session
+    # c 31 0d 0a 9a     ..+CIEV: 4,1..
+    self.call_start_re = re.compile(r'.*?\+CIEV:\s4,(\d).*?')
+
+    # > SCO Data RX: Handle 257 flags 0x00 dlen 60           #13826 [hci0] 650.388305
+    #         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+    #         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+    #         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+    #         00 00 00 00 00 00 00 00 00 00 00 00
+    self.sco_rx_re = re.compile(r'.*?SCO\sData\sRX.*?flags\s0x(\d+).*?')
+    self.sco_f = None
+    self.output_idx = 0
+    self.pk_count = 0
+    self.pl_count = 0
+
+    atexit.register(self._cleanup)
+
+  def _cleanup(self):
+    if self.sco_f is not None:
+      print(
+          "Current file contains %d packets (%d with erroneous status flag)" %
+          (self.pk_count, self.pl_count))
+      self.pk_count = 0
+      self.pl_count = 0
+      self.sco_f.close()
+
+  def _new_session(self):
+    if self.sco_f is not None:
+      close(self.sco_f)
+
+    new_file = "sco_file_%d" % self.output_idx
+    print("Record to %s" % new_file)
+    self.sco_f = open(new_file, 'wb')
+    self.output_idx += 1
+
+    return self.sco_f
+
+  def parse(self, filename):
+    if not os.path.exists(filename):
+      print("%s doesn't exist" % filename)
+      return
+
+    print("Start parsing %s" % filename)
+    parse_rx_data = 0
+    with open(filename, "r") as f:
+      for line in f.readlines():
+        if parse_rx_data > 0:
+          self.sco_f.write(binascii.unhexlify(''.join(line[:56].split())))
+          parse_rx_data = (parse_rx_data + 1) % 5
+
+        # Start a new session and output following SCO data to a new file
+        match = self.call_start_re.search(line)
+        if match and (1 == int(match.group(1))):
+          self._new_session()
+          continue
+
+        match = self.sco_rx_re.search(line)
+        if match:
+          if self.sco_f is None:
+            self._new_session()
+
+          self.pk_count += 1
+
+          status_flag = int(match.group(1))
+          hdr = ['01', str(status_flag) + '1', '3c']
+          if status_flag != 0:
+            self.pl_count += 1
+
+          self.sco_f.write(binascii.unhexlify(''.join(hdr)))
+          parse_rx_data = 1
+
+
+def main(argv):
+  if len(argv) < 1:
+    print("parse_sco.py [btsnoop.txt]")
+    return
+
+  p = SCOParser()
+  p.parse(argv[0])
+
+
+if __name__ == "__main__":
+  main(sys.argv[1:])
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index 879b3c4..df713ca 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -835,6 +835,7 @@
 	while (1) {
 		struct timespec *wait_ts;
 		struct iodev_callback_list *iodev_cb;
+		int non_empty;
 
 		wait_ts = NULL;
 		thread->num_pollfds = 1;
@@ -844,6 +845,9 @@
 			   &thread->open_devs[CRAS_STREAM_INPUT],
 			   thread->remix_converter);
 
+		non_empty = dev_io_check_non_empty_state_transition(
+			thread->open_devs[CRAS_STREAM_OUTPUT]);
+
 		if (fill_next_sleep_interval(thread, &ts))
 			wait_ts = &ts;
 
@@ -884,7 +888,7 @@
 		log_busyloop(wait_ts);
 
 		ATLOG(atlog, AUDIO_THREAD_SLEEP, wait_ts ? wait_ts->tv_sec : 0,
-		      wait_ts ? wait_ts->tv_nsec : 0, 0);
+		      wait_ts ? wait_ts->tv_nsec : 0, non_empty);
 		if (wait_ts)
 			check_busyloop(wait_ts);
 
@@ -894,6 +898,16 @@
 
 		rc = ppoll(thread->pollfds, thread->num_pollfds, wait_ts, NULL);
 		ATLOG(atlog, AUDIO_THREAD_WAKE, rc, 0, 0);
+
+		/* Handle callbacks registered by TRIGGER_WAKEUP */
+		DL_FOREACH (iodev_callbacks, iodev_cb) {
+			if (iodev_cb->trigger == TRIGGER_WAKEUP) {
+				ATLOG(atlog, AUDIO_THREAD_IODEV_CB, 0, 0, 0);
+				iodev_cb->cb(iodev_cb->cb_data, 0);
+			}
+		}
+
+		/* If there's no pollfd ready to handle. */
 		if (rc <= 0)
 			continue;
 
@@ -911,9 +925,6 @@
 				      iodev_cb->events, 0);
 				iodev_cb->cb(iodev_cb->cb_data,
 					     iodev_cb->pollfd->revents);
-			} else if (iodev_cb->trigger == TRIGGER_WAKEUP) {
-				ATLOG(atlog, AUDIO_THREAD_IODEV_CB, 0, 0, 0);
-				iodev_cb->cb(iodev_cb->cb_data, 0);
 			}
 		}
 	}
diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c
index f7dffcd..d04d626 100644
--- a/cras/src/server/config/cras_board_config.c
+++ b/cras/src/server/config/cras_board_config.c
@@ -12,7 +12,7 @@
 static const int32_t DEFAULT_OUTPUT_BUFFER_SIZE = 512;
 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 = 0;
+static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1;
 
 #define CONFIG_NAME "board.ini"
 #define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size"
@@ -33,6 +33,7 @@
 	board_config->aec_supported = AEC_SUPPORTED_DEFAULT;
 	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;
 	if (config_path == NULL)
 		return;
 
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index 3e29ca1..792305b 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.
- * jack_always_plugged - true if this node is always plugged even without jack.
  * 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.
@@ -149,7 +148,6 @@
 	enum CRAS_ALSA_CARD_TYPE card_type;
 	int is_first;
 	int fully_specified;
-	int jack_always_plugged;
 	int enable_htimestamp;
 	snd_pcm_t *handle;
 	unsigned int num_severe_underruns;
@@ -798,7 +796,7 @@
 					 ain ? ain->mixer_input : NULL);
 
 	/* For USB device without UCM config, not change a gain control. */
-	if (ain->base.type == CRAS_NODE_TYPE_USB && !aio->ucm)
+	if (ain && ain->base.type == CRAS_NODE_TYPE_USB && !aio->ucm)
 		return;
 
 	/* Set hardware gain to 0dB if software gain is needed. */
@@ -1935,6 +1933,10 @@
 	struct alsa_io *aio = (struct alsa_io *)odev;
 	int rc;
 
+	/* Restart rate estimation because free run internval should not
+	 * be included. */
+	cras_iodev_reset_rate_estimator(odev);
+
 	if (aio->free_running)
 		rc = adjust_appl_ptr_for_leaving_free_run(odev);
 	else
@@ -2053,7 +2055,6 @@
 	aio->is_first = is_first;
 	aio->handle = NULL;
 	aio->num_severe_underruns = 0;
-	aio->jack_always_plugged = 0;
 	if (dev_name) {
 		aio->dev_name = strdup(dev_name);
 		if (!aio->dev_name)
@@ -2318,9 +2319,6 @@
 		} else if (input_node) {
 			input_node->jack = jack;
 		}
-	} else if (aio->card_type == ALSA_CARD_TYPE_USB) {
-		/* No jack is found, assume device is always plugged */
-		aio->jack_always_plugged = 1;
 	}
 	return 0;
 }
@@ -2328,6 +2326,7 @@
 void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev)
 {
 	struct alsa_io *aio = (struct alsa_io *)iodev;
+	struct cras_ionode *node;
 
 	if (!iodev)
 		return;
@@ -2345,12 +2344,13 @@
 
 	/*
 	 * Set plugged for the USB device per card when it appears if
-	 * there is no jack reporting plug status and the jack is set
-	 * to be always plugged.
+	 * there is no jack reporting plug status
 	 */
-	if (aio->card_type == ALSA_CARD_TYPE_USB && aio->jack_always_plugged &&
-	    !get_jack_from_node(iodev->active_node)) {
-		cras_iodev_set_node_plugged(iodev->active_node, 1);
+	if (aio->card_type == ALSA_CARD_TYPE_USB) {
+		DL_FOREACH (iodev->nodes, node) {
+			if (!get_jack_from_node(node))
+				cras_iodev_set_node_plugged(node, 1);
+		}
 	}
 
 	set_default_hotword_model(iodev);
diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c
index 58e5366..52a227e 100644
--- a/cras/src/server/cras_alsa_jack.c
+++ b/cras/src/server/cras_alsa_jack.c
@@ -719,6 +719,21 @@
 	return data->rc;
 }
 
+/* Find ELD control for HDMI/DP gpio jack. */
+static snd_hctl_elem_t *find_eld_control_by_dev_index(snd_hctl_t *hctl,
+						      unsigned int dev_idx)
+{
+	static const char eld_control_name[] = "ELD";
+	snd_ctl_elem_id_t *elem_id;
+
+	snd_ctl_elem_id_alloca(&elem_id);
+	snd_ctl_elem_id_clear(elem_id);
+	snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_PCM);
+	snd_ctl_elem_id_set_device(elem_id, dev_idx);
+	snd_ctl_elem_id_set_name(elem_id, eld_control_name);
+	return snd_hctl_find_elem(hctl, elem_id);
+}
+
 /* Find GPIO jacks for this jack_list.
  * Args:
  *    jack_list - Jack list to add to.
@@ -754,8 +769,16 @@
 		gpio_switch_list_for_each(gpio_switch_list_with_section, &data);
 	else
 		gpio_switch_list_for_each(gpio_switch_list_by_matching, &data);
-	if (result_jack)
+	if (result_jack) {
 		*result_jack = data.result_jack;
+
+		/* Find ELD control for HDMI/DP gpio jack. */
+		if (*result_jack)
+			(*result_jack)->eld_control =
+				find_eld_control_by_dev_index(
+					jack_list->hctl,
+					jack_list->device_index);
+	}
 	return data.rc;
 }
 
@@ -858,7 +881,6 @@
 	static const char *const input_jack_base_names[] = {
 		"Mic Jack",
 	};
-	static const char eld_control_name[] = "ELD";
 	const char *const *jack_names;
 	unsigned int num_jack_names;
 
@@ -932,17 +954,9 @@
 		name = snd_hctl_elem_get_name(jack->elem);
 		if (!is_jack_hdmi_dp(name))
 			continue;
-		for (elem = snd_hctl_first_elem(jack_list->hctl); elem != NULL;
-		     elem = snd_hctl_elem_next(elem)) {
-			if (strcmp(snd_hctl_elem_get_name(elem),
-				   eld_control_name))
-				continue;
-			if (snd_hctl_elem_get_device(elem) !=
-			    jack_list->device_index)
-				continue;
-			jack->eld_control = elem;
-			break;
-		}
+
+		jack->eld_control = find_eld_control_by_dev_index(
+			jack_list->hctl, jack_list->device_index);
 	}
 
 	return 0;
@@ -968,7 +982,6 @@
 				      struct ucm_section *section,
 				      struct cras_alsa_jack **result_jack)
 {
-	static const char eld_control_name[] = "ELD";
 	snd_hctl_elem_t *elem;
 	snd_ctl_elem_id_t *elem_id;
 	struct cras_alsa_jack *jack;
@@ -1019,10 +1032,8 @@
 		return 0;
 
 	/* Look up ELD control. */
-	snd_ctl_elem_id_set_name(elem_id, eld_control_name);
-	elem = snd_hctl_find_elem(jack_list->hctl, elem_id);
-	if (elem)
-		jack->eld_control = elem;
+	jack->eld_control = find_eld_control_by_dev_index(
+		jack_list->hctl, jack_list->device_index);
 	return 0;
 }
 
diff --git a/cras/src/server/cras_alsa_mixer.c b/cras/src/server/cras_alsa_mixer.c
index 99f4d61..1070557 100644
--- a/cras/src/server/cras_alsa_mixer.c
+++ b/cras/src/server/cras_alsa_mixer.c
@@ -417,7 +417,7 @@
 				  int muted)
 {
 	const struct mixer_control_element *elem = NULL;
-	int rc;
+	int rc = -EINVAL;
 	if (!control)
 		return -EINVAL;
 	DL_FOREACH (control->elements, elem) {
@@ -959,9 +959,9 @@
 
 		if (!c->has_volume)
 			continue;
-		mixer_control_set_dBFS(c, to_set);
-		mixer_control_get_dBFS(c, &actual_dB);
-		to_set -= actual_dB;
+		if (mixer_control_set_dBFS(c, to_set) == 0 &&
+		    mixer_control_get_dBFS(c, &actual_dB) == 0)
+			to_set -= actual_dB;
 	}
 	/* Apply the rest to the output-specific control. */
 	if (cras_alsa_mixer_has_volume(mixer_output))
@@ -1002,9 +1002,9 @@
 
 		if (!c->has_volume)
 			continue;
-		mixer_control_set_dBFS(c, to_set);
-		mixer_control_get_dBFS(c, &actual_dB);
-		to_set -= actual_dB;
+		if (mixer_control_set_dBFS(c, to_set) == 0 &&
+		    mixer_control_get_dBFS(c, &actual_dB) == 0)
+			to_set -= actual_dB;
 	}
 
 	/* Apply the reset to input specific control */
diff --git a/cras/src/server/cras_alsa_plugin_io.c b/cras/src/server/cras_alsa_plugin_io.c
index 0d5f379..9c557a4 100644
--- a/cras/src/server/cras_alsa_plugin_io.c
+++ b/cras/src/server/cras_alsa_plugin_io.c
@@ -149,6 +149,16 @@
 
 	DL_APPEND(plugins, plugin);
 
+	ucm_sections = ucm_get_sections(plugin->ucm);
+	DL_FOREACH (ucm_sections, section) {
+		rc = cras_alsa_mixer_add_controls_in_section(plugin->mixer,
+							     section);
+		if (rc)
+			syslog(LOG_ERR,
+			       "Failed adding control to plugin,"
+			       "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 */
@@ -156,7 +166,6 @@
 				  plugin->hctl, direction, DUMMY_USB_VID,
 				  DUMMY_USB_PID, DUMMY_USB_SERIAL_NUMBER);
 
-	ucm_sections = ucm_get_sections(plugin->ucm);
 	DL_FOREACH (ucm_sections, section) {
 		if (section->dir != plugin->iodev->direction)
 			continue;
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index be0ce66..3782cb2 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -789,10 +789,125 @@
 	return -1;
 }
 
+static const char *ucm_get_dir_for_device(struct cras_use_case_mgr *mgr,
+					  const char *dev_name,
+					  enum CRAS_STREAM_DIRECTION *dir)
+{
+	const char *pcm_name;
+
+	pcm_name = ucm_get_playback_device_name_for_dev(mgr, dev_name);
+
+	if (pcm_name) {
+		*dir = CRAS_STREAM_OUTPUT;
+		return pcm_name;
+	}
+
+	pcm_name = ucm_get_capture_device_name_for_dev(mgr, dev_name);
+	if (pcm_name) {
+		*dir = CRAS_STREAM_INPUT;
+		return pcm_name;
+	}
+
+	*dir = CRAS_STREAM_UNDEFINED;
+	return NULL;
+}
+
+static int ucm_parse_device_section(struct cras_use_case_mgr *mgr,
+				    const char *dev,
+				    struct ucm_section **sections)
+{
+	enum CRAS_STREAM_DIRECTION dir;
+	int dev_idx = -1;
+	int dependent_dev_idx = -1;
+	const char *jack_name = NULL;
+	const char *jack_type = NULL;
+	const char *jack_dev = NULL;
+	const char *jack_control = NULL;
+	const char *mixer_name = NULL;
+	struct mixer_name *m_name;
+	int rc = 0;
+	const char *pcm_name;
+	const char *dependent_dev_name = NULL;
+	struct ucm_section *dev_sec;
+	const char *dev_name;
+
+	dev_name = strdup(dev);
+	if (!dev_name)
+		return 0;
+
+	pcm_name = ucm_get_dir_for_device(mgr, dev_name, &dir);
+
+	if (pcm_name)
+		dev_idx = get_device_index_from_target(pcm_name);
+
+	if (dir == CRAS_STREAM_UNDEFINED) {
+		syslog(LOG_ERR,
+		       "UCM configuration for device '%s' missing"
+		       " PlaybackPCM or CapturePCM definition.",
+		       dev_name);
+		rc = -EINVAL;
+		goto error_cleanup;
+	}
+
+	dependent_dev_name =
+		ucm_get_dependent_device_name_for_dev(mgr, dev_name);
+	if (dependent_dev_name) {
+		dependent_dev_idx =
+			get_device_index_from_target(dependent_dev_name);
+	}
+
+	jack_dev = ucm_get_jack_dev_for_dev(mgr, dev_name);
+	jack_control = ucm_get_jack_control_for_dev(mgr, dev_name);
+	if (dir == CRAS_STREAM_OUTPUT)
+		mixer_name = ucm_get_playback_mixer_elem_for_dev(mgr, dev_name);
+	else if (dir == CRAS_STREAM_INPUT)
+		mixer_name = ucm_get_capture_mixer_elem_for_dev(mgr, dev_name);
+
+	if (jack_dev) {
+		jack_name = jack_dev;
+		jack_type = "gpio";
+	} else if (jack_control) {
+		jack_name = jack_control;
+		jack_type = "hctl";
+	}
+
+	dev_sec = ucm_section_create(dev_name, pcm_name, dev_idx,
+				     dependent_dev_idx, dir, jack_name,
+				     jack_type);
+
+	if (!dev_sec) {
+		syslog(LOG_ERR, "Failed to allocate memory.");
+		rc = -ENOMEM;
+		goto error_cleanup;
+	}
+
+	dev_sec->jack_switch = ucm_get_jack_switch_for_dev(mgr, dev_name);
+
+	if (mixer_name) {
+		rc = ucm_section_set_mixer_name(dev_sec, mixer_name);
+		if (rc)
+			goto error_cleanup;
+	}
+
+	m_name = ucm_get_mixer_names(mgr, dev_name, coupled_mixers, dir,
+				     MIXER_NAME_VOLUME);
+	ucm_section_concat_coupled(dev_sec, m_name);
+
+	DL_APPEND(*sections, dev_sec);
+	ucm_section_dump(dev_sec);
+error_cleanup:
+	free((void *)dev_name);
+	free((void *)dependent_dev_name);
+	free((void *)jack_dev);
+	free((void *)jack_control);
+	free((void *)mixer_name);
+	free((void *)pcm_name);
+	return rc;
+}
+
 struct ucm_section *ucm_get_sections(struct cras_use_case_mgr *mgr)
 {
 	struct ucm_section *sections = NULL;
-	struct ucm_section *dev_sec;
 	const char **list;
 	int num_devs;
 	int i;
@@ -806,117 +921,17 @@
 
 	/* snd_use_case_get_list fills list with pairs of device name and
 	 * comment, so device names are in even-indexed elements. */
-	const char *dev_name;
 	for (i = 0; i < num_devs; i += 2) {
-		enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_UNDEFINED;
-		int dev_idx = -1;
-		int dependent_dev_idx = -1;
-		const char *jack_name = NULL;
-		const char *jack_type = NULL;
-		const char *jack_dev;
-		const char *jack_control;
-		const char *mixer_name;
-		struct mixer_name *m_name;
-		int rc;
-		const char *pcm_name;
-		const char *dependent_dev_name;
-
-		dev_name = strdup(list[i]);
-		if (!dev_name)
-			continue;
-
-		pcm_name = ucm_get_playback_device_name_for_dev(mgr, dev_name);
-		if (pcm_name) {
-			dir = CRAS_STREAM_OUTPUT;
-		} else {
-			pcm_name = ucm_get_capture_device_name_for_dev(
-				mgr, dev_name);
-			if (pcm_name)
-				dir = CRAS_STREAM_INPUT;
+		if (ucm_parse_device_section(mgr, list[i], &sections) < 0) {
+			ucm_section_free_list(sections);
+			sections = NULL;
+			break;
 		}
-		if (pcm_name)
-			dev_idx = get_device_index_from_target(pcm_name);
-
-		if (dir == CRAS_STREAM_UNDEFINED) {
-			syslog(LOG_ERR,
-			       "UCM configuration for device '%s' missing"
-			       " PlaybackPCM or CapturePCM definition.",
-			       dev_name);
-			free((void *)pcm_name);
-			goto error_cleanup;
-		}
-
-		dependent_dev_name =
-			ucm_get_dependent_device_name_for_dev(mgr, dev_name);
-		if (dependent_dev_name) {
-			dependent_dev_idx = get_device_index_from_target(
-				dependent_dev_name);
-			free((void *)dependent_dev_name);
-		}
-
-		jack_dev = ucm_get_jack_dev_for_dev(mgr, dev_name);
-		jack_control = ucm_get_jack_control_for_dev(mgr, dev_name);
-		if (dir == CRAS_STREAM_OUTPUT)
-			mixer_name = ucm_get_playback_mixer_elem_for_dev(
-				mgr, dev_name);
-		else if (dir == CRAS_STREAM_INPUT)
-			mixer_name = ucm_get_capture_mixer_elem_for_dev(
-				mgr, dev_name);
-
-		if (jack_dev) {
-			jack_name = jack_dev;
-			jack_type = "gpio";
-		} else if (jack_control) {
-			jack_name = jack_control;
-			jack_type = "hctl";
-		}
-
-		dev_sec = ucm_section_create(dev_name, pcm_name, dev_idx,
-					     dependent_dev_idx, dir, jack_name,
-					     jack_type);
-		if (pcm_name)
-			free((void *)pcm_name);
-		if (jack_dev)
-			free((void *)jack_dev);
-		if (jack_control)
-			free((void *)jack_control);
-
-		if (!dev_sec) {
-			syslog(LOG_ERR, "Failed to allocate memory.");
-			if (mixer_name)
-				free((void *)mixer_name);
-			goto error_cleanup;
-		}
-
-		dev_sec->jack_switch =
-			ucm_get_jack_switch_for_dev(mgr, dev_name);
-
-		if (mixer_name) {
-			rc = ucm_section_set_mixer_name(dev_sec, mixer_name);
-			free((void *)mixer_name);
-			if (rc)
-				goto error_cleanup;
-		}
-
-		m_name = ucm_get_mixer_names(mgr, dev_name, coupled_mixers, dir,
-					     MIXER_NAME_VOLUME);
-		ucm_section_concat_coupled(dev_sec, m_name);
-
-		DL_APPEND(sections, dev_sec);
-		ucm_section_dump(dev_sec);
-		free((void *)dev_name);
 	}
 
 	if (num_devs > 0)
 		snd_use_case_free_list(list, num_devs);
 	return sections;
-
-error_cleanup:
-	if (num_devs > 0)
-		snd_use_case_free_list(list, num_devs);
-	ucm_section_free_list(sections);
-	free((void *)dev_name);
-	return NULL;
 }
 
 char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr)
diff --git a/cras/src/server/cras_alsa_ucm_section.c b/cras/src/server/cras_alsa_ucm_section.c
index 205a189..d4df8c7 100644
--- a/cras/src/server/cras_alsa_ucm_section.c
+++ b/cras/src/server/cras_alsa_ucm_section.c
@@ -13,16 +13,11 @@
 
 static void ucm_section_free(struct ucm_section *section)
 {
-	if (section->name)
-		free((void *)section->name);
-	if (section->pcm_name)
-		free((void *)section->pcm_name);
-	if (section->jack_name)
-		free((void *)section->jack_name);
-	if (section->jack_type)
-		free((void *)section->jack_type);
-	if (section->mixer_name)
-		free((void *)section->mixer_name);
+	free((void *)section->name);
+	free((void *)section->pcm_name);
+	free((void *)section->jack_name);
+	free((void *)section->jack_type);
+	free((void *)section->mixer_name);
 	mixer_name_free(section->coupled);
 	free(section);
 }
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 06b9771..0607ac8 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -1036,12 +1036,23 @@
 	struct sco_options so;
 	socklen_t len = sizeof(so);
 	struct cras_bt_adapter *adapter;
+	uint32_t wbs_pkt_len = 0;
+	socklen_t optlen = sizeof(wbs_pkt_len);
 
 	adapter = cras_bt_adapter_get(device->adapter_obj_path);
 
 	if (cras_bt_adapter_on_usb(adapter)) {
-		return (codec == HFP_CODEC_ID_MSBC) ? USB_MSBC_PKT_SIZE :
-						      USB_CVSD_PKT_SIZE;
+		if (codec == HFP_CODEC_ID_MSBC) {
+			/* BT_SNDMTU and BT_RCVMTU return the same value. */
+			if (getsockopt(sco_socket, SOL_BLUETOOTH, BT_SNDMTU,
+				       &wbs_pkt_len, &optlen))
+				syslog(LOG_ERR, "Failed to get BT_SNDMTU");
+
+			return (wbs_pkt_len > 0) ? wbs_pkt_len :
+						   USB_MSBC_PKT_SIZE;
+		} else {
+			return USB_CVSD_PKT_SIZE;
+		}
 	}
 
 	/* For non-USB cases, query the SCO MTU from driver. */
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 2062b9f..3cffe14 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -522,7 +522,7 @@
 	 * point it to the first profile dev. */
 	active = (struct bt_node *)calloc(1, sizeof(*active));
 	if (!active)
-		return NULL;
+		goto error;
 	active->base.dev = iodev;
 	active->base.idx = btio->next_node_id++;
 	active->base.type = dev->active_node->type;
diff --git a/cras/src/server/cras_bt_transport.c b/cras/src/server/cras_bt_transport.c
index d137dbb..402cd75 100644
--- a/cras/src/server/cras_bt_transport.c
+++ b/cras/src/server/cras_bt_transport.c
@@ -287,17 +287,24 @@
 		} else if (type == DBUS_TYPE_OBJECT_PATH) {
 			const char *obj_path;
 
-			/* Property: object Device [readonly] */
-			dbus_message_iter_get_basic(&variant_iter, &obj_path);
-			transport->device = cras_bt_device_get(obj_path);
-			if (!transport->device) {
-				syslog(LOG_ERR,
-				       "Device %s not found at update"
-				       "transport properties",
-				       obj_path);
-				transport->device = cras_bt_device_create(
-					transport->conn, obj_path);
-				cras_bt_transport_update_device(transport);
+			if (strcmp(key, "Device") == 0) {
+				/* Property: object Device [readonly] */
+				dbus_message_iter_get_basic(&variant_iter,
+							    &obj_path);
+				transport->device =
+					cras_bt_device_get(obj_path);
+				if (!transport->device) {
+					syslog(LOG_ERR,
+					       "Device %s not found at update "
+					       "transport properties",
+					       obj_path);
+					transport->device =
+						cras_bt_device_create(
+							transport->conn,
+							obj_path);
+					cras_bt_transport_update_device(
+						transport);
+				}
 			}
 		} else if (strcmp(dbus_message_iter_get_signature(&variant_iter),
 				  "ay") == 0 &&
@@ -324,6 +331,7 @@
 
 			dbus_message_iter_get_basic(&variant_iter, &volume);
 			transport->volume = volume;
+			BTLOG(btlog, BT_TRANSPORT_UPDATE_VOLUME, volume, 0);
 			cras_bt_transport_update_device(transport);
 		}
 
@@ -377,6 +385,7 @@
 	DBusMessageIter message_iter, variant;
 	DBusPendingCall *pending_call;
 
+	BTLOG(btlog, BT_TRANSPORT_SET_VOLUME, volume, 0);
 	method_call =
 		dbus_message_new_method_call(BLUEZ_SERVICE,
 					     transport->object_path,
diff --git a/cras/src/server/cras_control_rclient.c b/cras/src/server/cras_control_rclient.c
index 2b30fe5..3906a23 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_main_thread_log.h"
 #include "cras_messages.h"
 #include "cras_observer.h"
 #include "cras_rclient.h"
@@ -401,6 +402,19 @@
 	case CRAS_SERVER_GET_ATLOG_FD:
 		get_atlog_fd(client);
 		break;
+	case CRAS_SERVER_DUMP_MAIN: {
+		struct cras_client_audio_debug_info_ready msg;
+		struct cras_server_state *state;
+
+		state = cras_system_state_get_no_lock();
+		memcpy(&state->main_thread_debug_info.main_log, main_log,
+		       sizeof(struct main_thread_event_log));
+
+		cras_fill_client_audio_debug_info_ready(&msg);
+		client->ops->send_message_to_client(client, &msg.header, NULL,
+						    0);
+		break;
+	}
 	case CRAS_SERVER_DUMP_BT: {
 		struct cras_client_audio_debug_info_ready msg;
 		struct cras_server_state *state;
@@ -463,7 +477,7 @@
 		    m->num_channels > CRAS_MAX_REMIX_CHANNELS)
 			return -EINVAL;
 		const size_t coefficient_len =
-			m->num_channels * m->num_channels;
+			(size_t)m->num_channels * (size_t)m->num_channels;
 		const size_t size_with_coefficients =
 			sizeof(*m) +
 			coefficient_len * sizeof(m->coefficient[0]);
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 0a60c67..628ec22 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -17,6 +17,7 @@
 #include "cras_dbus_util.h"
 #include "cras_hfp_ag_profile.h"
 #include "cras_iodev_list.h"
+#include "cras_main_thread_log.h"
 #include "cras_observer.h"
 #include "cras_system_state.h"
 #include "cras_utf8.h"
@@ -92,9 +93,6 @@
 	"    <method name=\"RemoveActiveOutputNode\">\n"                        \
 	"      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"           \
 	"    </method>\n"                                                       \
-	"    <method name=\"SetNextHandsfreeProfile\">\n"                       \
-	"      <arg name=\"toggle\" type=\"b\" direction=\"in\"/>\n"            \
-	"    </method>\n"                                                       \
 	"    <method name=\"SetFixA2dpPacketSize\">\n"                          \
 	"      <arg name=\"toggle\" type=\"b\" direction=\"in\"/>\n"            \
 	"    </method>\n"                                                       \
@@ -133,6 +131,8 @@
 	"    <method name=\"SetPlayerMetadata\">\n"                             \
 	"      <arg name=\"metadata\" type=\"a{sv}\" direction=\"in\"/>\n"      \
 	"    </method>\n"                                                       \
+	"    <method name=\"ResendBluetoothBattery\">\n"                        \
+	"    </method>\n"                                                       \
 	"  </interface>\n"                                                      \
 	"  <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"            \
 	"    <method name=\"Introspect\">\n"                                    \
@@ -369,6 +369,7 @@
 		return rc;
 
 	cras_system_set_user_mute(new_mute);
+	MAINLOG(main_log, MAIN_THREAD_SET_OUTPUT_USER_MUTE, new_mute, 0, 0);
 
 	send_empty_reply(conn, message);
 
@@ -497,10 +498,8 @@
 	dbus_uint64_t id;
 	const char *dev_name = dev->name;
 	dbus_uint64_t stable_dev_id = node->stable_id;
-	dbus_uint32_t max_supported_channels = dev->max_supported_channels;
 	const char *node_type = node->type;
 	const char *node_name = node->name;
-	const char *mic_positions = node->mic_positions;
 	dbus_bool_t active;
 	dbus_uint64_t plugged_time = node->plugged_time.tv_sec * 1000000ULL +
 				     node->plugged_time.tv_usec;
@@ -540,19 +539,12 @@
 	if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
 			      DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
 		return FALSE;
-	if (!append_key_value(&dict, "MaxSupportedChannels", DBUS_TYPE_UINT32,
-			      DBUS_TYPE_UINT32_AS_STRING,
-			      &max_supported_channels))
-		return FALSE;
 	if (!append_key_value(&dict, "Type", DBUS_TYPE_STRING,
 			      DBUS_TYPE_STRING_AS_STRING, &node_type))
 		return FALSE;
 	if (!append_key_value(&dict, "Name", DBUS_TYPE_STRING,
 			      DBUS_TYPE_STRING_AS_STRING, &node_name))
 		return FALSE;
-	if (!append_key_value(&dict, "MicPositions", DBUS_TYPE_STRING,
-			      DBUS_TYPE_STRING_AS_STRING, &mic_positions))
-		return FALSE;
 	if (!append_key_value(&dict, "Active", DBUS_TYPE_BOOLEAN,
 			      DBUS_TYPE_BOOLEAN_AS_STRING, &active))
 		return FALSE;
@@ -732,28 +724,6 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-static DBusHandlerResult handle_set_next_handsfree_profile(DBusConnection *conn,
-							   DBusMessage *message,
-							   void *arg)
-{
-	int rc;
-	dbus_bool_t enabled;
-
-	rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &enabled);
-	if (rc)
-		return rc;
-
-	/* Change HFP version to register with BlueZ and the
-	 * wbs enabled flag for codec negotiation in SLC.
-	 */
-	cras_hfp_ag_profile_next_handsfree(conn, enabled);
-	cras_system_set_bt_wbs_enabled(enabled);
-
-	send_empty_reply(conn, message);
-
-	return DBUS_HANDLER_RESULT_HANDLED;
-}
-
 static DBusHandlerResult handle_set_fix_a2dp_packet_size(DBusConnection *conn,
 							 DBusMessage *message,
 							 void *arg)
@@ -1004,6 +974,17 @@
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static DBusHandlerResult handle_resend_bluetooth_battery(DBusConnection *conn,
+							 DBusMessage *message,
+							 void *arg)
+{
+	cras_hfp_ag_resend_device_battery_level();
+
+	send_empty_reply(conn, message);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 /* Handle incoming messages. */
 static DBusHandlerResult handle_control_message(DBusConnection *conn,
 						DBusMessage *message, void *arg)
@@ -1095,9 +1076,6 @@
 		return handle_rm_active_node(conn, message, arg,
 					     CRAS_STREAM_OUTPUT);
 	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
-					       "SetNextHandsfreeProfile")) {
-		return handle_set_next_handsfree_profile(conn, message, arg);
-	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
 					       "SetFixA2dpPacketSize")) {
 		return handle_set_fix_a2dp_packet_size(conn, message, arg);
 	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
@@ -1138,6 +1116,9 @@
 	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
 					       "SetPlayerMetadata")) {
 		return handle_set_player_metadata(conn, message, arg);
+	} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
+					       "ResendBluetoothBattery")) {
+		return handle_resend_bluetooth_battery(conn, message, arg);
 	}
 
 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
diff --git a/cras/src/server/cras_dsp_pipeline.c b/cras/src/server/cras_dsp_pipeline.c
index 17b5de5..9e492ac 100644
--- a/cras/src/server/cras_dsp_pipeline.c
+++ b/cras/src/server/cras_dsp_pipeline.c
@@ -435,6 +435,7 @@
 
 	if (rc < 0) {
 		syslog(LOG_ERR, "failed to construct pipeline");
+		cras_dsp_pipeline_free(pipeline);
 		return NULL;
 	}
 
@@ -524,6 +525,17 @@
 			peak_buf = MAX(peak_buf, need_buf);
 		}
 	}
+	/*
+	 * cras_dsp_pipeline_create creates pipeline with source and sink and it
+	 * makes sure all ports could be accessed from some sources, which means
+	 * that there is at least one source with out > 0 and in == 0.
+	 * This will give us peak_buf > 0 in the previous calculation.
+	 */
+	if (peak_buf <= 0) {
+		syslog(LOG_ERR, "peak_buf = %d, which must be greater than 0.",
+		       peak_buf);
+		return -1;
+	}
 
 	/* then allocate the buffers */
 	pipeline->peak_buf = peak_buf;
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index 71ee366..478452d 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -384,11 +384,12 @@
 		conv->ch_conv_mtx = cras_channel_conv_matrix_create(in, out);
 		if (conv->ch_conv_mtx == NULL) {
 			syslog(LOG_ERR,
-			       "Failed to create channel conversion matrix");
-			cras_fmt_conv_destroy(&conv);
-			return NULL;
+			       "Failed to create channel conversion matrix."
+			       "Fallback to default_all_to_all.");
+			conv->channel_converter = default_all_to_all;
+		} else {
+			conv->channel_converter = convert_channels;
 		}
-		conv->channel_converter = convert_channels;
 	}
 	/* Set up sample rate conversion. */
 	if (in->frame_rate != out->frame_rate) {
diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c
index d75ce1d..87358af 100644
--- a/cras/src/server/cras_fmt_conv_ops.c
+++ b/cras/src/server/cras_fmt_conv_ops.c
@@ -331,14 +331,22 @@
 	unsigned int in_ch, out_ch, i;
 	const int16_t *in = (const int16_t *)_in;
 	int16_t *out = (int16_t *)_out;
+	int32_t sum;
 
-	memset(out, 0, in_frames * cras_get_format_bytes(out_fmt));
-	for (out_ch = 0; out_ch < num_out_ch; out_ch++) {
+	for (i = 0; i < in_frames; i++) {
+		sum = 0;
 		for (in_ch = 0; in_ch < num_in_ch; in_ch++) {
-			for (i = 0; i < in_frames; i++) {
-				out[out_ch + i * num_out_ch] +=
-					in[in_ch + i * num_in_ch] / num_in_ch;
-			}
+			sum += (int32_t)in[in_ch + i * num_in_ch];
+		}
+		/*
+		 * 1. Divide `int32_t` by `size_t` without an explicit
+		 *    conversion will generate corrupted results.
+		 * 2. After the division, `sum` should be in the range of
+		 *    int16_t. No clipping is needed.
+		 */
+		sum /= (int32_t)num_in_ch;
+		for (out_ch = 0; out_ch < num_out_ch; out_ch++) {
+			out[out_ch + i * num_out_ch] = (int16_t)sum;
 		}
 	}
 	return in_frames;
diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c
index b433635..8cf4a43 100644
--- a/cras/src/server/cras_hfp_ag_profile.c
+++ b/cras/src/server/cras_hfp_ag_profile.c
@@ -20,12 +20,12 @@
 #include "cras_server_metrics.h"
 #include "cras_system_state.h"
 #include "cras_iodev_list.h"
+#include "cras_observer.h"
 #include "utlist.h"
 
 #define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway"
 #define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG"
-#define HFP_VERSION_1_5 0x0105
-#define HFP_VERSION_1_7 0x0107
+#define HFP_VERSION 0x0107
 #define HSP_AG_PROFILE_NAME "Headset Voice gateway"
 #define HSP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HSPAG"
 #define HSP_VERSION_1_2 0x0102
@@ -76,8 +76,7 @@
 #define BSRF_SUPPORTED_FEATURES (AG_ENHANCED_CALL_STATUS | AG_HF_INDICATORS)
 
 /* The "SupportedFeatures" attribute value of HFP AG service record in CRAS. */
-#define SDP_SUPPORTED_FEATURES 0
-#define SDP_SUPPORTED_FEATURES_1_7 FEATURES_AG_WIDE_BAND_SPEECH
+#define SDP_SUPPORTED_FEATURES FEATURES_AG_WIDE_BAND_SPEECH
 
 /* Object representing the audio gateway role for HFP/HSP.
  * Members:
@@ -269,7 +268,7 @@
 	 * control whether to turn on WBS feature.
 	 */
 	ag_features = BSRF_SUPPORTED_FEATURES;
-	if (cras_system_get_bt_wbs_enabled() &&
+	if (cras_system_get_bt_wbs_enabled() && adapter &&
 	    cras_bt_adapter_wbs_supported(adapter))
 		ag_features |= AG_CODEC_NEGOTIATION;
 
@@ -289,10 +288,10 @@
 
 	DL_FOREACH (connected_ags, ag) {
 		if (ag->slc_handle && ag->device == device) {
-			destroy_audio_gateway(ag);
 			cras_bt_device_notify_profile_dropped(
 				ag->device,
 				CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
+			destroy_audio_gateway(ag);
 		}
 	}
 }
@@ -305,7 +304,7 @@
 	.name = HFP_AG_PROFILE_NAME,
 	.object_path = HFP_AG_PROFILE_PATH,
 	.uuid = HFP_AG_UUID,
-	.version = HFP_VERSION_1_5,
+	.version = HFP_VERSION,
 	.role = NULL,
 	.features = SDP_SUPPORTED_FEATURES,
 	.record = NULL,
@@ -315,30 +314,6 @@
 	.cancel = cras_hfp_ag_cancel
 };
 
-int cras_hfp_ag_profile_next_handsfree(DBusConnection *conn, bool enabled)
-{
-	unsigned int target_version = HFP_VERSION_1_5;
-	unsigned int target_feature = SDP_SUPPORTED_FEATURES;
-
-	if (enabled) {
-		target_version = HFP_VERSION_1_7;
-		target_feature = SDP_SUPPORTED_FEATURES_1_7;
-	}
-
-	if (cras_hfp_ag_profile.version == target_version)
-		return 0;
-
-	cras_bt_unregister_profile(conn, &cras_hfp_ag_profile);
-	cras_bt_rm_profile(conn, &cras_hfp_ag_profile);
-
-	cras_hfp_ag_profile.features = target_feature;
-	cras_hfp_ag_profile.version = target_version;
-
-	cras_bt_add_profile(conn, &cras_hfp_ag_profile);
-	cras_bt_register_profile(conn, &cras_hfp_ag_profile);
-	return 0;
-}
-
 int cras_hfp_ag_profile_create(DBusConnection *conn)
 {
 	return cras_bt_add_profile(conn, &cras_hfp_ag_profile);
@@ -385,9 +360,9 @@
 
 	DL_FOREACH (connected_ags, ag) {
 		if (ag->slc_handle && ag->device == device) {
-			destroy_audio_gateway(ag);
 			cras_bt_device_notify_profile_dropped(
 				ag->device, CRAS_BT_DEVICE_PROFILE_HSP_HEADSET);
+			destroy_audio_gateway(ag);
 		}
 	}
 }
@@ -478,6 +453,19 @@
 	return NULL;
 }
 
+void cras_hfp_ag_resend_device_battery_level()
+{
+	struct audio_gateway *ag;
+	int level;
+	DL_FOREACH (connected_ags, ag) {
+		level = hfp_slc_get_hf_battery_level(ag->slc_handle);
+		if (level >= 0 && level <= 100)
+			cras_observer_notify_bt_battery_changed(
+				cras_bt_device_address(ag->device),
+				(uint32_t)(level));
+	}
+}
+
 int cras_hsp_ag_profile_create(DBusConnection *conn)
 {
 	return cras_bt_add_profile(conn, &cras_hsp_ag_profile);
diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h
index 6a411b1..fb58efb 100644
--- a/cras/src/server/cras_hfp_ag_profile.h
+++ b/cras/src/server/cras_hfp_ag_profile.h
@@ -26,12 +26,6 @@
 
 struct hfp_slc_handle;
 
-/* Re-registers HFP to the next version. Currently on HFP 1.5 and next is 1.7.
- * Args:
- *    enabled - True to register HFP 1.7 otherwise to HFP 1.5
- */
-int cras_hfp_ag_profile_next_handsfree(DBusConnection *conn, bool enabled);
-
 /* Adds a profile instance for HFP AG (Hands-Free Profile Audio Gateway). */
 int cras_hfp_ag_profile_create(DBusConnection *conn);
 
@@ -59,4 +53,8 @@
 /* Gets the SLC handle for given cras_bt_device. */
 struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device);
 
+/* Iterate all possible AGs (theoratically only one) and signal its battery
+ * level */
+void cras_hfp_ag_resend_device_battery_level();
+
 #endif /* CRAS_HFP_AG_PROFILE_H_ */
diff --git a/cras/src/server/cras_hfp_alsa_iodev.c b/cras/src/server/cras_hfp_alsa_iodev.c
index 2ca7901..532b6c4 100644
--- a/cras/src/server/cras_hfp_alsa_iodev.c
+++ b/cras/src/server/cras_hfp_alsa_iodev.c
@@ -52,18 +52,7 @@
 
 static int hfp_alsa_update_supported_formats(struct cras_iodev *iodev)
 {
-	struct hfp_alsa_io *hfp_alsa_io = (struct hfp_alsa_io *)iodev;
-	struct cras_iodev *aio = hfp_alsa_io->aio;
-
 	/* 16 bit, mono, 8kHz (narrow band speech); */
-	free(aio->format);
-	aio->format = malloc(sizeof(struct cras_audio_format));
-	if (!aio->format)
-		return -ENOMEM;
-	aio->format->format = SND_PCM_FORMAT_S16_LE;
-	aio->format->frame_rate = 8000;
-	aio->format->num_channels = 1;
-
 	free(iodev->supported_rates);
 	iodev->supported_rates = malloc(2 * sizeof(*iodev->supported_rates));
 	if (!iodev->supported_rates)
@@ -96,6 +85,15 @@
 	struct cras_iodev *aio = hfp_alsa_io->aio;
 	int rc;
 
+	/* Fill back the format iodev is using. */
+	if (aio->format == NULL) {
+		aio->format = (struct cras_audio_format *)malloc(
+			sizeof(*aio->format));
+		if (!aio->format)
+			return -ENOMEM;
+		*aio->format = *iodev->format;
+	}
+
 	rc = aio->configure_dev(aio);
 	if (rc) {
 		syslog(LOG_ERR, "Failed to configure aio: %d\n", rc);
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index bc916d7..550de58 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -40,11 +40,21 @@
 /* For one mSBC 1 compressed wideband audio channel the HCI packets will
  * be 3 octets of HCI header + 60 octets of data. */
 #define MSBC_PKT_SIZE 60
-#define WRITE_BUF_SIZE_BYTES MSBC_PKT_SIZE
-#define HCI_SCO_PKT_SIZE (MSBC_PKT_SIZE)
 
 #define H2_HEADER_0 0x01
 
+/* Supported HCI SCO packet sizes. The wideband speech mSBC frame parsing
+ * code ties to limited packet size values. Specifically list them out
+ * to check against when setting packet size.
+ *
+ * Temp buffer size should be set to least common multiple of HCI SCO packet
+ * size and MSBC_PKT_SIZE for optimizing buffer copy.
+ * To add a new supported packet size value, add corresponding entry to the
+ * lists, test the read/write msbc code, and fix the code if needed.
+ */
+static const size_t wbs_supported_packet_size[] = { 60, 24, 0 };
+static const size_t wbs_hci_sco_buffer_size[] = { 60, 120, 0 };
+
 /* Second octet of H2 header is composed by 4 bits fixed 0x8 and 4 bits
  * sequence number 0000, 0011, 1100, 1111. */
 static const uint8_t h2_header_frames_count[] = { 0x08, 0x38, 0xc8, 0xf8 };
@@ -70,11 +80,19 @@
  *     read_cb - Callback to call when SCO socket can read. It returns the
  *         number of PCM bytes read.
  *     write_cb - Callback to call when SCO socket can write.
- *     hci_sco_buf - Buffer to read one HCI SCO packet.
+ *     write_buf - Temp buffer for writeing HCI SCO packet in wideband.
+ *     read_buf - Temp buffer for reading HCI SCO packet in wideband.
  *     input_format_bytes - The audio format bytes for input device. 0 means
  *         there is no input device for the hfp_info.
  *     output_format_bytes - The audio format bytes for output device. 0 means
  *         there is no output device for the hfp_info.
+ *     write_wp - Write pointer of write_buf.
+ *     write_rp - Read pointer of write_buf.
+ *     read_wp - Write pointer of read_buf.
+ *     read_rp - Read pointer of read_buf.
+ *     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.
  */
 struct hfp_info {
 	int fd;
@@ -91,10 +109,16 @@
 	unsigned int msbc_num_lost_frames;
 	int (*read_cb)(struct hfp_info *info);
 	int (*write_cb)(struct hfp_info *info);
-	uint8_t write_buf[WRITE_BUF_SIZE_BYTES];
-	uint8_t hci_sco_buf[HCI_SCO_PKT_SIZE];
+	uint8_t *write_buf;
+	uint8_t *read_buf;
 	size_t input_format_bytes;
 	size_t output_format_bytes;
+	size_t write_wp;
+	size_t write_rp;
+	size_t read_wp;
+	size_t read_rp;
+	int (*read_align_cb)(uint8_t *buf);
+	bool msbc_read_current_corrupted;
 };
 
 int hfp_info_add_iodev(struct hfp_info *info,
@@ -243,6 +267,9 @@
 	uint8_t *samples;
 	uint8_t *wp;
 
+	if (info->write_rp + info->packet_size <= info->write_wp)
+		goto msbc_send_again;
+
 	/* Make sure there are MSBC_CODE_SIZE bytes to encode. */
 	samples = buf_read_pointer_size(info->playback_buf, &pcm_avail);
 	if (pcm_avail < MSBC_CODE_SIZE) {
@@ -261,31 +288,41 @@
 	}
 
 	/* Encode the next MSBC_CODE_SIZE of bytes. */
-	wp = info->write_buf;
+	wp = info->write_buf + info->write_wp;
 	wp[0] = H2_HEADER_0;
 	wp[1] = h2_header_frames_count[info->msbc_num_out_frames % 4];
 	pcm_encoded = info->msbc_write->encode(
 		info->msbc_write, samples, pcm_avail, wp + MSBC_H2_HEADER_LEN,
-		WRITE_BUF_SIZE_BYTES - MSBC_H2_HEADER_LEN, &encoded);
+		MSBC_PKT_SIZE - MSBC_H2_HEADER_LEN, &encoded);
 	if (pcm_encoded < 0) {
 		syslog(LOG_ERR, "msbc encoding err: %s", strerror(pcm_encoded));
 		return pcm_encoded;
 	}
 	buf_increment_read(info->playback_buf, pcm_encoded);
 	pcm_avail -= pcm_encoded;
+	info->write_wp += MSBC_PKT_SIZE;
+	info->msbc_num_out_frames++;
+
+	if (info->write_rp + info->packet_size > info->write_wp)
+		return 0;
 
 msbc_send_again:
-	err = send(info->fd, info->write_buf, MSBC_PKT_SIZE, 0);
+	err = send(info->fd, info->write_buf + info->write_rp,
+		   info->packet_size, 0);
 	if (err < 0) {
 		if (errno == EINTR)
 			goto msbc_send_again;
 		return err;
 	}
-	if (err != MSBC_PKT_SIZE) {
+	if (err != (int)info->packet_size) {
 		syslog(LOG_ERR, "Partially write %d bytes for mSBC", err);
 		return -1;
 	}
-	info->msbc_num_out_frames++;
+	info->write_rp += info->packet_size;
+	if (info->write_rp == info->write_wp) {
+		info->write_rp = 0;
+		info->write_wp = 0;
+	}
 
 	return err;
 }
@@ -396,6 +433,16 @@
 	return decoded;
 }
 
+/* Checks if mSBC frame header aligns with the beginning of buffer. */
+static int msbc_frame_align(uint8_t *buf)
+{
+	if ((buf[0] != H2_HEADER_0) || (buf[2] != MSBC_SYNC_WORD)) {
+		syslog(LOG_DEBUG, "Waiting for valid mSBC frame head");
+		return 0;
+	}
+	return 1;
+}
+
 int hfp_read_msbc(struct hfp_info *info)
 {
 	int err = 0;
@@ -411,14 +458,15 @@
 	struct iovec iov;
 	struct cmsghdr *cmsg;
 	const unsigned int control_size = CMSG_SPACE(sizeof(int));
-	char control[control_size] = { 0 };
+	char control[control_size];
 	uint8_t pkt_status;
 
+	memset(control, 0, sizeof(control));
 recv_msbc_bytes:
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
-	iov.iov_base = info->hci_sco_buf;
-	iov.iov_len = HCI_SCO_PKT_SIZE;
+	iov.iov_base = info->read_buf + info->read_wp;
+	iov.iov_len = info->packet_size;
 	msg.msg_control = control;
 	msg.msg_controllen = control_size;
 
@@ -433,11 +481,21 @@
 	 * Treat return code 0 (socket shutdown) as error here. BT stack
 	 * shall send signal to main thread for device disconnection.
 	 */
-	if (err != HCI_SCO_PKT_SIZE) {
+	if (err != (int)info->packet_size) {
 		syslog(LOG_ERR, "Partially read %d bytes for mSBC packet", err);
 		return -1;
 	}
 
+	/* Offset in input data breaks mSBC frame parsing. Discard this packet
+	 * until read alignment succeed. */
+	if (info->read_align_cb) {
+		if (!info->read_align_cb(info->read_buf))
+			return 0;
+		else
+			info->read_align_cb = NULL;
+	}
+	info->read_wp += err;
+
 	pkt_status = 0;
 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
 	     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@@ -454,26 +512,47 @@
 	 * 0x01 - possibly invalid data.
 	 * 0x10 - No data received.
 	 * 0x11 - Data partially lost.
+	 *
+	 * If the latest SCO packet read doesn't cross the boundary of a mSBC
+	 * frame, the packet status flag can be used to derive if the current
+	 * mSBC frame is corrupted.
 	 */
-	if (pkt_status) {
-		syslog(LOG_ERR, "HCI SCO status flag %u", pkt_status);
-		return handle_packet_loss(info);
+	if (info->read_rp + MSBC_PKT_SIZE >= info->read_wp)
+		info->msbc_read_current_corrupted |= (pkt_status > 0);
+
+	/* Read buffer not enough to parse another mSBC frame. */
+	if (info->read_rp + MSBC_PKT_SIZE > info->read_wp)
+		return 0;
+
+	if (info->msbc_read_current_corrupted) {
+		syslog(LOG_DEBUG, "mSBC frame corrputed from packet status");
+		info->msbc_read_current_corrupted = 0;
+		frame_head = NULL;
+	} else {
+		frame_head =
+			extract_msbc_frame(info->read_buf + info->read_rp,
+					   info->read_wp - info->read_rp, &seq);
+		if (!frame_head)
+			syslog(LOG_DEBUG, "Failed to extract msbc frame");
 	}
 
-	/* There is chance that erroneous data reporting gives us false positive.
-	 * If mSBC frame extraction fails, we shall handle it as packet loss.
+	/*
+	 * Done with parsing the raw bytes just read. If mSBC frame head not
+	 * found, we shall handle it as packet loss.
 	 */
-	frame_head = extract_msbc_frame(info->hci_sco_buf, MSBC_PKT_SIZE, &seq);
-	if (!frame_head) {
-		syslog(LOG_ERR, "Failed to extract msbc frame");
-		return handle_packet_loss(info);
+	info->read_rp += MSBC_PKT_SIZE;
+	if (info->read_rp == info->read_wp) {
+		info->read_rp = 0;
+		info->read_wp = 0;
 	}
+	if (!frame_head)
+		return handle_packet_loss(info);
 
 	/*
 	 * Consider packet loss when found discontinuity in sequence number.
 	 */
 	while (seq != (info->msbc_num_in_frames % 4)) {
-		syslog(LOG_ERR, "SCO packet seq unmatch");
+		syslog(LOG_DEBUG, "SCO packet seq unmatch");
 		err = handle_packet_loss(info);
 		if (err < 0)
 			return err;
@@ -660,6 +739,22 @@
 	buf_reset(info->capture_buf);
 
 	if (codec == HFP_CODEC_ID_MSBC) {
+		int i;
+		for (i = 0; wbs_supported_packet_size[i] != 0; i++) {
+			if (info->packet_size == wbs_supported_packet_size[i])
+				break;
+		}
+		/* In case of unsupported value, error log and fallback to
+		 * MSBC_PKT_SIZE(60). */
+		if (wbs_supported_packet_size[i] == 0) {
+			syslog(LOG_ERR, "Unsupported packet size %u",
+			       info->packet_size);
+			i = 0;
+		}
+		info->packet_size = wbs_supported_packet_size[i];
+		info->write_buf = (uint8_t *)malloc(wbs_hci_sco_buffer_size[i]);
+		info->read_buf = (uint8_t *)malloc(wbs_hci_sco_buffer_size[i]);
+
 		info->write_cb = hfp_write_msbc;
 		info->read_cb = hfp_read_msbc;
 		info->msbc_read = cras_msbc_codec_create();
@@ -677,6 +772,15 @@
 	info->msbc_num_out_frames = 0;
 	info->msbc_num_in_frames = 0;
 	info->msbc_num_lost_frames = 0;
+	info->write_rp = 0;
+	info->write_wp = 0;
+	info->read_rp = 0;
+	info->read_wp = 0;
+
+	/* Mark as aligned if packet size equals to MSBC_PKT_SIZE. */
+	info->read_align_cb =
+		(info->packet_size == MSBC_PKT_SIZE) ? NULL : msbc_frame_align;
+	info->msbc_read_current_corrupted = 0;
 
 	return 0;
 }
@@ -697,6 +801,11 @@
 	info->write_cb = NULL;
 	info->read_cb = NULL;
 
+	if (info->write_buf)
+		free(info->write_buf);
+	if (info->read_buf)
+		free(info->read_buf);
+
 	if (info->msbc_read) {
 		cras_sbc_codec_destroy(info->msbc_read);
 		info->msbc_read = NULL;
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index ff56d57..1cf003a 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -836,6 +836,7 @@
 			       gain);
 			return hfp_send(handle, AT_CMD("ERROR"));
 		}
+		BTLOG(btlog, BT_HFP_UPDATE_SPEAKER_GAIN, gain, 0);
 		cras_bt_device_update_hardware_volume(handle->device,
 						      (gain + 1) * 100 / 16);
 	}
@@ -903,6 +904,7 @@
 
 	/* Normailize gain value to 0-15 */
 	gain = gain * 15 / 100;
+	BTLOG(btlog, BT_HFP_SET_SPEAKER_GAIN, gain, 0);
 	snprintf(command, 128, AT_CMD("+VGS=%d"), gain);
 
 	return hfp_send(handle, command);
@@ -1297,3 +1299,8 @@
 {
 	return handle->hf_supports_battery_indicator;
 }
+
+int hfp_slc_get_hf_battery_level(struct hfp_slc_handle *handle)
+{
+	return handle->hf_battery;
+}
diff --git a/cras/src/server/cras_hfp_slc.h b/cras/src/server/cras_hfp_slc.h
index 90c1e79..c3cdc11 100644
--- a/cras/src/server/cras_hfp_slc.h
+++ b/cras/src/server/cras_hfp_slc.h
@@ -145,6 +145,10 @@
  * Apple, HFP, none, or both. */
 int hfp_slc_get_hf_supports_battery_indicator(struct hfp_slc_handle *handle);
 
+/* Gets the battery level for the HF. The data ranges 0 ~ 100. Use -1 for no
+ * battery level reported.*/
+int hfp_slc_get_hf_battery_level(struct hfp_slc_handle *handle);
+
 /* Init the codec negotiation process if needed. */
 int hfp_slc_codec_connection_setup(struct hfp_slc_handle *handle);
 
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index 60dc34c..f426eb5 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -21,6 +21,7 @@
 #include "cras_dsp_pipeline.h"
 #include "cras_fmt_conv.h"
 #include "cras_iodev.h"
+#include "cras_main_thread_log.h"
 #include "cras_iodev_list.h"
 #include "cras_mix.h"
 #include "cras_ramp.h"
@@ -685,6 +686,8 @@
 	if (node->plugged == plugged)
 		return;
 	node->plugged = plugged;
+	MAINLOG(main_log, MAIN_THREAD_NODE_PLUGGED, node->dev->info.idx,
+		plugged, 0);
 	if (plugged) {
 		gettimeofday(&node->plugged_time, NULL);
 	} else if (node == node->dev->active_node) {
@@ -1016,7 +1019,8 @@
 
 	rc = iodev->close_dev(iodev);
 	if (rc)
-		return rc;
+		syslog(LOG_ERR, "Error closing dev %s, rc %d", iodev->info.name,
+		       rc);
 	iodev->state = CRAS_IODEV_STATE_CLOSE;
 	if (iodev->ramp)
 		cras_ramp_reset(iodev->ramp);
@@ -1271,9 +1275,6 @@
 	int rc;
 
 	rc = iodev->frames_queued(iodev, hw_tstamp);
-	if (rc == -EPIPE)
-		cras_audio_thread_event_severe_underrun();
-
 	if (rc < 0)
 		return rc;
 
@@ -1323,7 +1324,7 @@
 
 		/* This assumes consecutive channel areas. */
 		buf = area->channels[0].buf;
-		memset(buf, 0, frames_written * frame_bytes);
+		memset(buf, 0, (size_t)frames_written * (size_t)frame_bytes);
 		cras_iodev_put_output_buffer(odev, buf, frames_written, NULL,
 					     NULL);
 		frames -= frames_written;
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index bdaa36f..553cb80 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -103,9 +103,6 @@
  *    left_right_swapped - If left and right output channels are swapped.
  *    type - Type displayed to the user.
  *    position - Specify where on the system this node locates.
- *    mic_positions - Whitespace-separated microphone positions using Cartesian
- *      coordinates in meters with ordering x, y, z. The string is formatted as:
- *      "x1 y1 z1 ... xn yn zn" for an n-microphone array.
  *    name - Name displayed to the user.
  *    dsp_name - The "DspName" variable specified in the ucm config.
  *    active_hotword_model - name of the currently selected hotword model.
@@ -129,7 +126,6 @@
 	int left_right_swapped;
 	enum CRAS_NODE_TYPE type;
 	enum CRAS_NODE_POSITION position;
-	char mic_positions[CRAS_NODE_MIC_POS_BUFFER_SIZE];
 	char name[CRAS_NODE_NAME_BUFFER_SIZE];
 	const char *dsp_name;
 	char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index d6a92cb..bba793d 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -11,6 +11,7 @@
 #include "cras_iodev_info.h"
 #include "cras_iodev_list.h"
 #include "cras_loopback_iodev.h"
+#include "cras_main_thread_log.h"
 #include "cras_observer.h"
 #include "cras_rstream.h"
 #include "cras_server.h"
@@ -53,6 +54,8 @@
 	struct device_enabled_cb *next, *prev;
 };
 
+struct main_thread_event_log *main_log;
+
 /* Lists for devs[CRAS_STREAM_INPUT] and devs[CRAS_STREAM_OUTPUT]. */
 static struct iodev_list devs[CRAS_NUM_DIRECTIONS];
 /* The observer client iodev_list used to listen on various events. */
@@ -261,7 +264,6 @@
 			node_info->left_right_swapped =
 				node->left_right_swapped;
 			node_info->stable_id = node->stable_id;
-			strcpy(node_info->mic_positions, node->mic_positions);
 			strcpy(node_info->name, node->name);
 			strcpy(node_info->active_hotword_model,
 			       node->active_hotword_model);
@@ -375,33 +377,18 @@
 }
 
 /*
- * Close dev if it's opened, without the extra call to idle_dev_check.
- * This is useful for closing a dev inside idle_dev_check function to
- * avoid infinite recursive call.
- *
- * Returns:
- *    -EINVAL if device was not opened, otherwise return 0.
+ * Removes all attached streams and close dev if it's opened.
  */
-static int close_dev_without_idle_check(struct cras_iodev *dev)
+static void close_dev(struct cras_iodev *dev)
 {
 	if (!cras_iodev_is_open(dev))
-		return -EINVAL;
+		return;
 
+	MAINLOG(main_log, MAIN_THREAD_DEV_CLOSE, dev->info.idx, 0, 0);
 	remove_all_streams_from_dev(dev);
 	dev->idle_timeout.tv_sec = 0;
 	cras_iodev_close(dev);
 	possibly_disable_echo_reference(dev);
-	return 0;
-}
-
-static void close_dev(struct cras_iodev *dev)
-{
-	if (close_dev_without_idle_check(dev))
-		return;
-
-	if (idle_timer)
-		cras_tm_cancel_timer(cras_system_state_get_tm(), idle_timer);
-	idle_dev_check(NULL, NULL);
 }
 
 static void idle_dev_check(struct cras_timer *timer, void *data)
@@ -420,7 +407,7 @@
 		if (edev->dev->idle_timeout.tv_sec == 0)
 			continue;
 		if (timespec_after(&now, &edev->dev->idle_timeout)) {
-			close_dev_without_idle_check(edev->dev);
+			close_dev(edev->dev);
 			continue;
 		}
 		num_idle_devs++;
@@ -475,6 +462,8 @@
 	if (cras_iodev_is_open(dev))
 		return 0;
 	cancel_pending_init_retries(dev->info.idx);
+	MAINLOG(main_log, MAIN_THREAD_DEV_INIT, dev->info.idx,
+		rstream->format.num_channels, rstream->format.frame_rate);
 
 	rc = cras_iodev_open(dev, rstream->cb_threshold, &rstream->format);
 	if (rc)
@@ -494,6 +483,8 @@
 	struct enabled_dev *edev;
 	struct cras_rstream *rstream;
 
+	MAINLOG(main_log, MAIN_THREAD_SUSPEND_DEVS, 0, 0, 0);
+
 	DL_FOREACH (stream_list_get(stream_list), rstream) {
 		if (rstream->is_pinned) {
 			struct cras_iodev *dev;
@@ -533,6 +524,8 @@
 	int has_output_stream = 0;
 	stream_list_suspended = 0;
 
+	MAINLOG(main_log, MAIN_THREAD_RESUME_DEVS, 0, 0, 0);
+
 	/*
 	 * To remove the short popped noise caused by applications that can not
 	 * stop playback "right away" after resume, we mute all output devices
@@ -683,6 +676,11 @@
 		if (!can_attach)
 			continue;
 
+		/*
+		 * Note that the stream list is descending ordered by channel
+		 * count, which guarantees the first attachable stream will have
+		 * the highest channel count.
+		 */
 		rc = init_device(dev, stream);
 		if (rc) {
 			syslog(LOG_ERR, "Enable %s failed, rc = %d",
@@ -754,6 +752,11 @@
 	return 0;
 }
 
+/*
+ * Close device enabled by pinned stream. Since it's NOT in the enabled
+ * dev list, make sure update_active_node() is called to correctly
+ * configure the ALSA UCM or BT profile state.
+ */
 static int close_pinned_device(struct cras_iodev *dev)
 {
 	close_dev(dev);
@@ -807,34 +810,64 @@
 	struct cras_iodev *iodevs[10];
 	unsigned int num_iodevs;
 	int rc;
+	bool iodev_reopened;
 
 	if (stream_list_suspended)
 		return 0;
 
+	MAINLOG(main_log, MAIN_THREAD_STREAM_ADDED, rstream->stream_id,
+		rstream->direction, rstream->buffer_frames);
+
 	if (rstream->is_pinned)
 		return pinned_stream_added(rstream);
 
 	/* Add the new stream to all enabled iodevs at once to avoid offset
 	 * in shm level between different ouput iodevs. */
 	num_iodevs = 0;
+	iodev_reopened = false;
 	DL_FOREACH (enabled_devs[rstream->direction], edev) {
 		if (num_iodevs >= ARRAY_SIZE(iodevs)) {
 			syslog(LOG_ERR, "too many enabled devices");
 			break;
 		}
 
-		rc = init_device(edev->dev, rstream);
-		if (rc) {
-			/* Error log but don't return error here, because
-			 * stopping audio could block video playback.
+		if (cras_iodev_is_open(edev->dev) &&
+		    (rstream->format.num_channels >
+		     edev->dev->format->num_channels) &&
+		    (rstream->format.num_channels <=
+		     edev->dev->info.max_supported_channels)) {
+			/* Re-open the device with the format of the attached
+			 * stream if it has higher channel count than the
+			 * current format of the device, and doesn't exceed the
+			 * max_supported_channels of the device.
+			 * Fallback device will be transciently enabled during
+			 * the device re-opening.
 			 */
-			syslog(LOG_ERR, "Init %s failed, rc = %d",
-			       edev->dev->info.name, rc);
-			schedule_init_device_retry(edev->dev);
-			continue;
-		}
+			MAINLOG(main_log, MAIN_THREAD_DEV_REOPEN,
+				rstream->format.num_channels,
+				edev->dev->format->num_channels,
+				edev->dev->format->frame_rate);
+			syslog(LOG_INFO, "re-open %s for higher channel count",
+			       edev->dev->info.name);
+			possibly_enable_fallback(rstream->direction, false);
+			cras_iodev_list_suspend_dev(edev->dev->info.idx);
+			cras_iodev_list_resume_dev(edev->dev->info.idx);
+			possibly_disable_fallback(rstream->direction);
+			iodev_reopened = true;
+		} else {
+			rc = init_device(edev->dev, rstream);
+			if (rc) {
+				/* Error log but don't return error here, because
+				 * stopping audio could block video playback.
+				 */
+				syslog(LOG_ERR, "Init %s failed, rc = %d",
+				       edev->dev->info.name, rc);
+				schedule_init_device_retry(edev->dev);
+				continue;
+			}
 
-		iodevs[num_iodevs++] = edev->dev;
+			iodevs[num_iodevs++] = edev->dev;
+		}
 	}
 	if (num_iodevs) {
 		rc = add_stream_to_open_devs(rstream, iodevs, num_iodevs);
@@ -842,9 +875,9 @@
 			syslog(LOG_ERR, "adding stream to thread fail");
 			return rc;
 		}
-	} else {
+	} else if (!iodev_reopened) {
 		/* Enable fallback device if no other iodevs can be initialized
-		 * successfully.
+		 * or re-opened successfully.
 		 * For error codes like EAGAIN and ENOENT, a new iodev will be
 		 * enabled soon so streams are going to route there. As for the
 		 * rest of the error cases, silence will be played or recorded
@@ -911,6 +944,8 @@
 	if (rc)
 		return rc;
 
+	MAINLOG(main_log, MAIN_THREAD_STREAM_REMOVED, rstream->stream_id, 0, 0);
+
 	if (rstream->is_pinned)
 		pinned_stream_removed(rstream);
 
@@ -957,6 +992,7 @@
 	struct cras_rstream *stream;
 	struct device_enabled_cb *callback;
 
+	MAINLOG(main_log, MAIN_THREAD_DEV_DISABLE, dev->info.idx, force, 0);
 	/*
 	 * Remove from enabled dev list. However this dev could have a stream
 	 * pinned to it, only cancel pending init timers when force flag is set.
@@ -964,26 +1000,23 @@
 	DL_DELETE(enabled_devs[dir], edev);
 	free(edev);
 	dev->is_enabled = 0;
-	if (force)
+	if (force) {
 		cancel_pending_init_retries(dev->info.idx);
-
-	/*
-	 * Pull all default streams off this device.
-	 * Pull all pinned streams off as well if force is true.
-	 */
-	DL_FOREACH (stream_list_get(stream_list), stream) {
-		if (stream->direction != dev->direction)
-			continue;
-		if (stream->is_pinned && !force)
-			continue;
-		audio_thread_disconnect_stream(audio_thread, stream, dev);
 	}
-	/* If this is a force disable call, that guarantees pinned streams have
-	 * all been detached. Otherwise check with stream_list to see if
-	 * there's still a pinned stream using this device.
-	 */
-	if (!force && stream_list_has_pinned_stream(stream_list, dev->info.idx))
+	/* If there's a pinned stream exists, simply disconnect all the normal
+	 * streams off this device and return. */
+	else if (stream_list_has_pinned_stream(stream_list, dev->info.idx)) {
+		DL_FOREACH (stream_list_get(stream_list), stream) {
+			if (stream->direction != dev->direction)
+				continue;
+			if (stream->is_pinned)
+				continue;
+			audio_thread_disconnect_stream(audio_thread, stream,
+						       dev);
+		}
 		return 0;
+	}
+
 	DL_FOREACH (device_enable_cbs, callback)
 		callback->disabled_cb(dev, callback->cb_data);
 	close_dev(dev);
@@ -993,36 +1026,6 @@
 }
 
 /*
- * Assume the device is not in enabled_devs list.
- * Assume there is no default stream on the device.
- * An example is that this device is unplugged while it is playing
- * a pinned stream. The device and stream may have been removed in
- * audio thread due to I/O error handling.
- */
-static int force_close_pinned_only_device(struct cras_iodev *dev)
-{
-	struct cras_rstream *rstream;
-
-	/* Pull pinned streams off this device. Note that this is initiated
-	 * from server side, so the pin stream still exist in stream_list
-	 * pending client side to actually remove it.
-	 */
-	DL_FOREACH (stream_list_get(stream_list), rstream) {
-		if (rstream->direction != dev->direction)
-			continue;
-		if (!rstream->is_pinned)
-			continue;
-		if (dev->info.idx != rstream->pinned_dev_idx)
-			continue;
-		audio_thread_disconnect_stream(audio_thread, rstream, dev);
-	}
-
-	close_dev(dev);
-	dev->update_active_node(dev, dev->active_node->idx, 0);
-	return 0;
-}
-
-/*
  * Exported Interface.
  */
 
@@ -1038,6 +1041,8 @@
 	list_observer = cras_observer_add(&observer_ops, NULL);
 	idle_timer = NULL;
 
+	main_log = main_thread_event_log_init();
+
 	/* Create the audio stream list for the system. */
 	stream_list =
 		stream_list_create(stream_added_cb, stream_removed_cb,
@@ -1079,6 +1084,7 @@
 	empty_iodev_destroy(fallback_devs[CRAS_STREAM_INPUT]);
 	empty_iodev_destroy(fallback_devs[CRAS_STREAM_OUTPUT]);
 	stream_list_destroy(stream_list);
+	main_thread_event_log_deinit(main_log);
 	if (list_observer) {
 		cras_observer_remove(list_observer);
 		list_observer = NULL;
@@ -1114,6 +1120,8 @@
 	if (!new_dev || new_dev->direction != dir)
 		return;
 
+	MAINLOG(main_log, MAIN_THREAD_ADD_ACTIVE_NODE, new_dev->info.idx, 0, 0);
+
 	/* If the new dev is already enabled but its active node needs to be
 	 * changed. Disable new dev first, update active node, and then
 	 * re-enable it again.
@@ -1153,7 +1161,7 @@
 	 */
 	if (!edev_to_disable) {
 		if (force_close)
-			force_close_pinned_only_device(dev);
+			close_pinned_device(dev);
 		return;
 	}
 
@@ -1171,25 +1179,13 @@
 
 void cras_iodev_list_suspend_dev(unsigned int dev_idx)
 {
-	struct cras_rstream *rstream;
 	struct cras_iodev *dev = find_dev(dev_idx);
 
 	if (!dev)
 		return;
 
-	DL_FOREACH (stream_list_get(stream_list), rstream) {
-		if (rstream->direction != dev->direction)
-			continue;
-		/* Disconnect all streams that are either:
-		 * (1) normal stream while dev is enabled by UI, or
-		 * (2) stream specifically pins to this dev.
-		 */
-		if ((dev->is_enabled && !rstream->is_pinned) ||
-		    (rstream->is_pinned &&
-		     (dev->info.idx != rstream->pinned_dev_idx)))
-			audio_thread_disconnect_stream(audio_thread, rstream,
-						       dev);
-	}
+	/* Remove all streams including the pinned streams, and close
+	 * this iodev. */
 	close_dev(dev);
 	dev->update_active_node(dev, dev->active_node->idx, 0);
 }
@@ -1250,6 +1246,8 @@
 	if (rc)
 		return rc;
 
+	MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, output->info.idx,
+		CRAS_STREAM_OUTPUT, 0);
 	return 0;
 }
 
@@ -1264,6 +1262,8 @@
 	if (rc)
 		return rc;
 
+	MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, input->info.idx,
+		CRAS_STREAM_INPUT, 0);
 	return 0;
 }
 
@@ -1513,6 +1513,8 @@
 	/* find the devices for the id. */
 	new_dev = find_dev(dev_index_of(node_id));
 
+	MAINLOG(main_log, MAIN_THREAD_SELECT_NODE, dev_index_of(node_id), 0, 0);
+
 	/* Do nothing if the direction is mismatched. The new_dev == NULL case
 	   could happen if node_id is 0 (no selection), or the client tries
 	   to select a non-existing node (maybe it's unplugged just before
@@ -1619,6 +1621,8 @@
 	if (iodev->set_volume)
 		iodev->set_volume(iodev);
 	cras_iodev_list_notify_node_volume(node);
+	MAINLOG(main_log, MAIN_THREAD_OUTPUT_NODE_VOLUME, iodev->info.idx,
+		volume, 0);
 	return 0;
 }
 
@@ -1626,6 +1630,7 @@
 				 unsigned int node_idx, int value)
 {
 	struct cras_ionode *node;
+	int db_scale;
 
 	node = find_node(iodev, node_idx);
 	if (!node)
@@ -1637,14 +1642,17 @@
 	if (value > 100)
 		value = 100;
 
-	/* Linear maps (0, 100) to (-4000, 4000) dBFS. Calculate and store
-	 * corresponding scaler in ui_gain_scaler. */
+	/* Linear maps (0, 50) to (-4000, 0) and (50, 100) to (0, 2000) dBFS.
+	 * Calculate and store corresponding scaler in ui_gain_scaler. */
+	db_scale = (value > 50) ? 40 : 80;
 	node->ui_gain_scaler =
-		convert_softvol_scaler_from_dB((value - 50) * 80);
+		convert_softvol_scaler_from_dB((value - 50) * db_scale);
 
 	if (iodev->set_capture_gain)
 		iodev->set_capture_gain(iodev);
 	cras_iodev_list_notify_node_capture_gain(node);
+	MAINLOG(main_log, MAIN_THREAD_INPUT_NODE_GAIN, iodev->info.idx, value,
+		0);
 	return 0;
 }
 
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index 0faa28f..0947313 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -236,7 +236,7 @@
 	struct byte_buffer *sbuf = loopdev->sample_buffer;
 	unsigned int frame_bytes = cras_get_format_bytes(iodev->format);
 
-	buf_increment_read(sbuf, nframes * frame_bytes);
+	buf_increment_read(sbuf, (size_t)nframes * (size_t)frame_bytes);
 	loopdev->read_frames += nframes;
 	ATLOG(atlog, AUDIO_THREAD_LOOPBACK_PUT, nframes, 0, 0);
 	return 0;
diff --git a/cras/src/server/cras_main_thread_log.h b/cras/src/server/cras_main_thread_log.h
new file mode 100644
index 0000000..1d92585
--- /dev/null
+++ b/cras/src/server/cras_main_thread_log.h
@@ -0,0 +1,64 @@
+/* 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 CRAS_MAIN_THREAD_LOG_H_
+#define CRAS_MAIN_THREAD_LOG_H_
+
+#include <stdint.h>
+
+#include "cras_types.h"
+
+#define CRAS_MAIN_THREAD_LOGGING 1
+
+#if (CRAS_MAIN_THREAD_LOGGING)
+#define MAINLOG(log, event, data1, data2, data3)                               \
+	main_thread_event_log_data(log, event, data1, data2, data3);
+#else
+#define MAINLOG(log, event, data1, data2, data3)
+#endif
+
+extern struct main_thread_event_log *main_log;
+
+static inline struct main_thread_event_log *main_thread_event_log_init()
+{
+	struct main_thread_event_log *log;
+	log = (struct main_thread_event_log *)calloc(
+		1, sizeof(struct main_thread_event_log));
+	if (!log)
+		return NULL;
+
+	log->len = MAIN_THREAD_EVENT_LOG_SIZE;
+	return log;
+}
+
+static inline void
+main_thread_event_log_deinit(struct main_thread_event_log *log)
+{
+	if (log)
+		free(log);
+}
+
+static inline void main_thread_event_log_data(struct main_thread_event_log *log,
+					      enum MAIN_THREAD_LOG_EVENTS event,
+					      uint32_t data1, uint32_t data2,
+					      uint32_t data3)
+{
+	struct timespec now;
+
+	if (!log)
+		return;
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+	log->log[log->write_pos].tag_sec =
+		(event << 24) | ((now.tv_sec % 86400) & 0x00ffffff);
+	log->log[log->write_pos].nsec = now.tv_nsec;
+	log->log[log->write_pos].data1 = data1;
+	log->log[log->write_pos].data2 = data2;
+	log->log[log->write_pos].data3 = data3;
+	log->write_pos++;
+	log->write_pos %= MAIN_THREAD_EVENT_LOG_SIZE;
+}
+
+#endif /* CRAS_MAIN_THREAD_LOG_H_ */
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index 1038c92..91556d8 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -302,6 +302,8 @@
 		return "CrOSVM";
 	case CRAS_CLIENT_TYPE_SERVER_STREAM:
 		return "ServerStream";
+	case CRAS_CLIENT_TYPE_LACROS:
+		return "LaCrOS";
 	default:
 		return "InvalidType";
 	}
@@ -971,6 +973,11 @@
 {
 	char metrics_name[METRICS_NAME_BUFFER_SIZE];
 
+	snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE, "Cras.%sStreamRuntime",
+		 data.direction == CRAS_STREAM_INPUT ? "Input" : "Output");
+	cras_metrics_log_histogram(metrics_name, (unsigned)data.runtime.tv_sec,
+				   0, 10000, 20);
+
 	snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE,
 		 "Cras.%sStreamRuntime.%s",
 		 data.direction == CRAS_STREAM_INPUT ? "Input" : "Output",
@@ -1008,10 +1015,15 @@
 
 	va_start(valist, sample);
 
-	for (i = 0; i < num; i++) {
-		len += snprintf(metrics_name + len,
+	for (i = 0; i < num && len < METRICS_NAME_BUFFER_SIZE; i++) {
+		int metric_len = snprintf(metrics_name + len,
 				METRICS_NAME_BUFFER_SIZE - len, "%s%s",
 				i ? "." : "", va_arg(valist, char *));
+		// Exit early on error or running out of bufferspace. Avoids
+		// logging partial or corrupted strings.
+		if (metric_len < 0 || metric_len > METRICS_NAME_BUFFER_SIZE - len)
+			break;
+		len += metric_len;
 		cras_metrics_log_sparse_histogram(metrics_name, sample);
 	}
 
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index 14403c8..a583419 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -394,6 +394,11 @@
 
 bool cras_system_check_ignore_ucm_suffix(const char *card_name)
 {
+	/* Check the general case: ALSA Loopback card "Loopback". */
+	if (!strcmp("Loopback", card_name))
+		return true;
+
+	/* Check board-specific ignore ucm suffix cards. */
 	struct name_list *card;
 	DL_FOREACH (state.ignore_suffix_cards, card) {
 		if (!strcmp(card->name, card_name))
diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c
index 1a0b54f..20b25f7 100644
--- a/cras/src/server/dev_io.c
+++ b/cras/src/server/dev_io.c
@@ -92,7 +92,7 @@
 	return count;
 }
 
-static void check_non_empty_state_transition(struct open_dev *adevs)
+int dev_io_check_non_empty_state_transition(struct open_dev *adevs)
 {
 	int new_non_empty_dev_count = count_non_empty_dev(adevs);
 
@@ -103,6 +103,7 @@
 									    0);
 
 	non_empty_device_count = new_non_empty_dev_count;
+	return non_empty_device_count > 0;
 }
 
 /* Checks whether it is time to fetch. */
@@ -385,11 +386,14 @@
 		clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
 
 	/*
-	 * If any input device has more than largest_cb_level * 1.5 frames, need to
-	 * drop frames from all devices.
+	 * Drop frames from all devices if any device meets these requirements:
+	 * 1. The hw_level is larger than largest_cb_level * 1.5 or larger than
+	 *    buffer_size * 0.5.
+	 * 2. The time of those frames is larger than DROP_FRAMES_THRESHOLD_MS.
 	 */
 	if (input_devices_can_drop_samples(adev->dev) &&
-	    rc >= adev->dev->largest_cb_level * 1.5 &&
+	    (rc >= adev->dev->largest_cb_level * 1.5 ||
+	     rc >= adev->dev->buffer_size * 0.5) &&
 	    cras_frames_to_ms(rc, adev->dev->format->frame_rate) >=
 		    DROP_FRAMES_THRESHOLD_MS)
 		*need_to_drop = true;
@@ -849,7 +853,7 @@
 static void dev_io_drop_samples(struct open_dev *idev_list)
 {
 	struct open_dev *adev;
-	struct timespec drop_time;
+	struct timespec drop_time = {};
 	int rc;
 
 	get_input_devices_drop_time(idev_list, &drop_time);
@@ -919,6 +923,7 @@
 		ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN, adev->dev->info.idx,
 		      0, 0);
 		cras_iodev_reset_request(adev->dev);
+		cras_audio_thread_event_severe_underrun();
 	}
 	/* Device error, remove it. */
 	dev_io_rm_open_dev(odevs, adev);
@@ -1087,8 +1092,6 @@
 	dev_io_capture(idevs);
 	dev_io_send_captured_samples(*idevs);
 	dev_io_playback_write(odevs, output_converter);
-
-	check_non_empty_state_transition(*odevs);
 }
 
 static int input_adev_ignore_wake(const struct open_dev *adev)
@@ -1214,7 +1217,7 @@
 	cras_server_metrics_highest_hw_level(dev_to_rm->dev->highest_hw_level,
 					     dev_to_rm->dev->direction);
 
-	check_non_empty_state_transition(*odev_list);
+	dev_io_check_non_empty_state_transition(*odev_list);
 
 	ATLOG(atlog, AUDIO_THREAD_DEV_REMOVED, dev_to_rm->dev->info.idx, 0, 0);
 
diff --git a/cras/src/server/dev_io.h b/cras/src/server/dev_io.h
index 51aa319..259bbab 100644
--- a/cras/src/server/dev_io.h
+++ b/cras/src/server/dev_io.h
@@ -71,6 +71,12 @@
 		struct cras_fmt_conv *output_converter);
 
 /*
+ * Checks the non-empty device state in active output lists and return
+ * if there's at least one non-empty device.
+ */
+int dev_io_check_non_empty_state_transition(struct open_dev *adevs);
+
+/*
  * Fills min_ts with the next time the system should wake to service input.
  * Returns the number of devices waiting.
  */
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index 02da56a..f0fcb71 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -259,7 +259,8 @@
 		total_read += read_frames;
 		source_samples += read_frames * source_frame_bytes;
 		buf_increment_write(dev_stream->conv_buffer,
-				    write_frames * dst_frame_bytes);
+				    (size_t)write_frames *
+					    (size_t)dst_frame_bytes);
 	}
 
 	return total_read;
@@ -317,7 +318,7 @@
 				     software_gain_scaler);
 
 		buf_increment_read(dev_stream->conv_buffer,
-				   write_frames * frame_bytes);
+				   (size_t)write_frames * (size_t)frame_bytes);
 		total_written += write_frames;
 		cras_rstream_dev_offset_update(rstream, write_frames,
 					       dev_stream->dev_id);
diff --git a/cras/src/server/float_buffer.h b/cras/src/server/float_buffer.h
index 39a0187..ba3523d 100644
--- a/cras/src/server/float_buffer.h
+++ b/cras/src/server/float_buffer.h
@@ -37,7 +37,7 @@
 	b->fp = (float **)malloc(num_channels * sizeof(float *));
 	b->buf = (struct byte_buffer *)calloc(
 		1, sizeof(struct byte_buffer) +
-			   max_size * num_channels * sizeof(float));
+			   sizeof(float) * max_size * num_channels);
 	b->buf->max_size = max_size;
 	b->buf->used_size = max_size;
 	return b;
diff --git a/cras/src/server/rust/src/headers/rate_estimator.h b/cras/src/server/rust/src/headers/rate_estimator.h
index f8726ba..3ac9cfa 100644
--- a/cras/src/server/rust/src/headers/rate_estimator.h
+++ b/cras/src/server/rust/src/headers/rate_estimator.h
@@ -31,12 +31,16 @@
 typedef struct rate_estimator rate_estimator;
 
 /**
+ * # Safety
+ *
  * To use this function safely, `re` must be a pointer returned from
  * rate_estimator_create, or null.
  */
 void rate_estimator_add_frames(rate_estimator *re, int frames);
 
 /**
+ * # Safety
+ *
  * To use this function safely, `re` must be a pointer returned from
  * rate_estimator_create, or null, and `now` must be a valid pointer to a
  * timespec.
@@ -45,6 +49,8 @@
 			     const struct timespec *now);
 
 /**
+ * # Safety
+ *
  * To use this function safely, `window_size` must be a valid pointer to a
  * timespec.
  */
@@ -53,18 +59,24 @@
 				      double smooth_factor);
 
 /**
+ * # Safety
+ *
  * To use this function safely, `re` must be a pointer returned from
  * rate_estimator_create, or null.
  */
 void rate_estimator_destroy(rate_estimator *re);
 
 /**
+ * # Safety
+ *
  * To use this function safely, `re` must be a pointer returned from
  * rate_estimator_create, or null.
  */
 double rate_estimator_get_rate(const rate_estimator *re);
 
 /**
+ * # Safety
+ *
  * To use this function safely, `re` must be a pointer returned from
  * rate_estimator_create, or null.
  */
diff --git a/cras/src/server/rust/src/rate_estimator.rs b/cras/src/server/rust/src/rate_estimator.rs
index d41c725..585f346 100644
--- a/cras/src/server/rust/src/rate_estimator.rs
+++ b/cras/src/server/rust/src/rate_estimator.rs
@@ -183,6 +183,6 @@
             self.window_frames = 0;
             return true;
         }
-        return false;
+        false
     }
 }
diff --git a/cras/src/server/rust/src/rate_estimator_bindings.rs b/cras/src/server/rust/src/rate_estimator_bindings.rs
index 129c63d..3073ba7 100644
--- a/cras/src/server/rust/src/rate_estimator_bindings.rs
+++ b/cras/src/server/rust/src/rate_estimator_bindings.rs
@@ -8,9 +8,11 @@
 
 use crate::RateEstimator;
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `window_size` must be a valid pointer to a
 /// timespec.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_create(
     rate: libc::c_uint,
     window_size: *const libc::timespec,
@@ -29,9 +31,11 @@
     }
 }
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `re` must be a pointer returned from
 /// rate_estimator_create, or null.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_destroy(re: *mut RateEstimator) {
     if re.is_null() {
         return;
@@ -40,9 +44,11 @@
     drop(Box::from_raw(re));
 }
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `re` must be a pointer returned from
 /// rate_estimator_create, or null.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_add_frames(re: *mut RateEstimator, frames: libc::c_int) {
     if re.is_null() {
         return;
@@ -51,10 +57,12 @@
     (*re).add_frames(frames)
 }
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `re` must be a pointer returned from
 /// rate_estimator_create, or null, and `now` must be a valid pointer to a
 /// timespec.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_check(
     re: *mut RateEstimator,
     level: libc::c_int,
@@ -75,9 +83,11 @@
     (*re).update_estimated_rate(level, now) as i32
 }
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `re` must be a pointer returned from
 /// rate_estimator_create, or null.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_get_rate(re: *const RateEstimator) -> libc::c_double {
     if re.is_null() {
         return 0.0;
@@ -86,9 +96,11 @@
     (*re).get_estimated_rate()
 }
 
-#[no_mangle]
+/// # Safety
+///
 /// To use this function safely, `re` must be a pointer returned from
 /// rate_estimator_create, or null.
+#[no_mangle]
 pub unsafe extern "C" fn rate_estimator_reset_rate(re: *mut RateEstimator, rate: libc::c_uint) {
     if re.is_null() {
         return;
diff --git a/cras/src/server/stream_list.c b/cras/src/server/stream_list.c
index d247ec8..719608a 100644
--- a/cras/src/server/stream_list.c
+++ b/cras/src/server/stream_list.c
@@ -81,12 +81,19 @@
 		    struct cras_rstream **stream)
 {
 	int rc;
+	struct cras_rstream *next_stream;
 
 	rc = list->stream_create_cb(stream_config, stream);
 	if (rc)
 		return rc;
 
-	DL_APPEND(list->streams, *stream);
+	/* Keep stream list in descending order by channel count. */
+	DL_FOREACH (list->streams, next_stream) {
+		if ((*stream)->format.num_channels >=
+		    next_stream->format.num_channels)
+			break;
+	}
+	DL_INSERT(list->streams, next_stream, *stream);
 	rc = list->stream_added_cb(*stream);
 	if (rc) {
 		DL_DELETE(list->streams, *stream);
diff --git a/cras/src/server/stream_list.h b/cras/src/server/stream_list.h
index ae77a33..0a9b86a 100644
--- a/cras/src/server/stream_list.h
+++ b/cras/src/server/stream_list.h
@@ -30,8 +30,8 @@
 
 struct cras_rstream *stream_list_get(struct stream_list *list);
 
-/* Creates a cras_rstream from cras_rstreaem_config and adds the cras_rstream
- * to stream_list.
+/* Creates a cras_rstream from cras_rstream_config and inserts the cras_rstream
+ * to stream_list in descending order by channel count.
  *
  * Args:
  *   list - stream_list to add streams.
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index e0cbccd..266b62a 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -102,7 +102,8 @@
 	struct test_iodev *testio = (struct test_iodev *)iodev;
 
 	/* Input */
-	buf_increment_read(testio->audbuff, frames * testio->fmt_bytes);
+	buf_increment_read(testio->audbuff,
+			   (size_t)frames * (size_t)testio->fmt_bytes);
 
 	return 0;
 }
@@ -241,8 +242,9 @@
 
 	write_ptr = buf_write_pointer_size(testio->audbuff, &avail);
 	count = MIN(count, avail);
-	memcpy(write_ptr, samples, count * testio->fmt_bytes);
-	buf_increment_write(testio->audbuff, count * testio->fmt_bytes);
+	memcpy(write_ptr, samples, (size_t)count * (size_t)testio->fmt_bytes);
+	buf_increment_write(testio->audbuff,
+			    (size_t)count * (size_t)testio->fmt_bytes);
 	return count;
 }
 
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 8f37de2..f03a614 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -722,6 +722,7 @@
 int cras_bt_transport_configuration(const struct cras_bt_transport* transport,
                                     void* configuration,
                                     int len) {
+  memset(configuration, 0, len);
   cras_bt_transport_configuration_called++;
   return 0;
 }
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index 56073e8..ba650a2 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -146,6 +146,7 @@
 static void* audio_thread_cb_data;
 static int hotword_send_triggered_msg_called;
 static struct timespec clock_gettime_retspec;
+static unsigned cras_iodev_reset_rate_estimator_called;
 
 void ResetStubData() {
   cras_alsa_open_called = 0;
@@ -224,6 +225,7 @@
   cras_iodev_dsp_set_swap_mode_for_node_called = 0;
   ucm_get_default_node_gain_values.clear();
   ucm_get_intrinsic_sensitivity_values.clear();
+  cras_iodev_reset_rate_estimator_called = 0;
 }
 
 static long fake_get_dBFS(const struct cras_volume_curve* curve,
@@ -1073,7 +1075,6 @@
 }
 
 TEST(AlsaOutputNode, MaxSupportedChannels) {
-  struct alsa_io* aio;
   struct cras_use_case_mgr* const fake_ucm = (struct cras_use_case_mgr*)3;
   struct cras_iodev* iodev;
   struct ucm_section* section;
@@ -1090,7 +1091,6 @@
         1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm,
         CRAS_STREAM_OUTPUT);
     ASSERT_NE(iodev, (void*)NULL);
-    aio = reinterpret_cast<struct alsa_io*>(iodev);
 
     // Node without controls or jacks.
     section = ucm_section_create(INTERNAL_SPEAKER, "hw:0,1", 1, -1,
@@ -2209,6 +2209,7 @@
   EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames);
   EXPECT_EQ(0, aio.free_running);
   EXPECT_EQ(0, aio.filled_zeros_for_draining);
+  EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
 }
 
 TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunLessRemain) {
@@ -2233,6 +2234,7 @@
   EXPECT_EQ(96, cras_iodev_fill_odev_zeros_frames);
   EXPECT_EQ(0, aio.free_running);
   EXPECT_EQ(0, aio.filled_zeros_for_draining);
+  EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
 }
 
 TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) {
@@ -2250,6 +2252,7 @@
             cras_alsa_resume_appl_ptr_ahead);
   EXPECT_EQ(0, aio.free_running);
   EXPECT_EQ(0, aio.filled_zeros_for_draining);
+  EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
 }
 
 // Reuse AlsaFreeRunTestSuite for output underrun handling because they are
@@ -2918,6 +2921,7 @@
 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
 
 int cras_iodev_reset_rate_estimator(const struct cras_iodev* iodev) {
+  cras_iodev_reset_rate_estimator_called++;
   return 0;
 }
 
diff --git a/cras/src/tests/alsa_jack_unittest.cc b/cras/src/tests/alsa_jack_unittest.cc
index 47933d7..1aa9549 100644
--- a/cras/src/tests/alsa_jack_unittest.cc
+++ b/cras/src/tests/alsa_jack_unittest.cc
@@ -216,14 +216,15 @@
   EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
   EXPECT_EQ(ucm ? njacks : 0, ucm_get_dev_for_jack_called);
   EXPECT_EQ(ucm ? njacks : 0, ucm_get_override_type_name_called);
-  EXPECT_EQ(1 + nhdmi_jacks, snd_hctl_first_elem_called);
+  EXPECT_EQ(1, snd_hctl_first_elem_called);
   EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
+  EXPECT_EQ(nhdmi_jacks, snd_hctl_find_elem_called);
 
   /* For some functions, the number of calls to them could
    * be larger then expected count if there is ELD control
    * in given elements. */
-  EXPECT_GE(snd_hctl_elem_next_called, nelems + nhdmi_jacks);
-  EXPECT_GE(snd_hctl_elem_get_name_called, nelems + njacks);
+  EXPECT_GE(snd_hctl_elem_next_called, nelems);
+  EXPECT_GE(snd_hctl_elem_get_name_called, nelems);
 
   if (direction == CRAS_STREAM_OUTPUT) {
     EXPECT_EQ(njacks, cras_alsa_mixer_get_output_matching_name_called);
@@ -678,7 +679,7 @@
   ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
 
   /* Assert get device is called for the ELD control */
-  EXPECT_EQ(1, snd_hctl_elem_get_device_called);
+  EXPECT_EQ(1, snd_hctl_find_elem_called);
   cras_alsa_jack_list_destroy(jack_list);
 }
 
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index c9baecc..efb8265 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -1140,6 +1140,7 @@
   struct ucm_section* section;
   struct mixer_name* m_name;
   int section_count = 0;
+  int dev_idx;
   int i = 0;
   const char* devices[] = {"Headphone",    "The headphones jack.",
                            "Speaker",      "The speakers.",
@@ -1153,6 +1154,7 @@
 
                        "=PlaybackPCM/Speaker/HiFi",
                        "=CoupledMixers/Speaker/HiFi",
+                       "=DependentPCM/Speaker/HiFi",
 
                        "=CapturePCM/Mic/HiFi",
                        "=JackDev/Mic/HiFi",
@@ -1173,19 +1175,20 @@
       "2",
       "HP-L,HP-R",
 
-      "hw:my-sound-card,0",
+      "hw:my-sound-card,1",
       "SPK-L,SPK-R",
-
       "hw:my-sound-card,0",
+
+      "hw:my-sound-card,2",
       "my-sound-card Headset Jack",
       "0",
       "CAPTURE",
 
-      "hw:my-sound-card,0",
+      "hw:my-sound-card,3",
       "MIC-L,MIC-R",
       "-10",
 
-      "hw:my-sound-card,2",
+      "hw:my-sound-card,4",
       "HDMI",
   };
 
@@ -1206,6 +1209,7 @@
 
   // Headphone
   section = sections;
+  EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,0"));
   EXPECT_EQ(0, strcmp(section->name, "Headphone"));
   EXPECT_EQ(0, section->dev_idx);
   EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
@@ -1219,16 +1223,19 @@
   EXPECT_EQ(0, strcmp(m_name->name, "HP-R"));
   EXPECT_EQ(NULL, m_name->next);
   EXPECT_EQ(2, section->jack_switch);
+  dev_idx = section->dev_idx;
 
   // Speaker
   section = section->next;
+  EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,1"));
   EXPECT_EQ(0, strcmp(section->name, "Speaker"));
-  EXPECT_EQ(0, section->dev_idx);
+  EXPECT_EQ(1, section->dev_idx);
   EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
   EXPECT_EQ(NULL, section->jack_name);
   EXPECT_EQ(NULL, section->jack_type);
   EXPECT_EQ(-1, section->jack_switch);
   EXPECT_EQ(NULL, section->mixer_name);
+  EXPECT_EQ(dev_idx, section->dependent_dev_idx);
   ASSERT_NE((struct mixer_name*)NULL, section->coupled);
   m_name = section->coupled;
   EXPECT_EQ(0, strcmp(m_name->name, "SPK-L"));
@@ -1238,8 +1245,9 @@
 
   // Mic
   section = section->next;
+  EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,2"));
   EXPECT_EQ(0, strcmp(section->name, "Mic"));
-  EXPECT_EQ(0, section->dev_idx);
+  EXPECT_EQ(2, section->dev_idx);
   EXPECT_EQ(CRAS_STREAM_INPUT, section->dir);
   EXPECT_EQ(0, strcmp(section->jack_name, values[1]));
   EXPECT_EQ(0, strcmp(section->jack_type, "gpio"));
@@ -1250,8 +1258,9 @@
 
   // Internal Mic
   section = section->next;
+  EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,3"));
   EXPECT_EQ(0, strcmp(section->name, "Internal Mic"));
-  EXPECT_EQ(0, section->dev_idx);
+  EXPECT_EQ(3, section->dev_idx);
   EXPECT_EQ(CRAS_STREAM_INPUT, section->dir);
   EXPECT_EQ(NULL, section->jack_name);
   EXPECT_EQ(NULL, section->jack_type);
@@ -1265,8 +1274,9 @@
 
   // HDMI
   section = section->next;
+  EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,4"));
   EXPECT_EQ(0, strcmp(section->name, "HDMI"));
-  EXPECT_EQ(2, section->dev_idx);
+  EXPECT_EQ(4, section->dev_idx);
   EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
   EXPECT_EQ(NULL, section->jack_name);
   EXPECT_EQ(NULL, section->jack_type);
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index 0280a1e..6b78c69 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -18,6 +18,7 @@
 #define FIRST_CB_LEVEL 480
 
 static int cras_audio_thread_event_busyloop_called;
+static int cras_audio_thread_event_severe_underrun_called;
 static unsigned int cras_rstream_dev_offset_called;
 static unsigned int cras_rstream_dev_offset_ret[MAX_CALLS];
 static const struct cras_rstream*
@@ -963,6 +964,7 @@
   thread_add_stream(thread_, &rstream, &piodev, 1);
 
   // Assume device is running and there is a severe underrun.
+  cras_audio_thread_event_severe_underrun_called = 0;
   iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
   frames_queued_ = -EPIPE;
 
@@ -975,6 +977,7 @@
   // Audio thread should ask main thread to reset device.
   EXPECT_EQ(1, cras_iodev_reset_request_called);
   EXPECT_EQ(&iodev, cras_iodev_reset_request_iodev);
+  EXPECT_EQ(1, cras_audio_thread_event_severe_underrun_called);
 
   thread_rm_open_dev(thread_, CRAS_STREAM_OUTPUT, iodev.info.idx);
   TearDownRstream(&rstream);
@@ -1444,6 +1447,11 @@
   return 0;
 }
 
+int cras_audio_thread_event_severe_underrun() {
+  cras_audio_thread_event_severe_underrun_called++;
+  return 0;
+}
+
 float input_data_get_software_gain_scaler(struct input_data* data,
                                           float idev_sw_gain_scaler,
                                           struct cras_rstream* stream) {
diff --git a/cras/src/tests/control_rclient_unittest.cc b/cras/src/tests/control_rclient_unittest.cc
index 189d22a..b0d01fc 100644
--- a/cras/src/tests/control_rclient_unittest.cc
+++ b/cras/src/tests/control_rclient_unittest.cc
@@ -9,6 +9,7 @@
 extern "C" {
 #include "audio_thread.h"
 #include "cras_bt_log.h"
+#include "cras_main_thread_log.h"
 #include "cras_messages.h"
 #include "cras_rclient.h"
 #include "cras_rstream.h"
@@ -159,6 +160,7 @@
     connect_msg_.dev_idx = NO_DEVICE;
     connect_msg_.client_shm_size = 0;
     btlog = cras_bt_event_log_init();
+    main_log = main_thread_event_log_init();
     ResetStubData();
   }
 
@@ -168,6 +170,7 @@
     close(pipe_fds_[0]);
     close(pipe_fds_[1]);
     cras_bt_event_log_deinit(btlog);
+    main_thread_event_log_deinit(main_log);
   }
 
   void RegisterNotification(enum CRAS_CLIENT_MESSAGE_ID msg_id,
@@ -734,6 +737,7 @@
 extern "C" {
 
 struct cras_bt_event_log* btlog;
+struct main_thread_event_log* main_log;
 
 struct audio_thread* cras_iodev_list_get_audio_thread() {
   return iodev_get_thread_return;
diff --git a/cras/src/tests/dev_io_unittest.cc b/cras/src/tests/dev_io_unittest.cc
index 02dc638..096e3ed 100644
--- a/cras/src/tests/dev_io_unittest.cc
+++ b/cras/src/tests/dev_io_unittest.cc
@@ -137,6 +137,49 @@
 }
 
 /*
+ * If any hw_level is larger than 0.5 * buffer_size and
+ * DROP_FRAMES_THRESHOLD_MS, reset all input devices.
+ */
+
+TEST_F(DevIoSuite, SendCapturedNeedToResetDevices2) {
+  struct timespec start;
+  struct timespec drop_time;
+  struct open_dev* dev_list = NULL;
+  bool rc;
+
+  stream = create_stream(1, 1, CRAS_STREAM_INPUT, 2000, &format);
+
+  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+  AddFakeDataToStream(stream.get(), 0);
+
+  DevicePtr dev1 =
+      create_device(CRAS_STREAM_INPUT, 2048, &format, CRAS_NODE_TYPE_MIC);
+  DevicePtr dev2 =
+      create_device(CRAS_STREAM_INPUT, 10000, &format, CRAS_NODE_TYPE_MIC);
+  DL_APPEND(dev_list, dev1->odev.get());
+  DL_APPEND(dev_list, dev2->odev.get());
+  add_stream_to_dev(dev1->dev, stream);
+  add_stream_to_dev(dev2->dev, stream);
+
+  iodev_stub_frames_queued(dev1->dev.get(), 2480, start);
+  iodev_stub_frames_queued(dev2->dev.get(), 2480, start);
+  EXPECT_EQ(0, dev_io_send_captured_samples(dev_list));
+
+  /*
+   * Should drop frames to one min_cb_level, which is 2480 - 2000 = 480 (10ms).
+   */
+  rc = iodev_stub_get_drop_time(dev1->dev.get(), &drop_time);
+  EXPECT_EQ(true, rc);
+  EXPECT_EQ(0, drop_time.tv_sec);
+  EXPECT_EQ(10000000, drop_time.tv_nsec);
+
+  rc = iodev_stub_get_drop_time(dev2->dev.get(), &drop_time);
+  EXPECT_EQ(true, rc);
+  EXPECT_EQ(0, drop_time.tv_sec);
+  EXPECT_EQ(10000000, drop_time.tv_nsec);
+}
+
+/*
  * If the hw_level is larger than 1.5 * largest_cb_level but less than
  * DROP_FRAMES_THRESHOLD_MS, do nothing.
  */
@@ -161,7 +204,10 @@
   EXPECT_EQ(false, rc);
 }
 
-/* If all hw_level is less than 1.5 * largest_cb_level, do nothing. */
+/*
+ * If all hw_level is less than 1.5 * largest_cb_level and 0.5 * buffer_size,
+ * do nothing.
+ */
 TEST_F(DevIoSuite, SendCapturedNoNeedToResetDevices) {
   struct timespec start;
   struct timespec drop_time;
@@ -263,6 +309,10 @@
   return 0;
 }
 
+int cras_audio_thread_event_severe_underrun() {
+  return 0;
+}
+
 int dev_stream_attached_devs(const struct dev_stream* dev_stream) {
   return 0;
 }
diff --git a/cras/src/tests/fmt_conv_ops_unittest.cc b/cras/src/tests/fmt_conv_ops_unittest.cc
index a76087f..d8feeab 100644
--- a/cras/src/tests/fmt_conv_ops_unittest.cc
+++ b/cras/src/tests/fmt_conv_ops_unittest.cc
@@ -619,10 +619,10 @@
   EXPECT_EQ(ret, frames);
 
   for (size_t i = 0; i < frames; ++i) {
+    int32_t sum = 0;
     for (size_t k = 0; k < in_ch; ++k)
-      src[i * in_ch + k] /= in_ch;
-    for (size_t k = 1; k < in_ch; ++k)
-      src[i * in_ch + 0] += src[i * in_ch + k];
+      sum += (int32_t)src[i * in_ch + k];
+    src[i * in_ch + 0] = (int16_t)(sum / (int32_t)in_ch);
   }
   for (size_t i = 0; i < frames; ++i) {
     for (size_t k = 0; k < out_ch; ++k)
@@ -630,6 +630,36 @@
   }
 }
 
+// Test 6ch to 8ch conversion.  S16_LE.
+TEST(FormatConverterOpsTest, 6chTo8chS16LE) {
+  const size_t frames = 65536;
+  const size_t in_ch = 6;
+  const size_t out_ch = 8;
+  struct cras_audio_format fmt = {
+      .format = SND_PCM_FORMAT_S16_LE,
+      .frame_rate = 48000,
+      .num_channels = 8,
+  };
+
+  S16LEPtr src = CreateS16LE(frames * in_ch);
+  S16LEPtr dst = CreateS16LE(frames * out_ch);
+  for (size_t i = 0; i < frames; ++i) {
+    for (size_t k = 0; k < in_ch; k++) {
+      src[i * in_ch + k] = (k == 0) ? (INT16_MIN + (int16_t)i) : 0;
+    }
+  }
+
+  size_t ret = s16_default_all_to_all(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
+                                      frames, (uint8_t*)dst.get());
+  EXPECT_EQ(ret, frames);
+
+  for (size_t i = 0; i < frames; ++i) {
+    src[i * in_ch + 0] /= (int16_t)in_ch;
+    for (size_t k = 0; k < out_ch; ++k)
+      EXPECT_EQ(src[i * in_ch + 0], dst[i * out_ch + k]);
+  }
+}
+
 // Test Multiply with Coef.  S16_LE.
 TEST(FormatConverterOpsTest, MultiplyWithCoefS16LE) {
   const size_t buf_size = 4096;
diff --git a/cras/src/tests/hfp_ag_profile_unittest.cc b/cras/src/tests/hfp_ag_profile_unittest.cc
index 2baea4f..1a115f0 100644
--- a/cras/src/tests/hfp_ag_profile_unittest.cc
+++ b/cras/src/tests/hfp_ag_profile_unittest.cc
@@ -255,6 +255,10 @@
   return 0;
 }
 
+int hfp_slc_get_hf_battery_level(struct hfp_slc_handle* handle) {
+  return -1;
+}
+
 struct cras_bt_device* cras_a2dp_connected_device() {
   return NULL;
 }
@@ -266,6 +270,10 @@
 
 void cras_a2dp_suspend_connected_device(struct cras_bt_device* device) {}
 
+const char* cras_bt_device_address(const struct cras_bt_device* device) {
+  return "";
+}
+
 int cras_bt_device_audio_gateway_initialized(struct cras_bt_device* device) {
   return 0;
 }
@@ -278,6 +286,11 @@
   cras_bt_device_notify_profile_dropped_profile = profile;
 }
 
+void cras_observer_notify_bt_battery_changed(const char* address,
+                                             uint32_t level) {
+  return;
+}
+
 bool cras_system_get_bt_wbs_enabled() {
   return true;
 }
diff --git a/cras/src/tests/hfp_alsa_iodev_unittest.cc b/cras/src/tests/hfp_alsa_iodev_unittest.cc
index 73e9638..8975694 100644
--- a/cras/src/tests/hfp_alsa_iodev_unittest.cc
+++ b/cras/src/tests/hfp_alsa_iodev_unittest.cc
@@ -22,6 +22,7 @@
 static struct cras_iodev fake_sco_out, fake_sco_in;
 static struct cras_bt_device* fake_device;
 static struct hfp_slc_handle* fake_slc;
+static struct cras_audio_format fake_format;
 
 static size_t cras_bt_device_append_iodev_called;
 static size_t cras_bt_device_rm_iodev_called;
@@ -240,13 +241,23 @@
 TEST_F(HfpAlsaIodev, ConfigureDev) {
   struct cras_iodev* iodev;
   size_t buf_size = 8192;
+  struct hfp_alsa_io* hfp_alsa_io;
 
   fake_sco_out.direction = CRAS_STREAM_OUTPUT;
   fake_sco_out.buffer_size = buf_size;
   iodev = hfp_alsa_iodev_create(&fake_sco_out, fake_device, fake_slc,
                                 CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
+  hfp_alsa_io = (struct hfp_alsa_io*)iodev;
+  iodev->format = &fake_format;
   iodev->configure_dev(iodev);
 
+  EXPECT_EQ(fake_format.num_channels, hfp_alsa_io->aio->format->num_channels);
+  EXPECT_EQ(fake_format.frame_rate, hfp_alsa_io->aio->format->frame_rate);
+  EXPECT_EQ(fake_format.format, hfp_alsa_io->aio->format->format);
+  for (int i = 0; i < CRAS_CH_MAX; i++)
+    EXPECT_EQ(fake_format.channel_layout[i],
+              hfp_alsa_io->aio->format->channel_layout[i]);
+
   EXPECT_EQ(1, fake_configure_dev_called);
   EXPECT_EQ(0, hfp_set_call_status_called);
   EXPECT_EQ(buf_size, iodev->buffer_size);
diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc
index b5e7b25..48a1c89 100644
--- a/cras/src/tests/hfp_info_unittest.cc
+++ b/cras/src/tests/hfp_info_unittest.cc
@@ -346,10 +346,11 @@
 
   hci_sco_buf[1] = headers[seq % 4];
 
+  /* Assume typical 60 bytes case. */
   msg.msg_iov = &iov;
   msg.msg_iovlen = 1;
   iov.iov_base = hci_sco_buf;
-  iov.iov_len = HCI_SCO_PKT_SIZE;
+  iov.iov_len = 60;
   msg.msg_control = control;
   msg.msg_controllen = control_size;
 
diff --git a/cras/src/tests/hfp_slc_unittest.cc b/cras/src/tests/hfp_slc_unittest.cc
index 8e78727..4501b07 100644
--- a/cras/src/tests/hfp_slc_unittest.cc
+++ b/cras/src/tests/hfp_slc_unittest.cc
@@ -61,6 +61,8 @@
   char* chp;
   ResetStubData();
 
+  btlog = cras_bt_event_log_init();
+
   ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
   handle = hfp_slc_create(sock[0], 0, AG_ENHANCED_CALL_STATUS, device,
                           slc_initialized_cb, slc_disconnected_cb);
@@ -110,6 +112,7 @@
   ASSERT_EQ(1, cras_bt_device_update_hardware_volume_called);
 
   hfp_slc_destroy(handle);
+  cras_bt_event_log_deinit(btlog);
 }
 
 TEST(HfpSlc, DisconnectSlc) {
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index f9644fa..d40faca 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -12,6 +12,7 @@
 #include "audio_thread.h"
 #include "cras_iodev.h"
 #include "cras_iodev_list.h"
+#include "cras_main_thread_log.h"
 #include "cras_observer_ops.h"
 #include "cras_ramp.h"
 #include "cras_rstream.h"
@@ -83,6 +84,7 @@
 static size_t cras_observer_notify_input_node_gain_called;
 static int cras_iodev_open_called;
 static int cras_iodev_open_ret[8];
+static struct cras_audio_format cras_iodev_open_fmt;
 static int set_mute_called;
 static std::vector<struct cras_iodev*> set_mute_dev_vector;
 static std::vector<unsigned int> audio_thread_dev_start_ramp_dev_vector;
@@ -129,6 +131,10 @@
     channel_counts_[0] = 2;
     channel_counts_[1] = 0;
 
+    fmt_.format = SND_PCM_FORMAT_S16_LE;
+    fmt_.frame_rate = 48000;
+    fmt_.num_channels = 2;
+
     memset(&d1_, 0, sizeof(d1_));
     memset(&d2_, 0, sizeof(d2_));
     memset(&d3_, 0, sizeof(d3_));
@@ -234,7 +240,9 @@
     dummy_hotword_iodev.update_active_node = update_active_node;
   }
 
-  virtual void TearDown() { cras_iodev_list_reset(); }
+  virtual void TearDown() {
+    cras_iodev_list_reset();
+  }
 
   static void set_volume_1(struct cras_iodev* iodev) { set_volume_1_called_++; }
 
@@ -266,6 +274,7 @@
   struct cras_iodev d1_;
   struct cras_iodev d2_;
   struct cras_iodev d3_;
+  struct cras_audio_format fmt_;
   size_t sample_rates_[3];
   size_t channel_counts_[2];
   static int set_volume_1_called_;
@@ -303,6 +312,8 @@
   rc = cras_iodev_list_add_output(&d1_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
                                   cras_make_node_id(d1_.info.idx, 1));
@@ -348,6 +359,71 @@
   EXPECT_EQ(3, cras_observer_notify_active_node_called);
 }
 
+/* Check that the suspend/resume call of active iodev will be triggered and
+ * fallback device will be transciently enabled while adding a new stream whose
+ * channel count is higher than the active iodev. */
+TEST_F(IoDevTestSuite, ReopenDevForHigherChannels) {
+  struct cras_rstream rstream, rstream2;
+  struct cras_rstream* stream_list = NULL;
+  int rc;
+
+  memset(&rstream, 0, sizeof(rstream));
+  memset(&rstream2, 0, sizeof(rstream2));
+  rstream.format = fmt_;
+  rstream2.format = fmt_;
+  rstream2.format.num_channels = 6;
+
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  rc = cras_iodev_list_add_output(&d1_);
+  ASSERT_EQ(0, rc);
+
+  d1_.format = &fmt_;
+  d1_.info.max_supported_channels = 2;
+
+  audio_thread_add_open_dev_called = 0;
+  cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+                                  cras_make_node_id(d1_.info.idx, 1));
+  DL_APPEND(stream_list, &rstream);
+  stream_list_get_ret = stream_list;
+  stream_add_cb(&rstream);
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(1, audio_thread_add_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_open_called);
+  EXPECT_EQ(2, cras_iodev_open_fmt.num_channels);
+
+  audio_thread_add_stream_called = 0;
+  audio_thread_add_open_dev_called = 0;
+  cras_iodev_open_called = 0;
+
+  /* stream_list should be descending ordered by channel count. */
+  DL_PREPEND(stream_list, &rstream2);
+  stream_list_get_ret = stream_list;
+  stream_add_cb(&rstream2);
+  /* The channel count(=6) of rstream2 exceeds d1's max_supported_channels(=2),
+   * rstream2 will be added directly to d1, which will not be re-opened. */
+  EXPECT_EQ(1, audio_thread_add_stream_called);
+  EXPECT_EQ(0, audio_thread_add_open_dev_called);
+  EXPECT_EQ(0, cras_iodev_open_called);
+
+  d1_.info.max_supported_channels = 6;
+  stream_rm_cb(&rstream2);
+
+  audio_thread_add_stream_called = 0;
+  audio_thread_add_open_dev_called = 0;
+  cras_iodev_open_called = 0;
+
+  stream_add_cb(&rstream2);
+  /* Added both rstreams to fallback device, then re-opened d1. */
+  EXPECT_EQ(4, audio_thread_add_stream_called);
+  EXPECT_EQ(2, audio_thread_add_open_dev_called);
+  EXPECT_EQ(2, cras_iodev_open_called);
+  EXPECT_EQ(6, cras_iodev_open_fmt.num_channels);
+
+  cras_iodev_list_deinit();
+}
+
 /* Check that after resume, all output devices enter ramp mute state if there is
  * any output stream. */
 TEST_F(IoDevTestSuite, RampMuteAfterResume) {
@@ -369,6 +445,9 @@
   rc = cras_iodev_list_add_input(&d2_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
                                   cras_make_node_id(d1_.info.idx, 1));
@@ -424,6 +503,8 @@
   rc = cras_iodev_list_add_output(&d1_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+
   cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
                               cras_make_node_id(d1_.info.idx, 0));
 
@@ -457,6 +538,9 @@
   rc = cras_iodev_list_add_input(&d2_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
                               cras_make_node_id(d1_.info.idx, 0));
   /* No close call happened, because no stream exists. */
@@ -502,6 +586,9 @@
   rc = cras_iodev_list_add_output(&d2_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
                               cras_make_node_id(d1_.info.idx, 1));
   DL_APPEND(stream_list, &rstream);
@@ -571,12 +658,15 @@
   struct cras_rstream* stream_list = NULL;
 
   memset(&rstream, 0, sizeof(rstream));
+  rstream.format = fmt_;
   cras_iodev_list_init();
 
   d1_.direction = CRAS_STREAM_OUTPUT;
   rc = cras_iodev_list_add_output(&d1_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+
   cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
                               cras_make_node_id(d1_.info.idx, 0));
 
@@ -603,6 +693,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_;
   cras_tm_timer_cb = NULL;
   cras_iodev_open_ret[3] = -5;
   stream_add_cb(&rstream);
@@ -626,6 +717,8 @@
   rc = cras_iodev_list_add_output(&d1_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+
   rstream.is_pinned = 1;
   rstream.pinned_dev_idx = d1_.info.idx;
 
@@ -681,6 +774,9 @@
   rc = cras_iodev_list_add_output(&d2_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   audio_thread_rm_open_dev_called = 0;
 
@@ -751,6 +847,9 @@
   rc = cras_iodev_list_add_output(&d2_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   audio_thread_rm_open_dev_called = 0;
   device_enabled_count = 0;
@@ -883,6 +982,7 @@
 TEST_F(IoDevTestSuite, AddRemoveOutput) {
   struct cras_iodev_info* dev_info;
   int rc;
+  cras_iodev_list_init();
 
   rc = cras_iodev_list_add_output(&d1_);
   EXPECT_EQ(0, rc);
@@ -914,6 +1014,7 @@
   EXPECT_EQ(0, rc);
   free(dev_info);
   EXPECT_EQ(0, cras_observer_notify_active_node_called);
+  cras_iodev_list_deinit();
 }
 
 // Test output_mute_changed callback.
@@ -992,39 +1093,56 @@
 
 // Test enable/disable an iodev.
 TEST_F(IoDevTestSuite, EnableDisableDevice) {
+  struct cras_rstream rstream;
+  cras_iodev_list_init();
   device_enabled_count = 0;
   device_disabled_count = 0;
+  memset(&rstream, 0, sizeof(rstream));
 
   EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
 
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
                    device_enabled_cb, device_disabled_cb, (void*)0xABCD));
 
-  // Enable a device.
+  // Enable a device, fallback should be diabled accordingly.
   cras_iodev_list_enable_dev(&d1_);
   EXPECT_EQ(&d1_, device_enabled_dev);
   EXPECT_EQ((void*)0xABCD, device_enabled_cb_data);
   EXPECT_EQ(1, device_enabled_count);
+  EXPECT_EQ(1, device_disabled_count);
   EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
 
-  // Disable a device.
+  // Connect a normal stream.
+  cras_iodev_open_called = 0;
+  stream_add_cb(&rstream);
+  EXPECT_EQ(1, cras_iodev_open_called);
+
+  stream_list_has_pinned_stream_ret[d1_.info.idx] = 0;
+  // Disable a device. Expect dev is closed because there's no pinned stream.
+  update_active_node_called = 0;
   cras_iodev_list_disable_dev(&d1_, false);
   EXPECT_EQ(&d1_, device_disabled_dev);
-  EXPECT_EQ(1, device_disabled_count);
+  EXPECT_EQ(2, device_disabled_count);
   EXPECT_EQ((void*)0xABCD, device_disabled_cb_data);
 
+  EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_close_called);
+  EXPECT_EQ(&d1_, cras_iodev_close_dev);
+  EXPECT_EQ(1, update_active_node_called);
+
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
                    device_enabled_cb, device_disabled_cb, (void*)0xCDEF));
   EXPECT_EQ(2, cras_observer_notify_active_node_called);
 
   EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL));
+  cras_iodev_list_deinit();
 }
 
 // Test adding/removing an input dev to the list.
 TEST_F(IoDevTestSuite, AddRemoveInput) {
   struct cras_iodev_info* dev_info;
   int rc, i;
-  uint32_t found_mask;
+  uint64_t found_mask;
 
   d1_.direction = CRAS_STREAM_INPUT;
   d2_.direction = CRAS_STREAM_INPUT;
@@ -1057,8 +1175,8 @@
     found_mask = 0;
     for (i = 0; i < rc; i++) {
       uint32_t idx = dev_info[i].idx;
-      EXPECT_EQ(0, (found_mask & (1 << idx)));
-      found_mask |= (1 << idx);
+      EXPECT_EQ(0, (found_mask & (static_cast<uint64_t>(1) << idx)));
+      found_mask |= (static_cast<uint64_t>(1) << idx);
     }
   }
   if (rc > 0)
@@ -1094,6 +1212,7 @@
   d2_.direction = CRAS_STREAM_INPUT;
 
   server_state_update_begin_return = NULL;
+  cras_iodev_list_init();
 
   rc = cras_iodev_list_add_input(&d1_);
   EXPECT_EQ(0, rc);
@@ -1104,6 +1223,7 @@
 
   EXPECT_EQ(0, cras_iodev_list_rm_input(&d1_));
   EXPECT_EQ(0, cras_iodev_list_rm_input(&d2_));
+  cras_iodev_list_deinit();
 }
 
 // Test removing the last input.
@@ -1317,6 +1437,10 @@
   rc = cras_iodev_list_add_output(&d3_);
   ASSERT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+  d3_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
                                   cras_make_node_id(d3_.info.idx, 1));
@@ -1353,6 +1477,57 @@
   cras_iodev_list_deinit();
 }
 
+TEST_F(IoDevTestSuite, OutputDevIdleClose) {
+  int rc;
+  struct cras_rstream rstream;
+
+  memset(&rstream, 0, sizeof(rstream));
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  rc = cras_iodev_list_add_output(&d1_);
+  EXPECT_EQ(0, rc);
+
+  d1_.format = &fmt_;
+
+  audio_thread_add_open_dev_called = 0;
+  cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+                                  cras_make_node_id(d1_.info.idx, 1));
+  EXPECT_EQ(0, audio_thread_add_open_dev_called);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+  // If a stream is added, the device should be opened.
+  stream_add_cb(&rstream);
+  EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+  audio_thread_rm_open_dev_called = 0;
+  audio_thread_drain_stream_return = 0;
+  clock_gettime_retspec.tv_sec = 15;
+  stream_rm_cb(&rstream);
+  EXPECT_EQ(1, audio_thread_drain_stream_called);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(1, cras_tm_create_timer_called);
+
+  // Expect no rm dev happen because idle time not yet expire, and
+  // new timer should be scheduled for the rest of the idle time.
+  clock_gettime_retspec.tv_sec += 7;
+  cras_tm_timer_cb(NULL, NULL);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(2, cras_tm_create_timer_called);
+
+  // Expect d1_ be closed upon unplug, and the timer stay armed.
+  cras_iodev_list_rm_output(&d1_);
+  EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(0, cras_tm_cancel_timer_called);
+
+  // When timer eventually fired expect there's no more new
+  // timer scheduled because d1_ has closed already.
+  clock_gettime_retspec.tv_sec += 4;
+  cras_tm_timer_cb(NULL, NULL);
+  EXPECT_EQ(2, cras_tm_create_timer_called);
+  cras_iodev_list_deinit();
+}
+
 TEST_F(IoDevTestSuite, DrainTimerCancel) {
   int rc;
   struct cras_rstream rstream;
@@ -1365,6 +1540,8 @@
   rc = cras_iodev_list_add_output(&d1_);
   EXPECT_EQ(0, rc);
 
+  d1_.format = &fmt_;
+
   audio_thread_add_open_dev_called = 0;
   cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
                                   cras_make_node_id(d1_.info.idx, 1));
@@ -1429,6 +1606,107 @@
   cras_iodev_list_deinit();
 }
 
+TEST_F(IoDevTestSuite, CloseDevWithPinnedStream) {
+  int rc;
+  struct cras_rstream rstream1, rstream2;
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  d1_.info.idx = 1;
+  rc = cras_iodev_list_add_output(&d1_);
+  EXPECT_EQ(0, rc);
+
+  memset(&rstream1, 0, sizeof(rstream1));
+  memset(&rstream2, 0, sizeof(rstream2));
+  rstream2.is_pinned = 1;
+  rstream2.pinned_dev_idx = d1_.info.idx;
+
+  d1_.format = &fmt_;
+  audio_thread_add_open_dev_called = 0;
+  EXPECT_EQ(0, audio_thread_add_open_dev_called);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+  // Add a normal stream
+  stream_add_cb(&rstream1);
+  EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+  // Add a pinned stream, expect another dev open call triggered.
+  cras_iodev_open_called = 0;
+  stream_add_cb(&rstream2);
+  EXPECT_EQ(1, cras_iodev_open_called);
+
+  // Force disable d1_ and make sure d1_ gets closed.
+  audio_thread_rm_open_dev_called = 0;
+  update_active_node_called = 0;
+  cras_iodev_close_called = 0;
+  cras_iodev_list_disable_dev(&d1_, 1);
+  EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_close_called);
+  EXPECT_EQ(&d1_, cras_iodev_close_dev);
+  EXPECT_EQ(1, update_active_node_called);
+
+  // Add back the two streams, one normal one pinned.
+  audio_thread_add_open_dev_called = 0;
+  audio_thread_rm_open_dev_called = 0;
+  cras_iodev_open_called = 0;
+  stream_add_cb(&rstream2);
+  EXPECT_EQ(1, audio_thread_add_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_open_called);
+  stream_add_cb(&rstream1);
+
+  // Suspend d1_ and make sure d1_ gets closed.
+  update_active_node_called = 0;
+  cras_iodev_close_called = 0;
+  cras_iodev_list_suspend_dev(d1_.info.idx);
+  EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_close_called);
+  EXPECT_EQ(&d1_, cras_iodev_close_dev);
+  EXPECT_EQ(1, update_active_node_called);
+
+  cras_iodev_list_resume_dev(d1_.info.idx);
+
+  cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, DisableDevWithPinnedStream) {
+  int rc;
+  struct cras_rstream rstream1;
+  cras_iodev_list_init();
+
+  d1_.direction = CRAS_STREAM_OUTPUT;
+  rc = cras_iodev_list_add_output(&d1_);
+  EXPECT_EQ(0, rc);
+
+  memset(&rstream1, 0, sizeof(rstream1));
+  rstream1.is_pinned = 1;
+  rstream1.pinned_dev_idx = d1_.info.idx;
+
+  d1_.format = &fmt_;
+  audio_thread_add_open_dev_called = 0;
+  cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+                                  cras_make_node_id(d1_.info.idx, 1));
+  EXPECT_EQ(0, audio_thread_add_open_dev_called);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+  // Add a pinned stream.
+  cras_iodev_open_called = 0;
+  stream_add_cb(&rstream1);
+  EXPECT_EQ(1, audio_thread_add_open_dev_called);
+  EXPECT_EQ(1, cras_iodev_open_called);
+
+  // Disable d1_ expect no close dev triggered because pinned stream.
+  stream_list_has_pinned_stream_ret[d1_.info.idx] = 1;
+  audio_thread_rm_open_dev_called = 0;
+  update_active_node_called = 0;
+  cras_iodev_close_called = 0;
+  cras_iodev_list_disable_dev(&d1_, 0);
+  EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+  EXPECT_EQ(0, cras_iodev_close_called);
+  EXPECT_EQ(0, update_active_node_called);
+
+  cras_iodev_list_deinit();
+}
+
 TEST_F(IoDevTestSuite, AddRemovePinnedStream) {
   struct cras_rstream rstream;
 
@@ -1447,6 +1725,9 @@
   d2_.info.idx = 2;
   EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   // Setup pinned stream.
   memset(&rstream, 0, sizeof(rstream));
   rstream.is_pinned = 1;
@@ -1506,6 +1787,9 @@
   d2_.direction = CRAS_STREAM_OUTPUT;
   EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
 
+  d1_.format = &fmt_;
+  d2_.format = &fmt_;
+
   // Setup pinned stream.
   memset(&rstream, 0, sizeof(rstream));
   rstream.is_pinned = 1;
@@ -1561,6 +1845,8 @@
   d1_.direction = CRAS_STREAM_INPUT;
   EXPECT_EQ(0, cras_iodev_list_add_input(&d1_));
 
+  d1_.format = &fmt_;
+
   memset(&rstream, 0, sizeof(rstream));
   rstream.is_pinned = 1;
   rstream.pinned_dev_idx = d1_.info.idx;
@@ -1606,6 +1892,8 @@
   d1_.direction = CRAS_STREAM_INPUT;
   EXPECT_EQ(0, cras_iodev_list_add_input(&d1_));
 
+  d1_.format = &fmt_;
+
   memset(&rstream, 0, sizeof(rstream));
   rstream.is_pinned = 1;
   rstream.pinned_dev_idx = d1_.info.idx;
@@ -1665,6 +1953,7 @@
 extern "C" {
 
 // Stubs
+struct main_thread_event_log* main_log;
 
 struct cras_server_state* cras_system_state_update_begin() {
   return server_state_update_begin_return;
@@ -1787,6 +2076,8 @@
                     const struct cras_audio_format* fmt) {
   if (cras_iodev_open_ret[cras_iodev_open_called] == 0)
     iodev->state = CRAS_IODEV_STATE_OPEN;
+  cras_iodev_open_fmt = *fmt;
+  iodev->format = &cras_iodev_open_fmt;
   return cras_iodev_open_ret[cras_iodev_open_called++];
 }
 
@@ -1794,6 +2085,7 @@
   iodev->state = CRAS_IODEV_STATE_CLOSE;
   cras_iodev_close_called++;
   cras_iodev_close_dev = iodev;
+  iodev->format = NULL;
   return 0;
 }
 
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index f16094d..03cfee6 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -9,6 +9,7 @@
 #include "audio_thread_log.h"
 #include "cras_audio_area.h"
 #include "cras_iodev.h"
+#include "cras_main_thread_log.h"
 #include "cras_ramp.h"
 #include "cras_rstream.h"
 #include "dev_stream.h"
@@ -267,9 +268,14 @@
     iodev_.dsp_context = NULL;
 
     cras_audio_format_set_channel_layout_called = 0;
+
+    main_log = main_thread_event_log_init();
   }
 
-  virtual void TearDown() { cras_iodev_free_format(&iodev_); }
+  virtual void TearDown() {
+    cras_iodev_free_format(&iodev_);
+    main_thread_event_log_deinit(main_log);
+  }
 
   struct cras_iodev iodev_;
   size_t sample_rates_[3];
@@ -1056,6 +1062,8 @@
   struct cras_iodev iodev;
   struct cras_ionode ionode, ionode2;
 
+  main_log = main_thread_event_log_init();
+
   memset(&iodev, 0, sizeof(iodev));
   memset(&ionode, 0, sizeof(ionode));
   memset(&ionode2, 0, sizeof(ionode2));
@@ -1077,6 +1085,7 @@
   EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
   cras_iodev_set_node_plugged(&ionode2, 0);
   EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
+  main_thread_event_log_deinit(main_log);
 }
 
 TEST(IoDev, AddRemoveNode) {
@@ -2397,6 +2406,8 @@
 
 extern "C" {
 
+struct main_thread_event_log* main_log;
+
 //  From libpthread.
 int pthread_create(pthread_t* thread,
                    const pthread_attr_t* attr,
@@ -2732,10 +2743,6 @@
 void input_data_set_all_streams_read(struct input_data* data,
                                      unsigned int nframes) {}
 
-int cras_audio_thread_event_severe_underrun() {
-  return 0;
-}
-
 int cras_audio_thread_event_underrun() {
   return 0;
 }
diff --git a/cras/src/tests/stream_list_unittest.cc b/cras/src/tests/stream_list_unittest.cc
index 8f3c2e3..40be35d 100644
--- a/cras/src/tests/stream_list_unittest.cc
+++ b/cras/src/tests/stream_list_unittest.cc
@@ -28,13 +28,16 @@
 
 static unsigned int create_called;
 static struct cras_rstream_config* create_config;
-static struct cras_rstream dummy_rstream;
 static int create_rstream_cb(struct cras_rstream_config* stream_config,
                              struct cras_rstream** stream) {
   create_called++;
   create_config = stream_config;
-  *stream = &dummy_rstream;
-  dummy_rstream.stream_id = 0x3003;
+  *stream = (struct cras_rstream*)malloc(sizeof(struct cras_rstream));
+  (*stream)->stream_id = stream_config->stream_id;
+  (*stream)->direction = stream_config->direction;
+  if (stream_config->format)
+    (*stream)->format = *(stream_config->format);
+
   return 0;
 }
 
@@ -43,6 +46,7 @@
 static void destroy_rstream_cb(struct cras_rstream* rstream) {
   destroy_called++;
   destroyed_stream = rstream;
+  free(rstream);
 }
 
 static void reset_test_data() {
@@ -57,6 +61,10 @@
   struct cras_rstream* s1;
   struct cras_rstream_config s1_config;
 
+  s1_config.stream_id = 0x3003;
+  s1_config.direction = CRAS_STREAM_OUTPUT;
+  s1_config.format = NULL;
+
   reset_test_data();
   l = stream_list_create(added_cb, removed_cb, create_rstream_cb,
                          destroy_rstream_cb, NULL);
@@ -72,6 +80,55 @@
   stream_list_destroy(l);
 }
 
+TEST(StreamList, AddInDescendingOrderByChannels) {
+  struct stream_list* l;
+  struct cras_rstream* s1;
+  struct cras_rstream* s2;
+  struct cras_rstream* s3;
+  struct cras_audio_format s1_format, s2_format, s3_format;
+  struct cras_rstream_config s1_config, s2_config, s3_config;
+
+  s1_config.stream_id = 0x4001;
+  s1_config.direction = CRAS_STREAM_INPUT;
+  s1_format.num_channels = 6;
+  s1_config.format = &s1_format;
+
+  s2_config.stream_id = 0x4002;
+  s2_config.direction = CRAS_STREAM_OUTPUT;
+  s2_format.num_channels = 8;
+  s2_config.format = &s2_format;
+
+  s3_config.stream_id = 0x4003;
+  s3_config.direction = CRAS_STREAM_OUTPUT;
+  s3_format.num_channels = 2;
+  s3_config.format = &s3_format;
+
+  reset_test_data();
+  l = stream_list_create(added_cb, removed_cb, create_rstream_cb,
+                         destroy_rstream_cb, NULL);
+  stream_list_add(l, &s1_config, &s1);
+  EXPECT_EQ(1, add_called);
+  EXPECT_EQ(1, create_called);
+  EXPECT_EQ(6, stream_list_get(l)->format.num_channels);
+
+  stream_list_add(l, &s2_config, &s2);
+  EXPECT_EQ(2, add_called);
+  EXPECT_EQ(2, create_called);
+  EXPECT_EQ(8, stream_list_get(l)->format.num_channels);
+  EXPECT_EQ(6, stream_list_get(l)->next->format.num_channels);
+
+  stream_list_add(l, &s3_config, &s3);
+  EXPECT_EQ(3, add_called);
+  EXPECT_EQ(3, create_called);
+  EXPECT_EQ(8, stream_list_get(l)->format.num_channels);
+  EXPECT_EQ(6, stream_list_get(l)->next->format.num_channels);
+  EXPECT_EQ(2, stream_list_get(l)->next->next->format.num_channels);
+  EXPECT_EQ(0, stream_list_rm(l, 0x4001));
+  EXPECT_EQ(0, stream_list_rm(l, 0x4002));
+  EXPECT_EQ(0, stream_list_rm(l, 0x4003));
+  stream_list_destroy(l);
+}
+
 extern "C" {
 
 struct cras_timer* cras_tm_create_timer(struct cras_tm* tm,
diff --git a/cras/src/tests/timing_unittest.cc b/cras/src/tests/timing_unittest.cc
index d0ecb81..8a2de65 100644
--- a/cras/src/tests/timing_unittest.cc
+++ b/cras/src/tests/timing_unittest.cc
@@ -1177,6 +1177,11 @@
 int cras_audio_thread_event_drop_samples() {
   return 0;
 }
+
+int cras_audio_thread_event_severe_underrun() {
+  return 0;
+}
+
 void* buffer_share_get_data(const struct buffer_share* mix, unsigned int id) {
   return NULL;
 };
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 6eaf4a6..947b945 100644
--- a/cras/src/tools/cras_test_client/cras_test_client.c
+++ b/cras/src/tools/cras_test_client/cras_test_client.c
@@ -310,7 +310,7 @@
 	int rc = 0;
 	uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format);
 
-	rc = read(0, playback_samples, frames * frame_bytes);
+	rc = read(0, playback_samples, (size_t)frames * (size_t)frame_bytes);
 	if (rc <= 0) {
 		terminate_stream_loop();
 		return -1;
@@ -504,17 +504,17 @@
 	unsigned int data2 = log->log[tag_idx].data2;
 	unsigned int data3 = log->log[tag_idx].data3;
 	time_t lt;
-	struct tm *t;
+	struct tm t;
 
 	/* Skip unused log entries. */
 	if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
 		return;
 
-	/* Convert from monotomic raw clock to realtime clock. */
+	/* Convert from monotonic raw clock to realtime clock. */
 	convert_time(&sec, &nsec, sec_offset, nsec_offset);
 	lt = sec;
-	t = localtime(&lt);
-	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", t);
+	localtime_r(&lt, &t);
+	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
 
 	printf("%s.%09u cras atlog  ", time_str, nsec);
 
@@ -533,16 +533,16 @@
 	}
 	convert_time(&sec, &nsec, sec_offset, nsec_offset);
 	lt = sec;
-	t = localtime(&lt);
-	strftime(time_str, 128, " %H:%M:%S", t);
+	localtime_r(&lt, &t);
+	strftime(time_str, 128, " %H:%M:%S", &t);
 
 	switch (tag) {
 	case AUDIO_THREAD_WAKE:
 		printf("%-30s num_fds:%d\n", "WAKE", (int)data1);
 		break;
 	case AUDIO_THREAD_SLEEP:
-		printf("%-30s sleep:%09d.%09d\n", "SLEEP", (int)data1,
-		       (int)data2);
+		printf("%-30s sleep:%09d.%09d non_empty %u\n", "SLEEP",
+		       (int)data1, (int)data2, (int)data3);
 		break;
 	case AUDIO_THREAD_READ_AUDIO:
 		printf("%-30s dev:%u hw_level:%u read:%u\n", "READ_AUDIO",
@@ -775,8 +775,8 @@
 
 	for (i = 0; i < info->num_streams; i++) {
 		int channel;
-		printf("stream: %llu dev: %u\n",
-		       (unsigned long long)info->streams[i].stream_id,
+		printf("stream: 0x%" PRIx64 " dev: %u\n",
+		       info->streams[i].stream_id,
 		       (unsigned int)info->streams[i].dev_idx);
 		printf("direction: %s\n",
 		       (info->streams[i].direction == CRAS_STREAM_INPUT) ?
@@ -848,6 +848,92 @@
 	pthread_mutex_unlock(&done_mutex);
 }
 
+static void show_mainlog_tag(const struct main_thread_event_log *log,
+			     unsigned int tag_idx, int32_t sec_offset,
+			     int32_t nsec_offset)
+{
+	unsigned int tag = (log->log[tag_idx].tag_sec >> 24) & 0xff;
+	unsigned int sec = log->log[tag_idx].tag_sec & 0x00ffffff;
+	unsigned int nsec = log->log[tag_idx].nsec;
+	unsigned int data1 = log->log[tag_idx].data1;
+	unsigned int data2 = log->log[tag_idx].data2;
+	unsigned int data3 = log->log[tag_idx].data3;
+	time_t lt;
+	struct tm t;
+
+	/* Skip unused log entries. */
+	if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
+		return;
+
+	/* Convert from monotomic raw clock to realtime clock. */
+	convert_time(&sec, &nsec, sec_offset, nsec_offset);
+	lt = sec;
+	localtime_r(&lt, &t);
+	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
+
+	printf("%s.%09u cras mainlog  ", time_str, nsec);
+
+	switch (tag) {
+	case MAIN_THREAD_DEV_CLOSE:
+		printf("%-30s dev %u\n", "DEV_CLOSE", data1);
+		break;
+	case MAIN_THREAD_DEV_DISABLE:
+		printf("%-30s dev %u force %u\n", "DEV_DISABLE", data1, data2);
+		break;
+	case MAIN_THREAD_DEV_INIT:
+		printf("%-30s dev %u ch %u rate %u\n", "DEV_INIT", data1, data2,
+		       data3);
+		break;
+	case MAIN_THREAD_DEV_REOPEN:
+		printf("%-30s new ch %u old ch %u rate %u\n", "DEV_REOPEN",
+		       data1, data2, data3);
+		break;
+	case MAIN_THREAD_ADD_ACTIVE_NODE:
+		printf("%-30s dev %u\n", "ADD_ACTIVE_NODE", data1);
+		break;
+	case MAIN_THREAD_SELECT_NODE:
+		printf("%-30s dev %u\n", "SELECT_NODE", data1);
+		break;
+	case MAIN_THREAD_ADD_TO_DEV_LIST:
+		printf("%-30s dev %u %s\n", "ADD_TO_DEV_LIST", data1,
+		       (data2 == CRAS_STREAM_OUTPUT) ? "output" : "input");
+		break;
+	case MAIN_THREAD_NODE_PLUGGED:
+		printf("%-30s dev %u %s\n", "NODE_PLUGGED", data1,
+		       data2 ? "plugged" : "unplugged");
+		break;
+	case MAIN_THREAD_INPUT_NODE_GAIN:
+		printf("%-30s dev %u gain %u\n", "INPUT_NODE_GAIN", data1,
+		       data2);
+		break;
+	case MAIN_THREAD_OUTPUT_NODE_VOLUME:
+		printf("%-30s dev %u volume %u\n", "OUTPUT_NODE_VOLUME", data1,
+		       data2);
+		break;
+	case MAIN_THREAD_SET_OUTPUT_USER_MUTE:
+		printf("%-30s mute %u\n", "SET_OUTPUT_USER_MUTE", data1);
+		break;
+	case MAIN_THREAD_RESUME_DEVS:
+		printf("RESUME_DEVS\n");
+		break;
+	case MAIN_THREAD_SUSPEND_DEVS:
+		printf("SUSPEND_DEVS\n");
+		break;
+	case MAIN_THREAD_STREAM_ADDED:
+		printf("%-30s %s stream 0x%x buffer frames %u\n",
+		       "STREAM_ADDED",
+		       (data2 == CRAS_STREAM_OUTPUT ? "output" : "input"),
+		       data1, data3);
+		break;
+	case MAIN_THREAD_STREAM_REMOVED:
+		printf("%-30s stream 0x%x\n", "STREAM_REMOVED", data1);
+		break;
+	default:
+		printf("%-30s\n", "UNKNOWN");
+		break;
+	}
+}
+
 static void show_btlog_tag(const struct cras_bt_event_log *log,
 			   unsigned int tag_idx, int32_t sec_offset,
 			   int32_t nsec_offset)
@@ -858,17 +944,17 @@
 	unsigned int data1 = log->log[tag_idx].data1;
 	unsigned int data2 = log->log[tag_idx].data2;
 	time_t lt;
-	struct tm *t;
+	struct tm t;
 
 	/* Skip unused log entries. */
 	if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
 		return;
 
-	/* Convert from monotomic raw clock to realtime clock. */
+	/* Convert from monotonic raw clock to realtime clock. */
 	convert_time(&sec, &nsec, sec_offset, nsec_offset);
 	lt = sec;
-	t = localtime(&lt);
-	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", t);
+	localtime_r(&lt, &t);
+	strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
 
 	printf("%s.%09u cras btlog  ", time_str, nsec);
 
@@ -905,7 +991,7 @@
 		       data2);
 		break;
 	case BT_DEV_CONNECTED_CHANGE:
-		printf("%-30s profiles %u now %u\n", "DEV_CONENCTED_CHANGE",
+		printf("%-30s profiles %u now %u\n", "DEV_CONNECTED_CHANGE",
 		       data1, data2);
 		break;
 	case BT_DEV_CONN_WATCH_CB:
@@ -919,6 +1005,15 @@
 	case BT_HFP_HF_INDICATOR:
 		printf("%-30s HF read AG %s indicator\n", "HFP_HF_INDICATOR",
 		       data1 ? "enabled" : "supported");
+		break;
+	case BT_HFP_SET_SPEAKER_GAIN:
+		printf("%-30s HF set speaker gain %u\n", "HFP_SET_SPEAKER_GAIN",
+		       data1);
+		break;
+	case BT_HFP_UPDATE_SPEAKER_GAIN:
+		printf("%-30s HF update speaker gain %u\n",
+		       "HFP_UPDATE_SPEAKER_GAIN", data1);
+		break;
 	case BT_HFP_NEW_CONNECTION:
 		printf("%-30s\n", "HFP_NEW_CONNECTION");
 		break;
@@ -953,6 +1048,12 @@
 	case BT_TRANSPORT_RELEASE:
 		printf("%-30s\n", "TRANSPORT_RELEASE");
 		break;
+	case BT_TRANSPORT_SET_VOLUME:
+		printf("%-30s %d\n", "TRANSPORT_SET_VOLUME", data1);
+		break;
+	case BT_TRANSPORT_UPDATE_VOLUME:
+		printf("%-30s %d\n", "TRANSPORT_UPDATE_VOLUME", data1);
+		break;
 	default:
 		printf("%-30s\n", "UNKNOWN");
 		break;
@@ -983,6 +1084,30 @@
 	pthread_mutex_unlock(&done_mutex);
 }
 
+static void main_thread_debug_info(struct cras_client *client)
+{
+	const struct main_thread_debug_info *info;
+	time_t sec_offset;
+	int32_t nsec_offset;
+	int i, j;
+
+	info = cras_client_get_main_thread_debug_info(client);
+	fill_time_offset(&sec_offset, &nsec_offset);
+	j = info->main_log.write_pos;
+	i = 0;
+	printf("Main debug log:\n");
+	for (; i < info->main_log.len; i++) {
+		show_mainlog_tag(&info->main_log, j, sec_offset, nsec_offset);
+		j++;
+		j %= info->main_log.len;
+	}
+
+	/* Signal main thread we are done after the last chunk. */
+	pthread_mutex_lock(&done_mutex);
+	pthread_cond_signal(&done_cond);
+	pthread_mutex_unlock(&done_mutex);
+}
+
 static void print_cras_audio_thread_snapshot(
 	const struct cras_audio_thread_snapshot *snapshot)
 {
@@ -1416,6 +1541,22 @@
 	pthread_mutex_unlock(&done_mutex);
 }
 
+static void show_main_thread_debug_info(struct cras_client *client)
+{
+	struct timespec wait_time;
+	cras_client_run_thread(client);
+	cras_client_connected_wait(client); /* To synchronize data. */
+	cras_client_update_main_thread_debug_info(client,
+						  main_thread_debug_info);
+
+	clock_gettime(CLOCK_REALTIME, &wait_time);
+	wait_time.tv_sec += 2;
+
+	pthread_mutex_lock(&done_mutex);
+	pthread_cond_timedwait(&done_cond, &done_mutex, &wait_time);
+	pthread_mutex_unlock(&done_mutex);
+}
+
 static void hotword_models_cb(struct cras_client *client,
 			      const char *hotword_models)
 {
@@ -1446,7 +1587,7 @@
 	       cras_client_output_dev_plugged(client, name) ? "Yes" : "No");
 }
 
-/* Repeatedly mute and unmute the output until there is an error. */
+/* Repeatedly mute and un-mute the output until there is an error. */
 static void mute_loop_test(struct cras_client *client, int auto_reconnect)
 {
 	int mute = 0;
@@ -1587,6 +1728,7 @@
 	{"connection_type",     required_argument,      0, 'K'},
 	{"loopback_file",       required_argument,      0, 'L'},
 	{"mute_loop_test",      required_argument,      0, 'M'},
+	{"dump_main",		no_argument,		0, 'N'},
 	{"playback_file",       required_argument,      0, 'P'},
 	{"stream_type",         required_argument,      0, 'T'},
 	{0, 0, 0, 0}
@@ -1610,7 +1752,7 @@
 	printf("--capture_file <name> - "
 	       "Name of file to record to.\n");
 	printf("--capture_gain <dB> - "
-	       "Set system caputre gain in dB*100 (100 = 1dB).\n");
+	       "Set system capture gain in dB*100 (100 = 1dB).\n");
 	printf("--capture_mute <0|1> - "
 	       "Set capture mute state.\n");
 	printf("--channel_layout <layout_str> - "
@@ -1633,6 +1775,8 @@
 	       "Dumps audio thread info.\n");
 	printf("--dump_bt - "
 	       "Dumps debug info for bt audio\n");
+	printf("--dump_main - "
+	       "Dumps debug info from main thread\n");
 	printf("--dump_dsp - "
 	       "Print status of dsp to syslog.\n");
 	printf("--dump_server_info - "
@@ -1657,7 +1801,7 @@
 	printf("--mute <0|1> - "
 	       "Set system mute state.\n");
 	printf("--mute_loop_test <0|1> - "
-	       "Continuously loop mute/umute.\n"
+	       "Continuously loop mute/un-mute.\n"
 	       "                         "
 	       "Argument: 0 - stop on error.\n"
 	       "                         "
@@ -1711,7 +1855,7 @@
 	printf("--suspend <0|1> - "
 	       "Set audio suspend state.\n");
 	printf("--swap_left_right <N>:<M>:<0|1> - "
-	       "Swap or unswap (1 or 0) the left and right channel for the "
+	       "Swap or un-swap (1 or 0) the left and right channel for the "
 	       "ionode with the given index M on the device with index N\n");
 	printf("--stream_type <N> - "
 	       "Specify the type of the stream.\n");
@@ -2030,7 +2174,8 @@
 
 			s = strtok(optarg, ":");
 			nch = atoi(s);
-			coeff = (float *)calloc(nch * nch, sizeof(*coeff));
+			coeff = (float *)calloc((size_t)nch * (size_t)nch,
+						sizeof(*coeff));
 			for (size = 0; size < nch * nch; size++) {
 				s = strtok(NULL, ",");
 				if (NULL == s)
@@ -2151,6 +2296,9 @@
 		case 'M':
 			mute_loop_test(client, atoi(optarg));
 			break;
+		case 'N':
+			show_main_thread_debug_info(client);
+			break;
 		case 'P':
 			playback_file = optarg;
 			break;
diff --git a/scripts/audio_diagnostics b/scripts/audio_diagnostics
index e6818d7..b754c07 100755
--- a/scripts/audio_diagnostics
+++ b/scripts/audio_diagnostics
@@ -7,7 +7,8 @@
 # Collect information about the audio system from top to bottom.
 
 dump_cards() {
-    for card in "${@}"
+    # shellcheck disable=SC2068
+    for card in ${@}
     do
         echo "=== amixer -c ${card} scontents ==="
         amixer -c "${card}" scontents
@@ -34,6 +35,9 @@
 echo '=== cras_test_client --dump_audio_thread ==='
 cras_test_client --dump_audio_thread
 
+echo '=== cras_test_client --dump_main ==='
+cras_test_client --dump_main
+
 echo '=== cras_test_client --dump_bt ==='
 cras_test_client --dump_bt
 
diff --git a/sound_card_init/max98390d/src/amp_calibration.rs b/sound_card_init/max98390d/src/amp_calibration.rs
index 5ee96bb..50f4227 100644
--- a/sound_card_init/max98390d/src/amp_calibration.rs
+++ b/sound_card_init/max98390d/src/amp_calibration.rs
@@ -25,10 +25,11 @@
 const CALI_ERROR_LOWER_LIMIT: f32 = 0.03;
 
 const FRAMES_PER_BUFFER: usize = 256;
-const FRAME_RATE: usize = 48000;
+const FRAME_RATE: u32 = 48000;
 const NUM_CHANNELS: usize = 2;
 const FORMAT: SampleFormat = SampleFormat::S16LE;
-const DURATION_MS: usize = 1000;
+const DURATION_MS: u32 = 1000;
+const WARM_UP_DURATION_MS: u32 = 300;
 
 /// Amp volume mode emulation used by set_volume().
 #[derive(PartialEq)]
@@ -267,7 +268,9 @@
 
         let handle = thread::spawn(move || -> Result<()> {
             let local_buffer = [0u8; FRAMES_PER_BUFFER * NUM_CHANNELS * 2];
-            let iterations: usize = (FRAME_RATE * DURATION_MS) / FRAMES_PER_BUFFER / 1000;
+            let iterations = (FRAME_RATE * DURATION_MS) / FRAMES_PER_BUFFER as u32 / 1000;
+            let warm_up_iterations =
+                (FRAME_RATE * WARM_UP_DURATION_MS) / FRAMES_PER_BUFFER as u32 / 1000;
 
             let (_control, mut stream) = cras_client
                 .new_pinned_playback_stream(
@@ -290,7 +293,8 @@
                 let _write_frames = buffer.write(&local_buffer).map_err(Error::PlaybackFailed)?;
 
                 // Notifies the main thread to start the calibration.
-                if i == 1 {
+                // The mute playing time need to be longer than WARM_UP_DURATION_MS to get rdc properly.
+                if i == warm_up_iterations {
                     let (lock, cvar) = &*playback_started;
                     let mut started = lock.lock()?;
                     *started = true;
diff --git a/sound_card_init/max98390d/src/error.rs b/sound_card_init/max98390d/src/error.rs
index 6381180..572340d 100644
--- a/sound_card_init/max98390d/src/error.rs
+++ b/sound_card_init/max98390d/src/error.rs
@@ -25,6 +25,7 @@
     InvalidDatastore,
     InvalidTemperature(i32),
     LargeCalibrationDiff(i32, i32),
+    MissingDSMParam,
     MutexPoisonError,
     NewPlayStreamFailed(libcras::BoxError),
     NextPlaybackBufferFailed(libcras::BoxError),
@@ -79,6 +80,7 @@
                 rdc, temp
             ),
             MutexPoisonError => write!(f, "mutex is poisoned"),
+            MissingDSMParam => write!(f, "missing dsm_param.bin"),
             NewPlayStreamFailed(e) => write!(f, "{}", e),
             NextPlaybackBufferFailed(e) => write!(f, "{}", e),
             PlaybackFailed(e) => write!(f, "{}", e),
diff --git a/sound_card_init/max98390d/src/lib.rs b/sound_card_init/max98390d/src/lib.rs
index c9f63f5..7e445c5 100644
--- a/sound_card_init/max98390d/src/lib.rs
+++ b/sound_card_init/max98390d/src/lib.rs
@@ -10,6 +10,8 @@
 mod settings;
 mod vpd;
 
+use std::path::Path;
+
 use cros_alsa::Card;
 use sys_util::error;
 
@@ -32,6 +34,21 @@
     let settings = DeviceSettings::from_yaml_str(conf)?;
     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);
+            }
+        }
+        return Err(Error::MissingDSMParam);
+    }
     // If some error occurs during the calibration, the iteration will continue running the
     // calibration for the next amp.
     let results: Vec<Result<()>> = settings
diff --git a/sound_card_init/max98390d/src/settings.rs b/sound_card_init/max98390d/src/settings.rs
index 5e7ffe3..32feefd 100644
--- a/sound_card_init/max98390d/src/settings.rs
+++ b/sound_card_init/max98390d/src/settings.rs
@@ -7,10 +7,13 @@
 
 use crate::error::{Error, Result};
 
-/// `DeviceSettings` includes the settings of max98390. It currently includes the settings of amplifier calibration.
+/// `DeviceSettings` includes the settings of max98390. It currently includes:
+/// * the settings of amplifier calibration.
+/// * the path of dsm_param.
 #[derive(Debug, Default, PartialEq, Deserialize, Clone)]
 pub struct DeviceSettings {
     pub amp_calibrations: Vec<AmpCalibSettings>,
+    pub dsm_param: String,
 }
 
 /// `AmpCalibSettings` includes the settings needed for amplifier calibration.
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 5bbdb73..bddf6ea 100644
--- a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
+++ b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
@@ -75,3 +75,4 @@
 rt_sigreturn: 1
 wait4: 1
 restart_syscall: 1
+sched_yield: 1
\ No newline at end of file
diff --git a/sound_card_init/sound_card_init.conf b/sound_card_init/sound_card_init.conf
index 0f504fa..7ad1a80 100644
--- a/sound_card_init/sound_card_init.conf
+++ b/sound_card_init/sound_card_init.conf
@@ -26,8 +26,8 @@
       "Invalid SOUND_CARD_ID supplied"
     exit 1
   else
-    mkdir -p /var/lib/sound_card_init/"${SOUND_CARD_ID}"
-    chown sound_card_init:sound_card_init /var/lib/sound_card_init/"${SOUND_CARD_ID}"
+    mkdir -m 0755 -p /var/lib/sound_card_init/"${SOUND_CARD_ID}"
+    chown -R sound_card_init:sound_card_init /var/lib/sound_card_init
   fi
 end script
 
diff --git a/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf b/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf
new file mode 100644
index 0000000..c44de8d
--- /dev/null
+++ b/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf
@@ -0,0 +1,132 @@
+SectionVerb {
+	Value {
+		FullySpecifiedUCM "1"
+	}
+
+	EnableSequence [
+		cdev "hw:ICUSBAUDIO7D"
+
+		cset "name='Line Capture Switch', off"
+		cset "name='Mic Capture Switch', off"
+		cset "name='IEC958 In Capture Switch', off"
+		cset "name='PCM Capture Switch', off"
+		cset "name='Speaker Playback Switch', off"
+		cset "name='Mic Playback Switch', off"
+	]
+
+	DisableSequence [
+	]
+}
+
+SectionDevice."Speaker-Headset".0 {
+	Comment "Speaker out"
+
+	Value {
+		PlaybackPCM "hw:ICUSBAUDIO7D,0"
+		PlaybackMixerElem "Speaker"
+	}
+
+	EnableSequence [
+		cset "name='Speaker Playback Switch', on"
+	]
+
+	DisableSequence [
+		cset "name='Speaker Playback Switch', off"
+	]
+}
+
+SectionDevice."Line In".0 {
+	Comment "Line In"
+
+	Value {
+		CapturePCM "hw:ICUSBAUDIO7D,0"
+		CaptureMixerElem "Line"
+	}
+
+	ConflictingDevice [
+		"Mic"
+		"SPDIF In"
+		"PCM"
+	]
+
+	EnableSequence [
+		cset "name='Line Capture Switch', on"
+		cset "name='PCM Capture Source', Line"
+	]
+
+	DisableSequence [
+		cset "name='Line Capture Switch', off"
+	]
+}
+
+SectionDevice."Mic".0 {
+	Comment "Mic Input"
+
+	Value {
+		CapturePCM "hw:ICUSBAUDIO7D,0"
+		CaptureMixerElem "Mic"
+	}
+
+	ConflictingDevice [
+		"Line In"
+		"SPDIF In"
+		"PCM"
+	]
+
+	EnableSequence [
+		cset "name='Mic Capture Switch', on"
+		cset "name='PCM Capture Source', Mic"
+	]
+
+	DisableSequence [
+		cset "name='Mic Capture Switch', off"
+	]
+}
+
+SectionDevice."SPDIF In".0 {
+	Comment "S/PDIF In"
+
+	Value {
+		CapturePCM "hw:ICUSBAUDIO7D,0"
+		CaptureMixerElem "IEC958 In"
+	}
+
+	ConflictingDevice [
+		"Line In"
+		"Mic"
+		"PCM"
+	]
+
+	EnableSequence [
+		cset "name='IEC958 In Capture Switch', on"
+		cset "name='PCM Capture Source', IEC958 In"
+	]
+
+	DisableSequence [
+		cset "name='IEC958 In Capture Switch', off"
+	]
+}
+
+SectionDevice."PCM".0 {
+	Comment "PCM Capture"
+
+	Value {
+		CapturePCM "hw:ICUSBAUDIO7D,0"
+		CaptureMixerElem "PCM"
+	}
+
+	ConflictingDevice [
+		"Line In"
+		"Mic"
+		"SPDIF In"
+	]
+
+	EnableSequence [
+		cset "name='PCM Capture Switch', on"
+		cset "name='PCM Capture Source', Mixer"
+	]
+
+	DisableSequence [
+		cset "name='PCM Capture Switch', off"
+	]
+}
diff --git a/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf b/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf
new file mode 100644
index 0000000..3dc10fa
--- /dev/null
+++ b/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf
@@ -0,0 +1,6 @@
+Comment "Startech USB 7D Audio"
+
+SectionUseCase."HiFi" {
+	File "HiFi.conf"
+	Comment "Default"
+}