Merge change 24178 into eclair

* changes:
  The qcom decoder requires that the output buffers be allocated by the component for hardware accelerated display to work.
diff --git a/api/current.xml b/api/current.xml
index ce2ac27..83b201c 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -165277,6 +165277,51 @@
 </parameter>
 </method>
 </class>
+<class name="PluginStub"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PluginStub"
+ type="android.webkit.PluginStub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="npp" type="int">
+</parameter>
+</constructor>
+<method name="getEmbeddedView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getFullScreenView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+</class>
 <class name="SslErrorHandler"
  extends="android.os.Handler"
  abstract="false"
diff --git a/core/java/android/webkit/PluginActivity.java b/core/java/android/webkit/PluginActivity.java
new file mode 100644
index 0000000..f9e3080
--- /dev/null
+++ b/core/java/android/webkit/PluginActivity.java
@@ -0,0 +1,67 @@
+/*
+ * 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.webkit;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This activity  is invoked when a plugin elects to go into full screen mode.
+ * @hide
+ */
+public class PluginActivity extends Activity {
+
+    /* package */ static final String INTENT_EXTRA_PACKAGE_NAME =
+            "android.webkit.plugin.PACKAGE_NAME";
+    /* package */ static final String INTENT_EXTRA_CLASS_NAME =
+            "android.webkit.plugin.CLASS_NAME";
+    /* package */ static final String INTENT_EXTRA_NPP_INSTANCE =
+            "android.webkit.plugin.NPP_INSTANCE";
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Intent intent = getIntent();
+        if (intent == null) {
+            // No intent means no class to lookup.
+            finish();
+        }
+        final String packageName =
+                intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME);
+        final String className = intent.getStringExtra(INTENT_EXTRA_CLASS_NAME);
+        final int npp = intent.getIntExtra(INTENT_EXTRA_NPP_INSTANCE, -1);
+        // Retrieve the PluginStub implemented in packageName.className
+        PluginStub stub =
+                PluginUtil.getPluginStub(this, packageName, className, npp);
+
+        if (stub != null) {
+            View pluginView = stub.getFullScreenView(this);
+            if (pluginView != null) {
+                setContentView(pluginView);
+            } else {
+                // No custom full-sreen view returned by the plugin, odd but
+                // just in case, finish the activity.
+                finish();
+            }
+        } else {
+            finish();
+        }
+    }
+}
diff --git a/core/java/android/webkit/PluginStub.java b/core/java/android/webkit/PluginStub.java
new file mode 100644
index 0000000..c24da8d
--- /dev/null
+++ b/core/java/android/webkit/PluginStub.java
@@ -0,0 +1,50 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * This abstract class is used to implement plugins in a WebView. A plugin
+ * package may extend this class and implement the abstract functions to create
+ * embedded or fullscreeen views displayed in a WebView. The PluginStub
+ * implementation will be provided the same NPP instance that is created
+ * through the native interface.
+ */
+public abstract class PluginStub {
+    /**
+     * Construct a new PluginStub implementation for the given NPP instance.
+     * @param npp The native NPP instance.
+     */
+    public PluginStub(int npp) { }
+
+    /**
+     * Return a custom embedded view to draw the plugin.
+     * @param context The current application's Context.
+     * @return A custom View that will be managed by WebView.
+     */
+    public abstract View getEmbeddedView(Context context);
+
+    /**
+     * Return a custom full-screen view to be displayed when the user requests
+     * a plugin display as full-screen. Note that the application may choose not
+     * to display this View as completely full-screen.
+     * @param context The current application's Context.
+     * @return A custom View that will be managed by the application.
+     */
+    public abstract View getFullScreenView(Context context);
+}
diff --git a/core/java/android/webkit/PluginUtil.java b/core/java/android/webkit/PluginUtil.java
new file mode 100644
index 0000000..c0a7375
--- /dev/null
+++ b/core/java/android/webkit/PluginUtil.java
@@ -0,0 +1,62 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+class PluginUtil {
+
+    private static final String LOGTAG = "PluginUtil";
+
+    /**
+     * 
+     * @param packageName the name of the apk where the class can be found
+     * @param className the fully qualified name of a subclass of PluginStub
+     */
+    /* package */
+    static PluginStub getPluginStub(Context context, String packageName,
+            String className, int NPP) {
+        try {
+            Context pluginContext = context.createPackageContext(packageName,
+                    Context.CONTEXT_INCLUDE_CODE |
+                    Context.CONTEXT_IGNORE_SECURITY);
+            ClassLoader pluginCL = pluginContext.getClassLoader();
+
+            Class<?> stubClass =
+                    pluginCL.loadClass(className);
+            Constructor<?> stubConstructor =
+                    stubClass.getConstructor(int.class);
+            Object stubObject = stubConstructor.newInstance(NPP);
+
+            if (stubObject instanceof PluginStub) {
+                return (PluginStub) stubObject;
+            } else {
+                Log.e(LOGTAG, "The plugin class is not of type PluginStub");
+            }
+        } catch (Exception e) {
+            // Any number of things could have happened. Log the exception and
+            // return null. Careful not to use Log.e(LOGTAG, "String", e)
+            // because that reports the exception to the checkin service.
+            Log.e(LOGTAG, Log.getStackTraceString(e));
+        }
+        return null;
+    }
+}
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index b178836..e228357 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -44,6 +44,28 @@
 {
 public:
     virtual             ~MediaMetadataRetrieverInterface() {}
+
+    // @param mode The intended mode of operations:
+    // can be any of the following:
+    // METADATA_MODE_NOOP: Experimental - just add and remove data source.
+    // METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.
+    // METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.
+    // METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame
+    //     capture and meta data retrieval.
+    virtual status_t    setMode(int mode) {
+                            if (mode < METADATA_MODE_NOOP ||
+                                mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) {
+                                return BAD_VALUE;
+                            }
+                            return NO_ERROR;
+                        }
+
+    virtual status_t    getMode(int* mode) const { *mode = mMode; return NO_ERROR; }
+    virtual VideoFrame* captureFrame() { return NULL; }
+    virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
+    virtual const char* extractMetadata(int keyCode) { return NULL; }
+
+    uint32_t mMode;
 };
 
 }; // namespace android
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 9ea2775..cfc205c 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -56,6 +56,18 @@
     // Add more here...
 };
 
