diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 10:35:26 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 10:35:26 -0800 |
commit | bae5cf92cfa9e2867af91057265df7f029f7865e (patch) | |
tree | 36c850418ad1433bef13b30be214f4f844a14efa | |
parent | af822c43cf144a991db49d8c25068d054265ae37 (diff) | |
parent | c9940a2bfe94dbe2ef3bfe5e8692bf4e3cea5ba0 (diff) | |
download | libhardware-bae5cf92cfa9e2867af91057265df7f029f7865e.tar.gz |
Merge commit 'c9940a2bfe94dbe2ef3bfe5e8692bf4e3cea5ba0' into HEAD
48 files changed, 5273 insertions, 182 deletions
@@ -154,6 +154,10 @@ int hw_get_module_by_class(const char *class_id, const char *inst, if (access(path, R_OK) == 0) break; } else { snprintf(path, sizeof(path), "%s/%s.default.so", + HAL_LIBRARY_PATH2, name); + if (access(path, R_OK) == 0) break; + + snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH1, name); if (access(path, R_OK) == 0) break; } diff --git a/include/hardware/audio.h b/include/hardware/audio.h index 3a0962ea..6ba2544e 100644 --- a/include/hardware/audio.h +++ b/include/hardware/audio.h @@ -67,6 +67,7 @@ __BEGIN_DECLS #define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp" #define AUDIO_HARDWARE_MODULE_ID_USB "usb" #define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix" +#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload" /**************************************/ @@ -117,16 +118,35 @@ __BEGIN_DECLS * "sup_sampling_rates=44100|48000" */ #define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates" +/** + * audio codec parameters + */ + +#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param" +#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample" +#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate" +#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate" +#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id" +#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align" +#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate" +#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option" +#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL "music_offload_num_channels" +#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING "music_offload_down_sampling" +#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES "delay_samples" +#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES "padding_samples" /**************************************/ -/* common audio stream configuration parameters */ +/* common audio stream configuration parameters + * You should memset() the entire structure to zero before use to + * ensure forward compatibility + */ struct audio_config { uint32_t sample_rate; audio_channel_mask_t channel_mask; audio_format_t format; + audio_offload_info_t offload_info; }; - typedef struct audio_config audio_config_t; /* common audio stream parameters and operations */ @@ -213,6 +233,22 @@ struct audio_stream { }; typedef struct audio_stream audio_stream_t; +/* type of asynchronous write callback events. Mutually exclusive */ +typedef enum { + STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */ + STREAM_CBK_EVENT_DRAIN_READY /* drain completed */ +} stream_callback_event_t; + +typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie); + +/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */ +typedef enum { + AUDIO_DRAIN_ALL, /* drain() returns when all data has been played */ + AUDIO_DRAIN_EARLY_NOTIFY /* drain() returns a short time before all data + from the current track has been played to + give time for gapless track switch */ +} audio_drain_type_t; + /** * audio_stream_out is the abstraction interface for the audio output hardware. * @@ -242,6 +278,13 @@ struct audio_stream_out { * negative status_t. If at least one frame was written successfully prior to the error, * it is suggested that the driver return that successful (short) byte count * and then return an error in the subsequent call. + * + * If set_callback() has previously been called to enable non-blocking mode + * the write() is not allowed to block. It must write only the number of + * bytes that currently fit in the driver/hardware buffer and then return + * this byte count. If this is less than the requested write size the + * callback function must be called when more space is available in the + * driver/hardware buffer. */ ssize_t (*write)(struct audio_stream_out *stream, const void* buffer, size_t bytes); @@ -259,6 +302,80 @@ struct audio_stream_out { int (*get_next_write_timestamp)(const struct audio_stream_out *stream, int64_t *timestamp); + /** + * set the callback function for notifying completion of non-blocking + * write and drain. + * Calling this function implies that all future write() and drain() + * must be non-blocking and use the callback to signal completion. + */ + int (*set_callback)(struct audio_stream_out *stream, + stream_callback_t callback, void *cookie); + + /** + * Notifies to the audio driver to stop playback however the queued buffers are + * retained by the hardware. Useful for implementing pause/resume. Empty implementation + * if not supported however should be implemented for hardware with non-trivial + * latency. In the pause state audio hardware could still be using power. User may + * consider calling suspend after a timeout. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*pause)(struct audio_stream_out* stream); + + /** + * Notifies to the audio driver to resume playback following a pause. + * Returns error if called without matching pause. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*resume)(struct audio_stream_out* stream); + + /** + * Requests notification when data buffered by the driver/hardware has + * been played. If set_callback() has previously been called to enable + * non-blocking mode, the drain() must not block, instead it should return + * quickly and completion of the drain is notified through the callback. + * If set_callback() has not been called, the drain() must block until + * completion. + * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written + * data has been played. + * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all + * data for the current track has played to allow time for the framework + * to perform a gapless track switch. + * + * Drain must return immediately on stop() and flush() call + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type ); + + /** + * Notifies to the audio driver to flush the queued data. Stream must already + * be paused before calling flush(). + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*flush)(struct audio_stream_out* stream); + + /** + * Return a recent count of the number of audio frames presented to an external observer. + * This excludes frames which have been written but are still in the pipeline. + * The count is not reset to zero when output enters standby. + * Also returns the value of CLOCK_MONOTONIC as of this presentation count. + * The returned count is expected to be 'recent', + * but does not need to be the most recent possible value. + * However, the associated time should correspond to whatever count is returned. + * Example: assume that N+M frames have been presented, where M is a 'small' number. + * Then it is permissible to return N instead of N+M, + * and the timestamp should correspond to N rather than N+M. + * The terms 'recent' and 'small' are not defined. + * They reflect the quality of the implementation. + * + * 3.0 and higher only. + */ + int (*get_presentation_position)(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp); + }; typedef struct audio_stream_out audio_stream_out_t; @@ -296,18 +413,14 @@ typedef struct audio_stream_in audio_stream_in_t; static inline size_t audio_stream_frame_size(const struct audio_stream *s) { size_t chan_samp_sz; + audio_format_t format = s->get_format(s); - switch (s->get_format(s)) { - case AUDIO_FORMAT_PCM_16_BIT: - chan_samp_sz = sizeof(int16_t); - break; - case AUDIO_FORMAT_PCM_8_BIT: - default: - chan_samp_sz = sizeof(int8_t); - break; + if (audio_is_linear_pcm(format)) { + chan_samp_sz = audio_bytes_per_sample(format); + return popcount(s->get_channels(s)) * chan_samp_sz; } - return popcount(s->get_channels(s)) * chan_samp_sz; + return sizeof(int8_t); } diff --git a/include/hardware/audio_effect.h b/include/hardware/audio_effect.h index 2940b1af..b49d02d6 100644 --- a/include/hardware/audio_effect.h +++ b/include/hardware/audio_effect.h @@ -147,6 +147,9 @@ typedef struct effect_descriptor_s { // | | | 1 requires audio source updates // | | | 2..3 reserved // +---------------------------+-----------+----------------------------------- +// | Effect offload supported | 22 | 0 The effect cannot be offloaded to an audio DSP +// | | | 1 The effect can be offloaded to an audio DSP +// +---------------------------+-----------+----------------------------------- // Insert mode #define EFFECT_FLAG_TYPE_SHIFT 0 @@ -229,6 +232,14 @@ typedef struct effect_descriptor_s { #define EFFECT_FLAG_AUDIO_SOURCE_IND (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) #define EFFECT_FLAG_AUDIO_SOURCE_NONE (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) +// Effect offload indication +#define EFFECT_FLAG_OFFLOAD_SHIFT (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \ + EFFECT_FLAG_AUDIO_SOURCE_SIZE) +#define EFFECT_FLAG_OFFLOAD_SIZE 1 +#define EFFECT_FLAG_OFFLOAD_MASK (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \ + << EFFECT_FLAG_OFFLOAD_SHIFT) +#define EFFECT_FLAG_OFFLOAD_SUPPORTED (1 << EFFECT_FLAG_OFFLOAD_SHIFT) + #define EFFECT_MAKE_API_VERSION(M, m) (((M)<<16) | ((m) & 0xFFFF)) #define EFFECT_API_VERSION_MAJOR(v) ((v)>>16) #define EFFECT_API_VERSION_MINOR(v) ((m) & 0xFFFF) @@ -426,6 +437,8 @@ enum effect_command_e { EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t) + EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one, + // send the ioHandle of the effect thread EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code }; @@ -732,6 +745,20 @@ enum effect_command_e { // size: 0 // data: N/A //================================================================================================== +// command: EFFECT_CMD_OFFLOAD +//-------------------------------------------------------------------------------------------------- +// description: +// 1.indicate if the playback thread the effect is attached to is offloaded or not +// 2.update the io handle of the playback thread the effect is attached to +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_offload_param_t) +// data: effect_offload_param_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- // command: EFFECT_CMD_FIRST_PROPRIETARY //-------------------------------------------------------------------------------------------------- // description: @@ -868,6 +895,11 @@ typedef struct effect_param_s { char data[]; // Start of Parameter + Value data } effect_param_t; +// structure used by EFFECT_CMD_OFFLOAD command +typedef struct effect_offload_param_s { + bool isOffload; // true if the playback thread the effect is attached to is offloaded + int ioHandle; // io handle of the playback thread the effect is attached to +} effect_offload_param_t; ///////////////////////////////////////////////// diff --git a/include/hardware/audio_policy.h b/include/hardware/audio_policy.h index c635ebf5..4e75e02a 100644 --- a/include/hardware/audio_policy.h +++ b/include/hardware/audio_policy.h @@ -133,7 +133,8 @@ struct audio_policy { uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - audio_output_flags_t flags); + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); /* indicates to the audio policy manager that the output starts being used * by corresponding stream. */ @@ -241,6 +242,10 @@ struct audio_policy { /* dump state */ int (*dump)(const struct audio_policy *pol, int fd); + + /* check if offload is possible for given sample rate, bitrate, duration, ... */ + bool (*is_offload_supported)(const struct audio_policy *pol, + const audio_offload_info_t *info); }; /* audio hw module handle used by load_hw_module(), open_output_on_module() @@ -390,7 +395,8 @@ struct audio_policy_service_ops { audio_format_t *pFormat, audio_channel_mask_t *pChannelMask, uint32_t *pLatencyMs, - audio_output_flags_t flags); + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); /* Opens an audio input on a particular HW module. * diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h index 27413327..c00a8f7b 100755..100644 --- a/include/hardware/bluetooth.h +++ b/include/hardware/bluetooth.h @@ -451,6 +451,8 @@ typedef struct { /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */ int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len); + /* enable or disable bluetooth HCI snoop log */ + int (*config_hci_snoop_log)(uint8_t enable); } bt_interface_t; /** TODO: Need to add APIs for Service Discovery, Service authorization and diff --git a/include/hardware/bt_gatt_client.h b/include/hardware/bt_gatt_client.h index 8b49f594..d6b0cb40 100644 --- a/include/hardware/bt_gatt_client.h +++ b/include/hardware/bt_gatt_client.h @@ -40,8 +40,8 @@ typedef struct typedef struct { btgatt_srvc_id_t srvc_id; - btgatt_char_id_t char_id; - bt_uuid_t descr_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; btgatt_unformatted_value_t value; uint16_t value_type; uint8_t status; @@ -51,8 +51,8 @@ typedef struct typedef struct { btgatt_srvc_id_t srvc_id; - btgatt_char_id_t char_id; - bt_uuid_t descr_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; uint8_t status; } btgatt_write_params_t; @@ -62,7 +62,7 @@ typedef struct uint8_t value[BTGATT_MAX_ATTR_LEN]; bt_bdaddr_t bda; btgatt_srvc_id_t srvc_id; - btgatt_char_id_t char_id; + btgatt_gatt_id_t char_id; uint16_t len; uint8_t is_notify; } btgatt_notify_params_t; @@ -105,13 +105,13 @@ typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id); /** GATT characteristic enumeration result callback */ typedef void (*get_characteristic_callback)(int conn_id, int status, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int char_prop); /** GATT descriptor enumeration result callback */ typedef void (*get_descriptor_callback)(int conn_id, int status, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, - bt_uuid_t *descr_id); + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id); /** GATT included service enumeration result callback */ typedef void (*get_included_service_callback)(int conn_id, int status, @@ -120,7 +120,7 @@ typedef void (*get_included_service_callback)(int conn_id, int status, /** Callback invoked in response to [de]register_for_notification */ typedef void (*register_for_notification_callback)(int conn_id, int registered, int status, btgatt_srvc_id_t *srvc_id, - btgatt_char_id_t *char_id); + btgatt_gatt_id_t *char_id); /** * Remote device notification callback, invoked when a remote device sends @@ -151,6 +151,11 @@ typedef void (*write_descriptor_callback)(int conn_id, int status, typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda, int rssi, int status); +/** + * Callback indicationg the status of a listen() operation + */ +typedef void (*listen_callback)(int status, int server_if); + typedef struct { register_client_callback register_client_cb; scan_result_callback scan_result_cb; @@ -169,6 +174,7 @@ typedef struct { write_descriptor_callback write_descriptor_cb; execute_write_callback execute_write_cb; read_remote_rssi_callback read_remote_rssi_cb; + listen_callback listen_cb; } btgatt_client_callbacks_t; /** Represents the standard BT-GATT client interface. */ @@ -191,6 +197,9 @@ typedef struct { bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr, int conn_id); + /** Start or stop advertisements to listen for incoming connections */ + bt_status_t (*listen)(int client_if, bool start); + /** Clear the attribute cache for a given device */ bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr ); @@ -212,36 +221,36 @@ typedef struct { * Set start_char_id to NULL to get the first characteristic. */ bt_status_t (*get_characteristic)( int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *start_char_id); + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id); /** * Enumerate descriptors for a given characteristic. * Set start_descr_id to NULL to get the first descriptor. */ bt_status_t (*get_descriptor)( int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, - bt_uuid_t *start_descr_id); + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id); /** Read a characteristic on a remote device */ bt_status_t (*read_characteristic)( int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int auth_req ); /** Write a remote characteristic */ bt_status_t (*write_characteristic)(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, int write_type, int len, int auth_req, char* p_value); /** Read the descriptor for a given characteristic */ bt_status_t (*read_descriptor)(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, - bt_uuid_t *descr_id, int auth_req); + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int auth_req); /** Write a remote descriptor for a given characteristic */ bt_status_t (*write_descriptor)( int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id, - bt_uuid_t *descr_id, int write_type, int len, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int write_type, int len, int auth_req, char* p_value); /** Execute a prepared write operation */ @@ -253,12 +262,12 @@ typedef struct { */ bt_status_t (*register_for_notification)( int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, - btgatt_char_id_t *char_id); + btgatt_gatt_id_t *char_id); /** Deregister a previous request for notifications/indications */ bt_status_t (*deregister_for_notification)( int client_if, const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, - btgatt_char_id_t *char_id); + btgatt_gatt_id_t *char_id); /** Request RSSI for a given remote device */ bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr); @@ -266,6 +275,11 @@ typedef struct { /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */ int (*get_device_type)( const bt_bdaddr_t *bd_addr ); + /** Set the advertising data or scan response data */ + bt_status_t (*set_adv_data)(int server_if, bool set_scan_rsp, bool include_name, + bool include_txpower, int min_interval, int max_interval, int appearance, + uint16_t manufacturer_len, char* manufacturer_data); + /** Test mode interface */ bt_status_t (*test_command)( int command, btgatt_test_params_t* params); } btgatt_client_interface_t; diff --git a/include/hardware/bt_gatt_types.h b/include/hardware/bt_gatt_types.h index fee9bb54..0ac217e8 100644 --- a/include/hardware/bt_gatt_types.h +++ b/include/hardware/bt_gatt_types.h @@ -29,17 +29,17 @@ __BEGIN_DECLS #define BTGATT_SERVICE_TYPE_PRIMARY 0 #define BTGATT_SERVICE_TYPE_SECONDARY 1 -/** GATT Characteristic ID adding instance id tracking to the UUID */ +/** GATT ID adding instance id tracking to the UUID */ typedef struct { bt_uuid_t uuid; uint8_t inst_id; -} btgatt_char_id_t; +} btgatt_gatt_id_t; /** GATT Service ID also identifies the service type (primary/secondary) */ typedef struct { - btgatt_char_id_t id; + btgatt_gatt_id_t id; uint8_t is_primary; } btgatt_srvc_id_t; diff --git a/include/hardware/bt_rc.h b/include/hardware/bt_rc.h index 952652e1..d4555437 100755..100644 --- a/include/hardware/bt_rc.h +++ b/include/hardware/bt_rc.h @@ -30,6 +30,13 @@ __BEGIN_DECLS typedef uint8_t btrc_uid_t[BTRC_UID_SIZE]; typedef enum { + BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */ + BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */ + BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */ + BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */ +} btrc_remote_features_t; + +typedef enum { BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */ BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */ BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */ @@ -114,6 +121,10 @@ typedef struct { uint8_t text[BTRC_MAX_ATTR_STR_LEN]; } btrc_element_attr_val_t; +/** Callback for the controller's supported feautres */ +typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr, + btrc_remote_features_t features); + /** Callback for play status request */ typedef void (* btrc_get_play_status_callback)(); @@ -151,10 +162,20 @@ typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_at */ typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param); +/* AVRCP 1.4 Enhancements */ +/** Callback for volume change on CT +** volume: Current volume setting on the CT (0-127) +*/ +typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype); + +/** Callback for passthrough commands */ +typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state); + /** BT-RC callback structure. */ typedef struct { /** set to sizeof(BtRcCallbacks) */ size_t size; + btrc_remote_features_callback remote_features_cb; btrc_get_play_status_callback get_play_status_cb; btrc_list_player_app_attr_callback list_player_app_attr_cb; btrc_list_player_app_values_callback list_player_app_values_cb; @@ -164,6 +185,8 @@ typedef struct { btrc_set_player_app_value_callback set_player_app_value_cb; btrc_get_element_attr_callback get_element_attr_cb; btrc_register_notification_callback register_notification_cb; + btrc_volume_change_callback volume_change_cb; + btrc_passthrough_cmd_callback passthrough_cmd_cb; } btrc_callbacks_t; /** Represents the standard BT-RC interface. */ @@ -225,6 +248,15 @@ typedef struct { btrc_notification_type_t type, btrc_register_notification_t *p_param); + /* AVRCP 1.4 enhancements */ + + /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume + ** This can be enhanced to support Relative Volume (AVRCP 1.0). + ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level + ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set + */ + bt_status_t (*set_volume)(uint8_t volume); + /** Closes the interface. */ void (*cleanup)( void ); } btrc_interface_t; diff --git a/include/hardware/camera3.h b/include/hardware/camera3.h index 3a900fc7..afc9d9f1 100644 --- a/include/hardware/camera3.h +++ b/include/hardware/camera3.h @@ -21,17 +21,17 @@ #include "camera_common.h" /** - * Camera device HAL 3.0 [ CAMERA_DEVICE_API_VERSION_3_0 ] + * Camera device HAL 3.1 [ CAMERA_DEVICE_API_VERSION_3_1 ] * * EXPERIMENTAL. * * Supports the android.hardware.Camera API. * * Camera devices that support this version of the HAL must return - * CAMERA_DEVICE_API_VERSION_3_0 in camera_device_t.common.version and in + * CAMERA_DEVICE_API_VERSION_3_1 in camera_device_t.common.version and in * camera_info_t.device_version (from camera_module_t.get_camera_info). * - * Camera modules that may contain version 3.0 devices must implement at least + * Camera modules that may contain version 3.1 devices must implement at least * version 2.0 of the camera module interface (as defined by * camera_module_t.common.module_api_version). * @@ -82,6 +82,12 @@ * management. Bidirectional streams replace STREAM_FROM_STREAM construct. * * - Limited mode semantics for older/limited hardware devices. + * + * 3.1: Minor revision of expanded-capability HAL: + * + * - configure_streams passes consumer usage flags to the HAL. + * + * - flush call to drop all in-flight requests/buffers as fast as possible. */ /** @@ -392,6 +398,10 @@ * well focused. The lens is not moving. The HAL may spontaneously leave * this state. * + * AF_STATE_PASSIVE_UNFOCUSED: A continuous focus algorithm believes it is + * not well focused. The lens is not moving. The HAL may spontaneously + * leave this state. + * * AF_STATE_ACTIVE_SCAN: A scan triggered by the user is underway. * * AF_STATE_FOCUSED_LOCKED: The AF algorithm believes it is focused. The @@ -565,10 +575,16 @@ * * S4.5. AF state machines * + * when enabling AF or changing AF mode + *| state | trans. cause | new state | notes | + *+--------------------+---------------+--------------------+------------------+ + *| Any | AF mode change| INACTIVE | | + *+--------------------+---------------+--------------------+------------------+ + * * mode = AF_MODE_OFF or AF_MODE_EDOF *| state | trans. cause | new state | notes | *+--------------------+---------------+--------------------+------------------+ - *| INACTIVE | | | AF is disabled | + *| INACTIVE | | INACTIVE | Never changes | *+--------------------+---------------+--------------------+------------------+ * * mode = AF_MODE_AUTO or AF_MODE_MACRO @@ -611,6 +627,9 @@ *| PASSIVE_SCAN | HAL completes | PASSIVE_FOCUSED | End AF scan | *| | current scan | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ + *| PASSIVE_SCAN | HAL fails | PASSIVE_UNFOCUSED | End AF scan | + *| | current scan | | Lens now locked | + *+--------------------+---------------+--------------------+------------------+ *| PASSIVE_SCAN | AF_TRIGGER | FOCUSED_LOCKED | Immediate trans. | *| | | | if focus is good | *| | | | Lens now locked | @@ -626,12 +645,13 @@ *| PASSIVE_FOCUSED | HAL initiates | PASSIVE_SCAN | Start AF scan | *| | new scan | | Lens now moving | *+--------------------+---------------+--------------------+------------------+ + *| PASSIVE_UNFOCUSED | HAL initiates | PASSIVE_SCAN | Start AF scan | + *| | new scan | | Lens now moving | + *+--------------------+---------------+--------------------+------------------+ *| PASSIVE_FOCUSED | AF_TRIGGER | FOCUSED_LOCKED | Immediate trans. | - *| | | | if focus is good | *| | | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ - *| PASSIVE_FOCUSED | AF_TRIGGER | NOT_FOCUSED_LOCKED | Immediate trans. | - *| | | | if focus is bad | + *| PASSIVE_UNFOCUSED | AF_TRIGGER | NOT_FOCUSED_LOCKED | Immediate trans. | *| | | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ *| FOCUSED_LOCKED | AF_TRIGGER | FOCUSED_LOCKED | No effect | @@ -655,6 +675,9 @@ *| PASSIVE_SCAN | HAL completes | PASSIVE_FOCUSED | End AF scan | *| | current scan | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ + *| PASSIVE_SCAN | HAL fails | PASSIVE_UNFOCUSED | End AF scan | + *| | current scan | | Lens now locked | + *+--------------------+---------------+--------------------+------------------+ *| PASSIVE_SCAN | AF_TRIGGER | FOCUSED_LOCKED | Eventual trans. | *| | | | once focus good | *| | | | Lens now locked | @@ -670,12 +693,13 @@ *| PASSIVE_FOCUSED | HAL initiates | PASSIVE_SCAN | Start AF scan | *| | new scan | | Lens now moving | *+--------------------+---------------+--------------------+------------------+ + *| PASSIVE_UNFOCUSED | HAL initiates | PASSIVE_SCAN | Start AF scan | + *| | new scan | | Lens now moving | + *+--------------------+---------------+--------------------+------------------+ *| PASSIVE_FOCUSED | AF_TRIGGER | FOCUSED_LOCKED | Immediate trans. | - *| | | | if focus is good | *| | | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ - *| PASSIVE_FOCUSED | AF_TRIGGER | NOT_FOCUSED_LOCKED | Immediate trans. | - *| | | | if focus is bad | + *| PASSIVE_UNFOCUSED | AF_TRIGGER | NOT_FOCUSED_LOCKED | Immediate trans. | *| | | | Lens now locked | *+--------------------+---------------+--------------------+------------------+ *| FOCUSED_LOCKED | AF_TRIGGER | FOCUSED_LOCKED | No effect | @@ -693,10 +717,16 @@ * FLASH_REQUIRED and PRECAPTURE states. So rows below that refer to those two * states should be ignored for the AWB state machine. * + * when enabling AE/AWB or changing AE/AWB mode + *| state | trans. cause | new state | notes | + *+--------------------+---------------+--------------------+------------------+ + *| Any | mode change | INACTIVE | | + *+--------------------+---------------+--------------------+------------------+ + * * mode = AE_MODE_OFF / AWB mode not AUTO *| state | trans. cause | new state | notes | *+--------------------+---------------+--------------------+------------------+ - *| INACTIVE | | | AE/AWB disabled | + *| INACTIVE | | INACTIVE | AE/AWB disabled | *+--------------------+---------------+--------------------+------------------+ * * mode = AE_MODE_ON_* / AWB_MODE_AUTO @@ -1041,6 +1071,9 @@ typedef enum camera3_stream_type { * remain valid as if configure_streams() had not been called. * * The endpoint of the stream is not visible to the camera HAL device. + * In DEVICE_API_VERSION_3_1, this was changed to share consumer usage flags + * on streams where the camera is a producer (OUTPUT and BIDIRECTIONAL stream + * types) see the usage field below. */ typedef struct camera3_stream { @@ -1092,6 +1125,25 @@ typedef struct camera3_stream { * the producer and the consumer will be combined together and then passed * to the platform gralloc HAL module for allocating the gralloc buffers for * each stream. + * + * Version information: + * + * == CAMERA_DEVICE_API_VERSION_3_0: + * + * No initial value guaranteed when passed via configure_streams(). + * HAL may not use this field as input, and must write over this field + * with its usage flags. + * + * >= CAMERA_DEVICE_API_VERSION_3_1: + * + * For stream_type OUTPUT and BIDIRECTIONAL, when passed via + * configure_streams(), the initial value of this is the consumer's + * usage flags. The HAL may use these consumer flags to decide stream + * configuration. + * For stream_type INPUT, when passed via configure_streams(), the initial + * value of this is 0. + * For all streams passed via configure_streams(), the HAL must write + * over this field with its usage flags. */ uint32_t usage; @@ -2035,6 +2087,49 @@ typedef struct camera3_device_ops { */ void (*dump)(const struct camera3_device *, int fd); + /** + * flush: + * + * Flush all currently in-process captures and all buffers in the pipeline + * on the given device. The framework will use this to dump all state as + * quickly as possible in order to prepare for a configure_streams() call. + * + * No buffers are required to be successfully returned, so every buffer + * held at the time of flush() (whether sucessfully filled or not) may be + * returned with CAMERA3_BUFFER_STATUS_ERROR. Note the HAL is still allowed + * to return valid (STATUS_OK) buffers during this call, provided they are + * succesfully filled. + * + * All requests currently in the HAL are expected to be returned as soon as + * possible. Not-in-process requests should return errors immediately. Any + * interruptible hardware blocks should be stopped, and any uninterruptible + * blocks should be waited on. + * + * flush() should only return when there are no more outstanding buffers or + * requests left in the HAL. The framework may call configure_streams (as + * the HAL state is now quiesced) or may issue new requests. + * + * A flush() call should only take 100ms or less. The maximum time it can + * take is 1 second. + * + * Version information: + * + * only available if device version >= CAMERA_DEVICE_API_VERSION_3_1. + * + * Return values: + * + * 0: On a successful flush of the camera HAL. + * + * -EINVAL: If the input is malformed (the device is not valid). + * + * -ENODEV: If the camera device has encountered a serious error. After this + * error is returned, only the close() method can be successfully + * called by the framework. + */ + int (*flush)(const struct camera3_device *); + + /* reserved for future use */ + void *reserved[8]; } camera3_device_ops_t; /********************************************************************** diff --git a/include/hardware/camera_common.h b/include/hardware/camera_common.h index 62c57402..3a1233f6 100644 --- a/include/hardware/camera_common.h +++ b/include/hardware/camera_common.h @@ -67,6 +67,13 @@ __BEGIN_DECLS * framework from the camera HAL module, which is used to notify the framework * about changes to the camera module state. Modules that provide a valid * set_callbacks() method must report at least this version number. + * + ******************************************************************************* + * Version: 2.2 [CAMERA_MODULE_API_VERSION_2_2] + * + * This camera module version adds vendor tag support from the module, and + * deprecates the old vendor_tag_query_ops that were previously only + * accessible with a device open. */ /** @@ -80,8 +87,9 @@ __BEGIN_DECLS #define CAMERA_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) #define CAMERA_MODULE_API_VERSION_2_0 HARDWARE_MODULE_API_VERSION(2, 0) #define CAMERA_MODULE_API_VERSION_2_1 HARDWARE_MODULE_API_VERSION(2, 1) +#define CAMERA_MODULE_API_VERSION_2_2 HARDWARE_MODULE_API_VERSION(2, 2) -#define CAMERA_MODULE_API_VERSION_CURRENT CAMERA_MODULE_API_VERSION_2_1 +#define CAMERA_MODULE_API_VERSION_CURRENT CAMERA_MODULE_API_VERSION_2_2 /** * All device versions <= HARDWARE_DEVICE_API_VERSION(1, 0xFF) must be treated @@ -91,6 +99,7 @@ __BEGIN_DECLS #define CAMERA_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0) #define CAMERA_DEVICE_API_VERSION_2_1 HARDWARE_DEVICE_API_VERSION(2, 1) #define CAMERA_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0) +#define CAMERA_DEVICE_API_VERSION_3_1 HARDWARE_DEVICE_API_VERSION(3, 1) // Device version 2.x is outdated; device version 3.0 is experimental #define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_1_0 @@ -242,6 +251,65 @@ typedef struct camera_module_callbacks { } camera_module_callbacks_t; +/** + * Set up vendor-specific tag query methods. These are needed to properly query + * entries with vendor-specified tags, potentially returned by get_camera_info. + * + * This should be used in place of vendor_tag_query_ops, which are deprecated. + */ +typedef struct vendor_tag_ops vendor_tag_ops_t; +struct vendor_tag_ops { + /** + * Get the number of vendor tags supported on this platform. Used to + * calculate the size of buffer needed for holding the array of all tags + * returned by get_all_tags(). + */ + int (*get_tag_count)(const vendor_tag_ops_t *v); + + /** + * Fill an array with all the supported vendor tags on this platform. + * get_tag_count() returns the number of tags supported, and + * tag_array will be allocated with enough space to hold all of the tags. + */ + void (*get_all_tags)(const vendor_tag_ops_t *v, uint32_t *tag_array); + + /** + * Get vendor section name for a vendor-specified entry tag. Only called for + * vendor-defined tags. The section name must start with the name of the + * vendor in the Java package style. For example, CameraZoom Inc. must + * prefix their sections with "com.camerazoom." Must return NULL if the tag + * is outside the bounds of vendor-defined sections. + * + * There may be different vendor-defined tag sections, for example the + * phone maker, the chipset maker, and the camera module maker may each + * have their own "com.vendor."-prefixed section. + * + * The memory pointed to by the return value must remain valid for the + * lifetime that the module is loaded, and is owned by the module. + */ + const char *(*get_section_name)(const vendor_tag_ops_t *v, uint32_t tag); + + /** + * Get tag name for a vendor-specified entry tag. Only called for + * vendor-defined tags. Must return NULL if the it is not a vendor-defined + * tag. + * + * The memory pointed to by the return value must remain valid for the + * lifetime that the module is loaded, and is owned by the module. + */ + const char *(*get_tag_name)(const vendor_tag_ops_t *v, uint32_t tag); + + /** + * Get tag type for a vendor-specified entry tag. Only called for tags >= + * 0x80000000. Must return -1 if the tag is outside the bounds of + * vendor-defined sections. + */ + int (*get_tag_type)(const vendor_tag_ops_t *v, uint32_t tag); + + /* reserved for future use */ + void* reserved[8]; +}; + typedef struct camera_module { hw_module_t common; @@ -290,6 +358,25 @@ typedef struct camera_module { */ int (*set_callbacks)(const camera_module_callbacks_t *callbacks); + /** + * get_vendor_tag_ops: + * + * Get methods to query for vendor extension metadata tag information. The + * HAL should fill in all the vendor tag operation methods, or leave ops + * unchanged if no vendor tags are defined. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_1_x/2_0/2_1: + * Not provided by HAL module. Framework may not call this function. + * + * CAMERA_MODULE_API_VERSION_2_2: + * Valid to be called by the framework. + */ + void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops); + + /* reserved for future use */ + void* reserved[8]; } camera_module_t; __END_DECLS diff --git a/include/hardware/consumerir.h b/include/hardware/consumerir.h new file mode 100644 index 00000000..5adf6be5 --- /dev/null +++ b/include/hardware/consumerir.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_HARDWARE_CONSUMERIR_H +#define ANDROID_INCLUDE_HARDWARE_CONSUMERIR_H + +#include <stdint.h> +#include <sys/cdefs.h> +#include <hardware/hardware.h> +#include <hardware/hwcomposer_defs.h> + +#define CONSUMERIR_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) +#define CONSUMERIR_HARDWARE_MODULE_ID "consumerir" +#define CONSUMERIR_TRANSMITTER "transmitter" + +typedef struct consumerir_freq_range { + int min; + int max; +} consumerir_freq_range_t; + +typedef struct consumerir_module { + struct hw_module_t common; +} consumerir_module_t; + +typedef struct consumerir_device { + struct hw_device_t common; + + /* + * (*transmit)() is called to by the ConsumerIrService to send an IR pattern + * at a given carrier_freq. + * + * The pattern is alternating series of carrier on and off periods measured in + * microseconds. The carrier should be turned off at the end of a transmit + * even if there are and odd number of entries in the pattern array. + * + * This call should return when the transmit is complete or encounters an error. + * + * returns: 0 on success. A negative error code on error. + */ + int (*transmit)(struct consumerir_device *dev, int carrier_freq, + const int pattern[], int pattern_len); + + /* + * (*get_num_carrier_freqs)() is called by the ConsumerIrService to get the + * number of carrier freqs to allocate space for, which is then filled by + * a subsequent call to (*get_carrier_freqs)(). + * + * returns: the number of ranges on success. A negative error code on error. + */ + int (*get_num_carrier_freqs)(struct consumerir_device *dev); + + /* + * (*get_carrier_freqs)() is called by the ConsumerIrService to enumerate + * which frequencies the IR transmitter supports. The HAL implementation + * should fill an array of consumerir_freq_range structs with the + * appropriate values for the transmitter, up to len elements. + * + * returns: the number of ranges on success. A negative error code on error. + */ + int (*get_carrier_freqs)(struct consumerir_device *dev, + size_t len, consumerir_freq_range_t *ranges); + + /* Reserved for future use. Must be NULL. */ + void* reserved[8 - 3]; +} consumerir_device_t; + +#endif /* ANDROID_INCLUDE_HARDWARE_CONSUMERIR_H */ diff --git a/include/hardware/fused_location.h b/include/hardware/fused_location.h new file mode 100644 index 00000000..5c7821c8 --- /dev/null +++ b/include/hardware/fused_location.h @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_HARDWARE_FUSED_LOCATION_H +#define ANDROID_INCLUDE_HARDWARE_FUSED_LOCATION_H + +#include <hardware/hardware.h> + + +/** + * This header file defines the interface of the Fused Location Provider. + * Fused Location Provider is designed to fuse data from various sources + * like GPS, Wifi, Cell, Sensors, Bluetooth etc to provide a fused location to the + * upper layers. The advantage of doing fusion in hardware is power savings. + * The goal is to do this without waking up the AP to get additional data. + * The software implementation of FLP will decide when to use + * the hardware fused location. Other location features like geofencing will + * also be implemented using fusion in hardware. + */ +__BEGIN_DECLS + +#define FLP_HEADER_VERSION 1 +#define FLP_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) +#define FLP_DEVICE_API_VERSION_0_1 HARDWARE_DEVICE_API_VERSION_2(0, 1, FLP_HEADER_VERSION) + +/** + * The id of this module + */ +#define FUSED_LOCATION_HARDWARE_MODULE_ID "flp" + +/** + * Name for the FLP location interface + */ +#define FLP_LOCATION_INTERFACE "flp_location" + +/** + * Name for the FLP location interface + */ +#define FLP_DIAGNOSTIC_INTERFACE "flp_diagnostic" + +/** + * Name for the FLP_Geofencing interface. + */ +#define FLP_GEOFENCING_INTERFACE "flp_geofencing" + +/** + * Name for the FLP_device context interface. + */ +#define FLP_DEVICE_CONTEXT_INTERFACE "flp_device_context" + +/** + * Constants to indicate the various subsystems + * that will be used. + */ +#define FLP_TECH_MASK_GNSS (1U<<0) +#define FLP_TECH_MASK_WIFI (1U<<1) +#define FLP_TECH_MASK_SENSORS (1U<<2) +#define FLP_TECH_MASK_CELL (1U<<3) +#define FLP_TECH_MASK_BLUETOOTH (1U<<4) + +/** + * This constant is used with the batched locations + * APIs. Batching is mandatory when FLP implementation + * is supported. If the flag is set, the hardware implementation + * will wake up the application processor when the FIFO is full, + * If the flag is not set, the hardware implementation will drop + * the oldest data when the FIFO is full. + */ +#define FLP_BATCH_WAKEUP_ON_FIFO_FULL 0x0000001 + +/** + * While batching, the implementation should not call the + * flp_location_callback on every location fix. However, + * sometimes in high power mode, the system might need + * a location callback every single time the location + * fix has been obtained. This flag controls that option. + * Its the responsibility of the upper layers (caller) to switch + * it off, if it knows that the AP might go to sleep. + * When this bit is on amidst a batching session, batching should + * continue while location fixes are reported in real time. + */ +#define FLP_BATCH_CALLBACK_ON_LOCATION_FIX 0x0000002 + +/** Flags to indicate which values are valid in a FlpLocation. */ +typedef uint16_t FlpLocationFlags; + +// IMPORTANT: Note that the following values must match +// constants in the corresponding java file. + +/** FlpLocation has valid latitude and longitude. */ +#define FLP_LOCATION_HAS_LAT_LONG (1U<<0) +/** FlpLocation has valid altitude. */ +#define FLP_LOCATION_HAS_ALTITUDE (1U<<1) +/** FlpLocation has valid speed. */ +#define FLP_LOCATION_HAS_SPEED (1U<<2) +/** FlpLocation has valid bearing. */ +#define FLP_LOCATION_HAS_BEARING (1U<<4) +/** FlpLocation has valid accuracy. */ +#define FLP_LOCATION_HAS_ACCURACY (1U<<8) + + +typedef int64_t FlpUtcTime; + +/** Represents a location. */ +typedef struct { + /** set to sizeof(FlpLocation) */ + size_t size; + + /** Flags associated with the location object. */ + FlpLocationFlags flags; + + /** Represents latitude in degrees. */ + double latitude; + + /** Represents longitude in degrees. */ + double longitude; + + /** + * Represents altitude in meters above the WGS 84 reference + * ellipsoid. */ + double altitude; + + /** Represents speed in meters per second. */ + float speed; + + /** Represents heading in degrees. */ + float bearing; + + /** Represents expected accuracy in meters. */ + float accuracy; + + /** Timestamp for the location fix. */ + FlpUtcTime timestamp; + + /** Sources used, will be Bitwise OR of the FLP_TECH_MASK bits. */ + uint32_t sources_used; +} FlpLocation; + +typedef enum { + ASSOCIATE_JVM, + DISASSOCIATE_JVM, +} ThreadEvent; + +/** + * Callback with location information. + * Can only be called from a thread associated to JVM using set_thread_event_cb. + * Parameters: + * num_locations is the number of batched locations available. + * location is the pointer to an array of pointers to location objects. + */ +typedef void (*flp_location_callback)(int32_t num_locations, FlpLocation** location); + +/** + * Callback utility for acquiring a wakelock. + * This can be used to prevent the CPU from suspending while handling FLP events. + */ +typedef void (*flp_acquire_wakelock)(); + +/** + * Callback utility for releasing the FLP wakelock. + */ +typedef void (*flp_release_wakelock)(); + +/** + * Callback for associating a thread that can call into the Java framework code. + * This must be used to initialize any threads that report events up to the framework. + * Return value: + * FLP_RESULT_SUCCESS on success. + * FLP_RESULT_ERROR if the association failed in the current thread. + */ +typedef int (*flp_set_thread_event)(ThreadEvent event); + +/** FLP callback structure. */ +typedef struct { + /** set to sizeof(FlpCallbacks) */ + size_t size; + flp_location_callback location_cb; + flp_acquire_wakelock acquire_wakelock_cb; + flp_release_wakelock release_wakelock_cb; + flp_set_thread_event set_thread_event_cb; +} FlpCallbacks; + + +/** Options with the batching FLP APIs */ +typedef struct { + /** + * Maximum power in mW that the underlying implementation + * can use for this batching call. + * If max_power_allocation_mW is 0, only fixes that are generated + * at no additional cost of power shall be reported. + */ + double max_power_allocation_mW; + + /** Bitwise OR of the FLP_TECH_MASKS to use */ + uint32_t sources_to_use; + + /** + * FLP_BATCH_WAKEUP_ON_FIFO_FULL - If set the hardware + * will wake up the AP when the buffer is full. If not set, the + * hardware will drop the oldest location object. + * + * FLP_BATCH_CALLBACK_ON_LOCATION_FIX - If set the location + * callback will be called every time there is a location fix. + * Its the responsibility of the upper layers (caller) to switch + * it off, if it knows that the AP might go to sleep. When this + * bit is on amidst a batching session, batching should continue + * while location fixes are reported in real time. + * + * Other flags to be bitwised ORed in the future. + */ + uint32_t flags; + + /** + * Frequency with which location needs to be batched in nano + * seconds. + */ + int64_t period_ns; +} FlpBatchOptions; + +#define FLP_RESULT_SUCCESS 0 +#define FLP_RESULT_ERROR -1 +#define FLP_RESULT_INSUFFICIENT_MEMORY -2 +#define FLP_RESULT_TOO_MANY_GEOFENCES -3 +#define FLP_RESULT_ID_EXISTS -4 +#define FLP_RESULT_ID_UNKNOWN -5 +#define FLP_RESULT_INVALID_GEOFENCE_TRANSITION -6 + +/** + * Represents the standard FLP interface. + */ +typedef struct { + /** + * set to sizeof(FlpLocationInterface) + */ + size_t size; + + /** + * Opens the interface and provides the callback routines + * to the implemenation of this interface. + */ + int (*init)(FlpCallbacks* callbacks ); + + /** + * Return the batch size (in number of FlpLocation objects) + * available in the hardware. Note, different HW implementations + * may have different sample sizes. This shall return number + * of samples defined in the format of FlpLocation. + * This will be used by the upper layer, to decide on the batching + * interval and whether the AP should be woken up or not. + */ + int (*get_batch_size)(); + + /** + * Start batching locations. This API is primarily used when the AP is + * asleep and the device can batch locations in the hardware. + * flp_location_callback is used to return the locations. When the buffer + * is full and FLP_BATCH_WAKEUP_ON_FIFO_FULL is used, the AP is woken up. + * When the buffer is full and FLP_BATCH_WAKEUP_ON_FIFO_FULL is not set, + * the oldest location object is dropped. In this case the AP will not be + * woken up. The upper layer will use get_batched_location + * API to explicitly ask for the location. + * If FLP_BATCH_CALLBACK_ON_LOCATION_FIX is set, the implementation + * will call the flp_location_callback every single time there is a location + * fix. This overrides FLP_BATCH_WAKEUP_ON_FIFO_FULL flag setting. + * It's the responsibility of the upper layers (caller) to switch + * it off, if it knows that the AP might go to sleep. This is useful + * for nagivational applications when the system is in high power mode. + * Parameters: + * id - Id for the request. + * options - See FlpBatchOptions struct definition. + * Return value: + * FLP_RESULT_SUCCESS on success, FLP_RESULT_INSUFFICIENT_MEMORY, + * FLP_RESULT_ID_EXISTS, FLP_RESULT_ERROR on failure. + */ + int (*start_batching)(int id, FlpBatchOptions* options); + + /** + * Update FlpBatchOptions associated with a batching request. + * When a batching operation is in progress and a batching option + * such as FLP_BATCH_WAKEUP_ON_FIFO_FULL needs to be updated, this API + * will be used. For instance, this can happen when the AP is awake and + * the maps application is being used. + * Parameters: + * id - Id of an existing batch request. + * new_options - Updated FlpBatchOptions + * Return value: + * FLP_RESULT_SUCCESS on success, FLP_RESULT_ID_UNKNOWN, + * FLP_RESULT_ERROR on error. + */ + int (*update_batching_options)(int id, FlpBatchOptions* new_options); + + /** + * Stop batching. + * Parameters: + * id - Id for the request. + * Return Value: + * FLP_RESULT_SUCCESS on success, FLP_RESULT_ID_UNKNOWN or + * FLP_RESULT_ERROR on failure. + */ + int (*stop_batching)(int id); + + /** + * Closes the interface. If any batch operations are in progress, + * they should be stopped. + */ + void (*cleanup)(); + + /** + * Get the fused location that was batched. + * flp_location_callback is used to return the location. The location object + * is dropped from the buffer only when the buffer is full. Do not remove it + * from the buffer just because it has been returned using the callback. + * In other words, when there is no new location object, two calls to + * get_batched_location(1) should return the same location object. + * Parameters: + * last_n_locations - Number of locations to get. This can be one or many. + * If the last_n_locations is 1, you get the latest location known to the + * hardware. + */ + void (*get_batched_location)(int last_n_locations); + + /** + * Injects current location from another location provider + * latitude and longitude are measured in degrees + * expected accuracy is measured in meters + * Parameters: + * location - The location object being injected. + * Return value: FLP_RESULT_SUCCESS or FLP_RESULT_ERROR. + */ + int (*inject_location)(FlpLocation* location); + + /** + * Get a pointer to extension information. + */ + const void* (*get_extension)(const char* name); +} FlpLocationInterface; + +struct flp_device_t { + struct hw_device_t common; + + /** + * Get a handle to the FLP Interface. + */ + const FlpLocationInterface* (*get_flp_interface)(struct flp_device_t* dev); +}; + +/** + * Callback for reports diagnostic data into the Java framework code. +*/ +typedef void (*report_data)(char* data, int length); + +/** + * FLP diagnostic callback structure. + * Currently, not used - but this for future extension. + */ +typedef struct { + /** set to sizeof(FlpDiagnosticCallbacks) */ + size_t size; + + flp_set_thread_event set_thread_event_cb; + + /** reports diagnostic data into the Java framework code */ + report_data data_cb; +} FlpDiagnosticCallbacks; + +/** Extended interface for diagnostic support. */ +typedef struct { + /** set to sizeof(FlpDiagnosticInterface) */ + size_t size; + + /** + * Opens the diagnostic interface and provides the callback routines + * to the implemenation of this interface. + */ + void (*init)(FlpDiagnosticCallbacks* callbacks); + + /** + * Injects diagnostic data into the FLP subsystem. + * Return 0 on success, -1 on error. + **/ + int (*inject_data)(char* data, int length ); +} FlpDiagnosticInterface; + +/** + * Context setting information. + * All these settings shall be injected to FLP HAL at FLP init time. + * Following that, only the changed setting need to be re-injected + * upon changes. + */ + +#define FLP_DEVICE_CONTEXT_GPS_ENABLED (1U<<0) +#define FLP_DEVICE_CONTEXT_AGPS_ENABLED (1U<<1) +#define FLP_DEVICE_CONTEXT_NETWORK_POSITIONING_ENABLED (1U<<2) +#define FLP_DEVICE_CONTEXT_WIFI_CONNECTIVITY_ENABLED (1U<<3) +#define FLP_DEVICE_CONTEXT_WIFI_POSITIONING_ENABLED (1U<<4) +#define FLP_DEVICE_CONTEXT_HW_NETWORK_POSITIONING_ENABLED (1U<<5) +#define FLP_DEVICE_CONTEXT_AIRPLANE_MODE_ON (1U<<6) +#define FLP_DEVICE_CONTEXT_DATA_ENABLED (1U<<7) +#define FLP_DEVICE_CONTEXT_ROAMING_ENABLED (1U<<8) +#define FLP_DEVICE_CONTEXT_CURRENTLY_ROAMING (1U<<9) +#define FLP_DEVICE_CONTEXT_SENSOR_ENABLED (1U<<10) +#define FLP_DEVICE_CONTEXT_BLUETOOTH_ENABLED (1U<<11) +#define FLP_DEVICE_CONTEXT_CHARGER_ON (1U<<12) + +/** Extended interface for device context support. */ +typedef struct { + /** set to sizeof(FlpDeviceContextInterface) */ + size_t size; + + /** + * Injects debug data into the FLP subsystem. + * Return 0 on success, -1 on error. + **/ + int (*inject_device_context)(uint32_t enabledMask); +} FlpDeviceContextInterface; + + +/** + * There are 3 states associated with a Geofence: Inside, Outside, Unknown. + * There are 3 transitions: ENTERED, EXITED, UNCERTAIN. + * + * An example state diagram with confidence level: 95% and Unknown time limit + * set as 30 secs is shown below. (confidence level and Unknown time limit are + * explained latter) + * ____________________________ + * | Unknown (30 secs) | + * """""""""""""""""""""""""""" + * ^ | | ^ + * UNCERTAIN| |ENTERED EXITED| |UNCERTAIN + * | v v | + * ________ EXITED _________ + * | Inside | -----------> | Outside | + * | | <----------- | | + * """""""" ENTERED """"""""" + * + * Inside state: We are 95% confident that the user is inside the geofence. + * Outside state: We are 95% confident that the user is outside the geofence + * Unknown state: Rest of the time. + * + * The Unknown state is better explained with an example: + * + * __________ + * | c| + * | ___ | _______ + * | |a| | | b | + * | """ | """"""" + * | | + * """""""""" + * In the diagram above, "a" and "b" are 2 geofences and "c" is the accuracy + * circle reported by the FLP subsystem. Now with regard to "b", the system is + * confident that the user is outside. But with regard to "a" is not confident + * whether it is inside or outside the geofence. If the accuracy remains the + * same for a sufficient period of time, the UNCERTAIN transition would be + * triggered with the state set to Unknown. If the accuracy improves later, an + * appropriate transition should be triggered. This "sufficient period of time" + * is defined by the parameter in the add_geofence_area API. + * In other words, Unknown state can be interpreted as a state in which the + * FLP subsystem isn't confident enough that the user is either inside or + * outside the Geofence. It moves to Unknown state only after the expiry of the + * timeout. + * + * The geofence callback needs to be triggered for the ENTERED and EXITED + * transitions, when the FLP system is confident that the user has entered + * (Inside state) or exited (Outside state) the Geofence. An implementation + * which uses a value of 95% as the confidence is recommended. The callback + * should be triggered only for the transitions requested by the + * add_geofence_area call. + * + * Even though the diagram and explanation talks about states and transitions, + * the callee is only interested in the transistions. The states are mentioned + * here for illustrative purposes. + * + * Startup Scenario: When the device boots up, if an application adds geofences, + * and then we get an accurate FLP location fix, it needs to trigger the + * appropriate (ENTERED or EXITED) transition for every Geofence it knows about. + * By default, all the Geofences will be in the Unknown state. + * + * When the FLP system is unavailable, flp_geofence_status_callback should be + * called to inform the upper layers of the same. Similarly, when it becomes + * available the callback should be called. This is a global state while the + * UNKNOWN transition described above is per geofence. + * + */ +#define FLP_GEOFENCE_TRANSITION_ENTERED (1L<<0) +#define FLP_GEOFENCE_TRANSITION_EXITED (1L<<1) +#define FLP_GEOFENCE_TRANSITION_UNCERTAIN (1L<<2) + +#define FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE (1L<<0) +#define FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE (1L<<1) + +/** + * The callback associated with the geofence. + * Parameters: + * geofence_id - The id associated with the add_geofence_area. + * location - The current location as determined by the FLP subsystem. + * transition - Can be one of FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED, + * FLP_GEOFENCE_TRANSITION_UNCERTAIN. + * timestamp - Timestamp when the transition was detected; -1 if not available. + * sources_used - Bitwise OR of FLP_TECH_MASK flags indicating which + * subsystems were used. + * + * The callback should only be called when the caller is interested in that + * particular transition. For instance, if the caller is interested only in + * ENTERED transition, then the callback should NOT be called with the EXITED + * transition. + * + * IMPORTANT: If a transition is triggered resulting in this callback, the + * subsystem will wake up the application processor, if its in suspend state. + */ +typedef void (*flp_geofence_transition_callback) (int32_t geofence_id, FlpLocation* location, + int32_t transition, FlpUtcTime timestamp, uint32_t sources_used); + +/** + * The callback associated with the availablity of one the sources used for geofence + * monitoring by the FLP sub-system For example, if the GPS system determines that it cannot + * monitor geofences because of lack of reliability or unavailability of the GPS signals, + * it will call this callback with FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE parameter and the + * source set to FLP_TECH_MASK_GNSS. + * + * Parameters: + * status - FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE or FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE. + * source - One of the FLP_TECH_MASKS + * last_location - Last known location. + */ +typedef void (*flp_geofence_monitor_status_callback) (int32_t status, uint32_t source, + FlpLocation* last_location); + +/** + * The callback associated with the add_geofence call. + * + * Parameter: + * geofence_id - Id of the geofence. + * result - FLP_RESULT_SUCCESS + * FLP_RESULT_ERROR_TOO_MANY_GEOFENCES - geofence limit has been reached. + * FLP_RESULT_ID_EXISTS - geofence with id already exists + * FLP_RESULT_INVALID_GEOFENCE_TRANSITION - the monitorTransition contains an + * invalid transition + * FLP_RESULT_ERROR - for other errors. + */ +typedef void (*flp_geofence_add_callback) (int32_t geofence_id, int32_t result); + +/** + * The callback associated with the remove_geofence call. + * + * Parameter: + * geofence_id - Id of the geofence. + * result - FLP_RESULT_SUCCESS + * FLP_RESULT_ID_UNKNOWN - for invalid id + * FLP_RESULT_ERROR for others. + */ +typedef void (*flp_geofence_remove_callback) (int32_t geofence_id, int32_t result); + + +/** + * The callback associated with the pause_geofence call. + * + * Parameter: + * geofence_id - Id of the geofence. + * result - FLP_RESULT_SUCCESS + * FLP_RESULT__ID_UNKNOWN - for invalid id + * FLP_RESULT_INVALID_TRANSITION - + * when monitor_transitions is invalid + * FLP_RESULT_ERROR for others. + */ +typedef void (*flp_geofence_pause_callback) (int32_t geofence_id, int32_t result); + +/** + * The callback associated with the resume_geofence call. + * + * Parameter: + * geofence_id - Id of the geofence. + * result - FLP_RESULT_SUCCESS + * FLP_RESULT_ID_UNKNOWN - for invalid id + * FLP_RESULT_ERROR for others. + */ +typedef void (*flp_geofence_resume_callback) (int32_t geofence_id, int32_t result); + +typedef struct { + /** set to sizeof(FlpGeofenceCallbacks) */ + size_t size; + flp_geofence_transition_callback geofence_transition_callback; + flp_geofence_monitor_status_callback geofence_status_callback; + flp_geofence_add_callback geofence_add_callback; + flp_geofence_remove_callback geofence_remove_callback; + flp_geofence_pause_callback geofence_pause_callback; + flp_geofence_resume_callback geofence_resume_callback; + flp_set_thread_event set_thread_event_cb; +} FlpGeofenceCallbacks; + + +/** Type of geofence */ +typedef enum { + TYPE_CIRCLE = 0, +} GeofenceType; + +/** Circular geofence is represented by lat / long / radius */ +typedef struct { + double latitude; + double longitude; + double radius_m; +} GeofenceCircle; + +/** Represents the type of geofence and data */ +typedef struct { + GeofenceType type; + union { + GeofenceCircle circle; + } geofence; +} GeofenceData; + +/** Geofence Options */ +typedef struct { + /** + * The current state of the geofence. For example, if + * the system already knows that the user is inside the geofence, + * this will be set to FLP_GEOFENCE_TRANSITION_ENTERED. In most cases, it + * will be FLP_GEOFENCE_TRANSITION_UNCERTAIN. */ + int last_transition; + + /** + * Transitions to monitor. Bitwise OR of + * FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED and + * FLP_GEOFENCE_TRANSITION_UNCERTAIN. + */ + int monitor_transitions; + + /** + * Defines the best-effort description + * of how soon should the callback be called when the transition + * associated with the Geofence is triggered. For instance, if set + * to 1000 millseconds with FLP_GEOFENCE_TRANSITION_ENTERED, the callback + * should be called 1000 milliseconds within entering the geofence. + * This parameter is defined in milliseconds. + * NOTE: This is not to be confused with the rate that the GPS is + * polled at. It is acceptable to dynamically vary the rate of + * sampling the GPS for power-saving reasons; thus the rate of + * sampling may be faster or slower than this. + */ + int notification_responsivenes_ms; + + /** + * The time limit after which the UNCERTAIN transition + * should be triggered. This paramter is defined in milliseconds. + */ + int unknown_timer_ms; + + /** + * The sources to use for monitoring geofences. Its a BITWISE-OR + * of FLP_TECH_MASK flags. + */ + uint32_t sources_to_use; +} GeofenceOptions; + +/** Geofence struct */ +typedef struct { + int32_t geofence_id; + GeofenceData* data; + GeofenceOptions* options; +} Geofence; + +/** Extended interface for FLP_Geofencing support */ +typedef struct { + /** set to sizeof(FlpGeofencingInterface) */ + size_t size; + + /** + * Opens the geofence interface and provides the callback routines + * to the implemenation of this interface. + */ + void (*init)( FlpGeofenceCallbacks* callbacks ); + + /** + * Add a list of geofences. + * Parameters: + * number_of_geofences - The number of geofences that needed to be added. + * geofences - Pointer to array of pointers to Geofence structure. + */ + void (*add_geofences) (int32_t number_of_geofences, Geofence** geofences); + + /** + * Pause monitoring a particular geofence. + * Parameters: + * geofence_id - The id for the geofence. + */ + void (*pause_geofence) (int32_t geofence_id); + + /** + * Resume monitoring a particular geofence. + * Parameters: + * geofence_id - The id for the geofence. + * monitor_transitions - Which transitions to monitor. Bitwise OR of + * FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED and + * FLP_GEOFENCE_TRANSITION_UNCERTAIN. + * This supersedes the value associated provided in the + * add_geofence_area call. + */ + void (*resume_geofence) (int32_t geofence_id, int monitor_transitions); + + /** + * Modify a particular geofence option. + * Parameters: + * geofence_id - The id for the geofence. + * options - Various options associated with the geofence. See + * GeofenceOptions structure for details. + */ + void (*modify_geofence_option) (int32_t geofence_id, GeofenceOptions* options); + + /** + * Remove a list of geofences. After the function returns, no notifications + * should be sent. + * Parameter: + * number_of_geofences - The number of geofences that needed to be added. + * geofence_id - Pointer to array of geofence_ids to be removed. + */ + void (*remove_geofences) (int32_t number_of_geofences, int32_t* geofence_id); +} FlpGeofencingInterface; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_HARDWARE_FLP_H */ + diff --git a/include/hardware/gralloc.h b/include/hardware/gralloc.h index 9c622421..0dbebcff 100644 --- a/include/hardware/gralloc.h +++ b/include/hardware/gralloc.h @@ -104,6 +104,9 @@ enum { /* mask for the software usage bit-mask */ GRALLOC_USAGE_HW_MASK = 0x00071F00, + /* buffer will be used as a RenderScript Allocation */ + GRALLOC_USAGE_RENDERSCRIPT = 0x00100000, + /* buffer should be displayed full-screen on an external display when * possible */ diff --git a/include/hardware/hwcomposer.h b/include/hardware/hwcomposer.h index d75a0472..846bab49 100644 --- a/include/hardware/hwcomposer.h +++ b/include/hardware/hwcomposer.h @@ -54,6 +54,13 @@ typedef struct hwc_rect { int bottom; } hwc_rect_t; +typedef struct hwc_frect { + float left; + float top; + float right; + float bottom; +} hwc_frect_t; + typedef struct hwc_region { size_t numRects; hwc_rect_t const* rects; @@ -149,8 +156,17 @@ typedef struct hwc_layer_1 { int32_t blending; /* area of the source to consider, the origin is the top-left corner of - * the buffer */ - hwc_rect_t sourceCrop; + * the buffer. As of HWC_DEVICE_API_VERSION_1_3, sourceRect uses floats. + * If the h/w can't support a non-integer source crop rectangle, it should + * punt to OpenGL ES composition. + */ + union { + // crop rectangle in integer (pre HWC_DEVICE_API_VERSION_1_3) + hwc_rect_t sourceCropi; + hwc_rect_t sourceCrop; // just for source compatibility + // crop rectangle in floats (as of HWC_DEVICE_API_VERSION_1_3) + hwc_frect_t sourceCropf; + }; /* where to composite the sourceCrop onto the display. The sourceCrop * is scaled using linear filtering to the displayFrame. The origin is the @@ -433,12 +449,12 @@ typedef struct hwc_composer_device_1 { * For HWC 1.0, numDisplays will always be one, and displays[0] will be * non-NULL. * - * For HWC 1.1, numDisplays will always be HWC_NUM_DISPLAY_TYPES. Entries - * for unsupported or disabled/disconnected display types will be NULL. + * For HWC 1.1, numDisplays will always be HWC_NUM_PHYSICAL_DISPLAY_TYPES. + * Entries for unsupported or disabled/disconnected display types will be + * NULL. * - * In a future version, numDisplays may be larger than - * HWC_NUM_DISPLAY_TYPES. The extra entries correspond to enabled virtual - * displays, and will be non-NULL. + * In HWC 1.3, numDisplays may be up to HWC_NUM_DISPLAY_TYPES. The extra + * entries correspond to enabled virtual displays, and will be non-NULL. * * returns: 0 on success. An negative error code on error. If an error is * returned, SurfaceFlinger will assume that none of the layer will be @@ -466,12 +482,12 @@ typedef struct hwc_composer_device_1 { * For HWC 1.0, numDisplays will always be one, and displays[0] will be * non-NULL. * - * For HWC 1.1, numDisplays will always be HWC_NUM_DISPLAY_TYPES. Entries - * for unsupported or disabled/disconnected display types will be NULL. + * For HWC 1.1, numDisplays will always be HWC_NUM_PHYSICAL_DISPLAY_TYPES. + * Entries for unsupported or disabled/disconnected display types will be + * NULL. * - * In a future version, numDisplays may be larger than - * HWC_NUM_DISPLAY_TYPES. The extra entries correspond to enabled virtual - * displays, and will be non-NULL. + * In HWC 1.3, numDisplays may be up to HWC_NUM_DISPLAY_TYPES. The extra + * entries correspond to enabled virtual displays, and will be non-NULL. * * IMPORTANT NOTE: There is an implicit layer containing opaque black * pixels behind all the layers in the list. It is the responsibility of diff --git a/include/hardware/hwcomposer_defs.h b/include/hardware/hwcomposer_defs.h index 1edfd3da..c69a4bca 100644 --- a/include/hardware/hwcomposer_defs.h +++ b/include/hardware/hwcomposer_defs.h @@ -35,6 +35,7 @@ __BEGIN_DECLS #define HWC_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION_2(1, 0, HWC_HEADER_VERSION) #define HWC_DEVICE_API_VERSION_1_1 HARDWARE_DEVICE_API_VERSION_2(1, 1, HWC_HEADER_VERSION) #define HWC_DEVICE_API_VERSION_1_2 HARDWARE_DEVICE_API_VERSION_2(1, 2, HWC_HEADER_VERSION) +#define HWC_DEVICE_API_VERSION_1_3 HARDWARE_DEVICE_API_VERSION_2(1, 3, HWC_HEADER_VERSION) enum { /* hwc_composer_device_t::set failed in EGL */ @@ -181,12 +182,16 @@ enum { enum { HWC_DISPLAY_PRIMARY = 0, HWC_DISPLAY_EXTERNAL = 1, // HDMI, DP, etc. - HWC_NUM_DISPLAY_TYPES + HWC_DISPLAY_VIRTUAL = 2, + + HWC_NUM_PHYSICAL_DISPLAY_TYPES = 2, + HWC_NUM_DISPLAY_TYPES = 3, }; enum { HWC_DISPLAY_PRIMARY_BIT = 1 << HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL_BIT = 1 << HWC_DISPLAY_EXTERNAL, + HWC_DISPLAY_VIRTUAL_BIT = 1 << HWC_DISPLAY_VIRTUAL, }; /*****************************************************************************/ diff --git a/include/hardware/memtrack.h b/include/hardware/memtrack.h new file mode 100644 index 00000000..57ba4ad7 --- /dev/null +++ b/include/hardware/memtrack.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_HARDWARE_MEMTRACK_H +#define ANDROID_INCLUDE_HARDWARE_MEMTRACK_H + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <hardware/hardware.h> + +__BEGIN_DECLS + +#define MEMTRACK_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) + +/** + * The id of this module + */ +#define MEMTRACK_HARDWARE_MODULE_ID "memtrack" + +/* + * The Memory Tracker HAL is designed to return information about device-specific + * memory usage. The primary goal is to be able to track memory that is not + * trackable in any other way, for example texture memory that is allocated by + * a process, but not mapped in to that process' address space. + * A secondary goal is to be able to categorize memory used by a process into + * GL, graphics, etc. All memory sizes should be in real memory usage, + * accounting for stride, bit depth, rounding up to page size, etc. + * + * A process collecting memory statistics will call getMemory for each + * combination of pid and memory type. For each memory type that it recognizes + * the HAL should fill out an array of memtrack_record structures breaking + * down the statistics of that memory type as much as possible. For example, + * getMemory(<pid>, MEMTRACK_TYPE_GL) might return: + * { { 4096, ACCOUNTED | PRIVATE | SYSTEM }, + * { 40960, UNACCOUNTED | PRIVATE | SYSTEM }, + * { 8192, ACCOUNTED | PRIVATE | DEDICATED }, + * { 8192, UNACCOUNTED | PRIVATE | DEDICATED } } + * If the HAL could not differentiate between SYSTEM and DEDICATED memory, it + * could return: + * { { 12288, ACCOUNTED | PRIVATE }, + * { 49152, UNACCOUNTED | PRIVATE } } + * + * Memory should not overlap between types. For example, a graphics buffer + * that has been mapped into the GPU as a surface should show up when + * MEMTRACK_TYPE_GRAPHICS is requested, and not when MEMTRACK_TYPE_GL + * is requested. + */ + +enum memtrack_type { + MEMTRACK_TYPE_OTHER = 0, + MEMTRACK_TYPE_GL = 1, + MEMTRACK_TYPE_GRAPHICS = 2, + MEMTRACK_TYPE_MULTIMEDIA = 3, + MEMTRACK_TYPE_CAMERA = 4, + MEMTRACK_NUM_TYPES, +}; + +struct memtrack_record { + size_t size_in_bytes; + unsigned int flags; +}; + +/** + * Flags to differentiate memory that can already be accounted for in + * /proc/<pid>/smaps, + * (Shared_Clean + Shared_Dirty + Private_Clean + Private_Dirty = Size). + * In general, memory mapped in to a userspace process is accounted unless + * it was mapped with remap_pfn_range. + * Exactly one of these should be set. + */ +#define MEMTRACK_FLAG_SMAPS_ACCOUNTED (1 << 1) +#define MEMTRACK_FLAG_SMAPS_UNACCOUNTED (1 << 2) + +/** + * Flags to differentiate memory shared across multiple processes vs. memory + * used by a single process. Only zero or one of these may be set in a record. + * If none are set, record is assumed to count shared + private memory. + */ +#define MEMTRACK_FLAG_SHARED (1 << 3) +#define MEMTRACK_FLAG_SHARED_PSS (1 << 4) /* shared / num_procesess */ +#define MEMTRACK_FLAG_PRIVATE (1 << 5) + +/** + * Flags to differentiate memory taken from the kernel's allocation pool vs. + * memory that is dedicated to non-kernel allocations, for example a carveout + * or separate video memory. Only zero or one of these may be set in a record. + * If none are set, record is assumed to count system + dedicated memory. + */ +#define MEMTRACK_FLAG_SYSTEM (1 << 6) +#define MEMTRACK_FLAG_DEDICATED (1 << 7) + +/** + * Flags to differentiate memory accessible by the CPU in non-secure mode vs. + * memory that is protected. Only zero or one of these may be set in a record. + * If none are set, record is assumed to count secure + nonsecure memory. + */ +#define MEMTRACK_FLAG_NONSECURE (1 << 8) +#define MEMTRACK_FLAG_SECURE (1 << 9) + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +typedef struct memtrack_module { + struct hw_module_t common; + + /** + * (*init)() performs memtrack management setup actions and is called + * once before any calls to getMemory(). + * Returns 0 on success, -errno on error. + */ + int (*init)(const struct memtrack_module *module); + + /** + * (*getMemory)() expects an array of record objects and populates up to + * *num_record structures with the sizes of memory plus associated flags for + * that memory. It also updates *num_records with the total number of + * records it could return if *num_records was large enough when passed in. + * Returning records with size 0 is expected, the number of records should + * not vary between calls to getMemory for the same memory type, even + * for different pids. + * + * The caller will often call getMemory for a type and pid with + * *num_records == 0 to determine how many records to allocate room for, + * this case should be a fast-path in the HAL, returning a constant and + * not querying any kernel files. If *num_records passed in is 0, + * then records may be NULL. + * + * This function must be thread-safe, it may get called from multiple + * threads at the same time. + * + * Returns 0 on success, -ENODEV if the type is not supported, -errno + * on other errors. + */ + int (*getMemory)(const struct memtrack_module *module, + pid_t pid, + int type, + struct memtrack_record *records, + size_t *num_records); +} memtrack_module_t; + +__END_DECLS + +#endif // ANDROID_INCLUDE_HARDWARE_MEMTRACK_H diff --git a/include/hardware/sensors.h b/include/hardware/sensors.h index 7778e8f1..4c13848b 100644 --- a/include/hardware/sensors.h +++ b/include/hardware/sensors.h @@ -32,6 +32,7 @@ __BEGIN_DECLS #define SENSORS_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) #define SENSORS_DEVICE_API_VERSION_0_1 HARDWARE_DEVICE_API_VERSION_2(0, 1, SENSORS_HEADER_VERSION) #define SENSORS_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION_2(1, 0, SENSORS_HEADER_VERSION) +#define SENSORS_DEVICE_API_VERSION_1_1 HARDWARE_DEVICE_API_VERSION_2(1, 1, SENSORS_HEADER_VERSION) /** * The id of this module @@ -64,6 +65,15 @@ enum { SENSORS_BATCH_WAKE_UPON_FIFO_FULL = 0x00000002 }; +/* + * what field for meta_data_event_t + */ +enum { + /* a previous flush operation has completed */ + META_DATA_FLUSH_COMPLETE = 1, + META_DATA_VERSION /* always last, leave auto-assigned */ +}; + /** * Definition of the axis used by the sensor HAL API * @@ -132,9 +142,20 @@ enum { * * Each sensor has a type which defines what this sensor measures and how * measures are reported. All types are defined below. + * + * Device manufacturers (OEMs) can define their own sensor types, for + * their private use by applications or services provided by them. Such + * sensor types are specific to an OEM and can't be exposed in the SDK. + * These types must start at SENSOR_TYPE_DEVICE_PRIVATE_BASE. */ /* + * Base for device manufacturers private sensor types. + * These sensor types can't be exposed in the SDK. + */ +#define SENSOR_TYPE_DEVICE_PRIVATE_BASE 0x10000 + +/* * Sensor fusion and virtual sensors * * Many sensor types are or can be implemented as virtual sensors from @@ -174,6 +195,40 @@ enum { * */ + +/* + * SENSOR_TYPE_META_DATA + * trigger-mode: n/a + * wake-up sensor: n/a + * + * NO SENSOR OF THAT TYPE MUST BE RETURNED (*get_sensors_list)() + * + * SENSOR_TYPE_META_DATA is a special token used to populate the + * sensors_meta_data_event structure. It doesn't correspond to a physical + * sensor. sensors_meta_data_event are special, they exist only inside + * the HAL and are generated spontaneously, as opposed to be related to + * a physical sensor. + * + * sensors_meta_data_event_t.version must be META_DATA_VERSION + * sensors_meta_data_event_t.sensor must be 0 + * sensors_meta_data_event_t.type must be SENSOR_TYPE_META_DATA + * sensors_meta_data_event_t.reserved must be 0 + * sensors_meta_data_event_t.timestamp must be 0 + * + * The payload is a meta_data_event_t, where: + * meta_data_event_t.what can take the following values: + * + * META_DATA_FLUSH_COMPLETE + * This event indicates that a previous (*flush)() call has completed for the sensor + * handle specified in meta_data_event_t.sensor. + * see (*flush)() for more details + * + * All other values for meta_data_event_t.what are reserved and + * must not be used. + * + */ +#define SENSOR_TYPE_META_DATA (0) + /* * SENSOR_TYPE_ACCELEROMETER * trigger-mode: continuous @@ -451,6 +506,9 @@ enum { * SENSOR_TYPE_MAGNETIC_FIELD must be present and both must return the * same sensor_t::name and sensor_t::vendor. * + * Minimum filtering should be applied to this sensor. In particular, low pass + * filters should be avoided. + * * See SENSOR_TYPE_MAGNETIC_FIELD for more information */ #define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED (14) @@ -608,7 +666,7 @@ enum { * * A sensor of this type returns the number of steps taken by the user since * the last reboot while activated. The value is returned as a uint64_t and is - * reset to zero only on a system reboot. + * reset to zero only on a system / android reboot. * * The timestamp of the event is set to the time when the first step * for that event was taken. @@ -662,7 +720,9 @@ enum { * of using a gyroscope. * * This sensor must be based on a magnetometer. It cannot be implemented using - * a gyroscope, and gyroscope input cannot be used by this sensor. + * a gyroscope, and gyroscope input cannot be used by this sensor, as the + * goal of this sensor is to be low power. + * The accelerometer can be (and usually is) used. * * Just like SENSOR_TYPE_ROTATION_VECTOR, this sensor reports an estimated * heading accuracy: @@ -740,6 +800,11 @@ typedef struct { }; } uncalibrated_event_t; +typedef struct meta_data_event { + int32_t what; + int32_t sensor; +} meta_data_event_t; + /** * Union of the various types of sensor data * that can be returned. @@ -761,48 +826,63 @@ typedef struct sensors_event_t { int64_t timestamp; union { - float data[16]; + union { + float data[16]; - /* acceleration values are in meter per second per second (m/s^2) */ - sensors_vec_t acceleration; + /* acceleration values are in meter per second per second (m/s^2) */ + sensors_vec_t acceleration; - /* magnetic vector values are in micro-Tesla (uT) */ - sensors_vec_t magnetic; + /* magnetic vector values are in micro-Tesla (uT) */ + sensors_vec_t magnetic; - /* orientation values are in degrees */ - sensors_vec_t orientation; + /* orientation values are in degrees */ + sensors_vec_t orientation; - /* gyroscope values are in rad/s */ - sensors_vec_t gyro; + /* gyroscope values are in rad/s */ + sensors_vec_t gyro; - /* temperature is in degrees centigrade (Celsius) */ - float temperature; + /* temperature is in degrees centigrade (Celsius) */ + float temperature; - /* distance in centimeters */ - float distance; + /* distance in centimeters */ + float distance; - /* light in SI lux units */ - float light; + /* light in SI lux units */ + float light; - /* pressure in hectopascal (hPa) */ - float pressure; + /* pressure in hectopascal (hPa) */ + float pressure; - /* relative humidity in percent */ - float relative_humidity; + /* relative humidity in percent */ + float relative_humidity; - /* step-counter */ - uint64_t step_counter; + /* uncalibrated gyroscope values are in rad/s */ + uncalibrated_event_t uncalibrated_gyro; - /* uncalibrated gyroscope values are in rad/s */ - uncalibrated_event_t uncalibrated_gyro; + /* uncalibrated magnetometer values are in micro-Teslas */ + uncalibrated_event_t uncalibrated_magnetic; - /* uncalibrated magnetometer values are in micro-Teslas */ - uncalibrated_event_t uncalibrated_magnetic; + /* this is a special event. see SENSOR_TYPE_META_DATA above. + * sensors_meta_data_event_t events are all reported with a type of + * SENSOR_TYPE_META_DATA. The handle is ignored and must be zero. + */ + meta_data_event_t meta_data; + }; + + union { + uint64_t data[8]; + + /* step-counter */ + uint64_t step_counter; + } u64; }; - uint32_t reserved1[4]; + uint32_t reserved1[4]; } sensors_event_t; +/* see SENSOR_TYPE_META_DATA */ +typedef sensors_event_t sensors_meta_data_event_t; + struct sensor_t; @@ -865,8 +945,21 @@ struct sensor_t { */ int32_t minDelay; + /* number of events reserved for this sensor in the batch mode FIFO. + * If there is a dedicated FIFO for this sensor, then this is the + * size of this FIFO. If the FIFO is shared with other sensors, + * this is the size reserved for that sensor and it can be zero. + */ + uint32_t fifoReservedEventCount; + + /* maximum number of events of this sensor that could be batched. + * This is especially relevant when the FIFO is shared between + * several sensors; this value is then set to the size of that FIFO. + */ + uint32_t fifoMaxEventCount; + /* reserved fields, must be zero */ - void* reserved[8]; + void* reserved[6]; }; @@ -903,6 +996,10 @@ typedef struct sensors_poll_device_1 { * handle is the handle of the sensor to change. * enabled set to 1 to enable, or 0 to disable the sensor. * + * if enabled is set to 1, the sensor is activated even if + * setDelay() wasn't called before. In this case, a default rate + * should be used. + * * unless otherwise noted in the sensor types definitions, an * activated sensor never prevents the SoC to go into suspend * mode; that is, the HAL shall not hold a partial wake-lock on @@ -912,10 +1009,10 @@ typedef struct sensors_poll_device_1 { * receiving an event and they must still accept to be deactivated * through a call to activate(..., ..., 0). * - * if "enabled" is true and the sensor is already activated, this + * if "enabled" is 1 and the sensor is already activated, this * function is a no-op and succeeds. * - * if "enabled" is false and the sensor is already de-activated, + * if "enabled" is 0 and the sensor is already de-activated, * this function is a no-op and succeeds. * * return 0 on success, negative errno code otherwise @@ -939,6 +1036,9 @@ typedef struct sensors_poll_device_1 { * sensor_t::minDelay unless sensor_t::minDelay is 0, in which * case it is clamped to >= 1ms. * + * setDelay will not be called when the sensor is in batching mode. + * In this case, batch() will be called with the new period. + * * @return 0 if successful, < 0 on error */ int (*setDelay)(struct sensors_poll_device_t *dev, @@ -1068,19 +1168,30 @@ typedef struct sensors_poll_device_1 { * if a batch call with SENSORS_BATCH_DRY_RUN is successful, * the same call without SENSORS_BATCH_DRY_RUN must succeed as well). * - * If successful, 0 is returned. - * If the specified sensor doesn't support batch mode, -EINVAL is returned. - * If the specified sensor's trigger-mode is one-shot, -EINVAL is returned. - * If WAKE_UPON_FIFO_FULL is specified and the specified sensor's internal - * FIFO is too small to store at least 10 seconds worth of data at the - * given rate, -EINVAL is returned. Note that as stated above, this has to - * be determined at compile time, and not based on the state of the system. - * If some other constraints above cannot be satisfied, -EINVAL is returned. + * When timeout is not 0: + * If successful, 0 is returned. + * If the specified sensor doesn't support batch mode, return -EINVAL. + * If the specified sensor's trigger-mode is one-shot, return -EINVAL. + * If WAKE_UPON_FIFO_FULL is specified and the specified sensor's internal + * FIFO is too small to store at least 10 seconds worth of data at the + * given rate, -EINVAL is returned. Note that as stated above, this has to + * be determined at compile time, and not based on the state of the + * system. + * If some other constraints above cannot be satisfied, return -EINVAL. * * Note: the timeout parameter, when > 0, has no impact on whether this * function succeeds or fails. * - * If timeout is set to 0, this function must succeed. + * When timeout is 0: + * The caller will never set the wake_upon_fifo_full flag. + * The function must succeed, and batch mode must be deactivated. + * + * Independently of whether DRY_RUN is specified, When the call to batch() + * fails, no state should be changed. In particular, a failed call to + * batch() should not change the rate of the sensor. Example: + * setDelay(..., 10ms) + * batch(..., 20ms, ...) fails + * rate should stay 10ms. * * * IMPLEMENTATION NOTES: @@ -1150,6 +1261,35 @@ typedef struct sensors_poll_device_1 { int (*batch)(struct sensors_poll_device_1* dev, int handle, int flags, int64_t period_ns, int64_t timeout); + /* + * Flush adds a META_DATA_FLUSH_COMPLETE event (sensors_event_meta_data_t) + * to the end of the "batch mode" FIFO for the specified sensor and flushes + * the FIFO; those events are delivered as usual (i.e.: as if the batch + * timeout had expired) and removed from the FIFO. + * + * See the META_DATA_FLUSH_COMPLETE section for details about the + * META_DATA_FLUSH_COMPLETE event. + * + * The flush happens asynchronously (i.e.: this function must return + * immediately). + * + * If the implementation uses a single FIFO for several sensors, that + * FIFO is flushed and the META_DATA_FLUSH_COMPLETE event is added only + * for the specified sensor. + * + * If the specified sensor wasn't in batch mode, flush succeeds and + * promptly sends a META_DATA_FLUSH_COMPLETE event for that sensor. + * + * If the FIFO was empty at the time of the call, flush returns + * 0 (success) and promptly sends a META_DATA_FLUSH_COMPLETE event + * for that sensor. + * + * If the specified sensor wasn't enabled, flush returns -EINVAL. + * + * return 0 on success, negative errno code otherwise. + */ + int (*flush)(struct sensors_poll_device_1* dev, int handle); + void (*reserved_procs[8])(void); } sensors_poll_device_1_t; diff --git a/modules/Android.mk b/modules/Android.mk index 486b42dd..b2d5a2a7 100644 --- a/modules/Android.mk +++ b/modules/Android.mk @@ -1,2 +1,3 @@ -hardware_modules := gralloc hwcomposer audio nfc nfc-nci local_time power usbaudio audio_remote_submix camera +hardware_modules := gralloc hwcomposer audio nfc nfc-nci local_time \ + power usbaudio audio_remote_submix camera consumerir include $(call all-named-subdir-makefiles,$(hardware_modules)) diff --git a/modules/audio/audio_policy.c b/modules/audio/audio_policy.c index 2dd3dbef..9335654e 100644 --- a/modules/audio/audio_policy.c +++ b/modules/audio/audio_policy.c @@ -99,7 +99,8 @@ static audio_io_handle_t ap_get_output(struct audio_policy *pol, uint32_t sampling_rate, audio_format_t format, audio_channel_mask_t channelMask, - audio_output_flags_t flags) + audio_output_flags_t flags, + const audio_offload_info_t *info) { return 0; } @@ -229,6 +230,12 @@ static int ap_dump(const struct audio_policy *pol, int fd) return -ENOSYS; } +static bool ap_is_offload_supported(const struct audio_policy *pol, + const audio_offload_info_t *info) +{ + return false; +} + static int create_default_ap(const struct audio_policy_device *device, struct audio_policy_service_ops *aps_ops, void *service, @@ -278,6 +285,8 @@ static int create_default_ap(const struct audio_policy_device *device, dap->policy.is_stream_active = ap_is_stream_active; dap->policy.dump = ap_dump; + dap->policy.is_offload_supported = ap_is_offload_supported; + dap->service = service; dap->aps_ops = aps_ops; diff --git a/modules/audio_remote_submix/audio_hw.cpp b/modules/audio_remote_submix/audio_hw.cpp index 5e88ef73..9df17b64 100755..100644 --- a/modules/audio_remote_submix/audio_hw.cpp +++ b/modules/audio_remote_submix/audio_hw.cpp @@ -271,7 +271,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, return 0; } else { // write() returned UNDERRUN or WOULD_BLOCK, retry - ALOGE("out_write() write to pipe returned unexpected %16lx", written_frames); + ALOGE("out_write() write to pipe returned unexpected %d", written_frames); written_frames = sink->write(buffer, frames); } } @@ -281,7 +281,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, pthread_mutex_unlock(&out->dev->lock); if (written_frames < 0) { - ALOGE("out_write() failed writing to pipe with %16lx", written_frames); + ALOGE("out_write() failed writing to pipe with %d", written_frames); return 0; } else { ALOGV("out_write() wrote %lu bytes)", written_frames * frame_size); @@ -549,7 +549,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev, config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; rsxadev->config.channel_mask = config->channel_mask; - if ((config->sample_rate != 48000) || (config->sample_rate != 44100)) { + if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) { config->sample_rate = DEFAULT_RATE_HZ; } rsxadev->config.rate = config->sample_rate; @@ -708,7 +708,7 @@ static int adev_open_input_stream(struct audio_hw_device *dev, config->channel_mask = AUDIO_CHANNEL_IN_STEREO; rsxadev->config.channel_mask = config->channel_mask; - if ((config->sample_rate != 48000) || (config->sample_rate != 44100)) { + if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) { config->sample_rate = DEFAULT_RATE_HZ; } rsxadev->config.rate = config->sample_rate; diff --git a/modules/camera/Android.mk b/modules/camera/Android.mk index eebffc12..e02a143d 100644 --- a/modules/camera/Android.mk +++ b/modules/camera/Android.mk @@ -26,11 +26,14 @@ LOCAL_C_INCLUDES += \ LOCAL_SRC_FILES := \ CameraHAL.cpp \ Camera.cpp \ + Metadata.cpp \ + Stream.cpp \ LOCAL_SHARED_LIBRARIES := \ libcamera_metadata \ libcutils \ liblog \ + libsync \ LOCAL_CFLAGS += -Wall -Wextra -fvisibility=hidden diff --git a/modules/camera/Camera.cpp b/modules/camera/Camera.cpp index 203b7727..973380ef 100644 --- a/modules/camera/Camera.cpp +++ b/modules/camera/Camera.cpp @@ -17,7 +17,12 @@ #include <cstdlib> #include <pthread.h> #include <hardware/camera3.h> +#include <sync/sync.h> +#include <system/camera_metadata.h> +#include <system/graphics.h> #include "CameraHAL.h" +#include "Metadata.h" +#include "Stream.h" //#define LOG_NDEBUG 0 #define LOG_TAG "Camera" @@ -25,9 +30,14 @@ #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) #include <cutils/trace.h> +#include "ScopedTrace.h" #include "Camera.h" +#define CAMERA_SYNC_TIMEOUT 5000 // in msecs + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + namespace default_camera_hal { extern "C" { @@ -42,14 +52,19 @@ static int close_device(hw_device_t* dev) Camera::Camera(int id) : mId(id), + mStaticInfo(NULL), mBusy(false), - mCallbackOps(NULL) + mCallbackOps(NULL), + mStreams(NULL), + mNumStreams(0), + mSettings(NULL) { - pthread_mutex_init(&mMutex, - NULL); // No pthread mutex attributes. + pthread_mutex_init(&mMutex, NULL); + pthread_mutex_init(&mStaticInfoMutex, NULL); memset(&mDevice, 0, sizeof(mDevice)); mDevice.common.tag = HARDWARE_DEVICE_TAG; + mDevice.common.version = CAMERA_DEVICE_API_VERSION_3_0; mDevice.common.close = close_device; mDevice.ops = const_cast<camera3_device_ops_t*>(&sOps); mDevice.priv = this; @@ -57,16 +72,17 @@ Camera::Camera(int id) Camera::~Camera() { + pthread_mutex_destroy(&mMutex); + pthread_mutex_destroy(&mStaticInfoMutex); } int Camera::open(const hw_module_t *module, hw_device_t **device) { ALOGI("%s:%d: Opening camera device", __func__, mId); - ATRACE_BEGIN(__func__); + CAMTRACE_CALL(); pthread_mutex_lock(&mMutex); if (mBusy) { pthread_mutex_unlock(&mMutex); - ATRACE_END(); ALOGE("%s:%d: Error! Camera device already opened", __func__, mId); return -EBUSY; } @@ -77,18 +93,33 @@ int Camera::open(const hw_module_t *module, hw_device_t **device) *device = &mDevice.common; pthread_mutex_unlock(&mMutex); - ATRACE_END(); + return 0; +} + +int Camera::getInfo(struct camera_info *info) +{ + info->facing = CAMERA_FACING_FRONT; + info->orientation = 0; + info->device_version = mDevice.common.version; + + pthread_mutex_lock(&mStaticInfoMutex); + if (mStaticInfo == NULL) { + mStaticInfo = initStaticInfo(); + } + pthread_mutex_unlock(&mStaticInfoMutex); + + info->static_camera_characteristics = mStaticInfo; + return 0; } int Camera::close() { ALOGI("%s:%d: Closing camera device", __func__, mId); - ATRACE_BEGIN(__func__); + CAMTRACE_CALL(); pthread_mutex_lock(&mMutex); if (!mBusy) { pthread_mutex_unlock(&mMutex); - ATRACE_END(); ALOGE("%s:%d: Error! Camera device not open", __func__, mId); return -EINVAL; } @@ -97,7 +128,6 @@ int Camera::close() mBusy = false; pthread_mutex_unlock(&mMutex); - ATRACE_END(); return 0; } @@ -105,44 +135,500 @@ int Camera::initialize(const camera3_callback_ops_t *callback_ops) { ALOGV("%s:%d: callback_ops=%p", __func__, mId, callback_ops); mCallbackOps = callback_ops; + // Create standard settings templates + // 0 is invalid as template + mTemplates[0] = NULL; + // CAMERA3_TEMPLATE_PREVIEW = 1 + mTemplates[1] = new Metadata(ANDROID_CONTROL_MODE_OFF, + ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW); + // CAMERA3_TEMPLATE_STILL_CAPTURE = 2 + mTemplates[2] = new Metadata(ANDROID_CONTROL_MODE_OFF, + ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE); + // CAMERA3_TEMPLATE_VIDEO_RECORD = 3 + mTemplates[3] = new Metadata(ANDROID_CONTROL_MODE_OFF, + ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + // CAMERA3_TEMPLATE_VIDEO_SNAPSHOT = 4 + mTemplates[4] = new Metadata(ANDROID_CONTROL_MODE_OFF, + ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT); + // CAMERA3_TEMPLATE_STILL_ZERO_SHUTTER_LAG = 5 + mTemplates[5] = new Metadata(ANDROID_CONTROL_MODE_OFF, + ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG); + // Pre-generate metadata structures + for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; i++) { + mTemplates[i]->generate(); + } + // TODO: create vendor templates return 0; } -int Camera::configureStreams(camera3_stream_configuration_t *stream_list) +camera_metadata_t *Camera::initStaticInfo() +{ + /* + * Setup static camera info. This will have to customized per camera + * device. + */ + Metadata m; + + /* android.control */ + int32_t android_control_ae_available_target_fps_ranges[] = {30, 30}; + m.addInt32(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, + ARRAY_SIZE(android_control_ae_available_target_fps_ranges), + android_control_ae_available_target_fps_ranges); + + int32_t android_control_ae_compensation_range[] = {-4, 4}; + m.addInt32(ANDROID_CONTROL_AE_COMPENSATION_RANGE, + ARRAY_SIZE(android_control_ae_compensation_range), + android_control_ae_compensation_range); + + camera_metadata_rational_t android_control_ae_compensation_step[] = {{2,1}}; + m.addRational(ANDROID_CONTROL_AE_COMPENSATION_STEP, + ARRAY_SIZE(android_control_ae_compensation_step), + android_control_ae_compensation_step); + + int32_t android_control_max_regions[] = {1}; + m.addInt32(ANDROID_CONTROL_MAX_REGIONS, + ARRAY_SIZE(android_control_max_regions), + android_control_max_regions); + + /* android.jpeg */ + int32_t android_jpeg_available_thumbnail_sizes[] = {0, 0, 128, 96}; + m.addInt32(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, + ARRAY_SIZE(android_jpeg_available_thumbnail_sizes), + android_jpeg_available_thumbnail_sizes); + + /* android.lens */ + float android_lens_info_available_focal_lengths[] = {1.0}; + m.addFloat(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, + ARRAY_SIZE(android_lens_info_available_focal_lengths), + android_lens_info_available_focal_lengths); + + /* android.request */ + int32_t android_request_max_num_output_streams[] = {0, 3, 1}; + m.addInt32(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, + ARRAY_SIZE(android_request_max_num_output_streams), + android_request_max_num_output_streams); + + /* android.scaler */ + int32_t android_scaler_available_formats[] = { + HAL_PIXEL_FORMAT_RAW_SENSOR, + HAL_PIXEL_FORMAT_BLOB, + HAL_PIXEL_FORMAT_RGBA_8888, + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + // These are handled by YCbCr_420_888 + // HAL_PIXEL_FORMAT_YV12, + // HAL_PIXEL_FORMAT_YCrCb_420_SP, + HAL_PIXEL_FORMAT_YCbCr_420_888}; + m.addInt32(ANDROID_SCALER_AVAILABLE_FORMATS, + ARRAY_SIZE(android_scaler_available_formats), + android_scaler_available_formats); + + int64_t android_scaler_available_jpeg_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_jpeg_min_durations), + android_scaler_available_jpeg_min_durations); + + int32_t android_scaler_available_jpeg_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, + ARRAY_SIZE(android_scaler_available_jpeg_sizes), + android_scaler_available_jpeg_sizes); + + float android_scaler_available_max_digital_zoom[] = {1}; + m.addFloat(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, + ARRAY_SIZE(android_scaler_available_max_digital_zoom), + android_scaler_available_max_digital_zoom); + + int64_t android_scaler_available_processed_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_processed_min_durations), + android_scaler_available_processed_min_durations); + + int32_t android_scaler_available_processed_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, + ARRAY_SIZE(android_scaler_available_processed_sizes), + android_scaler_available_processed_sizes); + + int64_t android_scaler_available_raw_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_raw_min_durations), + android_scaler_available_raw_min_durations); + + int32_t android_scaler_available_raw_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_RAW_SIZES, + ARRAY_SIZE(android_scaler_available_raw_sizes), + android_scaler_available_raw_sizes); + + /* android.sensor */ + + int32_t android_sensor_info_active_array_size[] = {0, 0, 640, 480}; + m.addInt32(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, + ARRAY_SIZE(android_sensor_info_active_array_size), + android_sensor_info_active_array_size); + + int32_t android_sensor_info_sensitivity_range[] = + {100, 1600}; + m.addInt32(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE, + ARRAY_SIZE(android_sensor_info_sensitivity_range), + android_sensor_info_sensitivity_range); + + int64_t android_sensor_info_max_frame_duration[] = {30000000000}; + m.addInt64(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, + ARRAY_SIZE(android_sensor_info_max_frame_duration), + android_sensor_info_max_frame_duration); + + float android_sensor_info_physical_size[] = {3.2, 2.4}; + m.addFloat(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, + ARRAY_SIZE(android_sensor_info_physical_size), + android_sensor_info_physical_size); + + int32_t android_sensor_info_pixel_array_size[] = {640, 480}; + m.addInt32(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, + ARRAY_SIZE(android_sensor_info_pixel_array_size), + android_sensor_info_pixel_array_size); + + int32_t android_sensor_orientation[] = {0}; + m.addInt32(ANDROID_SENSOR_ORIENTATION, + ARRAY_SIZE(android_sensor_orientation), + android_sensor_orientation); + + /* End of static camera characteristics */ + + return clone_camera_metadata(m.generate()); +} + +int Camera::configureStreams(camera3_stream_configuration_t *stream_config) { - ALOGV("%s:%d: stream_list=%p", __func__, mId, stream_list); - // TODO: validate input, create internal stream representations + camera3_stream_t *astream; + Stream **newStreams = NULL; + + CAMTRACE_CALL(); + ALOGV("%s:%d: stream_config=%p", __func__, mId, stream_config); + + if (stream_config == NULL) { + ALOGE("%s:%d: NULL stream configuration array", __func__, mId); + return -EINVAL; + } + if (stream_config->num_streams == 0) { + ALOGE("%s:%d: Empty stream configuration array", __func__, mId); + return -EINVAL; + } + + // Create new stream array + newStreams = new Stream*[stream_config->num_streams]; + ALOGV("%s:%d: Number of Streams: %d", __func__, mId, + stream_config->num_streams); + + pthread_mutex_lock(&mMutex); + + // Mark all current streams unused for now + for (int i = 0; i < mNumStreams; i++) + mStreams[i]->mReuse = false; + // Fill new stream array with reused streams and new streams + for (unsigned int i = 0; i < stream_config->num_streams; i++) { + astream = stream_config->streams[i]; + if (astream->max_buffers > 0) { + ALOGV("%s:%d: Reusing stream %d", __func__, mId, i); + newStreams[i] = reuseStream(astream); + } else { + ALOGV("%s:%d: Creating new stream %d", __func__, mId, i); + newStreams[i] = new Stream(mId, astream); + } + + if (newStreams[i] == NULL) { + ALOGE("%s:%d: Error processing stream %d", __func__, mId, i); + goto err_out; + } + astream->priv = newStreams[i]; + } + + // Verify the set of streams in aggregate + if (!isValidStreamSet(newStreams, stream_config->num_streams)) { + ALOGE("%s:%d: Invalid stream set", __func__, mId); + goto err_out; + } + + // Set up all streams (calculate usage/max_buffers for each) + setupStreams(newStreams, stream_config->num_streams); + + // Destroy all old streams and replace stream array with new one + destroyStreams(mStreams, mNumStreams); + mStreams = newStreams; + mNumStreams = stream_config->num_streams; + + // Clear out last seen settings metadata + setSettings(NULL); + + pthread_mutex_unlock(&mMutex); return 0; + +err_out: + // Clean up temporary streams, preserve existing mStreams/mNumStreams + destroyStreams(newStreams, stream_config->num_streams); + pthread_mutex_unlock(&mMutex); + return -EINVAL; +} + +void Camera::destroyStreams(Stream **streams, int count) +{ + if (streams == NULL) + return; + for (int i = 0; i < count; i++) { + // Only destroy streams that weren't reused + if (streams[i] != NULL && !streams[i]->mReuse) + delete streams[i]; + } + delete [] streams; +} + +Stream *Camera::reuseStream(camera3_stream_t *astream) +{ + Stream *priv = reinterpret_cast<Stream*>(astream->priv); + // Verify the re-used stream's parameters match + if (!priv->isValidReuseStream(mId, astream)) { + ALOGE("%s:%d: Mismatched parameter in reused stream", __func__, mId); + return NULL; + } + // Mark stream to be reused + priv->mReuse = true; + return priv; +} + +bool Camera::isValidStreamSet(Stream **streams, int count) +{ + int inputs = 0; + int outputs = 0; + + if (streams == NULL) { + ALOGE("%s:%d: NULL stream configuration streams", __func__, mId); + return false; + } + if (count == 0) { + ALOGE("%s:%d: Zero count stream configuration streams", __func__, mId); + return false; + } + // Validate there is at most one input stream and at least one output stream + for (int i = 0; i < count; i++) { + // A stream may be both input and output (bidirectional) + if (streams[i]->isInputType()) + inputs++; + if (streams[i]->isOutputType()) + outputs++; + } + ALOGV("%s:%d: Configuring %d output streams and %d input streams", + __func__, mId, outputs, inputs); + if (outputs < 1) { + ALOGE("%s:%d: Stream config must have >= 1 output", __func__, mId); + return false; + } + if (inputs > 1) { + ALOGE("%s:%d: Stream config must have <= 1 input", __func__, mId); + return false; + } + // TODO: check for correct number of Bayer/YUV/JPEG/Encoder streams + return true; +} + +void Camera::setupStreams(Stream **streams, int count) +{ + /* + * This is where the HAL has to decide internally how to handle all of the + * streams, and then produce usage and max_buffer values for each stream. + * Note, the stream array has been checked before this point for ALL invalid + * conditions, so it must find a successful configuration for this stream + * array. The HAL may not return an error from this point. + * + * In this demo HAL, we just set all streams to be the same dummy values; + * real implementations will want to avoid USAGE_SW_{READ|WRITE}_OFTEN. + */ + for (int i = 0; i < count; i++) { + uint32_t usage = 0; + + if (streams[i]->isOutputType()) + usage |= GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_CAMERA_WRITE; + if (streams[i]->isInputType()) + usage |= GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_HW_CAMERA_READ; + + streams[i]->setUsage(usage); + streams[i]->setMaxBuffers(1); + } } int Camera::registerStreamBuffers(const camera3_stream_buffer_set_t *buf_set) { ALOGV("%s:%d: buffer_set=%p", __func__, mId, buf_set); - // TODO: register buffers with hardware - return 0; + if (buf_set == NULL) { + ALOGE("%s:%d: NULL buffer set", __func__, mId); + return -EINVAL; + } + if (buf_set->stream == NULL) { + ALOGE("%s:%d: NULL stream handle", __func__, mId); + return -EINVAL; + } + Stream *stream = reinterpret_cast<Stream*>(buf_set->stream->priv); + return stream->registerBuffers(buf_set); } const camera_metadata_t* Camera::constructDefaultRequestSettings(int type) { ALOGV("%s:%d: type=%d", __func__, mId, type); - // TODO: return statically built default request - return NULL; + + if (type < 1 || type >= CAMERA3_TEMPLATE_COUNT) { + ALOGE("%s:%d: Invalid template request type: %d", __func__, mId, type); + return NULL; + } + return mTemplates[type]->generate(); } int Camera::processCaptureRequest(camera3_capture_request_t *request) { + camera3_capture_result result; + ALOGV("%s:%d: request=%p", __func__, mId, request); - ATRACE_BEGIN(__func__); + CAMTRACE_CALL(); if (request == NULL) { ALOGE("%s:%d: NULL request recieved", __func__, mId); - ATRACE_END(); return -EINVAL; } - // TODO: verify request; submit request to hardware - ATRACE_END(); + ALOGV("%s:%d: Request Frame:%d Settings:%p", __func__, mId, + request->frame_number, request->settings); + + // NULL indicates use last settings + if (request->settings == NULL) { + if (mSettings == NULL) { + ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p", + __func__, mId, request->frame_number, request); + return -EINVAL; + } + } else { + setSettings(request->settings); + } + + if (request->input_buffer != NULL) { + ALOGV("%s:%d: Reprocessing input buffer %p", __func__, mId, + request->input_buffer); + + if (!isValidReprocessSettings(request->settings)) { + ALOGE("%s:%d: Invalid settings for reprocess request: %p", + __func__, mId, request->settings); + return -EINVAL; + } + } else { + ALOGV("%s:%d: Capturing new frame.", __func__, mId); + + if (!isValidCaptureSettings(request->settings)) { + ALOGE("%s:%d: Invalid settings for capture request: %p", + __func__, mId, request->settings); + return -EINVAL; + } + } + + if (request->num_output_buffers <= 0) { + ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId, + request->num_output_buffers); + return -EINVAL; + } + result.num_output_buffers = request->num_output_buffers; + result.output_buffers = new camera3_stream_buffer_t[result.num_output_buffers]; + for (unsigned int i = 0; i < request->num_output_buffers; i++) { + int res = processCaptureBuffer(&request->output_buffers[i], + const_cast<camera3_stream_buffer_t*>(&result.output_buffers[i])); + if (res) + goto err_out; + } + + result.frame_number = request->frame_number; + // TODO: return actual captured/reprocessed settings + result.result = request->settings; + // TODO: asynchronously return results + notifyShutter(request->frame_number, 0); + mCallbackOps->process_capture_result(mCallbackOps, &result); + return 0; + +err_out: + delete [] result.output_buffers; + // TODO: this should probably be a total device failure; transient for now + return -EINVAL; +} + +void Camera::setSettings(const camera_metadata_t *new_settings) +{ + if (mSettings != NULL) { + free_camera_metadata(mSettings); + mSettings = NULL; + } + + if (new_settings != NULL) + mSettings = clone_camera_metadata(new_settings); +} + +bool Camera::isValidCaptureSettings(const camera_metadata_t* /*settings*/) +{ + // TODO: reject settings that cannot be captured + return true; +} + +bool Camera::isValidReprocessSettings(const camera_metadata_t* /*settings*/) +{ + // TODO: reject settings that cannot be reprocessed + // input buffers unimplemented, use this to reject reprocessing requests + ALOGE("%s:%d: Input buffer reprocessing not implemented", __func__, mId); + return false; +} + +int Camera::processCaptureBuffer(const camera3_stream_buffer_t *in, + camera3_stream_buffer_t *out) +{ + if (in->acquire_fence != -1) { + int res = sync_wait(in->acquire_fence, CAMERA_SYNC_TIMEOUT); + if (res == -ETIME) { + ALOGE("%s:%d: Timeout waiting on buffer acquire fence", + __func__, mId); + return res; + } else if (res) { + ALOGE("%s:%d: Error waiting on buffer acquire fence: %s(%d)", + __func__, mId, strerror(-res), res); + return res; + } + } + + out->stream = in->stream; + out->buffer = in->buffer; + out->status = CAMERA3_BUFFER_STATUS_OK; + // TODO: use driver-backed release fences + out->acquire_fence = -1; + out->release_fence = -1; + + // TODO: lock and software-paint buffer + return 0; +} + +void Camera::notifyShutter(uint32_t frame_number, uint64_t timestamp) +{ + int res; + struct timespec ts; + + // If timestamp is 0, get timestamp from right now instead + if (timestamp == 0) { + ALOGW("%s:%d: No timestamp provided, using CLOCK_BOOTTIME", + __func__, mId); + res = clock_gettime(CLOCK_BOOTTIME, &ts); + if (res == 0) { + timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec; + } else { + ALOGE("%s:%d: No timestamp and failed to get CLOCK_BOOTTIME %s(%d)", + __func__, mId, strerror(errno), errno); + } + } + camera3_notify_msg_t m; + memset(&m, 0, sizeof(m)); + m.type = CAMERA3_MSG_SHUTTER; + m.message.shutter.frame_number = frame_number; + m.message.shutter.timestamp = timestamp; + mCallbackOps->notify(mCallbackOps, &m); } void Camera::getMetadataVendorTagOps(vendor_tag_query_ops_t *ops) @@ -153,7 +639,7 @@ void Camera::getMetadataVendorTagOps(vendor_tag_query_ops_t *ops) void Camera::dump(int fd) { - ALOGV("%s:%d: Dumping to fd %d", fd); + ALOGV("%s:%d: Dumping to fd %d", __func__, mId, fd); // TODO: dprintf all relevant state to fd } diff --git a/modules/camera/Camera.h b/modules/camera/Camera.h index f2ad0934..be672f90 100644 --- a/modules/camera/Camera.h +++ b/modules/camera/Camera.h @@ -20,6 +20,8 @@ #include <pthread.h> #include <hardware/hardware.h> #include <hardware/camera3.h> +#include "Metadata.h" +#include "Stream.h" namespace default_camera_hal { // Camera represents a physical camera on a device. @@ -35,6 +37,7 @@ class Camera { // Common Camera Device Operations (see <hardware/camera_common.h>) int open(const hw_module_t *module, hw_device_t **device); + int getInfo(struct camera_info *info); int close(); // Camera v3 Device Operations (see <hardware/camera3.h>) @@ -50,8 +53,34 @@ class Camera { camera3_device_t mDevice; private: + // Separate initialization method for static metadata + camera_metadata_t *initStaticInfo(); + // Reuse a stream already created by this device + Stream *reuseStream(camera3_stream_t *astream); + // Destroy all streams in a stream array, and the array itself + void destroyStreams(Stream **array, int count); + // Verify a set of streams is valid in aggregate + bool isValidStreamSet(Stream **array, int count); + // Calculate usage and max_bufs of each stream + void setupStreams(Stream **array, int count); + // Copy new settings for re-use and clean up old settings. + void setSettings(const camera_metadata_t *new_settings); + // Verify settings are valid for a capture + bool isValidCaptureSettings(const camera_metadata_t *settings); + // Verify settings are valid for reprocessing an input buffer + bool isValidReprocessSettings(const camera_metadata_t *settings); + // Process an output buffer + int processCaptureBuffer(const camera3_stream_buffer_t *in, + camera3_stream_buffer_t *out); + // Send a shutter notify message with start of exposure time + void notifyShutter(uint32_t frame_number, uint64_t timestamp); + // Identifier used by framework to distinguish cameras const int mId; + // Metadata containing persistent camera characteristics + Metadata mMetadata; + // camera_metadata structure containing static characteristics + camera_metadata_t *mStaticInfo; // Busy flag indicates camera is in use bool mBusy; // Camera device operations handle shared by all devices @@ -60,6 +89,17 @@ class Camera { const camera3_callback_ops_t *mCallbackOps; // Lock protecting the Camera object for modifications pthread_mutex_t mMutex; + // Lock protecting only static camera characteristics, which may + // be accessed without the camera device open + pthread_mutex_t mStaticInfoMutex; + // Array of handles to streams currently in use by the device + Stream **mStreams; + // Number of streams in mStreams + int mNumStreams; + // Static array of standard camera settings templates + Metadata *mTemplates[CAMERA3_TEMPLATE_COUNT]; + // Most recent request settings seen, memoized to be reused + camera_metadata_t *mSettings; }; } // namespace default_camera_hal diff --git a/modules/camera/CameraHAL.cpp b/modules/camera/CameraHAL.cpp index 6cae7d22..dfbbe4ca 100644 --- a/modules/camera/CameraHAL.cpp +++ b/modules/camera/CameraHAL.cpp @@ -76,7 +76,7 @@ int CameraHAL::getCameraInfo(int id, struct camera_info* info) return -ENODEV; } // TODO: return device-specific static metadata - return 0; + return mCameras[id]->getInfo(info); } int CameraHAL::setCallbacks(const camera_module_callbacks_t *callbacks) @@ -90,11 +90,14 @@ int CameraHAL::open(const hw_module_t* mod, const char* name, hw_device_t** dev) { int id; char *nameEnd; - Camera *cam; ALOGV("%s: module=%p, name=%s, device=%p", __func__, mod, name, dev); + if (*name == '\0') { + ALOGE("%s: Invalid camera id name is NULL", __func__); + return -EINVAL; + } id = strtol(name, &nameEnd, 10); - if (nameEnd != NULL) { + if (*nameEnd != '\0') { ALOGE("%s: Invalid camera id name %s", __func__, name); return -EINVAL; } else if (id < 0 || id >= mNumberOfCameras) { diff --git a/modules/camera/Metadata.cpp b/modules/camera/Metadata.cpp new file mode 100644 index 00000000..d5854f9a --- /dev/null +++ b/modules/camera/Metadata.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <pthread.h> +#include <system/camera_metadata.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Metadata" +#include <cutils/log.h> + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <cutils/trace.h> +#include "ScopedTrace.h" + +#include "Metadata.h" + +namespace default_camera_hal { + +Metadata::Metadata() + : mHead(NULL), + mTail(NULL), + mEntryCount(0), + mDataCount(0), + mGenerated(NULL), + mDirty(true) +{ + // NULL (default) pthread mutex attributes + pthread_mutex_init(&mMutex, NULL); +} + +Metadata::~Metadata() +{ + Entry *current = mHead; + + while (current != NULL) { + Entry *tmp = current; + current = current->mNext; + delete tmp; + } + + if (mGenerated != NULL) + free_camera_metadata(mGenerated); + + pthread_mutex_destroy(&mMutex); +} + +Metadata::Metadata(uint8_t mode, uint8_t intent) + : mHead(NULL), + mTail(NULL), + mEntryCount(0), + mDataCount(0), + mGenerated(NULL), + mDirty(true) +{ + pthread_mutex_init(&mMutex, NULL); + + if (validate(ANDROID_CONTROL_MODE, TYPE_BYTE, 1)) { + int res = add(ANDROID_CONTROL_MODE, 1, &mode); + if (res != 0) { + ALOGE("%s: Unable to add mode to template!", __func__); + } + } else { + ALOGE("%s: Invalid mode constructing template!", __func__); + } + + if (validate(ANDROID_CONTROL_CAPTURE_INTENT, TYPE_BYTE, 1)) { + int res = add(ANDROID_CONTROL_CAPTURE_INTENT, 1, &intent); + if (res != 0) { + ALOGE("%s: Unable to add capture intent to template!", __func__); + } + } else { + ALOGE("%s: Invalid capture intent constructing template!", __func__); + } +} + +int Metadata::addUInt8(uint32_t tag, int count, uint8_t *data) +{ + if (!validate(tag, TYPE_BYTE, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addInt32(uint32_t tag, int count, int32_t *data) +{ + if (!validate(tag, TYPE_INT32, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addFloat(uint32_t tag, int count, float *data) +{ + if (!validate(tag, TYPE_FLOAT, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addInt64(uint32_t tag, int count, int64_t *data) +{ + if (!validate(tag, TYPE_INT64, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addDouble(uint32_t tag, int count, double *data) +{ + if (!validate(tag, TYPE_DOUBLE, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addRational(uint32_t tag, int count, + camera_metadata_rational_t *data) +{ + if (!validate(tag, TYPE_RATIONAL, count)) return -EINVAL; + return add(tag, count, data); +} + +bool Metadata::validate(uint32_t tag, int tag_type, int count) +{ + if (get_camera_metadata_tag_type(tag) < 0) { + ALOGE("%s: Invalid metadata entry tag: %d", __func__, tag); + return false; + } + if (tag_type < 0 || tag_type >= NUM_TYPES) { + ALOGE("%s: Invalid metadata entry tag type: %d", __func__, tag_type); + return false; + } + if (tag_type != get_camera_metadata_tag_type(tag)) { + ALOGE("%s: Tag %d called with incorrect type: %s(%d)", __func__, tag, + camera_metadata_type_names[tag_type], tag_type); + return false; + } + if (count < 1) { + ALOGE("%s: Invalid metadata entry count: %d", __func__, count); + return false; + } + return true; +} + +int Metadata::add(uint32_t tag, int count, void *tag_data) +{ + int tag_type = get_camera_metadata_tag_type(tag); + size_t type_sz = camera_metadata_type_size[tag_type]; + + // Allocate array to hold new metadata + void *data = malloc(count * type_sz); + if (data == NULL) + return -ENOMEM; + memcpy(data, tag_data, count * type_sz); + + pthread_mutex_lock(&mMutex); + mEntryCount++; + mDataCount += calculate_camera_metadata_entry_data_size(tag_type, count); + push(new Entry(tag, data, count)); + mDirty = true; + pthread_mutex_unlock(&mMutex); + return 0; +} + +camera_metadata_t* Metadata::generate() +{ + pthread_mutex_lock(&mMutex); + // Reuse if old generated metadata still valid + if (!mDirty && mGenerated != NULL) { + ALOGV("%s: Reusing generated metadata at %p", __func__, mGenerated); + goto out; + } + // Destroy old metadata + if (mGenerated != NULL) { + ALOGV("%s: Freeing generated metadata at %p", __func__, mGenerated); + free_camera_metadata(mGenerated); + mGenerated = NULL; + } + // Generate new metadata structure + ALOGV("%s: Generating new camera metadata structure, Entries:%d Data:%d", + __func__, mEntryCount, mDataCount); + mGenerated = allocate_camera_metadata(mEntryCount, mDataCount); + if (mGenerated == NULL) { + ALOGE("%s: Failed to allocate metadata (%d entries %d data)", + __func__, mEntryCount, mDataCount); + goto out; + } + // Walk list of entries adding each one to newly allocated metadata + for (Entry *current = mHead; current != NULL; current = current->mNext) { + int res = add_camera_metadata_entry(mGenerated, current->mTag, + current->mData, current->mCount); + if (res != 0) { + ALOGE("%s: Failed to add camera metadata: %d", __func__, res); + free_camera_metadata(mGenerated); + mGenerated = NULL; + goto out; + } + } + +out: + pthread_mutex_unlock(&mMutex); + return mGenerated; +} + +Metadata::Entry::Entry(uint32_t tag, void *data, int count) + : mNext(NULL), + mPrev(NULL), + mTag(tag), + mData(data), + mCount(count) +{ +} + +void Metadata::push(Entry *e) +{ + if (mHead == NULL) { + mHead = mTail = e; + } else { + mTail->insertAfter(e); + mTail = e; + } +} + +Metadata::Entry::~Entry() +{ + if (mNext != NULL) + mNext->mPrev = mPrev; + if (mPrev != NULL) + mPrev->mNext = mNext; +} + +void Metadata::Entry::insertAfter(Entry *e) +{ + if (e == NULL) + return; + if (mNext != NULL) + mNext->mPrev = e; + e->mNext = mNext; + e->mPrev = this; + mNext = e; +} + +} // namespace default_camera_hal diff --git a/modules/camera/Metadata.h b/modules/camera/Metadata.h new file mode 100644 index 00000000..22d2f223 --- /dev/null +++ b/modules/camera/Metadata.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef METADATA_H_ +#define METADATA_H_ + +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <system/camera_metadata.h> +#include <system/graphics.h> + +namespace default_camera_hal { +// Metadata is a convenience class for dealing with libcamera_metadata +class Metadata { + public: + Metadata(); + ~Metadata(); + // Constructor used for request metadata templates + Metadata(uint8_t mode, uint8_t intent); + + // Parse and add an entry + int addUInt8(uint32_t tag, int count, uint8_t *data); + int addInt32(uint32_t tag, int count, int32_t *data); + int addFloat(uint32_t tag, int count, float *data); + int addInt64(uint32_t tag, int count, int64_t *data); + int addDouble(uint32_t tag, int count, double *data); + int addRational(uint32_t tag, int count, + camera_metadata_rational_t *data); + // Generate a camera_metadata structure and fill it with internal data + camera_metadata_t *generate(); + + private: + // Validate the tag, type and count for a metadata entry + bool validate(uint32_t tag, int tag_type, int count); + // Add a verified tag with data to this Metadata structure + int add(uint32_t tag, int count, void *tag_data); + + class Entry { + public: + Entry(uint32_t tag, void *data, int count); + ~Entry(); + Entry *mNext; + Entry *mPrev; + const uint32_t mTag; + const void *mData; + const int mCount; + void insertAfter(Entry *e); + }; + // List ends + Entry *mHead; + Entry *mTail; + // Append entry to list + void push(Entry *e); + // Total of entries and entry data size + int mEntryCount; + int mDataCount; + // Save generated metadata, invalidated on update + camera_metadata_t *mGenerated; + // Flag to force metadata regeneration + bool mDirty; + // Lock protecting the Metadata object for modifications + pthread_mutex_t mMutex; +}; +} // namespace default_camera_hal + +#endif // METADATA_H_ diff --git a/modules/camera/ScopedTrace.h b/modules/camera/ScopedTrace.h new file mode 100644 index 00000000..ed005709 --- /dev/null +++ b/modules/camera/ScopedTrace.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMERA_SCOPED_TRACE_H +#define CAMERA_SCOPED_TRACE_H + +#include <stdint.h> +#include <cutils/trace.h> + +// See <cutils/trace.h> for more tracing macros. + +// CAMTRACE_NAME traces the beginning and end of the current scope. To trace +// the correct start and end times this macro should be declared first in the +// scope body. +#define CAMTRACE_NAME(name) ScopedTrace ___tracer(ATRACE_TAG, name) +// CAMTRACE_CALL is an ATRACE_NAME that uses the current function name. +#define CAMTRACE_CALL() CAMTRACE_NAME(__FUNCTION__) + +namespace default_camera_hal { + +class ScopedTrace { +public: +inline ScopedTrace(uint64_t tag, const char* name) + : mTag(tag) { + atrace_begin(mTag,name); +} + +inline ~ScopedTrace() { + atrace_end(mTag); +} + +private: + uint64_t mTag; +}; + +}; // namespace default_camera_hal + +#endif // CAMERA_SCOPED_TRACE_H diff --git a/modules/camera/Stream.cpp b/modules/camera/Stream.cpp new file mode 100644 index 00000000..aae7adb8 --- /dev/null +++ b/modules/camera/Stream.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <pthread.h> +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <system/graphics.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Stream" +#include <cutils/log.h> + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <cutils/trace.h> +#include "ScopedTrace.h" + +#include "Stream.h" + +namespace default_camera_hal { + +Stream::Stream(int id, camera3_stream_t *s) + : mReuse(false), + mId(id), + mStream(s), + mType(s->stream_type), + mWidth(s->width), + mHeight(s->height), + mFormat(s->format), + mUsage(0), + mMaxBuffers(0), + mRegistered(false), + mBuffers(0), + mNumBuffers(0) +{ + // NULL (default) pthread mutex attributes + pthread_mutex_init(&mMutex, NULL); +} + +Stream::~Stream() +{ + pthread_mutex_lock(&mMutex); + unregisterBuffers_L(); + pthread_mutex_unlock(&mMutex); +} + +void Stream::setUsage(uint32_t usage) +{ + pthread_mutex_lock(&mMutex); + if (usage != mUsage) { + mUsage = usage; + mStream->usage = usage; + unregisterBuffers_L(); + } + pthread_mutex_unlock(&mMutex); +} + +void Stream::setMaxBuffers(uint32_t max_buffers) +{ + pthread_mutex_lock(&mMutex); + if (max_buffers != mMaxBuffers) { + mMaxBuffers = max_buffers; + mStream->max_buffers = max_buffers; + unregisterBuffers_L(); + } + pthread_mutex_unlock(&mMutex); +} + +int Stream::getType() +{ + return mType; +} + +bool Stream::isInputType() +{ + return mType == CAMERA3_STREAM_INPUT || + mType == CAMERA3_STREAM_BIDIRECTIONAL; +} + +bool Stream::isOutputType() +{ + return mType == CAMERA3_STREAM_OUTPUT || + mType == CAMERA3_STREAM_BIDIRECTIONAL; +} + +bool Stream::isRegistered() +{ + return mRegistered; +} + +bool Stream::isValidReuseStream(int id, camera3_stream_t *s) +{ + if (id != mId) { + ALOGE("%s:%d: Invalid camera id for reuse. Got %d expect %d", + __func__, mId, id, mId); + return false; + } + if (s != mStream) { + ALOGE("%s:%d: Invalid stream handle for reuse. Got %p expect %p", + __func__, mId, s, mStream); + return false; + } + if (s->stream_type != mType) { + // TODO: prettyprint type string + ALOGE("%s:%d: Mismatched type in reused stream. Got %d expect %d", + __func__, mId, s->stream_type, mType); + return false; + } + if (s->format != mFormat) { + // TODO: prettyprint format string + ALOGE("%s:%d: Mismatched format in reused stream. Got %d expect %d", + __func__, mId, s->format, mFormat); + return false; + } + if (s->width != mWidth) { + ALOGE("%s:%d: Mismatched width in reused stream. Got %d expect %d", + __func__, mId, s->width, mWidth); + return false; + } + if (s->height != mHeight) { + ALOGE("%s:%d: Mismatched height in reused stream. Got %d expect %d", + __func__, mId, s->height, mHeight); + return false; + } + return true; +} + +int Stream::registerBuffers(const camera3_stream_buffer_set_t *buf_set) +{ + CAMTRACE_CALL(); + + if (buf_set->stream != mStream) { + ALOGE("%s:%d: Buffer set for invalid stream. Got %p expect %p", + __func__, mId, buf_set->stream, mStream); + return -EINVAL; + } + + pthread_mutex_lock(&mMutex); + + mNumBuffers = buf_set->num_buffers; + mBuffers = new buffer_handle_t*[mNumBuffers]; + + for (unsigned int i = 0; i < mNumBuffers; i++) { + ALOGV("%s:%d: Registering buffer %p", __func__, mId, + buf_set->buffers[i]); + mBuffers[i] = buf_set->buffers[i]; + // TODO: register buffers with hw, handle error cases + } + mRegistered = true; + + pthread_mutex_unlock(&mMutex); + + return 0; +} + +// This must only be called with mMutex held +void Stream::unregisterBuffers_L() +{ + mRegistered = false; + mNumBuffers = 0; + delete [] mBuffers; + // TODO: unregister buffers from hw +} + +} // namespace default_camera_hal diff --git a/modules/camera/Stream.h b/modules/camera/Stream.h new file mode 100644 index 00000000..34abd958 --- /dev/null +++ b/modules/camera/Stream.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STREAM_H_ +#define STREAM_H_ + +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <system/graphics.h> + +namespace default_camera_hal { +// Stream represents a single input or output stream for a camera device. +class Stream { + public: + Stream(int id, camera3_stream_t *s); + ~Stream(); + + // validate that astream's parameters match this stream's parameters + bool isValidReuseStream(int id, camera3_stream_t *s); + + // Register buffers with hardware + int registerBuffers(const camera3_stream_buffer_set_t *buf_set); + + void setUsage(uint32_t usage); + void setMaxBuffers(uint32_t max_buffers); + + int getType(); + bool isInputType(); + bool isOutputType(); + bool isRegistered(); + + // This stream is being reused. Used in stream configuration passes + bool mReuse; + + private: + // Clean up buffer state. must be called with mMutex held. + void unregisterBuffers_L(); + + // The camera device id this stream belongs to + const int mId; + // Handle to framework's stream, used as a cookie for buffers + camera3_stream_t *mStream; + // Stream type: CAMERA3_STREAM_* (see <hardware/camera3.h>) + const int mType; + // Width in pixels of the buffers in this stream + const uint32_t mWidth; + // Height in pixels of the buffers in this stream + const uint32_t mHeight; + // Gralloc format: HAL_PIXEL_FORMAT_* (see <system/graphics.h>) + const int mFormat; + // Gralloc usage mask : GRALLOC_USAGE_* (see <hardware/gralloc.h>) + uint32_t mUsage; + // Max simultaneous in-flight buffers for this stream + uint32_t mMaxBuffers; + // Buffers have been registered for this stream and are ready + bool mRegistered; + // Array of handles to buffers currently in use by the stream + buffer_handle_t **mBuffers; + // Number of buffers in mBuffers + unsigned int mNumBuffers; + // Lock protecting the Stream object for modifications + pthread_mutex_t mMutex; +}; +} // namespace default_camera_hal + +#endif // STREAM_H_ diff --git a/modules/consumerir/Android.mk b/modules/consumerir/Android.mk new file mode 100644 index 00000000..ed6aa0f1 --- /dev/null +++ b/modules/consumerir/Android.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := consumerir.default +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SRC_FILES := consumerir.c +LOCAL_SHARED_LIBRARIES := liblog libcutils +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/modules/consumerir/consumerir.c b/modules/consumerir/consumerir.c new file mode 100644 index 00000000..83eba757 --- /dev/null +++ b/modules/consumerir/consumerir.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "ConsumerIrHal" + +#include <errno.h> +#include <string.h> +#include <cutils/log.h> +#include <hardware/hardware.h> +#include <hardware/consumerir.h> + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static const consumerir_freq_range_t consumerir_freqs[] = { + {.min = 30000, .max = 30000}, + {.min = 33000, .max = 33000}, + {.min = 36000, .max = 36000}, + {.min = 38000, .max = 38000}, + {.min = 40000, .max = 40000}, + {.min = 56000, .max = 56000}, +}; + +static int consumerir_transmit(struct consumerir_device *dev, + int carrier_freq, int pattern[], int pattern_len) +{ + int total_time = 0; + long i; + + for (i = 0; i < pattern_len; i++) + total_time += pattern[i]; + + /* simulate the time spent transmitting by sleeping */ + ALOGD("transmit for %d uS at %d Hz", total_time, carrier_freq); + usleep(total_time); + + return 0; +} + +static int consumerir_get_num_carrier_freqs(struct consumerir_device *dev) +{ + return ARRAY_SIZE(consumerir_freqs); +} + +static int consumerir_get_carrier_freqs(struct consumerir_device *dev, + size_t len, consumerir_freq_range_t *ranges) +{ + size_t to_copy = ARRAY_SIZE(consumerir_freqs); + + to_copy = len < to_copy ? len : to_copy; + memcpy(ranges, consumerir_freqs, to_copy * sizeof(consumerir_freq_range_t)); + return to_copy; +} + +static int consumerir_close(hw_device_t *dev) +{ + free(dev); + return 0; +} + +/* + * Generic device handling + */ +static int consumerir_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + if (strcmp(name, CONSUMERIR_TRANSMITTER) != 0) { + return -EINVAL; + } + if (device == NULL) { + ALOGE("NULL device on open"); + return -EINVAL; + } + + consumerir_device_t *dev = malloc(sizeof(consumerir_device_t)); + memset(dev, 0, sizeof(consumerir_device_t)); + + dev->common.tag = HARDWARE_DEVICE_TAG; + dev->common.version = 0; + dev->common.module = (struct hw_module_t*) module; + dev->common.close = consumerir_close; + + dev->transmit = consumerir_transmit; + dev->get_num_carrier_freqs = consumerir_get_num_carrier_freqs; + dev->get_carrier_freqs = consumerir_get_carrier_freqs; + + *device = (hw_device_t*) dev; + return 0; +} + +static struct hw_module_methods_t consumerir_module_methods = { + .open = consumerir_open, +}; + +consumerir_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = CONSUMERIR_MODULE_API_VERSION_1_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = CONSUMERIR_HARDWARE_MODULE_ID, + .name = "Demo IR HAL", + .author = "The Android Open Source Project", + .methods = &consumerir_module_methods, + }, +}; diff --git a/modules/gralloc/gralloc.cpp b/modules/gralloc/gralloc.cpp index 03d46d59..f832f35c 100644 --- a/modules/gralloc/gralloc.cpp +++ b/modules/gralloc/gralloc.cpp @@ -217,8 +217,6 @@ static int gralloc_alloc(alloc_device_t* dev, bpp = 3; break; case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: case HAL_PIXEL_FORMAT_RAW_SENSOR: bpp = 2; break; diff --git a/tests/camera2/Android.mk b/tests/camera2/Android.mk index 8f2278a4..9efac0fa 100644 --- a/tests/camera2/Android.mk +++ b/tests/camera2/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \ CameraStreamTests.cpp \ CameraFrameTests.cpp \ CameraBurstTests.cpp \ + CameraMultiStreamTests.cpp\ ForkedTests.cpp \ TestForkerEventListener.cpp \ TestSettings.cpp \ diff --git a/tests/camera2/CameraBurstTests.cpp b/tests/camera2/CameraBurstTests.cpp index 5c4b6e7f..7301fce3 100644 --- a/tests/camera2/CameraBurstTests.cpp +++ b/tests/camera2/CameraBurstTests.cpp @@ -19,13 +19,14 @@ #define LOG_TAG "CameraBurstTest" //#define LOG_NDEBUG 0 #include <utils/Log.h> +#include <utils/Timers.h> #include <cmath> #include "CameraStreamFixture.h" #include "TestExtensions.h" -#define CAMERA_FRAME_TIMEOUT 1000000000 //nsecs (1 secs) +#define CAMERA_FRAME_TIMEOUT 1000000000LL //nsecs (1 secs) #define CAMERA_HEAP_COUNT 2 //HALBUG: 1 means registerBuffers fails #define CAMERA_BURST_DEBUGGING 0 #define CAMERA_FRAME_BURST_COUNT 10 @@ -37,12 +38,21 @@ #define CAMERA_EXPOSURE_FORMAT CAMERA_STREAM_AUTO_CPU_FORMAT #define CAMERA_EXPOSURE_STARTING 100000 // 1/10ms, up to 51.2ms with 10 steps +#define USEC 1000LL // in ns +#define MSEC 1000000LL // in ns +#define SEC 1000000000LL // in ns + #if CAMERA_BURST_DEBUGGING #define dout std::cout #else #define dout if (0) std::cout #endif +#define WARN_UNLESS(condition) (!(condition) ? (std::cerr) : (std::ostream(NULL)) << "Warning: ") +#define WARN_LE(exp, act) WARN_UNLESS((exp) <= (act)) +#define WARN_LT(exp, act) WARN_UNLESS((exp) < (act)) +#define WARN_GT(exp, act) WARN_UNLESS((exp) > (act)) + using namespace android; using namespace android::camera2; @@ -122,6 +132,23 @@ public: return acc; } + + // Parses a comma-separated string list into a Vector + template<typename T> + void ParseList(const char *src, Vector<T> &list) { + std::istringstream s(src); + while (!s.eof()) { + char c = s.peek(); + if (c == ',' || c == ' ') { + s.ignore(1, EOF); + continue; + } + T val; + s >> val; + list.push_back(val); + } + } + }; TEST_F(CameraBurstTest, ManualExposureControl) { @@ -161,7 +188,7 @@ TEST_F(CameraBurstTest, ManualExposureControl) { ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, &previewRequest)); { - Vector<uint8_t> outputStreamIds; + Vector<int32_t> outputStreamIds; outputStreamIds.push(mStreamId); ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreamIds)); @@ -251,10 +278,447 @@ TEST_F(CameraBurstTest, ManualExposureControl) { dout << "max doubling count: " << max_doubling_count << std::endl; - EXPECT_LE(CAMERA_EXPOSURE_DOUBLING_COUNT, max_doubling_count) - << "average brightness should double at least " - << CAMERA_EXPOSURE_DOUBLING_COUNT - << " times over each consecutive frame as the exposure is doubled"; + /** + * Make this check warning only, since the brightness calculation is not reliable + * and we have separate test to cover this case. Plus it is pretty subtle to make + * it right without complicating the test too much. + */ + WARN_LE(CAMERA_EXPOSURE_DOUBLING_COUNT, max_doubling_count) + << "average brightness should double at least " + << CAMERA_EXPOSURE_DOUBLING_COUNT + << " times over each consecutive frame as the exposure is doubled" + << std::endl; +} + +/** + * This test varies exposure time, frame duration, and sensitivity for a + * burst of captures. It picks values by default, but the selection can be + * overridden with the environment variables + * CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES + * CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS + * CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES + * which must all be a list of comma-separated values, and each list must be + * the same length. In addition, if the environment variable + * CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES + * is set to 1, then the YUV buffers are dumped into files named + * "camera2_test_variable_burst_frame_NNN.yuv" + * + * For example: + * $ setenv CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES 10000000,20000000 + * $ setenv CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS 40000000,40000000 + * $ setenv CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES 200,100 + * $ setenv CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES 1 + * $ /data/nativetest/camera2_test/camera2_test --gtest_filter="*VariableBurst" + */ +TEST_F(CameraBurstTest, VariableBurst) { + + TEST_EXTENSION_FORKING_INIT; + + // Bounds for checking frame duration is within range + const nsecs_t DURATION_UPPER_BOUND = 10 * MSEC; + const nsecs_t DURATION_LOWER_BOUND = 20 * MSEC; + + // Threshold for considering two captures to have equivalent exposure value, + // as a ratio of the smaller EV to the larger EV. + const float EV_MATCH_BOUND = 0.95; + // Bound for two captures with equivalent exp values to have the same + // measured brightness, in 0-255 luminance. + const float BRIGHTNESS_MATCH_BOUND = 5; + + // Environment variables to look for to override test settings + const char *expEnv = "CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES"; + const char *durationEnv = "CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS"; + const char *sensitivityEnv = "CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES"; + const char *dumpFrameEnv = "CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES"; + + // Range of valid exposure times, in nanoseconds + int64_t minExp = 0, maxExp = 0; + // List of valid sensor sensitivities + Vector<int32_t> sensitivities; + // Range of valid frame durations, in nanoseconds + int64_t minDuration = 0, maxDuration = 0; + + { + camera_metadata_ro_entry exposureTimeRange = + GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); + + EXPECT_EQ(2u, exposureTimeRange.count) << "Bad exposure time range tag." + "Using default values"; + if (exposureTimeRange.count == 2) { + minExp = exposureTimeRange.data.i64[0]; + maxExp = exposureTimeRange.data.i64[1]; + } + + EXPECT_LT(0, minExp) << "Minimum exposure time is 0"; + EXPECT_LT(0, maxExp) << "Maximum exposure time is 0"; + EXPECT_LE(minExp, maxExp) << "Minimum exposure is greater than maximum"; + + if (minExp == 0) { + minExp = 1 * MSEC; // Fallback minimum exposure time + } + + if (maxExp == 0) { + maxExp = 10 * SEC; // Fallback maximum exposure time + } + } + + camera_metadata_ro_entry hardwareLevel = + GetStaticEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL); + ASSERT_EQ(1u, hardwareLevel.count); + uint8_t level = hardwareLevel.data.u8[0]; + ASSERT_GE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED); + ASSERT_LE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL); + if (level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + std::cerr << "Skipping test " + << test_info->test_case_name() << "." + << test_info->name() + << " because HAL hardware supported level is limited " + << std::endl; + return; + } + + dout << "Stream size is " << mWidth << " x " << mHeight << std::endl; + dout << "Valid exposure range is: " << + minExp << " - " << maxExp << " ns " << std::endl; + + { + camera_metadata_ro_entry sensivityRange = + GetStaticEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE); + EXPECT_EQ(2u, sensivityRange.count) << "No sensitivity range listed." + "Falling back to default set."; + int32_t minSensitivity = 100; + int32_t maxSensitivity = 800; + if (sensivityRange.count == 2) { + ASSERT_GT(sensivityRange.data.i32[0], 0); + ASSERT_GT(sensivityRange.data.i32[1], 0); + minSensitivity = sensivityRange.data.i32[0]; + maxSensitivity = sensivityRange.data.i32[1]; + } + int32_t count = (maxSensitivity - minSensitivity + 99) / 100; + sensitivities.push_back(minSensitivity); + for (int i = 1; i < count; i++) { + sensitivities.push_back(minSensitivity + i * 100); + } + sensitivities.push_back(maxSensitivity); + } + + dout << "Available sensitivities: "; + for (size_t i = 0; i < sensitivities.size(); i++) { + dout << sensitivities[i] << " "; + } + dout << std::endl; + + { + camera_metadata_ro_entry availableProcessedSizes = + GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + + camera_metadata_ro_entry availableProcessedMinFrameDurations = + GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS); + + EXPECT_EQ(availableProcessedSizes.count, + availableProcessedMinFrameDurations.count * 2) << + "The number of minimum frame durations doesn't match the number of " + "available sizes. Using fallback values"; + + if (availableProcessedSizes.count == + availableProcessedMinFrameDurations.count * 2) { + bool gotSize = false; + for (size_t i = 0; i < availableProcessedSizes.count; i += 2) { + if (availableProcessedSizes.data.i32[i] == mWidth && + availableProcessedSizes.data.i32[i+1] == mHeight) { + gotSize = true; + minDuration = availableProcessedMinFrameDurations.data.i64[i/2]; + } + } + EXPECT_TRUE(gotSize) << "Can't find stream size in list of " + "available sizes: " << mWidth << ", " << mHeight; + } + if (minDuration == 0) { + minDuration = 1 * SEC / 30; // Fall back to 30 fps as minimum duration + } + + ASSERT_LT(0, minDuration); + + camera_metadata_ro_entry maxFrameDuration = + GetStaticEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION); + + EXPECT_EQ(1u, maxFrameDuration.count) << "No valid maximum frame duration"; + + if (maxFrameDuration.count == 1) { + maxDuration = maxFrameDuration.data.i64[0]; + } + + EXPECT_GT(maxDuration, 0) << "Max duration is 0 or not given, using fallback"; + + if (maxDuration == 0) { + maxDuration = 10 * SEC; // Fall back to 10 seconds as max duration + } + + } + dout << "Available frame duration range for configured stream size: " + << minDuration << " - " << maxDuration << " ns" << std::endl; + + // Get environment variables if set + const char *expVal = getenv(expEnv); + const char *durationVal = getenv(durationEnv); + const char *sensitivityVal = getenv(sensitivityEnv); + + bool gotExp = (expVal != NULL); + bool gotDuration = (durationVal != NULL); + bool gotSensitivity = (sensitivityVal != NULL); + + // All or none must be provided if using override envs + ASSERT_TRUE( (gotDuration && gotExp && gotSensitivity) || + (!gotDuration && !gotExp && !gotSensitivity) ) << + "Incomplete set of environment variable overrides provided"; + + Vector<int64_t> expList, durationList; + Vector<int32_t> sensitivityList; + if (gotExp) { + ParseList(expVal, expList); + ParseList(durationVal, durationList); + ParseList(sensitivityVal, sensitivityList); + + ASSERT_TRUE( + (expList.size() == durationList.size()) && + (durationList.size() == sensitivityList.size())) << + "Mismatched sizes in env lists, or parse error"; + + dout << "Using burst list from environment with " << expList.size() << + " captures" << std::endl; + } else { + // Create a default set of controls based on the available ranges + + int64_t e; + int64_t d; + int32_t s; + + // Exposure ramp + + e = minExp; + d = minDuration; + s = sensitivities[0]; + while (e < maxExp) { + expList.push_back(e); + durationList.push_back(d); + sensitivityList.push_back(s); + e = e * 2; + } + e = maxExp; + expList.push_back(e); + durationList.push_back(d); + sensitivityList.push_back(s); + + // Duration ramp + + e = 30 * MSEC; + d = minDuration; + s = sensitivities[0]; + while (d < maxDuration) { + // make sure exposure <= frame duration + expList.push_back(e > d ? d : e); + durationList.push_back(d); + sensitivityList.push_back(s); + d = d * 2; + } + + // Sensitivity ramp + + e = 30 * MSEC; + d = 30 * MSEC; + d = d > minDuration ? d : minDuration; + for (size_t i = 0; i < sensitivities.size(); i++) { + expList.push_back(e); + durationList.push_back(d); + sensitivityList.push_back(sensitivities[i]); + } + + // Constant-EV ramp, duration == exposure + + e = 30 * MSEC; // at ISO 100 + for (size_t i = 0; i < sensitivities.size(); i++) { + int64_t e_adj = e * 100 / sensitivities[i]; + expList.push_back(e_adj); + durationList.push_back(e_adj > minDuration ? e_adj : minDuration); + sensitivityList.push_back(sensitivities[i]); + } + + dout << "Default burst sequence created with " << expList.size() << + " entries" << std::endl; + } + + // Validate the list, but warn only + for (size_t i = 0; i < expList.size(); i++) { + EXPECT_GE(maxExp, expList[i]) + << "Capture " << i << " exposure too long: " << expList[i]; + EXPECT_LE(minExp, expList[i]) + << "Capture " << i << " exposure too short: " << expList[i]; + EXPECT_GE(maxDuration, durationList[i]) + << "Capture " << i << " duration too long: " << durationList[i]; + EXPECT_LE(minDuration, durationList[i]) + << "Capture " << i << " duration too short: " << durationList[i]; + bool validSensitivity = false; + for (size_t j = 0; j < sensitivities.size(); j++) { + if (sensitivityList[i] == sensitivities[j]) { + validSensitivity = true; + break; + } + } + EXPECT_TRUE(validSensitivity) + << "Capture " << i << " sensitivity not in list: " << sensitivityList[i]; + } + + // Check if debug yuv dumps are requested + + bool dumpFrames = false; + { + const char *frameDumpVal = getenv(dumpFrameEnv); + if (frameDumpVal != NULL) { + if (frameDumpVal[0] == '1') dumpFrames = true; + } + } + + dout << "Dumping YUV frames " << + (dumpFrames ? "enabled, not checking timing" : "disabled") << std::endl; + + // Create a base preview request, turning off all 3A + CameraMetadata previewRequest; + ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &previewRequest)); + { + Vector<int32_t> outputStreamIds; + outputStreamIds.push(mStreamId); + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreamIds)); + + // Disable all 3A routines + uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); + ASSERT_EQ(OK, previewRequest.update(ANDROID_CONTROL_MODE, + &cmOff, 1)); + + int requestId = 1; + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, + &requestId, 1)); + } + + // Submit capture requests + + for (size_t i = 0; i < expList.size(); ++i) { + CameraMetadata tmpRequest = previewRequest; + ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME, + &expList[i], 1)); + ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_FRAME_DURATION, + &durationList[i], 1)); + ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_SENSITIVITY, + &sensitivityList[i], 1)); + ALOGV("Submitting capture %d with exposure %lld, frame duration %lld, sensitivity %d", + i, expList[i], durationList[i], sensitivityList[i]); + dout << "Capture request " << i << + ": exposure is " << (expList[i]/1e6f) << " ms" << + ", frame duration is " << (durationList[i]/1e6f) << " ms" << + ", sensitivity is " << sensitivityList[i] << + std::endl; + ASSERT_EQ(OK, mDevice->capture(tmpRequest)); + } + + Vector<float> brightnesses; + Vector<nsecs_t> captureTimes; + brightnesses.setCapacity(expList.size()); + captureTimes.setCapacity(expList.size()); + + // Get each frame (metadata) and then the buffer. Calculate brightness. + for (size_t i = 0; i < expList.size(); ++i) { + + ALOGV("Reading request %d", i); + dout << "Waiting for capture " << i << ": " << + " exposure " << (expList[i]/1e6f) << " ms," << + " frame duration " << (durationList[i]/1e6f) << " ms," << + " sensitivity " << sensitivityList[i] << + std::endl; + + // Set wait limit based on expected frame duration, or minimum timeout + int64_t waitLimit = CAMERA_FRAME_TIMEOUT; + if (expList[i] * 2 > waitLimit) waitLimit = expList[i] * 2; + if (durationList[i] * 2 > waitLimit) waitLimit = durationList[i] * 2; + + ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit)); + ALOGV("Reading capture request-1 %d", i); + CameraMetadata frameMetadata; + ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata)); + ALOGV("Reading capture request-2 %d", i); + + ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); + ALOGV("We got the frame now"); + + captureTimes.push_back(systemTime()); + + CpuConsumer::LockedBuffer imgBuffer; + ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); + + int underexposed, overexposed; + float avgBrightness = 0; + long long brightness = TotalBrightness(imgBuffer, &underexposed, + &overexposed); + int numValidPixels = mWidth * mHeight - (underexposed + overexposed); + if (numValidPixels != 0) { + avgBrightness = brightness * 1.0f / numValidPixels; + } else if (underexposed < overexposed) { + avgBrightness = 255; + } + + ALOGV("Total brightness for frame %d was %lld (underexposed %d, " + "overexposed %d), avg %f", i, brightness, underexposed, + overexposed, avgBrightness); + dout << "Average brightness (frame " << i << ") was " << avgBrightness + << " (underexposed " << underexposed << ", overexposed " + << overexposed << ")" << std::endl; + brightnesses.push_back(avgBrightness); + + if (i != 0) { + float prevEv = static_cast<float>(expList[i - 1]) * sensitivityList[i - 1]; + float currentEv = static_cast<float>(expList[i]) * sensitivityList[i]; + float evRatio = (prevEv > currentEv) ? (currentEv / prevEv) : + (prevEv / currentEv); + if ( evRatio > EV_MATCH_BOUND ) { + WARN_LT(fabs(brightnesses[i] - brightnesses[i - 1]), + BRIGHTNESS_MATCH_BOUND) << + "Capture brightness different from previous, even though " + "they have the same EV value. Ev now: " << currentEv << + ", previous: " << prevEv << ". Brightness now: " << + brightnesses[i] << ", previous: " << brightnesses[i-1] << + std::endl; + } + // Only check timing if not saving to disk, since that slows things + // down substantially + if (!dumpFrames) { + nsecs_t timeDelta = captureTimes[i] - captureTimes[i-1]; + nsecs_t expectedDelta = expList[i] > durationList[i] ? + expList[i] : durationList[i]; + WARN_LT(timeDelta, expectedDelta + DURATION_UPPER_BOUND) << + "Capture took " << timeDelta << " ns to receive, but expected" + " frame duration was " << expectedDelta << " ns." << + std::endl; + WARN_GT(timeDelta, expectedDelta - DURATION_LOWER_BOUND) << + "Capture took " << timeDelta << " ns to receive, but expected" + " frame duration was " << expectedDelta << " ns." << + std::endl; + dout << "Time delta from previous frame: " << timeDelta / 1e6 << + " ms. Expected " << expectedDelta / 1e6 << " ms" << std::endl; + } + } + + if (dumpFrames) { + String8 dumpName = + String8::format("/data/local/tmp/camera2_test_variable_burst_frame_%03d.yuv", i); + dout << " Writing YUV dump to " << dumpName << std::endl; + DumpYuvToFile(dumpName, imgBuffer); + } + + ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); + } + } } diff --git a/tests/camera2/CameraFrameTests.cpp b/tests/camera2/CameraFrameTests.cpp index 84450984..e78a862c 100644 --- a/tests/camera2/CameraFrameTests.cpp +++ b/tests/camera2/CameraFrameTests.cpp @@ -23,9 +23,8 @@ #include "hardware/hardware.h" #include "hardware/camera2.h" -#include "CameraDeviceBase.h" -#include "utils/StrongPointer.h" - +#include <common/CameraDeviceBase.h> +#include <utils/StrongPointer.h> #include <gui/CpuConsumer.h> #include <gui/Surface.h> @@ -91,7 +90,7 @@ TEST_P(CameraFrameTest, GetFrame) { ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, &previewRequest)); { - Vector<uint8_t> outputStreamIds; + Vector<int32_t> outputStreamIds; outputStreamIds.push(mStreamId); ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreamIds)); diff --git a/tests/camera2/CameraMetadataTests.cpp b/tests/camera2/CameraMetadataTests.cpp index 66c4847d..eddc5930 100644 --- a/tests/camera2/CameraMetadataTests.cpp +++ b/tests/camera2/CameraMetadataTests.cpp @@ -25,7 +25,7 @@ #include "hardware/hardware.h" #include "hardware/camera2.h" -#include "CameraDeviceBase.h" +#include "common/CameraDeviceBase.h" #include "utils/StrongPointer.h" #include <gui/CpuConsumer.h> @@ -136,10 +136,6 @@ TEST_F(CameraMetadataTest, RequiredFormats) { EXPECT_TRUE( HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS, - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)); - - EXPECT_TRUE( - HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS, HAL_PIXEL_FORMAT_BLOB)); // JPEG if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_0) { @@ -169,9 +165,11 @@ TEST_F(CameraMetadataTest, SaneResolutions) { // Iff there are listed raw resolutions, the format should be available int rawResolutionsCount = GetEntryCountFromStaticTag(ANDROID_SCALER_AVAILABLE_RAW_SIZES); - EXPECT_EQ(rawResolutionsCount > 0, - HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS, - HAL_PIXEL_FORMAT_RAW_SENSOR)); + if (rawResolutionsCount > 0) { + EXPECT_TRUE( + HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS, + HAL_PIXEL_FORMAT_RAW_SENSOR)); + } // Required processed sizes. int processedSizeCount = diff --git a/tests/camera2/CameraModuleFixture.h b/tests/camera2/CameraModuleFixture.h index ef4a9a52..acf41e10 100644 --- a/tests/camera2/CameraModuleFixture.h +++ b/tests/camera2/CameraModuleFixture.h @@ -22,8 +22,9 @@ #include "hardware/hardware.h" #include "hardware/camera2.h" -#include "Camera2Device.h" -#include "Camera3Device.h" +#include <device2/Camera2Device.h> +#include <device3/Camera3Device.h> + #include "camera2_utils.h" #include "TestExtensions.h" diff --git a/tests/camera2/CameraModuleTests.cpp b/tests/camera2/CameraModuleTests.cpp index 44d3e3bf..ae4267b8 100644 --- a/tests/camera2/CameraModuleTests.cpp +++ b/tests/camera2/CameraModuleTests.cpp @@ -19,12 +19,12 @@ #define LOG_TAG "CameraModuleTest" #define LOG_NDEBUG 0 #include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <common/CameraDeviceBase.h> #include "hardware/hardware.h" #include "hardware/camera2.h" -#include "CameraDeviceBase.h" -#include "utils/StrongPointer.h" #include "CameraModuleFixture.h" namespace android { @@ -78,17 +78,19 @@ TEST_F(CameraModuleTest, LoadModuleBadIndices) { TEST_EXTENSION_FORKING_INIT; int idx[] = { -1, mNumberOfCameras, mNumberOfCameras + 1 }; + hw_device_t *device = NULL; for (unsigned i = 0; i < sizeof(idx)/sizeof(idx[0]); ++i) { - // Since the initialization should fail at device open(), it doesn't - // matter which version of CameraNDevice is used here - mDevice = new Camera2Device(idx[i]); - status_t deviceInitializeCode = initializeDevice(idx[i]); - EXPECT_NE(OK, deviceInitializeCode); - EXPECT_EQ(-ENODEV, deviceInitializeCode) - << "Incorrect error code when trying to initialize invalid index " - << idx[i]; - mDevice.clear(); + String8 deviceName = String8::format("%d", idx[i]); + status_t res = + mModule->common.methods->open( + &mModule->common, + deviceName, + &device); + EXPECT_NE(OK, res); + EXPECT_EQ(-ENODEV, res) + << "Incorrect error code when trying to open camera with invalid id " + << deviceName; } } diff --git a/tests/camera2/CameraMultiStreamTests.cpp b/tests/camera2/CameraMultiStreamTests.cpp new file mode 100644 index 00000000..de1cfd6f --- /dev/null +++ b/tests/camera2/CameraMultiStreamTests.cpp @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CameraMultiStreamTest" +//#define LOG_NDEBUG 0 +#include "CameraStreamFixture.h" +#include "TestExtensions.h" + +#include <gtest/gtest.h> +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <common/CameraDeviceBase.h> +#include <hardware/hardware.h> +#include <hardware/camera2.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/Surface.h> + +#define DEFAULT_FRAME_DURATION 33000000LL // 33ms +#define CAMERA_HEAP_COUNT 1 +#define CAMERA_EXPOSURE_FORMAT CAMERA_STREAM_AUTO_CPU_FORMAT +#define CAMERA_DISPLAY_FORMAT HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED +#define CAMERA_MULTI_STREAM_DEBUGGING 0 +#define CAMERA_FRAME_TIMEOUT 1000000000LL // nsecs (1 secs) +#define PREVIEW_RENDERING_TIME_INTERVAL 200000 // in unit of us, 200ms +#define TOLERANCE_MARGIN 0.01 // 1% tolerance margin for exposure sanity check. +/* constants for display */ +#define DISPLAY_BUFFER_HEIGHT 1024 +#define DISPLAY_BUFFER_WIDTH 1024 +#define DISPLAY_BUFFER_FORMAT PIXEL_FORMAT_RGB_888 + +// This test intends to test large preview size but less than 1080p. +#define PREVIEW_WIDTH_CAP 1920 +#define PREVIEW_HEIGHT_CAP 1080 +// This test intends to test small metering burst size that is less than 640x480 +#define METERING_WIDTH_CAP 640 +#define METERING_HEIGHT_CAP 480 + +#define EXP_WAIT_MULTIPLIER 2 + +namespace android { +namespace camera2 { +namespace tests { + +static const CameraStreamParams DEFAULT_STREAM_PARAMETERS = { + /*mFormat*/ CAMERA_EXPOSURE_FORMAT, + /*mHeapCount*/ CAMERA_HEAP_COUNT +}; + +static const CameraStreamParams DISPLAY_STREAM_PARAMETERS = { + /*mFormat*/ CAMERA_DISPLAY_FORMAT, + /*mHeapCount*/ CAMERA_HEAP_COUNT +}; + +class CameraMultiStreamTest + : public ::testing::Test, + public CameraStreamFixture { + +public: + CameraMultiStreamTest() : CameraStreamFixture(DEFAULT_STREAM_PARAMETERS) { + TEST_EXTENSION_FORKING_CONSTRUCTOR; + + if (HasFatalFailure()) { + return; + } + /** + * Don't create default stream, each test is in charge of creating + * its own streams. + */ + } + + ~CameraMultiStreamTest() { + TEST_EXTENSION_FORKING_DESTRUCTOR; + } + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + + void CreateOnScreenSurface(sp<ANativeWindow>& surface) { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface( + String8("CameraMultiStreamTest StreamingImage Surface"), + DISPLAY_BUFFER_HEIGHT, DISPLAY_BUFFER_WIDTH, + DISPLAY_BUFFER_FORMAT, 0); + + ASSERT_NE((void*)NULL, mSurfaceControl.get()); + ASSERT_TRUE(mSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + surface = mSurfaceControl->getSurface(); + + ASSERT_NE((void*)NULL, surface.get()); + } + + struct Size { + int32_t width; + int32_t height; + }; + + // Select minimal size by number of pixels. + void GetMinSize(const int32_t* data, size_t count, + Size* min, int32_t* idx) { + ASSERT_NE((int32_t*)NULL, data); + int32_t minIdx = 0; + int32_t minSize = INT_MAX, tempSize; + for (size_t i = 0; i < count; i+=2) { + tempSize = data[i] * data[i+1]; + if (minSize > tempSize) { + minSize = tempSize; + minIdx = i; + } + } + min->width = data[minIdx]; + min->height = data[minIdx + 1]; + *idx = minIdx; + } + + // Select maximal size by number of pixels. + void GetMaxSize(const int32_t* data, size_t count, + Size* max, int32_t* idx) { + ASSERT_NE((int32_t*)NULL, data); + int32_t maxIdx = 0; + int32_t maxSize = INT_MIN, tempSize; + for (size_t i = 0; i < count; i+=2) { + tempSize = data[i] * data[i+1]; + if (maxSize < tempSize) { + maxSize = tempSize; + maxIdx = i; + } + } + max->width = data[maxIdx]; + max->height = data[maxIdx + 1]; + *idx = maxIdx; + } + + // Cap size by number of pixels. + Size CapSize(Size cap, Size input) { + if (input.width * input.height > cap.width * cap.height) { + return cap; + } + return input; + } + + struct CameraStream : public RefBase { + + public: + /** + * Only initialize the variables here, do the ASSERT check in + * SetUp function. To make this stream useful, the SetUp must + * be called before using it. + */ + CameraStream( + int width, + int height, + const sp<CameraDeviceBase>& device, + CameraStreamParams param, sp<ANativeWindow> surface, + bool useCpuConsumer) + : mDevice(device), + mWidth(width), + mHeight(height) { + mFormat = param.mFormat; + if (useCpuConsumer) { + sp<BufferQueue> bq = new BufferQueue(); + mCpuConsumer = new CpuConsumer(bq, param.mHeapCount); + mCpuConsumer->setName(String8( + "CameraMultiStreamTest::mCpuConsumer")); + mNativeWindow = new Surface(bq); + } else { + // Render the stream to screen. + mCpuConsumer = NULL; + mNativeWindow = surface; + } + + mFrameListener = new FrameListener(); + if (mCpuConsumer != 0) { + mCpuConsumer->setFrameAvailableListener(mFrameListener); + } + } + + /** + * Finally create camera stream, and do the ASSERT check, since we + * can not do it in ctor. + */ + void SetUp() { + ASSERT_EQ(OK, + mDevice->createStream(mNativeWindow, + mWidth, mHeight, mFormat, /*size (for jpegs)*/0, + &mStreamId)); + + ASSERT_NE(-1, mStreamId); + } + + int GetStreamId() { return mStreamId; } + sp<CpuConsumer> GetConsumer() { return mCpuConsumer; } + sp<FrameListener> GetFrameListener() { return mFrameListener; } + + protected: + ~CameraStream() { + if (mDevice.get()) { + mDevice->waitUntilDrained(); + mDevice->deleteStream(mStreamId); + } + // Clear producer before consumer. + mNativeWindow.clear(); + mCpuConsumer.clear(); + } + + private: + sp<FrameListener> mFrameListener; + sp<CpuConsumer> mCpuConsumer; + sp<ANativeWindow> mNativeWindow; + sp<CameraDeviceBase> mDevice; + int mStreamId; + int mWidth; + int mHeight; + int mFormat; + }; + + int64_t GetExposureValue(const CameraMetadata& metaData) { + camera_metadata_ro_entry_t entry = + metaData.find(ANDROID_SENSOR_EXPOSURE_TIME); + EXPECT_EQ(1u, entry.count); + if (entry.count == 1) { + return entry.data.i64[0]; + } + return -1; + } + + int32_t GetSensitivity(const CameraMetadata& metaData) { + camera_metadata_ro_entry_t entry = + metaData.find(ANDROID_SENSOR_SENSITIVITY); + EXPECT_EQ(1u, entry.count); + if (entry.count == 1) { + return entry.data.i32[0]; + } + return -1; + } + + int64_t GetFrameDuration(const CameraMetadata& metaData) { + camera_metadata_ro_entry_t entry = + metaData.find(ANDROID_SENSOR_FRAME_DURATION); + EXPECT_EQ(1u, entry.count); + if (entry.count == 1) { + return entry.data.i64[0]; + } + return -1; + } + + void CreateRequests(CameraMetadata& previewRequest, + CameraMetadata& meteringRequest, + CameraMetadata& captureRequest, + int previewStreamId, + int meteringStreamId, + int captureStreamId) { + int32_t requestId = 0; + Vector<int32_t> previewStreamIds; + previewStreamIds.push(previewStreamId); + ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &previewRequest)); + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, + previewStreamIds)); + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, + &requestId, 1)); + + // Create metering request, manual settings + // Manual control: Disable 3A, noise reduction, edge sharping + uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); + uint8_t nrOff = static_cast<uint8_t>(ANDROID_NOISE_REDUCTION_MODE_OFF); + uint8_t sharpOff = static_cast<uint8_t>(ANDROID_EDGE_MODE_OFF); + Vector<int32_t> meteringStreamIds; + meteringStreamIds.push(meteringStreamId); + ASSERT_EQ(OK, mDevice->createDefaultRequest( + CAMERA2_TEMPLATE_PREVIEW, + &meteringRequest)); + ASSERT_EQ(OK, meteringRequest.update( + ANDROID_REQUEST_OUTPUT_STREAMS, + meteringStreamIds)); + ASSERT_EQ(OK, meteringRequest.update( + ANDROID_CONTROL_MODE, + &cmOff, 1)); + ASSERT_EQ(OK, meteringRequest.update( + ANDROID_NOISE_REDUCTION_MODE, + &nrOff, 1)); + ASSERT_EQ(OK, meteringRequest.update( + ANDROID_EDGE_MODE, + &sharpOff, 1)); + + // Create capture request, manual settings + Vector<int32_t> captureStreamIds; + captureStreamIds.push(captureStreamId); + ASSERT_EQ(OK, mDevice->createDefaultRequest( + CAMERA2_TEMPLATE_PREVIEW, + &captureRequest)); + ASSERT_EQ(OK, captureRequest.update( + ANDROID_REQUEST_OUTPUT_STREAMS, + captureStreamIds)); + ASSERT_EQ(OK, captureRequest.update( + ANDROID_CONTROL_MODE, + &cmOff, 1)); + ASSERT_EQ(OK, captureRequest.update( + ANDROID_NOISE_REDUCTION_MODE, + &nrOff, 1)); + ASSERT_EQ(OK, captureRequest.update( + ANDROID_EDGE_MODE, + &sharpOff, 1)); + } + + sp<CameraStream> CreateStream( + int width, + int height, + const sp<CameraDeviceBase>& device, + CameraStreamParams param = DEFAULT_STREAM_PARAMETERS, + sp<ANativeWindow> surface = NULL, + bool useCpuConsumer = true) { + param.mFormat = MapAutoFormat(param.mFormat); + return new CameraStream(width, height, device, + param, surface, useCpuConsumer); + } + + void CaptureBurst(CameraMetadata& request, size_t requestCount, + const Vector<int64_t>& exposures, + const Vector<int32_t>& sensitivities, + const sp<CameraStream>& stream, + int64_t minFrameDuration, + int32_t* requestIdStart) { + ASSERT_EQ(OK, request.update(ANDROID_SENSOR_FRAME_DURATION, + &minFrameDuration, 1)); + // Submit a series of requests with the specified exposure/gain values. + int32_t targetRequestId = *requestIdStart; + for (size_t i = 0; i < requestCount; i++) { + ASSERT_EQ(OK, request.update(ANDROID_REQUEST_ID, requestIdStart, 1)); + ASSERT_EQ(OK, request.update(ANDROID_SENSOR_EXPOSURE_TIME, &exposures[i], 1)); + ASSERT_EQ(OK, request.update(ANDROID_SENSOR_SENSITIVITY, &sensitivities[i], 1)); + ASSERT_EQ(OK, mDevice->capture(request)); + ALOGV("Submitting request with: id %d with exposure %lld, sensitivity %d", + *requestIdStart, exposures[i], sensitivities[i]); + if (CAMERA_MULTI_STREAM_DEBUGGING) { + request.dump(STDOUT_FILENO); + } + (*requestIdStart)++; + } + // Get capture burst results. + Vector<nsecs_t> captureBurstTimes; + sp<CpuConsumer> consumer = stream->GetConsumer(); + sp<FrameListener> listener = stream->GetFrameListener(); + + // Set wait limit based on expected frame duration. + int64_t waitLimit = CAMERA_FRAME_TIMEOUT; + for (size_t i = 0; i < requestCount; i++) { + ALOGV("Reading request result %d", i); + + /** + * Raise the timeout to be at least twice as long as the exposure + * time. to avoid a false positive when the timeout is too short. + */ + if ((exposures[i] * EXP_WAIT_MULTIPLIER) > waitLimit) { + waitLimit = exposures[i] * EXP_WAIT_MULTIPLIER; + } + + CameraMetadata frameMetadata; + int32_t resultRequestId; + do { + ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit)); + ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata)); + + camera_metadata_entry_t resultEntry = frameMetadata.find(ANDROID_REQUEST_ID); + ASSERT_EQ(1u, resultEntry.count); + resultRequestId = resultEntry.data.i32[0]; + if (CAMERA_MULTI_STREAM_DEBUGGING) { + std::cout << "capture result req id: " << resultRequestId << std::endl; + } + } while (resultRequestId != targetRequestId); + targetRequestId++; + ALOGV("Got capture burst result for request %d", i); + + // Validate capture result + if (CAMERA_MULTI_STREAM_DEBUGGING) { + frameMetadata.dump(STDOUT_FILENO); + } + + // TODO: Need revisit it to figure out an accurate margin. + int64_t resultExposure = GetExposureValue(frameMetadata); + int32_t resultSensitivity = GetSensitivity(frameMetadata); + EXPECT_LE(sensitivities[i] * (1.0 - TOLERANCE_MARGIN), resultSensitivity); + EXPECT_GE(sensitivities[i] * (1.0 + TOLERANCE_MARGIN), resultSensitivity); + EXPECT_LE(exposures[i] * (1.0 - TOLERANCE_MARGIN), resultExposure); + EXPECT_GE(exposures[i] * (1.0 + TOLERANCE_MARGIN), resultExposure); + + ASSERT_EQ(OK, listener->waitForFrame(waitLimit)); + captureBurstTimes.push_back(systemTime()); + CpuConsumer::LockedBuffer imgBuffer; + ASSERT_EQ(OK, consumer->lockNextBuffer(&imgBuffer)); + ALOGV("Got capture buffer for request %d", i); + + /** + * TODO: Validate capture buffer. Current brightness calculation + * is too slow, it also doesn't account for saturation effects, + * which is quite common since we are going over a significant + * range of EVs. we need figure out some reliable way to validate + * buffer data. + */ + + ASSERT_EQ(OK, consumer->unlockBuffer(imgBuffer)); + if (i > 0) { + nsecs_t timeDelta = + captureBurstTimes[i] - captureBurstTimes[i-1]; + EXPECT_GE(timeDelta, exposures[i]); + } + } + } + + /** + * Intentionally shadow default CreateStream function from base class, + * because we don't want any test in this class to use the default + * stream creation function. + */ + void CreateStream() { + } +}; + +/** + * This test adds multiple stream use case test, basically, test 3 + * streams: + * + * 1. Preview stream, with large size that is no bigger than 1080p + * we render this stream to display and vary the exposure time for + * for certain amount of time for visualization purpose. + * + * 2. Metering stream, with small size that is no bigger than VGA size. + * a burst is issued for different exposure times and analog gains + * (or analog gain implemented sensitivities) then check if the capture + * result metadata matches the request. + * + * 3. Capture stream, this is basically similar as meterting stream, but + * has large size, which is the largest supported JPEG capture size. + * + * This multiple stream test is to test if HAL supports: + * + * 1. Multiple streams like above, HAL should support at least 3 streams + * concurrently: one preview stream, 2 other YUV stream. + * + * 2. Manual control(gain/exposure) of mutiple burst capture. + */ +TEST_F(CameraMultiStreamTest, MultiBurst) { + + TEST_EXTENSION_FORKING_INIT; + + camera_metadata_ro_entry availableProcessedSizes = + GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + ASSERT_EQ(0u, availableProcessedSizes.count % 2); + ASSERT_GE(availableProcessedSizes.count, 2u); + camera_metadata_ro_entry availableProcessedMinFrameDurations = + GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS); + EXPECT_EQ(availableProcessedSizes.count, + availableProcessedMinFrameDurations.count * 2); + + camera_metadata_ro_entry availableJpegSizes = + GetStaticEntry(ANDROID_SCALER_AVAILABLE_JPEG_SIZES); + ASSERT_EQ(0u, availableJpegSizes.count % 2); + ASSERT_GE(availableJpegSizes.count, 2u); + + camera_metadata_ro_entry hardwareLevel = + GetStaticEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL); + ASSERT_EQ(1u, hardwareLevel.count); + uint8_t level = hardwareLevel.data.u8[0]; + ASSERT_GE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED); + ASSERT_LE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL); + if (level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + std::cerr << "Skipping test " + << test_info->test_case_name() << "." + << test_info->name() + << " because HAL hardware supported level is limited " + << std::endl; + return; + } + + // Find the right sizes for preview, metering, and capture streams + // assumes at least 2 entries in availableProcessedSizes. + int64_t minFrameDuration = DEFAULT_FRAME_DURATION; + Size processedMinSize, processedMaxSize, jpegMaxSize; + const int32_t* data = availableProcessedSizes.data.i32; + size_t count = availableProcessedSizes.count; + + int32_t minIdx, maxIdx; + GetMinSize(data, count, &processedMinSize, &minIdx); + GetMaxSize(data, count, &processedMaxSize, &maxIdx); + ALOGV("Found processed max size: %dx%d, min size = %dx%d", + processedMaxSize.width, processedMaxSize.height, + processedMinSize.width, processedMinSize.height); + + if (availableProcessedSizes.count == + availableProcessedMinFrameDurations.count * 2) { + minFrameDuration = + availableProcessedMinFrameDurations.data.i64[maxIdx / 2]; + } + + EXPECT_GT(minFrameDuration, 0); + + if (minFrameDuration <= 0) { + minFrameDuration = DEFAULT_FRAME_DURATION; + } + + ALOGV("targeted minimal frame duration is: %lldns", minFrameDuration); + + data = &(availableJpegSizes.data.i32[0]); + count = availableJpegSizes.count; + GetMaxSize(data, count, &jpegMaxSize, &maxIdx); + ALOGV("Found Jpeg size max idx = %d", maxIdx); + + // Max Jpeg size should be available in processed sizes. Use it for + // YUV capture anyway. + EXPECT_EQ(processedMaxSize.width, jpegMaxSize.width); + EXPECT_EQ(processedMaxSize.height, jpegMaxSize.height); + + // Cap preview size. + Size previewLimit = { PREVIEW_WIDTH_CAP, PREVIEW_HEIGHT_CAP }; + // FIXME: need make sure the previewLimit is supported by HAL. + Size previewSize = CapSize(previewLimit, processedMaxSize); + // Cap Metering size. + Size meteringLimit = { METERING_WIDTH_CAP, METERING_HEIGHT_CAP }; + // Cap metering size to VGA (VGA is mandatory by CDD) + Size meteringSize = CapSize(meteringLimit, processedMinSize); + // Capture stream should be the max size of jpeg sizes. + ALOGV("preview size: %dx%d, metering size: %dx%d, capture size: %dx%d", + previewSize.width, previewSize.height, + meteringSize.width, meteringSize.height, + jpegMaxSize.width, jpegMaxSize.height); + + // Create streams + // Preview stream: small resolution, render on the screen. + sp<CameraStream> previewStream; + { + sp<ANativeWindow> surface; + ASSERT_NO_FATAL_FAILURE(CreateOnScreenSurface(/*out*/surface)); + previewStream = CreateStream( + previewSize.width, + previewSize.height, + mDevice, + DISPLAY_STREAM_PARAMETERS, + surface, + false); + ASSERT_NE((void*)NULL, previewStream.get()); + ASSERT_NO_FATAL_FAILURE(previewStream->SetUp()); + } + // Metering burst stream: small resolution yuv stream + sp<CameraStream> meteringStream = + CreateStream( + meteringSize.width, + meteringSize.height, + mDevice); + ASSERT_NE((void*)NULL, meteringStream.get()); + ASSERT_NO_FATAL_FAILURE(meteringStream->SetUp()); + // Capture burst stream: full resolution yuv stream + sp<CameraStream> captureStream = + CreateStream( + jpegMaxSize.width, + jpegMaxSize.height, + mDevice); + ASSERT_NE((void*)NULL, captureStream.get()); + ASSERT_NO_FATAL_FAILURE(captureStream->SetUp()); + + // Create Preview request. + CameraMetadata previewRequest, meteringRequest, captureRequest; + ASSERT_NO_FATAL_FAILURE(CreateRequests(previewRequest, meteringRequest, + captureRequest, previewStream->GetStreamId(), + meteringStream->GetStreamId(), captureStream->GetStreamId())); + + // Start preview + if (CAMERA_MULTI_STREAM_DEBUGGING) { + previewRequest.dump(STDOUT_FILENO); + } + + // Generate exposure and sensitivity lists + camera_metadata_ro_entry exposureTimeRange = + GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); + ASSERT_EQ(exposureTimeRange.count, 2u); + int64_t minExp = exposureTimeRange.data.i64[0]; + int64_t maxExp = exposureTimeRange.data.i64[1]; + ASSERT_GT(maxExp, minExp); + + camera_metadata_ro_entry sensivityRange = + GetStaticEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE); + ASSERT_EQ(2u, sensivityRange.count); + int32_t minSensitivity = sensivityRange.data.i32[0]; + int32_t maxSensitivity = sensivityRange.data.i32[1]; + camera_metadata_ro_entry maxAnalogSenEntry = + GetStaticEntry(ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY); + EXPECT_EQ(1u, maxAnalogSenEntry.count); + int32_t maxAnalogSensitivity = maxAnalogSenEntry.data.i32[0]; + EXPECT_LE(maxAnalogSensitivity, maxSensitivity); + // Only test the sensitivity implemented by analog gain. + if (maxAnalogSensitivity > maxSensitivity) { + // Fallback to maxSensitity + maxAnalogSensitivity = maxSensitivity; + } + + // sensitivity list, only include the sensitivities that are implemented + // purely by analog gain if possible. + Vector<int32_t> sensitivities; + Vector<int64_t> exposures; + count = (maxAnalogSensitivity - minSensitivity + 99) / 100; + sensitivities.push_back(minSensitivity); + for (size_t i = 1; i < count; i++) { + sensitivities.push_back(minSensitivity + i * 100); + } + sensitivities.push_back(maxAnalogSensitivity); + ALOGV("Sensitivity Range: min=%d, max=%d", minSensitivity, + maxAnalogSensitivity); + int64_t exp = minExp; + while (exp < maxExp) { + exposures.push_back(exp); + exp *= 2; + } + // Sweep the exposure value for preview, just for visual inspection purpose. + uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); + for (size_t i = 0; i < exposures.size(); i++) { + ASSERT_EQ(OK, previewRequest.update( + ANDROID_CONTROL_MODE, + &cmOff, 1)); + ASSERT_EQ(OK, previewRequest.update( + ANDROID_SENSOR_EXPOSURE_TIME, + &exposures[i], 1)); + ALOGV("Submitting preview request %d with exposure %lld", + i, exposures[i]); + + ASSERT_EQ(OK, mDevice->setStreamingRequest(previewRequest)); + + // Let preview run 200ms on screen for each exposure time. + usleep(PREVIEW_RENDERING_TIME_INTERVAL); + } + + size_t requestCount = sensitivities.size(); + if (requestCount > exposures.size()) { + requestCount = exposures.size(); + } + + // To maintain the request id uniqueness (preview request id is 0), make burst capture start + // request id 1 here. + int32_t requestIdStart = 1; + /** + * Submit metering request, set default frame duration to minimal possible + * value, we want the capture to run as fast as possible. HAL should adjust + * the frame duration to minimal necessary value to support the requested + * exposure value if exposure is larger than frame duration. + */ + CaptureBurst(meteringRequest, requestCount, exposures, sensitivities, + meteringStream, minFrameDuration, &requestIdStart); + + /** + * Submit capture request, set default frame duration to minimal possible + * value, we want the capture to run as fast as possible. HAL should adjust + * the frame duration to minimal necessary value to support the requested + * exposure value if exposure is larger than frame duration. + */ + CaptureBurst(captureRequest, requestCount, exposures, sensitivities, + captureStream, minFrameDuration, &requestIdStart); + + ASSERT_EQ(OK, mDevice->clearStreamingRequest()); +} + +} +} +} diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h index a4dc4a81..a1f3aae8 100644 --- a/tests/camera2/CameraStreamFixture.h +++ b/tests/camera2/CameraStreamFixture.h @@ -19,6 +19,7 @@ #include <gtest/gtest.h> #include <iostream> +#include <fstream> #include <gui/CpuConsumer.h> #include <gui/Surface.h> @@ -29,6 +30,8 @@ #include "CameraModuleFixture.h" #include "TestExtensions.h" +#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) + namespace android { namespace camera2 { namespace tests { @@ -90,7 +93,6 @@ private: CameraModuleFixture::SetUp(); - CameraStreamParams p = mParam; sp<CameraDeviceBase> device = mDevice; /* use an arbitrary w,h */ @@ -159,11 +161,11 @@ protected: sp<CameraDeviceBase> device = mDevice; CameraStreamParams p = mParam; - mCpuConsumer = new CpuConsumer(p.mHeapCount); + sp<BufferQueue> bq = new BufferQueue(); + mCpuConsumer = new CpuConsumer(bq, p.mHeapCount); mCpuConsumer->setName(String8("CameraStreamTest::mCpuConsumer")); - mNativeWindow = new Surface( - mCpuConsumer->getProducerInterface()); + mNativeWindow = new Surface(bq); int format = MapAutoFormat(p.mFormat); @@ -194,6 +196,80 @@ protected: return format; } + void DumpYuvToFile(const String8 &fileName, const CpuConsumer::LockedBuffer &img) { + uint8_t *dataCb, *dataCr; + uint32_t stride; + uint32_t chromaStride; + uint32_t chromaStep; + + switch (img.format) { + case HAL_PIXEL_FORMAT_YCbCr_420_888: + stride = img.stride; + chromaStride = img.chromaStride; + chromaStep = img.chromaStep; + dataCb = img.dataCb; + dataCr = img.dataCr; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + stride = img.width; + chromaStride = img.width; + chromaStep = 2; + dataCr = img.data + img.width * img.height; + dataCb = dataCr + 1; + break; + case HAL_PIXEL_FORMAT_YV12: + stride = img.stride; + chromaStride = ALIGN(img.width / 2, 16); + chromaStep = 1; + dataCr = img.data + img.stride * img.height; + dataCb = dataCr + chromaStride * img.height/2; + break; + default: + ALOGE("Unknown format %d, not dumping", img.format); + return; + } + + // Write Y + FILE *yuvFile = fopen(fileName.string(), "w"); + + size_t bytes; + + for (size_t y = 0; y < img.height; ++y) { + bytes = fwrite( + reinterpret_cast<const char*>(img.data + stride * y), + 1, img.width, yuvFile); + if (bytes != img.width) { + ALOGE("Unable to write to file %s", fileName.string()); + fclose(yuvFile); + return; + } + } + + // Write Cb/Cr + uint8_t *src = dataCb; + for (int c = 0; c < 2; ++c) { + for (size_t y = 0; y < img.height / 2; ++y) { + uint8_t *px = src + y * chromaStride; + if (chromaStep != 1) { + for (size_t x = 0; x < img.width / 2; ++x) { + fputc(*px, yuvFile); + px += chromaStep; + } + } else { + bytes = fwrite(reinterpret_cast<const char*>(px), + 1, img.width / 2, yuvFile); + if (bytes != img.width / 2) { + ALOGE("Unable to write to file %s", fileName.string()); + fclose(yuvFile); + return; + } + } + } + src = dataCr; + } + fclose(yuvFile); + } + int mWidth; int mHeight; diff --git a/tests/camera2/CameraStreamTests.cpp b/tests/camera2/CameraStreamTests.cpp index 164e0e54..69ee274d 100644 --- a/tests/camera2/CameraStreamTests.cpp +++ b/tests/camera2/CameraStreamTests.cpp @@ -25,12 +25,12 @@ #include "hardware/hardware.h" #include "hardware/camera2.h" -#include "Camera2Device.h" -#include "utils/StrongPointer.h" - +#include <utils/StrongPointer.h> #include <gui/CpuConsumer.h> #include <gui/Surface.h> +#include <device2/Camera2Device.h> + #include "CameraStreamFixture.h" #include "TestExtensions.h" diff --git a/tests/camera2/camera2.cpp b/tests/camera2/camera2.cpp index 92ef2212..600d4404 100644 --- a/tests/camera2/camera2.cpp +++ b/tests/camera2/camera2.cpp @@ -201,7 +201,8 @@ class Camera2Test: public testing::Test { if (mDevice != NULL) { closeCameraDevice(&mDevice); } - mDevice = openCameraDevice(id); + mId = id; + mDevice = openCameraDevice(mId); ASSERT_TRUE(NULL != mDevice) << "Failed to open camera device"; camera_info info; @@ -334,6 +335,7 @@ class Camera2Test: public testing::Test { TearDownModule(); } + int mId; camera2_device *mDevice; const camera_metadata_t *mStaticInfo; @@ -386,7 +388,8 @@ TEST_F(Camera2Test, Capture1Raw) { ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - sp<CpuConsumer> rawConsumer = new CpuConsumer(1); + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> rawConsumer = new CpuConsumer(bq, 1); sp<FrameWaiter> rawWaiter = new FrameWaiter(); rawConsumer->setFrameAvailableListener(rawWaiter); @@ -417,8 +420,7 @@ TEST_F(Camera2Test, Capture1Raw) { int streamId; ASSERT_NO_FATAL_FAILURE( - setUpStream(rawConsumer->getProducerInterface(), - width, height, format, &streamId) ); + setUpStream(bq, width, height, format, &streamId) ); camera_metadata_t *request; request = allocate_camera_metadata(20, 2000); @@ -520,7 +522,8 @@ TEST_F(Camera2Test, CaptureBurstRaw) { ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - sp<CpuConsumer> rawConsumer = new CpuConsumer(1); + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> rawConsumer = new CpuConsumer(bq, 1); sp<FrameWaiter> rawWaiter = new FrameWaiter(); rawConsumer->setFrameAvailableListener(rawWaiter); @@ -551,8 +554,7 @@ TEST_F(Camera2Test, CaptureBurstRaw) { int streamId; ASSERT_NO_FATAL_FAILURE( - setUpStream(rawConsumer->getProducerInterface(), - width, height, format, &streamId) ); + setUpStream(bq, width, height, format, &streamId) ); camera_metadata_t *request; request = allocate_camera_metadata(20, 2000); @@ -701,7 +703,8 @@ TEST_F(Camera2Test, Capture1Jpeg) { ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - sp<CpuConsumer> jpegConsumer = new CpuConsumer(1); + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> jpegConsumer = new CpuConsumer(bq, 1); sp<FrameWaiter> jpegWaiter = new FrameWaiter(); jpegConsumer->setFrameAvailableListener(jpegWaiter); @@ -720,8 +723,7 @@ TEST_F(Camera2Test, Capture1Jpeg) { int streamId; ASSERT_NO_FATAL_FAILURE( - setUpStream(jpegConsumer->getProducerInterface(), - width, height, format, &streamId) ); + setUpStream(bq, width, height, format, &streamId) ); camera_metadata_t *request; request = allocate_camera_metadata(20, 2000); diff --git a/tests/camera2/camera2_utils.cpp b/tests/camera2/camera2_utils.cpp index 76a72333..3c0767a3 100644 --- a/tests/camera2/camera2_utils.cpp +++ b/tests/camera2/camera2_utils.cpp @@ -210,7 +210,6 @@ int MetadataQueue::consumer_dequeue(const camera2_request_queue_src_ops_t *q, int MetadataQueue::consumer_free(const camera2_request_queue_src_ops_t *q, camera_metadata_t *old_buffer) { - MetadataQueue *queue = getInstance(q); free_camera_metadata(old_buffer); return OK; } diff --git a/tests/hwc/Android.mk b/tests/hwc/Android.mk new file mode 100644 index 00000000..4cae9eb1 --- /dev/null +++ b/tests/hwc/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libcnativewindow +LOCAL_SRC_FILES := cnativewindow.c util.c +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := hwc-test-arrows +LOCAL_SRC_FILES := test-arrows.c +LOCAL_STATIC_LIBRARIES := libcnativewindow +LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES +include $(BUILD_EXECUTABLE) diff --git a/tests/hwc/cnativewindow.c b/tests/hwc/cnativewindow.c new file mode 100644 index 00000000..76f45643 --- /dev/null +++ b/tests/hwc/cnativewindow.c @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include <pthread.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> +#include <hardware/hwcomposer.h> + +#include <system/window.h> +#include <cutils/native_handle.h> + +// normalize and shorten type names +typedef struct android_native_base_t aBase; +typedef struct ANativeWindowBuffer aBuffer; +typedef struct ANativeWindow aWindow; + +static int trace_level = 1; + +#define _TRACE(n,fmt...) \ + do { if (trace_level >= n) fprintf(stderr, "CNW: " fmt); } while (0) + +#define ERROR(fmt...) _TRACE(0, fmt) +#define INFO(fmt...) _TRACE(1, fmt) +#define LOG(fmt...) _TRACE(2, fmt) +#define TRACE(fmt...) _TRACE(3, fmt) + +#define QCT_WORKAROUND 1 + +typedef struct CNativeBuffer { + aBuffer base; + struct CNativeBuffer *next; + struct CNativeBuffer *prev; + int ffd; +} CNativeBuffer; + +typedef struct CNativeWindow { + aWindow base; + + hwc_composer_device_1_t *hwc; + framebuffer_device_t *fb; + alloc_device_t *gr; + + pthread_mutex_t lock; + pthread_cond_t cvar; + + aBuffer *front; + aBuffer *spare; + + CNativeBuffer free_buffer_queue; + + unsigned width; + unsigned height; + unsigned xdpi; + unsigned ydpi; + unsigned format; + + hwc_display_contents_1_t *dclist[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; + + hwc_display_contents_1_t dc; + hwc_layer_1_t layer[4]; +} CNativeWindow; + +static inline CNativeBuffer *from_abuffer(aBuffer *buf) { + return (CNativeBuffer*) buf; +} + +static CNativeBuffer *get_front(struct CNativeBuffer *queue) { + CNativeBuffer *buf = queue->next; + if (buf == queue) + return 0; + buf->next->prev = queue; + queue->next = buf->next; + buf->next = buf->prev = 0; + return buf; +} + +static void put_front(struct CNativeBuffer *queue, aBuffer *_buf) { + struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; + buf->prev = queue; + buf->next = queue->next; + queue->next->prev = buf; + queue->next = buf; +} + +static void put_back(struct CNativeBuffer *queue, aBuffer *_buf) { + struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; + buf->next = queue; + buf->prev = queue->prev; + queue->prev->next = buf; + queue->prev = buf; +} + +static void cnw_inc_ref(aBase *base) { TRACE("buf %p ref++\n",base); } +static void cnw_dec_ref(aBase *base) { TRACE("buf %p ref--\n",base); } + +static inline CNativeWindow *from_base(aWindow *base) { + return (CNativeWindow *) base; +} + +static inline CNativeWindow *from_base_const(const aWindow *base) { + return (CNativeWindow *) base; +} + +static int cnw_set_swap_interval(aWindow *base, int interval) { + CNativeWindow *win = from_base(base); + if (win->fb && win->fb->setSwapInterval) + return win->fb->setSwapInterval(win->fb, interval); + return 0; +} + +static int cnw_dequeue_buffer1(aWindow *base, aBuffer **buf, int *ffd) { + CNativeWindow *win = from_base(base); + CNativeBuffer *cnb; + + pthread_mutex_lock(&win->lock); + + while ((cnb = get_front(&win->free_buffer_queue)) == 0) { + pthread_cond_wait(&win->cvar, &win->lock); + } + + *ffd = cnb->ffd; + *buf = &cnb->base; + cnb->ffd = -1; + LOG("<< dequeue buffer %p %d\n", *buf, *ffd); + + pthread_mutex_unlock(&win->lock); + return 0; +} + +static int cnw_lock_buffer0(aWindow *base, aBuffer *buffer) { + return 0; +} + +static void set_layer(hwc_layer_1_t *dl, aBuffer *buf, int ffd) { + int right = buf->width; + int bottom = buf->height; + + dl->compositionType = HWC_FRAMEBUFFER; + dl->hints = 0; + dl->flags = 0; + + dl->handle = buf->handle; + dl->transform = 0; + dl->blending = HWC_BLENDING_NONE; + dl->sourceCrop.left = 0; + dl->sourceCrop.top = 0; + dl->sourceCrop.right = right; + dl->sourceCrop.bottom = bottom; + dl->displayFrame.left = 0; + dl->displayFrame.top = 0; + dl->displayFrame.right = right; + dl->displayFrame.bottom = bottom; + dl->visibleRegionScreen.numRects = 1; + dl->visibleRegionScreen.rects = &dl->displayFrame; + + dl->acquireFenceFd = ffd; + dl->releaseFenceFd = -1; +} + +static void hwc_post(CNativeWindow *win, aBuffer *buf, int ffd) { + hwc_composer_device_1_t *hwc = win->hwc; + hwc_display_contents_1_t *dc = &(win->dc); + hwc_layer_1_t *dl = win->dc.hwLayers; + int r, i; + + dc->retireFenceFd = -1; + dc->outbufAcquireFenceFd = -1; + dc->flags = HWC_GEOMETRY_CHANGED; + dc->numHwLayers = 1; + + // some hwcomposers fail if these are NULL + dc->dpy = (void*) 0xdeadbeef; + dc->sur = (void*) 0xdeadbeef; + + set_layer(&dl[0], buf, ffd); + + if (QCT_WORKAROUND) { + set_layer(&dl[1], win->spare, -1); + dl[1].compositionType = HWC_FRAMEBUFFER_TARGET; + dc->numHwLayers++; + } + + r = hwc->prepare(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); + if (r) { + ERROR("hwc->prepare failed r=%d\n",r); + return; + } + +// for (i = 0; i < dc->numHwLayers; i++) +// LOG("dl[%d] ctype=0x%08x hints=0x%08x flags=0x%08x\n", i, +// dl[i].compositionType, dl[0].hints, dl[0].flags); + + r = hwc->set(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); + if (r) { + ERROR("hwc->set failed, r=%d\n", r); + return; + } + + if (dc->retireFenceFd != -1) + close(dc->retireFenceFd); + if (dl->releaseFenceFd != -1) { + CNativeBuffer *cnb = from_abuffer(buf); + cnb->ffd = dl->releaseFenceFd; + } + if (QCT_WORKAROUND) + if (dl[1].releaseFenceFd != -1) + close(dl[1].releaseFenceFd); +} + +static int cnw_queue_buffer1(aWindow *base, aBuffer *buffer, int ffd) { + CNativeWindow *win = from_base(base); + int res; + LOG(">> queue buffer %p %d\n", buffer, ffd); + if (win->fb) { + res = win->fb->post(win->fb, buffer->handle); + if (ffd != -1) + close(ffd); + } else { + hwc_post(win, buffer, ffd); + res = 0; + } + pthread_mutex_lock(&win->lock); + if (win->front) + put_back(&win->free_buffer_queue, win->front); + win->front = buffer; + pthread_cond_signal(&win->cvar); + pthread_mutex_unlock(&win->lock); + + return res; +} + +static int cnw_cancel_buffer1(aWindow *base, aBuffer *buf, int ffd) { + CNativeWindow *win = from_base(base); + CNativeBuffer *cnb = from_abuffer(buf); + LOG("<< cancel buffer %p %d\n", buf, ffd); + cnb->ffd = ffd; + pthread_mutex_lock(&win->lock); + put_front(&win->free_buffer_queue, buf); + pthread_mutex_unlock(&win->lock); + return 0; +} + +static int cnw_dequeue_buffer0(aWindow *base, aBuffer **buf) { + int ffd = -1; + int r; + r = cnw_dequeue_buffer1(base, buf, &ffd); + if (ffd != -1) + close(ffd); + return r; +} + +static int cnw_queue_buffer0(aWindow *base, aBuffer *buf) { + return cnw_queue_buffer1(base, buf, -1); +} + +static int cnw_cancel_buffer0(aWindow *base, aBuffer *buf) { + return cnw_cancel_buffer1(base, buf, -1); +} + +static int cnw_query(const aWindow *base, int what, int *value) { + CNativeWindow *win = from_base_const(base); + + switch (what) { + case NATIVE_WINDOW_WIDTH: + case NATIVE_WINDOW_DEFAULT_WIDTH: + *value = win->width; + TRACE("query window width: %d\n", *value); + return 0; + case NATIVE_WINDOW_HEIGHT: + case NATIVE_WINDOW_DEFAULT_HEIGHT: + *value = win->height; + TRACE("query window height: %d\n", *value); + return 0; + case NATIVE_WINDOW_FORMAT: + *value = win->format; + TRACE("query window format: %d\n", *value); + return 0; + case NATIVE_WINDOW_TRANSFORM_HINT: + TRACE("query transform hint: 0\n"); + *value = 0; + return 0; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + TRACE("query min undequeued buffers: 1\n"); + *value = 1; + return 0; + default: + *value = 0; + ERROR("query %d unknown!\n", what); + return -EINVAL; + } +} + +static int cnw_perform(aWindow *base, int op, ...) { + CNativeWindow *win = from_base(base); + va_list ap; + va_start(ap, op); + + switch (op) { + case NATIVE_WINDOW_SET_USAGE: + TRACE("set usage %d\n", va_arg(ap,int)); + return 0; + case NATIVE_WINDOW_CONNECT: + case NATIVE_WINDOW_DISCONNECT: + case NATIVE_WINDOW_API_CONNECT: + case NATIVE_WINDOW_API_DISCONNECT: + return 0; + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + TRACE("set buffers format %d\n", va_arg(ap,int)); + return 0; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + TRACE("set buffers transform %d\n", va_arg(ap,int)); + return 0; + case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: + TRACE("set buffers timestamp %lld\n", va_arg(ap,long long)); + return 0; + case NATIVE_WINDOW_SET_SCALING_MODE: + TRACE("set scaling mode %d\n", va_arg(ap,int)); + return 0; + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: { + int w = va_arg(ap,int); + int h = va_arg(ap,int); + if ((w == win->width) && (h == win->height)) { + TRACE("set buffers dimensions %d x %d\n", w, h); + return 0; + } + ERROR("cannot resize buffers to %d x %d\n", w, h); + return -1; + } + default: + ERROR("perform %d unknown!\n", op); + return -ENODEV; + } +} + +static void hwc_invalidate(const struct hwc_procs *procs) {} +static void hwc_vsync(const struct hwc_procs *procs, int disp, int64_t ts) {} +static void hwc_hotplug(const struct hwc_procs *procs, int disp, int conn) {} + +struct hwc_procs hprocs = { + .invalidate = hwc_invalidate, + .vsync = hwc_vsync, + .hotplug = hwc_hotplug, +}; + +uint32_t attrs[] = { + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_NO_ATTRIBUTE, +}; + +static int hwc_init(CNativeWindow *win) { + hw_module_t const* module; + hwc_composer_device_1_t *hwc; + unsigned i; + int r; + uint32_t configs[32]; + uint32_t numconfigs = 32; + int32_t values[8]; + + if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { + ERROR("cannot open hw composer module\n"); + return -ENODEV; + } + + if (hwc_open_1(module, &hwc)) { + ERROR("cannot open hwc device\n"); + return -ENODEV; + } + win->hwc = hwc; + + LOG("hwc version 0x%08x\n", hwc->common.version); + + if ((hwc->common.version & 0xFFFF0000) < 0x01010000) { + ERROR("hwc version less than 1.1\n"); + hwc_close_1(hwc); + return -ENODEV; + } + + hwc->registerProcs(hwc, &hprocs); + + if (hwc->getDisplayConfigs(hwc, 0, configs, &numconfigs)) { + ERROR("cannot get configs\n"); + return -ENODEV; + } + for (i = 0; i < numconfigs; i++) + LOG("cfg[%d] = 0x%08x\n", i, configs[i]); + + if ((r = hwc->getDisplayAttributes(hwc, 0, configs[0], attrs, values))) { + ERROR("cannot get attributes %d\n", r); + return -ENODEV; + } + + win->width = values[0]; + win->height = values[1]; + win->xdpi = values[3]; + win->ydpi = values[4]; + win->format = HAL_PIXEL_FORMAT_RGBA_8888; + + hwc->blank(hwc, 0, 0); + + win->dclist[0] = &(win->dc); + return 0; +} + +static aBuffer *cnw_alloc(CNativeWindow *win, unsigned format, unsigned usage) { + CNativeBuffer *cnb; + aBuffer *buf; + int err; + + if (!(cnb = malloc(sizeof(CNativeBuffer)))) + return 0; + + buf = &cnb->base; + cnb->ffd = -1; + + buf->common.magic = ANDROID_NATIVE_BUFFER_MAGIC; + buf->common.version = sizeof(aBuffer); + buf->common.incRef = cnw_inc_ref; + buf->common.decRef = cnw_dec_ref; + + buf->width = win->width; + buf->height = win->height; + buf->format = format; + buf->usage = usage; + + err = win->gr->alloc(win->gr, win->width, win->height, + format, usage, &buf->handle, &buf->stride); + if (err) { + ERROR("gralloc of %d x %d failed: err=%d\n", + win->width, win->height, err); + free(buf); + return 0; + } + INFO("alloc buffer %p %d x %d\n", buf, win->width, win->height); + return buf; +} + +static int cnw_init(CNativeWindow *win) { + hw_module_t const* module; + framebuffer_device_t *fb = NULL; + alloc_device_t *gr; + int err, i, n; + unsigned usage, format; + + memset(win, 0, sizeof(CNativeWindow)); + + win->free_buffer_queue.next = &(win->free_buffer_queue); + win->free_buffer_queue.prev = &(win->free_buffer_queue); + + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) != 0) { + ERROR("cannot open gralloc module\n"); + return -ENODEV; + } + + if (hwc_init(win)) { + ERROR("cannot open hwcomposer, trying legacy fb HAL\n"); + err = framebuffer_open(module, &fb); + if (err) { + ERROR("cannot open fb HAL (%s)", strerror(-err)); + return -ENODEV; + } + win->width = fb->width; + win->height = fb->height; + win->format = fb->format; + win->xdpi = fb->xdpi; + win->ydpi = fb->ydpi; + win->fb = fb; + } + + INFO("display %d x %d fmt=%d\n", + win->width, win->height, win->format); + + err = gralloc_open(module, &gr); + if (err) { + ERROR("couldn't open gralloc HAL (%s)", strerror(-err)); + return -ENODEV; + } + win->gr = gr; + + usage = GRALLOC_USAGE_HW_FB | + GRALLOC_USAGE_HW_COMPOSER | + GRALLOC_USAGE_HW_RENDER; + + for (i = 0; i < 2; i++) { + aBuffer *buf = cnw_alloc(win, win->format, usage); + if (!buf) + return -ENOMEM; + put_back(&win->free_buffer_queue, buf); + } + + if (!win->fb && QCT_WORKAROUND) { + win->spare = cnw_alloc(win, win->format, usage); + if (!win->spare) + return -ENOMEM; + } + + // Disgusting, but we need to init these "const" fields + // and unlike C++ we can't use const_cast<> + *((float*) &win->base.xdpi) = win->xdpi; + *((float*) &win->base.ydpi) = win->ydpi; + *((int*) &win->base.minSwapInterval) = 1; + *((int*) &win->base.maxSwapInterval) = 1; + + win->base.common.magic = ANDROID_NATIVE_WINDOW_MAGIC; + win->base.common.version = sizeof(aWindow); + win->base.common.incRef = cnw_inc_ref; + win->base.common.decRef = cnw_dec_ref; + + win->base.setSwapInterval = cnw_set_swap_interval; + win->base.dequeueBuffer_DEPRECATED = cnw_dequeue_buffer0; + win->base.lockBuffer_DEPRECATED = cnw_lock_buffer0; + win->base.queueBuffer_DEPRECATED = cnw_queue_buffer0; + win->base.query = cnw_query; + win->base.perform = cnw_perform; + win->base.cancelBuffer_DEPRECATED = cnw_cancel_buffer0; + win->base.dequeueBuffer = cnw_dequeue_buffer1; + win->base.queueBuffer = cnw_queue_buffer1; + win->base.cancelBuffer = cnw_cancel_buffer1; + + pthread_mutex_init(&win->lock, NULL); + pthread_cond_init(&win->cvar, NULL); + + return 0; +} + +void cnw_destroy(CNativeWindow *win) { + if (win->fb) + framebuffer_close(win->fb); + if (win->hwc) + hwc_close_1(win->hwc); + if (win->gr) + gralloc_close(win->gr); + free(win); +} + +CNativeWindow *cnw_create(void) { + CNativeWindow *win; + char *x; + if ((x = getenv("CNWDEBUG"))) + trace_level = atoi(x); + if (!(win = malloc(sizeof(CNativeWindow)))) + return NULL; + if (cnw_init(win)) { + cnw_destroy(win); + return NULL; + } + return win; +} + +void cnw_info(CNativeWindow *win, unsigned *w, unsigned *h, unsigned *fmt) { + *w = win->width; + *h = win->height; + *fmt = win->format; +} + diff --git a/tests/hwc/test-arrows.c b/tests/hwc/test-arrows.c new file mode 100644 index 00000000..a35faa7b --- /dev/null +++ b/tests/hwc/test-arrows.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include "util.h" + +static const char gVertexShader[] = + "attribute vec4 aPosition;\n" + "uniform mat4 uTransform;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " gl_Position = aPosition * uTransform;\n" + " vTexCoord = aPosition * vec4(1.0/16.0,-1.0/16.0,0.0,0.0);\n" + "}\n"; + +static const char gFragmentShader[] = + "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "uniform float uAnim;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " vec2 tc = vec2(vTexCoord.x, uAnim + vTexCoord.y);\n" + " gl_FragColor = texture2D(uTexture, tc);\n" + "}\n"; + +static GLuint pgm; +static GLint aPosition, uTransform, uTexture, uAnim; + +static GLfloat vtx[2 * 3 * 2]; +static GLfloat mtx[16]; + +//#define R (0xFF0000FF) +#define R (0xFF000000) +#define G (0xFF00FF00) +uint32_t t32[] = { + R, R, R, R, R, R, R, G, G, R, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, G, G, G, G, G, G, R, R, R, R, R, + R, R, R, R, G, G, G, G, G, G, G, G, R, R, R, R, + R, R, R, G, G, G, G, G, G, G, G, G, G, R, R, R, + R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R, + R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, +}; +#undef R +#undef G + +int prepare(int w, int h) { + GLuint texid; + + int left = w / 4; + int top = h / 4; + int right = (w / 4) * 3; + int bottom = (h / 4) * 3; + + vtx[0] = left; + vtx[1] = top; + vtx[2] = left; + vtx[3] = bottom; + vtx[4] = right; + vtx[5] = bottom; + + vtx[6] = right; + vtx[7] = bottom; + vtx[8] = right; + vtx[9] = top; + vtx[10] = left; + vtx[11] = top; + + matrix_init_ortho(mtx, w, h); + + pgm = load_program(gVertexShader, gFragmentShader); + if (!pgm) + return -1; + + aPosition = glGetAttribLocation(pgm, "aPosition"); + uTexture = glGetUniformLocation(pgm, "uTexture"); + uTransform = glGetUniformLocation(pgm, "uTransform"); + uAnim = glGetUniformLocation(pgm, "uAnim"); + + glViewport(0, 0, w, h); + + glGenTextures(1, &texid); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glEnable(GL_TEXTURE_2D); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, t32); + + return 0; +} + +static float anim = 0.0; + +void render() { + anim += 0.1; + if (anim >= 16.0) anim = 0.0; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glUseProgram(pgm); + glUniform1i(uTexture, 0); + glUniform1f(uAnim, anim); + glUniformMatrix4fv(uTransform, 1, 0, mtx); + glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, vtx); + glEnableVertexAttribArray(aPosition); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +int main(int argc, char **argv) { + EGLDisplay display; + EGLSurface surface; + int w, h, count; + + if (argc > 1) + count = atoi(argv[1]); + + if (egl_create(&display, &surface, &w, &h)) + return -1; + + if (prepare(w, h)) + return -1; + + for (;;) { + render(); + eglSwapBuffers(display, surface); + if (count > 0) + if (--count == 0) + break; + } + + egl_destroy(display, surface); + return 0; +} diff --git a/tests/hwc/util.c b/tests/hwc/util.c new file mode 100644 index 00000000..89313054 --- /dev/null +++ b/tests/hwc/util.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include <system/graphics.h> + +#include "util.h" + +void matrix_init_ortho(GLfloat *m, float w, float h) { + m[0] = 2.0 / w; + m[1] = 0.0; + m[2] = 0.0; + m[3] = -1.0; + m[4] = 0.0; + m[5] = 2.0 / h; + m[6] = 0.0; + m[7] = -1.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] -1.0; + m[11] = 0.0; + m[12] = 0.0; + m[13] = 0.0; + m[14] = 0.0; + m[15] = 1.0; +} + +static GLuint load_shader(GLenum shaderType, const char *src) { + GLint status = 0, len = 0; + GLuint shader; + + if (!(shader = glCreateShader(shaderType))) + return 0; + + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + + if (status) + return shader; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + if (len) { + char *msg = malloc(len); + if (msg) { + glGetShaderInfoLog(shader, len, NULL, msg); + msg[len-1] = 0; + fprintf(stderr, "error compiling shader:\n%s\n", msg); + free(msg); + } + } + glDeleteShader(shader); + return 0; +} + +GLuint load_program(const char *vert_src, const char *frag_src) { + GLuint vert, frag, prog; + GLint status = 0, len = 0; + + if (!(vert = load_shader(GL_VERTEX_SHADER, vert_src))) + return 0; + if (!(frag = load_shader(GL_FRAGMENT_SHADER, frag_src))) + goto fail_frag; + if (!(prog = glCreateProgram())) + goto fail_prog; + + glAttachShader(prog, vert); + glAttachShader(prog, frag); + glLinkProgram(prog); + + glGetProgramiv(prog, GL_LINK_STATUS, &status); + if (status) + return prog; + + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); + if (len) { + char *buf = (char*) malloc(len); + if (buf) { + glGetProgramInfoLog(prog, len, NULL, buf); + buf[len-1] = 0; + fprintf(stderr, "error linking program:\n%s\n", buf); + free(buf); + } + } + glDeleteProgram(prog); +fail_prog: + glDeleteShader(frag); +fail_frag: + glDeleteShader(vert); + return 0; +} + +int select_config_for_window(EGLDisplay dpy, EGLint *attr, + unsigned format, EGLConfig *config) { + EGLint R,G,B,A,r,g,b,a; + EGLint i, n, max; + EGLConfig *cfg; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + R = G = B = A = 8; + break; + case HAL_PIXEL_FORMAT_RGB_565: + R = 5; G = 6; B = 5; A = 0; + break; + default: + fprintf(stderr, "unknown fb pixel format %d\n", format); + return -1; + } + + if (eglGetConfigs(dpy, NULL, 0, &max) == EGL_FALSE) { + fprintf(stderr, "no EGL configurations available?!\n"); + return -1; + } + + cfg = (EGLConfig*) malloc(sizeof(EGLConfig) * max); + if (!cfg) + return -1; + + if (eglChooseConfig(dpy, attr, cfg, max, &n) == EGL_FALSE) { + fprintf(stderr, "eglChooseConfig failed\n"); + return -1; + } + + for (i = 0; i < n; i++) { + EGLint r,g,b,a; + eglGetConfigAttrib(dpy, cfg[i], EGL_RED_SIZE, &r); + eglGetConfigAttrib(dpy, cfg[i], EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(dpy, cfg[i], EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(dpy, cfg[i], EGL_ALPHA_SIZE, &a); + if (r == R && g == G && b == B && a == A) { + *config = cfg[i]; + free(cfg); + return 0; + } + } + + fprintf(stderr, "cannot find matching config\n"); + free(cfg); + return -1; +} + +static struct CNativeWindow *_cnw = 0; + +int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h) { + EGLBoolean res; + EGLConfig config = { 0 }; + EGLint context_attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE }; + EGLint major, minor; + EGLContext context; + EGLSurface surface; + EGLint w, h; + EGLDisplay display; + EGLNativeWindowType window; + unsigned width, height, format; + struct CNativeWindow *cnw; + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) + return -1; + + if (!(res = eglInitialize(display, &major, &minor))) + return -1; + + fprintf(stderr, "egl version: %d.%d\n", major, minor); + + if ((cnw = cnw_create()) == 0) + return -1; + + cnw_info(cnw, &width, &height, &format); + window = (EGLNativeWindowType) cnw; + + if ((res = select_config_for_window(display, config_attrs, format, &config))) + goto fail; + + surface = eglCreateWindowSurface(display, config, window, NULL); + if (surface == EGL_NO_SURFACE) + goto fail; + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs); + if (context == EGL_NO_CONTEXT) + goto fail; + + if (!(res = eglMakeCurrent(display, surface, surface, context))) + goto fail; + + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + + fprintf(stderr, "window: %d x %d\n", w, h); + + *_display = display; + *_surface = surface; + *_w = w; + *_h = h; + + _cnw = cnw; + return 0; + +fail: + cnw_destroy(cnw); + return -1; +} + +void egl_destroy(EGLDisplay display, EGLSurface surface) { + if (_cnw) { + eglDestroySurface(display, surface); + eglTerminate(display); + cnw_destroy(_cnw); + _cnw = 0; + } +} diff --git a/tests/hwc/util.h b/tests/hwc/util.h new file mode 100644 index 00000000..a0d38ce0 --- /dev/null +++ b/tests/hwc/util.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GL_UTIL_H_ +#define _GL_UTIL_H_ + +/* convenience */ + +GLuint load_program(const char *vert_src, const char *frag_src); +void matrix_init_ortho(GLfloat *m, float w, float h); + +/* context setup / teardown */ + +int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h); +void egl_destroy(EGLDisplay display, EGLSurface surface); + +/* internals needed by util.c */ + +struct CNativeWindow; +struct CNativeWindow *cnw_create(void); +void cnw_destroy(struct CNativeWindow *win); +void cnw_info(struct CNativeWindow *win, + unsigned *w, unsigned *h, unsigned *fmt); + +#endif |