1 /*
2  * Copyright (C) 2010 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 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wconversion"
20 
21 #undef LOG_TAG
22 #define LOG_TAG "BufferLayerConsumer"
23 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
24 //#define LOG_NDEBUG 0
25 
26 #include "BufferLayerConsumer.h"
27 #include "Layer.h"
28 #include "Scheduler/DispSync.h"
29 
30 #include <inttypes.h>
31 
32 #include <cutils/compiler.h>
33 
34 #include <hardware/hardware.h>
35 
36 #include <math/mat4.h>
37 
38 #include <gui/BufferItem.h>
39 #include <gui/GLConsumer.h>
40 #include <gui/ISurfaceComposer.h>
41 #include <gui/SurfaceComposerClient.h>
42 #include <private/gui/ComposerService.h>
43 #include <renderengine/Image.h>
44 #include <renderengine/RenderEngine.h>
45 #include <utils/Log.h>
46 #include <utils/String8.h>
47 #include <utils/Trace.h>
48 
49 namespace android {
50 
51 // Macros for including the BufferLayerConsumer name in log messages
52 #define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
53 #define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
54 //#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
55 #define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
56 #define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
57 
58 static const mat4 mtxIdentity;
59 
BufferLayerConsumer(const sp<IGraphicBufferConsumer> & bq,renderengine::RenderEngine & engine,uint32_t tex,Layer * layer)60 BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
61                                          renderengine::RenderEngine& engine, uint32_t tex,
62                                          Layer* layer)
63       : ConsumerBase(bq, false),
64         mCurrentCrop(Rect::EMPTY_RECT),
65         mCurrentTransform(0),
66         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
67         mCurrentFence(Fence::NO_FENCE),
68         mCurrentTimestamp(0),
69         mCurrentDataSpace(ui::Dataspace::UNKNOWN),
70         mCurrentFrameNumber(0),
71         mCurrentTransformToDisplayInverse(false),
72         mCurrentSurfaceDamage(),
73         mCurrentApi(0),
74         mDefaultWidth(1),
75         mDefaultHeight(1),
76         mFilteringEnabled(true),
77         mRE(engine),
78         mTexName(tex),
79         mLayer(layer),
80         mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
81     BLC_LOGV("BufferLayerConsumer");
82 
83     memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
84 
85     mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
86 }
87 
setDefaultBufferSize(uint32_t w,uint32_t h)88 status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
89     Mutex::Autolock lock(mMutex);
90     if (mAbandoned) {
91         BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
92         return NO_INIT;
93     }
94     mDefaultWidth = w;
95     mDefaultHeight = h;
96     return mConsumer->setDefaultBufferSize(w, h);
97 }
98 
setContentsChangedListener(const wp<ContentsChangedListener> & listener)99 void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
100     setFrameAvailableListener(listener);
101     Mutex::Autolock lock(mMutex);
102     mContentsChangedListener = listener;
103 }
104 
updateTexImage(BufferRejecter * rejecter,nsecs_t expectedPresentTime,bool * autoRefresh,bool * queuedBuffer,uint64_t maxFrameNumber)105 status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
106                                              bool* autoRefresh, bool* queuedBuffer,
107                                              uint64_t maxFrameNumber) {
108     ATRACE_CALL();
109     BLC_LOGV("updateTexImage");
110     Mutex::Autolock lock(mMutex);
111 
112     if (mAbandoned) {
113         BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
114         return NO_INIT;
115     }
116 
117     BufferItem item;
118 
119     // Acquire the next buffer.
120     // In asynchronous mode the list is guaranteed to be one buffer
121     // deep, while in synchronous mode we use the oldest buffer.
122     status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
123     if (err != NO_ERROR) {
124         if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
125             err = NO_ERROR;
126         } else if (err == BufferQueue::PRESENT_LATER) {
127             // return the error, without logging
128         } else {
129             BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
130         }
131         return err;
132     }
133 
134     if (autoRefresh) {
135         *autoRefresh = item.mAutoRefresh;
136     }
137 
138     if (queuedBuffer) {
139         *queuedBuffer = item.mQueuedBuffer;
140     }
141 
142     // We call the rejecter here, in case the caller has a reason to
143     // not accept this buffer.  This is used by SurfaceFlinger to
144     // reject buffers which have the wrong size
145     int slot = item.mSlot;
146     if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
147         releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
148         return BUFFER_REJECTED;
149     }
150 
151     // Release the previous buffer.
152     err = updateAndReleaseLocked(item, &mPendingRelease);
153     if (err != NO_ERROR) {
154         return err;
155     }
156 
157     if (!mRE.useNativeFenceSync()) {
158         // Bind the new buffer to the GL texture.
159         //
160         // Older devices require the "implicit" synchronization provided
161         // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
162         // devices will either call this in Layer::onDraw, or (if it's not
163         // a GL-composited layer) not at all.
164         err = bindTextureImageLocked();
165     }
166 
167     return err;
168 }
169 
bindTextureImage()170 status_t BufferLayerConsumer::bindTextureImage() {
171     Mutex::Autolock lock(mMutex);
172     return bindTextureImageLocked();
173 }
174 
setReleaseFence(const sp<Fence> & fence)175 void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
176     if (!fence->isValid()) {
177         return;
178     }
179 
180     auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
181     if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
182         return;
183     }
184 
185     auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
186                                             : mCurrentTextureBuffer->graphicBuffer();
187     auto err = addReleaseFence(slot, buffer, fence);
188     if (err != OK) {
189         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
190     }
191 }
192 
releasePendingBuffer()193 bool BufferLayerConsumer::releasePendingBuffer() {
194     if (!mPendingRelease.isPending) {
195         BLC_LOGV("Pending buffer already released");
196         return false;
197     }
198     BLC_LOGV("Releasing pending buffer");
199     Mutex::Autolock lock(mMutex);
200     status_t result =
201             releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
202     if (result < NO_ERROR) {
203         BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
204     }
205     mPendingRelease = PendingRelease();
206     return true;
207 }
208 
getPrevFinalReleaseFence() const209 sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
210     Mutex::Autolock lock(mMutex);
211     return ConsumerBase::mPrevFinalReleaseFence;
212 }
213 
acquireBufferLocked(BufferItem * item,nsecs_t presentWhen,uint64_t maxFrameNumber)214 status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
215                                                   uint64_t maxFrameNumber) {
216     status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
217     if (err != NO_ERROR) {
218         return err;
219     }
220 
221     // If item->mGraphicBuffer is not null, this buffer has not been acquired
222     // before, so we need to clean up old references.
223     if (item->mGraphicBuffer != nullptr) {
224         std::lock_guard<std::mutex> lock(mImagesMutex);
225         if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
226             mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
227             mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
228         }
229     }
230 
231     return NO_ERROR;
232 }
233 
updateAndReleaseLocked(const BufferItem & item,PendingRelease * pendingRelease)234 status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
235                                                      PendingRelease* pendingRelease) {
236     status_t err = NO_ERROR;
237 
238     int slot = item.mSlot;
239 
240     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
241              (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
242                      ? mCurrentTextureBuffer->graphicBuffer()->handle
243                      : 0,
244              slot, mSlots[slot].mGraphicBuffer->handle);
245 
246     // Hang onto the pointer so that it isn't freed in the call to
247     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
248     // the same.
249 
250     std::shared_ptr<Image> nextTextureBuffer;
251     {
252         std::lock_guard<std::mutex> lock(mImagesMutex);
253         nextTextureBuffer = mImages[slot];
254     }
255 
256     // release old buffer
257     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
258         if (pendingRelease == nullptr) {
259             status_t status =
260                     releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
261             if (status < NO_ERROR) {
262                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
263                          status);
264                 err = status;
265                 // keep going, with error raised [?]
266             }
267         } else {
268             pendingRelease->currentTexture = mCurrentTexture;
269             pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
270             pendingRelease->isPending = true;
271         }
272     }
273 
274     // Update the BufferLayerConsumer state.
275     mCurrentTexture = slot;
276     mCurrentTextureBuffer = nextTextureBuffer;
277     mCurrentCrop = item.mCrop;
278     mCurrentTransform = item.mTransform;
279     mCurrentScalingMode = item.mScalingMode;
280     mCurrentTimestamp = item.mTimestamp;
281     mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace);
282     mCurrentHdrMetadata = item.mHdrMetadata;
283     mCurrentFence = item.mFence;
284     mCurrentFenceTime = item.mFenceTime;
285     mCurrentFrameNumber = item.mFrameNumber;
286     mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
287     mCurrentSurfaceDamage = item.mSurfaceDamage;
288     mCurrentApi = item.mApi;
289 
290     computeCurrentTransformMatrixLocked();
291 
292     return err;
293 }
294 
bindTextureImageLocked()295 status_t BufferLayerConsumer::bindTextureImageLocked() {
296     ATRACE_CALL();
297 
298     if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
299         return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
300                                              mCurrentFence);
301     }
302 
303     return NO_INIT;
304 }
305 
getTransformMatrix(float mtx[16])306 void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
307     Mutex::Autolock lock(mMutex);
308     memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
309 }
310 
setFilteringEnabled(bool enabled)311 void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
312     Mutex::Autolock lock(mMutex);
313     if (mAbandoned) {
314         BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
315         return;
316     }
317     bool needsRecompute = mFilteringEnabled != enabled;
318     mFilteringEnabled = enabled;
319 
320     if (needsRecompute && mCurrentTextureBuffer == nullptr) {
321         BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
322     }
323 
324     if (needsRecompute && mCurrentTextureBuffer != nullptr) {
325         computeCurrentTransformMatrixLocked();
326     }
327 }
328 
computeCurrentTransformMatrixLocked()329 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
330     BLC_LOGV("computeCurrentTransformMatrixLocked");
331     if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
332         BLC_LOGD("computeCurrentTransformMatrixLocked: "
333                  "mCurrentTextureBuffer is nullptr");
334     }
335     GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
336                                        mCurrentTextureBuffer == nullptr
337                                                ? nullptr
338                                                : mCurrentTextureBuffer->graphicBuffer(),
339                                        getCurrentCropLocked(), mCurrentTransform,
340                                        mFilteringEnabled);
341 }
342 
getTimestamp()343 nsecs_t BufferLayerConsumer::getTimestamp() {
344     BLC_LOGV("getTimestamp");
345     Mutex::Autolock lock(mMutex);
346     return mCurrentTimestamp;
347 }
348 
getCurrentDataSpace()349 ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() {
350     BLC_LOGV("getCurrentDataSpace");
351     Mutex::Autolock lock(mMutex);
352     return mCurrentDataSpace;
353 }
354 
getCurrentHdrMetadata() const355 const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
356     BLC_LOGV("getCurrentHdrMetadata");
357     Mutex::Autolock lock(mMutex);
358     return mCurrentHdrMetadata;
359 }
360 
getFrameNumber()361 uint64_t BufferLayerConsumer::getFrameNumber() {
362     BLC_LOGV("getFrameNumber");
363     Mutex::Autolock lock(mMutex);
364     return mCurrentFrameNumber;
365 }
366 
getTransformToDisplayInverse() const367 bool BufferLayerConsumer::getTransformToDisplayInverse() const {
368     Mutex::Autolock lock(mMutex);
369     return mCurrentTransformToDisplayInverse;
370 }
371 
getSurfaceDamage() const372 const Region& BufferLayerConsumer::getSurfaceDamage() const {
373     return mCurrentSurfaceDamage;
374 }
375 
mergeSurfaceDamage(const Region & damage)376 void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
377     if (damage.bounds() == Rect::INVALID_RECT ||
378         mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
379         mCurrentSurfaceDamage = Region::INVALID_REGION;
380     } else {
381         mCurrentSurfaceDamage |= damage;
382     }
383 }
384 
getCurrentApi() const385 int BufferLayerConsumer::getCurrentApi() const {
386     Mutex::Autolock lock(mMutex);
387     return mCurrentApi;
388 }
389 
getCurrentBuffer(int * outSlot,sp<Fence> * outFence) const390 sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
391     Mutex::Autolock lock(mMutex);
392 
393     if (outSlot != nullptr) {
394         *outSlot = mCurrentTexture;
395     }
396 
397     if (outFence != nullptr) {
398         *outFence = mCurrentFence;
399     }
400 
401     return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
402 }
403 
getCurrentCrop() const404 Rect BufferLayerConsumer::getCurrentCrop() const {
405     Mutex::Autolock lock(mMutex);
406     return getCurrentCropLocked();
407 }
408 
getCurrentCropLocked() const409 Rect BufferLayerConsumer::getCurrentCropLocked() const {
410     uint32_t width = mDefaultWidth;
411     uint32_t height = mDefaultHeight;
412     // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
413     // to scale and crop correctly.
414     if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
415         width = mDefaultHeight;
416         height = mDefaultWidth;
417     }
418 
419     return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
420             ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
421             : mCurrentCrop;
422 }
423 
getCurrentTransform() const424 uint32_t BufferLayerConsumer::getCurrentTransform() const {
425     Mutex::Autolock lock(mMutex);
426     return mCurrentTransform;
427 }
428 
getCurrentScalingMode() const429 uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
430     Mutex::Autolock lock(mMutex);
431     return mCurrentScalingMode;
432 }
433 
getCurrentFence() const434 sp<Fence> BufferLayerConsumer::getCurrentFence() const {
435     Mutex::Autolock lock(mMutex);
436     return mCurrentFence;
437 }
438 
getCurrentFenceTime() const439 std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
440     Mutex::Autolock lock(mMutex);
441     return mCurrentFenceTime;
442 }
443 
freeBufferLocked(int slotIndex)444 void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
445     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
446     std::lock_guard<std::mutex> lock(mImagesMutex);
447     if (slotIndex == mCurrentTexture) {
448         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
449     }
450     mImages[slotIndex] = nullptr;
451     ConsumerBase::freeBufferLocked(slotIndex);
452 }
453 
onDisconnect()454 void BufferLayerConsumer::onDisconnect() {
455     Mutex::Autolock lock(mMutex);
456 
457     if (mAbandoned) {
458         // Nothing to do if we're already abandoned.
459         return;
460     }
461 
462     mLayer->onDisconnect();
463 }
464 
onSidebandStreamChanged()465 void BufferLayerConsumer::onSidebandStreamChanged() {
466     FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
467     {
468         Mutex::Autolock lock(mFrameAvailableMutex);
469         unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
470     }
471     sp<ContentsChangedListener> listener;
472     { // scope for the lock
473         Mutex::Autolock lock(mMutex);
474         ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
475         listener = mContentsChangedListener.promote();
476     }
477 
478     if (listener != nullptr) {
479         listener->onSidebandStreamChanged();
480     }
481 }
482 
onBufferAvailable(const BufferItem & item)483 void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
484     if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
485         std::lock_guard<std::mutex> lock(mImagesMutex);
486         const std::shared_ptr<Image>& oldImage = mImages[item.mSlot];
487         if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
488             oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
489             mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
490         }
491     }
492 }
493 
addAndGetFrameTimestamps(const NewFrameEventsEntry * newTimestamps,FrameEventHistoryDelta * outDelta)494 void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
495                                                    FrameEventHistoryDelta* outDelta) {
496     Mutex::Autolock lock(mMutex);
497 
498     if (mAbandoned) {
499         // Nothing to do if we're already abandoned.
500         return;
501     }
502 
503     mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
504 }
505 
abandonLocked()506 void BufferLayerConsumer::abandonLocked() {
507     BLC_LOGV("abandonLocked");
508     mCurrentTextureBuffer = nullptr;
509     for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
510         std::lock_guard<std::mutex> lock(mImagesMutex);
511         mImages[i] = nullptr;
512     }
513     ConsumerBase::abandonLocked();
514 }
515 
setConsumerUsageBits(uint64_t usage)516 status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
517     return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
518 }
519 
dumpLocked(String8 & result,const char * prefix) const520 void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
521     result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
522                         "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
523                         prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
524                         mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
525                         mCurrentTransform);
526 
527     ConsumerBase::dumpLocked(result, prefix);
528 }
529 
Image(const sp<GraphicBuffer> & graphicBuffer,renderengine::RenderEngine & engine)530 BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
531                                   renderengine::RenderEngine& engine)
532       : mGraphicBuffer(graphicBuffer), mRE(engine) {
533     mRE.cacheExternalTextureBuffer(mGraphicBuffer);
534 }
535 
~Image()536 BufferLayerConsumer::Image::~Image() {
537     if (mGraphicBuffer != nullptr) {
538         ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
539         mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
540     }
541 }
542 }; // namespace android
543 
544 // TODO(b/129481165): remove the #pragma below and fix conversion issues
545 #pragma clang diagnostic pop // ignored "-Wconversion"
546