New Metadata API for the MediaPlayer.

MediaPlayer.java
New method getMetadata to fetch metadata from the player.
New method setMetadataFilter to filter metadata notification and fetches.

Metadata.java
Added basic interface. Implementation incomplete.
diff --git a/api/current.xml b/api/current.xml
index a2273fd..3b9fa76 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -73919,6 +73919,17 @@
  visibility="public"
 >
 </field>
+<field name="MEDIA_INFO_METADATA_UPDATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="802"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MEDIA_INFO_NOT_SEEKABLE"
  type="int"
  transient="false"
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 298cce9..139fb41 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -34,7 +34,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
-
+import java.util.Set;
 import java.lang.ref.WeakReference;
 
 /**
@@ -431,6 +431,39 @@
  */
 public class MediaPlayer
 {
+    /**
+       Constant to retrieve only the new metadata since the last
+       call.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean METADATA_UPDATE_ONLY = true;
+
+    /**
+       Constant to retrieve all the metadata.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean METADATA_ALL = false;
+
+    /**
+       Constant to enable the metadata filter during retrieval.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean APPLY_METADATA_FILTER = true;
+
+    /**
+       Constant to disable the metadata filter during retrieval.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean BYPASS_METADATA_FILTER = false;
+
     static {
         System.loadLibrary("media_jni");
     }
@@ -880,6 +913,52 @@
     public native int getDuration();
 
     /**
+     * Gets the media metadata.
+     *
+     * @param update_only controls whether the full set of available
+     * metadata is returned or just the set that changed since the
+     * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
+     * #METADATA_ALL}.
+     *
+     * @param apply_filter if true only metadata that matches the
+     * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
+     * #BYPASS_METADATA_FILTER}.
+     *
+     * @return The metadata, possibly empty. null if an error occured.
+     // FIXME: unhide.
+     * {@hide}
+     */
+    public Metadata getMetadata(final boolean update_only,
+                                final boolean apply_filter) {
+        // FIXME: Implement.
+        return new Metadata();
+    }
+
+    /**
+     * Set a filter for the metadata update notification and update
+     * retrieval. The caller provides 2 set of metadata keys, allowed
+     * and disallowed. The disallow set always takes the precedence
+     * over the allowed one.
+     * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
+     * shorthands to allow/disallow all or no metadata.
+     *
+     * By default, there is no filter set.
+     *
+     * @param allow Is the set of metadata the client is interested
+     *              receiving new notifications for.
+     * @param disallow Is the set of metadata the client is not interested
+     *                 receiving new notifications for.
+     * @return The call status code.
+     *
+     // FIXME: unhide.
+     * {@hide}
+     */
+    public int setMetadataFilter(Set<Integer> allow, Set<Integer> disallow) {
+        // FIXME: Implement.
+        return 0;
+    }
+
+    /**
      * Releases resources associated with this MediaPlayer object.
      * It is considered good practice to call this method when you're
      * done using the MediaPlayer.
@@ -1306,6 +1385,11 @@
      */
     public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
 
+    /** A new set of metadata is available.
+     * @see android.media.MediaPlayer.OnInfoListener
+     */
+    public static final int MEDIA_INFO_METADATA_UPDATE = 802;
+
     /**
      * Interface definition of a callback to be invoked to communicate some
      * info and/or warning about the media or its playback.
@@ -1322,6 +1406,7 @@
          * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
          * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
          * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
+         * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
          * </ul>
          * @param extra an extra code, specific to the info. Typically
          * implementation dependant.
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
new file mode 100644
index 0000000..80748a9
--- /dev/null
+++ b/media/java/android/media/Metadata.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+   Class to hold the media's metadata.  Metadata are used
+   for human consumption and can be embedded in the media (e.g
+   shoutcast) or available from an external source. The source can be
+   local (e.g thumbnail stored in the DB) or remote (e.g caption
+   server).
+
+   Metadata is like a Bundle. It is sparse and each key can occur at
+   most once. The key is an integer and the value is the actual metadata.
+
+   The caller is expected to know the type of the metadata and call
+   the right get* method to fetch its value.
+
+   // FIXME: unhide.
+   {@hide}
+ */
+public class Metadata
+{
+    // The metadata are keyed using integers rather than more heavy
+    // weight strings. We considered using Bundle to ship the metadata
+    // between the native layer and the java layer but dropped that
+    // option since keeping in sync a native implementation of Bundle
+    // and the java one would be too burdensome. Besides Bundle uses
+    // String for its keys.
+    // The key range [0 8192) is reserved for the system.
+    //
+    // We manually serialize the data in Parcels. For large memory
+    // blob (bitmaps, raw pictures) we use MemoryFile which allow the
+    // client to make the data purgeable once it is done with it.
+    //
+
+    public static final int ANY = 0;  // Never used for metadata returned, only for filtering.
+
+    // TODO: Should we use numbers compatible with the metadata retriever?
+    public static final int TITLE = 1;           // String
+    public static final int COMMENT = 2;         // String
+    public static final int COPYRIGHT = 3;       // String
+    public static final int ALBUM = 4;           // String
+    public static final int ARTIST = 5;          // String
+    public static final int AUTHOR = 6;          // String
+    public static final int COMPOSER = 7;        // String
+    public static final int GENRE = 8;           // String
+    public static final int DATE = 9;            // Date
+    public static final int DURATION = 10;       // Integer(millisec)
+    public static final int CD_TRACK_NUM = 11;   // Integer 1-based
+    public static final int CD_TRACK_MAX = 12;   // Integer
+    public static final int RATING = 13;         // String
+    public static final int ALBUM_ART = 14;      // byte[]
+    public static final int VIDEO_FRAME = 15;    // Bitmap
+    public static final int CAPTION = 16;        // TimedText
+
+    public static final int BIT_RATE = 17;       // Integer, Aggregate rate of
+                                                 // all the streams in bps.
+
+    public static final int AUDIO_BIT_RATE = 18; // Integer, bps
+    public static final int VIDEO_BIT_RATE = 19; // Integer, bps
+    public static final int AUDIO_SAMPLE_RATE = 20; // Integer, Hz
+    public static final int VIDEO_FRAME_RATE = 21;  // Integer, Hz
+
+    // See RFC2046 and RFC4281.
+    public static final int MIME_TYPE = 22;      // String
+    public static final int AUDIO_CODEC = 23;    // String
+    public static final int VIDEO_CODEC = 24;    // String
+
+    public static final int VIDEO_HEIGHT = 25;   // Integer
+    public static final int VIDEO_WIDTH = 26;    // Integer
+    public static final int NUM_TRACKS = 27;     // Integer
+    public static final int DRM_CRIPPLED = 28;   // Boolean
+    public static final int LAST_SYSTEM = 29;
+    public static final int FIRST_CUSTOM = 8092;
+
+    // Shorthands to set the MediaPlayer's metadata filter.
+    public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET;
+    public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY);
+
+    /**
+     * Helper class to hold a pair (time, text). Can be used to implement caption.
+     */
+    public class TimedText {
+        private Date mTime;
+        private String mText;
+        public TimedText(final Date time, final String text) {
+            mTime = time;
+            mText = text;
+        }
+        public String toString() {
+            StringBuilder res = new StringBuilder(80);
+            res.append(mTime).append(":").append(mText);
+            return res.toString();
+        }
+    }
+
+    /* package */ Metadata() {}
+
+    /**
+     * @return the number of element in this metadata set.
+     */
+    public int size() {
+        // FIXME: Implement.
+        return 0;
+    }
+
+    /**
+     * @return an iterator over the keys.
+     */
+    public Iterator<Integer> iterator() {
+        // FIXME: Implement.
+        return new java.util.HashSet<Integer>().iterator();
+    }
+
+    /**
+     * @return true if a value is present for the given key.
+     */
+    public boolean has(final int key) {
+        if (key <= ANY) {
+            throw new IllegalArgumentException("Invalid key: " + key);
+        }
+        if (LAST_SYSTEM <= key && key < FIRST_CUSTOM) {
+            throw new IllegalArgumentException("Key in reserved range: " + key);
+        }
+        // FIXME: Implement.
+        return true;
+    }
+
+    // Accessors
+    public String getString(final int key) {
+        // FIXME: Implement.
+        return new String();
+    }
+
+    public int getInt(final int key) {
+        // FIXME: Implement.
+        return 0;
+    }
+
+    public long getLong(final int key) {
+        // FIXME: Implement.
+        return 0;
+    }
+
+    public double getDouble(final int key) {
+        // FIXME: Implement.
+        return 0.0;
+    }
+
+    public byte[] getByteArray(final int key) {
+        return new byte[0];
+    }
+
+    public Bitmap getBitmap(final int key) {
+        // FIXME: Implement.
+        return null;
+    }
+
+    public Date getDate(final int key) {
+        // FIXME: Implement.
+        return new Date();
+    }
+
+    public TimedText getTimedText(final int key) {
+        // FIXME: Implement.
+        return new TimedText(new Date(0), "<missing>");
+    }
+}