Search code examples
android-ndkandroid-permissionsmediastoreandroid-10.0mediaextractor

Error setting extractor data source, err -10002 when using AMediaExtractor with audio file located in media store


I am trying to fetch an audio file from MediaStore, extract audio data from it, decode it, and play it, on Android 10.

I am getting the following error when I call setDataSource on my MediaExtractor instance:

Error setting extractor data source, err -10002

To reproduce:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val path = getSong().get(0).path
        stringToJNI("file://"+ path)

        val URI = Uri.parse(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString() + "/" + getSong().get(0).id)
        stringToJNI(URI!!.toString())
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringToJNI(URI: String)

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
        }
    }

    fun getSong() : MutableList<Songs> {
        val SONGS_PROJECTION = arrayOf(
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.DATA
        )

        val cursor = contentResolver.query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            SONGS_PROJECTION,
            null,
             null,
            MediaStore.Audio.Media._ID +
                    " ASC LIMIT 100"
        )

        val items: MutableList<Songs> = mutableListOf()
        cursor?.let {
            if (cursor.count > 0) {
                cursor.moveToFirst()
                while (!cursor.isAfterLast) {
                    val s0 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[0]))
                    val s1 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[1]))
                    val s2 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[2]))
                    items.add(Songs(s0, s1, s2))
                    cursor.moveToNext()
                }
            }
            cursor.close()
        }
        return items
    }
}

In MainActivity, I pass to native side once the path and second time the URI, each time without luck.

#include <jni.h>
#include <string>
#include <media/NdkMediaExtractor.h>
#include <android/log.h>
#include <bitset>

#define APP_NAME "MediaStoreToNativeAudio"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__))

extern "C" JNIEXPORT void JNICALL
Java_com_example_mediastoretonativeaudio_MainActivity_stringToJNI(
    JNIEnv *env,
    jobject jobj,
    jstring URI) {

const char *uri = env->GetStringUTFChars(URI, NULL);
std::string s(uri);

AMediaExtractor *extractor = AMediaExtractor_new();
media_status_t amresult = AMediaExtractor_setDataSource(extractor, uri);
if (amresult != AMEDIA_OK) {
    LOGE("AMediaExtractor_setDataSource called with: [%s]", s.c_str());
    LOGE("Error setting extractor data source, err %d", amresult);
}

return;
}

from logs:

2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/NdkMediaExtractor: can't create http service
2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: AMediaExtractor_setDataSource called with: [file:///storage/emulated/0/Music/Thank you for the drum machine/01 - Everything Moves.mp3]
2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: Error setting extractor data source, err -10002
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/NdkMediaExtractor: can't create http service
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: AMediaExtractor_setDataSource called with: [content://media/external/audio/media/472]
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: Error setting extractor data source, err -10002

My manifest:

After installing app I also give permission through settings.

Edit:

A public repository with test code: https://github.com/AndrewBloom/MediaStoreToNativeAudioSample

the same behaviour results using:

android:requestLegacyExternalStorage="true"

Solution

  • This to me seems a bug on Android 10. It seems that android:requestLegacyExternalStorage="true" does not change the situation. You may have to request on manifest and ask same permission at runtime. The AMediaExtractor_setDataSource function must be called on a thread that is attached to Java. Doing all of that correctly will allow you to make it work on other versions of Android but not on Android 10. I've reported the issue on Android Bug Tracker here: https://issuetracker.google.com/144837266 As per google answer, it seems that all the app using native libraries that require file access through path can be affected and they know the issue https://www.youtube.com/watch?v=UnJ3amzJM94 .

    A workaround in my case was to use AMediaExtractor_setDataSourceFd, getting the file descriptor at Java level through contentResolver and its method openFileDescriptor.