Support for ID3 version 1 (and 1.1) tags in .mp3 files.

related-to-bug: 2375219
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 2b3ef1a..0547978 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -33,7 +33,11 @@
       mSize(0),
       mFirstFrameOffset(0),
       mVersion(ID3_UNKNOWN) {
-    mIsValid = parse(source);
+    mIsValid = parseV2(source);
+
+    if (!mIsValid) {
+        mIsValid = parseV1(source);
+    }
 }
 
 ID3::~ID3() {
@@ -51,7 +55,7 @@
     return mVersion;
 }
 
-bool ID3::parse(const sp<DataSource> &source) {
+bool ID3::parseV2(const sp<DataSource> &source) {
     struct id3_header {
         char id[3];
         uint8_t version_major;
@@ -119,7 +123,7 @@
     }
 
     if (header.flags & 0x80) {
-        LOGI("removing unsynchronization");
+        LOGV("removing unsynchronization");
         removeUnsynchronization();
     }
 
@@ -128,12 +132,18 @@
         // Version 2.3 has an optional extended header.
 
         if (mSize < 4) {
+            free(mData);
+            mData = NULL;
+
             return false;
         }
 
         size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
 
         if (extendedHeaderSize > mSize) {
+            free(mData);
+            mData = NULL;
+
             return false;
         }
 
@@ -147,6 +157,9 @@
                 size_t paddingSize = U32_AT(&mData[6]);
 
                 if (mFirstFrameOffset + paddingSize > mSize) {
+                    free(mData);
+                    mData = NULL;
+
                     return false;
                 }
 
@@ -154,7 +167,7 @@
             }
 
             if (extendedFlags & 0x8000) {
-                LOGI("have crc");
+                LOGV("have crc");
             }
         }
     }
@@ -221,9 +234,37 @@
 
     if (mParent.mVersion == ID3_V2_2) {
         id->setTo((const char *)&mParent.mData[mOffset], 3);
-    } else {
-        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+    } else if (mParent.mVersion == ID3_V2_3) {
         id->setTo((const char *)&mParent.mData[mOffset], 4);
+    } else {
+        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+
+        switch (mOffset) {
+            case 3:
+                id->setTo("TT2");
+                break;
+            case 33:
+                id->setTo("TP1");
+                break;
+            case 63:
+                id->setTo("TAL");
+                break;
+            case 93:
+                id->setTo("TYE");
+                break;
+            case 97:
+                id->setTo("COM");
+                break;
+            case 126:
+                id->setTo("TRK");
+                break;
+            case 127:
+                id->setTo("TCO");
+                break;
+            default:
+                CHECK(!"should not be here.");
+                break;
+        }
     }
 }
 
@@ -273,6 +314,20 @@
         return;
     }
 
+    if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
+        if (mOffset == 126 || mOffset == 127) {
+            // Special treatment for the track number and genre.
+            char tmp[16];
+            sprintf(tmp, "%d", (int)*mFrameData);
+
+            id->setTo(tmp);
+            return;
+        }
+
+        id->setTo((const char *)mFrameData, mFrameSize);
+        return;
+    }
+
     size_t n = mFrameSize - getHeaderLength() - 1;
 
     if (*mFrameData == 0x00) {
@@ -299,9 +354,11 @@
 size_t ID3::Iterator::getHeaderLength() const {
     if (mParent.mVersion == ID3_V2_2) {
         return 6;
-    } else {
-        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+    } else if (mParent.mVersion == ID3_V2_3) {
         return 10;
+    } else {
+        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+        return 0;
     }
 }
 
@@ -345,9 +402,7 @@
             if (!strcmp(id, mID)) {
                 break;
             }
-        } else {
-            CHECK_EQ(mParent.mVersion, ID3_V2_3);
-
+        } else if (mParent.mVersion == ID3_V2_3) {
             if (mOffset + 10 > mParent.mSize) {
                 return;
             }
@@ -377,6 +432,52 @@
             if (!strcmp(id, mID)) {
                 break;
             }
+        } else {
+            CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+
+            if (mOffset >= mParent.mSize) {
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset];
+
+            switch (mOffset) {
+                case 3:
+                case 33:
+                case 63:
+                    mFrameSize = 30;
+                    break;
+                case 93:
+                    mFrameSize = 4;
+                    break;
+                case 97:
+                    if (mParent.mVersion == ID3_V1) {
+                        mFrameSize = 30;
+                    } else {
+                        mFrameSize = 29;
+                    }
+                    break;
+                case 126:
+                    mFrameSize = 1;
+                    break;
+                case 127:
+                    mFrameSize = 1;
+                    break;
+                default:
+                    CHECK(!"Should not be here, invalid offset.");
+                    break;
+            }
+
+            if (!mID) {
+                break;
+            }
+
+            String8 id;
+            getID(&id);
+
+            if (id == mID) {
+                break;
+            }
         }
 
         mOffset += mFrameSize;
@@ -461,5 +562,40 @@
     return NULL;
 }
 
+bool ID3::parseV1(const sp<DataSource> &source) {
+    const size_t V1_TAG_SIZE = 128;
+
+    off_t size;
+    if (source->getSize(&size) != OK || size < (off_t)V1_TAG_SIZE) {
+        return false;
+    }
+
+    mData = (uint8_t *)malloc(V1_TAG_SIZE);
+    if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
+            != (ssize_t)V1_TAG_SIZE) {
+        free(mData);
+        mData = NULL;
+
+        return false;
+    }
+
+    if (memcmp("TAG", mData, 3)) {
+        free(mData);
+        mData = NULL;
+
+        return false;
+    }
+
+    mSize = V1_TAG_SIZE;
+    mFirstFrameOffset = 3;
+
+    if (mData[V1_TAG_SIZE - 3] != 0) {
+        mVersion = ID3_V1;
+    } else {
+        mVersion = ID3_V1_1;
+    }
+
+    return true;
+}
 
 }  // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 305b065..0741045 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -16,6 +16,8 @@
 
 #include "../include/ID3.h"
 
+#include <sys/stat.h>
+
 #include <ctype.h>
 #include <dirent.h>
 
@@ -108,6 +110,12 @@
 }
 
 void scan(const char *path) {
+    struct stat st;
+    if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
+        scanFile(path);
+        return;
+    }
+
     DIR *dir = opendir(path);
 
     if (dir == NULL) {
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index 79931ac..da042a3 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -28,6 +28,8 @@
 struct ID3 {
     enum Version {
         ID3_UNKNOWN,
+        ID3_V1,
+        ID3_V1_1,
         ID3_V2_2,
         ID3_V2_3
     };
@@ -74,7 +76,8 @@
     size_t mFirstFrameOffset;
     Version mVersion;
 
-    bool parse(const sp<DataSource> &source);
+    bool parseV1(const sp<DataSource> &source);
+    bool parseV2(const sp<DataSource> &source);
     void removeUnsynchronization();
 
     ID3(const ID3 &);