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/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