1/*
2 * Copyright (c) 2009 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_NDEBUG 0
18#define LOG_TAG "IOMX"
19#include <utils/Log.h>
20
21#include <binder/IMemory.h>
22#include <binder/Parcel.h>
23#include <media/IOMX.h>
24#include <media/stagefright/foundation/ADebug.h>
25#include <surfaceflinger/ISurface.h>
26#include <surfaceflinger/Surface.h>
27
28namespace android {
29
30enum {
31    CONNECT = IBinder::FIRST_CALL_TRANSACTION,
32    LIVES_LOCALLY,
33    LIST_NODES,
34    ALLOCATE_NODE,
35    FREE_NODE,
36    SEND_COMMAND,
37    GET_PARAMETER,
38    SET_PARAMETER,
39    GET_CONFIG,
40    SET_CONFIG,
41    GET_STATE,
42    ENABLE_GRAPHIC_BUFFERS,
43    USE_BUFFER,
44    USE_GRAPHIC_BUFFER,
45    STORE_META_DATA_IN_BUFFERS,
46    ALLOC_BUFFER,
47    ALLOC_BUFFER_WITH_BACKUP,
48    FREE_BUFFER,
49    FILL_BUFFER,
50    EMPTY_BUFFER,
51    GET_EXTENSION_INDEX,
52    OBSERVER_ON_MSG,
53    GET_GRAPHIC_BUFFER_USAGE,
54};
55
56class BpOMX : public BpInterface<IOMX> {
57public:
58    BpOMX(const sp<IBinder> &impl)
59        : BpInterface<IOMX>(impl) {
60    }
61
62    virtual bool livesLocally(pid_t pid) {
63        Parcel data, reply;
64        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
65        data.writeInt32(pid);
66        remote()->transact(LIVES_LOCALLY, data, &reply);
67
68        return reply.readInt32() != 0;
69    }
70
71    virtual status_t listNodes(List<ComponentInfo> *list) {
72        list->clear();
73
74        Parcel data, reply;
75        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
76        remote()->transact(LIST_NODES, data, &reply);
77
78        int32_t n = reply.readInt32();
79        for (int32_t i = 0; i < n; ++i) {
80            list->push_back(ComponentInfo());
81            ComponentInfo &info = *--list->end();
82
83            info.mName = reply.readString8();
84            int32_t numRoles = reply.readInt32();
85            for (int32_t j = 0; j < numRoles; ++j) {
86                info.mRoles.push_back(reply.readString8());
87            }
88        }
89
90        return OK;
91    }
92
93    virtual status_t allocateNode(
94            const char *name, const sp<IOMXObserver> &observer, node_id *node) {
95        Parcel data, reply;
96        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
97        data.writeCString(name);
98        data.writeStrongBinder(observer->asBinder());
99        remote()->transact(ALLOCATE_NODE, data, &reply);
100
101        status_t err = reply.readInt32();
102        if (err == OK) {
103            *node = (void*)reply.readIntPtr();
104        } else {
105            *node = 0;
106        }
107
108        return err;
109    }
110
111    virtual status_t freeNode(node_id node) {
112        Parcel data, reply;
113        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
114        data.writeIntPtr((intptr_t)node);
115        remote()->transact(FREE_NODE, data, &reply);
116
117        return reply.readInt32();
118    }
119
120    virtual status_t sendCommand(
121            node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
122        Parcel data, reply;
123        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
124        data.writeIntPtr((intptr_t)node);
125        data.writeInt32(cmd);
126        data.writeInt32(param);
127        remote()->transact(SEND_COMMAND, data, &reply);
128
129        return reply.readInt32();
130    }
131
132    virtual status_t getParameter(
133            node_id node, OMX_INDEXTYPE index,
134            void *params, size_t size) {
135        Parcel data, reply;
136        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
137        data.writeIntPtr((intptr_t)node);
138        data.writeInt32(index);
139        data.writeInt32(size);
140        data.write(params, size);
141        remote()->transact(GET_PARAMETER, data, &reply);
142
143        status_t err = reply.readInt32();
144        if (err != OK) {
145            return err;
146        }
147
148        reply.read(params, size);
149
150        return OK;
151    }
152
153    virtual status_t setParameter(
154            node_id node, OMX_INDEXTYPE index,
155            const void *params, size_t size) {
156        Parcel data, reply;
157        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
158        data.writeIntPtr((intptr_t)node);
159        data.writeInt32(index);
160        data.writeInt32(size);
161        data.write(params, size);
162        remote()->transact(SET_PARAMETER, data, &reply);
163
164        return reply.readInt32();
165    }
166
167    virtual status_t getConfig(
168            node_id node, OMX_INDEXTYPE index,
169            void *params, size_t size) {
170        Parcel data, reply;
171        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
172        data.writeIntPtr((intptr_t)node);
173        data.writeInt32(index);
174        data.writeInt32(size);
175        data.write(params, size);
176        remote()->transact(GET_CONFIG, data, &reply);
177
178        status_t err = reply.readInt32();
179        if (err != OK) {
180            return err;
181        }
182
183        reply.read(params, size);
184
185        return OK;
186    }
187
188    virtual status_t setConfig(
189            node_id node, OMX_INDEXTYPE index,
190            const void *params, size_t size) {
191        Parcel data, reply;
192        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
193        data.writeIntPtr((intptr_t)node);
194        data.writeInt32(index);
195        data.writeInt32(size);
196        data.write(params, size);
197        remote()->transact(SET_CONFIG, data, &reply);
198
199        return reply.readInt32();
200    }
201
202    virtual status_t getState(
203            node_id node, OMX_STATETYPE* state) {
204        Parcel data, reply;
205        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
206        data.writeIntPtr((intptr_t)node);
207        remote()->transact(GET_STATE, data, &reply);
208
209        *state = static_cast<OMX_STATETYPE>(reply.readInt32());
210        return reply.readInt32();
211    }
212
213    virtual status_t enableGraphicBuffers(
214            node_id node, OMX_U32 port_index, OMX_BOOL enable) {
215        Parcel data, reply;
216        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
217        data.writeIntPtr((intptr_t)node);
218        data.writeInt32(port_index);
219        data.writeInt32((uint32_t)enable);
220        remote()->transact(ENABLE_GRAPHIC_BUFFERS, data, &reply);
221
222        status_t err = reply.readInt32();
223        return err;
224    }
225
226    virtual status_t getGraphicBufferUsage(
227            node_id node, OMX_U32 port_index, OMX_U32* usage) {
228        Parcel data, reply;
229        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
230        data.writeIntPtr((intptr_t)node);
231        data.writeInt32(port_index);
232        remote()->transact(GET_GRAPHIC_BUFFER_USAGE, data, &reply);
233
234        status_t err = reply.readInt32();
235        *usage = reply.readInt32();
236        return err;
237    }
238
239    virtual status_t useBuffer(
240            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
241            buffer_id *buffer) {
242        Parcel data, reply;
243        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
244        data.writeIntPtr((intptr_t)node);
245        data.writeInt32(port_index);
246        data.writeStrongBinder(params->asBinder());
247        remote()->transact(USE_BUFFER, data, &reply);
248
249        status_t err = reply.readInt32();
250        if (err != OK) {
251            *buffer = 0;
252
253            return err;
254        }
255
256        *buffer = (void*)reply.readIntPtr();
257
258        return err;
259    }
260
261
262    virtual status_t useGraphicBuffer(
263            node_id node, OMX_U32 port_index,
264            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
265        Parcel data, reply;
266        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
267        data.writeIntPtr((intptr_t)node);
268        data.writeInt32(port_index);
269        data.write(*graphicBuffer);
270        remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
271
272        status_t err = reply.readInt32();
273        if (err != OK) {
274            *buffer = 0;
275
276            return err;
277        }
278
279        *buffer = (void*)reply.readIntPtr();
280
281        return err;
282    }
283
284    virtual status_t storeMetaDataInBuffers(
285            node_id node, OMX_U32 port_index, OMX_BOOL enable) {
286        Parcel data, reply;
287        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
288        data.writeIntPtr((intptr_t)node);
289        data.writeInt32(port_index);
290        data.writeInt32((uint32_t)enable);
291        remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply);
292
293        status_t err = reply.readInt32();
294        return err;
295    }
296
297    virtual status_t allocateBuffer(
298            node_id node, OMX_U32 port_index, size_t size,
299            buffer_id *buffer, void **buffer_data) {
300        Parcel data, reply;
301        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
302        data.writeIntPtr((intptr_t)node);
303        data.writeInt32(port_index);
304        data.writeInt32(size);
305        remote()->transact(ALLOC_BUFFER, data, &reply);
306
307        status_t err = reply.readInt32();
308        if (err != OK) {
309            *buffer = 0;
310
311            return err;
312        }
313
314        *buffer = (void *)reply.readIntPtr();
315        *buffer_data = (void *)reply.readIntPtr();
316
317        return err;
318    }
319
320    virtual status_t allocateBufferWithBackup(
321            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
322            buffer_id *buffer) {
323        Parcel data, reply;
324        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
325        data.writeIntPtr((intptr_t)node);
326        data.writeInt32(port_index);
327        data.writeStrongBinder(params->asBinder());
328        remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
329
330        status_t err = reply.readInt32();
331        if (err != OK) {
332            *buffer = 0;
333
334            return err;
335        }
336
337        *buffer = (void*)reply.readIntPtr();
338
339        return err;
340    }
341
342    virtual status_t freeBuffer(
343            node_id node, OMX_U32 port_index, buffer_id buffer) {
344        Parcel data, reply;
345        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
346        data.writeIntPtr((intptr_t)node);
347        data.writeInt32(port_index);
348        data.writeIntPtr((intptr_t)buffer);
349        remote()->transact(FREE_BUFFER, data, &reply);
350
351        return reply.readInt32();
352    }
353
354    virtual status_t fillBuffer(node_id node, buffer_id buffer) {
355        Parcel data, reply;
356        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
357        data.writeIntPtr((intptr_t)node);
358        data.writeIntPtr((intptr_t)buffer);
359        remote()->transact(FILL_BUFFER, data, &reply);
360
361        return reply.readInt32();
362    }
363
364    virtual status_t emptyBuffer(
365            node_id node,
366            buffer_id buffer,
367            OMX_U32 range_offset, OMX_U32 range_length,
368            OMX_U32 flags, OMX_TICKS timestamp) {
369        Parcel data, reply;
370        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
371        data.writeIntPtr((intptr_t)node);
372        data.writeIntPtr((intptr_t)buffer);
373        data.writeInt32(range_offset);
374        data.writeInt32(range_length);
375        data.writeInt32(flags);
376        data.writeInt64(timestamp);
377        remote()->transact(EMPTY_BUFFER, data, &reply);
378
379        return reply.readInt32();
380    }
381
382    virtual status_t getExtensionIndex(
383            node_id node,
384            const char *parameter_name,
385            OMX_INDEXTYPE *index) {
386        Parcel data, reply;
387        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
388        data.writeIntPtr((intptr_t)node);
389        data.writeCString(parameter_name);
390
391        remote()->transact(GET_EXTENSION_INDEX, data, &reply);
392
393        status_t err = reply.readInt32();
394        if (err == OK) {
395            *index = static_cast<OMX_INDEXTYPE>(reply.readInt32());
396        } else {
397            *index = OMX_IndexComponentStartUnused;
398        }
399
400        return err;
401    }
402};
403
404IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
405
406////////////////////////////////////////////////////////////////////////////////
407
408#define CHECK_INTERFACE(interface, data, reply) \
409        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
410            LOGW("Call incorrectly routed to " #interface); \
411            return PERMISSION_DENIED; \
412        } } while (0)
413
414status_t BnOMX::onTransact(
415    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
416    switch (code) {
417        case LIVES_LOCALLY:
418        {
419            CHECK_INTERFACE(IOMX, data, reply);
420            reply->writeInt32(livesLocally((pid_t)data.readInt32()));
421
422            return OK;
423        }
424
425        case LIST_NODES:
426        {
427            CHECK_INTERFACE(IOMX, data, reply);
428
429            List<ComponentInfo> list;
430            listNodes(&list);
431
432            reply->writeInt32(list.size());
433            for (List<ComponentInfo>::iterator it = list.begin();
434                 it != list.end(); ++it) {
435                ComponentInfo &cur = *it;
436
437                reply->writeString8(cur.mName);
438                reply->writeInt32(cur.mRoles.size());
439                for (List<String8>::iterator role_it = cur.mRoles.begin();
440                     role_it != cur.mRoles.end(); ++role_it) {
441                    reply->writeString8(*role_it);
442                }
443            }
444
445            return NO_ERROR;
446        }
447
448        case ALLOCATE_NODE:
449        {
450            CHECK_INTERFACE(IOMX, data, reply);
451
452            const char *name = data.readCString();
453
454            sp<IOMXObserver> observer =
455                interface_cast<IOMXObserver>(data.readStrongBinder());
456
457            node_id node;
458
459            status_t err = allocateNode(name, observer, &node);
460            reply->writeInt32(err);
461            if (err == OK) {
462                reply->writeIntPtr((intptr_t)node);
463            }
464
465            return NO_ERROR;
466        }
467
468        case FREE_NODE:
469        {
470            CHECK_INTERFACE(IOMX, data, reply);
471
472            node_id node = (void*)data.readIntPtr();
473
474            reply->writeInt32(freeNode(node));
475
476            return NO_ERROR;
477        }
478
479        case SEND_COMMAND:
480        {
481            CHECK_INTERFACE(IOMX, data, reply);
482
483            node_id node = (void*)data.readIntPtr();
484
485            OMX_COMMANDTYPE cmd =
486                static_cast<OMX_COMMANDTYPE>(data.readInt32());
487
488            OMX_S32 param = data.readInt32();
489            reply->writeInt32(sendCommand(node, cmd, param));
490
491            return NO_ERROR;
492        }
493
494        case GET_PARAMETER:
495        case SET_PARAMETER:
496        case GET_CONFIG:
497        case SET_CONFIG:
498        {
499            CHECK_INTERFACE(IOMX, data, reply);
500
501            node_id node = (void*)data.readIntPtr();
502            OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
503
504            size_t size = data.readInt32();
505
506            void *params = malloc(size);
507            data.read(params, size);
508
509            status_t err;
510            switch (code) {
511                case GET_PARAMETER:
512                    err = getParameter(node, index, params, size);
513                    break;
514                case SET_PARAMETER:
515                    err = setParameter(node, index, params, size);
516                    break;
517                case GET_CONFIG:
518                    err = getConfig(node, index, params, size);
519                    break;
520                case SET_CONFIG:
521                    err = setConfig(node, index, params, size);
522                    break;
523                default:
524                    TRESPASS();
525            }
526
527            reply->writeInt32(err);
528
529            if ((code == GET_PARAMETER || code == GET_CONFIG) && err == OK) {
530                reply->write(params, size);
531            }
532
533            free(params);
534            params = NULL;
535
536            return NO_ERROR;
537        }
538
539        case GET_STATE:
540        {
541            CHECK_INTERFACE(IOMX, data, reply);
542
543            node_id node = (void*)data.readIntPtr();
544            OMX_STATETYPE state = OMX_StateInvalid;
545
546            status_t err = getState(node, &state);
547            reply->writeInt32(state);
548            reply->writeInt32(err);
549
550            return NO_ERROR;
551        }
552
553        case ENABLE_GRAPHIC_BUFFERS:
554        {
555            CHECK_INTERFACE(IOMX, data, reply);
556
557            node_id node = (void*)data.readIntPtr();
558            OMX_U32 port_index = data.readInt32();
559            OMX_BOOL enable = (OMX_BOOL)data.readInt32();
560
561            status_t err = enableGraphicBuffers(node, port_index, enable);
562            reply->writeInt32(err);
563
564            return NO_ERROR;
565        }
566
567        case GET_GRAPHIC_BUFFER_USAGE:
568        {
569            CHECK_INTERFACE(IOMX, data, reply);
570
571            node_id node = (void*)data.readIntPtr();
572            OMX_U32 port_index = data.readInt32();
573
574            OMX_U32 usage = 0;
575            status_t err = getGraphicBufferUsage(node, port_index, &usage);
576            reply->writeInt32(err);
577            reply->writeInt32(usage);
578
579            return NO_ERROR;
580        }
581
582        case USE_BUFFER:
583        {
584            CHECK_INTERFACE(IOMX, data, reply);
585
586            node_id node = (void*)data.readIntPtr();
587            OMX_U32 port_index = data.readInt32();
588            sp<IMemory> params =
589                interface_cast<IMemory>(data.readStrongBinder());
590
591            buffer_id buffer;
592            status_t err = useBuffer(node, port_index, params, &buffer);
593            reply->writeInt32(err);
594
595            if (err == OK) {
596                reply->writeIntPtr((intptr_t)buffer);
597            }
598
599            return NO_ERROR;
600        }
601
602        case USE_GRAPHIC_BUFFER:
603        {
604            CHECK_INTERFACE(IOMX, data, reply);
605
606            node_id node = (void*)data.readIntPtr();
607            OMX_U32 port_index = data.readInt32();
608            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
609            data.read(*graphicBuffer);
610
611            buffer_id buffer;
612            status_t err = useGraphicBuffer(
613                    node, port_index, graphicBuffer, &buffer);
614            reply->writeInt32(err);
615
616            if (err == OK) {
617                reply->writeIntPtr((intptr_t)buffer);
618            }
619
620            return NO_ERROR;
621        }
622
623        case STORE_META_DATA_IN_BUFFERS:
624        {
625            CHECK_INTERFACE(IOMX, data, reply);
626
627            node_id node = (void*)data.readIntPtr();
628            OMX_U32 port_index = data.readInt32();
629            OMX_BOOL enable = (OMX_BOOL)data.readInt32();
630
631            status_t err = storeMetaDataInBuffers(node, port_index, enable);
632            reply->writeInt32(err);
633
634            return NO_ERROR;
635        }
636
637        case ALLOC_BUFFER:
638        {
639            CHECK_INTERFACE(IOMX, data, reply);
640
641            node_id node = (void*)data.readIntPtr();
642            OMX_U32 port_index = data.readInt32();
643            size_t size = data.readInt32();
644
645            buffer_id buffer;
646            void *buffer_data;
647            status_t err = allocateBuffer(
648                    node, port_index, size, &buffer, &buffer_data);
649            reply->writeInt32(err);
650
651            if (err == OK) {
652                reply->writeIntPtr((intptr_t)buffer);
653                reply->writeIntPtr((intptr_t)buffer_data);
654            }
655
656            return NO_ERROR;
657        }
658
659        case ALLOC_BUFFER_WITH_BACKUP:
660        {
661            CHECK_INTERFACE(IOMX, data, reply);
662
663            node_id node = (void*)data.readIntPtr();
664            OMX_U32 port_index = data.readInt32();
665            sp<IMemory> params =
666                interface_cast<IMemory>(data.readStrongBinder());
667
668            buffer_id buffer;
669            status_t err = allocateBufferWithBackup(
670                    node, port_index, params, &buffer);
671
672            reply->writeInt32(err);
673
674            if (err == OK) {
675                reply->writeIntPtr((intptr_t)buffer);
676            }
677
678            return NO_ERROR;
679        }
680
681        case FREE_BUFFER:
682        {
683            CHECK_INTERFACE(IOMX, data, reply);
684
685            node_id node = (void*)data.readIntPtr();
686            OMX_U32 port_index = data.readInt32();
687            buffer_id buffer = (void*)data.readIntPtr();
688            reply->writeInt32(freeBuffer(node, port_index, buffer));
689
690            return NO_ERROR;
691        }
692
693        case FILL_BUFFER:
694        {
695            CHECK_INTERFACE(IOMX, data, reply);
696
697            node_id node = (void*)data.readIntPtr();
698            buffer_id buffer = (void*)data.readIntPtr();
699            reply->writeInt32(fillBuffer(node, buffer));
700
701            return NO_ERROR;
702        }
703
704        case EMPTY_BUFFER:
705        {
706            CHECK_INTERFACE(IOMX, data, reply);
707
708            node_id node = (void*)data.readIntPtr();
709            buffer_id buffer = (void*)data.readIntPtr();
710            OMX_U32 range_offset = data.readInt32();
711            OMX_U32 range_length = data.readInt32();
712            OMX_U32 flags = data.readInt32();
713            OMX_TICKS timestamp = data.readInt64();
714
715            reply->writeInt32(
716                    emptyBuffer(
717                        node, buffer, range_offset, range_length,
718                        flags, timestamp));
719
720            return NO_ERROR;
721        }
722
723        case GET_EXTENSION_INDEX:
724        {
725            CHECK_INTERFACE(IOMX, data, reply);
726
727            node_id node = (void*)data.readIntPtr();
728            const char *parameter_name = data.readCString();
729
730            OMX_INDEXTYPE index;
731            status_t err = getExtensionIndex(node, parameter_name, &index);
732
733            reply->writeInt32(err);
734
735            if (err == OK) {
736                reply->writeInt32(index);
737            }
738
739            return OK;
740        }
741
742        default:
743            return BBinder::onTransact(code, data, reply, flags);
744    }
745}
746
747////////////////////////////////////////////////////////////////////////////////
748
749class BpOMXObserver : public BpInterface<IOMXObserver> {
750public:
751    BpOMXObserver(const sp<IBinder> &impl)
752        : BpInterface<IOMXObserver>(impl) {
753    }
754
755    virtual void onMessage(const omx_message &msg) {
756        Parcel data, reply;
757        data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
758        data.write(&msg, sizeof(msg));
759
760        remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
761    }
762};
763
764IMPLEMENT_META_INTERFACE(OMXObserver, "android.hardware.IOMXObserver");
765
766status_t BnOMXObserver::onTransact(
767    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
768    switch (code) {
769        case OBSERVER_ON_MSG:
770        {
771            CHECK_INTERFACE(IOMXObserver, data, reply);
772
773            omx_message msg;
774            data.read(&msg, sizeof(msg));
775
776            // XXX Could use readInplace maybe?
777            onMessage(msg);
778
779            return NO_ERROR;
780        }
781
782        default:
783            return BBinder::onTransact(code, data, reply, flags);
784    }
785}
786
787}  // namespace android
788