| /* |
| * 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, const KeyedVector<String8, String8> *headers) { |
| 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, headers); |
| } |
| |
| // Internal cleanup. |
| status_t TestPlayerStub::resetInternal() |
| { |
| if(mUrl) { |
| free(mUrl); |
| mUrl = NULL; |
| } |
| mFilename = NULL; |
| mContentUrl = NULL; |
| |
| if (mPlayer) { |
| LOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is 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 |