Commit message: None
CVE: CVE-2017-0820
Vulnerability description: A vulnerability in the Android media framework (n/a). Product: Android. Versions: 7.0, 7.1.1, 7.1.2, 8.0. Android ID: A-62187433.

Project: Android

status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
    ALOGV("entering parseChunk %lld/%d", (long long)*offset, depth);

 if (*offset < 0) {
        ALOGE("b/23540914");
 return ERROR_MALFORMED;
 }
 if (depth > 100) {
        ALOGE("b/27456299");
 return ERROR_MALFORMED;
 }
 uint32_t hdr[2];
 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
 return ERROR_IO;
 }
 uint64_t chunk_size = ntohl(hdr[0]);
 int32_t chunk_type = ntohl(hdr[1]);
 off64_t data_offset = *offset + 8;

 if (chunk_size == 1) {
 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
 return ERROR_IO;
 }
        chunk_size = ntoh64(chunk_size);
        data_offset += 8;

 if (chunk_size < 16) {
 return ERROR_MALFORMED;
 }
 } else if (chunk_size == 0) {
 if (depth == 0) {
 off64_t sourceSize;
 if (mDataSource->getSize(&sourceSize) == OK) {
                chunk_size = (sourceSize - *offset);
 } else {
                ALOGE("atom size is 0, and data source has no size");
 return ERROR_MALFORMED;
 }
 } else {
 *offset += 4;
 return OK;
 }
 } else if (chunk_size < 8) {
        ALOGE("invalid chunk size: %" PRIu64, chunk_size);
 return ERROR_MALFORMED;
 }

 char chunk[5];
 MakeFourCCString(chunk_type, chunk);
    ALOGV("chunk: %s @ %lld, %d", chunk, (long long)*offset, depth);

 if (kUseHexDump) {
 static const char kWhitespace[] = "                                        ";
 const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
        printf("%sfound chunk '%s' of size %" PRIu64 "\n", indent, chunk, chunk_size);

 char buffer[256];
 size_t n = chunk_size;
 if (n > sizeof(buffer)) {
            n = sizeof(buffer);
 }
 if (mDataSource->readAt(*offset, buffer, n)
 < (ssize_t)n) {
 return ERROR_IO;
 }

        hexdump(buffer, n);
 }

 PathAdder autoAdder(&mPath, chunk_type);

 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
 if (chunk_data_size < 0) {
        ALOGE("b/23540914");
 return ERROR_MALFORMED;
 }
 if (chunk_type != FOURCC('m', 'd', 'a', 't') && chunk_data_size > kMaxAtomSize) {
 char errMsg[100];
        sprintf(errMsg, "%s atom has size %" PRId64, chunk, chunk_data_size);
        ALOGE("%s (b/28615448)", errMsg);
        android_errorWriteWithInfoLog(0x534e4554, "28615448", -1, errMsg, strlen(errMsg));
 return ERROR_MALFORMED;
 }

 if (chunk_type != FOURCC('c', 'p', 'r', 't')
 && chunk_type != FOURCC('c', 'o', 'v', 'r')
 && mPath.size() == 5 && underMetaDataPath(mPath)) {
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }

 return OK;
 }

 switch(chunk_type) {
 case FOURCC('m', 'o', 'o', 'v'):
 case FOURCC('t', 'r', 'a', 'k'):
 case FOURCC('m', 'd', 'i', 'a'):
 case FOURCC('m', 'i', 'n', 'f'):
 case FOURCC('d', 'i', 'n', 'f'):
 case FOURCC('s', 't', 'b', 'l'):
 case FOURCC('m', 'v', 'e', 'x'):
 case FOURCC('m', 'o', 'o', 'f'):
 case FOURCC('t', 'r', 'a', 'f'):
 case FOURCC('m', 'f', 'r', 'a'):
 case FOURCC('u', 'd', 't', 'a'):
 case FOURCC('i', 'l', 's', 't'):
 case FOURCC('s', 'i', 'n', 'f'):
 case FOURCC('s', 'c', 'h', 'i'):
 case FOURCC('e', 'd', 't', 's'):
 case FOURCC('w', 'a', 'v', 'e'):
 {
 if (chunk_type == FOURCC('m', 'o', 'o', 'v') && depth != 0) {

                 ALOGE("moov: depth %d", depth);
                 return ERROR_MALFORMED;
             }

            if (chunk_type == FOURCC('m', 'o', 'o', 'v') && mInitCheck == OK) {
                ALOGE("duplicate moov");
                return ERROR_MALFORMED;
            }

             if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) {
                 mMoofFound = true;
                mMoofOffset = *offset;
 }

 if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
                ALOGV("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size);

 if (mDataSource->flags()
 & (DataSource::kWantsPrefetching
 | DataSource::kIsCachingDataSource)) {
                    sp<MPEG4DataSource> cachedSource =
 new MPEG4DataSource(mDataSource);

 if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
                        mDataSource = cachedSource;
 }
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

                mLastTrack->sampleTable = new SampleTable(mDataSource);
 }

 bool isTrack = false;
 if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
 if (depth != 1) {
                    ALOGE("trak: depth %d", depth);
 return ERROR_MALFORMED;
 }
                isTrack = true;

 Track *track = new Track;
                track->next = NULL;
 if (mLastTrack) {
                    mLastTrack->next = track;
 } else {
                    mFirstTrack = track;
 }
                mLastTrack = track;

                track->meta = new MetaData;
                track->includes_expensive_metadata = false;
                track->skipTrack = false;
                track->timescale = 0;
                track->meta->setCString(kKeyMIMEType, "application/octet-stream");
 }

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 if (isTrack) {
                        mLastTrack->skipTrack = true;
 break;
 }
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }

 if (isTrack) {
 int32_t trackId;

                 if (!mLastTrack->meta->findInt32(kKeyTrackID, &trackId)) {
                     mLastTrack->skipTrack = true;
                 }

                status_t err = verifyTrack(mLastTrack);
                if (err != OK) {
                    mLastTrack->skipTrack = true;
                }

                 if (mLastTrack->skipTrack) {
                     Track *cur = mFirstTrack;
 
 if (cur == mLastTrack) {
 delete cur;
                        mFirstTrack = mLastTrack = NULL;
 } else {
 while (cur && cur->next != mLastTrack) {
                            cur = cur->next;
 }
 if (cur) {
                            cur->next = NULL;
 }
 delete mLastTrack;
                        mLastTrack = cur;
 }

 
                     return OK;
                 }
             } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mInitCheck = OK;
 
 if (!mIsDrm) {
 return UNKNOWN_ERROR; // Return a dummy error.
 } else {
 return OK;
 }
 }
 break;
 }

 case FOURCC('e', 'l', 's', 't'):
 {
 *offset += chunk_size;

 uint8_t version;
 if (mDataSource->readAt(data_offset, &version, 1) < 1) {
 return ERROR_IO;
 }

 uint32_t entry_count;
 if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) {
 return ERROR_IO;
 }

 if (entry_count != 1) {
                ALOGW("ignoring edit list with %d entries", entry_count);
 } else if (mHeaderTimescale == 0) {
                ALOGW("ignoring edit list because timescale is 0");
 } else {
 off64_t entriesoffset = data_offset + 8;
 uint64_t segment_duration;
 int64_t media_time;

 if (version == 1) {
 if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
 !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
 return ERROR_IO;
 }
 } else if (version == 0) {
 uint32_t sd;
 int32_t mt;
 if (!mDataSource->getUInt32(entriesoffset, &sd) ||
 !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) {
 return ERROR_IO;
 }
                    segment_duration = sd;
                    media_time = mt;
 } else {
 return ERROR_IO;
 }

 uint64_t halfscale = mHeaderTimescale / 2;
                segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale;
                media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale;

 int64_t duration;
 int32_t samplerate;
 if (!mLastTrack) {
 return ERROR_MALFORMED;
 }
 if (mLastTrack->meta->findInt64(kKeyDuration, &duration) &&
                        mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) {

 int64_t delay = (media_time  * samplerate + 500000) / 1000000;
                    mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);

 int64_t paddingus = duration - (int64_t)(segment_duration + media_time);
 if (paddingus < 0) {
                        paddingus = 0;
 }
 int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
                    mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
 }
 }
 break;
 }

 case FOURCC('f', 'r', 'm', 'a'):
 {
 *offset += chunk_size;

 uint32_t original_fourcc;
 if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
 return ERROR_IO;
 }
            original_fourcc = ntohl(original_fourcc);
            ALOGV("read original format: %d", original_fourcc);

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
 uint32_t num_channels = 0;
 uint32_t sample_rate = 0;
 if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
                mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
                mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
 }
 break;
 }

 case FOURCC('t', 'e', 'n', 'c'):
 {
 *offset += chunk_size;

 if (chunk_size < 32) {
 return ERROR_MALFORMED;
 }

 char buf[4];
            memset(buf, 0, 4);
 if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
 return ERROR_IO;
 }
 uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
 if (defaultAlgorithmId > 1) {
 return ERROR_MALFORMED;
 }

            memset(buf, 0, 4);
 if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
 return ERROR_IO;
 }
 uint32_t defaultIVSize = ntohl(*((int32_t*)buf));

 if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
 (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
 return ERROR_MALFORMED;
 } else if (defaultIVSize != 0 &&
                    defaultIVSize != 8 &&
                    defaultIVSize != 16) {
 return ERROR_MALFORMED;
 }

 uint8_t defaultKeyId[16];

 if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
 return ERROR_IO;
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
            mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
            mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
 break;
 }

 case FOURCC('t', 'k', 'h', 'd'):
 {
 *offset += chunk_size;

 status_t err;
 if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
 return err;
 }

 break;
 }

 case FOURCC('p', 's', 's', 'h'):
 {
 *offset += chunk_size;

 PsshInfo pssh;

 if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
 return ERROR_IO;
 }

 uint32_t psshdatalen = 0;
 if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) {
 return ERROR_IO;
 }
            pssh.datalen = ntohl(psshdatalen);
            ALOGV("pssh data size: %d", pssh.datalen);
 if (chunk_size < 20 || pssh.datalen > chunk_size - 20) {
 return ERROR_MALFORMED;
 }

            pssh.data = new (std::nothrow) uint8_t[pssh.datalen];
 if (pssh.data == NULL) {
 return ERROR_MALFORMED;
 }
            ALOGV("allocated pssh @ %p", pssh.data);
 ssize_t requested = (ssize_t) pssh.datalen;
 if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
 delete[] pssh.data;
 return ERROR_IO;
 }
            mPssh.push_back(pssh);

 break;
 }

 case FOURCC('m', 'd', 'h', 'd'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 4 || mLastTrack == NULL) {
 return ERROR_MALFORMED;
 }

 uint8_t version;
 if (mDataSource->readAt(
                        data_offset, &version, sizeof(version))
 < (ssize_t)sizeof(version)) {
 return ERROR_IO;
 }

 off64_t timescale_offset;

 if (version == 1) {
                timescale_offset = data_offset + 4 + 16;
 } else if (version == 0) {
                timescale_offset = data_offset + 4 + 8;
 } else {
 return ERROR_IO;
 }

 uint32_t timescale;
 if (mDataSource->readAt(
                        timescale_offset, &timescale, sizeof(timescale))
 < (ssize_t)sizeof(timescale)) {
 return ERROR_IO;
 }

 if (!timescale) {
                ALOGE("timescale should not be ZERO.");
 return ERROR_MALFORMED;
 }

            mLastTrack->timescale = ntohl(timescale);

 int64_t duration = 0;
 if (version == 1) {
 if (mDataSource->readAt(
                            timescale_offset + 4, &duration, sizeof(duration))
 < (ssize_t)sizeof(duration)) {
 return ERROR_IO;
 }
 if (duration != -1) {
                    duration = ntoh64(duration);
 }
 } else {
 uint32_t duration32;
 if (mDataSource->readAt(
                            timescale_offset + 4, &duration32, sizeof(duration32))
 < (ssize_t)sizeof(duration32)) {
 return ERROR_IO;
 }
 if (duration32 != 0xffffffff) {
                    duration = ntohl(duration32);
 }
 }
 if (duration != 0 && mLastTrack->timescale != 0) {
                mLastTrack->meta->setInt64(
                        kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
 }

 uint8_t lang[2];
 off64_t lang_offset;
 if (version == 1) {
                lang_offset = timescale_offset + 4 + 8;
 } else if (version == 0) {
                lang_offset = timescale_offset + 4 + 4;
 } else {
 return ERROR_IO;
 }

 if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
 < (ssize_t)sizeof(lang)) {
 return ERROR_IO;
 }

 char lang_code[4];
            lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
            lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
            lang_code[2] = (lang[1] & 0x1f) + 0x60;
            lang_code[3] = '\0';

            mLastTrack->meta->setCString(
                    kKeyMediaLanguage, lang_code);

 break;
 }

 case FOURCC('s', 't', 's', 'd'):
 {
 uint8_t buffer[8];
 if (chunk_data_size < (off64_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, 8) < 8) {
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {
 return ERROR_MALFORMED;
 }

 uint32_t entry_count = U32_AT(&buffer[4]);

 if (entry_count > 1) {
 const char *mime;

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
                        strcasecmp(mime, "application/octet-stream")) {
                    mLastTrack->skipTrack = true;
 *offset += chunk_size;
 break;
 }
 }
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + 8;
 for (uint32_t i = 0; i < entry_count; ++i) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }
 case FOURCC('m', 'e', 't', 't'):
 {
 *offset += chunk_size;

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            sp<ABuffer> buffer = new ABuffer(chunk_data_size);
 if (buffer->data() == NULL) {
 return NO_MEMORY;
 }

 if (mDataSource->readAt(
                        data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 String8 mimeFormat((const char *)(buffer->data()), chunk_data_size);
            mLastTrack->meta->setCString(kKeyMIMEType, mimeFormat.string());

 break;
 }

 case FOURCC('m', 'p', '4', 'a'):
 case FOURCC('e', 'n', 'c', 'a'):
 case FOURCC('s', 'a', 'm', 'r'):
 case FOURCC('s', 'a', 'w', 'b'):
 {
 if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')
 && depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) {
 *offset += chunk_size;
 break;
 }

 uint8_t buffer[8 + 20];
 if (chunk_data_size < (ssize_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
 return ERROR_IO;
 }

 uint16_t data_ref_index __unused = U16_AT(&buffer[6]);
 uint16_t version = U16_AT(&buffer[8]);
 uint32_t num_channels = U16_AT(&buffer[16]);

 uint16_t sample_size = U16_AT(&buffer[18]);
 uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + sizeof(buffer);

 if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')) {
 if (version == 1) {
 if (mDataSource->readAt(*offset, buffer, 16) < 16) {
 return ERROR_IO;
 }

#if 0
                    U32_AT(buffer); // samples per packet
                    U32_AT(&buffer[4]); // bytes per packet
                    U32_AT(&buffer[8]); // bytes per frame
                    U32_AT(&buffer[12]); // bytes per sample
#endif
 *offset += 16;
 } else if (version == 2) {
 uint8_t v2buffer[36];
 if (mDataSource->readAt(*offset, v2buffer, 36) < 36) {
 return ERROR_IO;
 }

#if 0
                    U32_AT(v2buffer); // size of struct only
                    sample_rate = (uint32_t)U64_AT(&v2buffer[4]); // audio sample rate
                    num_channels = U32_AT(&v2buffer[12]); // num audio channels
                    U32_AT(&v2buffer[16]); // always 0x7f000000
                    sample_size = (uint16_t)U32_AT(&v2buffer[20]); // const bits per channel
                    U32_AT(&v2buffer[24]); // format specifc flags
                    U32_AT(&v2buffer[28]); // const bytes per audio packet
                    U32_AT(&v2buffer[32]); // const LPCM frames per audio packet
#endif
 *offset += 36;
 }
 }

 if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
 AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
 }
            ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
                   chunk, num_channels, sample_size, sample_rate);
            mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
            mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);

 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('m', 'p', '4', 'v'):
 case FOURCC('e', 'n', 'c', 'v'):
 case FOURCC('s', '2', '6', '3'):
 case FOURCC('H', '2', '6', '3'):
 case FOURCC('h', '2', '6', '3'):
 case FOURCC('a', 'v', 'c', '1'):
 case FOURCC('h', 'v', 'c', '1'):
 case FOURCC('h', 'e', 'v', '1'):
 {
 uint8_t buffer[78];
 if (chunk_data_size < (ssize_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
 return ERROR_IO;
 }

 uint16_t data_ref_index __unused = U16_AT(&buffer[6]);
 uint16_t width = U16_AT(&buffer[6 + 18]);
 uint16_t height = U16_AT(&buffer[6 + 20]);

 if (width == 0)  width  = 352;
 if (height == 0) height = 288;


 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

 if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
 }
            mLastTrack->meta->setInt32(kKeyWidth, width);
            mLastTrack->meta->setInt32(kKeyHeight, height);

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + sizeof(buffer);
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('s', 't', 'c', 'o'):
 case FOURCC('c', 'o', '6', '4'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 status_t err =
                mLastTrack->sampleTable->setChunkOffsetParams(
                        chunk_type, data_offset, chunk_data_size);

 *offset += chunk_size;

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC('s', 't', 's', 'c'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 status_t err =
                mLastTrack->sampleTable->setSampleToChunkParams(
                        data_offset, chunk_data_size);

 *offset += chunk_size;

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC('s', 't', 's', 'z'):
 case FOURCC('s', 't', 'z', '2'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 status_t err =
                mLastTrack->sampleTable->setSampleSizeParams(
                        chunk_type, data_offset, chunk_data_size);

 *offset += chunk_size;

 if (err != OK) {
 return err;
 }

 size_t max_size;
            err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);

 if (err != OK) {
 return err;
 }

 if (max_size != 0) {
 if (max_size > SIZE_MAX - 10 * 2) {
                    ALOGE("max sample size too big: %zu", max_size);
 return ERROR_MALFORMED;
 }
                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
 } else {
 uint32_t width, height;
 if (!mLastTrack->meta->findInt32(kKeyWidth, (int32_t*)&width) ||
 !mLastTrack->meta->findInt32(kKeyHeight,(int32_t*) &height)) {
                    ALOGE("No width or height, assuming worst case 1080p");
                    width = 1920;
                    height = 1080;
 } else {
 if (width > 32768 || height > 32768) {
                        ALOGE("can't support %u x %u video", width, height);
 return ERROR_MALFORMED;
 }
 }

 const char *mime;
                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
 || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
                    max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192;
 } else {
                    max_size = width * height * 3 / 2;
 }
                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size);
 }

 const char *mime;
            CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (!strncasecmp("video/", mime, 6)) {
 size_t nSamples = mLastTrack->sampleTable->countSamples();
 if (nSamples == 0) {
 int32_t trackId;
 if (mLastTrack->meta->findInt32(kKeyTrackID, &trackId)) {
 for (size_t i = 0; i < mTrex.size(); i++) {
 Trex *t = &mTrex.editItemAt(i);
 if (t->track_ID == (uint32_t) trackId) {
 if (t->default_sample_duration > 0) {
 int32_t frameRate =
                                            mLastTrack->timescale / t->default_sample_duration;
                                    mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
 }
 break;
 }
 }
 }
 } else {
 int64_t durationUs;
 if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
 if (durationUs > 0) {
 int32_t frameRate = (nSamples * 1000000LL +
 (durationUs >> 1)) / durationUs;
                            mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
 }
 }
 }
 }

 break;
 }

 case FOURCC('s', 't', 't', 's'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 *offset += chunk_size;

 status_t err =
                mLastTrack->sampleTable->setTimeToSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC('c', 't', 't', 's'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 *offset += chunk_size;

 status_t err =
                mLastTrack->sampleTable->setCompositionTimeToSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC('s', 't', 's', 's'):
 {
 if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
 return ERROR_MALFORMED;

 *offset += chunk_size;

 status_t err =
                mLastTrack->sampleTable->setSyncSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC(0xA9, 'x', 'y', 'z'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 8) {
 return ERROR_MALFORMED;
 }

 char buffer[18 + 8];

 off64_t location_length = chunk_data_size - 5;
 if (location_length >= (off64_t) sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset + 4, buffer, location_length) < location_length) {
 return ERROR_IO;
 }

            buffer[location_length] = '\0';
            mFileMetaData->setCString(kKeyLocation, buffer);
 break;
 }

 case FOURCC('e', 's', 'd', 's'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 4) {
 return ERROR_MALFORMED;
 }

 uint8_t buffer[256];
 if (chunk_data_size > (off64_t)sizeof(buffer)) {
 return ERROR_BUFFER_TOO_SMALL;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {
 return ERROR_MALFORMED;
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setData(
                    kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);

 if (mPath.size() >= 2
 && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {

 status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio(
 &buffer[4], chunk_data_size - 4);

 if (err != OK) {
 return err;
 }
 }
 if (mPath.size() >= 2
 && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'v')) {
                ESDS esds(&buffer[4], chunk_data_size - 4);

 uint8_t objectTypeIndication;
 if (esds.getObjectTypeIndication(&objectTypeIndication) == OK) {
 if (objectTypeIndication >= 0x60 && objectTypeIndication <= 0x65) {
                        mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
 }
 }
 }
 break;
 }

 case FOURCC('b', 't', 'r', 't'):
 {
 *offset += chunk_size;
 if (mLastTrack == NULL) {
 return ERROR_MALFORMED;
 }

 uint8_t buffer[12];
 if (chunk_data_size != sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                    data_offset, buffer, chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 uint32_t maxBitrate = U32_AT(&buffer[4]);
 uint32_t avgBitrate = U32_AT(&buffer[8]);
 if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
                mLastTrack->meta->setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
 }
 if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
                mLastTrack->meta->setInt32(kKeyBitRate, (int32_t)avgBitrate);
 }
 break;
 }

 case FOURCC('a', 'v', 'c', 'C'):
 {
 *offset += chunk_size;

            sp<ABuffer> buffer = new ABuffer(chunk_data_size);

 if (buffer->data() == NULL) {
                ALOGE("b/28471206");
 return NO_MEMORY;
 }

 if (mDataSource->readAt(
                        data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setData(
                    kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);

 break;
 }
 case FOURCC('h', 'v', 'c', 'C'):
 {
            sp<ABuffer> buffer = new ABuffer(chunk_data_size);

 if (buffer->data() == NULL) {
                ALOGE("b/28471206");
 return NO_MEMORY;
 }

 if (mDataSource->readAt(
                        data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setData(
                    kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);

 *offset += chunk_size;
 break;
 }

 case FOURCC('d', '2', '6', '3'):
 {
 *offset += chunk_size;
 /*
             * d263 contains a fixed 7 bytes part:
             *   vendor - 4 bytes
             *   version - 1 byte
             *   level - 1 byte
             *   profile - 1 byte
             * optionally, "d263" box itself may contain a 16-byte
             * bit rate box (bitr)
             *   average bit rate - 4 bytes
             *   max bit rate - 4 bytes
             */
 char buffer[23];
 if (chunk_data_size != 7 &&
                chunk_data_size != 23) {
                ALOGE("Incorrect D263 box size %lld", (long long)chunk_data_size);
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                    data_offset, buffer, chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

            mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);

 break;
 }

 case FOURCC('m', 'e', 't', 'a'):
 {
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 bool isParsingMetaKeys = underQTMetaPath(mPath, 2);
 if (!isParsingMetaKeys) {
 uint8_t buffer[4];
 if (chunk_data_size < (off64_t)sizeof(buffer)) {
 *offset = stop_offset;
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                            data_offset, buffer, 4) < 4) {
 *offset = stop_offset;
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {

 *offset = stop_offset;
 return OK;
 }
 *offset += sizeof(buffer);
 }

 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('m', 'e', 'a', 'n'):
 case FOURCC('n', 'a', 'm', 'e'):
 case FOURCC('d', 'a', 't', 'a'):
 {
 *offset += chunk_size;

 if (mPath.size() == 6 && underMetaDataPath(mPath)) {
 status_t err = parseITunesMetaData(data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }
 }

 break;
 }

 case FOURCC('m', 'v', 'h', 'd'):
 {
 *offset += chunk_size;

 if (depth != 1) {
                ALOGE("mvhd: depth %d", depth);
 return ERROR_MALFORMED;
 }
 if (chunk_data_size < 32) {
 return ERROR_MALFORMED;
 }

 uint8_t header[32];
 if (mDataSource->readAt(
                        data_offset, header, sizeof(header))
 < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 uint64_t creationTime;
 uint64_t duration = 0;
 if (header[0] == 1) {
                creationTime = U64_AT(&header[4]);
                mHeaderTimescale = U32_AT(&header[20]);
                duration = U64_AT(&header[24]);
 if (duration == 0xffffffffffffffff) {
                    duration = 0;
 }
 } else if (header[0] != 0) {
 return ERROR_MALFORMED;
 } else {
                creationTime = U32_AT(&header[4]);
                mHeaderTimescale = U32_AT(&header[12]);
 uint32_t d32 = U32_AT(&header[16]);
 if (d32 == 0xffffffff) {
                    d32 = 0;
 }
                duration = d32;
 }
 if (duration != 0 && mHeaderTimescale != 0 && duration < UINT64_MAX / 1000000) {
                mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
 }

 String8 s;
 if (convertTimeToDate(creationTime, &s)) {
                mFileMetaData->setCString(kKeyDate, s.string());
 }


 break;
 }

 case FOURCC('m', 'e', 'h', 'd'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 8) {
 return ERROR_MALFORMED;
 }

 uint8_t flags[4];
 if (mDataSource->readAt(
                        data_offset, flags, sizeof(flags))
 < (ssize_t)sizeof(flags)) {
 return ERROR_IO;
 }

 uint64_t duration = 0;
 if (flags[0] == 1) {
 if (chunk_data_size < 12) {
 return ERROR_MALFORMED;
 }
                mDataSource->getUInt64(data_offset + 4, &duration);
 if (duration == 0xffffffffffffffff) {
                    duration = 0;
 }
 } else if (flags[0] == 0) {
 uint32_t d32;
                mDataSource->getUInt32(data_offset + 4, &d32);
 if (d32 == 0xffffffff) {
                    d32 = 0;
 }
                duration = d32;
 } else {
 return ERROR_MALFORMED;
 }

 if (duration != 0 && mHeaderTimescale != 0) {
                mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
 }

 break;
 }

 case FOURCC('m', 'd', 'a', 't'):
 {
            ALOGV("mdat chunk, drm: %d", mIsDrm);

            mMdatFound = true;

 if (!mIsDrm) {
 *offset += chunk_size;
 break;
 }

 if (chunk_size < 8) {
 return ERROR_MALFORMED;
 }

 return parseDrmSINF(offset, data_offset);
 }

 case FOURCC('h', 'd', 'l', 'r'):
 {
 *offset += chunk_size;

 if (underQTMetaPath(mPath, 3)) {
 break;
 }

 uint32_t buffer;
 if (mDataSource->readAt(
                        data_offset + 8, &buffer, 4) < 4) {
 return ERROR_IO;
 }

 uint32_t type = ntohl(buffer);
 if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
 if (mLastTrack != NULL) {
                    mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
 }
 }

 break;
 }

 case FOURCC('k', 'e', 'y', 's'):
 {
 *offset += chunk_size;

 if (underQTMetaPath(mPath, 3)) {
 status_t err = parseQTMetaKey(data_offset, chunk_data_size);
 if (err != OK) {
 return err;
 }
 }
 break;
 }

 case FOURCC('t', 'r', 'e', 'x'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 24) {
 return ERROR_IO;
 }
 Trex trex;
 if (!mDataSource->getUInt32(data_offset + 4, &trex.track_ID) ||
 !mDataSource->getUInt32(data_offset + 8, &trex.default_sample_description_index) ||
 !mDataSource->getUInt32(data_offset + 12, &trex.default_sample_duration) ||
 !mDataSource->getUInt32(data_offset + 16, &trex.default_sample_size) ||
 !mDataSource->getUInt32(data_offset + 20, &trex.default_sample_flags)) {
 return ERROR_IO;
 }
            mTrex.add(trex);
 break;
 }

 case FOURCC('t', 'x', '3', 'g'):
 {
 if (mLastTrack == NULL)
 return ERROR_MALFORMED;

 uint32_t type;
 const void *data;
 size_t size = 0;
 if (!mLastTrack->meta->findData(
                    kKeyTextFormatData, &type, &data, &size)) {
                size = 0;
 }

 if ((chunk_size > SIZE_MAX) || (SIZE_MAX - chunk_size <= size)) {
 return ERROR_MALFORMED;
 }

 uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
 if (buffer == NULL) {
 return ERROR_MALFORMED;
 }

 if (size > 0) {
                memcpy(buffer, data, size);
 }

 if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
 < chunk_size) {
 delete[] buffer;
                buffer = NULL;

 *offset += chunk_size;
 return ERROR_IO;
 }

            mLastTrack->meta->setData(
                    kKeyTextFormatData, 0, buffer, size + chunk_size);

 delete[] buffer;

 *offset += chunk_size;
 break;
 }

 case FOURCC('c', 'o', 'v', 'r'):
 {
 *offset += chunk_size;

 if (mFileMetaData != NULL) {
                ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64,
                      chunk_data_size, data_offset);

 if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
 return ERROR_MALFORMED;
 }
                sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
 if (buffer->data() == NULL) {
                    ALOGE("b/28471206");
 return NO_MEMORY;
 }
 if (mDataSource->readAt(
                    data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
 return ERROR_IO;
 }
 const int kSkipBytesOfDataBox = 16;
 if (chunk_data_size <= kSkipBytesOfDataBox) {
 return ERROR_MALFORMED;
 }

                mFileMetaData->setData(
                    kKeyAlbumArt, MetaData::TYPE_NONE,
                    buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
 }

 break;
 }

 case FOURCC('c', 'o', 'l', 'r'):
 {
 *offset += chunk_size;
 if (depth >= 2 && mPath[depth - 2] == FOURCC('s', 't', 's', 'd')) {
 status_t err = parseColorInfo(data_offset, chunk_data_size);
 if (err != OK) {
 return err;
 }
 }

 break;
 }

 case FOURCC('t', 'i', 't', 'l'):
 case FOURCC('p', 'e', 'r', 'f'):
 case FOURCC('a', 'u', 't', 'h'):
 case FOURCC('g', 'n', 'r', 'e'):
 case FOURCC('a', 'l', 'b', 'm'):
 case FOURCC('y', 'r', 'r', 'c'):
 {
 *offset += chunk_size;

 status_t err = parse3GPPMetaData(data_offset, chunk_data_size, depth);

 if (err != OK) {
 return err;
 }

 break;
 }

 case FOURCC('I', 'D', '3', '2'):
 {
 *offset += chunk_size;

 if (chunk_data_size < 6) {
 return ERROR_MALFORMED;
 }

            parseID3v2MetaData(data_offset + 6);

 break;
 }

 case FOURCC('-', '-', '-', '-'):
 {
            mLastCommentMean.clear();
            mLastCommentName.clear();
            mLastCommentData.clear();
 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 'i', 'd', 'x'):
 {
 status_t err = parseSegmentIndex(data_offset, chunk_data_size);
 if (err != OK) {
 return err;
 }
 *offset += chunk_size;
 return UNKNOWN_ERROR; // stop parsing after sidx
 }

 case FOURCC('a', 'c', '-', '3'):
 {
 *offset += chunk_size;
 return parseAC3SampleEntry(data_offset);
 }

 case FOURCC('f', 't', 'y', 'p'):
 {
 if (chunk_data_size < 8 || depth != 0) {
 return ERROR_MALFORMED;
 }

 off64_t stop_offset = *offset + chunk_size;
 uint32_t numCompatibleBrands = (chunk_data_size - 8) / 4;
 for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
 if (i == 1) {
 continue;
 }

 uint32_t brand;
 if (mDataSource->readAt(data_offset + 4 * i, &brand, 4) < 4) {
 return ERROR_MALFORMED;
 }

                brand = ntohl(brand);
 if (brand == FOURCC('q', 't', ' ', ' ')) {
                    mIsQT = true;
 break;
 }
 }

 *offset = stop_offset;

 break;
 }

 default:
 {
 if (underQTMetaPath(mPath, 3)) {
 status_t err = parseQTMetaVal(chunk_type, data_offset, chunk_data_size);
 if (err != OK) {
 return err;
 }
 }

 *offset += chunk_size;
 break;
 }
 }

 return OK;
}