FrameTimestamps.cpp revision 7c3ba8aa288755fad78ddbabcee0ad5a0610ac1c
1/*
2* Copyright 2016 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 <gui/FrameTimestamps.h>
18
19#include <inttypes.h>
20#include <utils/String8.h>
21
22#include <algorithm>
23#include <limits>
24#include <numeric>
25
26namespace android {
27
28static inline bool isValidTimestamp(nsecs_t time) {
29    return time > 0 && time < INT64_MAX;
30}
31
32// ============================================================================
33// FrameEvents
34// ============================================================================
35
36bool FrameEvents::hasPostedInfo() const {
37    return isValidTimestamp(postedTime);
38}
39
40bool FrameEvents::hasRequestedPresentInfo() const {
41    return isValidTimestamp(requestedPresentTime);
42}
43
44bool FrameEvents::hasLatchInfo() const {
45    return isValidTimestamp(latchTime);
46}
47
48bool FrameEvents::hasFirstRefreshStartInfo() const {
49    return isValidTimestamp(firstRefreshStartTime);
50}
51
52bool FrameEvents::hasLastRefreshStartInfo() const {
53    // The last refresh start time may continue to update until a new frame
54    // is latched. We know we have the final value once the release or retire
55    // info is set. See ConsumerFrameEventHistory::addRetire/Release.
56    return addRetireCalled || addReleaseCalled;
57}
58
59bool FrameEvents::hasAcquireInfo() const {
60    return isValidTimestamp(acquireTime) || acquireFence->isValid();
61}
62
63bool FrameEvents::hasGpuCompositionDoneInfo() const {
64    // We may not get a gpuCompositionDone in addPostComposite if
65    // client/gles compositing isn't needed.
66    return addPostCompositeCalled;
67}
68
69bool FrameEvents::hasDisplayPresentInfo() const {
70    // We may not get a displayPresent in addPostComposite for HWC1.
71    return addPostCompositeCalled;
72}
73
74bool FrameEvents::hasDisplayRetireInfo() const {
75    // We may not get a displayRetire in addRetire for HWC2.
76    return addRetireCalled;
77}
78
79bool FrameEvents::hasReleaseInfo() const {
80    return addReleaseCalled;
81}
82
83static void checkFenceForCompletion(sp<Fence>* fence, nsecs_t* dstTime) {
84    if ((*fence)->isValid()) {
85        nsecs_t time = (*fence)->getSignalTime();
86        if (isValidTimestamp(time)) {
87            *dstTime = time;
88            *fence = Fence::NO_FENCE;
89        }
90    }
91}
92
93void FrameEvents::checkFencesForCompletion() {
94    checkFenceForCompletion(&acquireFence, &acquireTime);
95    checkFenceForCompletion(&gpuCompositionDoneFence, &gpuCompositionDoneTime);
96    checkFenceForCompletion(&displayPresentFence, &displayPresentTime);
97    checkFenceForCompletion(&displayRetireFence, &displayRetireTime);
98    checkFenceForCompletion(&releaseFence, &releaseTime);
99}
100
101void FrameEvents::dump(String8& outString) const
102{
103    if (!valid) {
104        return;
105    }
106
107    outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
108    outString.appendFormat("--- Posted      \t%" PRId64 "\n", postedTime);
109    outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
110
111    outString.appendFormat("--- Latched     \t");
112    if (isValidTimestamp(latchTime)) {
113        outString.appendFormat("%" PRId64 "\n", latchTime);
114    } else {
115        outString.appendFormat("Pending\n");
116    }
117
118    outString.appendFormat("--- Refresh (First)\t");
119    if (isValidTimestamp(firstRefreshStartTime)) {
120        outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
121    } else {
122        outString.appendFormat("Pending\n");
123    }
124
125    outString.appendFormat("--- Refresh (Last)\t");
126    if (isValidTimestamp(lastRefreshStartTime)) {
127        outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
128    } else {
129        outString.appendFormat("Pending\n");
130    }
131
132    outString.appendFormat("--- Acquire     \t");
133    if (isValidTimestamp(acquireTime)) {
134        outString.appendFormat("%" PRId64 "\n", acquireTime);
135    } else {
136        outString.appendFormat("Pending\n");
137    }
138
139    outString.appendFormat("--- GPU Composite Done\t");
140    if (isValidTimestamp(gpuCompositionDoneTime)) {
141        outString.appendFormat("%" PRId64 "\n", gpuCompositionDoneTime);
142    } else if (!addPostCompositeCalled || gpuCompositionDoneFence->isValid()) {
143        outString.appendFormat("Pending\n");
144    } else {
145        outString.appendFormat("N/A\n");
146    }
147
148    outString.appendFormat("--- Display Present\t");
149    if (isValidTimestamp(displayPresentTime)) {
150        outString.appendFormat("%" PRId64 "\n", displayPresentTime);
151    } else if (!addPostCompositeCalled || displayPresentFence->isValid()) {
152        outString.appendFormat("Pending\n");
153    } else {
154        outString.appendFormat("N/A\n");
155    }
156
157    outString.appendFormat("--- Display Retire\t");
158    if (isValidTimestamp(displayRetireTime)) {
159        outString.appendFormat("%" PRId64 "\n", displayRetireTime);
160    } else if (!addRetireCalled || displayRetireFence->isValid()) {
161        outString.appendFormat("Pending\n");
162    } else {
163        outString.appendFormat("N/A\n");
164    }
165
166    outString.appendFormat("--- Release     \t");
167    if (isValidTimestamp(releaseTime)) {
168        outString.appendFormat("%" PRId64 "\n", releaseTime);
169    } else {
170        outString.appendFormat("Pending\n");
171    }
172}
173
174
175// ============================================================================
176// FrameEventHistory
177// ============================================================================
178
179namespace {
180
181struct FrameNumberEqual {
182    FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
183    bool operator()(const FrameEvents& frame) {
184        return frame.valid && mFrameNumber == frame.frameNumber;
185    }
186    const uint64_t mFrameNumber;
187};
188
189}  // namespace
190
191FrameEventHistory::~FrameEventHistory() = default;
192
193FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
194    auto frame = std::find_if(
195            mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
196    return frame == mFrames.end() ? nullptr : &(*frame);
197}
198
199FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
200    *iHint = std::min(*iHint, mFrames.size());
201    auto hint = mFrames.begin() + *iHint;
202    auto frame = std::find_if(
203            hint, mFrames.end(), FrameNumberEqual(frameNumber));
204    if (frame == mFrames.end()) {
205        frame = std::find_if(
206                mFrames.begin(), hint, FrameNumberEqual(frameNumber));
207        if (frame == hint) {
208            return nullptr;
209        }
210    }
211    *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
212    return &(*frame);
213}
214
215void FrameEventHistory::checkFencesForCompletion() {
216    for (auto& frame : mFrames) {
217        frame.checkFencesForCompletion();
218    }
219}
220
221// Uses !|valid| as the MSB.
222static bool FrameNumberLessThan(
223        const FrameEvents& lhs, const FrameEvents& rhs) {
224    if (lhs.valid == rhs.valid) {
225        return lhs.frameNumber < rhs.frameNumber;
226    }
227    return lhs.valid;
228}
229
230void FrameEventHistory::dump(String8& outString) const {
231    auto earliestFrame = std::min_element(
232            mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
233    if (!earliestFrame->valid) {
234        outString.appendFormat("-- N/A\n");
235        return;
236    }
237    for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
238        frame->dump(outString);
239    }
240    for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
241        frame->dump(outString);
242    }
243}
244
245
246// ============================================================================
247// ProducerFrameEventHistory
248// ============================================================================
249
250ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
251
252void ProducerFrameEventHistory::updateAcquireFence(
253        uint64_t frameNumber, sp<Fence> acquire) {
254    FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
255    if (frame == nullptr) {
256        ALOGE("ProducerFrameEventHistory::updateAcquireFence: "
257              "Did not find frame.");
258        return;
259    }
260
261    if (acquire->isValid()) {
262        frame->acquireFence = acquire;
263    } else {
264        // If there isn't an acquire fence, assume that buffer was
265        // ready for the consumer when posted.
266        frame->acquireTime = frame->postedTime;
267    }
268}
269
270static void applyFenceDelta(sp<Fence>* dst, const sp<Fence>& src) {
271    if (src->isValid()) {
272        if ((*dst)->isValid()) {
273            ALOGE("applyFenceDelta: Unexpected fence.");
274        }
275        *dst = src;
276    }
277}
278
279void ProducerFrameEventHistory::applyDelta(
280        const FrameEventHistoryDelta& delta) {
281    for (auto& d : delta.mDeltas) {
282        // Avoid out-of-bounds access.
283        if (d.mIndex >= mFrames.size()) {
284            ALOGE("ProducerFrameEventHistory::applyDelta: Bad index.");
285            return;
286        }
287
288        FrameEvents& frame = mFrames[d.mIndex];
289
290        frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
291        frame.addRetireCalled = d.mAddRetireCalled != 0;
292        frame.addReleaseCalled = d.mAddReleaseCalled != 0;
293
294        frame.postedTime = d.mPostedTime;
295        frame.requestedPresentTime = d.mRequestedPresentTime;
296        frame.latchTime = d.mLatchTime;
297        frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
298        frame.lastRefreshStartTime = d.mLastRefreshStartTime;
299
300        if (frame.frameNumber == d.mFrameNumber) {
301            // Existing frame. Merge.
302            // Consumer never sends timestamps of fences, only the fences
303            // themselves, so we never need to update the fence timestamps here.
304            applyFenceDelta(
305                    &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
306            applyFenceDelta(&frame.displayPresentFence, d.mDisplayPresentFence);
307            applyFenceDelta(&frame.displayRetireFence, d.mDisplayRetireFence);
308            applyFenceDelta(&frame.releaseFence, d.mReleaseFence);
309        } else {
310            // New frame. Overwrite.
311            frame.frameNumber = d.mFrameNumber;
312
313            frame.gpuCompositionDoneFence = d.mGpuCompositionDoneFence;
314            frame.displayPresentFence = d.mDisplayPresentFence;
315            frame.displayRetireFence = d.mDisplayRetireFence;
316            frame.releaseFence = d.mReleaseFence;
317
318            // Set aquire fence and time at this point.
319            frame.acquireTime = 0;
320            frame.acquireFence = Fence::NO_FENCE;
321
322            // Reset fence-related timestamps
323            frame.gpuCompositionDoneTime = 0;
324            frame.displayPresentTime = 0;
325            frame.displayRetireTime = 0;
326            frame.releaseTime = 0;
327
328            // The consumer only sends valid frames.
329            frame.valid = true;
330        }
331    }
332}
333
334
335// ============================================================================
336// ConsumerFrameEventHistory
337// ============================================================================
338
339ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
340
341void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
342    // Overwrite all fields of the frame with default values unless set here.
343    FrameEvents newTimestamps;
344    newTimestamps.frameNumber = newEntry.frameNumber;
345    newTimestamps.postedTime = newEntry.postedTime;
346    newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
347    newTimestamps.acquireFence = newEntry.acquireFence;
348    newTimestamps.valid = true;
349    mFrames[mQueueOffset] = newTimestamps;
350
351    // Note: We avoid sending the acquire fence back to the caller since
352    // they have the original one already, so there is no need to set the
353    // acquire dirty bit.
354    mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
355
356    mQueueOffset = (mQueueOffset + 1) % mFrames.size();
357}
358
359void ConsumerFrameEventHistory::addLatch(
360        uint64_t frameNumber, nsecs_t latchTime) {
361    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
362    if (frame == nullptr) {
363        ALOGE("ConsumerFrameEventHistory::addLatch: Did not find frame.");
364        return;
365    }
366    frame->latchTime = latchTime;
367    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
368}
369
370void ConsumerFrameEventHistory::addPreComposition(
371        uint64_t frameNumber, nsecs_t refreshStartTime) {
372    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
373    if (frame == nullptr) {
374        ALOGE("ConsumerFrameEventHistory::addPreComposition: "
375              "Did not find frame.");
376        return;
377    }
378    frame->lastRefreshStartTime = refreshStartTime;
379    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
380    if (!isValidTimestamp(frame->firstRefreshStartTime)) {
381        frame->firstRefreshStartTime = refreshStartTime;
382        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
383    }
384}
385
386void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
387        sp<Fence> gpuCompositionDone, sp<Fence> displayPresent) {
388    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
389    if (frame == nullptr) {
390        ALOGE("ConsumerFrameEventHistory::addPostComposition: "
391              "Did not find frame.");
392        return;
393    }
394    // Only get GPU and present info for the first composite.
395    if (!frame->addPostCompositeCalled) {
396        frame->addPostCompositeCalled = true;
397        frame->gpuCompositionDoneFence = gpuCompositionDone;
398        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GL_COMPOSITION_DONE>();
399        if (!frame->displayPresentFence->isValid()) {
400            frame->displayPresentFence = displayPresent;
401            mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
402        }
403    }
404}
405
406void ConsumerFrameEventHistory::addRetire(
407        uint64_t frameNumber, sp<Fence> displayRetire) {
408    FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
409    if (frame == nullptr) {
410        ALOGE("ConsumerFrameEventHistory::addRetire: Did not find frame.");
411        return;
412    }
413    frame->addRetireCalled = true;
414    frame->displayRetireFence = displayRetire;
415    mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
416}
417
418void ConsumerFrameEventHistory::addRelease(
419        uint64_t frameNumber, sp<Fence> release) {
420    FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
421    if (frame == nullptr) {
422        ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
423        return;
424    }
425    frame->addReleaseCalled = true;
426    frame->releaseFence = release;
427    mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
428}
429
430void ConsumerFrameEventHistory::getAndResetDelta(
431        FrameEventHistoryDelta* delta) {
432    delta->mDeltas.reserve(mFramesDirty.size());
433    for (size_t i = 0; i < mFramesDirty.size(); i++) {
434        if (mFramesDirty[i].anyDirty()) {
435            delta->mDeltas.push_back(
436                    FrameEventsDelta(i, mFrames[i], mFramesDirty[i]));
437            mFramesDirty[i].reset();
438        }
439    }
440}
441
442
443// ============================================================================
444// FrameEventsDelta
445// ============================================================================
446
447FrameEventsDelta::FrameEventsDelta(
448        size_t index,
449        const FrameEvents& frameTimestamps,
450        const FrameEventDirtyFields& dirtyFields)
451    : mIndex(index),
452      mFrameNumber(frameTimestamps.frameNumber),
453      mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
454      mAddRetireCalled(frameTimestamps.addRetireCalled),
455      mAddReleaseCalled(frameTimestamps.addReleaseCalled),
456      mPostedTime(frameTimestamps.postedTime),
457      mRequestedPresentTime(frameTimestamps.requestedPresentTime),
458      mLatchTime(frameTimestamps.latchTime),
459      mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
460      mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime) {
461    mGpuCompositionDoneFence =
462            dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>() ?
463                    frameTimestamps.gpuCompositionDoneFence : Fence::NO_FENCE;
464    mDisplayPresentFence = dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>() ?
465            frameTimestamps.displayPresentFence : Fence::NO_FENCE;
466    mDisplayRetireFence = dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>() ?
467            frameTimestamps.displayRetireFence : Fence::NO_FENCE;
468    mReleaseFence = dirtyFields.isDirty<FrameEvent::RELEASE>() ?
469            frameTimestamps.releaseFence : Fence::NO_FENCE;
470}
471
472size_t FrameEventsDelta::minFlattenedSize() {
473    constexpr size_t min =
474            sizeof(FrameEventsDelta::mFrameNumber) +
475            sizeof(uint8_t) + // mIndex
476            sizeof(uint8_t) + // mAddPostCompositeCalled
477            sizeof(uint8_t) + // mAddRetireCalled
478            sizeof(uint8_t) + // mAddReleaseCalled
479            sizeof(FrameEventsDelta::mPostedTime) +
480            sizeof(FrameEventsDelta::mRequestedPresentTime) +
481            sizeof(FrameEventsDelta::mLatchTime) +
482            sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
483            sizeof(FrameEventsDelta::mLastRefreshStartTime);
484    return min;
485}
486
487// Flattenable implementation
488size_t FrameEventsDelta::getFlattenedSize() const {
489    auto fences = allFences(this);
490    return minFlattenedSize() +
491            std::accumulate(fences.begin(), fences.end(), size_t(0),
492                    [](size_t a, const sp<Fence>* fence) {
493                            return a + (*fence)->getFlattenedSize();
494                    });
495}
496
497size_t FrameEventsDelta::getFdCount() const {
498    auto fences = allFences(this);
499    return std::accumulate(fences.begin(), fences.end(), size_t(0),
500            [](size_t a, const sp<Fence>* fence) {
501                return a + (*fence)->getFdCount();
502            });
503}
504
505status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
506            size_t& count) const {
507    if (size < getFlattenedSize() || count < getFdCount()) {
508        return NO_MEMORY;
509    }
510
511    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
512            mIndex > std::numeric_limits<uint8_t>::max()) {
513        return BAD_VALUE;
514    }
515
516    FlattenableUtils::write(buffer, size, mFrameNumber);
517
518    // These are static_cast to uint8_t for alignment.
519    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(mIndex));
520    FlattenableUtils::write(
521            buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
522    FlattenableUtils::write(
523            buffer, size, static_cast<uint8_t>(mAddRetireCalled));
524    FlattenableUtils::write(
525            buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
526
527    FlattenableUtils::write(buffer, size, mPostedTime);
528    FlattenableUtils::write(buffer, size, mRequestedPresentTime);
529    FlattenableUtils::write(buffer, size, mLatchTime);
530    FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
531    FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
532
533    // Fences
534    for (auto fence : allFences(this)) {
535        status_t status = (*fence)->flatten(buffer, size, fds, count);
536        if (status != NO_ERROR) {
537            return status;
538        }
539    }
540    return NO_ERROR;
541}
542
543status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
544            int const*& fds, size_t& count) {
545    if (size < minFlattenedSize()) {
546        return NO_MEMORY;
547    }
548
549    FlattenableUtils::read(buffer, size, mFrameNumber);
550
551    // These were written as uint8_t for alignment.
552    uint8_t temp = 0;
553    FlattenableUtils::read(buffer, size, temp);
554    mIndex = temp;
555    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
556        return BAD_VALUE;
557    }
558    FlattenableUtils::read(buffer, size, temp);
559    mAddPostCompositeCalled = static_cast<bool>(temp);
560    FlattenableUtils::read(buffer, size, temp);
561    mAddRetireCalled = static_cast<bool>(temp);
562    FlattenableUtils::read(buffer, size, temp);
563    mAddReleaseCalled = static_cast<bool>(temp);
564
565    FlattenableUtils::read(buffer, size, mPostedTime);
566    FlattenableUtils::read(buffer, size, mRequestedPresentTime);
567    FlattenableUtils::read(buffer, size, mLatchTime);
568    FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
569    FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
570
571    // Fences
572    for (auto fence : allFences(this)) {
573        *fence = new Fence;
574        status_t status = (*fence)->unflatten(buffer, size, fds, count);
575        if (status != NO_ERROR) {
576            return status;
577        }
578    }
579    return NO_ERROR;
580}
581
582
583// ============================================================================
584// FrameEventHistoryDelta
585// ============================================================================
586
587size_t FrameEventHistoryDelta::minFlattenedSize() {
588    return sizeof(uint32_t);
589}
590
591size_t FrameEventHistoryDelta::getFlattenedSize() const {
592    return minFlattenedSize() +
593            std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
594                    [](size_t a, const FrameEventsDelta& delta) {
595                            return a + delta.getFlattenedSize();
596                    });
597}
598
599size_t FrameEventHistoryDelta::getFdCount() const {
600    return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
601            [](size_t a, const FrameEventsDelta& delta) {
602                    return a + delta.getFdCount();
603            });
604}
605
606status_t FrameEventHistoryDelta::flatten(
607            void*& buffer, size_t& size, int*& fds, size_t& count) const {
608    if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
609        return BAD_VALUE;
610    }
611    if (size < getFlattenedSize()) {
612        return NO_MEMORY;
613    }
614
615    FlattenableUtils::write(
616            buffer, size, static_cast<uint32_t>(mDeltas.size()));
617    for (auto& d : mDeltas) {
618        status_t status = d.flatten(buffer, size, fds, count);
619        if (status != NO_ERROR) {
620            return status;
621        }
622    }
623    return NO_ERROR;
624}
625
626status_t FrameEventHistoryDelta::unflatten(
627            void const*& buffer, size_t& size, int const*& fds, size_t& count) {
628    if (size < minFlattenedSize()) {
629        return NO_MEMORY;
630    }
631
632    uint32_t deltaCount = 0;
633    FlattenableUtils::read(buffer, size, deltaCount);
634    if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
635        return BAD_VALUE;
636    }
637    mDeltas.resize(deltaCount);
638    for (auto& d : mDeltas) {
639        status_t status = d.unflatten(buffer, size, fds, count);
640        if (status != NO_ERROR) {
641            return status;
642        }
643    }
644    return NO_ERROR;
645}
646
647
648} // namespace android
649