+// The intended mode of operations:$
+// METADATA_MODE_NOOP: Experimental - just add and remove data source.$
+// METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.$
+// METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.$
+// METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame capture
+//   and meta data retrieval.$
+enum {
+    METADATA_MODE_NOOP                                 = 0x00,
+    METADATA_MODE_FRAME_CAPTURE_ONLY                   = 0x01,
+    METADATA_MODE_METADATA_RETRIEVAL_ONLY              = 0x02,
+    METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL = 0x03
+};
 
 class MediaMetadataRetriever: public RefBase
 {
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 84f858c..59ecde6 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -13,6 +13,8 @@
     StagefrightPlayer.cpp       \
     TestPlayerStub.cpp          \
     VorbisPlayer.cpp            \
+    VorbisMetadataRetriever.cpp \
+    MidiMetadataRetriever.cpp \
     MidiFile.cpp
 
 ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index eeb4e49..8998f10 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -611,7 +611,7 @@
     return PV_PLAYER;
 }
 
-static player_type getPlayerType(int fd, int64_t offset, int64_t length)
+player_type getPlayerType(int fd, int64_t offset, int64_t length)
 {
     char buf[20];
     lseek(fd, offset, SEEK_SET);
@@ -644,7 +644,7 @@
     return getDefaultPlayerType();
 }
 
-static player_type getPlayerType(const char* url)
+player_type getPlayerType(const char* url)
 {
     if (TestPlayerStub::canBeUsed(url)) {
         return TEST_PLAYER;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index ba8d9a8..b34421d 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -34,12 +34,15 @@
 #include <media/MediaPlayerInterface.h>
 #include <media/PVMetadataRetriever.h>
 #include <private/media/VideoFrame.h>
-
+#include "VorbisMetadataRetriever.h"
+#include "MidiMetadataRetriever.h"
 #include "MetadataRetrieverClient.h"
 
-
 namespace android {
 
+extern player_type getPlayerType(const char* url);
+extern player_type getPlayerType(int fd, int64_t offset, int64_t length);
+
 MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid)
 {
     LOGV("MetadataRetrieverClient constructor pid(%d)", pid);
@@ -90,6 +93,36 @@
     IPCThreadState::self()->flushCommands();
 }
 
+static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType)
+{
+    sp<MediaMetadataRetrieverBase> p;
+    switch (playerType) {
+#ifndef NO_OPENCORE
+        case PV_PLAYER:
+            LOGV("create pv metadata retriever");
+            p = new PVMetadataRetriever();
+            break;
+#endif
+        case VORBIS_PLAYER:
+            LOGV("create vorbis metadata retriever");
+            p = new VorbisMetadataRetriever();
+            break;
+        case SONIVOX_PLAYER:
+            LOGV("create midi metadata retriever");
+            p = new MidiMetadataRetriever();
+            break;
+        default:
+            // TODO:
+            // support for STAGEFRIGHT_PLAYER and TEST_PLAYER
+            LOGE("player type %d is not supported",  playerType);
+            break;
+    }
+    if (p == NULL) {
+        LOGE("failed to create a retriever object");
+    }
+    return p;
+}
+
 status_t MetadataRetrieverClient::setDataSource(const char *url)
 {
     LOGV("setDataSource(%s)", url);
@@ -97,11 +130,13 @@
     if (url == NULL) {
         return UNKNOWN_ERROR;
     }
-    if (mRetriever == NULL) {
-        LOGE("retriever is not initialized");
-        return NO_INIT;
-    }
-    return mRetriever->setDataSource(url);
+    player_type playerType = getPlayerType(url);
+    LOGV("player type = %d", playerType);
+    sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+    if (p == NULL) return NO_INIT;
+    status_t ret = p->setDataSource(url);
+    if (ret == NO_ERROR) mRetriever = p;
+    return ret;
 }
 
 status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length)
