diff options
21 files changed, 548 insertions, 219 deletions
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index c1483f3..a8d0fcb 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -300,6 +300,7 @@ private: OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); status_t setPriority(int32_t priority); + status_t setOperatingRate(float rateFloat, bool isVideo); status_t setMinBufferSize(OMX_U32 portIndex, size_t size); diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h index 0786fb9..3e3c276 100644 --- a/include/media/stagefright/MediaCodec.h +++ b/include/media/stagefright/MediaCodec.h @@ -135,6 +135,8 @@ struct MediaCodec : public AHandler { status_t getOutputFormat(sp<AMessage> *format) const; status_t getInputFormat(sp<AMessage> *format) const; + status_t getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const; + status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const; status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 04ac699..3fff1e6 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -82,25 +82,69 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatCodecNotify: { - if (!isStaleReply(msg)) { - int32_t numInput, numOutput; + if (mPaused) { + break; + } + + int32_t cbID; + CHECK(msg->findInt32("callbackID", &cbID)); + + ALOGV("kWhatCodecNotify: cbID = %d", cbID); + switch (cbID) { + case MediaCodec::CB_INPUT_AVAILABLE: + { + int32_t index; + CHECK(msg->findInt32("index", &index)); - if (!msg->findInt32("input-buffers", &numInput)) { - numInput = INT32_MAX; + handleAnInputBuffer(index); + break; + } + + case MediaCodec::CB_OUTPUT_AVAILABLE: + { + int32_t index; + size_t offset; + size_t size; + int64_t timeUs; + int32_t flags; + + CHECK(msg->findInt32("index", &index)); + CHECK(msg->findSize("offset", &offset)); + CHECK(msg->findSize("size", &size)); + CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("flags", &flags)); + + handleAnOutputBuffer(index, offset, size, timeUs, flags); + break; } - if (!msg->findInt32("output-buffers", &numOutput)) { - numOutput = INT32_MAX; + case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: + { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + + handleOutputFormatChange(format); + break; } - if (!mPaused) { - while (numInput-- > 0 && handleAnInputBuffer()) {} + case MediaCodec::CB_ERROR: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + ALOGE("Decoder (%s) reported error : 0x%x", + mIsAudio ? "audio" : "video", err); + + handleError(err); + break; } - while (numOutput-- > 0 && handleAnOutputBuffer()) {} + default: + { + TRESPASS(); + break; + } } - requestCodecNotification(); break; } @@ -188,6 +232,9 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); + sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); + mCodec->setCallback(reply); + err = mCodec->start(); if (err != OK) { ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); @@ -197,18 +244,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { return; } - // the following should work after start - CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers)); releaseAndResetMediaBuffers(); - CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers)); - ALOGV("[%s] got %zu input and %zu output buffers", - mComponentName.c_str(), - mInputBuffers.size(), - mOutputBuffers.size()); - if (mRenderer != NULL) { - requestCodecNotification(); - } mPaused = false; mResumePending = false; } @@ -217,16 +254,14 @@ void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) { bool hadNoRenderer = (mRenderer == NULL); mRenderer = renderer; if (hadNoRenderer && mRenderer != NULL) { - requestCodecNotification(); + // this means that the widevine legacy source is ready + onRequestInputBuffers(); } } void NuPlayer::Decoder::onGetInputBuffers( Vector<sp<ABuffer> > *dstBuffers) { - dstBuffers->clear(); - for (size_t i = 0; i < mInputBuffers.size(); i++) { - dstBuffers->push(mInputBuffers[i]); - } + CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers)); } void NuPlayer::Decoder::onResume(bool notifyComplete) { @@ -235,6 +270,7 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) { if (notifyComplete) { mResumePending = true; } + mCodec->start(); } void NuPlayer::Decoder::doFlush(bool notifyComplete) { @@ -261,8 +297,10 @@ void NuPlayer::Decoder::doFlush(bool notifyComplete) { // we attempt to release the buffers even if flush fails. } releaseAndResetMediaBuffers(); + mPaused = true; } + void NuPlayer::Decoder::onFlush() { doFlush(true); @@ -276,7 +314,6 @@ void NuPlayer::Decoder::onFlush() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); - mPaused = true; } void NuPlayer::Decoder::onShutdown(bool notifyComplete) { @@ -320,7 +357,9 @@ void NuPlayer::Decoder::onShutdown(bool notifyComplete) { } void NuPlayer::Decoder::doRequestBuffers() { - if (isDiscontinuityPending()) { + // mRenderer is only NULL if we have a legacy widevine source that + // is not yet ready. In this case we must not fetch input. + if (isDiscontinuityPending() || mRenderer == NULL) { return; } status_t err = OK; @@ -347,34 +386,50 @@ void NuPlayer::Decoder::doRequestBuffers() { } } -bool NuPlayer::Decoder::handleAnInputBuffer() { +void NuPlayer::Decoder::handleError(int32_t err) +{ + // We cannot immediately release the codec due to buffers still outstanding + // in the renderer. We signal to the player the error so it can shutdown/release the + // decoder after flushing and increment the generation to discard unnecessary messages. + + ++mBufferGeneration; + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) { if (isDiscontinuityPending()) { return false; } - size_t bufferIx = -1; - status_t res = mCodec->dequeueInputBuffer(&bufferIx); - ALOGV("[%s] dequeued input: %d", - mComponentName.c_str(), res == OK ? (int)bufferIx : res); - if (res != OK) { - if (res != -EAGAIN) { - ALOGE("Failed to dequeue input buffer for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); + + sp<ABuffer> buffer; + mCodec->getInputBuffer(index, &buffer); + + if (index >= mInputBuffers.size()) { + for (size_t i = mInputBuffers.size(); i <= index; ++i) { + mInputBuffers.add(); + mMediaBuffers.add(); + mInputBufferIsDequeued.add(); + mMediaBuffers.editItemAt(i) = NULL; + mInputBufferIsDequeued.editItemAt(i) = false; } - return false; } + mInputBuffers.editItemAt(index) = buffer; - CHECK_LT(bufferIx, mInputBuffers.size()); + //CHECK_LT(bufferIx, mInputBuffers.size()); - if (mMediaBuffers[bufferIx] != NULL) { - mMediaBuffers[bufferIx]->release(); - mMediaBuffers.editItemAt(bufferIx) = NULL; + if (mMediaBuffers[index] != NULL) { + mMediaBuffers[index]->release(); + mMediaBuffers.editItemAt(index) = NULL; } - mInputBufferIsDequeued.editItemAt(bufferIx) = true; + mInputBufferIsDequeued.editItemAt(index) = true; if (!mCSDsToSubmit.isEmpty()) { sp<AMessage> msg = new AMessage(); - msg->setSize("buffer-ix", bufferIx); + msg->setSize("buffer-ix", index); sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0); ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); @@ -392,94 +447,38 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { mPendingInputMessages.erase(mPendingInputMessages.begin()); } - if (!mInputBufferIsDequeued.editItemAt(bufferIx)) { + if (!mInputBufferIsDequeued.editItemAt(index)) { return true; } - mDequeuedInputBuffers.push_back(bufferIx); + mDequeuedInputBuffers.push_back(index); onRequestInputBuffers(); return true; } -bool NuPlayer::Decoder::handleAnOutputBuffer() { - size_t bufferIx = -1; - size_t offset; - size_t size; - int64_t timeUs; - uint32_t flags; - status_t res = mCodec->dequeueOutputBuffer( - &bufferIx, &offset, &size, &timeUs, &flags); - - if (res != OK) { - ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res); - } else { - ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")", - mComponentName.c_str(), (int)bufferIx, timeUs, flags); +bool NuPlayer::Decoder::handleAnOutputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t timeUs, + int32_t flags) { + if (mFormatChangePending) { + return false; } - if (res == INFO_OUTPUT_BUFFERS_CHANGED) { - res = mCodec->getOutputBuffers(&mOutputBuffers); - if (res != OK) { - ALOGE("Failed to get output buffers for %s after INFO event (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - // NuPlayer ignores this - return true; - } else if (res == INFO_FORMAT_CHANGED) { - sp<AMessage> format = new AMessage(); - res = mCodec->getOutputFormat(&format); - if (res != OK) { - ALOGE("Failed to get output format for %s after INFO event (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - - if (!mIsAudio) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatVideoSizeChanged); - notify->setMessage("format", format); - notify->post(); - } else if (mRenderer != NULL) { - uint32_t flags; - int64_t durationUs; - bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); - if (!hasVideo && - mSource->getDuration(&durationUs) == OK && - durationUs - > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } else { - flags = AUDIO_OUTPUT_FLAG_NONE; - } +// CHECK_LT(bufferIx, mOutputBuffers.size()); + sp<ABuffer> buffer; + mCodec->getOutputBuffer(index, &buffer); - res = mRenderer->openAudioSink( - format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */); - if (res != OK) { - ALOGE("Failed to open AudioSink on format change for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); - return false; - } - } - return true; - } else if (res == INFO_DISCONTINUITY) { - // nothing to do - return true; - } else if (res != OK) { - if (res != -EAGAIN) { - ALOGE("Failed to dequeue output buffer for %s (err=%d)", - mComponentName.c_str(), res); - handleError(res); + if (index >= mOutputBuffers.size()) { + for (size_t i = mOutputBuffers.size(); i <= index; ++i) { + mOutputBuffers.add(); } - return false; } - CHECK_LT(bufferIx, mOutputBuffers.size()); - sp<ABuffer> buffer = mOutputBuffers[bufferIx]; + mOutputBuffers.editItemAt(index) = buffer; + buffer->setRange(offset, size); buffer->meta()->clear(); buffer->meta()->setInt64("timeUs", timeUs); @@ -488,7 +487,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() { // we do not expect CODECCONFIG or SYNCFRAME for decoder sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this); - reply->setSize("buffer-ix", bufferIx); + reply->setSize("buffer-ix", index); reply->setInt32("generation", mBufferGeneration); if (eos) { @@ -522,6 +521,29 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() { return true; } +void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) { + if (!mIsAudio) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatVideoSizeChanged); + notify->setMessage("format", format); + notify->post(); + } else if (mRenderer != NULL) { + uint32_t flags; + int64_t durationUs; + bool hasVideo = (mSource->getFormat(false /* audio */) != NULL); + if (!hasVideo && + mSource->getDuration(&durationUs) == OK && + durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } else { + flags = AUDIO_OUTPUT_FLAG_NONE; + } + + mRenderer->openAudioSink( + format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */); + } +} + void NuPlayer::Decoder::releaseAndResetMediaBuffers() { for (size_t i = 0; i < mMediaBuffers.size(); i++) { if (mMediaBuffers[i] != NULL) { @@ -825,7 +847,8 @@ void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) { mPaused = true; } else if (mTimeChangePending) { if (flushOnTimeChange) { - doFlush(false /*notifyComplete*/); + doFlush(false /* notifyComplete */); + signalResume(false /* notifyComplete */); } // restart fetching input diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 4aab2c6..0c0e90c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -87,8 +87,15 @@ private: bool mResumePending; AString mComponentName; - bool handleAnInputBuffer(); - bool handleAnOutputBuffer(); + void handleError(int32_t err); + bool handleAnInputBuffer(size_t index); + bool handleAnOutputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t timeUs, + int32_t flags); + void handleOutputFormatChange(const sp<AMessage> &format); void releaseAndResetMediaBuffers(); void requestCodecNotification(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index a2ec51c..827bdc1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -312,6 +312,9 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { int64_t delayUs = mAudioSink->msecsPerFrame() * numFramesPendingPlayout * 1000ll; + if (mPlaybackRate > 1.0f) { + delayUs /= mPlaybackRate; + } // Let's give it more data after about half that time // has elapsed. diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 4efc630..45f6339 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1685,6 +1685,16 @@ status_t ACodec::configureCodec( err = setPriority(priority); } + int32_t rateInt = -1; + float rateFloat = -1; + if (!msg->findFloat("operating-rate", &rateFloat)) { + msg->findInt32("operating-rate", &rateInt); + rateFloat = (float)rateInt; // 16MHz (FLINTMAX) is OK for upper bound. + } + if (rateFloat > 0) { + err = setOperatingRate(rateFloat, video); + } + mBaseOutputFormat = outputFormat; CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK); @@ -1711,6 +1721,34 @@ status_t ACodec::setPriority(int32_t priority) { return OK; } +status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) { + if (rateFloat < 0) { + return BAD_VALUE; + } + OMX_U32 rate; + if (isVideo) { + if (rateFloat > 65535) { + return BAD_VALUE; + } + rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f); + } else { + if (rateFloat > UINT_MAX) { + return BAD_VALUE; + } + rate = (OMX_U32)(rateFloat); + } + OMX_PARAM_U32TYPE config; + InitOMXParams(&config); + config.nU32 = rate; + status_t err = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate, + &config, sizeof(config)); + if (err != OK) { + ALOGI("codec does not support config operating rate (err %d)", err); + } + return OK; +} + status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 8186f63..5538cb0 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -49,16 +49,31 @@ namespace android { struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> { BatteryNotifier(); + virtual ~BatteryNotifier(); void noteStartVideo(); void noteStopVideo(); void noteStartAudio(); void noteStopAudio(); + void onBatteryStatServiceDied(); private: + struct DeathNotifier : public IBinder::DeathRecipient { + DeathNotifier() {} + virtual void binderDied(const wp<IBinder>& /*who*/) { + BatteryNotifier::getInstance().onBatteryStatServiceDied(); + } + }; + + Mutex mLock; int32_t mVideoRefCount; int32_t mAudioRefCount; sp<IBatteryStats> mBatteryStatService; + sp<DeathNotifier> mDeathNotifier; + + sp<IBatteryStats> getBatteryService_l(); + + DISALLOW_EVIL_CONSTRUCTORS(BatteryNotifier); }; ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier) @@ -66,54 +81,103 @@ ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier) MediaCodec::BatteryNotifier::BatteryNotifier() : mVideoRefCount(0), mAudioRefCount(0) { - // get battery service +} + +sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() { + if (mBatteryStatService != NULL) { + return mBatteryStatService; + } + // get battery service from service manager const sp<IServiceManager> sm(defaultServiceManager()); if (sm != NULL) { const String16 name("batterystats"); - mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name)); + mBatteryStatService = + interface_cast<IBatteryStats>(sm->getService(name)); if (mBatteryStatService == NULL) { ALOGE("batterystats service unavailable!"); + return NULL; + } + mDeathNotifier = new DeathNotifier(); + if (IInterface::asBinder(mBatteryStatService)-> + linkToDeath(mDeathNotifier) != OK) { + mBatteryStatService.clear(); + mDeathNotifier.clear(); + return NULL; + } + // notify start now if media already started + if (mVideoRefCount > 0) { + mBatteryStatService->noteStartVideo(AID_MEDIA); } + if (mAudioRefCount > 0) { + mBatteryStatService->noteStartAudio(AID_MEDIA); + } + } + return mBatteryStatService; +} + +MediaCodec::BatteryNotifier::~BatteryNotifier() { + if (mDeathNotifier != NULL) { + IInterface::asBinder(mBatteryStatService)-> + unlinkToDeath(mDeathNotifier); } } void MediaCodec::BatteryNotifier::noteStartVideo() { - if (mVideoRefCount == 0 && mBatteryStatService != NULL) { - mBatteryStatService->noteStartVideo(AID_MEDIA); + Mutex::Autolock _l(mLock); + sp<IBatteryStats> batteryService = getBatteryService_l(); + if (mVideoRefCount == 0 && batteryService != NULL) { + batteryService->noteStartVideo(AID_MEDIA); } mVideoRefCount++; } void MediaCodec::BatteryNotifier::noteStopVideo() { + Mutex::Autolock _l(mLock); if (mVideoRefCount == 0) { ALOGW("BatteryNotifier::noteStop(): video refcount is broken!"); return; } mVideoRefCount--; - if (mVideoRefCount == 0 && mBatteryStatService != NULL) { - mBatteryStatService->noteStopVideo(AID_MEDIA); + sp<IBatteryStats> batteryService = getBatteryService_l(); + if (mVideoRefCount == 0 && batteryService != NULL) { + batteryService->noteStopVideo(AID_MEDIA); } } void MediaCodec::BatteryNotifier::noteStartAudio() { - if (mAudioRefCount == 0 && mBatteryStatService != NULL) { - mBatteryStatService->noteStartAudio(AID_MEDIA); + Mutex::Autolock _l(mLock); + sp<IBatteryStats> batteryService = getBatteryService_l(); + if (mAudioRefCount == 0 && batteryService != NULL) { + batteryService->noteStartAudio(AID_MEDIA); } mAudioRefCount++; } void MediaCodec::BatteryNotifier::noteStopAudio() { + Mutex::Autolock _l(mLock); if (mAudioRefCount == 0) { ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!"); return; } mAudioRefCount--; - if (mAudioRefCount == 0 && mBatteryStatService != NULL) { - mBatteryStatService->noteStopAudio(AID_MEDIA); + sp<IBatteryStats> batteryService = getBatteryService_l(); + if (mAudioRefCount == 0 && batteryService != NULL) { + batteryService->noteStopAudio(AID_MEDIA); } } + +void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() { + Mutex::Autolock _l(mLock); + mBatteryStatService.clear(); + mDeathNotifier.clear(); + // Do not reset mVideoRefCount and mAudioRefCount here. The ref + // counting is independent of the battery service availability. + // We need this if battery service becomes available after media + // started. +} + // static sp<MediaCodec> MediaCodec::CreateByType( const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) { @@ -546,6 +610,16 @@ status_t MediaCodec::getName(AString *name) const { return OK; } +status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); + msg->setInt32("portIndex", kPortIndexInput); + msg->setPointer("buffers", buffers); + msg->setInt32("widevine", true); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { sp<AMessage> msg = new AMessage(kWhatGetBuffers, this); msg->setInt32("portIndex", kPortIndexInput); @@ -1602,8 +1676,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { { sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + // Unfortunately widevine legacy source requires knowing all of the + // codec input buffers, so we have to provide them even in async mode. + int32_t widevine = 0; + msg->findInt32("widevine", &widevine); - if (!isExecuting() || (mFlags & kFlagIsAsync)) { + if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) { PostReplyWithError(replyID, INVALID_OPERATION); break; } else if (mFlags & kFlagStickyError) { diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index b6fa810..6568d25 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -399,6 +399,9 @@ status_t MediaCodecSource::initEncoder() { ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); + mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector); + mEncoder->setCallback(mEncoderActivityNotify); + status_t err = mEncoder->configure( mOutputFormat, NULL /* nativeWindow */, @@ -422,9 +425,6 @@ status_t MediaCodecSource::initEncoder() { } } - mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector); - mEncoder->setCallback(mEncoderActivityNotify); - err = mEncoder->start(); if (err != OK) { diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 6e6a78a..a35909e 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -139,7 +139,7 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por uint32_t height = mImg->d_h; outInfo = *outQueue.begin(); outHeader = outInfo->mHeader; - CHECK_EQ(mImg->fmt, IMG_FMT_I420); + CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420); handlePortSettingsChange(portWillReset, width, height); if (*portWillReset) { return true; @@ -151,12 +151,12 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv; uint8_t *dst = outHeader->pBuffer; - const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y]; - const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U]; - const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V]; - size_t srcYStride = mImg->stride[PLANE_Y]; - size_t srcUStride = mImg->stride[PLANE_U]; - size_t srcVStride = mImg->stride[PLANE_V]; + const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y]; + const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U]; + const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V]; + size_t srcYStride = mImg->stride[VPX_PLANE_Y]; + size_t srcUStride = mImg->stride[VPX_PLANE_U]; + size_t srcVStride = mImg->stride[VPX_PLANE_V]; copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); mImg = NULL; diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 0e2e48c..ba47172 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -45,7 +45,8 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/mediaresourcemanager \ $(call include-path-for, audio-utils) \ frameworks/av/services/soundtrigger \ - frameworks/av/services/radio + frameworks/av/services/radio \ + external/sonic LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index f8446ac..c359be5 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -50,6 +50,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES := \ $(TOPDIR)frameworks/av/services/audiopolicy \ + $(TOPDIR)external/sonic \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) @@ -68,7 +69,8 @@ LOCAL_SHARED_LIBRARIES := \ libhardware_legacy \ libeffects \ libpowermanager \ - libserviceutility + libserviceutility \ + libsonic LOCAL_STATIC_LIBRARIES := \ libscheduling_policy \ diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp index e058e6c..dcae5e7 100644 --- a/services/audioflinger/BufferProviders.cpp +++ b/services/audioflinger/BufferProviders.cpp @@ -370,16 +370,22 @@ TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount, mPitch(pitch), mLocalBufferFrameCount(0), mLocalBufferData(NULL), - mRemaining(0) + mRemaining(0), + mSonicStream(sonicCreateStream(sampleRate, mChannelCount)) { ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)", this, channelCount, format, sampleRate, speed, pitch); mBuffer.frameCount = 0; + + LOG_ALWAYS_FATAL_IF(mSonicStream == NULL, + "TimestretchBufferProvider can't allocate Sonic stream"); + sonicSetSpeed(mSonicStream, speed); } TimestretchBufferProvider::~TimestretchBufferProvider() { ALOGV("~TimestretchBufferProvider(%p)", this); + sonicDestroyStream(mSonicStream); if (mBuffer.frameCount != 0) { mTrackBufferProvider->releaseBuffer(&mBuffer); } @@ -489,6 +495,9 @@ status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch) { mSpeed = speed; mPitch = pitch; + + sonicSetSpeed(mSonicStream, speed); + //TODO: pitch is ignored for now return OK; } @@ -506,17 +515,24 @@ void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames *srcFrames = targetSrc + 1; } - // Do the time stretch by memory copy without any local buffer. - if (*dstFrames <= *srcFrames) { - size_t copySize = mFrameSize * *dstFrames; - memcpy(dstBuffer, srcBuffer, copySize); - } else { - // cyclically repeat the source. - for (size_t count = 0; count < *dstFrames; count += *srcFrames) { - size_t remaining = min(*srcFrames, *dstFrames - count); - memcpy((uint8_t*)dstBuffer + mFrameSize * count, - srcBuffer, mFrameSize * *srcFrames); + switch (mFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) { + ALOGE("sonicWriteFloatToStream cannot realloc"); + *srcFrames = 0; // cannot consume all of srcBuffer } + *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames); + break; + case AUDIO_FORMAT_PCM_16_BIT: + if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) { + ALOGE("sonicWriteShortToStream cannot realloc"); + *srcFrames = 0; // cannot consume all of srcBuffer + } + *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames); + break; + default: + // could also be caught on construction + LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat); } } diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h index 2b6ea47..42030c0 100644 --- a/services/audioflinger/BufferProviders.h +++ b/services/audioflinger/BufferProviders.h @@ -23,6 +23,7 @@ #include <hardware/audio_effect.h> #include <media/AudioBufferProvider.h> #include <system/audio.h> +#include <sonic.h> namespace android { @@ -183,6 +184,7 @@ private: size_t mLocalBufferFrameCount; void *mLocalBufferData; size_t mRemaining; + sonicStream mSonicStream; }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index fae19a1..8246fef 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -50,13 +50,6 @@ bool captureHotwordAllowed() { return ok; } -bool captureFmTunerAllowed() { - static const String16 sCaptureFmTunerAllowed("android.permission.ACCESS_FM_RADIO"); - bool ok = checkCallingPermission(sCaptureFmTunerAllowed); - if (!ok) ALOGE("android.permission.ACCESS_FM_RADIO"); - return ok; -} - bool settingsAllowed() { if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS"); diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h index ce18a90..df6f6f4 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/services/audioflinger/ServiceUtilities.h @@ -23,7 +23,6 @@ extern pid_t getpid_cached; bool recordingAllowed(); bool captureAudioOutputAllowed(); bool captureHotwordAllowed(); -bool captureFmTunerAllowed(); bool settingsAllowed(); bool modifyAudioRoutingAllowed(); bool dumpAllowed(); diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk index 76997be..536eb93 100644 --- a/services/audioflinger/tests/Android.mk +++ b/services/audioflinger/tests/Android.mk @@ -44,7 +44,8 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) \ - frameworks/av/services/audioflinger + frameworks/av/services/audioflinger \ + external/sonic LOCAL_STATIC_LIBRARIES := \ libsndfile @@ -58,7 +59,8 @@ LOCAL_SHARED_LIBRARIES := \ libdl \ libcutils \ libutils \ - liblog + liblog \ + libsonic LOCAL_MODULE:= test-mixer diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index a763151..9510727 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -262,8 +262,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, return BAD_VALUE; } - if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || - ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { + if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { return BAD_VALUE; } sp<AudioPolicyEffects>audioPolicyEffects; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp index 372a9fa..e4ca5dc 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp @@ -255,8 +255,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, inputSource = AUDIO_SOURCE_MIC; } - if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || - ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { + if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { return BAD_VALUE; } diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index e9c96c6..414d563 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -122,7 +122,7 @@ static void torch_mode_status_change( // should be ok for now. static CameraService *gCameraService; -CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), +CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH), mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) { ALOGI("CameraService started (pid=%d)", getpid()); gCameraService = this; @@ -242,6 +242,8 @@ void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId, } if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { + logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus, + newStatus)); sp<BasicClient> clientToDisconnect; { // Don't do this in updateStatus to avoid deadlock over mServiceLock @@ -274,6 +276,10 @@ void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId, } } else { + if (oldStatus == ICameraServiceListener::Status::STATUS_NOT_PRESENT) { + logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus, + newStatus)); + } updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id); } @@ -765,8 +771,8 @@ status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/ } else { // We only trust our own process to forward client UIDs if (callingPid != getpid()) { - ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)", - callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)", + callingPid, clientUid); return PERMISSION_DENIED; } } @@ -796,10 +802,12 @@ status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/ return -EACCES; } - // Only allow clients who are being used by the current foreground device user. - if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) { - ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground " - "device user)", callingPid); + // Only allow clients who are being used by the current foreground device user, unless calling + // from our own process. + if (callingPid != getpid() && + (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) { + ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous " + "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId); return PERMISSION_DENIED; } @@ -858,7 +866,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) { status_t ret = NO_ERROR; - std::vector<sp<BasicClient>> evictedClients; + std::vector<DescriptorPtr> evictedClients; DescriptorPtr clientDescriptor; { if (effectiveApiLevel == API_1) { @@ -934,7 +942,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien mActiveClientManager.getIncompatibleClients(clientDescriptor); String8 msg = String8::format("%s : DENIED connect device %s client for package %s " - "(PID %d, priority %d)", curTime.string(), + "(PID %d, priority %d) due to eviction policy", curTime.string(), cameraId.string(), packageName.string(), clientPid, getCameraPriorityFromProcState(priorities[priorities.size() - 1])); @@ -946,6 +954,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien } // Log the client's attempt + Mutex::Autolock l(mLogLock); mEventLog.add(msg); return -EBUSY; @@ -965,14 +974,12 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien ALOGE("CameraService::connect evicting conflicting client for camera ID %s", i->getKey().string()); - evictedClients.push_back(clientSp); - - String8 curTime = getFormattedCurrentTime(); + evictedClients.push_back(i); // Log the clients evicted - mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" - PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for " - "package %s (PID %d, priority %" PRId32 ")", curTime.string(), + logEvent(String8::format("EVICT device %s client held by package %s (PID" + " %" PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for" + " package %s (PID %d, priority %" PRId32 ")", i->getKey().string(), String8{clientSp->getPackageName()}.string(), i->getOwnerId(), i->getPriority(), cameraId.string(), packageName.string(), clientPid, @@ -994,12 +1001,31 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien // Destroy evicted clients for (auto& i : evictedClients) { // Disconnect is blocking, and should only have returned when HAL has cleaned up - i->disconnect(); // Clients will remove themselves from the active client list here + i->getValue()->disconnect(); // Clients will remove themselves from the active client list } - evictedClients.clear(); IPCThreadState::self()->restoreCallingIdentity(token); + for (const auto& i : evictedClients) { + ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")", + __FUNCTION__, i->getKey().string(), i->getOwnerId()); + ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS); + if (ret == TIMED_OUT) { + ALOGE("%s: Timed out waiting for client for device %s to disconnect, " + "current clients:\n%s", __FUNCTION__, i->getKey().string(), + mActiveClientManager.toString().string()); + return -EBUSY; + } + if (ret != NO_ERROR) { + ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), " + "current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret), + ret, mActiveClientManager.toString().string()); + return ret; + } + } + + evictedClients.clear(); + // Once clients have been disconnected, relock mServiceLock.lock(); @@ -1027,6 +1053,8 @@ status_t CameraService::connect( clientPackageName, clientUid, API_1, false, false, /*out*/client); if(ret != NO_ERROR) { + logRejected(id, getCallingPid(), String8(clientPackageName), + String8::format("%s (%d)", strerror(-ret), ret)); return ret; } @@ -1042,6 +1070,7 @@ status_t CameraService::connectLegacy( /*out*/ sp<ICamera>& device) { + String8 id = String8::format("%d", cameraId); int apiVersion = mModule->getModuleApiVersion(); if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED && apiVersion < CAMERA_MODULE_API_VERSION_2_3) { @@ -1053,16 +1082,19 @@ status_t CameraService::connectLegacy( */ ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!", __FUNCTION__, apiVersion); + logRejected(id, getCallingPid(), String8(clientPackageName), + String8("HAL module version doesn't support legacy HAL connections")); return INVALID_OPERATION; } status_t ret = NO_ERROR; - String8 id = String8::format("%d", cameraId); sp<Client> client = nullptr; ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName, clientUid, API_1, true, false, /*out*/client); if(ret != NO_ERROR) { + logRejected(id, getCallingPid(), String8(clientPackageName), + String8::format("%s (%d)", strerror(-ret), ret)); return ret; } @@ -1086,6 +1118,8 @@ status_t CameraService::connectDevice( /*out*/client); if(ret != NO_ERROR) { + logRejected(id, getCallingPid(), String8(clientPackageName), + String8::format("%s (%d)", strerror(-ret), ret)); return ret; } @@ -1426,6 +1460,8 @@ void CameraService::doUserSwitch(int newUserId) { newUserId = DEFAULT_LAST_USER_ID; } + logUserSwitch(mLastUserId, newUserId); + mLastUserId = newUserId; // Current user has switched, evict all current clients. @@ -1444,12 +1480,12 @@ void CameraService::doUserSwitch(int newUserId) { ALOGE("Evicting conflicting client for camera ID %s due to user change", i->getKey().string()); + // Log the clients evicted - mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" + logEvent(String8::format("EVICT device %s client held by package %s (PID %" PRId32 ", priority %" PRId32 ")\n - Evicted due to user switch.", - curTime.string(), i->getKey().string(), - String8{clientSp->getPackageName()}.string(), i->getOwnerId(), - i->getPriority())); + i->getKey().string(), String8{clientSp->getPackageName()}.string(), + i->getOwnerId(), i->getPriority())); } @@ -1470,22 +1506,52 @@ void CameraService::doUserSwitch(int newUserId) { mServiceLock.lock(); } -void CameraService::logDisconnected(const String8& cameraId, int clientPid, - const String8& clientPackage) { - +void CameraService::logEvent(const char* event) { String8 curTime = getFormattedCurrentTime(); - // Log the clients evicted - mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)", - curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); + Mutex::Autolock l(mLogLock); + mEventLog.add(String8::format("%s : %s", curTime.string(), event)); } -void CameraService::logConnected(const String8& cameraId, int clientPid, - const String8& clientPackage) { +void CameraService::logDisconnected(const char* cameraId, int clientPid, + const char* clientPackage) { + // Log the clients evicted + logEvent(String8::format("DISCONNECT device %s client for package %s (PID %d)", cameraId, + clientPackage, clientPid)); +} - String8 curTime = getFormattedCurrentTime(); +void CameraService::logConnected(const char* cameraId, int clientPid, + const char* clientPackage) { // Log the clients evicted - mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)", - curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); + logEvent(String8::format("CONNECT device %s client for package %s (PID %d)", cameraId, + clientPackage, clientPid)); +} + +void CameraService::logRejected(const char* cameraId, int clientPid, + const char* clientPackage, const char* reason) { + // Log the client rejected + logEvent(String8::format("REJECT device %s client for package %s (PID %d), reason: (%s)", + cameraId, clientPackage, clientPid, reason)); +} + +void CameraService::logUserSwitch(int oldUserId, int newUserId) { + // Log the new and old users + logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId, + newUserId)); +} + +void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) { + // Log the device removal + logEvent(String8::format("REMOVE device %s, reason: (%s)", cameraId, reason)); +} + +void CameraService::logDeviceAdded(const char* cameraId, const char* reason) { + // Log the device removal + logEvent(String8::format("ADD device %s, reason: (%s)", cameraId, reason)); +} + +void CameraService::logClientDied(int clientPid, const char* reason) { + // Log the device removal + logEvent(String8::format("DIED client(s) with PID %d, reason: (%s)", clientPid, reason)); } status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, @@ -1911,7 +1977,7 @@ static bool tryLock(Mutex& mutex) } status_t CameraService::dump(int fd, const Vector<String16>& args) { - String8 result; + String8 result("Dump of the Camera Service:\n"); if (checkCallingPermission(String16("android.permission.DUMP")) == false) { result.appendFormat("Permission Denial: " "can't dump CameraService from pid=%d, uid=%d\n", @@ -1957,12 +2023,15 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { result = String8("Prior client events (most recent at top):\n"); - for (const auto& msg : mEventLog) { - result.appendFormat("%s\n", msg.string()); - } + { + Mutex::Autolock l(mLogLock); + for (const auto& msg : mEventLog) { + result.appendFormat("%s\n", msg.string()); + } - if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) { - result.append("...\n"); + if (mEventLog.size() == DEFAULT_EVENT_LOG_LENGTH) { + result.append("...\n"); + } } write(fd, result.string(), result.size()); @@ -2094,10 +2163,12 @@ void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) { /*virtual*/void CameraService::binderDied(const wp<IBinder> &who) { /** - * While tempting to promote the wp<IBinder> into a sp, - * it's actually not supported by the binder driver + * While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the + * binder driver */ + logClientDied(getCallingPid(), String8("Binder died unexpectedly")); + // check torch client handleTorchClientBinderDied(who); diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index ca1c504..91c7d59 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -90,8 +90,11 @@ public: // 3 second busy timeout when other clients are connecting static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000; + // 1 second busy timeout when other clients are disconnecting + static const nsecs_t DEFAULT_DISCONNECT_TIMEOUT_NS = 1000000000; + // Default number of messages to store in eviction log - static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50; + static const size_t DEFAULT_EVENT_LOG_LENGTH = 100; enum { // Default last user id @@ -492,6 +495,7 @@ private: // Circular buffer for storing event logging for dumps RingBuffer<String8> mEventLog; + Mutex mLogLock; // UID of last user. int mLastUserId; @@ -546,14 +550,45 @@ private: void doUserSwitch(int newUserId); /** - * Add a event log message that a client has been disconnected. + * Add an event log message. + */ + void logEvent(const char* event); + + /** + * Add an event log message that a client has been disconnected. + */ + void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage); + + /** + * Add an event log message that a client has been connected. + */ + void logConnected(const char* cameraId, int clientPid, const char* clientPackage); + + /** + * Add an event log message that a client's connect attempt has been rejected. */ - void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage); + void logRejected(const char* cameraId, int clientPid, const char* clientPackage, + const char* reason); /** - * Add a event log message that a client has been connected. + * Add an event log message that the current device user has been switched. */ - void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage); + void logUserSwitch(int oldUserId, int newUserId); + + /** + * Add an event log message that a device has been removed by the HAL + */ + void logDeviceRemoved(const char* cameraId, const char* reason); + + /** + * Add an event log message that a device has been added by the HAL + */ + void logDeviceAdded(const char* cameraId, const char* reason); + + /** + * Add an event log message that a client has unexpectedly died. + */ + void logClientDied(int clientPid, const char* reason); int mNumberOfCameras; @@ -714,9 +749,10 @@ status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String String8 clientName8(clientPackageName); int clientPid = getCallingPid(); - ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and " + ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and " "Camera API version %d", clientPid, clientName8.string(), cameraId.string(), - halVersion, static_cast<int>(effectiveApiLevel)); + (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(), + static_cast<int>(effectiveApiLevel)); sp<CLIENT> client = nullptr; { @@ -734,7 +770,15 @@ status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) { return ret; } - mLastUserId = multiuser_get_user_id(clientUid); + int userId = multiuser_get_user_id(clientUid); + + if (userId != mLastUserId && clientPid != getpid() ) { + // If no previous user ID had been set, set to the user of the caller. + logUserSwitch(mLastUserId, userId); + LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID, + "Invalid state: Should never update user ID here unless was default"); + mLastUserId = userId; + } // Check the shim parameters after acquiring lock, if they have already been updated and // we were doing a shim update, return immediately diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h index ad5486d..aa40a2d 100644 --- a/services/camera/libcameraservice/utils/ClientManager.h +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -17,7 +17,9 @@ #ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H #define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H +#include <utils/Condition.h> #include <utils/Mutex.h> +#include <utils/Timers.h> #include <algorithm> #include <utility> @@ -263,6 +265,16 @@ public: */ std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const; + /** + * Block until the given client is no longer in the active clients list, or the timeout + * occurred. + * + * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on + * failure. + */ + status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client, + nsecs_t timeout) const; + protected: ~ClientManager(); @@ -284,6 +296,7 @@ private: int64_t getCurrentCostLocked() const; mutable Mutex mLock; + mutable Condition mRemovedCondition; int32_t mMaxCost; // LRU ordered, most recent at end std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients; @@ -430,6 +443,7 @@ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VA }), mClients.end()); mClients.push_back(client); + mRemovedCondition.broadcast(); return evicted; } @@ -487,6 +501,7 @@ template<class KEY, class VALUE> void ClientManager<KEY, VALUE>::removeAll() { Mutex::Autolock lock(mLock); mClients.clear(); + mRemovedCondition.broadcast(); } template<class KEY, class VALUE> @@ -505,6 +520,39 @@ std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove( return false; }), mClients.end()); + mRemovedCondition.broadcast(); + return ret; +} + +template<class KEY, class VALUE> +status_t ClientManager<KEY, VALUE>::waitUntilRemoved( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client, + nsecs_t timeout) const { + status_t ret = NO_ERROR; + Mutex::Autolock lock(mLock); + + bool isRemoved = false; + + // Figure out what time in the future we should hit the timeout + nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout; + + while (!isRemoved) { + isRemoved = true; + for (const auto& i : mClients) { + if (i == client) { + isRemoved = false; + } + } + + if (!isRemoved) { + ret = mRemovedCondition.waitRelative(mLock, timeout); + if (ret != NO_ERROR) { + break; + } + timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC); + } + } + return ret; } @@ -520,6 +568,7 @@ void ClientManager<KEY, VALUE>::remove( } return false; }), mClients.end()); + mRemovedCondition.broadcast(); } template<class KEY, class VALUE> |