blob: b325c5cf3f5677e10d851abde37e5c0e59233804 [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
Chong Zhangb55c5452020-06-26 14:32:12 -070069constexpr const char* kTestActivityName = "/com.android.tests.transcoding.MainActivity";
70
Chong Zhang66469272020-06-04 16:51:55 -070071static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
72 PermissionController pc;
73 uid = pc.getPackageUid(packageName, 0);
74 if (uid <= 0) {
75 ALOGE("Unknown package: '%s'", String8(packageName).string());
76 return BAD_VALUE;
77 }
78
79 if (userId < 0) {
80 ALOGE("Invalid user: %d", userId);
81 return BAD_VALUE;
82 }
83
84 uid = multiuser_get_uid(userId, uid);
85 return NO_ERROR;
86}
87
88struct ShellHelper {
89 static bool RunCmd(const std::string& cmdStr) {
90 int ret = system(cmdStr.c_str());
91 if (ret != 0) {
92 ALOGE("Failed to run cmd: %s, exitcode %d", cmdStr.c_str(), ret);
93 return false;
94 }
95 return true;
96 }
97
98 static bool Start(const char* packageName, const char* activityName) {
99 return RunCmd("am start -W " + std::string(packageName) + std::string(activityName) +
100 " &> /dev/null");
101 }
102
103 static bool Stop(const char* packageName) {
104 return RunCmd("am force-stop " + std::string(packageName));
105 }
106};
107
108struct EventTracker {
109 struct Event {
110 enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
111 int64_t clientId;
112 int32_t jobId;
113 };
114
115#define DECLARE_EVENT(action) \
116 static Event action(int32_t clientId, int32_t jobId) { \
117 return {Event::action, clientId, jobId}; \
118 }
119
120 DECLARE_EVENT(Start);
121 DECLARE_EVENT(Pause);
122 DECLARE_EVENT(Resume);
123 DECLARE_EVENT(Finished);
124 DECLARE_EVENT(Failed);
125
126 static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
127
128 static std::string toString(const Event& event) {
129 std::string eventStr;
130 switch (event.type) {
131 case Event::Start:
132 eventStr = "Start";
133 break;
134 case Event::Pause:
135 eventStr = "Pause";
136 break;
137 case Event::Resume:
138 eventStr = "Resume";
139 break;
140 case Event::Finished:
141 eventStr = "Finished";
142 break;
143 case Event::Failed:
144 eventStr = "Failed";
145 break;
146 default:
147 return "NoEvent";
148 }
149 return "job {" + std::to_string(event.clientId) + ", " + std::to_string(event.jobId) +
150 "}: " + eventStr;
151 }
152
153 // Pop 1 event from front, wait for up to timeoutUs if empty.
154 const Event& pop(int64_t timeoutUs = 0) {
155 std::unique_lock lock(mLock);
156
157 if (mEventQueue.empty() && timeoutUs > 0) {
158 mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
159 }
160
161 if (mEventQueue.empty()) {
162 mPoppedEvent = NoEvent;
163 } else {
164 mPoppedEvent = *mEventQueue.begin();
165 mEventQueue.pop_front();
166 }
167
168 return mPoppedEvent;
169 }
170
Chong Zhangf9077512020-09-21 21:02:06 -0700171 bool waitForSpecificEventAndPop(const Event& target, std::list<Event>* outEvents,
172 int64_t timeoutUs = 0) {
173 std::unique_lock lock(mLock);
174
175 auto startTime = std::chrono::system_clock::now();
176
177 std::list<Event>::iterator it;
178 while (((it = std::find(mEventQueue.begin(), mEventQueue.end(), target)) ==
179 mEventQueue.end()) &&
180 timeoutUs > 0) {
181 std::cv_status status = mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
182 if (status == std::cv_status::timeout) {
183 break;
184 }
185 std::chrono::microseconds elapsedTime = std::chrono::system_clock::now() - startTime;
186 timeoutUs -= elapsedTime.count();
187 }
188
189 if (it == mEventQueue.end()) {
190 return false;
191 }
192 *outEvents = std::list<Event>(mEventQueue.begin(), std::next(it));
193 mEventQueue.erase(mEventQueue.begin(), std::next(it));
194 return true;
195 }
196
Chong Zhang66469272020-06-04 16:51:55 -0700197 // Push 1 event to back.
Chong Zhangb55c5452020-06-26 14:32:12 -0700198 void append(const Event& event,
199 const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
Chong Zhang66469272020-06-04 16:51:55 -0700200 ALOGD("%s", toString(event).c_str());
201
202 std::unique_lock lock(mLock);
203
204 mEventQueue.push_back(event);
Chong Zhangb55c5452020-06-26 14:32:12 -0700205 mLastErr = err;
Chong Zhang66469272020-06-04 16:51:55 -0700206 mCondition.notify_one();
207 }
208
Chong Zhang98b8a372020-07-08 17:27:37 -0700209 void updateProgress(int progress) {
210 std::unique_lock lock(mLock);
211 mLastProgress = progress;
212 mUpdateCount++;
213 }
214
Chong Zhangf9077512020-09-21 21:02:06 -0700215 int getUpdateCount(int* lastProgress) {
Chong Zhang98b8a372020-07-08 17:27:37 -0700216 std::unique_lock lock(mLock);
217 *lastProgress = mLastProgress;
218 return mUpdateCount;
219 }
220
Chong Zhangb55c5452020-06-26 14:32:12 -0700221 TranscodingErrorCode getLastError() {
222 std::unique_lock lock(mLock);
223 return mLastErr;
224 }
225
Chong Zhang66469272020-06-04 16:51:55 -0700226private:
227 std::mutex mLock;
228 std::condition_variable mCondition;
229 Event mPoppedEvent;
230 std::list<Event> mEventQueue;
Chong Zhangb55c5452020-06-26 14:32:12 -0700231 TranscodingErrorCode mLastErr;
Chong Zhang98b8a372020-07-08 17:27:37 -0700232 int mUpdateCount = 0;
233 int mLastProgress = -1;
Chong Zhang66469272020-06-04 16:51:55 -0700234};
235
236// Operators for GTest macros.
237bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
238 return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.jobId == rhs.jobId;
239}
240
241std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
242 str << EventTracker::toString(v);
243 return str;
244}
245
246struct TestClientCallback : public BnTranscodingClientCallback, public EventTracker {
247 TestClientCallback(int32_t id) : mClientId(id) {
248 ALOGI("TestClientCallback %d Created", mClientId);
249 }
250
251 virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
252
253 Status openFileDescriptor(const std::string& in_fileUri, const std::string& in_mode,
254 ::ndk::ScopedFileDescriptor* _aidl_return) override {
255 ALOGD("@@@ openFileDescriptor: %s", in_fileUri.c_str());
256 int fd;
257 if (in_mode == "w" || in_mode == "rw") {
Chong Zhangb55c5452020-06-26 14:32:12 -0700258 int kOpenFlags;
259 if (in_mode == "w") {
260 // Write-only, create file if non-existent, truncate existing file.
261 kOpenFlags = O_WRONLY | O_CREAT | O_TRUNC;
262 } else {
263 // Read-Write, create if non-existent, no truncate (service will truncate if needed)
264 kOpenFlags = O_RDWR | O_CREAT;
265 }
Chong Zhang66469272020-06-04 16:51:55 -0700266 // User R+W permission.
267 constexpr int kFileMode = S_IRUSR | S_IWUSR;
268 fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
269 } else {
270 fd = open(in_fileUri.c_str(), O_RDONLY);
271 }
272 _aidl_return->set(fd);
273 return Status::ok();
274 }
275
276 Status onTranscodingStarted(int32_t in_jobId) override {
277 append(EventTracker::Start(mClientId, in_jobId));
278 return Status::ok();
279 }
280
281 Status onTranscodingPaused(int32_t in_jobId) override {
282 append(EventTracker::Pause(mClientId, in_jobId));
283 return Status::ok();
284 }
285
286 Status onTranscodingResumed(int32_t in_jobId) override {
287 append(EventTracker::Resume(mClientId, in_jobId));
288 return Status::ok();
289 }
290
291 Status onTranscodingFinished(
292 int32_t in_jobId,
293 const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
294 append(Finished(mClientId, in_jobId));
295 return Status::ok();
296 }
297
Chong Zhangb55c5452020-06-26 14:32:12 -0700298 Status onTranscodingFailed(int32_t in_jobId,
299 ::aidl::android::media::TranscodingErrorCode in_errorCode) override {
300 append(Failed(mClientId, in_jobId), in_errorCode);
Chong Zhang66469272020-06-04 16:51:55 -0700301 return Status::ok();
302 }
303
304 Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
305 int32_t /* in_newAwaitNumber */) override {
306 return Status::ok();
307 }
308
Chong Zhang98b8a372020-07-08 17:27:37 -0700309 Status onProgressUpdate(int32_t /* in_jobId */, int32_t in_progress) override {
310 updateProgress(in_progress);
Chong Zhang66469272020-06-04 16:51:55 -0700311 return Status::ok();
312 }
313
314 int32_t mClientId;
315};
316
317class MediaTranscodingServiceTestBase : public ::testing::Test {
318public:
319 MediaTranscodingServiceTestBase() { ALOGI("MediaTranscodingServiceTestBase created"); }
320
321 virtual ~MediaTranscodingServiceTestBase() {
322 ALOGI("MediaTranscodingServiceTestBase destroyed");
323 }
324
325 void SetUp() override {
326 // Need thread pool to receive callbacks, otherwise oneway callbacks are
327 // silently ignored.
328 ABinderProcess_startThreadPool();
329 ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
330 mService = IMediaTranscodingService::fromBinder(binder);
331 if (mService == nullptr) {
332 ALOGE("Failed to connect to the media.trascoding service.");
333 return;
334 }
335 mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(1));
336 mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(2));
337 mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(CLIENT(3));
338 }
339
340 std::shared_ptr<ITranscodingClient> registerOneClient(
341 const char* packageName, const std::shared_ptr<TestClientCallback>& callback,
342 uid_t defaultUid) {
343 uid_t uid;
344 if (getUidForPackage(String16(packageName), 0 /*userId*/, uid) != NO_ERROR) {
345 uid = defaultUid;
346 }
347
348 ALOGD("registering %s with uid %d", packageName, uid);
349
350 std::shared_ptr<ITranscodingClient> client;
351 Status status = mService->registerClient(callback, kClientName, packageName, uid,
352 kClientUseCallingPid, &client);
353 return status.isOk() ? client : nullptr;
354 }
355
356 void registerMultipleClients() {
357 // Register 3 clients.
358 mClient1 = registerOneClient(kClientPackageA, mClientCallback1, UID(1));
359 EXPECT_TRUE(mClient1 != nullptr);
360
361 mClient2 = registerOneClient(kClientPackageB, mClientCallback2, UID(2));
362 EXPECT_TRUE(mClient2 != nullptr);
363
364 mClient3 = registerOneClient(kClientPackageC, mClientCallback3, UID(3));
365 EXPECT_TRUE(mClient3 != nullptr);
366
367 // Check the number of clients.
368 int32_t numOfClients;
369 Status status = mService->getNumOfClients(&numOfClients);
370 EXPECT_TRUE(status.isOk());
371 EXPECT_EQ(3, numOfClients);
372 }
373
374 void unregisterMultipleClients() {
375 Status status;
376
377 // Unregister the clients.
378 status = mClient1->unregister();
379 EXPECT_TRUE(status.isOk());
380
381 status = mClient2->unregister();
382 EXPECT_TRUE(status.isOk());
383
384 status = mClient3->unregister();
385 EXPECT_TRUE(status.isOk());
386
387 // Check the number of clients.
388 int32_t numOfClients;
389 status = mService->getNumOfClients(&numOfClients);
390 EXPECT_TRUE(status.isOk());
391 EXPECT_EQ(0, numOfClients);
392 }
393
394 static constexpr bool success = true;
395 static constexpr bool fail = false;
396
397 template <bool expectation = success>
398 bool submit(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
399 const char* sourceFilePath, const char* destinationFilePath,
400 TranscodingJobPriority priority = TranscodingJobPriority::kNormal,
401 int bitrateBps = -1) {
402 constexpr bool shouldSucceed = (expectation == success);
403 bool result;
404 TranscodingRequestParcel request;
405 TranscodingJobParcel job;
406
407 request.sourceFilePath = sourceFilePath;
408 request.destinationFilePath = destinationFilePath;
409 request.priority = priority;
410 if (bitrateBps > 0) {
411 request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
412 request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
413 }
414 Status status = client->submitRequest(request, &job, &result);
415
416 EXPECT_TRUE(status.isOk());
417 EXPECT_EQ(result, shouldSucceed);
418 if (shouldSucceed) {
419 EXPECT_EQ(job.jobId, jobId);
420 }
421
422 return status.isOk() && (result == shouldSucceed) && (!shouldSucceed || job.jobId == jobId);
423 }
424
425 template <bool expectation = success>
426 bool cancel(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId) {
427 constexpr bool shouldSucceed = (expectation == success);
428 bool result;
429 Status status = client->cancelJob(jobId, &result);
430
431 EXPECT_TRUE(status.isOk());
432 EXPECT_EQ(result, shouldSucceed);
433
434 return status.isOk() && (result == shouldSucceed);
435 }
436
437 template <bool expectation = success>
438 bool getJob(const std::shared_ptr<ITranscodingClient>& client, int32_t jobId,
439 const char* sourceFilePath, const char* destinationFilePath) {
440 constexpr bool shouldSucceed = (expectation == success);
441 bool result;
442 TranscodingJobParcel job;
443 Status status = client->getJobWithId(jobId, &job, &result);
444
445 EXPECT_TRUE(status.isOk());
446 EXPECT_EQ(result, shouldSucceed);
447 if (shouldSucceed) {
448 EXPECT_EQ(job.jobId, jobId);
449 EXPECT_EQ(job.request.sourceFilePath, sourceFilePath);
450 }
451
452 return status.isOk() && (result == shouldSucceed) &&
453 (!shouldSucceed ||
454 (job.jobId == jobId && job.request.sourceFilePath == sourceFilePath &&
455 job.request.destinationFilePath == destinationFilePath));
456 }
457
458 void deleteFile(const char* path) { unlink(path); }
459
460 std::shared_ptr<IMediaTranscodingService> mService;
461 std::shared_ptr<TestClientCallback> mClientCallback1;
462 std::shared_ptr<TestClientCallback> mClientCallback2;
463 std::shared_ptr<TestClientCallback> mClientCallback3;
464 std::shared_ptr<ITranscodingClient> mClient1;
465 std::shared_ptr<ITranscodingClient> mClient2;
466 std::shared_ptr<ITranscodingClient> mClient3;
467 const char* mTestName;
468};
469
470} // namespace media
471} // namespace android