1 /*
2  * Copyright (C) 2020 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 #include <future>
18 #include <thread>
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 
23 #include <gui/Surface.h>
24 #include <mediadrm/ICrypto.h>
25 #include <media/stagefright/CodecBase.h>
26 #include <media/stagefright/MediaCodec.h>
27 #include <media/stagefright/MediaCodecListWriter.h>
28 #include <media/MediaCodecInfo.h>
29 
30 #include "MediaTestHelper.h"
31 
32 namespace android {
33 
34 class MockBufferChannel : public BufferChannelBase {
35 public:
36     ~MockBufferChannel() override = default;
37 
38     MOCK_METHOD(void, setCrypto, (const sp<ICrypto> &crypto), (override));
39     MOCK_METHOD(void, setDescrambler, (const sp<IDescrambler> &descrambler), (override));
40     MOCK_METHOD(status_t, queueInputBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
41     MOCK_METHOD(status_t, queueSecureInputBuffer,
42             (const sp<MediaCodecBuffer> &buffer,
43              bool secure,
44              const uint8_t *key,
45              const uint8_t *iv,
46              CryptoPlugin::Mode mode,
47              CryptoPlugin::Pattern pattern,
48              const CryptoPlugin::SubSample *subSamples,
49              size_t numSubSamples,
50              AString *errorDetailMsg),
51             (override));
52     MOCK_METHOD(status_t, attachBuffer,
53             (const std::shared_ptr<C2Buffer> &c2Buffer, const sp<MediaCodecBuffer> &buffer),
54             (override));
55     MOCK_METHOD(status_t, attachEncryptedBuffer,
56             (const sp<hardware::HidlMemory> &memory,
57              bool secure,
58              const uint8_t *key,
59              const uint8_t *iv,
60              CryptoPlugin::Mode mode,
61              CryptoPlugin::Pattern pattern,
62              size_t offset,
63              const CryptoPlugin::SubSample *subSamples,
64              size_t numSubSamples,
65              const sp<MediaCodecBuffer> &buffer),
66             (override));
67     MOCK_METHOD(status_t, renderOutputBuffer,
68             (const sp<MediaCodecBuffer> &buffer, int64_t timestampNs),
69             (override));
70     MOCK_METHOD(status_t, discardBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
71     MOCK_METHOD(void, getInputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
72     MOCK_METHOD(void, getOutputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
73 };
74 
75 class MockCodec : public CodecBase {
76 public:
MockCodec(std::function<void (const std::shared_ptr<MockBufferChannel> &)> mock)77     MockCodec(std::function<void(const std::shared_ptr<MockBufferChannel> &)> mock) {
78         mMockBufferChannel = std::make_shared<MockBufferChannel>();
79         mock(mMockBufferChannel);
80     }
81     ~MockCodec() override = default;
82 
83     MOCK_METHOD(void, initiateAllocateComponent, (const sp<AMessage> &msg), (override));
84     MOCK_METHOD(void, initiateConfigureComponent, (const sp<AMessage> &msg), (override));
85     MOCK_METHOD(void, initiateCreateInputSurface, (), (override));
86     MOCK_METHOD(void, initiateSetInputSurface, (const sp<PersistentSurface> &surface), (override));
87     MOCK_METHOD(void, initiateStart, (), (override));
88     MOCK_METHOD(void, initiateShutdown, (bool keepComponentAllocated), (override));
89     MOCK_METHOD(void, onMessageReceived, (const sp<AMessage> &msg), (override));
90     MOCK_METHOD(status_t, setSurface, (const sp<Surface> &surface), (override));
91     MOCK_METHOD(void, signalFlush, (), (override));
92     MOCK_METHOD(void, signalResume, (), (override));
93     MOCK_METHOD(void, signalRequestIDRFrame, (), (override));
94     MOCK_METHOD(void, signalSetParameters, (const sp<AMessage> &msg), (override));
95     MOCK_METHOD(void, signalEndOfInputStream, (), (override));
96 
getBufferChannel()97     std::shared_ptr<BufferChannelBase> getBufferChannel() override {
98         return mMockBufferChannel;
99     }
100 
callback()101     const std::unique_ptr<CodecCallback> &callback() {
102         return mCallback;
103     }
104 
105     std::shared_ptr<MockBufferChannel> mMockBufferChannel;
106 };
107 
108 class Counter {
109 public:
110     Counter() = default;
Counter(int32_t initCount)111     explicit Counter(int32_t initCount) : mCount(initCount) {}
112     ~Counter() = default;
113 
advance()114     int32_t advance() {
115         std::unique_lock<std::mutex> lock(mMutex);
116         ++mCount;
117         mCondition.notify_all();
118         return mCount;
119     }
120 
121     template <typename Rep, typename Period, typename ...Args>
waitFor(const std::chrono::duration<Rep,Period> & duration,Args...values)122     int32_t waitFor(const std::chrono::duration<Rep, Period> &duration, Args... values) {
123         std::initializer_list<int32_t> list = {values...};
124         std::unique_lock<std::mutex> lock(mMutex);
125         mCondition.wait_for(
126                 lock,
127                 duration,
128                 [&list, this]{
129                     return std::find(list.begin(), list.end(), mCount) != list.end();
130                 });
131         return mCount;
132     }
133 
134     template <typename ...Args>
wait(Args...values)135     int32_t wait(Args... values) {
136         std::initializer_list<int32_t> list = {values...};
137         std::unique_lock<std::mutex> lock(mMutex);
138         mCondition.wait(
139                 lock,
140                 [&list, this]{
141                     return std::find(list.begin(), list.end(), mCount) != list.end();
142                 });
143         return mCount;
144     }
145 
146 private:
147     std::mutex mMutex;
148     std::condition_variable mCondition;
149     int32_t mCount = 0;
150 };
151 
152 }  // namespace android
153 
154 using namespace android;
155 using ::testing::_;
156 
SetupMediaCodec(const AString & owner,const AString & codecName,const AString & mediaType,const sp<ALooper> & looper,std::function<sp<CodecBase> (const AString & name,const char * owner)> getCodecBase)157 static sp<MediaCodec> SetupMediaCodec(
158         const AString &owner,
159         const AString &codecName,
160         const AString &mediaType,
161         const sp<ALooper> &looper,
162         std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase) {
163     std::shared_ptr<MediaCodecListWriter> listWriter =
164         MediaTestHelper::CreateCodecListWriter();
165     std::unique_ptr<MediaCodecInfoWriter> infoWriter = listWriter->addMediaCodecInfo();
166     infoWriter->setName(codecName.c_str());
167     infoWriter->setOwner(owner.c_str());
168     infoWriter->addMediaType(mediaType.c_str());
169     std::vector<sp<MediaCodecInfo>> codecInfos;
170     MediaTestHelper::WriteCodecInfos(listWriter, &codecInfos);
171     std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo =
172         [codecInfos](const AString &name, sp<MediaCodecInfo> *info) -> status_t {
173             auto it = std::find_if(
174                     codecInfos.begin(), codecInfos.end(),
175                     [&name](const sp<MediaCodecInfo> &info) {
176                         return name.equalsIgnoreCase(info->getCodecName());
177                     });
178 
179             *info = (it == codecInfos.end()) ? nullptr : *it;
180             return (*info) ? OK : NAME_NOT_FOUND;
181         };
182 
183     looper->start();
184     return MediaTestHelper::CreateCodec(
185             codecName, looper, getCodecBase, getCodecInfo);
186 }
187 
TEST(MediaCodecTest,ReclaimReleaseRace)188 TEST(MediaCodecTest, ReclaimReleaseRace) {
189     // Test scenario:
190     //
191     // 1) ResourceManager thread calls reclaim(), message posted to
192     //    MediaCodec looper thread.
193     // 2) MediaCodec looper thread calls initiateShutdown(), shutdown being
194     //    handled at the component thread.
195     // 3) Client thread calls release(), message posted to & handle at
196     //    MediaCodec looper thread.
197     // 4) MediaCodec looper thread may call initiateShutdown().
198     // 5) initiateShutdown() from 2) is handled at onReleaseComplete() event
199     //    posted to MediaCodec looper thread.
200     // 6) If called, initiateShutdown() from 4) is handled and
201     //    onReleaseComplete() event posted to MediaCodec looper thread.
202 
203     static const AString kCodecName{"test.codec"};
204     static const AString kCodecOwner{"nobody"};
205     static const AString kMediaType{"video/x-test"};
206 
207     enum {
208         kInit,
209         kShutdownFromReclaimReceived,
210         kReleaseCalled,
211     };
212     Counter counter{kInit};
213     sp<MockCodec> mockCodec;
214     std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
215         [&mockCodec, &counter](const AString &, const char *) {
216             mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
217                 // No mock setup, as we don't expect any buffer operations
218                 // in this scenario.
219             });
220             ON_CALL(*mockCodec, initiateAllocateComponent(_))
221                 .WillByDefault([mockCodec](const sp<AMessage> &) {
222                     mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
223                 });
224             ON_CALL(*mockCodec, initiateShutdown(_))
225                 .WillByDefault([mockCodec, &counter](bool) {
226                     int32_t stage = counter.wait(kInit, kReleaseCalled);
227                     if (stage == kInit) {
228                         // Mark that 2) happened, so test can proceed to 3)
229                         counter.advance();
230                     } else if (stage == kReleaseCalled) {
231                         // Handle 6)
232                         mockCodec->callback()->onReleaseCompleted();
233                     }
234                 });
235             return mockCodec;
236         };
237 
238     sp<ALooper> looper{new ALooper};
239     sp<MediaCodec> codec = SetupMediaCodec(
240             kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
241     ASSERT_NE(nullptr, codec) << "Codec must not be null";
242     ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
243     std::promise<void> reclaimCompleted;
244     std::promise<void> releaseCompleted;
245     Counter threadExitCounter;
246     std::thread([codec, &reclaimCompleted]{
247         // Simulate ResourceManager thread. Proceed with 1)
248         MediaTestHelper::Reclaim(codec, true /* force */);
249         reclaimCompleted.set_value();
250     }).detach();
251     std::thread([codec, &counter, &releaseCompleted]{
252         // Simulate client thread. Wait until 2) is complete
253         (void)counter.wait(kShutdownFromReclaimReceived);
254         // Proceed to 3), and mark that 5) is ready to happen.
255         // NOTE: it's difficult to pinpoint when 4) happens, so we will sleep
256         //       to meet the timing.
257         counter.advance();
258         codec->release();
259         releaseCompleted.set_value();
260     }).detach();
261     std::thread([mockCodec, &counter]{
262         // Simulate component thread. Wait until 3) is complete
263         (void)counter.wait(kReleaseCalled);
264         // We want 4) to complete before moving forward, but it is hard to
265         // wait for this exact event. Just sleep so that the other thread can
266         // proceed and complete 4).
267         std::this_thread::sleep_for(std::chrono::milliseconds(100));
268         // Proceed to 5).
269         mockCodec->callback()->onReleaseCompleted();
270     }).detach();
271     EXPECT_EQ(
272             std::future_status::ready,
273             reclaimCompleted.get_future().wait_for(std::chrono::seconds(5)))
274         << "reclaim timed out";
275     EXPECT_EQ(
276             std::future_status::ready,
277             releaseCompleted.get_future().wait_for(std::chrono::seconds(5)))
278         << "release timed out";
279     looper->stop();
280 }
281 
TEST(MediaCodecTest,ErrorWhileStopping)282 TEST(MediaCodecTest, ErrorWhileStopping) {
283     // Test scenario:
284     //
285     // 1) Client thread calls stop(); MediaCodec looper thread calls
286     //    initiateShutdown(); shutdown is being handled at the component thread.
287     // 2) Error occurred, but the shutdown operation is still being done.
288     // 3) Another error occurred during the shutdown operation.
289     // 4) MediaCodec looper thread handles the error.
290     // 5) Client releases the codec upon the error; previous shutdown is still
291     //    going on.
292     // 6) Component thread completes shutdown and posts onStopCompleted();
293     //    Shutdown from release also completes.
294 
295     static const AString kCodecName{"test.codec"};
296     static const AString kCodecOwner{"nobody"};
297     static const AString kMediaType{"video/x-test"};
298 
299     sp<MockCodec> mockCodec;
300     std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
301         [&mockCodec](const AString &, const char *) {
302             mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
303                 // No mock setup, as we don't expect any buffer operations
304                 // in this scenario.
305             });
306             ON_CALL(*mockCodec, initiateAllocateComponent(_))
307                 .WillByDefault([mockCodec](const sp<AMessage> &) {
308                     mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
309                 });
310             ON_CALL(*mockCodec, initiateConfigureComponent(_))
311                 .WillByDefault([mockCodec](const sp<AMessage> &msg) {
312                     mockCodec->callback()->onComponentConfigured(
313                             msg->dup(), msg->dup());
314                 });
315             ON_CALL(*mockCodec, initiateStart())
316                 .WillByDefault([mockCodec]() {
317                     mockCodec->callback()->onStartCompleted();
318                 });
319             ON_CALL(*mockCodec, initiateShutdown(true))
320                 .WillByDefault([mockCodec](bool) {
321                     // 2)
322                     mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
323                     // 3)
324                     mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
325                 });
326             ON_CALL(*mockCodec, initiateShutdown(false))
327                 .WillByDefault([mockCodec](bool) {
328                     // Previous stop finished now.
329                     mockCodec->callback()->onStopCompleted();
330                     // Release also finished.
331                     mockCodec->callback()->onReleaseCompleted();
332                 });
333             return mockCodec;
334         };
335 
336     sp<ALooper> looper{new ALooper};
337     sp<MediaCodec> codec = SetupMediaCodec(
338             kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
339     ASSERT_NE(nullptr, codec) << "Codec must not be null";
340     ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
341 
342     codec->configure(new AMessage, nullptr, nullptr, 0);
343     codec->start();
344     // stop() will fail because of the error
345     EXPECT_NE(OK, codec->stop());
346     // sleep here so that the looper thread can handle all the errors.
347     std::this_thread::sleep_for(std::chrono::milliseconds(100));
348     // upon receiving the error, client tries to release the codec.
349     codec->release();
350     looper->stop();
351 }
352 
TEST(MediaCodecTest,DeadWhileAsyncReleasing)353 TEST(MediaCodecTest, DeadWhileAsyncReleasing) {
354     // Test scenario:
355     //
356     // 1) Client thread calls release(); MediaCodec looper thread calls
357     //    initiateShutdown(); shutdown is being handled at the component thread.
358     // 2) Codec service died during the shutdown operation.
359     // 3) MediaCodec looper thread handles the death.
360 
361     static const AString kCodecName{"test.codec"};
362     static const AString kCodecOwner{"nobody"};
363     static const AString kMediaType{"video/x-test"};
364 
365     sp<MockCodec> mockCodec;
366     std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
367         [&mockCodec](const AString &, const char *) {
368             mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
369                 // No mock setup, as we don't expect any buffer operations
370                 // in this scenario.
371             });
372             ON_CALL(*mockCodec, initiateAllocateComponent(_))
373                 .WillByDefault([mockCodec](const sp<AMessage> &) {
374                     mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
375                 });
376             ON_CALL(*mockCodec, initiateShutdown(_))
377                 .WillByDefault([mockCodec](bool) {
378                     // 2)
379                     mockCodec->callback()->onError(DEAD_OBJECT, ACTION_CODE_FATAL);
380                     // Codec service has died, no callback.
381                 });
382             return mockCodec;
383         };
384 
385     sp<ALooper> looper{new ALooper};
386     sp<MediaCodec> codec = SetupMediaCodec(
387             kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
388     ASSERT_NE(nullptr, codec) << "Codec must not be null";
389     ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
390 
391     codec->releaseAsync(new AMessage);
392     // sleep here so that the looper thread can handle the error
393     std::this_thread::sleep_for(std::chrono::milliseconds(100));
394     looper->stop();
395 }
396