Commit message: None
CVE: CVE-2016-0804
CWE: CWE-119
Vulnerability description: The NuPlayer::GenericSource::notifyPreparedAndCleanup function in media/libmediaplayerservice/nuplayer/GenericSource.cpp in mediaserver in Android 5.x before 5.1.1 LMY49G and 6.x before 2016-02-01 improperly manages mDrmManagerClient objects, which allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 25070434.

Project: Android

NuPlayer::GenericSource::GenericSource(
 const sp<AMessage> &notify,
 bool uidValid,
 uid_t uid)
 : Source(notify),
      mFetchSubtitleDataGeneration(0),
      mFetchTimedTextDataGeneration(0),
      mDurationUs(0ll),
      mAudioIsVorbis(false),
      mIsWidevine(false),
      mUIDValid(uidValid),
      mUID(uid),
      mDrmManagerClient(NULL),
      mMetaDataSize(-1ll),
      mBitrate(-1ll),
      mPollBufferingGeneration(0),
      mPendingReadBufferTypes(0) {
    resetDataSource();
 DataSource::RegisterDefaultSniffers();
}

Project: Android

void NuPlayer::GenericSource::cancelPollBuffering() {
 ++mPollBufferingGeneration;
}

Project: Android

status_t NuPlayer::GenericSource::dequeueAccessUnit(
 bool audio, sp<ABuffer> *accessUnit) {
 Track *track = audio ? &mAudioTrack : &mVideoTrack;

 if (track->mSource == NULL) {
 return -EWOULDBLOCK;
 }

 if (mIsWidevine && !audio) {
        postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
 }

 status_t finalResult;
 if (!track->mPackets->hasBufferAvailable(&finalResult)) {
 return (finalResult == OK ? -EWOULDBLOCK : finalResult);
 }

 status_t result = track->mPackets->dequeueAccessUnit(accessUnit);

 if (!track->mPackets->hasBufferAvailable(&finalResult)) {
        postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO);
 }

 if (result != OK) {
 if (mSubtitleTrack.mSource != NULL) {
            mSubtitleTrack.mPackets->clear();
            mFetchSubtitleDataGeneration++;
 }
 if (mTimedTextTrack.mSource != NULL) {
            mTimedTextTrack.mPackets->clear();
            mFetchTimedTextDataGeneration++;
 }
 return result;
 }

 int64_t timeUs;
 status_t eosResult; // ignored
    CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs));

 if (mSubtitleTrack.mSource != NULL
 && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
        sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
        msg->setInt64("timeUs", timeUs);
        msg->setInt32("generation", mFetchSubtitleDataGeneration);
        msg->post();
 }

 if (mTimedTextTrack.mSource != NULL
 && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
        sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id());
        msg->setInt64("timeUs", timeUs);
        msg->setInt32("generation", mFetchTimedTextDataGeneration);
        msg->post();
 }

 return result;
}

Project: Android

status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
 if (mStopRead) {
 return INVALID_OPERATION;
 }
 if (mVideoTrack.mSource != NULL) {
 int64_t actualTimeUs;
        readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);

        seekTimeUs = actualTimeUs;
 }

 if (mAudioTrack.mSource != NULL) {
        readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs);
 }

    setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000);
 if (!mStarted) {
        setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
 }
 return OK;
}

Project: Android

