Nicolas Catania | 8f5fcab | 2009-07-13 14:37:49 -0700 | [diff] [blame] | 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. |
Andreas Huber | 2564300 | 2010-01-28 11:19:57 -0800 | [diff] [blame] | 115 | status_t TestPlayerStub::setDataSource( |
| 116 | const char *url, const KeyedVector<String8, String8> *headers) { |
Nicolas Catania | 8f5fcab | 2009-07-13 14:37:49 -0700 | [diff] [blame] | 117 | if (!isTestUrl(url) || NULL != mHandle) { |
| 118 | return INVALID_OPERATION; |
| 119 | } |
| 120 | |
| 121 | mUrl = strdup(url); |
| 122 | |
| 123 | status_t status = parseUrl(); |
| 124 | |
| 125 | if (OK != status) { |
| 126 | resetInternal(); |
| 127 | return status; |
| 128 | } |
| 129 | |
| 130 | ::dlerror(); // Clears any pending error. |
| 131 | |
| 132 | // Load the test player from the url. dlopen will fail if the lib |
| 133 | // is not there. dls are under /system/lib |
| 134 | // None of the entry points should be NULL. |
| 135 | mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL); |
| 136 | if (!mHandle) { |
| 137 | LOGE("dlopen failed: %s", ::dlerror()); |
| 138 | resetInternal(); |
| 139 | return UNKNOWN_ERROR; |
| 140 | } |
| 141 | |
| 142 | // Load the 2 entry points to create and delete instances. |
| 143 | const char *err; |
| 144 | mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle, |
| 145 | "newPlayer")); |
| 146 | err = ::dlerror(); |
| 147 | if (err || mNewPlayer == NULL) { |
| 148 | // if err is NULL the string <null> is inserted in the logs => |
| 149 | // mNewPlayer was NULL. |
| 150 | LOGE("dlsym for newPlayer failed %s", err); |
| 151 | resetInternal(); |
| 152 | return UNKNOWN_ERROR; |
| 153 | } |
| 154 | |
| 155 | mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle, |
| 156 | "deletePlayer")); |
| 157 | err = ::dlerror(); |
| 158 | if (err || mDeletePlayer == NULL) { |
| 159 | LOGE("dlsym for deletePlayer failed %s", err); |
| 160 | resetInternal(); |
| 161 | return UNKNOWN_ERROR; |
| 162 | } |
| 163 | |
| 164 | mPlayer = (*mNewPlayer)(); |
Andreas Huber | 2564300 | 2010-01-28 11:19:57 -0800 | [diff] [blame] | 165 | return mPlayer->setDataSource(mContentUrl, headers); |
Nicolas Catania | 8f5fcab | 2009-07-13 14:37:49 -0700 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | // Internal cleanup. |
| 169 | status_t TestPlayerStub::resetInternal() |
| 170 | { |
| 171 | if(mUrl) { |
| 172 | free(mUrl); |
| 173 | mUrl = NULL; |
| 174 | } |
| 175 | mFilename = NULL; |
| 176 | mContentUrl = NULL; |
| 177 | |
| 178 | if (mPlayer) { |
Patrick Scott | c5e3d73 | 2009-10-29 08:31:36 -0400 | [diff] [blame] | 179 | LOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null"); |
Nicolas Catania | 8f5fcab | 2009-07-13 14:37:49 -0700 | [diff] [blame] | 180 | (*mDeletePlayer)(mPlayer); |
| 181 | mPlayer = NULL; |
| 182 | } |
| 183 | |
| 184 | if (mHandle) { |
| 185 | ::dlclose(mHandle); |
| 186 | mHandle = NULL; |
| 187 | } |
| 188 | return OK; |
| 189 | } |
| 190 | |
| 191 | /* static */ bool TestPlayerStub::canBeUsed(const char *url) |
| 192 | { |
| 193 | return isTestBuild() && isTestUrl(url); |
| 194 | } |
| 195 | |
| 196 | } // namespace android |