Home | History | Annotate | Download | only in libmediaplayerservice
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "TestPlayerStub"
     19 #include "utils/Log.h"
     20 
     21 #include "TestPlayerStub.h"
     22 
     23 #include <dlfcn.h>  // for dlopen/dlclose
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <cutils/properties.h>
     27 #include <utils/Errors.h>  // for status_t
     28 
     29 #include "media/MediaPlayerInterface.h"
     30 
     31 
     32 namespace {
     33 using android::status_t;
     34 using android::MediaPlayerBase;
     35 
     36 const char *kTestUrlScheme = "test:";
     37 const char *kUrlParam = "url=";
     38 
     39 const char *kBuildTypePropName = "ro.build.type";
     40 const char *kEngBuild = "eng";
     41 const char *kTestBuild = "test";
     42 
     43 // @return true if the current build is 'eng' or 'test'.
     44 bool isTestBuild()
     45 {
     46     char prop[PROPERTY_VALUE_MAX] = { '\0', };
     47 
     48     property_get(kBuildTypePropName, prop, '\0');
     49     return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
     50 }
     51 
     52 // @return true if the url scheme is 'test:'
     53 bool isTestUrl(const char *url)
     54 {
     55     return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
     56 }
     57 
     58 }  // anonymous namespace
     59 
     60 namespace android {
     61 
     62 TestPlayerStub::TestPlayerStub()
     63     :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
     64      mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
     65      mPlayer(NULL) { }
     66 
     67 TestPlayerStub::~TestPlayerStub()
     68 {
     69     resetInternal();
     70 }
     71 
     72 status_t TestPlayerStub::initCheck()
     73 {
     74     return isTestBuild() ? OK : INVALID_OPERATION;
     75 }
     76 
     77 // Parse mUrl to get:
     78 // * The library to be dlopened.
     79 // * The url to be passed to the real setDataSource impl.
     80 //
     81 // mUrl is expected to be in following format:
     82 //
     83 // test:<name of the .so>?url=<url for setDataSource>
     84 //
     85 // The value of the url parameter is treated as a string (no
     86 // unescaping of illegal charaters).
     87 status_t TestPlayerStub::parseUrl()
     88 {
     89     if (strlen(mUrl) < strlen(kTestUrlScheme)) {
     90         resetInternal();
     91         return BAD_VALUE;
     92     }
     93 
     94     char *i = mUrl + strlen(kTestUrlScheme);
     95 
     96     mFilename = i;
     97 
     98     while (*i != '\0' && *i != '?') {
     99         ++i;
    100     }
    101 
    102     if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
    103         resetInternal();
    104         return BAD_VALUE;
    105     }
    106     *i = '\0';  // replace '?' to nul-terminate mFilename
    107 
    108     mContentUrl = i + 1 + strlen(kUrlParam);
    109     return OK;
    110 }
    111 
    112 // Load the dynamic library.
    113 // Create the test player.
    114 // Call setDataSource on the test player with the url in param.
    115 status_t TestPlayerStub::setDataSource(
    116         const sp<IMediaHTTPService> &httpService,
    117         const char *url,
    118         const KeyedVector<String8, String8> *headers) {
    119     if (!isTestUrl(url) || NULL != mHandle) {
    120         return INVALID_OPERATION;
    121     }
    122 
    123     mUrl = strdup(url);
    124 
    125     status_t status = parseUrl();
    126 
    127     if (OK != status) {
    128         resetInternal();
    129         return status;
    130     }
    131 
    132     ::dlerror();  // Clears any pending error.
    133 
    134     // Load the test player from the url. dlopen will fail if the lib
    135     // is not there. dls are under /system/lib
    136     // None of the entry points should be NULL.
    137     mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
    138     if (!mHandle) {
    139         ALOGE("dlopen failed: %s", ::dlerror());
    140         resetInternal();
    141         return UNKNOWN_ERROR;
    142     }
    143 
    144     // Load the 2 entry points to create and delete instances.
    145     const char *err;
    146     mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
    147                                                     "newPlayer"));
    148     err = ::dlerror();
    149     if (err || mNewPlayer == NULL) {
    150         // if err is NULL the string <null> is inserted in the logs =>
    151         // mNewPlayer was NULL.
    152         ALOGE("dlsym for newPlayer failed %s", err);
    153         resetInternal();
    154         return UNKNOWN_ERROR;
    155     }
    156 
    157     mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
    158                                                           "deletePlayer"));
    159     err = ::dlerror();
    160     if (err || mDeletePlayer == NULL) {
    161         ALOGE("dlsym for deletePlayer failed %s", err);
    162         resetInternal();
    163         return UNKNOWN_ERROR;
    164     }
    165 
    166     mPlayer = (*mNewPlayer)();
    167     return mPlayer->setDataSource(httpService, mContentUrl, headers);
    168 }
    169 
    170 // Internal cleanup.
    171 status_t TestPlayerStub::resetInternal()
    172 {
    173     if(mUrl) {
    174         free(mUrl);
    175         mUrl = NULL;
    176     }
    177     mFilename = NULL;
    178     mContentUrl = NULL;
    179 
    180     if (mPlayer) {
    181         ALOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
    182         (*mDeletePlayer)(mPlayer);
    183         mPlayer = NULL;
    184     }
    185 
    186     if (mHandle) {
    187         ::dlclose(mHandle);
    188         mHandle = NULL;
    189     }
    190     return OK;
    191 }
    192 
    193 /* static */ bool TestPlayerStub::canBeUsed(const char *url)
    194 {
    195     return isTestBuild() && isTestUrl(url);
    196 }
    197 
    198 }  // namespace android
    199