cpal icon indicating copy to clipboard operation
cpal copied to clipboard

`Android` / `Oboe` returns duplicate devices

Open setoelkahfi opened this issue 8 months ago • 0 comments

Click me

Image

This available_hosts() works correctly in other platforms, but Android returns duplicate entries. I investigated it and it turns out it calls the getDevices in the AudioManager API. The cpal implementation utils.rs line 168:


pub fn get_devices<'j>(
    env: &mut JNIEnv<'j>,
    subject: &JObject<'j>,
    flags: i32,
) -> JResult<JObjectArray<'j>> {
    env.call_method(
        subject,
        "getDevices",
        "(I)[Landroid/media/AudioDeviceInfo;",
        &[flags.into()],
    )?
    .l()
    .map(From::from)
}

Which is called from:


fn try_request_devices_info<'j>(
    env: &mut JNIEnv<'j>,
    context: &JObject<'j>,
    direction: AudioDeviceDirection,
) -> JResult<Vec<AudioDeviceInfo>> {
    let audio_manager = get_system_service(env, context, Context::AUDIO_SERVICE)?;

    let devices = get_devices(env, &audio_manager, direction as i32)?;

    let length = env.get_array_length(&devices)?;

    (0..length)
        .map(|index| {
            let device = env.get_object_array_element(&devices, index)?;
            let id = call_method_no_args_ret_int(env, &device, "getId")?;
            let address = call_method_no_args_ret_string(env, &device, "getAddress")?;
            let address = String::from(env.get_string(&address)?);
            let product_name =
                call_method_no_args_ret_char_sequence(env, &device, "getProductName")?;
            let product_name = String::from(env.get_string(&product_name)?);
            let device_type =
                FromPrimitive::from_i32(call_method_no_args_ret_int(env, &device, "getType")?)
                    .unwrap_or(AudioDeviceType::Unsupported);
            let direction = AudioDeviceDirection::new(
                call_method_no_args_ret_bool(env, &device, "isSource")?,
                call_method_no_args_ret_bool(env, &device, "isSink")?,
            );
            let channel_counts =
                call_method_no_args_ret_int_array(env, &device, "getChannelCounts")?;
            let sample_rates = call_method_no_args_ret_int_array(env, &device, "getSampleRates")?;
            let formats = call_method_no_args_ret_int_array(env, &device, "getEncodings")?
                .into_iter()
                .filter_map(SampleFormat::from_encoding)
                .collect::<Vec<_>>();

            Ok(AudioDeviceInfo {
                id,
                address,
                product_name,
                device_type,
                direction,
                channel_counts,
                sample_rates,
                formats,
            })
        })
        .collect::<Result<Vec<_>, _>>()
}

Is anyone familiar with this part of the codebase?

setoelkahfi avatar Apr 15 '25 19:04 setoelkahfi