1 /*
2 * Copyright 2021 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
18 #include <ftl/enum.h>
19 #include <fuzzer/FuzzedDataProvider.h>
20 #include <processgroup/sched_policy.h>
21
22 #include <scheduler/IVsyncSource.h>
23 #include <scheduler/PresentLatencyTracker.h>
24
25 #include "Scheduler/OneShotTimer.h"
26 #include "Scheduler/RefreshRateSelector.h"
27 #include "Scheduler/VSyncDispatchTimerQueue.h"
28 #include "Scheduler/VSyncPredictor.h"
29 #include "Scheduler/VSyncReactor.h"
30
31 #include "mock/MockVSyncDispatch.h"
32 #include "mock/MockVSyncTracker.h"
33
34 #include "surfaceflinger_fuzzers_utils.h"
35 #include "surfaceflinger_scheduler_fuzzer.h"
36
37 namespace android::fuzz {
38
39 using hardware::graphics::composer::hal::PowerMode;
40
41 constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriodNsecs(),
42 (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
43 (120_Hz).getPeriodNsecs()};
44
45 constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
46 constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>();
47
48 constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
49 PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
50
51 constexpr uint16_t kRandomStringLength = 256;
52 constexpr std::chrono::duration kSyncPeriod(16ms);
53 constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
54
55 template <typename T>
dump(T * component,FuzzedDataProvider * fdp)56 void dump(T* component, FuzzedDataProvider* fdp) {
57 std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength);
58 component->dump(res);
59 }
60
makeFakeFence()61 inline sp<Fence> makeFakeFence() {
62 return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
63 }
64
65 class SchedulerFuzzer {
66 public:
SchedulerFuzzer(const uint8_t * data,size_t size)67 SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
68 void process();
69
70 private:
71 void fuzzRefreshRateSelection();
72 void fuzzRefreshRateSelector();
73 void fuzzPresentLatencyTracker();
74 void fuzzFrameTargeter();
75 void fuzzVSyncModulator();
76 void fuzzVSyncPredictor();
77 void fuzzVSyncReactor();
78 void fuzzLayerHistory();
79 void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch);
80 void fuzzVSyncDispatchTimerQueue();
81 void fuzzOneShotTimer();
82 void fuzzEventThread();
83 PhysicalDisplayId getPhysicalDisplayId();
84
85 FuzzedDataProvider mFdp;
86
87 std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
88 };
89
getPhysicalDisplayId()90 PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() {
91 PhysicalDisplayId internalDispId = PhysicalDisplayId::fromPort(111u);
92 PhysicalDisplayId externalDispId = PhysicalDisplayId::fromPort(222u);
93 PhysicalDisplayId randomDispId = PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint16_t>());
94 PhysicalDisplayId dispId64Bit = PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
95 PhysicalDisplayId displayId = mFdp.PickValueInArray<PhysicalDisplayId>(
96 {internalDispId, externalDispId, dispId64Bit, randomDispId});
97 return displayId;
98 }
99
fuzzEventThread()100 void SchedulerFuzzer::fuzzEventThread() {
101 mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
102 new scheduler::VsyncSchedule(getPhysicalDisplayId(),
103 std::make_shared<mock::VSyncTracker>(),
104 std::make_shared<mock::VSyncDispatch>(), nullptr));
105 const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
106 std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
107 android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
108 (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
109 (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
110
111 thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
112 sp<EventThreadConnection> connection =
113 sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>(),
114 nullptr);
115 thread->requestNextVsync(connection);
116 thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection);
117
118 thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
119 (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
120 thread->registerDisplayEventConnection(connection);
121 thread->enableSyntheticVsync(mFdp.ConsumeBool());
122 dump<android::impl::EventThread>(thread.get(), &mFdp);
123 }
124
fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue * dispatch)125 void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) {
126 scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback(
127 [&](auto, auto, auto) {
128 dispatch->schedule(tmp,
129 {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
130 .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
131 .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
132 },
133 "o.o");
134 dispatch->schedule(tmp,
135 {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
136 .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
137 .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
138 dispatch->unregisterCallback(tmp);
139 dispatch->cancel(tmp);
140 }
141
fuzzVSyncDispatchTimerQueue()142 void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() {
143 auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>());
144 scheduler::VSyncDispatchTimerQueue
145 mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker,
146 mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/,
147 mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/};
148
149 fuzzCallbackToken(&mDispatch);
150
151 dump<scheduler::VSyncDispatchTimerQueue>(&mDispatch, &mFdp);
152
153 scheduler::VSyncDispatchTimerQueueEntry entry(
154 "fuzz", [](auto, auto, auto) {},
155 mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/);
156 entry.update(*stubTracker, 0);
157 entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
158 .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
159 .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
160 *stubTracker, 0);
161 entry.disarm();
162 entry.ensureNotRunning();
163 entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
164 .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
165 .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
166 *stubTracker, 0);
167 auto const wakeup = entry.wakeupTime();
168 auto const ready = entry.readyTime();
169 entry.callback(entry.executing(), *wakeup, *ready);
170 entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
171 .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
172 .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
173 dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
174 }
175
fuzzVSyncPredictor()176 void SchedulerFuzzer::fuzzVSyncPredictor() {
177 uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
178 uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
179 uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
180 scheduler::VSyncPredictor tracker{kDisplayId, mFdp.ConsumeIntegral<uint16_t>() /*period*/,
181 historySize, minimumSamplesForPrediction,
182 mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
183 uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
184 tracker.setPeriod(period);
185 for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) {
186 if (!tracker.needsMoreSamples()) {
187 break;
188 }
189 tracker.addVsyncTimestamp(now += period);
190 }
191 tracker.nextAnticipatedVSyncTimeFrom(now);
192 tracker.resetModel();
193 }
194
fuzzOneShotTimer()195 void SchedulerFuzzer::fuzzOneShotTimer() {
196 FakeClock* clock = new FakeClock();
197 std::unique_ptr<scheduler::OneShotTimer> idleTimer = std::make_unique<scheduler::OneShotTimer>(
198 mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
199 (std::chrono::milliseconds)mFdp.ConsumeIntegral<uint8_t>() /*val*/,
200 [] {} /*resetCallback*/, [] {} /*timeoutCallback*/, std::unique_ptr<FakeClock>(clock));
201 idleTimer->start();
202 idleTimer->reset();
203 idleTimer->stop();
204 }
205
fuzzLayerHistory()206 void SchedulerFuzzer::fuzzLayerHistory() {
207 TestableSurfaceFlinger flinger;
208 flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(),
209 std::make_unique<android::mock::VSyncTracker>(),
210 std::make_unique<android::mock::EventThread>(),
211 std::make_unique<android::mock::EventThread>());
212 flinger.setupTimeStats(std::make_unique<android::mock::TimeStats>());
213 std::unique_ptr<android::renderengine::RenderEngine> renderEngine =
214 std::make_unique<android::renderengine::mock::RenderEngine>();
215 flinger.setupRenderEngine(std::move(renderEngine));
216 flinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>());
217
218 scheduler::TestableScheduler* scheduler = flinger.scheduler();
219
220 scheduler::LayerHistory& historyV1 = scheduler->mutableLayerHistory();
221 nsecs_t time1 = systemTime();
222 nsecs_t time2 = time1;
223 uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>();
224
225 sp<FuzzImplLayer> layer1 = sp<FuzzImplLayer>::make(flinger.flinger());
226 sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
227
228 for (int i = 0; i < historySize; ++i) {
229 historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
230 scheduler::LayerHistory::LayerUpdateType::Buffer);
231 historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
232 scheduler::LayerHistory::LayerUpdateType::Buffer);
233 time1 += mFdp.PickValueInArray(kVsyncPeriods);
234 time2 += mFdp.PickValueInArray(kVsyncPeriods);
235 }
236 historyV1.summarize(*scheduler->refreshRateSelector(), time1);
237 historyV1.summarize(*scheduler->refreshRateSelector(), time2);
238
239 scheduler->createConnection(std::make_unique<android::mock::EventThread>());
240
241 scheduler::ConnectionHandle handle;
242 scheduler->createDisplayEventConnection(handle);
243 scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
244 (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
245
246 std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength);
247 utils::Dumper dumper(result);
248 scheduler->dump(dumper);
249 }
250
fuzzVSyncReactor()251 void SchedulerFuzzer::fuzzVSyncReactor() {
252 std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
253 scheduler::VSyncReactor reactor(kDisplayId,
254 std::make_unique<ClockWrapper>(
255 std::make_shared<FuzzImplClock>()),
256 *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
257 false);
258
259 reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool());
260 bool periodFlushed = false; // Value does not matter, since this is an out
261 // param from addHwVsyncTimestamp.
262 reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
263 reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
264 &periodFlushed);
265
266 const auto fence = std::make_shared<FenceTime>(makeFakeFence());
267 vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
268 FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
269 fence->applyTrustedSnapshot(snap);
270 reactor.setIgnorePresentFences(mFdp.ConsumeBool());
271 reactor.addPresentFence(fence);
272 dump<scheduler::VSyncReactor>(&reactor, &mFdp);
273 }
274
fuzzVSyncModulator()275 void SchedulerFuzzer::fuzzVSyncModulator() {
276 enum {
277 SF_OFFSET_LATE,
278 APP_OFFSET_LATE,
279 SF_DURATION_LATE,
280 APP_DURATION_LATE,
281 SF_OFFSET_EARLY,
282 APP_OFFSET_EARLY,
283 SF_DURATION_EARLY,
284 APP_DURATION_EARLY,
285 SF_OFFSET_EARLY_GPU,
286 APP_OFFSET_EARLY_GPU,
287 SF_DURATION_EARLY_GPU,
288 APP_DURATION_EARLY_GPU,
289 HWC_MIN_WORK_DURATION,
290 };
291 using Schedule = scheduler::TransactionSchedule;
292 using nanos = std::chrono::nanoseconds;
293 using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
294 const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
295 nanos(APP_DURATION_LATE)};
296 const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
297 nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
298 const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
299 nanos(APP_DURATION_EARLY_GPU)};
300 const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)};
301 sp<FuzzImplVsyncModulator> vSyncModulator =
302 sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now);
303 (void)vSyncModulator->setVsyncConfigSet(offsets);
304 (void)vSyncModulator->setTransactionSchedule(Schedule::Late);
305 const auto token = sp<BBinder>::make();
306 (void)vSyncModulator->setTransactionSchedule(Schedule::EarlyStart, token);
307 vSyncModulator->binderDied(token);
308 }
309
fuzzRefreshRateSelection()310 void SchedulerFuzzer::fuzzRefreshRateSelection() {
311 TestableSurfaceFlinger flinger;
312 flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(),
313 std::make_unique<android::mock::VSyncTracker>(),
314 std::make_unique<android::mock::EventThread>(),
315 std::make_unique<android::mock::EventThread>());
316
317 sp<Client> client;
318 LayerCreationArgs args(flinger.flinger(), client,
319 mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
320 mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata());
321 sp<Layer> layer = sp<Layer>::make(args);
322
323 layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
324 }
325
fuzzRefreshRateSelector()326 void SchedulerFuzzer::fuzzRefreshRateSelector() {
327 using RefreshRateSelector = scheduler::RefreshRateSelector;
328 using LayerRequirement = RefreshRateSelector::LayerRequirement;
329 using RefreshRateStats = scheduler::RefreshRateStats;
330
331 const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
332 const uint16_t maxRefreshRate =
333 mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX);
334
335 const DisplayModeId modeId{mFdp.ConsumeIntegralInRange<uint8_t>(0, 10)};
336
337 DisplayModes displayModes;
338 for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) {
339 displayModes.try_emplace(modeId,
340 mock::createDisplayMode(modeId,
341 Fps::fromValue(static_cast<float>(fps))));
342 }
343
344 RefreshRateSelector refreshRateSelector(displayModes, modeId);
345
346 const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
347 std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
348
349 refreshRateSelector.getRankedFrameRates(layers, globalSignals);
350
351 layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
352 layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
353 layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>());
354 layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
355 auto frameRateOverrides =
356 refreshRateSelector.getFrameRateOverrides(layers,
357 Fps::fromValue(
358 mFdp.ConsumeFloatingPoint<float>()),
359 globalSignals);
360
361 {
362 ftl::FakeGuard guard(kMainThreadContext);
363
364 refreshRateSelector.setPolicy(
365 RefreshRateSelector::
366 DisplayManagerPolicy{modeId,
367 {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
368 Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
369 refreshRateSelector.setPolicy(
370 RefreshRateSelector::OverridePolicy{modeId,
371 {Fps::fromValue(
372 mFdp.ConsumeFloatingPoint<float>()),
373 Fps::fromValue(
374 mFdp.ConsumeFloatingPoint<float>())}});
375 refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
376
377 refreshRateSelector.setActiveMode(modeId,
378 Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
379 }
380
381 RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
382 mFdp.ConsumeFloatingPoint<float>()),
383 Fps::fromValue(
384 mFdp.ConsumeFloatingPoint<float>()));
385 RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
386 Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
387
388 android::mock::TimeStats timeStats;
389 RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
390 PowerMode::OFF);
391
392 const auto fpsOpt = displayModes.get(modeId).transform(
393 [](const DisplayModePtr& mode) { return mode->getFps(); });
394 refreshRateStats.setRefreshRate(*fpsOpt);
395
396 refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
397 }
398
fuzzPresentLatencyTracker()399 void SchedulerFuzzer::fuzzPresentLatencyTracker() {
400 scheduler::PresentLatencyTracker tracker;
401
402 int i = 5;
403 while (i-- > 0) {
404 tracker.trackPendingFrame(getFuzzedTimePoint(mFdp),
405 std::make_shared<FenceTime>(makeFakeFence()));
406 }
407 }
408
fuzzFrameTargeter()409 void SchedulerFuzzer::fuzzFrameTargeter() {
410 scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
411
412 const struct VsyncSource final : scheduler::IVsyncSource {
413 explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
414 FuzzedDataProvider& fuzzer;
415
416 Period period() const { return getFuzzedDuration(fuzzer); }
417 TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
418 } vsyncSource{mFdp};
419
420 int i = 10;
421 while (i-- > 0) {
422 frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
423 .vsyncId = getFuzzedVsyncId(mFdp),
424 .expectedVsyncTime = getFuzzedTimePoint(mFdp),
425 .sfWorkDuration = getFuzzedDuration(mFdp)},
426 vsyncSource);
427
428 frameTargeter.setPresentFence(makeFakeFence());
429
430 frameTargeter.endFrame(
431 {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)});
432 }
433 }
434
process()435 void SchedulerFuzzer::process() {
436 fuzzRefreshRateSelection();
437 fuzzRefreshRateSelector();
438 fuzzPresentLatencyTracker();
439 fuzzFrameTargeter();
440 fuzzVSyncModulator();
441 fuzzVSyncPredictor();
442 fuzzVSyncReactor();
443 fuzzLayerHistory();
444 fuzzEventThread();
445 fuzzVSyncDispatchTimerQueue();
446 fuzzOneShotTimer();
447 }
448
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)449 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
450 SchedulerFuzzer schedulerFuzzer(data, size);
451 schedulerFuzzer.process();
452 return 0;
453 }
454
455 } // namespace android::fuzz
456