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