New test player stub to load mock native players.

Added a new class TestPlayerStub that takes a magic url in the setDataSource call.
Based on the value of the url, the stub is going to load a DL and create the concrete
player used during the test.
After these initialization steps TestPlayerStub is just a wrapper.

Added a new functional test MediaPlayerInvokeTest to demonstrate how a new
mock player to test the invoke method can be loaded.

Added a new mock player for the invoke test: invoke_mock_media_player.cpp.
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 6978b3d..f74ef3a 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -10,6 +10,7 @@
     MediaRecorderClient.cpp \
     MediaPlayerService.cpp \
     MetadataRetrieverClient.cpp \
+    TestPlayerStub.cpp \
     VorbisPlayer.cpp \
     MidiFile.cpp
 
@@ -28,6 +29,10 @@
     libmedia \
     libandroid_runtime
 
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
 LOCAL_C_INCLUDES := external/tremor/Tremor \
 	$(call include-path-for, graphics corecg) \
 	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index c575f6c..02327d8 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 
 #include <cutils/atomic.h>
-#include <cutils/properties.h>
+#include <cutils/properties.h> // for property_get
 
 #include <utils/misc.h>
 
@@ -58,6 +58,8 @@
 #include "MidiFile.h"
 #include "VorbisPlayer.h"
 #include <media/PVPlayer.h>
+#include "TestPlayerStub.h"
+
 #if USE_STAGEFRIGHT
 #include "StagefrightPlayer.h"
 #endif
@@ -68,6 +70,8 @@
 #include <media/IOMX.h>
 #endif
 
+
+
 /* desktop Linux needs a little help with gettid() */
 #if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
 #define __KERNEL__
@@ -656,6 +660,10 @@
 
 static player_type getPlayerType(const char* url)
 {
+    if (TestPlayerStub::canBeUsed(url)) {
+        return TEST_PLAYER;
+    }
+
     // use MidiFile for MIDI extensions
     int lenURL = strlen(url);
     for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -706,6 +714,10 @@
                     "Should not be here, stagefright player not enabled.");
             break;
 #endif