status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) {
 if (trackIndex >= mSources.size()) {
 return BAD_INDEX;
 }

 if (!select) {
 Track* track = NULL;
 if (mSubtitleTrack.mSource != NULL && trackIndex == mSubtitleTrack.mIndex) {
            track = &mSubtitleTrack;
            mFetchSubtitleDataGeneration++;
 } else if (mTimedTextTrack.mSource != NULL && trackIndex == mTimedTextTrack.mIndex) {
            track = &mTimedTextTrack;
            mFetchTimedTextDataGeneration++;
 }
 if (track == NULL) {
 return INVALID_OPERATION;
 }
        track->mSource->stop();
        track->mSource = NULL;
        track->mPackets->clear();
 return OK;
 }

 const sp<MediaSource> source = mSources.itemAt(trackIndex);
    sp<MetaData> meta = source->getFormat();
 const char *mime;
    CHECK(meta->findCString(kKeyMIMEType, &mime));
 if (!strncasecmp(mime, "text/", 5)) {
 bool isSubtitle = strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP);
 Track *track = isSubtitle ? &mSubtitleTrack : &mTimedTextTrack;
 if (track->mSource != NULL && track->mIndex == trackIndex) {
 return OK;
 }
        track->mIndex = trackIndex;
 if (track->mSource != NULL) {
            track->mSource->stop();
 }
        track->mSource = mSources.itemAt(trackIndex);
        track->mSource->start();
 if (track->mPackets == NULL) {
            track->mPackets = new AnotherPacketSource(track->mSource->getFormat());
 } else {
            track->mPackets->clear();
            track->mPackets->setFormat(track->mSource->getFormat());

 }

 if (isSubtitle) {
            mFetchSubtitleDataGeneration++;
 } else {
            mFetchTimedTextDataGeneration++;
 }

 return OK;
 } else if (!strncasecmp(mime, "audio/", 6) || !strncasecmp(mime, "video/", 6)) {
 bool audio = !strncasecmp(mime, "audio/", 6);
 Track *track = audio ? &mAudioTrack : &mVideoTrack;
 if (track->mSource != NULL && track->mIndex == trackIndex) {
 return OK;
 }

        sp<AMessage> msg = new AMessage(kWhatChangeAVSource, id());
        msg->setInt32("trackIndex", trackIndex);
        msg->post();
 return OK;
 }

 return INVALID_OPERATION;
}

Project: Android

void NuPlayer::GenericSource::fetchTextData(
 uint32_t sendWhat,
        media_track_type type,
 int32_t curGen,
        sp<AnotherPacketSource> packets,
        sp<AMessage> msg) {
 int32_t msgGeneration;
    CHECK(msg->findInt32("generation", &msgGeneration));
 if (msgGeneration != curGen) {
 return;
 }

 int32_t avail;
 if (packets->hasBufferAvailable(&avail)) {
 return;
 }

 int64_t timeUs;
    CHECK(msg->findInt64("timeUs", &timeUs));

 int64_t subTimeUs;
    readBuffer(type, timeUs, &subTimeUs);

 int64_t delayUs = subTimeUs - timeUs;
 if (msg->what() == kWhatFetchSubtitleData) {
 const int64_t oneSecUs = 1000000ll;
        delayUs -= oneSecUs;
 }
    sp<AMessage> msg2 = new AMessage(sendWhat, id());
    msg2->setInt32("generation", msgGeneration);
    msg2->post(delayUs < 0 ? 0 : delayUs);
}

Project: Android

ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const {
    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
    msg->setInt32("type", type);

    sp<AMessage> response;
 int32_t index;
 status_t err = msg->postAndAwaitResponse(&response);
 if (err == OK && response != NULL) {
        CHECK(response->findInt32("index", &index));
 return index;
 } else {
 return -1;
 }
}

Project: Android

sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const {
 size_t trackCount = mSources.size();
 if (trackIndex >= trackCount) {
 return NULL;
 }

    sp<AMessage> format = new AMessage();
    sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();

 const char *mime;
    CHECK(meta->findCString(kKeyMIMEType, &mime));

 int32_t trackType;
 if (!strncasecmp(mime, "video/", 6)) {
        trackType = MEDIA_TRACK_TYPE_VIDEO;
 } else if (!strncasecmp(mime, "audio/", 6)) {
        trackType = MEDIA_TRACK_TYPE_AUDIO;
 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
        trackType = MEDIA_TRACK_TYPE_TIMEDTEXT;
 } else {
        trackType = MEDIA_TRACK_TYPE_UNKNOWN;
 }
    format->setInt32("type", trackType);

 const char *lang;
 if (!meta->findCString(kKeyMediaLanguage, &lang)) {
        lang = "und";
 }
    format->setString("language", lang);

 if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
        format->setString("mime", mime);

 int32_t isAutoselect = 1, isDefault = 0, isForced = 0;
        meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect);
        meta->findInt32(kKeyTrackIsDefault, &isDefault);
        meta->findInt32(kKeyTrackIsForced, &isForced);

        format->setInt32("auto", !!isAutoselect);
        format->setInt32("default", !!isDefault);
        format->setInt32("forced", !!isForced);
 }

 return format;
}

Project: Android

sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer(
 MediaBuffer* mb,
        media_track_type trackType,
 int64_t *actualTimeUs) {
 bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO;
 size_t outLength = mb->range_length();

 if (audio && mAudioIsVorbis) {
        outLength += sizeof(int32_t);
 }

    sp<ABuffer> ab;
 if (mIsWidevine && !audio) {
        ab = new ABuffer(NULL, mb->range_length());
        mb->add_ref();
        ab->setMediaBufferBase(mb);
 } else {
        ab = new ABuffer(outLength);
        memcpy(ab->data(),
 (const uint8_t *)mb->data() + mb->range_offset(),
               mb->range_length());
 }

 if (audio && mAudioIsVorbis) {
 int32_t numPageSamples;
 if (!mb->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
            numPageSamples = -1;
 }

 uint8_t* abEnd = ab->data() + mb->range_length();
        memcpy(abEnd, &numPageSamples, sizeof(numPageSamples));
 }

    sp<AMessage> meta = ab->meta();

 int64_t timeUs;
    CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs));
    meta->setInt64("timeUs", timeUs);

 if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
 const char *mime;
        CHECK(mTimedTextTrack.mSource != NULL
 && mTimedTextTrack.mSource->getFormat()->findCString(kKeyMIMEType, &mime));
        meta->setString("mime", mime);
 }

 int64_t durationUs;
 if (mb->meta_data()->findInt64(kKeyDuration, &durationUs)) {
        meta->setInt64("durationUs", durationUs);
 }

 if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
        meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
 }

 if (actualTimeUs) {
 *actualTimeUs = timeUs;
 }

    mb->release();
    mb = NULL;

 return ab;
}

Project: Android

void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
    sp<AMessage> msg = dupNotify();
    msg->setInt32("what", kWhatBufferingUpdate);
    msg->setInt32("percentage", percentage);
    msg->post();
}

Project: Android

void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const {
 int32_t audio;
    CHECK(msg->findInt32("audio", &audio));

    sp<AMessage> response = new AMessage;
    sp<MetaData> format = doGetFormatMeta(audio);
    response->setPointer("format", format.get());

 uint32_t replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));
    response->postReply(replyID);
}

Project: Android

void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const {
 int32_t tmpType;
    CHECK(msg->findInt32("type", &tmpType));
    media_track_type type = (media_track_type)tmpType;

    sp<AMessage> response = new AMessage;
 ssize_t index = doGetSelectedTrack(type);
    response->setInt32("index", index);

 uint32_t replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));
    response->postReply(replyID);
}

Project: Android

