安全播放:在 MediaCodec 中观察到崩溃

Secure Playback: Crash observed in MediaCodec

我正在努力在 Lollipop 上启用安全播放。我正在使用 ExoPlayer 来验证用例。我能够创建安全的 OMX 视频解码器组件 (H264.secure)。

但是,创建后,我在 MediaCodec 中遇到崩溃,如下所示

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)

经过一些分析,我发现崩溃发生在函数 ACodec::allocateBuffersOnPort

我是 android 的新手。任何调试它的指针都会有所帮助

总而言之,该问题特定于 设置 kFlagIsSecure 的情况 以及在不同进程中创建 OMX 缓冲区MediaCodec,在 MediaCodec 中访问时会导致分段错误。有关此问题的详细背景,请参阅下文。

为了克服这个问题,我建议在 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);
   ...
   ...

并修改分配检查如下

-- if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
--      || mUseMetadataOnEncoderOutput) {

// Modified check
++if (isLocalComponent && ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure))
++      || mUseMetadataOnEncoderOutput)) {

P.S: 我建议您向 Google 咨询此解决方案。

背景:

ExoPlayer 创建视频解码器作为 MediaCodec 组件。当一个新的MediaCodec组件被创建时,这个过程中的corresponding object in JNI is created. Please note that there is no interaction with MediaPlayerService.

MediaCodec 在内部创建一个 ACodec,它与 OMX 核心交互,随后与 OMX 组件交互。

ACodec 是在与 MediaCodec 相同的上下文中创建的。调用 OMXClient::connect 时,会在 MediaPlayer 服务的上下文中创建 OMX 句柄。因此,OMX 组件和 ACodec 的进程 ID 将不同。

对于安全输入缓冲区,there is a special handling in ACodec::allocateBuffersOnPorts。在这里,从 allocateBuffer 返回的缓冲区指针被包装为 ABuffer 并排队等待使用。在我看来,当前的实现中存在如下潜在问题。

ACodec::allocateBufferOnPort 呼叫 mOMX->allocateBuffermOMX 属于 IOMX 类型,即涉及活页夹交互。请注意此变量 &buffer_data,它将在 ACodec::allocateBufferOnPorts 层中转换为 ptr,因为这对以下部分至关重要。

在实际运行在MediaPlayerService上下文中的OMXNodeInstance中,调用了传统的OMX_AllocateBuffer。在 OMXNodeInstance::allocateBuffer 中,分配后 *buffer_dataheader->pBuffer 初始化,这基本上是 OMX 组件可能通过简单的 malloc 调用分配的本地指针。

当控件returns时,相同的指针被写入binder接口here and subsequently read back here。所以,控件出来的时候mOMX->allocateBufferptr的值相当于OMX组件分配的header->pBuffer,但是两者是在2个不同的进程中。

因此,当 ACodec 基于此 ptr 创建 ABuffer 然后在 MediaCodec 中访问时,将在创建地址时发生访问冲突与 MediaCodec 的进程 ID 相比,在不同的进程上下文中。