Merge change 7624

* changes:
  Impl. of the metadata getters.
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index a345ef8..8dbe51a 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -20,10 +20,12 @@
 import android.os.Parcel;
 import android.util.Log;
 
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.Date;
-import java.util.Set;
 import java.util.HashMap;
+import java.util.Set;
+import java.util.TimeZone;
 
 
 /**
@@ -102,15 +104,20 @@
     public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET;
     public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY);
 
-    public static final int STRING_VAL = 1;
-    public static final int INTEGER_VAL = 2;
-    public static final int LONG_VAL = 3;
-    public static final int DOUBLE_VAL = 4;
-    public static final int TIMED_TEXT_VAL = 5;
-    private static final int LAST_TYPE = 5;
+    public static final int STRING_VAL     = 1;
+    public static final int INTEGER_VAL    = 2;
+    public static final int BOOLEAN_VAL    = 3;
+    public static final int LONG_VAL       = 4;
+    public static final int DOUBLE_VAL     = 5;
+    public static final int TIMED_TEXT_VAL = 6;
+    public static final int DATE_VAL       = 7;
+    public static final int BYTE_ARRAY_VAL = 8;
+    // FIXME: misses a type for shared heap is missing (MemoryFile).
+    // FIXME: misses a type for bitmaps.
+    private static final int LAST_TYPE = 8;
 
     private static final String TAG = "media.Metadata";
-    private static final int kMetaHeaderSize = 8;  // 8 bytes for the size + the marker
+    private static final int kMetaHeaderSize = 8;  //  size + marker
     private static final int kMetaMarker = 0x4d455441;  // 'M' 'E' 'T' 'A'
     private static final int kRecordHeaderSize = 12; // size + id + type
 
@@ -122,21 +129,28 @@
     // Used to look up if a key was present too.
     // Key: Metadata ID
     // Value: Offset of the metadata type field in the record.
-    private final HashMap<Integer, Integer> mKeyToPosMap = new HashMap<Integer, Integer>();
+    private final HashMap<Integer, Integer> mKeyToPosMap =
+            new HashMap<Integer, Integer>();
 
     /**
-     * Helper class to hold a pair (time, text). Can be used to implement caption.
+     * Helper class to hold a triple (time, duration, text). Can be used to
+     * implement caption.
      */
     public class TimedText {
         private Date mTime;
+        private int mDuration;  // millisec
         private String mText;
-        public TimedText(final Date time, final String text) {
+
+        public TimedText(Date time, int duration, String text) {
             mTime = time;
+            mDuration = duration;
             mText = text;
         }
+
         public String toString() {
             StringBuilder res = new StringBuilder(80);
-            res.append(mTime).append(":").append(mText);
+            res.append(mTime).append("-").append(mDuration)
+                    .append(":").append(mText);
             return res.toString();
         }
     }
@@ -300,44 +314,64 @@
         return mKeyToPosMap.containsKey(metadataId);
     }
 
-    // Accessors
+    // Accessors.
+    // Caller must make sure the key is present using the {@code has}
+    // method otherwise a RuntimeException will occur.
+
     public String getString(final int key) {
-        // FIXME: Implement.
-        return new String();
+        checkType(key, STRING_VAL);
+        return mParcel.readString();
     }
 
     public int getInt(final int key) {
-        // FIXME: Implement.
-        return 0;
+        checkType(key, INTEGER_VAL);
+        return mParcel.readInt();
+    }
+
+    public boolean getBoolean(final int key) {
+        checkType(key, BOOLEAN_VAL);
+        return mParcel.readInt() == 1;
     }
 
     public long getLong(final int key) {
-        // FIXME: Implement.
-        return 0;
+        checkType(key, LONG_VAL);
+        return mParcel.readLong();
     }
 
     public double getDouble(final int key) {
-        // FIXME: Implement.
-        return 0.0;
+        checkType(key, DOUBLE_VAL);
+        return mParcel.readDouble();
     }
 
     public byte[] getByteArray(final int key) {
-        return new byte[0];
-    }
-
-    public Bitmap getBitmap(final int key) {
-        // FIXME: Implement.
-        return null;
+        checkType(key, BYTE_ARRAY_VAL);
+        return mParcel.createByteArray();
     }
 
     public Date getDate(final int key) {
-        // FIXME: Implement.
-        return new Date();
+        checkType(key, DATE_VAL);
+        final long timeSinceEpoch = mParcel.readLong();
+        final String timeZone = mParcel.readString();
+
+        if (timeZone.length() == 0) {
+            return new Date(timeSinceEpoch);
+        } else {
+            TimeZone tz = TimeZone.getTimeZone(timeZone);
+            Calendar cal = Calendar.getInstance(tz);
+
+            cal.setTimeInMillis(timeSinceEpoch);
+            return cal.getTime();
+        }
     }
 
     public TimedText getTimedText(final int key) {
-        // FIXME: Implement.
-        return new TimedText(new Date(0), "<missing>");
+        checkType(key, TIMED_TEXT_VAL);
+        final Date startTime = new Date(mParcel.readLong());  // epoch
+        final int duration = mParcel.readInt();  // millisec
+
+        return new TimedText(startTime,
+                             duration,
+                             mParcel.readString());
     }
 
     // @return the last available system metadata id. Ids are