void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
 switch (msg->what()) {
 case kWhatPrepareAsync:
 {
          onPrepareAsync();
 break;
 }
 case kWhatFetchSubtitleData:
 {
          fetchTextData(kWhatSendSubtitleData, MEDIA_TRACK_TYPE_SUBTITLE,
                  mFetchSubtitleDataGeneration, mSubtitleTrack.mPackets, msg);
 break;
 }

 case kWhatFetchTimedTextData:
 {
          fetchTextData(kWhatSendTimedTextData, MEDIA_TRACK_TYPE_TIMEDTEXT,
                  mFetchTimedTextDataGeneration, mTimedTextTrack.mPackets, msg);
 break;
 }

 case kWhatSendSubtitleData:
 {
          sendTextData(kWhatSubtitleData, MEDIA_TRACK_TYPE_SUBTITLE,
                  mFetchSubtitleDataGeneration, mSubtitleTrack.mPackets, msg);
 break;
 }

 case kWhatSendTimedTextData:
 {
          sendTextData(kWhatTimedTextData, MEDIA_TRACK_TYPE_TIMEDTEXT,
                  mFetchTimedTextDataGeneration, mTimedTextTrack.mPackets, msg);
 break;
 }

 case kWhatChangeAVSource:
 {
 int32_t trackIndex;
          CHECK(msg->findInt32("trackIndex", &trackIndex));
 const sp<MediaSource> source = mSources.itemAt(trackIndex);

 Track* track;
 const char *mime;
          media_track_type trackType, counterpartType;
          sp<MetaData> meta = source->getFormat();
          meta->findCString(kKeyMIMEType, &mime);
 if (!strncasecmp(mime, "audio/", 6)) {
              track = &mAudioTrack;
              trackType = MEDIA_TRACK_TYPE_AUDIO;
              counterpartType = MEDIA_TRACK_TYPE_VIDEO;;
 } else {
              CHECK(!strncasecmp(mime, "video/", 6));
              track = &mVideoTrack;
              trackType = MEDIA_TRACK_TYPE_VIDEO;
              counterpartType = MEDIA_TRACK_TYPE_AUDIO;;
 }


 if (track->mSource != NULL) {
              track->mSource->stop();
 }
          track->mSource = source;
          track->mSource->start();
          track->mIndex = trackIndex;

 status_t avail;
 if (!track->mPackets->hasBufferAvailable(&avail)) {
              TRESPASS();
 break;
 }

 int64_t timeUs, actualTimeUs;
 const bool formatChange = true;
          sp<AMessage> latestMeta = track->mPackets->getLatestEnqueuedMeta();
          CHECK(latestMeta != NULL && latestMeta->findInt64("timeUs", &timeUs));
          readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
          readBuffer(counterpartType, -1, NULL, formatChange);
          ALOGV("timeUs %lld actualTimeUs %lld", timeUs, actualTimeUs);

 break;
 }
 case kWhatPollBuffering:
 {
 int32_t generation;
          CHECK(msg->findInt32("generation", &generation));
 if (generation == mPollBufferingGeneration) {
              onPollBuffering();
 }
 break;
 }

 case kWhatGetFormat:
 {
          onGetFormatMeta(msg);
 break;
 }

 case kWhatGetSelectedTrack:
 {
          onGetSelectedTrack(msg);
 break;
 }

 case kWhatSelectTrack:
 {
          onSelectTrack(msg);
 break;
 }

 case kWhatSeek:
 {
          onSeek(msg);
 break;
 }

 case kWhatReadBuffer:
 {
          onReadBuffer(msg);
 break;
 }

 case kWhatStopWidevine:
 {
          mStopRead = true;
 if (mVideoTrack.mSource != NULL) {
              mVideoTrack.mPackets->clear();
 }
          sp<AMessage> response = new AMessage;
 uint32_t replyID;
          CHECK(msg->senderAwaitsResponse(&replyID));
          response->postReply(replyID);
 break;
 }
 default:
 Source::onMessageReceived(msg);
 break;
 }
}

Project: Android

