blob: 4259f54055794aba5be4aaa5086594531b7e7d91 [file] [log] [blame]
Chong Zhang66469272020-06-04 16:51:55 -07001/*
2 * Copyright (C) 2019 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// Unit Test for MediaTranscodingService.
18
19#include <aidl/android/media/BnTranscodingClientCallback.h>
20#include <aidl/android/media/IMediaTranscodingService.h>
21#include <aidl/android/media/ITranscodingClient.h>
22#include <aidl/android/media/ITranscodingClientCallback.h>
23#include <aidl/android/media/TranscodingJobParcel.h>
24#include <aidl/android/media/TranscodingJobPriority.h>
25#include <aidl/android/media/TranscodingRequestParcel.h>
26#include <android-base/logging.h>
27#include <android/binder_manager.h>
28#include <android/binder_process.h>
29#include <binder/PermissionController.h>
30#include <cutils/multiuser.h>
31#include <fcntl.h>
32#include <gtest/gtest.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <utils/Log.h>
36
37#include <iostream>
38#include <list>
39
40#include "SimulatedTranscoder.h"
41
42namespace android {
43
44namespace media {
45
46using Status = ::ndk::ScopedAStatus;
47using aidl::android::media::BnTranscodingClientCallback;
48using aidl::android::media::IMediaTranscodingService;
49using aidl::android::media::ITranscodingClient;
50using aidl::android::media::ITranscodingClientCallback;
51using aidl::android::media::TranscodingJobParcel;
52using aidl::android::media::TranscodingJobPriority;
53using aidl::android::media::TranscodingRequestParcel;
54using aidl::android::media::TranscodingVideoTrackFormat;
55
56constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
57
58constexpr uid_t kClientUid = 5000;
59#define UID(n) (kClientUid + (n))
60
61constexpr int32_t kClientId = 0;
62#define CLIENT(n) (kClientId + (n))
63
64constexpr const char* kClientName = "TestClient";
65constexpr const char* kClientPackageA = "com.android.tests.transcoding.testapp.A";
66constexpr const char* kClientPackageB = "com.android.tests.transcoding.testapp.B";
67constexpr const char* kClientPackageC = "com.android.tests.transcoding.testapp.C";
68
69static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
70 PermissionController pc;
71 uid = pc.getPackageUid(packageName, 0);
72 if (uid <= 0) {
73 ALOGE("Unknown package: '%s'", String8(packageName).string());
74 return BAD_VALUE;
75 }
76
77 if (userId < 0) {
78 ALOGE("Invalid user: %d", userId);
79 return BAD_VALUE;
80 }
81
82 uid = multiuser_get_uid(userId, uid);
83 return NO_ERROR;
84}
85
86struct ShellHelper {
87 static bool RunCmd(const std::string& cmdStr) {
88 int ret = system(cmdStr.c_str());
89 if (ret != 0) {
90 ALOGE("Failed to run cmd: %s, exitcode %d", cmdStr.c_str(), ret);
91 return false;
92 }
93 return true;
94 }
95
96 static bool Start(const char* packageName, const char* activityName) {
97 return RunCmd("am start -W " + std::string(packageName) + std::string(activityName) +
98 " &> /dev/null");
99 }
100
101 static bool Stop(const char* packageName) {
102 return RunCmd("am force-stop " + std::string(packageName));
103 }
104};
105
106struct EventTracker {
107 struct Event {
108 enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
109 int64_t clientId;
110 int32_t jobId;
111 };
112
113#define DECLARE_EVENT(action) \
114 static Event action(int32_t clientId, int32_t jobId) { \
115 return {Event::action, clientId, jobId}; \
116 }
117
118 DECLARE_EVENT(Start);
119 DECLARE_EVENT(Pause);
120 DECLARE_EVENT(Resume);
121 DECLARE_EVENT(Finished);
122 DECLARE_EVENT(Failed);
123
124 static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
125
126 static std::string toString(const Event& event) {
127 std::string eventStr;
128 switch (event.type) {
129 case Event::Start:
130 eventStr = "Start";
131 break;
132 case Event::Pause:
133 eventStr = "Pause";
134 break;
135 case Event::Resume:
136 eventStr = "Resume";
137 break;
138 case Event::Finished:
139 eventStr = "Finished";
140 break;
141 case Event::Failed:
142 eventStr = "Failed";
143 break;
144 default:
145 return "NoEvent";
146 }
147 return "job {" + std::to_string(event.clientId) + ", " + std::to_string(event.jobId) +
148 "}: " + eventStr;
149 }
150
151 // Pop 1 event from front, wait for up to timeoutUs if empty.
152 const Event& pop(int64_t timeoutUs = 0) {
153 std::unique_lock lock(mLock);
154
155 if (mEventQueue.empty() && timeoutUs > 0) {
156 mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
157 }
158
159 if (mEventQueue.empty()) {
160 mPoppedEvent = NoEvent;
161 } else {
162 mPoppedEvent = *mEventQueue.begin();
163 mEventQueue.pop_front();
164 }
165
166 return mPoppedEvent;
167 }
168
169 // Push 1 event to back.
170 void append(const Event& event) {
171 ALOGD("%s", toString(event).c_str());
172
173 std::unique_lock lock(mLock);
174
175 mEventQueue.push_back(event);
176 mCondition.notify_one();
177 }
178
179private:
180 std::mutex mLock;
181 std::condition_variable mCondition;
182 Event mPoppedEvent;
183 std::list<Event> mEventQueue;
184};
185
186// Operators for GTest macros.
187bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
188 return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.jobId == rhs.jobId;
189}
190
191std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
192 str << EventTracker::toString(v);
193 return str;
194}
195
196struct TestClientCallback : public BnTranscodingClientCallback, public EventTracker {
197 TestClientCallback(int32_t id) : mClientId(id) {
198 ALOGI("TestClientCallback %d Created", mClientId);
199 }
200
201 virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
202
203 Status openFileDescriptor(const std::string& in_fileUri, const std::string& in_mode,
204 ::ndk::ScopedFileDescriptor* _aidl_return) override {
205 ALOGD("@@@ openFileDescriptor: %s", in_fileUri.c_str());
206 int fd;
207 if (in_mode == "w" || in_mode == "rw") {
208 // Write-only, create file if non-existent, don't overwrite existing file.
209 constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
210 // User R+W permission.
211 constexpr int kFileMode = S_IRUSR | S_IWUSR;
212 fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
213 } else {
214 fd = open(in_fileUri.c_str(), O_RDONLY);
215 }
216 _aidl_return->set(fd);
217 return Status::ok();
218 }
219
220 Status onTranscodingStarted(int32_t in_jobId) override {
221 append(EventTracker::Start(mClientId, in_jobId));
222 return Status::ok();
223 }
224
225 Status onTranscodingPaused(int32_t in_jobId) override {
226 append(EventTracker::Pause(mClientId, in_jobId));
227 return Status::ok();
228 }
229
230 Status onTranscodingResumed(int32_t in_jobId) override {
231 append(EventTracker::Resume(mClientId, in_jobId));
232 return Status::ok();
233 }
234
235 Status onTranscodingFinished(
236 int32_t in_jobId,
237 const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
238 append(Finished(mClientId, in_jobId));
239 return Status::ok();
240 }
241
242 Status onTranscodingFailed(
243 int32_t in_jobId,
244 ::aidl::android::media::TranscodingErrorCode /* in_errorCode */) override {
245 append(Failed(mClientId, in_jobId));
246 return Status::ok();
247 }
248
249 Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
250 int32_t /* in_newAwaitNumber */) override {
251 return Status::ok();
252 }
253
254 Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
255 return Status::ok();
256 }
257
258 int32_t mClientId;
259};
260
261class MediaTranscodingServiceTestBase : public ::testing::Test {
262public:
263 MediaTranscodingServiceTestBase() { ALOGI("MediaTranscodingServiceTestBase created"); }
264
265 virtual ~MediaTranscodingServiceTestBase() {
266 ALOGI("MediaTranscodingServiceTestBase destroyed");
267 }
268
269 void SetUp() override {
270 // Need thread pool to receive callbacks, otherwise oneway callbacks are
271 // silently ignored.
272 ABinderProcess_startThreadPool();
273 ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
274 mService = IMediaTranscodingService::fromBinder(binder);
275 if (mService == nullptr) {
276 ALOGE("Failed to connect to the media.trascoding service.");
277 return;
278 }
279 mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(1));
280 mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(2));
281 mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(3));
282 }
283
284 std::shared_ptr<ITranscodingClient> registerOneClient(
285 const char* packageName, const std::shared_ptr<TestClientCallback>& callback,
286 uid_t defaultUid) {
287 uid_t uid;
288 if (getUidForPackage(String16(packageName), 0 /*userId*/, uid) != NO_ERROR) {
289 uid = defaultUid;
290 }
291
292 ALOGD("registering %s with uid %d", packageName, uid);
293
294 std::shared_ptr<ITranscodingClient> client;
295 Status status = mService->registerClient(callback, kClientName, packageName, uid,
296 kClientUseCallingPid, &client);
297 return status.isOk() ? client : nullptr;
298 }
299
300 void registerMultipleClients() {
301 // Register 3 clients.
302 mClient1 = registerOneClient(kClientPackageA, mClientCallback1, UID(1));
303 EXPECT_TRUE(mClient1 != nullptr);
304
305 mClient2 = registerOneClient(kClientPackageB, mClientCallback2, UID(2));
306 EXPECT_TRUE(mClient2 != nullptr);
307
308 mClient3 = registerOneClient(kClientPackageC, mClientCallback3, UID(3));
309 EXPECT_TRUE(mClient3 != nullptr);
310
311 // Check the number of clients.
312 int32_t numOfClients;
313 Status status = mService->getNumOfClients(&numOfClients);
314 EXPECT_TRUE(status.isOk());
315 EXPECT_EQ(3, numOfClients);
316 }
317
318 void unregisterMultipleClients() {
319 Status status;
320
321 // Unregister the clients.
322 status = mClient1->unregister();
323 EXPECT_TRUE(status.isOk());
324
325 status = mClient2->unregister();
326 EXPECT_TRUE(status.isOk());
327
328 status = mClient3->unregister();
329 EXPECT_TRUE(status.isOk());
330
331 // Check the number of clients.
332 int32_t numOfClients;
333 status = mService->getNumOfClients(&numOfClients);
334 EXPECT_TRUE(status.isOk());
335 EXPECT_EQ(0, numOfClients);
336 }
337
338 static constexpr bool success = true;
339 static constexpr bool fail = false;
340
341 template <bool expectation = success>
342 bool submit(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
343 const char* sourceFilePath, const char* destinationFilePath,
344 TranscodingJobPriority priority = TranscodingJobPriority::kNormal,
345 int bitrateBps = -1) {
346 constexpr bool shouldSucceed = (expectation == success);
347 bool result;
348 TranscodingRequestParcel request;
349 TranscodingJobParcel job;
350
351 request.sourceFilePath = sourceFilePath;
352 request.destinationFilePath = destinationFilePath;
353 request.priority = priority;
354 if (bitrateBps > 0) {
355 request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
356 request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
357 }
358 Status status = client->submitRequest(request, &job, &result);
359
360 EXPECT_TRUE(status.isOk());
361 EXPECT_EQ(result, shouldSucceed);
362 if (shouldSucceed) {
363 EXPECT_EQ(job.jobId, jobId);
364 }
365
366 return status.isOk() && (result == shouldSucceed) && (!shouldSucceed || job.jobId == jobId);
367 }
368
369 template <bool expectation = success>
370 bool cancel(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId) {
371 constexpr bool shouldSucceed = (expectation == success);
372 bool result;
373 Status status = client->cancelJob(jobId, &result);
374
375 EXPECT_TRUE(status.isOk());
376 EXPECT_EQ(result, shouldSucceed);
377
378 return status.isOk() && (result == shouldSucceed);
379 }
380
381 template <bool expectation = success>
382 bool getJob(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
383 const char* sourceFilePath, const char* destinationFilePath) {
384 constexpr bool shouldSucceed = (expectation == success);
385 bool result;
386 TranscodingJobParcel job;
387 Status status = client->getJobWithId(jobId, &job, &result);
388
389 EXPECT_TRUE(status.isOk());
390 EXPECT_EQ(result, shouldSucceed);
391 if (shouldSucceed) {
392 EXPECT_EQ(job.jobId, jobId);
393 EXPECT_EQ(job.request.sourceFilePath, sourceFilePath);
394 }
395
396 return status.isOk() && (result == shouldSucceed) &&
397 (!shouldSucceed ||
398 (job.jobId == jobId && job.request.sourceFilePath == sourceFilePath &&
399 job.request.destinationFilePath == destinationFilePath));
400 }
401
402 void deleteFile(const char* path) { unlink(path); }
403
404 std::shared_ptr<IMediaTranscodingService> mService;
405 std::shared_ptr<TestClientCallback> mClientCallback1;
406 std::shared_ptr<TestClientCallback> mClientCallback2;
407 std::shared_ptr<TestClientCallback> mClientCallback3;
408 std::shared_ptr<ITranscodingClient> mClient1;
409 std::shared_ptr<ITranscodingClient> mClient2;
410 std::shared_ptr<ITranscodingClient> mClient3;
411 const char* mTestName;
412};
413
414} // namespace media
415} // namespace android