• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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/TranscodingRequestParcel.h>
24 #include <aidl/android/media/TranscodingSessionParcel.h>
25 #include <aidl/android/media/TranscodingSessionPriority.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 
42 namespace android {
43 
44 namespace media {
45 
46 using Status = ::ndk::ScopedAStatus;
47 using aidl::android::media::BnTranscodingClientCallback;
48 using aidl::android::media::IMediaTranscodingService;
49 using aidl::android::media::ITranscodingClient;
50 using aidl::android::media::ITranscodingClientCallback;
51 using aidl::android::media::TranscodingRequestParcel;
52 using aidl::android::media::TranscodingSessionParcel;
53 using aidl::android::media::TranscodingSessionPriority;
54 using aidl::android::media::TranscodingTestConfig;
55 using aidl::android::media::TranscodingVideoTrackFormat;
56 
57 constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
58 
59 constexpr uid_t kClientUid = 5000;
60 #define UID(n) (kClientUid + (n))
61 
62 constexpr pid_t kClientPid = 10000;
63 #define PID(n) (kClientPid + (n))
64 
65 constexpr int32_t kClientId = 0;
66 #define CLIENT(n) (kClientId + (n))
67 
68 constexpr const char* kClientName = "TestClient";
69 constexpr const char* kClientPackageA = "com.android.tests.transcoding.testapp.A";
70 constexpr const char* kClientPackageB = "com.android.tests.transcoding.testapp.B";
71 constexpr const char* kClientPackageC = "com.android.tests.transcoding.testapp.C";
72 
73 constexpr const char* kTestActivityName = "/com.android.tests.transcoding.MainActivity";
74 
getUidForPackage(String16 packageName,userid_t userId,uid_t & uid)75 static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
76     PermissionController pc;
77     uid = pc.getPackageUid(packageName, 0);
78     if (uid <= 0) {
79         ALOGE("Unknown package: '%s'", String8(packageName).string());
80         return BAD_VALUE;
81     }
82 
83     if (userId < 0) {
84         ALOGE("Invalid user: %d", userId);
85         return BAD_VALUE;
86     }
87 
88     uid = multiuser_get_uid(userId, uid);
89     return NO_ERROR;
90 }
91 
92 struct ShellHelper {
RunCmdShellHelper93     static bool RunCmd(const std::string& cmdStr) {
94         int ret = system(cmdStr.c_str());
95         if (ret != 0) {
96             ALOGE("Failed to run cmd: %s, exitcode %d", cmdStr.c_str(), ret);
97             return false;
98         }
99         return true;
100     }
101 
StartShellHelper102     static bool Start(const char* packageName, const char* activityName) {
103         return RunCmd("am start -W " + std::string(packageName) + std::string(activityName) +
104                       " &> /dev/null");
105     }
106 
StopShellHelper107     static bool Stop(const char* packageName) {
108         return RunCmd("am force-stop " + std::string(packageName));
109     }
110 };
111 
112 struct EventTracker {
113     struct Event {
114         enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
115         int64_t clientId;
116         int32_t sessionId;
117     };
118 
119 #define DECLARE_EVENT(action)                                  \
120     static Event action(int32_t clientId, int32_t sessionId) { \
121         return {Event::action, clientId, sessionId};           \
122     }
123 
124     DECLARE_EVENT(Start);
125     DECLARE_EVENT(Pause);
126     DECLARE_EVENT(Resume);
127     DECLARE_EVENT(Finished);
128     DECLARE_EVENT(Failed);
129 
130     static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
131 
toStringEventTracker132     static std::string toString(const Event& event) {
133         std::string eventStr;
134         switch (event.type) {
135         case Event::Start:
136             eventStr = "Start";
137             break;
138         case Event::Pause:
139             eventStr = "Pause";
140             break;
141         case Event::Resume:
142             eventStr = "Resume";
143             break;
144         case Event::Finished:
145             eventStr = "Finished";
146             break;
147         case Event::Failed:
148             eventStr = "Failed";
149             break;
150         default:
151             return "NoEvent";
152         }
153         return "session {" + std::to_string(event.clientId) + ", " +
154                std::to_string(event.sessionId) + "}: " + eventStr;
155     }
156 
157     // Pop 1 event from front, wait for up to timeoutUs if empty.
158     const Event& pop(int64_t timeoutUs = 0) {
159         std::unique_lock lock(mLock);
160 
161         if (mEventQueue.empty() && timeoutUs > 0) {
162             mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
163         }
164 
165         if (mEventQueue.empty()) {
166             mPoppedEvent = NoEvent;
167         } else {
168             mPoppedEvent = *mEventQueue.begin();
169             mEventQueue.pop_front();
170         }
171 
172         return mPoppedEvent;
173     }
174 
175     bool waitForSpecificEventAndPop(const Event& target, std::list<Event>* outEvents,
176                                     int64_t timeoutUs = 0) {
177         std::unique_lock lock(mLock);
178 
179         auto startTime = std::chrono::system_clock::now();
180         int64_t remainingUs = timeoutUs;
181 
182         std::list<Event>::iterator it;
183         while (((it = std::find(mEventQueue.begin(), mEventQueue.end(), target)) ==
184                 mEventQueue.end()) &&
185                remainingUs > 0) {
186             std::cv_status status =
187                     mCondition.wait_for(lock, std::chrono::microseconds(remainingUs));
188             if (status == std::cv_status::timeout) {
189                 break;
190             }
191             std::chrono::microseconds elapsedTime = std::chrono::system_clock::now() - startTime;
192             remainingUs = timeoutUs - elapsedTime.count();
193         }
194 
195         if (it == mEventQueue.end()) {
196             return false;
197         }
198         *outEvents = std::list<Event>(mEventQueue.begin(), std::next(it));
199         mEventQueue.erase(mEventQueue.begin(), std::next(it));
200         return true;
201     }
202 
203     // Push 1 event to back.
204     void append(const Event& event,
205                 const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
206         ALOGD("%s", toString(event).c_str());
207 
208         std::unique_lock lock(mLock);
209 
210         mEventQueue.push_back(event);
211         if (err != TranscodingErrorCode::kNoError) {
212             mLastErrQueue.push_back(err);
213         }
214         mCondition.notify_one();
215     }
216 
updateProgressEventTracker217     void updateProgress(int progress) {
218         std::unique_lock lock(mLock);
219         mLastProgress = progress;
220         mUpdateCount++;
221     }
222 
getUpdateCountEventTracker223     int getUpdateCount(int* lastProgress) {
224         std::unique_lock lock(mLock);
225         *lastProgress = mLastProgress;
226         return mUpdateCount;
227     }
228 
getLastErrorEventTracker229     TranscodingErrorCode getLastError() {
230         std::unique_lock lock(mLock);
231         if (mLastErrQueue.empty()) {
232             return TranscodingErrorCode::kNoError;
233         }
234         TranscodingErrorCode err = mLastErrQueue.front();
235         mLastErrQueue.pop_front();
236         return err;
237     }
238 
239 private:
240     std::mutex mLock;
241     std::condition_variable mCondition;
242     Event mPoppedEvent;
243     std::list<Event> mEventQueue;
244     std::list<TranscodingErrorCode> mLastErrQueue;
245     int mUpdateCount = 0;
246     int mLastProgress = -1;
247 };
248 
249 // Operators for GTest macros.
250 bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
251     return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId;
252 }
253 
254 std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
255     str << EventTracker::toString(v);
256     return str;
257 }
258 
259 static constexpr bool success = true;
260 static constexpr bool fail = false;
261 
262 struct TestClientCallback : public BnTranscodingClientCallback,
263                             public EventTracker,
264                             public std::enable_shared_from_this<TestClientCallback> {
TestClientCallbackTestClientCallback265     TestClientCallback(const char* packageName, int32_t id)
266           : mClientId(id), mClientPid(PID(id)), mClientUid(UID(id)), mPackageName(packageName) {
267         ALOGI("TestClientCallback %d created: pid %d, uid %d", id, PID(id), UID(id));
268 
269         // Use package uid if that's available.
270         uid_t packageUid;
271         if (getUidForPackage(String16(packageName), 0 /*userId*/, packageUid) == NO_ERROR) {
272             mClientUid = packageUid;
273         }
274     }
275 
~TestClientCallbackTestClientCallback276     virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
277 
openFileDescriptorTestClientCallback278     Status openFileDescriptor(const std::string& in_fileUri, const std::string& in_mode,
279                               ::ndk::ScopedFileDescriptor* _aidl_return) override {
280         ALOGD("@@@ openFileDescriptor: %s", in_fileUri.c_str());
281         int fd;
282         if (in_mode == "w" || in_mode == "rw") {
283             int kOpenFlags;
284             if (in_mode == "w") {
285                 // Write-only, create file if non-existent, truncate existing file.
286                 kOpenFlags = O_WRONLY | O_CREAT | O_TRUNC;
287             } else {
288                 // Read-Write, create if non-existent, no truncate (service will truncate if needed)
289                 kOpenFlags = O_RDWR | O_CREAT;
290             }
291             // User R+W permission.
292             constexpr int kFileMode = S_IRUSR | S_IWUSR;
293             fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
294         } else {
295             fd = open(in_fileUri.c_str(), O_RDONLY);
296         }
297         _aidl_return->set(fd);
298         return Status::ok();
299     }
300 
onTranscodingStartedTestClientCallback301     Status onTranscodingStarted(int32_t in_sessionId) override {
302         append(EventTracker::Start(mClientId, in_sessionId));
303         return Status::ok();
304     }
305 
onTranscodingPausedTestClientCallback306     Status onTranscodingPaused(int32_t in_sessionId) override {
307         append(EventTracker::Pause(mClientId, in_sessionId));
308         return Status::ok();
309     }
310 
onTranscodingResumedTestClientCallback311     Status onTranscodingResumed(int32_t in_sessionId) override {
312         append(EventTracker::Resume(mClientId, in_sessionId));
313         return Status::ok();
314     }
315 
onTranscodingFinishedTestClientCallback316     Status onTranscodingFinished(
317             int32_t in_sessionId,
318             const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
319         append(Finished(mClientId, in_sessionId));
320         return Status::ok();
321     }
322 
onTranscodingFailedTestClientCallback323     Status onTranscodingFailed(int32_t in_sessionId,
324                                ::aidl::android::media::TranscodingErrorCode in_errorCode) override {
325         append(Failed(mClientId, in_sessionId), in_errorCode);
326         return Status::ok();
327     }
328 
onAwaitNumberOfSessionsChangedTestClientCallback329     Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
330                                           int32_t /* in_oldAwaitNumber */,
331                                           int32_t /* in_newAwaitNumber */) override {
332         return Status::ok();
333     }
334 
onProgressUpdateTestClientCallback335     Status onProgressUpdate(int32_t /* in_sessionId */, int32_t in_progress) override {
336         updateProgress(in_progress);
337         return Status::ok();
338     }
339 
registerClientTestClientCallback340     Status registerClient(const char* packageName,
341                           const std::shared_ptr<IMediaTranscodingService>& service) {
342         // Override the default uid if the package uid is found.
343         uid_t uid;
344         if (getUidForPackage(String16(packageName), 0 /*userId*/, uid) == NO_ERROR) {
345             mClientUid = uid;
346         }
347 
348         ALOGD("registering %s with uid %d", packageName, mClientUid);
349 
350         std::shared_ptr<ITranscodingClient> client;
351         Status status =
352                 service->registerClient(shared_from_this(), kClientName, packageName, &client);
353 
354         mClient = status.isOk() ? client : nullptr;
355         return status;
356     }
357 
unregisterClientTestClientCallback358     Status unregisterClient() {
359         Status status;
360         if (mClient != nullptr) {
361             status = mClient->unregister();
362             mClient = nullptr;
363         }
364         return status;
365     }
366 
367     template <bool expectation = success>
368     bool submit(int32_t sessionId, const char* sourceFilePath, const char* destinationFilePath,
369                 TranscodingSessionPriority priority = TranscodingSessionPriority::kNormal,
370                 int bitrateBps = -1, int overridePid = -1, int overrideUid = -1,
371                 int sessionDurationMs = -1) {
372         constexpr bool shouldSucceed = (expectation == success);
373         bool result;
374         TranscodingRequestParcel request;
375         TranscodingSessionParcel session;
376 
377         request.sourceFilePath = sourceFilePath;
378         request.destinationFilePath = destinationFilePath;
379         request.priority = priority;
380         request.clientPid = (overridePid == -1) ? mClientPid : overridePid;
381         request.clientUid = (overrideUid == -1) ? mClientUid : overrideUid;
382         request.clientPackageName = (overrideUid == -1) ? mPackageName : "";
383         if (bitrateBps > 0) {
384             request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
385             request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
386         }
387         if (sessionDurationMs > 0) {
388             request.isForTesting = true;
389             request.testConfig.emplace(TranscodingTestConfig());
390             request.testConfig->processingTotalTimeMs = sessionDurationMs;
391         }
392         Status status = mClient->submitRequest(request, &session, &result);
393 
394         EXPECT_TRUE(status.isOk());
395         EXPECT_EQ(result, shouldSucceed);
396         if (shouldSucceed) {
397             EXPECT_EQ(session.sessionId, sessionId);
398         }
399 
400         return status.isOk() && (result == shouldSucceed) &&
401                (!shouldSucceed || session.sessionId == sessionId);
402     }
403 
404     template <bool expectation = success>
cancelTestClientCallback405     bool cancel(int32_t sessionId) {
406         constexpr bool shouldSucceed = (expectation == success);
407         bool result;
408         Status status = mClient->cancelSession(sessionId, &result);
409 
410         EXPECT_TRUE(status.isOk());
411         EXPECT_EQ(result, shouldSucceed);
412 
413         return status.isOk() && (result == shouldSucceed);
414     }
415 
416     template <bool expectation = success>
getSessionTestClientCallback417     bool getSession(int32_t sessionId, const char* sourceFilePath,
418                     const char* destinationFilePath) {
419         constexpr bool shouldSucceed = (expectation == success);
420         bool result;
421         TranscodingSessionParcel session;
422         Status status = mClient->getSessionWithId(sessionId, &session, &result);
423 
424         EXPECT_TRUE(status.isOk());
425         EXPECT_EQ(result, shouldSucceed);
426         if (shouldSucceed) {
427             EXPECT_EQ(session.sessionId, sessionId);
428             EXPECT_EQ(session.request.sourceFilePath, sourceFilePath);
429         }
430 
431         return status.isOk() && (result == shouldSucceed) &&
432                (!shouldSucceed || (session.sessionId == sessionId &&
433                                    session.request.sourceFilePath == sourceFilePath &&
434                                    session.request.destinationFilePath == destinationFilePath));
435     }
436 
437     template <bool expectation = success>
addClientUidTestClientCallback438     bool addClientUid(int32_t sessionId, uid_t clientUid) {
439         constexpr bool shouldSucceed = (expectation == success);
440         bool result;
441         Status status = mClient->addClientUid(sessionId, clientUid, &result);
442 
443         EXPECT_TRUE(status.isOk());
444         EXPECT_EQ(result, shouldSucceed);
445 
446         return status.isOk() && (result == shouldSucceed);
447     }
448 
449     template <bool expectation = success>
getClientUidsTestClientCallback450     bool getClientUids(int32_t sessionId, std::vector<int32_t>* clientUids) {
451         constexpr bool shouldSucceed = (expectation == success);
452         std::optional<std::vector<int32_t>> aidl_return;
453         Status status = mClient->getClientUids(sessionId, &aidl_return);
454 
455         EXPECT_TRUE(status.isOk());
456         bool success = (aidl_return != std::nullopt);
457         if (success) {
458             *clientUids = *aidl_return;
459         }
460         EXPECT_EQ(success, shouldSucceed);
461 
462         return status.isOk() && (success == shouldSucceed);
463     }
464 
465     int32_t mClientId;
466     pid_t mClientPid;
467     uid_t mClientUid;
468     std::string mPackageName;
469     std::shared_ptr<ITranscodingClient> mClient;
470 };
471 
472 class MediaTranscodingServiceTestBase : public ::testing::Test {
473 public:
MediaTranscodingServiceTestBase()474     MediaTranscodingServiceTestBase() { ALOGI("MediaTranscodingServiceTestBase created"); }
475 
~MediaTranscodingServiceTestBase()476     virtual ~MediaTranscodingServiceTestBase() {
477         ALOGI("MediaTranscodingServiceTestBase destroyed");
478     }
479 
SetUp()480     void SetUp() override {
481         // Need thread pool to receive callbacks, otherwise oneway callbacks are
482         // silently ignored.
483         ABinderProcess_startThreadPool();
484         ::ndk::SpAIBinder binder(AServiceManager_waitForService("media.transcoding"));
485         mService = IMediaTranscodingService::fromBinder(binder);
486         if (mService == nullptr) {
487             ALOGE("Failed to connect to the media.trascoding service.");
488             return;
489         }
490 
491         mClient1 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageA, 1);
492         mClient2 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageB, 2);
493         mClient3 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageC, 3);
494     }
495 
registerOneClient(const std::shared_ptr<TestClientCallback> & callback)496     Status registerOneClient(const std::shared_ptr<TestClientCallback>& callback) {
497         ALOGD("registering %s with uid %d", callback->mPackageName.c_str(), callback->mClientUid);
498 
499         std::shared_ptr<ITranscodingClient> client;
500         Status status =
501                 mService->registerClient(callback, kClientName, callback->mPackageName, &client);
502 
503         if (status.isOk()) {
504             callback->mClient = client;
505         } else {
506             callback->mClient = nullptr;
507         }
508         return status;
509     }
510 
registerMultipleClients()511     void registerMultipleClients() {
512         // Register 3 clients.
513         EXPECT_TRUE(registerOneClient(mClient1).isOk());
514         EXPECT_TRUE(registerOneClient(mClient2).isOk());
515         EXPECT_TRUE(registerOneClient(mClient3).isOk());
516 
517         // Check the number of clients.
518         int32_t numOfClients;
519         Status status = mService->getNumOfClients(&numOfClients);
520         EXPECT_TRUE(status.isOk());
521         EXPECT_GE(numOfClients, 3);
522     }
523 
unregisterMultipleClients()524     void unregisterMultipleClients() {
525         // Unregister the clients.
526         EXPECT_TRUE(mClient1->unregisterClient().isOk());
527         EXPECT_TRUE(mClient2->unregisterClient().isOk());
528         EXPECT_TRUE(mClient3->unregisterClient().isOk());
529     }
530 
prepareOutputFile(const char * path)531     const char* prepareOutputFile(const char* path) {
532         deleteFile(path);
533         return path;
534     }
535 
deleteFile(const char * path)536     void deleteFile(const char* path) { unlink(path); }
537 
dismissKeyguard()538     void dismissKeyguard() {
539         EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
540         EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
541     }
542 
stopAppPackages()543     void stopAppPackages() {
544         EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
545         EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
546         EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
547     }
548 
549     std::shared_ptr<IMediaTranscodingService> mService;
550     std::shared_ptr<TestClientCallback> mClient1;
551     std::shared_ptr<TestClientCallback> mClient2;
552     std::shared_ptr<TestClientCallback> mClient3;
553 };
554 
555 }  // namespace media
556 }  // namespace android
557