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