@@ -360,4 +394,16 @@
         }
         return true;
     }
+
+    // Check the type of the data match what is expected.
+    private void checkType(final int key, final int expectedType) {
+        final int pos = mKeyToPosMap.get(key);
+
+        mParcel.setDataPosition(pos);
+
+        final int type = mParcel.readInt();
+        if (type != expectedType) {
+            throw new IllegalStateException("Wrong type " + expectedType + " but got " + type);
+        }
+    }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
index f51b29e..576dddd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
@@ -21,6 +21,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import java.util.Calendar;
+import java.util.Date;
+
 /*
  * Check the Java layer that parses serialized metadata in Parcel
  * works as expected.
@@ -89,16 +92,6 @@
         mParcel.writeInt(kMarker);
     }
 
-    // Insert a string record at the current position.
-    private void writeStringRecord(int metadataId, String val) {
-        final int start = mParcel.dataPosition();
-        mParcel.writeInt(-1);  // Placeholder for the length
-        mParcel.writeInt(metadataId);
-        mParcel.writeInt(Metadata.STRING_VAL);
-        mParcel.writeString(val);
-        adjustSize(start);
-    }
-
     // ----------------------------------------------------------------------
     // START OF THE TESTS
 
@@ -133,7 +126,9 @@
         assertParse();
     }
 
+    // ----------------------------------------------------------------------
     // RECORDS
+    // ----------------------------------------------------------------------
 
     // A record header should be at least 12 bytes long
     @SmallTest
@@ -223,4 +218,221 @@
         assertFalse(mMetadata.has(Metadata.GENRE));
         assertFalse(mMetadata.has(Metadata.firstCustomId()));
     }
+
+    // ----------------------------------------------------------------------
+    // GETTERS
+    // ----------------------------------------------------------------------
+
+    // getString
+    @SmallTest
+    public void testGetString() throws Exception {
+        writeStringRecord(Metadata.TITLE, "a title");
+        writeStringRecord(Metadata.GENRE, "comedy");
+        adjustSize();
+        assertParse();
+
+        assertEquals("a title", mMetadata.getString(Metadata.TITLE));
+        assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
+    }
+
+    // get an empty string.
+    @SmallTest
+    public void testGetEmptyString() throws Exception {
+        writeStringRecord(Metadata.TITLE, "");
+        adjustSize();
+        assertParse();
+
+        assertEquals("", mMetadata.getString(Metadata.TITLE));
+    }
+
+    // get a string when a NULL value was in the parcel
+    @SmallTest
+    public void testGetNullString() throws Exception {
+        writeStringRecord(Metadata.TITLE, null);
+        adjustSize();
+        assertParse();
+
+        assertEquals(null, mMetadata.getString(Metadata.TITLE));
+    }
+
+    // get a string when an integer is actually present
+    @SmallTest
+    public void testWrongType() throws Exception {
+        writeIntRecord(Metadata.DURATION, 5);
+        adjustSize();
+        assertParse();
+
+        try {
+            mMetadata.getString(Metadata.DURATION);
+        } catch (IllegalStateException ise) {
+            return;
+        }
+        fail("Exception was not thrown");
+    }
+
+    // getInt
+    @SmallTest
+    public void testGetInt() throws Exception {
+        writeIntRecord(Metadata.CD_TRACK_NUM, 1);
+        adjustSize();
+        assertParse();
+
+        assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
+    }
+
+    // getBoolean
+    @SmallTest
+    public void testGetBoolean() throws Exception {
+        writeBooleanRecord(Metadata.DRM_CRIPPLED, true);
+        adjustSize();
+        assertParse();
+
+        assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED));
+    }
+
+    // getLong
+    @SmallTest
+    public void testGetLong() throws Exception {
+        writeLongRecord(Metadata.DURATION, 1L);
+        adjustSize();
+        assertParse();
+
+        assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
+    }
+
+    // getDouble
+    @SmallTest
+    public void testGetDouble() throws Exception {
+        writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
+        adjustSize();
+        assertParse();
+
+        assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
+    }
+
+    // getByteArray
+    @SmallTest
+    public void testGetByteArray() throws Exception {
+        byte data[] = new byte[]{1,2,3,4,5};
+
+        writeByteArrayRecord(Metadata.ALBUM_ART, data);
+        adjustSize();
+        assertParse();
+
+        byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
+        for (int i = 0; i < data.length; ++i) {
+            assertEquals(data[i], res[i]);
+        }
+    }
+
+    // getDate
+    @SmallTest
+    public void testGetDate() throws Exception {
+        writeDateRecord(Metadata.DATE, 0, "PST");
+        adjustSize();
+        assertParse();
+
+        assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
+    }
+
+    // getTimedText
+    @SmallTest
+    public void testGetTimedText() throws Exception {
+        Date now = Calendar.getInstance().getTime();
+        writeTimedTextRecord(Metadata.CAPTION, now.getTime(),
+                             10, "Some caption");
+        adjustSize();
+        assertParse();
+
+        Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION);
+        assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString());
+    }
+
+    // ----------------------------------------------------------------------
+    // HELPERS TO APPEND RECORDS
+    // ----------------------------------------------------------------------
+
+    // Insert a string record at the current position.
+    private void writeStringRecord(int metadataId, String val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.STRING_VAL);
+        mParcel.writeString(val);
+        adjustSize(start);
+    }
+
+    // Insert an int record at the current position.
+    private void writeIntRecord(int metadataId, int val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.INTEGER_VAL);
+        mParcel.writeInt(val);
+        adjustSize(start);
+    }
+
+    // Insert a boolean record at the current position.
+    private void writeBooleanRecord(int metadataId, boolean val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.BOOLEAN_VAL);
+        mParcel.writeInt(val ? 1 : 0);
+        adjustSize(start);
+    }
+
+    // Insert a Long record at the current position.
+    private void writeLongRecord(int metadataId, long val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.LONG_VAL);
+        mParcel.writeLong(val);
+        adjustSize(start);
+    }
+
+    // Insert a Double record at the current position.
+    private void writeDoubleRecord(int metadataId, double val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.DOUBLE_VAL);
+        mParcel.writeDouble(val);
+        adjustSize(start);
+    }
+
+    // Insert a ByteArray record at the current position.
+    private void writeByteArrayRecord(int metadataId, byte[] val) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
+        mParcel.writeByteArray(val);
+        adjustSize(start);
+    }
+
+    // Insert a Date record at the current position.
+    private void writeDateRecord(int metadataId, long time, String tz) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.DATE_VAL);
+        mParcel.writeLong(time);
+        mParcel.writeString(tz);
+        adjustSize(start);
+    }
+
+    // Insert a TimedText record at the current position.
+    private void writeTimedTextRecord(int metadataId, long begin,
+                                      int duration, String text) {
+        final int start = mParcel.dataPosition();
+        mParcel.writeInt(-1);  // Placeholder for the length
+        mParcel.writeInt(metadataId);
+        mParcel.writeInt(Metadata.TIMED_TEXT_VAL);
+        mParcel.writeLong(begin);
+        mParcel.writeInt(duration);
+        mParcel.writeString(text);
+        adjustSize(start);
+    }
 }