Home | History | Annotate | Download | only in id3
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "ID3"
     19 #include <utils/Log.h>
     20 
     21 #include "../include/ID3.h"
     22 
     23 #include <media/stagefright/foundation/ADebug.h>
     24 #include <media/stagefright/DataSource.h>
     25 #include <media/stagefright/Utils.h>
     26 #include <utils/String8.h>
     27 #include <byteswap.h>
     28 
     29 namespace android {
     30 
     31 static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
     32 
     33 struct MemorySource : public DataSource {
     34     MemorySource(const uint8_t *data, size_t size)
     35         : mData(data),
     36           mSize(size) {
     37     }
     38 
     39     virtual status_t initCheck() const {
     40         return OK;
     41     }
     42 
     43     virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
     44         off64_t available = (offset >= (off64_t)mSize) ? 0ll : mSize - offset;
     45 
     46         size_t copy = (available > (off64_t)size) ? size : available;
     47         memcpy(data, mData + offset, copy);
     48 
     49         return copy;
     50     }
     51 
     52 private:
     53     const uint8_t *mData;
     54     size_t mSize;
     55 
     56     DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
     57 };
     58 
     59 ID3::ID3(const sp<DataSource> &source, bool ignoreV1, off64_t offset)
     60     : mIsValid(false),
     61       mData(NULL),
     62       mSize(0),
     63       mFirstFrameOffset(0),
     64       mVersion(ID3_UNKNOWN),
     65       mRawSize(0) {
     66     mIsValid = parseV2(source, offset);
     67 
     68     if (!mIsValid && !ignoreV1) {
     69         mIsValid = parseV1(source);
     70     }
     71 }
     72 
     73 ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
     74     : mIsValid(false),
     75       mData(NULL),
     76       mSize(0),
     77       mFirstFrameOffset(0),
     78       mVersion(ID3_UNKNOWN),
     79       mRawSize(0) {
     80     sp<MemorySource> source = new MemorySource(data, size);
     81 
     82     mIsValid = parseV2(source, 0);
     83 
     84     if (!mIsValid && !ignoreV1) {
     85         mIsValid = parseV1(source);
     86     }
     87 }
     88 
     89 ID3::~ID3() {
     90     if (mData) {
     91         free(mData);
     92         mData = NULL;
     93     }
     94 }
     95 
     96 bool ID3::isValid() const {
     97     return mIsValid;
     98 }
     99 
    100 ID3::Version ID3::version() const {
    101     return mVersion;
    102 }
    103 
    104 // static
    105 bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) {
    106     *x = 0;
    107     for (int32_t i = 0; i < 4; ++i) {
    108         if (encoded[i] & 0x80) {
    109             return false;
    110         }
    111 
    112         *x = ((*x) << 7) | encoded[i];
    113     }
    114 
    115     return true;
    116 }
    117 
    118 bool ID3::parseV2(const sp<DataSource> &source, off64_t offset) {
    119 struct id3_header {
    120     char id[3];
    121     uint8_t version_major;
    122     uint8_t version_minor;
    123     uint8_t flags;
    124     uint8_t enc_size[4];
    125     };
    126 
    127     id3_header header;
    128     if (source->readAt(
    129                 offset, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
    130         return false;
    131     }
    132 
    133     if (memcmp(header.id, "ID3", 3)) {
    134         return false;
    135     }
    136 
    137     if (header.version_major == 0xff || header.version_minor == 0xff) {
    138         return false;
    139     }
    140 
    141     if (header.version_major == 2) {
    142         if (header.flags & 0x3f) {
    143             // We only support the 2 high bits, if any of the lower bits are
    144             // set, we cannot guarantee to understand the tag format.
    145             return false;
    146         }
    147 
    148         if (header.flags & 0x40) {
    149             // No compression scheme has been decided yet, ignore the
    150             // tag if compression is indicated.
    151 
    152             return false;
    153         }
    154     } else if (header.version_major == 3) {
    155         if (header.flags & 0x1f) {
    156             // We only support the 3 high bits, if any of the lower bits are
    157             // set, we cannot guarantee to understand the tag format.
    158             return false;
    159         }
    160     } else if (header.version_major == 4) {
    161         if (header.flags & 0x0f) {
    162             // The lower 4 bits are undefined in this spec.
    163             return false;
    164         }
    165     } else {
    166         return false;
    167     }
    168 
    169     size_t size;
    170     if (!ParseSyncsafeInteger(header.enc_size, &size)) {
    171         return false;
    172     }
    173 
    174     if (size > kMaxMetadataSize) {
    175         ALOGE("skipping huge ID3 metadata of size %zu", size);
    176         return false;
    177     }
    178 
    179     mData = (uint8_t *)malloc(size);
    180 
    181     if (mData == NULL) {
    182         return false;
    183     }
    184 
    185     mSize = size;
    186     mRawSize = mSize + sizeof(header);
    187 
    188     if (source->readAt(offset + sizeof(header), mData, mSize) != (ssize_t)mSize) {
    189         free(mData);
    190         mData = NULL;
    191 
    192         return false;
    193     }
    194 
    195     if (header.version_major == 4) {
    196         void *copy = malloc(size);
    197         memcpy(copy, mData, size);
    198 
    199         bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
    200         if (!success) {
    201             memcpy(mData, copy, size);
    202             mSize = size;
    203 
    204             success = removeUnsynchronizationV2_4(true /* iTunesHack */);
    205 
    206             if (success) {
    207                 ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
    208             }
    209         }
    210 
    211         free(copy);
    212         copy = NULL;
    213 
    214         if (!success) {
    215             free(mData);
    216             mData = NULL;
    217 
    218             return false;
    219         }
    220     } else if (header.flags & 0x80) {
    221         ALOGV("removing unsynchronization");
    222 
    223         removeUnsynchronization();
    224     }
    225 
    226     mFirstFrameOffset = 0;
    227     if (header.version_major == 3 && (header.flags & 0x40)) {
    228         // Version 2.3 has an optional extended header.
    229 
    230         if (mSize < 4) {
    231             free(mData);
    232             mData = NULL;
    233 
    234             return false;
    235         }
    236 
    237         size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
    238 
    239         if (extendedHeaderSize > mSize) {
    240             free(mData);
    241             mData = NULL;
    242 
    243             return false;
    244         }
    245 
    246         mFirstFrameOffset = extendedHeaderSize;
    247 
    248         uint16_t extendedFlags = 0;
    249         if (extendedHeaderSize >= 6) {
    250             extendedFlags = U16_AT(&mData[4]);
    251 
    252             if (extendedHeaderSize >= 10) {
    253                 size_t paddingSize = U32_AT(&mData[6]);
    254 
    255                 if (mFirstFrameOffset + paddingSize > mSize) {
    256                     free(mData);
    257                     mData = NULL;
    258 
    259                     return false;
    260                 }
    261 
    262                 mSize -= paddingSize;
    263             }
    264 
    265             if (extendedFlags & 0x8000) {
    266                 ALOGV("have crc");
    267             }
    268         }
    269     } else if (header.version_major == 4 && (header.flags & 0x40)) {
    270         // Version 2.4 has an optional extended header, that's different
    271         // from Version 2.3's...
    272 
    273         if (mSize < 4) {
    274             free(mData);
    275             mData = NULL;
    276 
    277             return false;
    278         }
    279 
    280         size_t ext_size;
    281         if (!ParseSyncsafeInteger(mData, &ext_size)) {
    282             free(mData);
    283             mData = NULL;
    284 
    285             return false;
    286         }
    287 
    288         if (ext_size < 6 || ext_size > mSize) {
    289             free(mData);
    290             mData = NULL;
    291 
    292             return false;
    293         }
    294 
    295         mFirstFrameOffset = ext_size;
    296     }
    297 
    298     if (header.version_major == 2) {
    299         mVersion = ID3_V2_2;
    300     } else if (header.version_major == 3) {
    301         mVersion = ID3_V2_3;
    302     } else {
    303         CHECK_EQ(header.version_major, 4);
    304         mVersion = ID3_V2_4;
    305     }
    306 
    307     return true;
    308 }
    309 
    310 void ID3::removeUnsynchronization() {
    311     for (size_t i = 0; i + 1 < mSize; ++i) {
    312         if (mData[i] == 0xff && mData[i + 1] == 0x00) {
    313             memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
    314             --mSize;
    315         }
    316     }
    317 }
    318 
    319 static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
    320     for (size_t i = 0; i < 4; ++i) {
    321         dst[3 - i] = (x & 0x7f);
    322         x >>= 7;
    323     }
    324 }
    325 
    326 bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
    327     size_t oldSize = mSize;
    328 
    329     size_t offset = 0;
    330     while (mSize >= 10 && offset <= mSize - 10) {
    331         if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
    332             break;
    333         }
    334 
    335         size_t dataSize;
    336         if (iTunesHack) {
    337             dataSize = U32_AT(&mData[offset + 4]);
    338         } else if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
    339             return false;
    340         }
    341 
    342         if (dataSize > mSize - 10 - offset) {
    343             return false;
    344         }
    345 
    346         uint16_t flags = U16_AT(&mData[offset + 8]);
    347         uint16_t prevFlags = flags;
    348 
    349         if (flags & 1) {
    350             // Strip data length indicator
    351 
    352             if (mSize < 14 || mSize - 14 < offset || dataSize < 4) {
    353                 return false;
    354             }
    355             memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
    356             mSize -= 4;
    357             dataSize -= 4;
    358 
    359             flags &= ~1;
    360         }
    361 
    362         if (flags & 2) {
    363             // This file has "unsynchronization", so we have to replace occurrences
    364             // of 0xff 0x00 with just 0xff in order to get the real data.
    365 
    366             size_t readOffset = offset + 11;
    367             size_t writeOffset = offset + 11;
    368             for (size_t i = 0; i + 1 < dataSize; ++i) {
    369                 if (mData[readOffset - 1] == 0xff
    370                         && mData[readOffset] == 0x00) {
    371                     ++readOffset;
    372                     --mSize;
    373                     --dataSize;
    374                 }
    375                 mData[writeOffset++] = mData[readOffset++];
    376             }
    377             // move the remaining data following this frame
    378             memmove(&mData[writeOffset], &mData[readOffset], oldSize - readOffset);
    379 
    380             flags &= ~2;
    381         }
    382 
    383         if (flags != prevFlags || iTunesHack) {
    384             WriteSyncsafeInteger(&mData[offset + 4], dataSize);
    385             mData[offset + 8] = flags >> 8;
    386             mData[offset + 9] = flags & 0xff;
    387         }
    388 
    389         offset += 10 + dataSize;
    390     }
    391 
    392     memset(&mData[mSize], 0, oldSize - mSize);
    393 
    394     return true;
    395 }
    396 
    397 ID3::Iterator::Iterator(const ID3 &parent, const char *id)
    398     : mParent(parent),
    399       mID(NULL),
    400       mOffset(mParent.mFirstFrameOffset),
    401       mFrameData(NULL),
    402       mFrameSize(0) {
    403     if (id) {
    404         mID = strdup(id);
    405     }
    406 
    407     findFrame();
    408 }
    409 
    410 ID3::Iterator::~Iterator() {
    411     if (mID) {
    412         free(mID);
    413         mID = NULL;
    414     }
    415 }
    416 
    417 bool ID3::Iterator::done() const {
    418     return mFrameData == NULL;
    419 }
    420 
    421 void ID3::Iterator::next() {
    422     if (mFrameData == NULL) {
    423         return;
    424     }
    425 
    426     mOffset += mFrameSize;
    427 
    428     findFrame();
    429 }
    430 
    431 void ID3::Iterator::getID(String8 *id) const {
    432     id->setTo("");
    433 
    434     if (mFrameData == NULL) {
    435         return;
    436     }
    437 
    438     if (mParent.mVersion == ID3_V2_2) {
    439         id->setTo((const char *)&mParent.mData[mOffset], 3);
    440     } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
    441         id->setTo((const char *)&mParent.mData[mOffset], 4);
    442     } else {
    443         CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    444 
    445         switch (mOffset) {
    446             case 3:
    447                 id->setTo("TT2");
    448                 break;
    449             case 33:
    450                 id->setTo("TP1");
    451                 break;
    452             case 63:
    453                 id->setTo("TAL");
    454                 break;
    455             case 93:
    456                 id->setTo("TYE");
    457                 break;
    458             case 97:
    459                 id->setTo("COM");
    460                 break;
    461             case 126:
    462                 id->setTo("TRK");
    463                 break;
    464             case 127:
    465                 id->setTo("TCO");
    466                 break;
    467             default:
    468                 CHECK(!"should not be here.");
    469                 break;
    470         }
    471     }
    472 }
    473 
    474 
    475 // the 2nd argument is used to get the data following the \0 in a comment field
    476 void ID3::Iterator::getString(String8 *id, String8 *comment) const {
    477     getstring(id, false);
    478     if (comment != NULL) {
    479         getstring(comment, true);
    480     }
    481 }
    482 
    483 // comment fields (COM/COMM) contain an initial short descriptor, followed by \0,
    484 // followed by more data. The data following the \0 can be retrieved by setting
    485 // "otherdata" to true.
    486 void ID3::Iterator::getstring(String8 *id, bool otherdata) const {
    487     id->setTo("");
    488 
    489     const uint8_t *frameData = mFrameData;
    490     if (frameData == NULL) {
    491         return;
    492     }
    493 
    494     uint8_t encoding = *frameData;
    495 
    496     if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
    497         if (mOffset == 126 || mOffset == 127) {
    498             // Special treatment for the track number and genre.
    499             char tmp[16];
    500             sprintf(tmp, "%d", (int)*frameData);
    501 
    502             id->setTo(tmp);
    503             return;
    504         }
    505 
    506         // this is supposed to be ISO-8859-1, but pass it up as-is to the caller, who will figure
    507         // out the real encoding
    508         id->setTo((const char*)frameData, mFrameSize);
    509         return;
    510     }
    511 
    512     if (mFrameSize < getHeaderLength() + 1) {
    513         return;
    514     }
    515     size_t n = mFrameSize - getHeaderLength() - 1;
    516     if (otherdata) {
    517         // skip past the encoding, language, and the 0 separator
    518         frameData += 4;
    519         int32_t i = n - 4;
    520         while(--i >= 0 && *++frameData != 0) ;
    521         int skipped = (frameData - mFrameData);
    522         if (skipped >= (int)n) {
    523             return;
    524         }
    525         n -= skipped;
    526     }
    527 
    528     if (encoding == 0x00) {
    529         // supposedly ISO 8859-1
    530         id->setTo((const char*)frameData + 1, n);
    531     } else if (encoding == 0x03) {
    532         // supposedly UTF-8
    533         id->setTo((const char *)(frameData + 1), n);
    534     } else if (encoding == 0x02) {
    535         // supposedly UTF-16 BE, no byte order mark.
    536         // API wants number of characters, not number of bytes...
    537         int len = n / 2;
    538         const char16_t *framedata = (const char16_t *) (frameData + 1);
    539         char16_t *framedatacopy = NULL;
    540 #if BYTE_ORDER == LITTLE_ENDIAN
    541         framedatacopy = new char16_t[len];
    542         for (int i = 0; i < len; i++) {
    543             framedatacopy[i] = bswap_16(framedata[i]);
    544         }
    545         framedata = framedatacopy;
    546 #endif
    547         id->setTo(framedata, len);
    548         if (framedatacopy != NULL) {
    549             delete[] framedatacopy;
    550         }
    551     } else if (encoding == 0x01) {
    552         // UCS-2
    553         // API wants number of characters, not number of bytes...
    554         int len = n / 2;
    555         const char16_t *framedata = (const char16_t *) (frameData + 1);
    556         char16_t *framedatacopy = NULL;
    557         if (*framedata == 0xfffe) {
    558             // endianness marker doesn't match host endianness, convert
    559             framedatacopy = new char16_t[len];
    560             for (int i = 0; i < len; i++) {
    561                 framedatacopy[i] = bswap_16(framedata[i]);
    562             }
    563             framedata = framedatacopy;
    564         }
    565         // If the string starts with an endianness marker, skip it
    566         if (*framedata == 0xfeff) {
    567             framedata++;
    568             len--;
    569         }
    570 
    571         // check if the resulting data consists entirely of 8-bit values
    572         bool eightBit = true;
    573         for (int i = 0; i < len; i++) {
    574             if (framedata[i] > 0xff) {
    575                 eightBit = false;
    576                 break;
    577             }
    578         }
    579         if (eightBit) {
    580             // collapse to 8 bit, then let the media scanner client figure out the real encoding
    581             char *frame8 = new char[len];
    582             for (int i = 0; i < len; i++) {
    583                 frame8[i] = framedata[i];
    584             }
    585             id->setTo(frame8, len);
    586             delete [] frame8;
    587         } else {
    588             id->setTo(framedata, len);
    589         }
    590 
    591         if (framedatacopy != NULL) {
    592             delete[] framedatacopy;
    593         }
    594     }
    595 }
    596 
    597 const uint8_t *ID3::Iterator::getData(size_t *length) const {
    598     *length = 0;
    599 
    600     if (mFrameData == NULL) {
    601         return NULL;
    602     }
    603 
    604     *length = mFrameSize - getHeaderLength();
    605 
    606     return mFrameData;
    607 }
    608 
    609 size_t ID3::Iterator::getHeaderLength() const {
    610     if (mParent.mVersion == ID3_V2_2) {
    611         return 6;
    612     } else if (mParent.mVersion == ID3_V2_3 || mParent.mVersion == ID3_V2_4) {
    613         return 10;
    614     } else {
    615         CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    616         return 0;
    617     }
    618 }
    619 
    620 void ID3::Iterator::findFrame() {
    621     for (;;) {
    622         mFrameData = NULL;
    623         mFrameSize = 0;
    624 
    625         if (mParent.mVersion == ID3_V2_2) {
    626             if (mOffset + 6 > mParent.mSize) {
    627                 return;
    628             }
    629 
    630             if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
    631                 return;
    632             }
    633 
    634             mFrameSize =
    635                 (mParent.mData[mOffset + 3] << 16)
    636                 | (mParent.mData[mOffset + 4] << 8)
    637                 | mParent.mData[mOffset + 5];
    638 
    639             if (mFrameSize == 0) {
    640                 return;
    641             }
    642             mFrameSize += 6; // add tag id and size field
    643 
    644             // Prevent integer overflow in validation
    645             if (SIZE_MAX - mOffset <= mFrameSize) {
    646                 return;
    647             }
    648 
    649             if (mOffset + mFrameSize > mParent.mSize) {
    650                 ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)",
    651                     mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)6);
    652                 return;
    653             }
    654 
    655             mFrameData = &mParent.mData[mOffset + 6];
    656 
    657             if (!mID) {
    658                 break;
    659             }
    660 
    661             char id[4];
    662             memcpy(id, &mParent.mData[mOffset], 3);
    663             id[3] = '\0';
    664 
    665             if (!strcmp(id, mID)) {
    666                 break;
    667             }
    668         } else if (mParent.mVersion == ID3_V2_3
    669                 || mParent.mVersion == ID3_V2_4) {
    670             if (mOffset + 10 > mParent.mSize) {
    671                 return;
    672             }
    673 
    674             if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
    675                 return;
    676             }
    677 
    678             size_t baseSize = 0;
    679             if (mParent.mVersion == ID3_V2_4) {
    680                 if (!ParseSyncsafeInteger(
    681                             &mParent.mData[mOffset + 4], &baseSize)) {
    682                     return;
    683                 }
    684             } else {
    685                 baseSize = U32_AT(&mParent.mData[mOffset + 4]);
    686             }
    687 
    688             if (baseSize == 0) {
    689                 return;
    690             }
    691 
    692             // Prevent integer overflow when adding
    693             if (SIZE_MAX - 10 <= baseSize) {
    694                 return;
    695             }
    696 
    697             mFrameSize = 10 + baseSize; // add tag id, size field and flags
    698 
    699             // Prevent integer overflow in validation
    700             if (SIZE_MAX - mOffset <= mFrameSize) {
    701                 return;
    702             }
    703 
    704             if (mOffset + mFrameSize > mParent.mSize) {
    705                 ALOGV("partial frame at offset %zu (size = %zu, bytes-remaining = %zu)",
    706                     mOffset, mFrameSize, mParent.mSize - mOffset - (size_t)10);
    707                 return;
    708             }
    709 
    710             uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
    711 
    712             if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
    713                 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
    714                 // Compression or encryption are not supported at this time.
    715                 // Per-frame unsynchronization and data-length indicator
    716                 // have already been taken care of.
    717 
    718                 ALOGV("Skipping unsupported frame (compression, encryption "
    719                      "or per-frame unsynchronization flagged");
    720 
    721                 mOffset += mFrameSize;
    722                 continue;
    723             }
    724 
    725             mFrameData = &mParent.mData[mOffset + 10];
    726 
    727             if (!mID) {
    728                 break;
    729             }
    730 
    731             char id[5];
    732             memcpy(id, &mParent.mData[mOffset], 4);
    733             id[4] = '\0';
    734 
    735             if (!strcmp(id, mID)) {
    736                 break;
    737             }
    738         } else {
    739             CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
    740 
    741             if (mOffset >= mParent.mSize) {
    742                 return;
    743             }
    744 
    745             mFrameData = &mParent.mData[mOffset];
    746 
    747             switch (mOffset) {
    748                 case 3:
    749                 case 33:
    750                 case 63:
    751                     mFrameSize = 30;
    752                     break;
    753                 case 93:
    754                     mFrameSize = 4;
    755                     break;
    756                 case 97:
    757                     if (mParent.mVersion == ID3_V1) {
    758                         mFrameSize = 30;
    759                     } else {
    760                         mFrameSize = 29;
    761                     }
    762                     break;
    763                 case 126:
    764                     mFrameSize = 1;
    765                     break;
    766                 case 127:
    767                     mFrameSize = 1;
    768                     break;
    769                 default:
    770                     CHECK(!"Should not be here, invalid offset.");
    771                     break;
    772             }
    773 
    774             if (!mID) {
    775                 break;
    776             }
    777 
    778             String8 id;
    779             getID(&id);
    780 
    781             if (id == mID) {
    782                 break;
    783             }
    784         }
    785 
    786         mOffset += mFrameSize;
    787     }
    788 }
    789 
    790 static size_t StringSize(const uint8_t *start, uint8_t encoding) {
    791     if (encoding == 0x00 || encoding == 0x03) {
    792         // ISO 8859-1 or UTF-8
    793         return strlen((const char *)start) + 1;
    794     }
    795 
    796     // UCS-2
    797     size_t n = 0;
    798     while (start[n] != '\0' || start[n + 1] != '\0') {
    799         n += 2;
    800     }
    801 
    802     // Add size of null termination.
    803     return n + 2;
    804 }
    805 
    806 const void *
    807 ID3::getAlbumArt(size_t *length, String8 *mime) const {
    808     *length = 0;
    809     mime->setTo("");
    810 
    811     Iterator it(
    812             *this,
    813             (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) ? "APIC" : "PIC");
    814 
    815     while (!it.done()) {
    816         size_t size;
    817         const uint8_t *data = it.getData(&size);
    818 
    819         if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
    820             uint8_t encoding = data[0];
    821             mime->setTo((const char *)&data[1]);
    822             size_t mimeLen = strlen((const char *)&data[1]) + 1;
    823 
    824 #if 0
    825             uint8_t picType = data[1 + mimeLen];
    826             if (picType != 0x03) {
    827                 // Front Cover Art
    828                 it.next();
    829                 continue;
    830             }
    831 #endif
    832 
    833             size_t descLen = StringSize(&data[2 + mimeLen], encoding);
    834 
    835             if (size < 2 ||
    836                     size - 2 < mimeLen ||
    837                     size - 2 - mimeLen < descLen) {
    838                 ALOGW("bogus album art sizes");
    839                 return NULL;
    840             }
    841             *length = size - 2 - mimeLen - descLen;
    842 
    843             return &data[2 + mimeLen + descLen];
    844         } else {
    845             uint8_t encoding = data[0];
    846 
    847             if (!memcmp(&data[1], "PNG", 3)) {
    848                 mime->setTo("image/png");
    849             } else if (!memcmp(&data[1], "JPG", 3)) {
    850                 mime->setTo("image/jpeg");
    851             } else if (!memcmp(&data[1], "-->", 3)) {
    852                 mime->setTo("text/plain");
    853             } else {
    854                 return NULL;
    855             }
    856 
    857 #if 0
    858             uint8_t picType = data[4];
    859             if (picType != 0x03) {
    860                 // Front Cover Art
    861                 it.next();
    862                 continue;
    863             }
    864 #endif
    865 
    866             size_t descLen = StringSize(&data[5], encoding);
    867 
    868             *length = size - 5 - descLen;
    869 
    870             return &data[5 + descLen];
    871         }
    872     }
    873 
    874     return NULL;
    875 }
    876 
    877 bool ID3::parseV1(const sp<DataSource> &source) {
    878     const size_t V1_TAG_SIZE = 128;
    879 
    880     off64_t size;
    881     if (source->getSize(&size) != OK || size < (off64_t)V1_TAG_SIZE) {
    882         return false;
    883     }
    884 
    885     mData = (uint8_t *)malloc(V1_TAG_SIZE);
    886     if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
    887             != (ssize_t)V1_TAG_SIZE) {
    888         free(mData);
    889         mData = NULL;
    890 
    891         return false;
    892     }
    893 
    894     if (memcmp("TAG", mData, 3)) {
    895         free(mData);
    896         mData = NULL;
    897 
    898         return false;
    899     }
    900 
    901     mSize = V1_TAG_SIZE;
    902     mFirstFrameOffset = 3;
    903 
    904     if (mData[V1_TAG_SIZE - 3] != 0) {
    905         mVersion = ID3_V1;
    906     } else {
    907         mVersion = ID3_V1_1;
    908     }
    909 
    910     return true;
    911 }
    912 
    913 }  // namespace android
    914