void NuPlayer::GenericSource::onPollBuffering() {
 status_t finalStatus = UNKNOWN_ERROR;
 int64_t cachedDurationUs = 0ll;

 if (mCachedSource != NULL) {
 size_t cachedDataRemaining =
                mCachedSource->approxDataRemaining(&finalStatus);

 if (finalStatus == OK) {
 off64_t size;
 int64_t bitrate = 0ll;
 if (mDurationUs > 0 && mCachedSource->getSize(&size) == OK) {
                bitrate = size * 8000000ll / mDurationUs;
 } else if (mBitrate > 0) {
                bitrate = mBitrate;
 }
 if (bitrate > 0) {
                cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate;
 }
 }
 } else if (mWVMExtractor != NULL) {
        cachedDurationUs
 = mWVMExtractor->getCachedDurationUs(&finalStatus);
 }

 if (finalStatus == ERROR_END_OF_STREAM) {
        notifyBufferingUpdate(100);
        cancelPollBuffering();
 return;
 } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) {
 int percentage = 100.0 * cachedDurationUs / mDurationUs;
 if (percentage > 100) {
            percentage = 100;
 }

        notifyBufferingUpdate(percentage);
 }

    schedulePollBuffering();
}

Project: Android

void NuPlayer::GenericSource::onPrepareAsync() {
 if (mDataSource == NULL) {
 if (!mUri.empty()) {
 const char* uri = mUri.c_str();
            mIsWidevine = !strncasecmp(uri, "widevine://", 11);

 if (!strncasecmp("http://", uri, 7)
 || !strncasecmp("https://", uri, 8)
 || mIsWidevine) {
                mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
 if (mHttpSource == NULL) {
                    ALOGE("Failed to create http source!");
                    notifyPreparedAndCleanup(UNKNOWN_ERROR);
 return;
 }
 }

            mDataSource = DataSource::CreateFromURI(
                   mHTTPService, uri, &mUriHeaders, &mContentType,
 static_cast<HTTPBase *>(mHttpSource.get()));
 } else {
            mIsWidevine = false;

            mDataSource = new FileSource(mFd, mOffset, mLength);
 }

 if (mDataSource == NULL) {
            ALOGE("Failed to create data source!");
            notifyPreparedAndCleanup(UNKNOWN_ERROR);
 return;
 }

 if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
            mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
 }

 if (mIsWidevine || mCachedSource != NULL) {
            schedulePollBuffering();
 }
 }

 status_t err = prefillCacheIfNecessary();
 if (err != OK) {
 if (err == -EAGAIN) {
 (new AMessage(kWhatPrepareAsync, id()))->post(200000);
 } else {
            ALOGE("Failed to prefill data cache!");
            notifyPreparedAndCleanup(UNKNOWN_ERROR);
 }
 return;
 }

    err = initFromDataSource();

 if (err != OK) {
        ALOGE("Failed to init from data source!");
        notifyPreparedAndCleanup(err);
 return;
 }

 if (mVideoTrack.mSource != NULL) {
        sp<MetaData> meta = doGetFormatMeta(false /* audio */);
        sp<AMessage> msg = new AMessage;
        err = convertMetaDataToMessage(meta, &msg);
 if(err != OK) {
            notifyPreparedAndCleanup(err);
 return;
 }
        notifyVideoSizeChanged(msg);
 }

    notifyFlagsChanged(
 (mIsWidevine ? FLAG_SECURE : 0)
 | FLAG_CAN_PAUSE
 | FLAG_CAN_SEEK_BACKWARD
 | FLAG_CAN_SEEK_FORWARD
 | FLAG_CAN_SEEK);

    notifyPrepared();
}

Project: Android

void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) {
 int32_t tmpType;
    CHECK(msg->findInt32("trackType", &tmpType));
    media_track_type trackType = (media_track_type)tmpType;
 {
 Mutex::Autolock _l(mReadBufferLock);
        mPendingReadBufferTypes &= ~(1 << trackType);
 }
    readBuffer(trackType);
}

Project: Android

void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) {
 int64_t seekTimeUs;
    CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));

    sp<AMessage> response = new AMessage;
 status_t err = doSeek(seekTimeUs);
    response->setInt32("err", err);

 uint32_t replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));
    response->postReply(replyID);
}

Project: Android

