Android App Crashes on Physical Device But Not Emulator: "Parcel: dup() failed in Parcel::read [...] error: Too many open files"

Viewed 721

I'm trying to turn an old phone into a networked security camera, since crime has increased dramatically in my area during all these riots (and I don't want to rely on someone else's app to control access to my private moments).

I'm chunking-and-sending, so the camera records video for a number of seconds, stops, encodes the binary of the captured file into Base64, then shoots it off to a home server via POST request, all in an endless loop. The server unwraps + decodes + saves it as the original binary "MP4" onto its own disk (TODO: fun post-processing for motion detection).

Using a variety of virtual devices at (and around) my target phone's OS version & screen size, this all works for extended periods of time. I've used 60-second chunks for 15+ minutes, plus 6-second chunks for over an hour. I consistently receive the goofy virtual room videos the emulator produces onto my server.

But on the Samsung Galaxy S5 running Android 6.0.1 that dreams of becoming a security camera, it usually takes 2 or 3 videos being sent before the app crashes... except when your resolution is set too high, then you run into a different symptom.

Symptom #0: Crashing at the End of a Chunk

E/Parcel: dup() failed in Parcel::read, i is 1, fds[i] is -1, fd_count is 2, error: Too many open files

E/Surface: dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: -22

W/Adreno-EGLSUB: DequeueBuffer:721: dequeue native buffer fail: Invalid argument, buffer=0x0, handle=0x0

W/Adreno-EGL: <qeglDrvAPI_eglSwapBuffers:3800>: EGL_BAD_SURFACE

Followed immediately by this second error:

E/CameraDeviceGLThread-1: Received exception on GL render thread:

java.lang.IllegalStateException: swapBuffers: EGL error: 0x300d

Then finally, once the chunk time is up and the camera goes to record again, the final error occurs to crash the entire app:

I/CameraDeviceState: Legacy camera service transitioning to state ERROR

E/AndroidRuntime: FATAL EXCEPTION: CameraThread

Process: com.example.roselawncam, PID: 14639

android.hardware.camera2.CameraAccessException: The camera device has encountered a serious error

Symptom #1: Crashing in the Middle of a Chunk Because You Went Too High-Res For Your Own Good

These warnings make it clear that resource strain causes this symptom. They occur as the app crashes, with higher resolutions causing sooner crashes. I clocked these bad boys at:

  • 1920x1080 (30 FPS): 5 seconds
  • 1280x720 (30 FPS): 9 seconds
  • 800x480 (30 FPS): 15 seconds

Times were similar for both the front and back cameras. At lower resolutions, you start running into Symptom #0 unless you bump your chunk time way up. Anyway:

W/Adreno-GSL: <gsl_ldd_control:475>: ioctl fd 28 code 0xc01c0915 (IOCTL_KGSL_MAP_USER_MEM) failed: errno 12 Out of memory

W/Adreno-EGLSUB: SyncBackBuffer:3130: failed to map the memory for fd=281 offs=0

E/Adreno-EGLSUB: SyncBackBuffer:3131: SyncBackBuffer: FATAL ERROR : (null)

A/Adreno-GSL: Exiting the process com.example.roselawncam from function SyncBackBuffer and line 3131

A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 19618 (CameraDeviceGLT)

And finally, the pertinent Kotlin pieces:

private fun createRecorder(surface: Surface) = MediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.MIC)
        setVideoSource(MediaRecorder.VideoSource.SURFACE)
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        setOutputFile(outputFile.absolutePath)
        setVideoEncodingBitRate(RECORDER_VIDEO_BITRATE)
        if (args_fps > 0) setVideoFrameRate(args_fps)
        setVideoSize(args_width, args_height)
        setVideoEncoder(MediaRecorder.VideoEncoder.H264)
        setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        setInputSurface(surface)
}

private fun recordIt(cameraManager: CameraManager, cameraThread: HandlerThread) {
    val cameraHandler = Handler(cameraThread.looper)
    val stateCallback: CameraDevice.StateCallback = object: CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            camera.createCaptureSession(
                listOf<Surface>(recorderSurface),
                object : CameraCaptureSession.StateCallback() {
                    override fun onConfigured(session: CameraCaptureSession) {
                        val recTimeSeconds = findViewById<TextView>(R.id.recTimeSeconds)
                        val chunkTimeMilliseconds = recTimeSeconds.text.toString().toLong() * 1000

                        // Boolean "stopREC" (e.g. "Stop Recording") is false at this point
                        while (!stopREC) {
                            // // // This loop should run forever, but crashes after a few times // // //
                            val recorder: MediaRecorder by lazy { createRecorder(recorderSurface) }
                            val recordRequest: CaptureRequest by lazy {
                                session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
                                    addTarget(recorderSurface)
                                    set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(args_fps, args_fps))
                                }.build()
                            }
                            session.setRepeatingRequest(recordRequest, null, cameraHandler)
                            recorder.apply { prepare(); start() }
                            Thread.sleep(chunkTimeMilliseconds)
                            recorder.apply { stop(); release() }
    
                            // Send the video file across the network in JSON via POST request:
                            val params = HashMap<String, String>()
                            params["videodata"] = convertToBase64(outputFile)
                            val jsonObject = JSONObject(params as Map<*, *>)
                            val request = JsonObjectRequest(Request.Method.POST, url, jsonObject, null, null)
                            queue.add(request)
                            // // // End of loop that should've ran forever, but crashes occasionally instead  // // //
                        }
                        camera.close()
                    }
                    override fun onConfigureFailed(session: CameraCaptureSession) {}
                }, 
                cameraHandler
            )
        }
        override fun onDisconnected(camera: CameraDevice) { recorder.stop(); recorder.release() }
        override fun onError(camera: CameraDevice, error:Int) { camera.close() }
    }
    cameraManager.openCamera(args_cameraId, stateCallback, cameraHandler)
}
2 Answers

I have some suggestions:

  • Force the use of synchronized functions where you need a file lock so the threads would execute that block of code sequentially, open and close the file stream in an organized way, for instance, access to files. That would avoid out of memory and file already in use errors.

  • Is it possible not to encode the file to Base 64? The string would be too large, check if it avoid any error.

And nice initiative for doind your own app.

Too many open files

The first error message of Symptom #0 is

E/Parcel: dup() failed in Parcel::read, i is 1, fds[i] is -1, fd_count is 2, error: Too many open files

which came from Parcel.cpp:

status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
    fds[i] = dup(this->readFileDescriptor());
    if (fds[i] < 0) {
        err = BAD_VALUE;
        ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
            i, fds[i], fd_count, strerror(errno));
    }
}

It shows that the above error took place at

fds[i] = dup(this->readFileDescriptor());

I also searched about Too many open files and found a similar question. It has a detailed error log and an answer. Both indicates File Descriptor again.

The root cause might be ...

params["videodata"] = convertToBase64(outputFile)

Please check the implementation of convertToBase64().

According to this and this, File Descriptors could leak.

Related