I9505 runs on APQ8064T and the way how the HAL layer set up the in-call voice recording audio path on the chipset has changed compared to the previous generation MSM8960. Now, in addition to set correct Mixer controls in Kernel, it also requires sending some sort of "magic" commands through libcsd-client.so (Qualcomm proprietary, close source) library to the baseband modem.
Google does this for the Nexus 4 (runs APQ8064) at the HAL layer by dlsym the libcsd-client.so (see the csd_start_record function). However, using the AudioRecord API with MediaRecorder.AudioSource.VOICE_DOWNLINK at App level won't work because AudioPolicyManagerBase.cpp has been hardcoded to take in IOProfile with channel mask "AUDIO_CHANNEL_IN_MONO" or "AUDIO_CHANNEL_IN_STEREO" only, while in the in-call voice recording use case, the channel masks in use has to be either AUDIO_CHANNEL_IN_VOICE_UPLINK or AUDIO_CHANNEL_IN_VOICE_DNLINK. It seems both Google and Samsung has no intention to correct this problem. This is weird that public android API for recording call is never guaranteed to work properly.
So, to overcome this issue, I decided to write a NDK executable to dlsym the csd_start_recording/csd_stop_recording just the same way as Google has done for Nexus 4 and use amix/arec utility to do recording natively on a rooted device. But now the question comes.
In my executable, I called csd_client_init and csd_start_record, both invocations returned no errors, but logcat shows some QMI(Qualcomm MSM Interface) error from the libcsd-client.so and recorded wave file had nothing but silence. Does any body tried things similar to this? I'm now totally out of idea how to get call recording working without having to flash modified ROM to the device.
UPDATE
I repeated the test again, but it seems the QMI errors are gone. I guess QMI errors I met last time was due to the fact that I've messed up the state of machine during the test. However, recording still doesn't work. Result is still silence only. Below is the logcat output that I grep by "csd":
D/ ( 217): csd_client_disable_device: rx 7, tx 4, client_state=1
E/ ( 217): csd_client_disable_device: Disable received in invalid state:1
D/ ( 217): csd_client_enable_device: APQ rx 7, tx 34, ec 43, tty 0x10012 state 1
D/ ( 217): csd_client_enable_device: Remote rx -1, tx -1
E/ ( 217): csd_client_enable_device: Enable received in invalid state 1
D/ ( 217): csd_client_start_voice: State 1
D/ ( 217): csd_client_async_cb: msg_id 0x33 result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x34 result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x55 result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x5c result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x4c result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x4c result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x57 result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x36 result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x3e result 0 error 0
D/ ( 217): csd_client_async_cb: msg_id 0x36 result 0 error 0
D/ ( 217): csd_client_volume: volume 0, state 2, rc 0
D/ ( 217): csd_client_async_cb: msg_id 0x37 result 0 error 0
D/ ( 217): csd_client_set_rx_mute: mute 0, state 2, rc 0
D/ ( 217): csd_client_async_cb: msg_id 0x3e result 0 error 0
D/ ( 217): csd_client_mic_mute: mute 0, state 2, rc 0
D/ ( 217): csd_client_async_cb: msg_id 0x36 result 0 error 0
D/ ( 217): csd_client_volume: volume 4, state 2, rc 0
D/ ( 6801): csd_client_init
E/ ( 6801): csd_client_service_init: Invalid rx device 0, setting to handset
E/ ( 6801): csd_client_service_init: Invalid tx device 0, setting to handset
D/ ( 217): csd_client_stop_voice: State 2
D/ ( 217): csd_client_async_cb: msg_id 0x58 result 1 error 3
E/ ( 217): csd_client_stop_voice: Error -1 stopping voice manager
E/ALSADevice( 217): s_close: csd_client error -1
PID 217 is the original csd_client on device, PID 6801 is my executable.
My executable do the client initialisation and call "csd_start_recording" on libcsd-client.so together AFTER a call is established. Seen from the log, it's obvious that the "real" csd-client is doing two-way communication with the modem by registering some callbacks when the device boots and regulating audio settings from then on. For example, "csd_client_start_voice" is called by HAL when the call gets through. I suspect my faked csd-client is missing out all those intermediate states, as a result, failed the purpose.
Anyway, info is really limited at this stage, and I'm feeling like shooting in the dark. Hope someone can help here.
csd_client_service_init: Invalid rx device 0, setting to handset
csd_client_service_init: Invalid tx device 0, setting to handset
doesn't matter because the csd-client initialized by /system/bin/mediaserver also emit them when booting up. – ReliefAudioPolicyManagerBase
from CodeAurora. Instead, I think they referenced the specific version from Google's git, which was for Nexus 4 implementation. I can tell I'm sure because if I useMediaRecorder.AudioSource.VOICE_DOWNLINK
on a AudioRecord, theAudioPolicyManagerBase
complained with an error log saying "can't get input profile ..." and it printed out the channel mask in request, which is not the one defined on CodeAurora, but the one defined in Google's source. – Relief