void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) {
 int32_t trackIndex, select;
    CHECK(msg->findInt32("trackIndex", &trackIndex));
    CHECK(msg->findInt32("select", &select));

    sp<AMessage> response = new AMessage;
 status_t err = doSelectTrack(trackIndex, select);
    response->setInt32("err", err);

 uint32_t replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));
    response->postReply(replyID);
}

Project: Android

void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
 Mutex::Autolock _l(mReadBufferLock);

 if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
        mPendingReadBufferTypes |= (1 << trackType);
        sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
        msg->setInt32("trackType", trackType);
        msg->post();
 }
}

Project: Android

status_t NuPlayer::GenericSource::prefillCacheIfNecessary() {
    CHECK(mDataSource != NULL);

 if (mCachedSource == NULL) {
 return OK;
 }

 if (!strncasecmp(mContentType.string(), "audio/", 6)) {
 return OK;
 }


 static const size_t kMinBytesForSniffing = 192 * 1024;
 static const size_t kDefaultMetaSize = 200000;

 status_t finalStatus;

 size_t cachedDataRemaining =
            mCachedSource->approxDataRemaining(&finalStatus);

 if (finalStatus != OK || (mMetaDataSize >= 0
 && (off64_t)cachedDataRemaining >= mMetaDataSize)) {
        ALOGV("stop caching, status %d, "
 "metaDataSize %lld, cachedDataRemaining %zu",
                finalStatus, mMetaDataSize, cachedDataRemaining);
 return OK;
 }

    ALOGV("now cached %zu bytes of data", cachedDataRemaining);

 if (mMetaDataSize < 0
 && cachedDataRemaining >= kMinBytesForSniffing) {
 String8 tmp;
 float confidence;
        sp<AMessage> meta;
 if (!mCachedSource->sniff(&tmp, &confidence, &meta)) {
 return UNKNOWN_ERROR;
 }

        mSniffedMIME = tmp.string();

 if (meta == NULL
 || !meta->findInt64("meta-data-size",
 reinterpret_cast<int64_t*>(&mMetaDataSize))) {
            mMetaDataSize = kDefaultMetaSize;
 }

 if (mMetaDataSize < 0ll) {
            ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize);
 return UNKNOWN_ERROR;
 }
 }

 return -EAGAIN;
}

Project: Android

void NuPlayer::GenericSource::prepareAsync() {
 if (mLooper == NULL) {
        mLooper = new ALooper;
        mLooper->setName("generic");
        mLooper->start();

        mLooper->registerHandler(this);
 }

    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id());
    msg->post();
}

Project: Android

void NuPlayer::GenericSource::readBuffer(
        media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
 if (mStopRead) {
 return;
 }
 Track *track;
 size_t maxBuffers = 1;
 switch (trackType) {
 case MEDIA_TRACK_TYPE_VIDEO:
            track = &mVideoTrack;
 if (mIsWidevine) {
                maxBuffers = 2;
 }
 break;
 case MEDIA_TRACK_TYPE_AUDIO:
            track = &mAudioTrack;
 if (mIsWidevine) {
                maxBuffers = 8;
 } else {
                maxBuffers = 64;
 }
 break;
 case MEDIA_TRACK_TYPE_SUBTITLE:
            track = &mSubtitleTrack;
 break;
 case MEDIA_TRACK_TYPE_TIMEDTEXT:
            track = &mTimedTextTrack;
 break;
 default:
            TRESPASS();
 }

 if (track->mSource == NULL) {
 return;
 }

 if (actualTimeUs) {
 *actualTimeUs = seekTimeUs;
 }

 MediaSource::ReadOptions options;

 bool seeking = false;

 if (seekTimeUs >= 0) {
        options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
        seeking = true;
 }

 if (mIsWidevine && trackType != MEDIA_TRACK_TYPE_AUDIO) {
        options.setNonBlocking();
 }

 for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
 MediaBuffer *mbuf;
 status_t err = track->mSource->read(&mbuf, &options);

        options.clearSeekTo();

 if (err == OK) {
 int64_t timeUs;
            CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
 if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
                mAudioTimeUs = timeUs;
 } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
                mVideoTimeUs = timeUs;
 }

 if ((seeking || formatChange)
 && (trackType == MEDIA_TRACK_TYPE_AUDIO
 || trackType == MEDIA_TRACK_TYPE_VIDEO)) {
 ATSParser::DiscontinuityType type = formatChange
 ? (seeking
 ? ATSParser::DISCONTINUITY_FORMATCHANGE
 : ATSParser::DISCONTINUITY_NONE)
 : ATSParser::DISCONTINUITY_SEEK;
                track->mPackets->queueDiscontinuity( type, NULL, true /* discard */);
 }

            sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs);
            track->mPackets->queueAccessUnit(buffer);
            formatChange = false;
            seeking = false;
 ++numBuffers;
 } else if (err == WOULD_BLOCK) {
 break;
 } else if (err == INFO_FORMAT_CHANGED) {
#if 0
            track->mPackets->queueDiscontinuity(
 ATSParser::DISCONTINUITY_FORMATCHANGE,
                    NULL,
 false /* discard */);
#endif
 } else {
            track->mPackets->signalEOS(err);
 break;
 }
 }
}

