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'.
isTestBuild()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:'
isTestUrl(const char * url)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
TestPlayerStub()62 TestPlayerStub::TestPlayerStub()
63 :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
64 mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
65 mPlayer(NULL) { }
66
~TestPlayerStub()67 TestPlayerStub::~TestPlayerStub()
68 {
69 resetInternal();
70 }
71
initCheck()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).
parseUrl()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.
setDataSource(const char * url,const KeyedVector<String8,String8> * headers)115 status_t TestPlayerStub::setDataSource(
116 const char *url, const KeyedVector<String8, String8> *headers) {
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 ALOGE("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 ALOGE("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 ALOGE("dlsym for deletePlayer failed %s", err);
160 resetInternal();
161 return UNKNOWN_ERROR;
162 }
163
164 mPlayer = (*mNewPlayer)();
165 return mPlayer->setDataSource(mContentUrl, headers);
166 }
167
168 // Internal cleanup.
resetInternal()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) {
179 ALOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
180 (*mDeletePlayer)(mPlayer);
181 mPlayer = NULL;
182 }
183
184 if (mHandle) {
185 ::dlclose(mHandle);
186 mHandle = NULL;
187 }
188 return OK;
189 }
190
canBeUsed(const char * url)191 /* static */ bool TestPlayerStub::canBeUsed(const char *url)
192 {
193 return isTestBuild() && isTestUrl(url);
194 }
195
196 } // namespace android
197