+        case TEST_PLAYER:
+            LOGV("Create Test Player stub");
+            p = new TestPlayerStub();
+            break;
     }
     if (p != NULL) {
         if (p->initCheck() == NO_ERROR) {
@@ -770,7 +782,11 @@
         // now set data source
         LOGV(" setDataSource");
         mStatus = p->setDataSource(url);
-        if (mStatus == NO_ERROR) mPlayer = p;
+        if (mStatus == NO_ERROR) {
+            mPlayer = p;
+        } else {
+            LOGE("  error: %d", mStatus);
+        }
         return mStatus;
     }
 }
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
new file mode 100644
index 0000000..8627708
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TestPlayerStub"
+#include "utils/Log.h"
+
+#include "TestPlayerStub.h"
+
+#include <dlfcn.h>  // for dlopen/dlclose
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h>  // for status_t
+
+#include "media/MediaPlayerInterface.h"
+
+
+namespace {
+using android::status_t;
+using android::MediaPlayerBase;
+
+const char *kTestUrlScheme = "test:";
+const char *kUrlParam = "url=";
+
+const char *kBuildTypePropName = "ro.build.type";
+const char *kEngBuild = "eng";
+const char *kTestBuild = "test";
+
+// @return true if the current build is 'eng' or 'test'.
+bool isTestBuild()
+{
+    char prop[PROPERTY_VALUE_MAX] = { '\0', };
+
+    property_get(kBuildTypePropName, prop, '\0');
+    return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
+}
+
+// @return true if the url scheme is 'test:'
+bool isTestUrl(const char *url)
+{
+    return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
+}
+
+}  // anonymous namespace
+
+namespace android {
+
+TestPlayerStub::TestPlayerStub()
+    :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
+     mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
+     mPlayer(NULL) { }
+
+TestPlayerStub::~TestPlayerStub()
+{
+    resetInternal();
+}
+
+status_t TestPlayerStub::initCheck()
+{
+    return isTestBuild() ? OK : INVALID_OPERATION;
+}
+
+// Parse mUrl to get:
+// * The library to be dlopened.
+// * The url to be passed to the real setDataSource impl.
+//
+// mUrl is expected to be in following format:
+//
+// test:<name of the .so>?url=<url for setDataSource>
+//
+// The value of the url parameter is treated as a string (no
+// unescaping of illegal charaters).
+status_t TestPlayerStub::parseUrl()
+{
+    if (strlen(mUrl) < strlen(kTestUrlScheme)) {
+        resetInternal();
+        return BAD_VALUE;
+    }
+
+    char *i = mUrl + strlen(kTestUrlScheme);
+
+    mFilename = i;
+
+    while (*i != '\0' && *i != '?') {
+        ++i;
+    }
+
+    if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
+        resetInternal();
+        return BAD_VALUE;
+    }
+    *i = '\0';  // replace '?' to nul-terminate mFilename
+
+    mContentUrl = i + 1 + strlen(kUrlParam);
+    return OK;
+}
+
+// Load the dynamic library.
+// Create the test player.
+// Call setDataSource on the test player with the url in param.
+status_t TestPlayerStub::setDataSource(const char *url)
+{
+    if (!isTestUrl(url) || NULL != mHandle) {
+        return INVALID_OPERATION;
+    }
+
+    mUrl = strdup(url);
+
+    status_t status = parseUrl();
+
+    if (OK != status) {
+        resetInternal();
+        return status;
+    }
+
+    ::dlerror();  // Clears any pending error.
+
+    // Load the test player from the url. dlopen will fail if the lib
+    // is not there. dls are under /system/lib
+    // None of the entry points should be NULL.
+    mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
+    if (!mHandle) {
+        LOGE("dlopen failed: %s", ::dlerror());
+        resetInternal();
+        return UNKNOWN_ERROR;
+    }
+
+    // Load the 2 entry points to create and delete instances.
+    const char *err;
+    mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
+                                                    "newPlayer"));
+    err = ::dlerror();
+    if (err || mNewPlayer == NULL) {
+        // if err is NULL the string <null> is inserted in the logs =>
+        // mNewPlayer was NULL.
+        LOGE("dlsym for newPlayer failed %s", err);
+        resetInternal();
+        return UNKNOWN_ERROR;
+    }
+
+    mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
+                                                          "deletePlayer"));
+    err = ::dlerror();
+    if (err || mDeletePlayer == NULL) {
+        LOGE("dlsym for deletePlayer failed %s", err);
+        resetInternal();
+        return UNKNOWN_ERROR;
+    }
+
+    mPlayer = (*mNewPlayer)();
+    return mPlayer->setDataSource(mContentUrl);
+}
+
+// Internal cleanup.
+status_t TestPlayerStub::resetInternal()
+{
+    if(mUrl) {
+        free(mUrl);
+        mUrl = NULL;
+    }
+    mFilename = NULL;
+    mContentUrl = NULL;
+
+    if (mPlayer) {
+        LOG_ASSERT(mDeletePlayer != NULL);
+        (*mDeletePlayer)(mPlayer);
+        mPlayer = NULL;
+    }
+
+    if (mHandle) {
+        ::dlclose(mHandle);
+        mHandle = NULL;
+    }
+    return OK;
+}
+
+/* static */ bool TestPlayerStub::canBeUsed(const char *url)
+{
+    return isTestBuild() && isTestUrl(url);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
new file mode 100644
index 0000000..80d53a8
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+
+#include <media/MediaPlayerInterface.h>
+#include <utils/Errors.h>
+
+namespace android {
+class MediaPlayerBase;  // in media/MediaPlayerInterface.h
+
+// Wrapper around a test media player that gets dynamically loaded.
+//
+// The URL passed to setDataSource has this format:
+//
+//   test:<name of the .so>?url=<url for the real setDataSource impl.>
+//
+// e.g:
+//   test:invoke_test_media_player.so?url=http://youtube.com/
+//   test:invoke_test_media_player.so?url=speedtest
+//
+// TestPlayerStub::setDataSource loads the library in the test url. 2
+// entry points with C linkage are expected. One to create the test
+// player and one to destroy it.
+//
+// extern "C" android::MediaPlayerBase* newPlayer();
+// extern "C" android::status_t deletePlayer(android::MediaPlayerBase *p);
+//
+// Once the test player has been loaded, its setDataSource
+// implementation is called with the value of the 'url' parameter.
+//
+// typical usage in a java test:
+// ============================
+//
+//  MediaPlayer p = new MediaPlayer();
+//  p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com");
+//  p.prepare();
+//  ...
+//  p.release();
+
+class TestPlayerStub : public MediaPlayerInterface {
+  public:
+    typedef MediaPlayerBase* (*NEW_PLAYER)();
+    typedef status_t (*DELETE_PLAYER)(MediaPlayerBase *);
+
+    TestPlayerStub();
+    virtual ~TestPlayerStub();
+
+    // Called right after the constructor. Check if the current build
+    // allows test players.
+    virtual status_t initCheck();
+
+    // @param url Should be a test url. See class comment.
+    virtual status_t setDataSource(const char* url);
+
+    // Test player for a file descriptor source is not supported.
+    virtual status_t setDataSource(int, int64_t, int64_t)  {
+        return INVALID_OPERATION;
+    }
+
+
+    // All the methods below wrap the mPlayer instance.
+    virtual status_t setVideoSurface(const android::sp<android::ISurface>& s)  {
+        return mPlayer->setVideoSurface(s);
+    }
+    virtual status_t prepare() {return mPlayer->prepare();}
+    virtual status_t prepareAsync()  {return mPlayer->prepareAsync();}
+    virtual status_t start()  {return mPlayer->start();}
+    virtual status_t stop()  {return mPlayer->stop();}
+    virtual status_t pause()  {return mPlayer->pause();}
+    virtual bool isPlaying() {return mPlayer->isPlaying();}
+    virtual status_t seekTo(int msec) {return mPlayer->seekTo(msec);}
+    virtual status_t getCurrentPosition(int *p)  {
+        return mPlayer->getCurrentPosition(p);
+    }
+    virtual status_t getDuration(int *d)  {return mPlayer->getDuration(d);}
+    virtual status_t reset() {return mPlayer->reset();}
+    virtual status_t setLooping(int b)  {return mPlayer->setLooping(b);}
+    virtual player_type playerType() {return mPlayer->playerType();}
+    virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
+        return mPlayer->invoke(in, out);
+    }
+
+
+    // @return true if the current build is 'eng' or 'test' and the
+    //              url's scheme is 'test:'
+    static bool canBeUsed(const char *url);
+
+  private:
+    // Release the player, dlclose the library.
+    status_t resetInternal();
+    status_t parseUrl();
+
+    char *mUrl;                // test:foo.so?url=http://bar
+    char *mFilename;           // foo.so
+    char *mContentUrl;         // http://bar
+    void *mHandle;             // returned by dlopen
+    NEW_PLAYER    mNewPlayer;
+    DELETE_PLAYER mDeletePlayer;
+    MediaPlayerBase *mPlayer;  // wrapped player
+};
+
+}  // namespace android
+
+#endif