Home | History | Annotate | Download | only in 2.0
      1 /*
      2  * Copyright (C) 2018 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 package android.hardware.graphics.bufferqueue@2.0;
     18 
     19 import android.hardware.graphics.common@1.2::HardwareBuffer;
     20 import android.hardware.graphics.common@1.2::HardwareBufferDescription;
     21 import android.hardware.graphics.common@1.2::Rect;
     22 
     23 import IProducerListener;
     24 
     25 /**
     26  * Ref: frameworks/native/include/gui/IGraphicBufferProducer.h:
     27  *      IGraphicBufferProducer
     28  * This is a wrapper/wrapped HAL interface for the actual binder interface.
     29  */
     30 interface IGraphicBufferProducer {
     31     /**
     32      * Sets the maximum number of buffers that can be dequeued at one time. If
     33      * this method succeeds, any new buffer slots shall be both unallocated and
     34      * owned by the buffer queue, i.e., they are not owned by the producer or
     35      * the consumer. Calling this may cause some buffer slots to be emptied. If
     36      * the caller is caching the contents of the buffer slots, it must empty
     37      * that cache after calling this method.
     38      *
     39      * @p maxDequeuedBuffers must not be less than the number of currently
     40      * dequeued buffer slots; otherwise, the returned @p status shall be
     41      * `BAD_VALUE`.
     42      *
     43      * @p maxDequeuedBuffers must be at least 1 (inclusive), but at most
     44      * (`NUM_BUFFER_SLOTS` - the minimum undequeued buffer count) (exclusive).
     45      * The minimum undequeued buffer count can be obtained by calling
     46      * `query(ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS)`.
     47      *
     48      * Before calling setMaxDequeuedBufferCount(), the caller must make sure
     49      * that
     50      *   - @p maxDequeuedBuffers is greater than or equal to 1.
     51      *   - @p maxDequeuedBuffers is greater than or equal to the number of
     52      *     currently dequeued buffer slots.
     53      * If any of these conditions do not hold, or if the request to set the new
     54      * maximum number of dequeued buffers cannot be accomplished for any other
     55      * reasons, `BAD_VALUE` shall be returned in @p status.
     56      *
     57      * @param maxDequeuedBuffers The desired number of buffers that can be
     58      *     dequeued at one time.
     59      * @return status Status of the call.
     60      */
     61     setMaxDequeuedBufferCount(
     62             int32_t maxDequeuedBuffers
     63         ) generates (
     64             Status status
     65         );
     66 
     67     /**
     68      * Assigns a newly created buffer to the given slot index. The client is
     69      * expected to mirror the slot-to-buffer mapping so that it is not necessary
     70      * to transfer a `HardwareBuffer` object for every dequeue operation.
     71      *
     72      * If @p slot is not a valid slot index corresponding to a dequeued buffer,
     73      * the call shall fail with @p status set to `BAD_VALUE`.
     74      *
     75      * @param slot Slot index.
     76      * @return status Status of the call.
     77      * @return buffer New buffer associated to the given slot index.
     78      * @return generationNumber Generation number of the buffer. If
     79      *     requestBuffer() is called immediately after dequeueBuffer() returns
     80      *     with `bufferNeedsReallocation` set to `true`, @p generationNumber must
     81      *     match the current generation number of the buffer queue previously
     82      *     set by setGenerationNumber(). Otherwise, @p generationNumber may not
     83      *     match the current generation number of the buffer queue.
     84      */
     85     requestBuffer(
     86             int32_t slot
     87         ) generates (
     88             Status status,
     89             HardwareBuffer buffer,
     90             uint32_t generationNumber
     91         );
     92 
     93     /**
     94      * Sets the async flag: whether the producer intends to asynchronously queue
     95      * buffers without blocking. Typically this is used for triple-buffering
     96      * and/or when the swap interval is set to zero.
     97      *
     98      * Enabling async mode may internally allocate an additional buffer to allow
     99      * for the asynchronous behavior. If it is not enabled, queue/dequeue calls
    100      * may block.
    101      *
    102      * Changing the async flag may affect the number of available slots. If the
    103      * adjustment to the number of slots cannot be made, @p status shall be set
    104      * to `BAD_VALUE`.
    105      *
    106      * @param async True if the asynchronous operation is desired; false
    107      *     otherwise.
    108      * @return status Status of the call.
    109      */
    110     setAsyncMode(
    111             bool async
    112         ) generates (
    113             Status status
    114         );
    115 
    116     /**
    117      * Input data for dequeueBuffer() specifying desired attributes of a buffer
    118      * to dequeue.
    119      *
    120      * This structure contains 4 fields from
    121      * +llndk libnativewindow#AHardwareBuffer_Desc.
    122      *
    123      * The `width` and `height` parameters must be no greater than the minimum
    124      * of `GL_MAX_VIEWPORT_DIMS` and `GL_MAX_TEXTURE_SIZE` (see:
    125      * glGetIntegerv()). An error due to invalid dimensions may not be reported
    126      * until updateTexImage() is called.
    127      *
    128      * If `width` and `height` are both zero, the default dimensions shall be
    129      * used. If only one of `width` and `height` is zero, dequeueBuffer() shall
    130      * return `BAD_VALUE` in `status`.
    131      *
    132      * If `format` is zero, the default format shall be used.
    133      *
    134      * `usage` shall be merged with the usage flags set from the consumer side.
    135      *
    136      * @sa +llndk libnativewindow#AHardwareBuffer_Desc.
    137      */
    138     struct DequeueBufferInput {
    139         uint32_t width;
    140         uint32_t height;
    141         uint32_t format;
    142         uint64_t usage;
    143     };
    144 
    145     /**
    146      * Output data for dequeueBuffer().
    147      *
    148      * A `DequeueBufferOutput` object returned from dequeueBuffer() shall be
    149      * valid if and only if `status` returned from the same call is `OK`.
    150      */
    151     struct DequeueBufferOutput {
    152         /**
    153          * The number of frames that have elapsed since the buffer was last
    154          * queued.
    155          */
    156         uint64_t bufferAge;
    157         /**
    158          * Whether the client must call requestBuffer().
    159          */
    160         bool bufferNeedsReallocation;
    161         /**
    162          * Whether the client must discard the mirrored slot-to-buffer
    163          * mapping.
    164          */
    165         bool releaseAllBuffers;
    166         /**
    167          * Fence associated with the buffer.
    168          *
    169          * If this is an empty fence, the buffer may be written immediately;
    170          * otherwise, the buffer must not be written to until the fence signals.
    171          */
    172         Fence fence;
    173     };
    174 
    175     /**
    176      * Requests a new buffer slot for the client to use. Ownership of the slot
    177      * is transfered to the client, meaning that the server shall not use the
    178      * contents of the buffer associated with that slot.
    179      *
    180      * On success, @p status shall be `OK`, and @p output shall contain valid
    181      * information of the call. Otherwise, the contents of @p output are
    182      * meaningless.
    183      *
    184      * The slot index returned in @p slot may or may not contain a buffer
    185      * (client-side). If the slot is empty, the client must call
    186      * requestBuffer() to assign a new buffer to that slot.
    187      *
    188      * Once the client is done filling this buffer, it is expected to transfer
    189      * buffer ownership back to the server with either cancelBuffer() on
    190      * the dequeued slot or to fill in the contents of its associated buffer
    191      * contents and call queueBuffer().
    192      *
    193      * If dequeueBuffer() returns with `output.releaseAllBuffers` set to `true`,
    194      * the client is expected to release all of the mirrored slot-to-buffer
    195      * mappings.
    196      *
    197      * If dequeueBuffer() returns with `output.bufferNeedsReallocation` set to
    198      * `true`, the client is expected to call requestBuffer() immediately.
    199      *
    200      * The returned `output.fence` shall be updated to hold the fence associated
    201      * with the buffer. The contents of the buffer must not be overwritten until
    202      * the fence signals. If the fence is an empty fence, the buffer may be
    203      * written immediately.
    204      *
    205      * This call shall block until a buffer is available to be dequeued. If
    206      * both the producer and consumer are controlled by the app, then this call
    207      * can never block and shall return `WOULD_BLOCK` in @p status if no buffer
    208      * is available.
    209      *
    210      * If a dequeue operation shall cause certain conditions on the number of
    211      * buffers to be violated (such as the maximum number of dequeued buffers),
    212      * @p status shall be set to `INVALID_OPERATION` to indicate failure.
    213      *
    214      * If a dequeue operation cannot be completed within the time period
    215      * previously set by setDequeueTimeout(), the return @p status shall
    216      * `TIMED_OUT`.
    217      *
    218      * See @ref DequeueBufferInput for more information on the @p input
    219      * parameter.
    220      *
    221      * @param input See #DequeueBufferInput for more information.
    222      * @return status Status of the call.
    223      * @return slot Slot index.
    224      * @return output See #DequeueBufferOutput for more information.
    225      *
    226      * @sa queueBuffer(), requestBuffer().
    227      */
    228     dequeueBuffer(
    229             DequeueBufferInput input
    230         ) generates (
    231             Status status,
    232             int32_t slot,
    233             DequeueBufferOutput output
    234         );
    235 
    236     /**
    237      * Attempts to remove all ownership of the buffer in the given slot from the
    238      * buffer queue.
    239      *
    240      * If this call succeeds, the slot shall be freed, and there shall be no way
    241      * to obtain the buffer from this interface. The freed slot shall remain
    242      * unallocated until either it is selected to hold a freshly allocated
    243      * buffer in dequeueBuffer() or a buffer is attached to the slot. The buffer
    244      * must have already been dequeued, and the caller must already possesses
    245      * the buffer (i.e., must have called requestBuffer()).
    246      *
    247      * @param slot Slot index.
    248      * @return status Status of the call.
    249      */
    250     detachBuffer(
    251             int32_t slot
    252         ) generates (
    253             Status status
    254         );
    255 
    256     /**
    257      * Dequeues a buffer slot, requests the buffer associated to the slot, and
    258      * detaches it from the buffer queue. This is equivalent to calling
    259      * dequeueBuffer(), requestBuffer(), and detachBuffer() in succession except
    260      * for two things:
    261      *   1. It is unnecessary to provide a #DequeueBufferInput object.
    262      *   2. The call shall not block, since if it cannot find an appropriate
    263      *      buffer to return, it shall return an error instead.
    264      *
    265      * Only slots that are free but still contain a buffer shall be considered,
    266      * and the oldest of those shall be returned. @p buffer is equivalent to the
    267      * buffer that would be returned from requestBuffer(), and @p fence is
    268      * equivalent to the fence that would be returned from dequeueBuffer().
    269      *
    270      * @return status Status of the call.
    271      * @return buffer Buffer just released from the buffer queue.
    272      * @return fence Fence associated to @p buffer.
    273      *
    274      * @sa dequeueBuffer(), requestBuffer(), detachBuffer().
    275      */
    276     detachNextBuffer(
    277         ) generates (
    278             Status status,
    279             HardwareBuffer buffer,
    280             Fence fence
    281         );
    282 
    283     /**
    284      * Attempts to transfer ownership of a buffer to the buffer queue.
    285      *
    286      * If this call succeeds, it shall be as if this buffer was dequeued from the
    287      * returned slot index. As such, this call shall fail if attaching this
    288      * buffer would cause too many buffers to be simultaneously dequeued.
    289      *
    290      * If the returned @p releaseAllBuffers is `true`, the caller is expected to
    291      * release all of the mirrored slot-to-buffer mappings.
    292      *
    293      * See dequeueBuffer() for conditions that may cause the call to fail.
    294      *
    295      * @param buffer Buffer to attach to the buffer queue.
    296      * @param generationNumber Generation number of the buffer. If this does not
    297      *     match the current generation number of the buffer queue, the call
    298      *     must fail with @p status set to `BAD_VALUE`.
    299      * @return status Status of the call.
    300      * @return slot Slot index assigned to @p buffer.
    301      * @return releaseAllBuffers Whether the caller is expected to release all
    302      *     of the mirrored slot-to-buffer mappings.
    303      *
    304      * @sa dequeueBuffer().
    305      */
    306     attachBuffer(
    307             HardwareBuffer buffer,
    308             uint32_t generationNumber
    309         ) generates (
    310             Status status,
    311             int32_t slot,
    312             bool releaseAllBuffers
    313         );
    314 
    315     struct QueueBufferInput {
    316         /**
    317          * Timestamp in nanoseconds.
    318          */
    319         int64_t timestamp;
    320         /**
    321          * Whether the timestamp was synthesized at queue time.
    322          */
    323         bool isAutoTimestamp;
    324         /**
    325          * Dataspace of the contents.
    326          *
    327          * @sa +ndk libnativewindow#ADataSpace.
    328          */
    329         int32_t dataSpace;
    330         /**
    331          * Crop rectangle that is used as a hint to the consumer.
    332          */
    333         Rect crop;
    334         /**
    335          * Transformation flags.
    336          *
    337          * @sa +ndk libnativewindow#ANativeWindowTransform.
    338          */
    339         int32_t transform;
    340         /**
    341          * The sticky transform set in Surface (only used by the LEGACY camera
    342          * mode).
    343          *
    344          * @sa +ndk libnativewindow#ANativeWindowTransform.
    345          */
    346         int32_t stickyTransform;
    347         /**
    348          * Fence that the consumer must wait on before reading the buffer. An
    349          * empty fence indicates that the buffer is ready immediately.
    350          */
    351         Fence fence;
    352         /**
    353          * List of rectangular pieces covering the damage region.
    354          */
    355         vec<Rect> surfaceDamage;
    356     };
    357 
    358     /**
    359      * Information about the queued buffer. `QueueBufferOutput` is used in both
    360      * queueBuffer() and connect().
    361      */
    362     struct QueueBufferOutput {
    363         /**
    364          * Default width of a buffer in the buffer queue.
    365          */
    366         uint32_t width;
    367         /**
    368          * Default height of a buffer in the buffer queue.
    369          */
    370         uint32_t height;
    371         /**
    372          * The transform hint of the buffer queue.
    373          *
    374          * @sa +ndk libnativewindow#ANativeWindowTransform.
    375          */
    376         int32_t transformHint;
    377         /**
    378          * The number of pending buffers in the buffer queue. If this is
    379          * returned from queueBuffer(), the number shall include the buffer that
    380          * has just been queued.
    381          */
    382         uint32_t numPendingBuffers;
    383         /**
    384          * The frame number of the next frame. The buffer queue maintains this
    385          * number and makes sure that it is increasing for every successful
    386          * queueBuffer() call.
    387          */
    388         uint64_t nextFrameNumber;
    389         /**
    390          * After a successful queueBuffer() call, #bufferReplaced shall be set to
    391          * true if the queued buffer replaced a previously queued buffer that
    392          * has not been consumed.
    393          */
    394         bool bufferReplaced;
    395     };
    396 
    397     /**
    398      * Indicates that the client has finished filling in the contents of the
    399      * buffer associated with slot and transfers ownership of that slot back to
    400      * the buffer queue.
    401      *
    402      * @p status may be set to `BAD_VALUE` if any of the following conditions
    403      * hold:
    404      *   - The buffer queue is operating in the asynchronous mode, and the
    405      *     buffer count was smaller than the maximum number of buffers that can
    406      *     be allocated at once.
    407      *   - @p slot is an invalid slot index, i.e., the slot is not owned by the
    408      *     client by previously calling dequeueBuffer(), requestBuffer() or
    409      *     attachBuffer().
    410      *   - The crop rectangle is not contained in the buffer.
    411      *
    412      * Upon success, the output shall be filled with meaningful values
    413      * (refer to the documentation of @ref QueueBufferOutput).
    414      *
    415      * @param slot Slot index.
    416      * @param input See @ref QueueBufferInput.
    417      * @return status Status of the call.
    418      * @return output See @ref QueueBufferOutput.
    419      *
    420      * @sa #QueueBufferInput, #QueueBufferOutput, dequeueBuffer().
    421      */
    422     queueBuffer(
    423             int32_t slot,
    424             QueueBufferInput input
    425         ) generates (
    426             Status status,
    427             QueueBufferOutput output
    428         );
    429 
    430     /**
    431      * Indicates that the client does not wish to fill in the buffer associated
    432      * with the slot and transfers ownership of the slot back to the server. The
    433      * buffer is not queued for use by the consumer.
    434      *
    435      * If @p fence is not an empty fence, the buffer shall not be overwritten
    436      * until the fence signals. @p fence is usually obtained from
    437      * dequeueBuffer().
    438      *
    439      * @param slot Slot index.
    440      * @param fence Fence for the canceled buffer.
    441      * @return status Status of the call.
    442      */
    443     cancelBuffer(
    444             int32_t slot,
    445             Fence fence
    446         ) generates (
    447             Status status
    448         );
    449 
    450     /**
    451      * Retrieves information for this surface.
    452      *
    453      * @param what What to query. @p what must be one of the values in
    454      *     +llndk libnativewindow#ANativeWindowQuery.
    455      * @return status Status of the call.
    456      * @return value The value queried. The set of possible values depends on
    457      *     the value of @p what.
    458      *
    459      * @sa +llndk libnativewindow#ANativeWindowQuery.
    460      */
    461     query(
    462             int32_t what
    463         ) generates (
    464             int32_t result,
    465             int32_t value
    466         );
    467 
    468     /**
    469      * Attempts to connect the client as a producer of the buffer queue.
    470      * This method must be called before any other methods in this interface.
    471      *
    472      * If the buffer queue does not have a consumer ready (connected), the
    473      * return @p status shall be `NO_INIT`.
    474      *
    475      * If any of the following conditions hold, the error code `BAD_VALUE` shall
    476      * be reported in @p status:
    477      *   - The producer is already connected.
    478      *   - The number of available slots cannot be adjusted to accommodate the
    479      *     supplied value of @p producerControlledByApp.
    480      *
    481      * @param listener An optional callback object that can be provided if the
    482      *     client wants to be notified when the consumer releases a buffer back
    483      *     to the buffer queue.
    484      * @param api How the client shall write to buffers.
    485      * @param producerControlledByApp `true` if the producer is hosted by an
    486      *     untrusted process (typically application-forked processes). If both
    487      *     the producer and the consumer are controlled by app, the buffer queue
    488      *     shall operate in the asynchronous mode regardless of the async flag
    489      *     set by setAsyncMode().
    490      * @return status Status of the call.
    491      * @return output See #QueueBufferOutput for more information.
    492      *
    493      * @sa #QueueBufferOutput, disconnect(), setAsyncMode().
    494      */
    495     connect(
    496             IProducerListener listener,
    497             ConnectionType api,
    498             bool producerControlledByApp
    499         ) generates (
    500             Status status,
    501             QueueBufferOutput output
    502         );
    503 
    504     /**
    505      * Attempts to disconnect the client from the producer end of the buffer
    506      * queue.
    507      *
    508      * Calling this method shall cause any subsequent calls to other
    509      * @ref IGraphicBufferProducer methods apart from connect() to fail.
    510      * A successful connect() call afterwards may allow other methods to succeed
    511      * again.
    512      *
    513      * Disconnecting from an abandoned buffer queue is legal and is considered a
    514      * no-op.
    515      *
    516      * @param api The type of connection to disconnect. Supplying the value of
    517      *     `CURRENTLY_CONNECTED` to @p api has the same effect as supplying the
    518      *     current connection type. If the producer end is not connected,
    519      *     supplying `CURRENTLY_CONNECTED` shall result in a successful no-op
    520      *     call.
    521      * @return status Status of the call.
    522      *
    523      * @sa connect().
    524      */
    525     disconnect(
    526             ConnectionType api
    527         ) generates (
    528             Status status
    529         );
    530 
    531     /**
    532      * Allocates buffers based on the given dimensions, format and usage.
    533      *
    534      * This function shall allocate up to the maximum number of buffers
    535      * permitted by the current buffer queue configuration. It shall use the
    536      * given format, dimensions, and usage bits, which are interpreted in the
    537      * same way as for dequeueBuffer(), and the async flag must be set the same
    538      * way as for dequeueBuffer() to ensure that the correct number of buffers
    539      * are allocated. This is most useful to avoid an allocation delay during
    540      * dequeueBuffer(). If there are already the maximum number of buffers
    541      * allocated, this function has no effect.
    542      *
    543      * A value of 0 in @p width, @p height or @p format indicates that the
    544      * buffer queue can pick the default value.
    545      *
    546      * @param width Width of buffers to allocate.
    547      * @param height Height of buffers to allocate.
    548      * @param format Format of buffers to allocate.
    549      * @param usage Usage of bufferes to allocate.
    550      * @return status Status of the call.
    551      */
    552     allocateBuffers(
    553             uint32_t width,
    554             uint32_t height,
    555             uint32_t format,
    556             uint64_t usage
    557         ) generates (
    558             Status status
    559         );
    560 
    561     /**
    562      * Sets whether dequeueBuffer() is allowed to allocate new buffers.
    563      *
    564      * Normally dequeueBuffer() does not discriminate between free slots which
    565      * already have an allocated buffer and those which do not, and shall
    566      * allocate a new buffer if the slot doesn't have a buffer or if the slot's
    567      * buffer doesn't match the requested size, format, or usage. This method
    568      * allows the producer to restrict the eligible slots to those which already
    569      * have an allocated buffer of the correct size, format, and usage. If no
    570      * eligible slot is available, dequeueBuffer() shall block or return an
    571      * error.
    572      *
    573      * @param allow Whether to allow new buffers to be allocated in
    574      *     dequeueBuffer().
    575      * @return status Status of the call.
    576      */
    577     allowAllocation(
    578             bool allow
    579         ) generates (
    580             Status status
    581         );
    582 
    583     /**
    584      * Sets the current generation number of the buffer queue.
    585      *
    586      * This generation number shall be inserted into any buffers allocated by the
    587      * buffer queue, and any attempts to attach a buffer with a different
    588      * generation number shall fail. Buffers already in the queue are not
    589      * affected and shall retain their current generation number. The generation
    590      * number defaults to 0, i.e., buffers allocated before the first call to
    591      * setGenerationNumber() shall be given 0 as their generation numbers.
    592      *
    593      * @param generationNumber New generation number. The client must make sure
    594      *     that @p generationNumber is different from the previous generation
    595      *     number if it wants to deprecate old buffers.
    596      * @return status Status of the call.
    597      */
    598     setGenerationNumber(
    599             uint32_t generationNumber
    600         ) generates (
    601             Status status
    602         );
    603 
    604     /**
    605      * Sets how long dequeueBuffer() shall wait for a buffer to become available
    606      * before returning an error `TIMED_OUT`.
    607      *
    608      * This timeout also affects the attachBuffer() call, which shall block if
    609      * there is not a free slot available into which the attached buffer can be
    610      * placed.
    611      *
    612      * By default, the buffer queue shall wait forever, which is equivalent to
    613      * setting @p timeoutNs equal to any negative number (such as `-1`). If
    614      * @p timeoutNs is non-negative, setDequeueTimeout() shall disable
    615      * non-blocking mode and its corresponding spare buffer (which is used to
    616      * ensure a buffer is always available).
    617      *
    618      * Changing the dequeue timeout may affect the number of buffers. (See
    619      * setAsyncMode().) If the adjustment to the number of buffers inside the
    620      * buffer queue is not feasible, @p status shall be set to `BAD_VALUE`.
    621      *
    622      * @param timeoutNs Amount of time dequeueBuffer() is allowed to block
    623      *     before returning `TIMED_OUT`. If @p timeoutNs is negative,
    624      *     dequeueBuffer() shall not be able to return `TIMED_OUT`. Instead, it
    625      *     may block forever or return `WOULD_BLOCK`.
    626      * @return status Status of the call.
    627      *
    628      * @sa dequeueBuffer(), setAsyncMode(), query().
    629      */
    630     setDequeueTimeout(
    631             int64_t timeoutNs
    632         ) generates (
    633             Status status
    634         );
    635 
    636     /**
    637      * Returns a unique id for this buffer queue.
    638      *
    639      * @return id System-wide unique id of the buffer queue.
    640      */
    641     getUniqueId(
    642         ) generates (
    643             uint64_t id
    644         );
    645 
    646     /**
    647      * Returns the name of the connected consumer.
    648      *
    649      * \note This is used for debugging only.
    650      *
    651      * @return name Name of the consumer.
    652      */
    653     getConsumerName(
    654         ) generates (
    655             string name
    656         );
    657 
    658 };
    659 
    660