@@ -118,7 +153,7 @@
     int ret = fstat(fd, &sb);
     if (ret != 0) {
         LOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
-        return UNKNOWN_ERROR;
+        return BAD_VALUE;
     }
     LOGV("st_dev  = %llu", sb.st_dev);
     LOGV("st_mode = %u", sb.st_mode);
@@ -129,13 +164,22 @@
     if (offset >= sb.st_size) {
         LOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size);
         ::close(fd);
-        return UNKNOWN_ERROR;
+        return BAD_VALUE;
     }
     if (offset + length > sb.st_size) {
         length = sb.st_size - offset;
-        LOGE("calculated length = %lld", length);
+        LOGV("calculated length = %lld", length);
     }
-    status_t status = mRetriever->setDataSource(fd, offset, length);
+
+    player_type playerType = getPlayerType(fd, offset, length);
+    LOGV("player type = %d", playerType);
+    sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+    if (p == NULL) {
+        ::close(fd);
+        return NO_INIT;
+    }
+    status_t status = p->setDataSource(fd, offset, length);
+    if (status == NO_ERROR) mRetriever = p;
     ::close(fd);
     return status;
 }
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
new file mode 100644
index 0000000..3795b7b
--- /dev/null
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
@@ -0,0 +1,91 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MidiMetadataRetriever"
+#include <utils/Log.h>
+
+#include "MidiMetadataRetriever.h"
+#include <media/mediametadataretriever.h>
+
+namespace android {
+
+static status_t ERROR_NOT_OPEN = -1;
+static status_t ERROR_OPEN_FAILED = -2;
+static status_t ERROR_EAS_FAILURE = -3;
+static status_t ERROR_ALLOCATE_FAILED = -4;
+
+void MidiMetadataRetriever::clearMetadataValues()
+{
+    LOGV("clearMetadataValues");
+    mMetadataValues[0][0] = '\0';
+}
+
+status_t MidiMetadataRetriever::setDataSource(const char *url)
+{
+    LOGV("setDataSource: %s", url? url: "NULL pointer");
+    Mutex::Autolock lock(mLock);
+    clearMetadataValues();
+    if (mMidiPlayer == 0) {
+        mMidiPlayer = new MidiFile();
+    }
+    return mMidiPlayer->setDataSource(url);
+}
+
+status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
+{
+    LOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length);
+    Mutex::Autolock lock(mLock);
+    clearMetadataValues();
+    if (mMidiPlayer == 0) {
+        mMidiPlayer = new MidiFile();
+    }
+    return mMidiPlayer->setDataSource(fd, offset, length);;
+}
+
+const char* MidiMetadataRetriever::extractMetadata(int keyCode)
+{
+    LOGV("extractMetdata: key(%d)", keyCode);
+    Mutex::Autolock lock(mLock);
+    if (mMidiPlayer == 0 || mMidiPlayer->initCheck() != NO_ERROR) {
+        LOGE("Midi player is not initialized yet");
+        return NULL;
+    }
+    switch (keyCode) {
+    case METADATA_KEY_DURATION:
+        {
+            if (mMetadataValues[0][0] == '\0') {
+                int duration = -1;
+                if (mMidiPlayer->getDuration(&duration) != NO_ERROR) {
+                    LOGE("failed to get duration");
+                    return NULL;
+                }
+                snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration);
+            }
+
+            LOGV("duration: %s ms", mMetadataValues[0]);
+            return mMetadataValues[0];
+        }
+    default:
+        LOGE("Unsupported key code (%d)", keyCode);
+        return NULL;
+    }
+    return NULL;
+}
+
+};
+
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h
new file mode 100644
index 0000000..73ff347
--- /dev/null
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.h
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_MIDIMETADATARETRIEVER_H
+#define ANDROID_MIDIMETADATARETRIEVER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include "MidiFile.h"
+
+namespace android {
+
+class MidiMetadataRetriever : public MediaMetadataRetrieverInterface {
+public:
+                                   MidiMetadataRetriever() {}
+                                   ~MidiMetadataRetriever() {}
+
+    virtual status_t                setDataSource(const char *url);
+    virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
+    virtual const char*             extractMetadata(int keyCode);
+
+private:
+    static const uint32_t MAX_METADATA_STRING_LENGTH = 128;
+    void clearMetadataValues();
+
+    Mutex               mLock;
+    sp<MidiFile>        mMidiPlayer;
+    char                mMetadataValues[1][MAX_METADATA_STRING_LENGTH];
+};
+
+}; // namespace android
+
+#endif // ANDROID_MIDIMETADATARETRIEVER_H
diff --git a/media/libmediaplayerservice/VorbisMetadataRetriever.cpp b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
new file mode 100644
index 0000000..e981678
--- /dev/null
+++ b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
@@ -0,0 +1,86 @@
+/*
+**
+** Copyright 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisMetadataRetriever"
+#include <utils/Log.h>
+
+#include "VorbisMetadataRetriever.h"
+#include <media/mediametadataretriever.h>
+#
+
+namespace android {
+
+void VorbisMetadataRetriever::clearMetadataValues()
+{
+    LOGV("cleearMetadataValues");
+    mMetadataValues[0][0] = '\0';
+}
+
+status_t VorbisMetadataRetriever::setDataSource(const char *url)
+{
+    LOGV("setDataSource: url(%s)", url? url: "NULL pointer");
+    Mutex::Autolock lock(mLock);
+    clearMetadataValues();
+    if (mVorbisPlayer == 0) {
+        mVorbisPlayer = new VorbisPlayer();
+    }
+    return mVorbisPlayer->setDataSource(url);
+}
+
+status_t VorbisMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
+{
+    LOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length);
+    Mutex::Autolock lock(mLock);
+    clearMetadataValues();
+    if (mVorbisPlayer == 0) {
+        mVorbisPlayer = new VorbisPlayer();
+    }
+    return mVorbisPlayer->setDataSource(fd, offset, length);
+}
+
+const char* VorbisMetadataRetriever::extractMetadata(int keyCode)
+{
+    LOGV("extractMetadata: key(%d)", keyCode);
+    Mutex::Autolock lock(mLock);
+    if (mVorbisPlayer == 0 || mVorbisPlayer->initCheck() != NO_ERROR) {
+        LOGE("no vorbis player is initialized yet");
+        return NULL;
+    }
+    switch (keyCode) {
+    case METADATA_KEY_DURATION:
+        {
+            if (mMetadataValues[0][0] == '\0') {
+                int duration = -1;
+                if (mVorbisPlayer->getDuration(&duration) != NO_ERROR) {
+                    LOGE("failed to get duration");
+                    return NULL;
+                }
+                snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration);
+            }
+            LOGV("duration: %s ms", mMetadataValues[0]);
+            return mMetadataValues[0];
+        }
+    default:
+        LOGE("Unsupported key code (%d)", keyCode);
+        return NULL;
+    }
+    return NULL;
+}
+
+};
+
diff --git a/media/libmediaplayerservice/VorbisMetadataRetriever.h b/media/libmediaplayerservice/VorbisMetadataRetriever.h
new file mode 100644
index 0000000..1c57fe3
--- /dev/null
+++ b/media/libmediaplayerservice/VorbisMetadataRetriever.h
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_VORBISMETADATARETRIEVER_H
+#define ANDROID_VORBISMETADATARETRIEVER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include "VorbisPlayer.h"
+
+namespace android {
+
+class VorbisMetadataRetriever : public MediaMetadataRetrieverInterface {
+public:
+                                   VorbisMetadataRetriever() {}
+                                   ~VorbisMetadataRetriever() {}
+
+    virtual status_t                setDataSource(const char *url);
+    virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
+    virtual const char*             extractMetadata(int keyCode);
+
+private:
+    static const uint32_t MAX_METADATA_STRING_LENGTH = 128;
+    void clearMetadataValues();
+
+    Mutex               mLock;
+    sp<VorbisPlayer>    mVorbisPlayer;
+    char                mMetadataValues[1][MAX_METADATA_STRING_LENGTH];
+};
+
+}; // namespace android
+
+#endif // ANDROID_VORBISMETADATARETRIEVER_H