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