SurfaceTextureClient.cpp revision bb66c9b5a9c16dee93559eb738746a2d0a9b2db3
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#define LOG_TAG "SurfaceTextureClient"
18//#define LOG_NDEBUG 0
19
20#include <gui/SurfaceTextureClient.h>
21
22#include <utils/Log.h>
23
24namespace android {
25
26SurfaceTextureClient::SurfaceTextureClient(
27        const sp<ISurfaceTexture>& surfaceTexture)
28{
29    SurfaceTextureClient::init();
30    SurfaceTextureClient::setISurfaceTexture(surfaceTexture);
31}
32
33SurfaceTextureClient::SurfaceTextureClient() {
34    SurfaceTextureClient::init();
35}
36
37void SurfaceTextureClient::init() {
38    // Initialize the ANativeWindow function pointers.
39    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
40    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
41    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
42    ANativeWindow::lockBuffer       = hook_lockBuffer;
43    ANativeWindow::queueBuffer      = hook_queueBuffer;
44    ANativeWindow::query            = hook_query;
45    ANativeWindow::perform          = hook_perform;
46
47    const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
48    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
49
50    mReqWidth = 0;
51    mReqHeight = 0;
52    mReqFormat = 0;
53    mReqUsage = 0;
54    mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
55    mQueryWidth = 0;
56    mQueryHeight = 0;
57    mQueryFormat = 0;
58    mDefaultWidth = 0;
59    mDefaultHeight = 0;
60    mTransformHint = 0;
61    mConnectedToCpu = false;
62}
63
64void SurfaceTextureClient::setISurfaceTexture(
65        const sp<ISurfaceTexture>& surfaceTexture)
66{
67    mSurfaceTexture = surfaceTexture;
68
69    // Get a reference to the allocator.
70    mAllocator = mSurfaceTexture->getAllocator();
71}
72
73sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const {
74    return mSurfaceTexture;
75}
76
77int SurfaceTextureClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
78    SurfaceTextureClient* c = getSelf(window);
79    return c->setSwapInterval(interval);
80}
81
82int SurfaceTextureClient::hook_dequeueBuffer(ANativeWindow* window,
83        ANativeWindowBuffer** buffer) {
84    SurfaceTextureClient* c = getSelf(window);
85    return c->dequeueBuffer(buffer);
86}
87
88int SurfaceTextureClient::hook_cancelBuffer(ANativeWindow* window,
89        ANativeWindowBuffer* buffer) {
90    SurfaceTextureClient* c = getSelf(window);
91    return c->cancelBuffer(buffer);
92}
93
94int SurfaceTextureClient::hook_lockBuffer(ANativeWindow* window,
95        ANativeWindowBuffer* buffer) {
96    SurfaceTextureClient* c = getSelf(window);
97    return c->lockBuffer(buffer);
98}
99
100int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window,
101        ANativeWindowBuffer* buffer) {
102    SurfaceTextureClient* c = getSelf(window);
103    return c->queueBuffer(buffer);
104}
105
106int SurfaceTextureClient::hook_query(const ANativeWindow* window,
107                                int what, int* value) {
108    const SurfaceTextureClient* c = getSelf(window);
109    return c->query(what, value);
110}
111
112int SurfaceTextureClient::hook_perform(ANativeWindow* window, int operation, ...) {
113    va_list args;
114    va_start(args, operation);
115    SurfaceTextureClient* c = getSelf(window);
116    return c->perform(operation, args);
117}
118
119int SurfaceTextureClient::setSwapInterval(int interval) {
120    // EGL specification states:
121    //  interval is silently clamped to minimum and maximum implementation
122    //  dependent values before being stored.
123    // Although we don't have to, we apply the same logic here.
124
125    if (interval < minSwapInterval)
126        interval = minSwapInterval;
127
128    if (interval > maxSwapInterval)
129        interval = maxSwapInterval;
130
131    status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
132
133    return res;
134}
135
136int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
137    LOGV("SurfaceTextureClient::dequeueBuffer");
138    Mutex::Autolock lock(mMutex);
139    int buf = -1;
140    status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
141            mReqFormat, mReqUsage);
142    if (result < 0) {
143        LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
144             "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
145             result);
146        return result;
147    }
148    sp<GraphicBuffer>& gbuf(mSlots[buf]);
149    if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
150        freeAllBuffers();
151    }
152
153    if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
154        gbuf = mSurfaceTexture->requestBuffer(buf);
155        if (gbuf == 0) {
156            LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
157            return NO_MEMORY;
158        }
159        mQueryWidth  = gbuf->width;
160        mQueryHeight = gbuf->height;
161        mQueryFormat = gbuf->format;
162    }
163    *buffer = gbuf.get();
164    return OK;
165}
166
167int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) {
168    LOGV("SurfaceTextureClient::cancelBuffer");
169    Mutex::Autolock lock(mMutex);
170    int i = getSlotFromBufferLocked(buffer);
171    if (i < 0) {
172        return i;
173    }
174    mSurfaceTexture->cancelBuffer(i);
175    return OK;
176}
177
178int SurfaceTextureClient::getSlotFromBufferLocked(
179        android_native_buffer_t* buffer) const {
180    bool dumpedState = false;
181    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
182        // XXX: Dump the slots whenever we hit a NULL entry while searching for
183        // a buffer.
184        if (mSlots[i] == NULL) {
185            if (!dumpedState) {
186                LOGD("getSlotFromBufferLocked: encountered NULL buffer in slot %d "
187                        "looking for buffer %p", i, buffer->handle);
188                for (int j = 0; j < NUM_BUFFER_SLOTS; j++) {
189                    if (mSlots[j] == NULL) {
190                        LOGD("getSlotFromBufferLocked:   %02d: NULL", j);
191                    } else {
192                        LOGD("getSlotFromBufferLocked:   %02d: %p", j, mSlots[j]->handle);
193                    }
194                }
195                dumpedState = true;
196            }
197        }
198
199        if (mSlots[i] != NULL && mSlots[i]->handle == buffer->handle) {
200            return i;
201        }
202    }
203    LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
204    return BAD_VALUE;
205}
206
207int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) {
208    LOGV("SurfaceTextureClient::lockBuffer");
209    Mutex::Autolock lock(mMutex);
210    return OK;
211}
212
213int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
214    LOGV("SurfaceTextureClient::queueBuffer");
215    Mutex::Autolock lock(mMutex);
216    int64_t timestamp;
217    if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
218        timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
219        LOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
220             timestamp / 1000000.f);
221    } else {
222        timestamp = mTimestamp;
223    }
224    int i = getSlotFromBufferLocked(buffer);
225    if (i < 0) {
226        return i;
227    }
228    mSurfaceTexture->queueBuffer(i, timestamp,
229            &mDefaultWidth, &mDefaultHeight, &mTransformHint);
230    return OK;
231}
232
233int SurfaceTextureClient::query(int what, int* value) const {
234    LOGV("SurfaceTextureClient::query");
235    { // scope for the lock
236        Mutex::Autolock lock(mMutex);
237        switch (what) {
238            case NATIVE_WINDOW_FORMAT:
239                if (mReqFormat) {
240                    *value = mReqFormat;
241                    return NO_ERROR;
242                }
243                break;
244            case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
245                *value = 0;
246                return NO_ERROR;
247            case NATIVE_WINDOW_CONCRETE_TYPE:
248                *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
249                return NO_ERROR;
250            case NATIVE_WINDOW_DEFAULT_WIDTH:
251                *value = mDefaultWidth;
252                return NO_ERROR;
253            case NATIVE_WINDOW_DEFAULT_HEIGHT:
254                *value = mDefaultHeight;
255                return NO_ERROR;
256            case NATIVE_WINDOW_TRANSFORM_HINT:
257                *value = mTransformHint;
258                return NO_ERROR;
259        }
260    }
261    return mSurfaceTexture->query(what, value);
262}
263
264int SurfaceTextureClient::perform(int operation, va_list args)
265{
266    int res = NO_ERROR;
267    switch (operation) {
268    case NATIVE_WINDOW_CONNECT:
269        res = dispatchConnect(args);
270        break;
271    case NATIVE_WINDOW_DISCONNECT:
272        res = dispatchDisconnect(args);
273        break;
274    case NATIVE_WINDOW_SET_USAGE:
275        res = dispatchSetUsage(args);
276        break;
277    case NATIVE_WINDOW_SET_CROP:
278        res = dispatchSetCrop(args);
279        break;
280    case NATIVE_WINDOW_SET_BUFFER_COUNT:
281        res = dispatchSetBufferCount(args);
282        break;
283    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
284        res = dispatchSetBuffersGeometry(args);
285        break;
286    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
287        res = dispatchSetBuffersTransform(args);
288        break;
289    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
290        res = dispatchSetBuffersTimestamp(args);
291        break;
292    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
293        res = dispatchSetBuffersDimensions(args);
294        break;
295    case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
296        res = dispatchSetBuffersFormat(args);
297        break;
298    case NATIVE_WINDOW_LOCK:
299        res = dispatchLock(args);
300        break;
301    case NATIVE_WINDOW_UNLOCK_AND_POST:
302        res = dispatchUnlockAndPost(args);
303        break;
304    case NATIVE_WINDOW_SET_SCALING_MODE:
305        res = dispatchSetScalingMode(args);
306        break;
307    default:
308        res = NAME_NOT_FOUND;
309        break;
310    }
311    return res;
312}
313
314int SurfaceTextureClient::dispatchConnect(va_list args) {
315    int api = va_arg(args, int);
316    return connect(api);
317}
318
319int SurfaceTextureClient::dispatchDisconnect(va_list args) {
320    int api = va_arg(args, int);
321    return disconnect(api);
322}
323
324int SurfaceTextureClient::dispatchSetUsage(va_list args) {
325    int usage = va_arg(args, int);
326    return setUsage(usage);
327}
328
329int SurfaceTextureClient::dispatchSetCrop(va_list args) {
330    android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
331    return setCrop(reinterpret_cast<Rect const*>(rect));
332}
333
334int SurfaceTextureClient::dispatchSetBufferCount(va_list args) {
335    size_t bufferCount = va_arg(args, size_t);
336    return setBufferCount(bufferCount);
337}
338
339int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) {
340    int w = va_arg(args, int);
341    int h = va_arg(args, int);
342    int f = va_arg(args, int);
343    int err = setBuffersDimensions(w, h);
344    if (err != 0) {
345        return err;
346    }
347    return setBuffersFormat(f);
348}
349
350int SurfaceTextureClient::dispatchSetBuffersDimensions(va_list args) {
351    int w = va_arg(args, int);
352    int h = va_arg(args, int);
353    return setBuffersDimensions(w, h);
354}
355
356int SurfaceTextureClient::dispatchSetBuffersFormat(va_list args) {
357    int f = va_arg(args, int);
358    return setBuffersFormat(f);
359}
360
361int SurfaceTextureClient::dispatchSetScalingMode(va_list args) {
362    int m = va_arg(args, int);
363    return setScalingMode(m);
364}
365
366int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) {
367    int transform = va_arg(args, int);
368    return setBuffersTransform(transform);
369}
370
371int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
372    int64_t timestamp = va_arg(args, int64_t);
373    return setBuffersTimestamp(timestamp);
374}
375
376int SurfaceTextureClient::dispatchLock(va_list args) {
377    ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
378    ARect* inOutDirtyBounds = va_arg(args, ARect*);
379    return lock(outBuffer, inOutDirtyBounds);
380}
381
382int SurfaceTextureClient::dispatchUnlockAndPost(va_list args) {
383    return unlockAndPost();
384}
385
386
387int SurfaceTextureClient::connect(int api) {
388    LOGV("SurfaceTextureClient::connect");
389    Mutex::Autolock lock(mMutex);
390    int err = mSurfaceTexture->connect(api);
391    if (!err && api == NATIVE_WINDOW_API_CPU) {
392        mConnectedToCpu = true;
393    }
394    return err;
395}
396
397int SurfaceTextureClient::disconnect(int api) {
398    LOGV("SurfaceTextureClient::disconnect");
399    Mutex::Autolock lock(mMutex);
400    int err = mSurfaceTexture->disconnect(api);
401    if (!err && api == NATIVE_WINDOW_API_CPU) {
402        mConnectedToCpu = false;
403    }
404    return err;
405}
406
407int SurfaceTextureClient::setUsage(uint32_t reqUsage)
408{
409    LOGV("SurfaceTextureClient::setUsage");
410    Mutex::Autolock lock(mMutex);
411    mReqUsage = reqUsage;
412    return OK;
413}
414
415int SurfaceTextureClient::setCrop(Rect const* rect)
416{
417    LOGV("SurfaceTextureClient::setCrop");
418    Mutex::Autolock lock(mMutex);
419
420    Rect realRect;
421    if (rect == NULL || rect->isEmpty()) {
422        realRect = Rect(0, 0);
423    } else {
424        realRect = *rect;
425    }
426
427    status_t err = mSurfaceTexture->setCrop(*rect);
428    LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err));
429
430    return err;
431}
432
433int SurfaceTextureClient::setBufferCount(int bufferCount)
434{
435    LOGV("SurfaceTextureClient::setBufferCount");
436    Mutex::Autolock lock(mMutex);
437
438    status_t err = mSurfaceTexture->setBufferCount(bufferCount);
439    LOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s",
440            bufferCount, strerror(-err));
441
442    if (err == NO_ERROR) {
443        freeAllBuffers();
444    }
445
446    return err;
447}
448
449int SurfaceTextureClient::setBuffersDimensions(int w, int h)
450{
451    LOGV("SurfaceTextureClient::setBuffersDimensions");
452    Mutex::Autolock lock(mMutex);
453
454    if (w<0 || h<0)
455        return BAD_VALUE;
456
457    if ((w && !h) || (!w && h))
458        return BAD_VALUE;
459
460    mReqWidth = w;
461    mReqHeight = h;
462
463    status_t err = mSurfaceTexture->setCrop(Rect(0, 0));
464    LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err));
465
466    return err;
467}
468
469int SurfaceTextureClient::setBuffersFormat(int format)
470{
471    LOGV("SurfaceTextureClient::setBuffersFormat");
472    Mutex::Autolock lock(mMutex);
473
474    if (format<0)
475        return BAD_VALUE;
476
477    mReqFormat = format;
478
479    return NO_ERROR;
480}
481
482int SurfaceTextureClient::setScalingMode(int mode)
483{
484    LOGV("SurfaceTextureClient::setScalingMode(%d)", mode);
485    Mutex::Autolock lock(mMutex);
486    // mode is validated on the server
487    status_t err = mSurfaceTexture->setScalingMode(mode);
488    LOGE_IF(err, "ISurfaceTexture::setScalingMode(%d) returned %s",
489            mode, strerror(-err));
490
491    return err;
492}
493
494int SurfaceTextureClient::setBuffersTransform(int transform)
495{
496    LOGV("SurfaceTextureClient::setBuffersTransform");
497    Mutex::Autolock lock(mMutex);
498    status_t err = mSurfaceTexture->setTransform(transform);
499    return err;
500}
501
502int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
503{
504    LOGV("SurfaceTextureClient::setBuffersTimestamp");
505    Mutex::Autolock lock(mMutex);
506    mTimestamp = timestamp;
507    return NO_ERROR;
508}
509
510void SurfaceTextureClient::freeAllBuffers() {
511    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
512        mSlots[i] = 0;
513    }
514}
515
516// ----------------------------------------------------------------------
517// the lock/unlock APIs must be used from the same thread
518
519static status_t copyBlt(
520        const sp<GraphicBuffer>& dst,
521        const sp<GraphicBuffer>& src,
522        const Region& reg)
523{
524    // src and dst with, height and format must be identical. no verification
525    // is done here.
526    status_t err;
527    uint8_t const * src_bits = NULL;
528    err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
529    LOGE_IF(err, "error locking src buffer %s", strerror(-err));
530
531    uint8_t* dst_bits = NULL;
532    err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
533    LOGE_IF(err, "error locking dst buffer %s", strerror(-err));
534
535    Region::const_iterator head(reg.begin());
536    Region::const_iterator tail(reg.end());
537    if (head != tail && src_bits && dst_bits) {
538        const size_t bpp = bytesPerPixel(src->format);
539        const size_t dbpr = dst->stride * bpp;
540        const size_t sbpr = src->stride * bpp;
541
542        while (head != tail) {
543            const Rect& r(*head++);
544            ssize_t h = r.height();
545            if (h <= 0) continue;
546            size_t size = r.width() * bpp;
547            uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
548            uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
549            if (dbpr==sbpr && size==sbpr) {
550                size *= h;
551                h = 1;
552            }
553            do {
554                memcpy(d, s, size);
555                d += dbpr;
556                s += sbpr;
557            } while (--h > 0);
558        }
559    }
560
561    if (src_bits)
562        src->unlock();
563
564    if (dst_bits)
565        dst->unlock();
566
567    return err;
568}
569
570// ----------------------------------------------------------------------------
571
572status_t SurfaceTextureClient::lock(
573        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
574{
575    if (mLockedBuffer != 0) {
576        LOGE("Surface::lock failed, already locked");
577        return INVALID_OPERATION;
578    }
579
580    if (!mConnectedToCpu) {
581        int err = SurfaceTextureClient::connect(NATIVE_WINDOW_API_CPU);
582        if (err) {
583            return err;
584        }
585        // we're intending to do software rendering from this point
586        setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
587    }
588
589    ANativeWindowBuffer* out;
590    status_t err = dequeueBuffer(&out);
591    LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
592    if (err == NO_ERROR) {
593        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
594        err = lockBuffer(backBuffer.get());
595        LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)",
596                backBuffer->handle, strerror(-err));
597        if (err == NO_ERROR) {
598            const Rect bounds(backBuffer->width, backBuffer->height);
599
600            Region newDirtyRegion;
601            if (inOutDirtyBounds) {
602                newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
603                newDirtyRegion.andSelf(bounds);
604            } else {
605                newDirtyRegion.set(bounds);
606            }
607
608            // figure out if we can copy the frontbuffer back
609            const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
610            const bool canCopyBack = (frontBuffer != 0 &&
611                    backBuffer->width  == frontBuffer->width &&
612                    backBuffer->height == frontBuffer->height &&
613                    backBuffer->format == frontBuffer->format);
614
615            if (canCopyBack) {
616                // copy the area that is invalid and not repainted this round
617                const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
618                if (!copyback.isEmpty())
619                    copyBlt(backBuffer, frontBuffer, copyback);
620            } else {
621                // if we can't copy-back anything, modify the user's dirty
622                // region to make sure they redraw the whole buffer
623                newDirtyRegion.set(bounds);
624            }
625
626            // keep track of the are of the buffer that is "clean"
627            // (ie: that will be redrawn)
628            mOldDirtyRegion = newDirtyRegion;
629
630            if (inOutDirtyBounds) {
631                *inOutDirtyBounds = newDirtyRegion.getBounds();
632            }
633
634            void* vaddr;
635            status_t res = backBuffer->lock(
636                    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
637                    newDirtyRegion.bounds(), &vaddr);
638
639            LOGW_IF(res, "failed locking buffer (handle = %p)",
640                    backBuffer->handle);
641
642            mLockedBuffer = backBuffer;
643            outBuffer->width  = backBuffer->width;
644            outBuffer->height = backBuffer->height;
645            outBuffer->stride = backBuffer->stride;
646            outBuffer->format = backBuffer->format;
647            outBuffer->bits   = vaddr;
648        }
649    }
650    return err;
651}
652
653status_t SurfaceTextureClient::unlockAndPost()
654{
655    if (mLockedBuffer == 0) {
656        LOGE("Surface::unlockAndPost failed, no locked buffer");
657        return INVALID_OPERATION;
658    }
659
660    status_t err = mLockedBuffer->unlock();
661    LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
662
663    err = queueBuffer(mLockedBuffer.get());
664    LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
665            mLockedBuffer->handle, strerror(-err));
666
667    mPostedBuffer = mLockedBuffer;
668    mLockedBuffer = 0;
669    return err;
670}
671
672}; // namespace android
673