Project: Android

void NuPlayer::GenericSource::resetDataSource() {
    mAudioTimeUs = 0;
    mVideoTimeUs = 0;
    mHTTPService.clear();
    mHttpSource.clear();
    mUri.clear();
    mUriHeaders.clear();
    mFd = -1;
    mOffset = 0;
    mLength = 0;
    setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
    mDecryptHandle = NULL;
    mDrmManagerClient = NULL;
    mStarted = false;
    mStopRead = true;
}

Project: Android

void NuPlayer::GenericSource::resume() {
    setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
    mStarted = true;
}

Project: Android

void NuPlayer::GenericSource::schedulePollBuffering() {
    sp<AMessage> msg = new AMessage(kWhatPollBuffering, id());
    msg->setInt32("generation", mPollBufferingGeneration);
    msg->post(1000000ll);
}

Project: Android

status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
    sp<AMessage> msg = new AMessage(kWhatSeek, id());
    msg->setInt64("seekTimeUs", seekTimeUs);

    sp<AMessage> response;
 status_t err = msg->postAndAwaitResponse(&response);
 if (err == OK && response != NULL) {
        CHECK(response->findInt32("err", &err));
 }

 return err;
}

Project: Android

status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) {
    ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
    msg->setInt32("trackIndex", trackIndex);
    msg->setInt32("select", select);

    sp<AMessage> response;
 status_t err = msg->postAndAwaitResponse(&response);
 if (err == OK && response != NULL) {
        CHECK(response->findInt32("err", &err));
 }

 return err;
}

Project: Android

status_t NuPlayer::GenericSource::setBuffers(
 bool audio, Vector<MediaBuffer *> &buffers) {
 if (mIsWidevine && !audio) {
 return mVideoTrack.mSource->setBuffers(buffers);
 }
 return INVALID_OPERATION;
}

Project: Android

void NuPlayer::GenericSource::start() {
    ALOGI("start");

    mStopRead = false;
 if (mAudioTrack.mSource != NULL) {
        CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);

        postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
 }

 if (mVideoTrack.mSource != NULL) {
        CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK);

        postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
 }

    setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
    mStarted = true;
}

Project: Android

void NuPlayer::GenericSource::stop() {
    setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
    mStarted = false;
 if (mIsWidevine) {
        sp<AMessage> msg = new AMessage(kWhatStopWidevine, id());
        sp<AMessage> response;
 (void) msg->postAndAwaitResponse(&response);
 }
}

Project: Android

NuPlayer::GenericSource::~GenericSource() {
 if (mLooper != NULL) {
        mLooper->unregisterHandler(id());
        mLooper->stop();
 }
}