I am working on enabling secure playback on Lollipop
. I am using ExoPlayer
to validate the usecase. I am able to create a secure OMX
video decoder component(H264.secure).
However, after the creation, I am facing a crash in MediaCodec
as shown below
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xf0300000
eax f0300000 ebx ec1da038 ecx 00000005 edx 00000002
esi ec3ca200 edi f325b148
xcs 00000023 xds 0000002b xes 0000002b xfs 000000bf xss 0000002b
eip ec0e1655 ebp e05ffb28 esp e05ffa90 flags 00210202
#00 pc 000b5655 /system/lib/libstagefright.so (android::MediaCodec::onQueueInputBuffer(android::sp<android::AMessage> const&)+1061)
#01 pc 000b7b16 /system/lib/libstagefright.so (android::MediaCodec::onMessageReceived(android::sp<android::AMessage> const&)+1894)
#02 pc 0000e039 /system/lib/libstagefright_foundation.so (android::ALooperRoster::deliverMessage(android::sp<android::AMessage> const&)+345)
#03 pc 0000d3d0 /system/lib/libstagefright_foundation.so (android::ALooper::loop()+256)
#04 pc 0000d4ed /system/lib/libstagefright_foundation.so (android::ALooper::LooperThread::threadLoop()+29)
#05 pc 000169de /system/lib/libutils.so (android::Thread::_threadLoop(void*)+398)
#06 pc 0006fe92 /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+98)
#07 pc 000160fa /system/lib/libutils.so (thread_data_t::trampoline(thread_data_t const*)+122)
After some analysis i found that the crash occurs at function ACodec::allocateBuffersOnPort
I am a newbie on android. Any pointers to debug this would be helpful
To summarize, the issue is specific to a case when kFlagIsSecure
is set and the creation of OMX
buffer in a different process as compared to MediaCodec
, which leads to a segmentation fault when accessed in MediaCodec
. Please refer below for the detailed background about this issue.
To overcome this issue, I would recommend the following changes in ACodec
size_t totalSize = def.nBufferCountActual * def.nBufferSize;
mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
/* Check if the component resides in same pid as ACodec */
bool isLocalComponent = mOMX->livesLocally(mNode, getpid()); // New Code
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
...
...
and modify the check for allocation as below
-- if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
-- || mUseMetadataOnEncoderOutput) {
// Modified check
++if (isLocalComponent && ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
++ || mUseMetadataOnEncoderOutput)) {
P.S: I would recommend you to check with Google about this solution.
Background:
ExoPlayer
creates a video decoder as a MediaCodec
component. When a new MediaCodec
component is created, the corresponding object in JNI
is created. Please note that there is no interaction with MediaPlayerService
in this process.
MediaCodec
internally creates an ACodec
which interacts with the OMX
core and subsequently OMX
component.
ACodec
is created in the same context as MediaCodec
. When OMXClient::connect
is invoked, the OMX
handle is created in the MediaPlayer
service's context. Hence, the process id of OMX
component and ACodec
would be different.
For secure input buffers, there is a special handling in ACodec::allocateBuffersOnPorts
. Here, the buffer pointer returned from allocateBuffer is wrapped as ABuffer
and queued for consumption. In my view, there is a potential issue in the current implementation as below.
ACodec::allocateBufferOnPort
calls mOMX->allocateBuffer
. mOMX
is of type IOMX
i.e. there is a binder interaction involved. Please do note this variable &buffer_data
which will translate to ptr
in ACodec::allocateBufferOnPorts
layer as this is critical for the following part.
In OMXNodeInstance
which actually runs in MediaPlayerService
's context, a traditional OMX_AllocateBuffer
is called. In OMXNodeInstance::allocateBuffer
, after the allocation *buffer_data
is initialized with header->pBuffer
which is basically a local pointer allocated by the OMX
component potentially through a simple malloc
call.
When the control returns, the same pointer is written into the binder interface here and subsequently read back here. So, when the control comes out mOMX->allocateBuffer
, the value of ptr
is equivalent to header->pBuffer
allocated by OMX
component, but both of which are in 2 different processes.
Hence, when ACodec
creates the ABuffer
based on this ptr
which is then accessed in MediaCodec
, there will be an access violation as the address was created in a different process' context as compared to MediaCodec
's process id.