diff options
56 files changed, 4042 insertions, 4899 deletions
@@ -4,3 +4,4 @@ jpawlowski@google.com malchev@google.com smoreland@google.com swillden@google.com +per-file include/hardware/camera*=etalvala@google.com diff --git a/include/hardware/audio.h b/include/hardware/audio.h index 2d6eb309..10a8789a 100644 --- a/include/hardware/audio.h +++ b/include/hardware/audio.h @@ -91,6 +91,17 @@ __BEGIN_DECLS /* Bluetooth SCO wideband */ #define AUDIO_PARAMETER_KEY_BT_SCO_WB "bt_wbs" +/* BT SCO headset name for debug */ +#define AUDIO_PARAMETER_KEY_BT_SCO_HEADSET_NAME "bt_headset_name" + +/* BT SCO HFP control */ +#define AUDIO_PARAMETER_KEY_HFP_ENABLE "hfp_enable" +#define AUDIO_PARAMETER_KEY_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate" +#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume" + +/* Set screen orientation */ +#define AUDIO_PARAMETER_KEY_ROTATION "rotation" + /** * audio stream parameters */ @@ -201,13 +212,24 @@ typedef enum { give time for gapless track switch */ } audio_drain_type_t; +typedef struct source_metadata { + size_t track_count; + /** Array of metadata of each track connected to this source. */ + struct playback_track_metadata* tracks; +} source_metadata_t; + +typedef struct sink_metadata { + size_t track_count; + /** Array of metadata of each track connected to this sink. */ + struct record_track_metadata* tracks; +} sink_metadata_t; + /** * audio_stream_out is the abstraction interface for the audio output hardware. * * It provides information about various properties of the audio output * hardware driver. */ - struct audio_stream_out { /** * Common methods of the audio stream out. This *must* be the first member of audio_stream_out @@ -392,6 +414,13 @@ struct audio_stream_out { */ int (*get_mmap_position)(const struct audio_stream_out *stream, struct audio_mmap_position *position); + + /** + * Called when the metadata of the stream's source has been changed. + * @param source_metadata Description of the audio that is played by the clients. + */ + void (*update_source_metadata)(struct audio_stream_out *stream, + const struct source_metadata* source_metadata); }; typedef struct audio_stream_out audio_stream_out_t; @@ -501,6 +530,31 @@ struct audio_stream_in { */ int (*get_mmap_position)(const struct audio_stream_in *stream, struct audio_mmap_position *position); + + /** + * Called by the framework to read active microphones + * + * \param[in] stream the stream object. + * \param[out] mic_array Pointer to first element on array with microphone info + * \param[out] mic_count When called, this holds the value of the max number of elements + * allowed in the mic_array. The actual number of elements written + * is returned here. + * if mic_count is passed as zero, mic_array will not be populated, + * and mic_count will return the actual number of active microphones. + * + * \return 0 if the microphone array is successfully filled. + * -ENOSYS if there is an error filling the data + */ + int (*get_active_microphones)(const struct audio_stream_in *stream, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count); + + /** + * Called when the metadata of the stream's sink has been changed. + * @param sink_metadata Description of the audio that is recorded by the clients. + */ + void (*update_sink_metadata)(struct audio_stream_in *stream, + const struct sink_metadata* sink_metadata); }; typedef struct audio_stream_in audio_stream_in_t; @@ -673,6 +727,25 @@ struct audio_hw_device { void (*close_input_stream)(struct audio_hw_device *dev, struct audio_stream_in *stream_in); + /** + * Called by the framework to read available microphones characteristics. + * + * \param[in] dev the hw_device object. + * \param[out] mic_array Pointer to first element on array with microphone info + * \param[out] mic_count When called, this holds the value of the max number of elements + * allowed in the mic_array. The actual number of elements written + * is returned here. + * if mic_count is passed as zero, mic_array will not be populated, + * and mic_count will return the actual number of microphones in the + * system. + * + * \return 0 if the microphone array is successfully filled. + * -ENOSYS if there is an error filling the data + */ + int (*get_microphones)(const struct audio_hw_device *dev, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count); + /** This method dumps the state of the audio hardware */ int (*dump)(const struct audio_hw_device *dev, int fd); diff --git a/include/hardware/camera3.h b/include/hardware/camera3.h index cec2ab3c..c3ea0aa7 100644 --- a/include/hardware/camera3.h +++ b/include/hardware/camera3.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-2018 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. @@ -21,7 +21,7 @@ #include "camera_common.h" /** - * Camera device HAL 3.4 [ CAMERA_DEVICE_API_VERSION_3_4 ] + * Camera device HAL 3.5[ CAMERA_DEVICE_API_VERSION_3_5 ] * * This is the current recommended version of the camera device HAL. * @@ -29,7 +29,7 @@ * android.hardware.camera2 API as LIMITED or above hardware level. * * Camera devices that support this version of the HAL must return - * CAMERA_DEVICE_API_VERSION_3_4 in camera_device_t.common.version and in + * CAMERA_DEVICE_API_VERSION_3_5 in camera_device_t.common.version and in * camera_info_t.device_version (from camera_module_t.get_camera_info). * * CAMERA_DEVICE_API_VERSION_3_3 and above: @@ -157,6 +157,32 @@ * - ANDROID_SENSOR_DYNAMIC_WHITE_LEVEL * - ANDROID_SENSOR_OPAQUE_RAW_SIZE * - ANDROID_SENSOR_OPTICAL_BLACK_REGIONS + * + * 3.5: Minor revisions to support session parameters and logical multi camera: + * + * - Add ANDROID_REQUEST_AVAILABLE_SESSION_KEYS static metadata, which is + * optional for implementations that want to support session parameters. If support is + * needed, then Hal should populate the list with all available capture request keys + * that can cause severe processing delays when modified by client. Typical examples + * include parameters that require time-consuming HW re-configuration or internal camera + * pipeline update. + * + * - Add a session parameter field to camera3_stream_configuration which can be populated + * by clients with initial values for the keys found in ANDROID_REQUEST_AVAILABLE_SESSION_KEYS. + * + * - Metadata additions for logical multi camera capability: + * - ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA + * - ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS + * - ANDROID_LOGICAL_MULTI_CAMERA_SYNC_TYPE + * + * - Add physical camera id field in camera3_stream, so that for a logical + * multi camera, the application has the option to specify which physical camera + * a particular stream is configured on. + * + * - Add physical camera id and settings field in camera3_capture_request, so that + * for a logical multi camera, the application has the option to specify individual + * settings for a particular physical device. + * */ /** @@ -1687,8 +1713,31 @@ typedef struct camera3_stream { */ int rotation; + /** + * The physical camera id this stream belongs to. + * + * <= CAMERA_DEVICE_API_VERISON_3_4: + * + * Not defined and must not be accessed. + * + * >= CAMERA_DEVICE_API_VERISON_3_5: + * + * Always set by camera service. If the camera device is not a logical + * multi camera, or if the camera is a logical multi camera but the stream + * is not a physical output stream, this field will point to a 0-length + * string. + * + * A logical multi camera is a camera device backed by multiple physical + * cameras that are also exposed to the application. And for a logical + * multi camera, a physical output stream is an output stream specifically + * requested on an underlying physical camera. + * + * For an input stream, this field is guaranteed to be a 0-length string. + */ + const char* physical_camera_id; + /* reserved for future use */ - void *reserved[7]; + void *reserved[6]; } camera3_stream_t; @@ -1734,6 +1783,18 @@ typedef struct camera3_stream_configuration { * */ uint32_t operation_mode; + + /** + * >= CAMERA_DEVICE_API_VERSION_3_5: + * + * The session metadata buffer contains the initial values of + * ANDROID_REQUEST_AVAILABLE_SESSION_KEYS. This field is optional + * and camera clients can choose to ignore it, in which case it will + * be set to NULL. If parameters are present, then Hal should examine + * the parameter values and configure its internal camera pipeline + * accordingly. + */ + const camera_metadata_t *session_parameters; } camera3_stream_configuration_t; /** @@ -2217,6 +2278,44 @@ typedef struct camera3_capture_request { */ const camera3_stream_buffer_t *output_buffers; + /** + * <= CAMERA_DEVICE_API_VERISON_3_4: + * + * Not defined and must not be accessed. + * + * >= CAMERA_DEVICE_API_VERSION_3_5: + * The number of physical camera settings to be applied. If 'num_physcam_settings' + * equals 0 or a physical device is not included, then Hal must decide the + * specific physical device settings based on the default 'settings'. + */ + uint32_t num_physcam_settings; + + /** + * <= CAMERA_DEVICE_API_VERISON_3_4: + * + * Not defined and must not be accessed. + * + * >= CAMERA_DEVICE_API_VERSION_3_5: + * The physical camera ids. The array will contain 'num_physcam_settings' + * camera id strings for all physical devices that have specific settings. + * In case some id is invalid, the process capture request must fail and return + * -EINVAL. + */ + const char **physcam_id; + + /** + * <= CAMERA_DEVICE_API_VERISON_3_4: + * + * Not defined and must not be accessed. + * + * >= CAMERA_DEVICE_API_VERSION_3_5: + * The capture settings for the physical cameras. The array will contain + * 'num_physcam_settings' settings for invididual physical devices. In + * case the settings at some particular index are empty, the process capture + * request must fail and return -EINVAL. + */ + const camera_metadata_t **physcam_settings; + } camera3_capture_request_t; /** @@ -2394,6 +2493,37 @@ typedef struct camera3_capture_result { */ uint32_t partial_result; + /** + * >= CAMERA_DEVICE_API_VERSION_3_5: + * + * Specifies the number of physical camera metadata this capture result + * contains. It must be equal to the number of physical cameras being + * requested from. + * + * If the current camera device is not a logical multi-camera, or the + * corresponding capture_request doesn't request on any physical camera, + * this field must be 0. + */ + uint32_t num_physcam_metadata; + + /** + * >= CAMERA_DEVICE_API_VERSION_3_5: + * + * An array of strings containing the physical camera ids for the returned + * physical camera metadata. The length of the array is + * num_physcam_metadata. + */ + const char **physcam_ids; + + /** + * >= CAMERA_DEVICE_API_VERSION_3_5: + * + * The array of physical camera metadata for the physical cameras being + * requested upon. This array should have a 1-to-1 mapping with the + * physcam_ids. The length of the array is num_physcam_metadata. + */ + const camera_metadata_t **physcam_metadata; + } camera3_capture_result_t; /********************************************************************** @@ -2926,7 +3056,8 @@ typedef struct camera3_device_ops { * 0: On a successful start to processing the capture request * * -EINVAL: If the input is malformed (the settings are NULL when not - * allowed, there are 0 output buffers, etc) and capture processing + * allowed, invalid physical camera settings, + * there are 0 output buffers, etc) and capture processing * cannot start. Failures during request processing should be * handled by calling camera3_callback_ops_t.notify(). In case of * this error, the framework will retain responsibility for the diff --git a/include/hardware/camera_common.h b/include/hardware/camera_common.h index 7bafa88f..edd1ada3 100644 --- a/include/hardware/camera_common.h +++ b/include/hardware/camera_common.h @@ -148,10 +148,11 @@ __BEGIN_DECLS #define CAMERA_DEVICE_API_VERSION_3_2 HARDWARE_DEVICE_API_VERSION(3, 2) #define CAMERA_DEVICE_API_VERSION_3_3 HARDWARE_DEVICE_API_VERSION(3, 3) #define CAMERA_DEVICE_API_VERSION_3_4 HARDWARE_DEVICE_API_VERSION(3, 4) +#define CAMERA_DEVICE_API_VERSION_3_5 HARDWARE_DEVICE_API_VERSION(3, 5) -// Device version 3.4 is current, older HAL camera device versions are not +// Device version 3.5 is current, older HAL camera device versions are not // recommended for new devices. -#define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_3_4 +#define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_3_5 /** * Defined in /system/media/camera/include/system/camera_metadata.h diff --git a/include/hardware/gralloc1.h b/include/hardware/gralloc1.h index 0a6843f0..03e84322 100644 --- a/include/hardware/gralloc1.h +++ b/include/hardware/gralloc1.h @@ -155,7 +155,10 @@ typedef enum { GRALLOC1_FUNCTION_UNLOCK = 20, GRALLOC1_FUNCTION_SET_LAYER_COUNT = 21, GRALLOC1_FUNCTION_GET_LAYER_COUNT = 22, - GRALLOC1_LAST_FUNCTION = 22, + GRALLOC1_FUNCTION_VALIDATE_BUFFER_SIZE = 23, + GRALLOC1_FUNCTION_GET_TRANSPORT_SIZE = 24, + GRALLOC1_FUNCTION_IMPORT_BUFFER = 25, + GRALLOC1_LAST_FUNCTION = 25, } gralloc1_function_descriptor_t; typedef enum { @@ -670,6 +673,65 @@ typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_GET_PRODUCER_USAGE)( typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_GET_STRIDE)( gralloc1_device_t* device, buffer_handle_t buffer, uint32_t* outStride); +/* getTransportSize(..., outNumFds, outNumInts) + * Function descriptor: GRALLOC1_FUNCTION_GET_TRANSPORT_SIZE + * This function is optional for all gralloc1 devices. + * + * Get the transport size of a buffer. An imported buffer handle is a raw + * buffer handle with the process-local runtime data appended. This + * function, for example, allows a caller to omit the process-local + * runtime data at the tail when serializing the imported buffer handle. + * + * Note that a client might or might not omit the process-local runtime + * data when sending an imported buffer handle. The mapper must support + * both cases on the receiving end. + * + * Parameters: + * outNumFds - the number of file descriptors needed for transport + * outNumInts - the number of integers needed for transport + * + * Returns GRALLOC1_ERROR_NONE or one of the following errors: + * GRALLOC1_ERROR_BAD_HANDLE - the buffer handle is invalid + * GRALLOC1_ERROR_UNSUPPORTED - the device is unable to retrieve the numFds + * and numInts; see note [1] in this section's header for more information + */ +typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_GET_TRANSPORT_SIZE)( + gralloc1_device_t* device, buffer_handle_t buffer, uint32_t *outNumFds, + uint32_t *outNumInts); + +typedef struct gralloc1_buffer_descriptor_info { + uint32_t width; + uint32_t height; + uint32_t layerCount; + int32_t /*android_pixel_format_t*/ format; + uint64_t producerUsage; + uint64_t consumerUsage; +} gralloc1_buffer_descriptor_info_t; + +/* validateBufferSize(..., ) + * Function descriptor: GRALLOC1_FUNCTION_VALIDATE_BUFFER_SIZE + * This function is optional for all gralloc1 devices. + * + * Validate that the buffer can be safely accessed by a caller who assumes + * the specified descriptorInfo and stride. This must at least validate + * that the buffer size is large enough. Validating the buffer against + * individual buffer attributes is optional. + * + * Parameters: + * descriptor - specifies the attributes of the buffer + * stride - the buffer stride returned by IAllocator::allocate + * + * Returns GRALLOC1_ERROR_NONE or one of the following errors: + * GRALLOC1_ERROR_BAD_HANDLE - the buffer handle is invalid + * GRALLOC1_ERROR_BAD_VALUE - when buffer cannot be safely accessed + * GRALLOC1_ERROR_UNSUPPORTED - the device is unable to validate the buffer + * size; see note [1] in this section's header for more information + */ +typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_VALIDATE_BUFFER_SIZE)( + gralloc1_device_t* device, buffer_handle_t buffer, + const gralloc1_buffer_descriptor_info_t* descriptorInfo, + uint32_t stride); + /* * Buffer management functions */ @@ -723,6 +785,37 @@ typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_ALLOCATE)( const gralloc1_buffer_descriptor_t* descriptors, buffer_handle_t* outBuffers); +/* importBuffer(..., rawHandle, outBuffer); + * Function descriptor: GRALLOC1_FUNCTION_IMPORT_BUFFER + * This function is optional for all gralloc1 devices. + * When supported, GRALLOC1_CAPABILITY_RELEASE_IMPLY_DELETE must also be + * supported. + * + * Explictly imports a buffer into a proccess. + * + * This function can be called in place of retain when a raw buffer handle is + * received by a remote process. Import producess a import handle that can + * be used to access the underlying graphic buffer. The new import handle has a + * ref count of 1. + * + * This function must at least validate the raw handle before creating the + * imported handle. It must also support importing the same raw handle + * multiple times to create multiple imported handles. The imported handle + * must be considered valid everywhere in the process. + * + * Parameters: + * rawHandle - the raw buffer handle to import + * outBuffer - a handle to the newly imported buffer + * + * Returns GRALLOC1_ERROR_NONE or one of the following errors: + * GRALLOC1_ERROR_BAD_HANDLE - the buffer handle is invalid + * GRALLOC1_ERROR_NO_RESOURCES - it is not possible to add a import to this + * buffer at this time + */ +typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_IMPORT_BUFFER)( + gralloc1_device_t* device, const buffer_handle_t rawHandle, + buffer_handle_t* outBuffer); + /* retain(..., buffer) * Function descriptor: GRALLOC1_FUNCTION_RETAIN * Must be provided by all gralloc1 devices diff --git a/include/hardware/hwcomposer2.h b/include/hardware/hwcomposer2.h index 11c9bdd0..c9809ce8 100644 --- a/include/hardware/hwcomposer2.h +++ b/include/hardware/hwcomposer2.h @@ -114,7 +114,7 @@ typedef enum { * presentDisplay should fail as fast as possible in the case a * validateDisplay step is needed. */ - HWC2_CAPABILITY_SKIP_VALIDATE= 4, + HWC2_CAPABILITY_SKIP_VALIDATE = 4, } hwc2_capability_t; /* Possible composition types for a given layer */ @@ -262,6 +262,15 @@ typedef enum { HWC2_FUNCTION_SET_POWER_MODE, HWC2_FUNCTION_SET_VSYNC_ENABLED, HWC2_FUNCTION_VALIDATE_DISPLAY, + HWC2_FUNCTION_SET_LAYER_FLOAT_COLOR, + HWC2_FUNCTION_SET_LAYER_PER_FRAME_METADATA, + HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS, + HWC2_FUNCTION_SET_READBACK_BUFFER, + HWC2_FUNCTION_GET_READBACK_BUFFER_ATTRIBUTES, + HWC2_FUNCTION_GET_READBACK_BUFFER_FENCE, + HWC2_FUNCTION_GET_RENDER_INTENTS, + HWC2_FUNCTION_SET_COLOR_MODE_WITH_RENDER_INTENT, + HWC2_FUNCTION_GET_DATASPACE_SATURATION_MATRIX } hwc2_function_descriptor_t; /* Layer requests returned from getDisplayRequests */ @@ -311,6 +320,33 @@ typedef enum { HWC2_VSYNC_DISABLE = 2, } hwc2_vsync_t; +/* MUST match HIDL's V2_2::IComposerClient::PerFrameMetadataKey */ +typedef enum { + /* SMPTE ST 2084:2014. + * Coordinates defined in CIE 1931 xy chromaticity space + */ + HWC2_DISPLAY_RED_PRIMARY_X = 0, + HWC2_DISPLAY_RED_PRIMARY_Y = 1, + HWC2_DISPLAY_GREEN_PRIMARY_X = 2, + HWC2_DISPLAY_GREEN_PRIMARY_Y = 3, + HWC2_DISPLAY_BLUE_PRIMARY_X = 4, + HWC2_DISPLAY_BLUE_PRIMARY_Y = 5, + HWC2_WHITE_POINT_X = 6, + HWC2_WHITE_POINT_Y = 7, + /* SMPTE ST 2084:2014. + * Units: nits + * max as defined by ST 2048: 10,000 nits + */ + HWC2_MAX_LUMINANCE = 8, + HWC2_MIN_LUMINANCE = 9, + + /* CTA 861.3 + * Units: nits + */ + HWC2_MAX_CONTENT_LIGHT_LEVEL = 10, + HWC2_MAX_FRAME_AVERAGE_LIGHT_LEVEL = 11, +} hwc2_per_frame_metadata_key_t; + /* * Stringification Functions */ @@ -479,6 +515,15 @@ static inline const char* getFunctionDescriptorName( case HWC2_FUNCTION_SET_POWER_MODE: return "SetPowerMode"; case HWC2_FUNCTION_SET_VSYNC_ENABLED: return "SetVsyncEnabled"; case HWC2_FUNCTION_VALIDATE_DISPLAY: return "ValidateDisplay"; + case HWC2_FUNCTION_SET_LAYER_FLOAT_COLOR: return "SetLayerFloatColor"; + case HWC2_FUNCTION_SET_LAYER_PER_FRAME_METADATA: return "SetLayerPerFrameMetadata"; + case HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS: return "GetPerFrameMetadataKeys"; + case HWC2_FUNCTION_SET_READBACK_BUFFER: return "SetReadbackBuffer"; + case HWC2_FUNCTION_GET_READBACK_BUFFER_ATTRIBUTES: return "GetReadbackBufferAttributes"; + case HWC2_FUNCTION_GET_READBACK_BUFFER_FENCE: return "GetReadbackBufferFence"; + case HWC2_FUNCTION_GET_RENDER_INTENTS: return "GetRenderIntents"; + case HWC2_FUNCTION_SET_COLOR_MODE_WITH_RENDER_INTENT: return "SetColorModeWithRenderIntent"; + case HWC2_FUNCTION_GET_DATASPACE_SATURATION_MATRIX: return "GetDataspaceSaturationMatrix"; default: return "Unknown"; } } @@ -668,6 +713,15 @@ enum class FunctionDescriptor : int32_t { SetPowerMode = HWC2_FUNCTION_SET_POWER_MODE, SetVsyncEnabled = HWC2_FUNCTION_SET_VSYNC_ENABLED, ValidateDisplay = HWC2_FUNCTION_VALIDATE_DISPLAY, + SetLayerFloatColor = HWC2_FUNCTION_SET_LAYER_FLOAT_COLOR, + SetLayerPerFrameMetadata = HWC2_FUNCTION_SET_LAYER_PER_FRAME_METADATA, + GetPerFrameMetadataKeys = HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS, + SetReadbackBuffer = HWC2_FUNCTION_SET_READBACK_BUFFER, + GetReadbackBufferAttributes = HWC2_FUNCTION_GET_READBACK_BUFFER_ATTRIBUTES, + GetReadbackBufferFence = HWC2_FUNCTION_GET_READBACK_BUFFER_FENCE, + GetRenderIntents = HWC2_FUNCTION_GET_RENDER_INTENTS, + SetColorModeWithRenderIntent = HWC2_FUNCTION_SET_COLOR_MODE_WITH_RENDER_INTENT, + GetDataspaceSaturationMatrix = HWC2_FUNCTION_GET_DATASPACE_SATURATION_MATRIX, }; TO_STRING(hwc2_function_descriptor_t, FunctionDescriptor, getFunctionDescriptorName) @@ -969,6 +1023,27 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_REGISTER_CALLBACK)( int32_t /*hwc2_callback_descriptor_t*/ descriptor, hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer); +/* getDataspaceSaturationMatrix(..., dataspace, outMatrix) + * Descriptor: HWC2_FUNCTION_GET_DATASPACE_SATURATION_MATRIX + * Provided by HWC2 devices which don't return nullptr function pointer. + * + * Get the saturation matrix of the specified dataspace. The saturation matrix + * can be used to approximate the dataspace saturation operation performed by + * the HWC2 device when non-colorimetric mapping is allowed. It is to be + * applied on linear pixel values. + * + * Parameters: + * dataspace - the dataspace to query for + * outMatrix - a column-major 4x4 matrix (16 floats). It must be an identity + * matrix unless dataspace is HAL_DATASPACE_SRGB_LINEAR. + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_PARAMETER - dataspace was invalid + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_DATASPACE_SATURATION_MATRIX)( + hwc2_device_t* device, int32_t /*android_dataspace_t*/ dataspace, + float* outMatrix); + /* * Display Functions * @@ -1146,6 +1221,35 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_COLOR_MODES)( hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumModes, int32_t* /*android_color_mode_t*/ outModes); +/* getRenderIntents(..., mode, outNumIntents, outIntents) + * Descriptor: HWC2_FUNCTION_GET_RENDER_INTENTS + * Provided by HWC2 devices which don't return nullptr function pointer. + * + * Returns the render intents supported on this display. + * + * The valid render intents can be found in android_render_intent_v1_1_t in + * <system/graphics.h>. All HWC2 devices must support at least + * HAL_RENDER_INTENT_COLORIMETRIC. + * + * outNumIntents may be NULL to retrieve the number of intents which will be + * returned. + * + * Parameters: + * mode - the color mode to query the render intents for + * outNumIntents - if outIntents was NULL, the number of intents which would + * have been returned; if outIntents was not NULL, the number of intents + * returned, which must not exceed the value stored in outNumIntents + * prior to the call; pointer will be non-NULL + * outIntents - an array of render intents + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_RENDER_INTENTS)( + hwc2_device_t* device, hwc2_display_t display, int32_t mode, + uint32_t* outNumIntents, + int32_t* /*android_render_intent_v1_1_t*/ outIntents); + /* getDisplayAttribute(..., config, attribute, outValue) * Descriptor: HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE * Must be provided by all HWC2 devices @@ -1470,8 +1574,8 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_CLIENT_TARGET)( * * Sets the color mode of the given display. * - * Upon returning from this function, the color mode change must have fully - * taken effect. + * This must be called outside of validateDisplay/presentDisplay, and it takes + * effect on next presentDisplay. * * The valid color modes can be found in android_color_mode_t in * <system/graphics.h>. All HWC2 devices must support at least @@ -1490,6 +1594,34 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_COLOR_MODE)( hwc2_device_t* device, hwc2_display_t display, int32_t /*android_color_mode_t*/ mode); +/* setColorModeWithIntent(..., mode, intent) + * Descriptor: HWC2_FUNCTION_SET_COLOR_MODE_WITH_RENDER_INTENT + * Provided by HWC2 devices which don't return nullptr function pointer. + * + * This must be called outside of validateDisplay/presentDisplay, and it takes + * effect on next presentDisplay. + * + * The valid color modes and render intents can be found in + * android_color_mode_t and android_render_intent_v1_1_t in + * <system/graphics.h>. All HWC2 devices must support at least + * HAL_COLOR_MODE_NATIVE and HAL_RENDER_INTENT_COLORIMETRIC, and displays are + * assumed to be in this mode and intent upon hotplug. + * + * Parameters: + * mode - the mode to set + * intent - the intent to set + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + * HWC2_ERROR_BAD_PARAMETER - mode/intent is not a valid color mode or + * render intent + * HWC2_ERROR_UNSUPPORTED - mode or intent is not supported on this display + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_COLOR_MODE_WITH_RENDER_INTENT)( + hwc2_device_t* device, hwc2_display_t display, + int32_t /*android_color_mode_t*/ mode, + int32_t /*android_render_intent_v1_1_t */ intent); + /* setColorTransform(..., matrix, hint) * Descriptor: HWC2_FUNCTION_SET_COLOR_TRANSFORM * Must be provided by all HWC2 devices @@ -1537,6 +1669,34 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_COLOR_TRANSFORM)( hwc2_device_t* device, hwc2_display_t display, const float* matrix, int32_t /*android_color_transform_t*/ hint); +/* getPerFrameMetadataKeys(..., outKeys) + * Descriptor: HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS + * Optional for HWC2 devices + * + * If supported (getFunction(HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS) is non-null), + * getPerFrameMetadataKeys returns the list of supported PerFrameMetadataKeys + * which are invariant with regard to the active configuration. + * + * Devices which are not HDR-capable, must return null when getFunction is called + * with HWC2_FUNCTION_GET_PER_FRAME_METADATA_KEYS. + * + * If outKeys is NULL, the required number of PerFrameMetadataKey keys + * must be returned in outNumKeys. + * + * Parameters: + * outNumKeys - if outKeys is NULL, the number of keys which would have + * been returned; if outKeys is not NULL, the number of keys stored in + * outKeys, which must not exceed the value stored in outNumKeys prior + * to the call; pointer will be non-NULL + * outKeys - an array of hwc2_per_frame_metadata_key_t keys + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_PER_FRAME_METADATA_KEYS)( + hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumKeys, + int32_t* /*hwc2_per_frame_metadata_key_t*/ outKeys); + /* setOutputBuffer(..., buffer, releaseFence) * Descriptor: HWC2_FUNCTION_SET_OUTPUT_BUFFER * Must be provided by all HWC2 devices @@ -1591,6 +1751,132 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_POWER_MODE)( hwc2_device_t* device, hwc2_display_t display, int32_t /*hwc2_power_mode_t*/ mode); +/* getReadbackBufferAttributes(..., outFormat, outDataspace) + * Optional for HWC2 devices + * + * Returns the format which should be used when allocating a buffer for use by + * device readback as well as the dataspace in which its contents should be + * interpreted. + * + * If readback is not supported by this HWC implementation, this call will also + * be able to return HWC2_ERROR_UNSUPPORTED so we can fall back to another method. + * Returning NULL to a getFunction request for this function will also indicate + * that readback is not supported. + * + * The width and height of this buffer will be those of the currently-active + * display configuration, and the usage flags will consist of the following: + * BufferUsage::CPU_READ | BufferUsage::GPU_TEXTURE | + * BufferUsage::COMPOSER_OUTPUT + * + * The format and dataspace provided must be sufficient such that if a + * correctly-configured buffer is passed into setReadbackBuffer, filled by + * the device, and then displayed by the client as a full-screen buffer, the + * output of the display remains the same (subject to the note about protected + * content in the description of setReadbackBuffer). + * + * If the active configuration or color mode of this display has changed since + * the previous call to this function, it will be called again prior to setting + * a readback buffer such that the returned format and dataspace can be updated + * accordingly. + * + * Parameters: + * outFormat - the format the client should use when allocating a device + * readback buffer; pointer will be non-NULL + * outDataspace - the dataspace the client will use when interpreting the + * contents of a device readback buffer; pointer will be non-NULL + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + * + * See also: + * setReadbackBuffer + * getReadbackBufferFence + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_READBACK_BUFFER_ATTRIBUTES)( + hwc2_device_t* device, hwc2_display_t display, + int32_t* /*android_pixel_format_t*/ outFormat, + int32_t* /*android_dataspace_t*/ outDataspace); + +/* getReadbackBufferFence(..., outFence) + * Optional for HWC2 devices + * + * Returns an acquire sync fence file descriptor which will signal when the + * buffer provided to setReadbackBuffer has been filled by the device and is + * safe for the client to read. + * + * If it is already safe to read from this buffer, -1 may be returned instead. + * The client takes ownership of this file descriptor and is responsible for + * closing it when it is no longer needed. + * + * This function will be called immediately after the composition cycle being + * captured into the readback buffer. The complete ordering of a readback buffer + * capture is as follows: + * + * getReadbackBufferAttributes + * // Readback buffer is allocated + * // Many frames may pass + * + * setReadbackBuffer + * validateDisplay + * presentDisplay + * getReadbackBufferFence + * // Implicitly wait on the acquire fence before accessing the buffer + * + * Parameters: + * outFence - a sync fence file descriptor as described above; pointer + * will be non-NULL + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + * HWC2_ERROR_NO_RESOURCES - the readback operation was successful, but + * resulted in a different validate result than would have occurred + * without readback + * HWC2_ERROR_UNSUPPORTED - the readback operation was unsuccessful because + * of resource constraints, the presence of protected content, or other + * reasons; -1 must be returned in outFence + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_GET_READBACK_BUFFER_FENCE)( + hwc2_device_t* device, hwc2_display_t display, + int32_t* outFence); + +/* setReadbackBuffer(..., buffer, releaseFence) + * Optional for HWC2 devices + * + * Sets the readback buffer to be filled with the contents of the next + * composition performed for this display (i.e., the contents present at the + * time of the next validateDisplay/presentDisplay cycle). + * + * This buffer will have been allocated as described in + * getReadbackBufferAttributes and will be interpreted as being in the dataspace + * provided by the same. + * + * If there is hardware protected content on the display at the time of the next + * composition, the area of the readback buffer covered by such content must be + * completely black. Any areas of the buffer not covered by such content may + * optionally be black as well. + * + * The release fence file descriptor provided works identically to the one + * described for setOutputBuffer. + * + * This function will not be called between any call to validateDisplay and a + * subsequent call to presentDisplay. + * + * Parameters: + * buffer - the new readback buffer + * releaseFence - a sync fence file descriptor as described in setOutputBuffer + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + * HWC2_ERROR_BAD_PARAMETER - the new readback buffer handle was invalid + * + * See also: + * getReadbackBufferAttributes + * getReadbackBufferFence + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_READBACK_BUFFER)( + hwc2_device_t* device, hwc2_display_t display, + buffer_handle_t buffer, int32_t releaseFence); + /* setVsyncEnabled(..., enabled) * Descriptor: HWC2_FUNCTION_SET_VSYNC_ENABLED * Must be provided by all HWC2 devices @@ -1758,6 +2044,35 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_LAYER_SURFACE_DAMAGE)( hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, hwc_region_t damage); +/* setLayerPerFrameMetadata(..., numMetadata, metadata) + * Descriptor: HWC2_FUNCTION_SET_LAYER_PER_FRAME_METADATA + * Optional for HWC2 devices + * + * If supported (getFunction(HWC2_FUNCTION_SET_LAYER_PER_FRAME_METADATA) is + * non-null), sets the metadata for the given display for all following + * frames. + * + * Upon returning from this function, the metadata change must have + * fully taken effect. + * + * This function will only be called if getPerFrameMetadataKeys is non-NULL + * and returns at least one key. + * + * Parameters: + * numElements is the number of elements in each of the keys and metadata arrays + * keys is a pointer to the array of keys. + * outMetadata is a pointer to the corresponding array of metadata. + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in + * HWC2_ERROR_BAD_PARAMETER - metadata is not valid + * HWC2_ERROR_UNSUPPORTED - metadata is not supported on this display + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_LAYER_PER_FRAME_METADATA)( + hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, + uint32_t numElements, const int32_t* /*hw2_per_frame_metadata_key_t*/ keys, + const float* metadata); + /* * Layer State Functions * @@ -1805,6 +2120,25 @@ typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_LAYER_COLOR)( hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, hwc_color_t color); +/* setLayerFloatColor(..., color) + * Descriptor: HWC2_FUNCTION_SET_LAYER_FLOAT_COLOR + * Provided by HWC2 devices which don't return nullptr function pointer. + * + * Sets the color of the given layer. If the composition type of the layer is + * not HWC2_COMPOSITION_SOLID_COLOR, this call must return HWC2_ERROR_NONE and + * have no other effect. + * + * Parameters: + * color - the new color in float type, rage is [0.0, 1.0], the colorspace is + * defined by the dataspace that gets set by calling setLayerDataspace. + * + * Returns HWC2_ERROR_NONE or one of the following errors: + * HWC2_ERROR_BAD_LAYER - an invalid layer handle was passed in + */ +typedef int32_t /*hwc2_error_t*/ (*HWC2_PFN_SET_LAYER_FLOAT_COLOR)( + hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, + hwc_float_color_t color); + /* setLayerCompositionType(..., type) * Descriptor: HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE * Must be provided by all HWC2 devices diff --git a/include/hardware/hwcomposer_defs.h b/include/hardware/hwcomposer_defs.h index 1e81e6e6..fd373e31 100644 --- a/include/hardware/hwcomposer_defs.h +++ b/include/hardware/hwcomposer_defs.h @@ -58,6 +58,13 @@ typedef struct hwc_color { uint8_t a; } hwc_color_t; +typedef struct hwc_float_color { + float r; + float g; + float b; + float a; +} hwc_float_color_t; + typedef struct hwc_frect { float left; float top; diff --git a/include/hardware/keymaster_defs.h b/include/hardware/keymaster_defs.h index 7d1b348d..cec486e7 100644 --- a/include/hardware/keymaster_defs.h +++ b/include/hardware/keymaster_defs.h @@ -112,6 +112,8 @@ typedef enum { KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506, /* Allow key to be used after authentication timeout * if device is still on-body (requires secure * on-body sensor. */ + KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 508, /* Require the device screen to be unlocked if the + * key is used. */ /* Application access control */ KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600, /* Specified to indicate key is usable by all @@ -182,6 +184,7 @@ typedef enum { /* Block ciphers algorithms */ KM_ALGORITHM_AES = 32, + KM_ALGORITHM_TRIPLE_DES = 33, /* MAC algorithms */ KM_ALGORITHM_HMAC = 128, @@ -296,6 +299,8 @@ typedef enum { KM_PURPOSE_SIGN = 2, /* Usable with RSA, EC and HMAC keys. */ KM_PURPOSE_VERIFY = 3, /* Usable with RSA, EC and HMAC keys. */ KM_PURPOSE_DERIVE_KEY = 4, /* Usable with EC keys. */ + KM_PURPOSE_WRAP = 5, /* Usable with wrapped keys. */ + } keymaster_purpose_t; typedef struct { @@ -449,6 +454,7 @@ typedef enum { KM_ERROR_KEYMASTER_NOT_CONFIGURED = -64, KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING = -65, KM_ERROR_CANNOT_ATTEST_IDS = -66, + KM_ERROR_DEVICE_LOCKED = -72, KM_ERROR_UNIMPLEMENTED = -100, KM_ERROR_VERSION_MISMATCH = -101, diff --git a/include/hardware/vehicle.h b/include/hardware/vehicle.h deleted file mode 100644 index 8b28e7a0..00000000 --- a/include/hardware/vehicle.h +++ /dev/null @@ -1,2610 +0,0 @@ -/* - * Copyright (C) 2015 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_VEHICLE_INTERFACE_H -#define ANDROID_VEHICLE_INTERFACE_H - -#include <stdint.h> -#include <sys/cdefs.h> -#include <sys/types.h> -#include <math.h> -#include <errno.h> - -#include <hardware/hardware.h> -#include <cutils/native_handle.h> - -__BEGIN_DECLS - -/*****************************************************************************/ - -#define VEHICLE_HEADER_VERSION 1 -#define VEHICLE_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) -#define VEHICLE_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION_2(1, 0, VEHICLE_HEADER_VERSION) - -/** - * Vehicle HAL to provide interfaces to various Car related sensors. The HAL is - * designed in a property, value maping where each property has a value which - * can be "get", "set" and "(un)subscribed" to. Subscribing will require the - * user of this HAL to provide parameters such as sampling rate. - */ - - -/* - * The id of this module - */ -#define VEHICLE_HARDWARE_MODULE_ID "vehicle" - -/** - * Name of the vehicle device to open - */ -#define VEHICLE_HARDWARE_DEVICE "vehicle_hw_device" - -/** - * Each vehicle property is defined with various annotations to specify the type of information. - * Annotations will be used by scripts to run some type check or generate some boiler-plate codes. - * Also the annotations are the specification for each property, and each HAL implementation should - * follow what is specified as annotations. - * Here is the list of annotations with explanation on what it does: - * @value_type: Type of data for this property. One of the value from vehicle_value_type should be - * set here. - * @change_mode: How this property changes. Value set is from vehicle_prop_change_mode. Some - * properties can allow either on change or continuous mode and it is up to HAL - * implementation to choose which mode to use. - * @access: Define how this property can be accessed. read only, write only or R/W from - * vehicle_prop_access - * @data_member: Name of member from vehicle_value union to access this data. - * @data_enum: enum type that should be used for the data. - * @unit: Unit of data. Should be from vehicle_unit_type. - * @config_flags: Usage of config_flags in vehicle_prop_config - * @config_array: Usage of config_array in vehicle_prop_config. When this is specified, - * @config_flags will not be used. - * @config_string: Explains the usage of config_string in vehicle_prop_config. Property with - * this annotation is expected to have additional information in config_string - * for that property to work. - * @zone_type type of zoned used. defined for zoned property - * @range_start, @range_end : define range of specific property values. - * @allow_out_of_range_value : This property allows out of range value to deliver additional - * information. Check VEHICLE_*_OUT_OF_RANGE_* for applicable values. - */ -//===== Vehicle Information ==== - -/** - * Invalid property value used for argument where invalid property gives different result. - */ -#define VEHICLE_PROPERTY_INVALID (0x0) - -/** - * VIN of vehicle - * @value_type VEHICLE_VALUE_TYPE_STRING - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member info_vin - */ -#define VEHICLE_PROPERTY_INFO_VIN (0x00000100) - -/** - * Maker name of vehicle - * @value_type VEHICLE_VALUE_TYPE_STRING - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member info_make - */ -#define VEHICLE_PROPERTY_INFO_MAKE (0x00000101) - -/** - * Model of vehicle - * @value_type VEHICLE_VALUE_TYPE_STRING - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member info_model - */ -#define VEHICLE_PROPERTY_INFO_MODEL (0x00000102) - -/** - * Model year of vehicle. - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member info_model_year - * @unit VEHICLE_UNIT_TYPE_YEAR - */ -#define VEHICLE_PROPERTY_INFO_MODEL_YEAR (0x00000103) - -/** - * Fuel capacity of the vehicle - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member info_fuel_capacity - * @unit VEHICLE_UNIT_TYPE_VEHICLE_UNIT_TYPE_MILLILITER - */ -#define VEHICLE_PROPERTY_INFO_FUEL_CAPACITY (0x00000104) - - -//==== Vehicle Performance Sensors ==== - -/** - * Current odometer value of the vehicle - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member odometer - * @unit VEHICLE_UNIT_TYPE_KILOMETER - */ -#define VEHICLE_PROPERTY_PERF_ODOMETER (0x00000204) - -/** - * Speed of the vehicle - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member vehicle_speed - * @unit VEHICLE_UNIT_TYPE_METER_PER_SEC - */ -#define VEHICLE_PROPERTY_PERF_VEHICLE_SPEED (0x00000207) - - -//==== Engine Sensors ==== - -/** - * Temperature of engine coolant - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member engine_coolant_temperature - * @unit VEHICLE_UNIT_TYPE_CELCIUS - */ -#define VEHICLE_PROPERTY_ENGINE_COOLANT_TEMP (0x00000301) - -/** - * Temperature of engine oil - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member engine_oil_temperature - * @unit VEHICLE_UNIT_TYPE_CELCIUS - */ -#define VEHICLE_PROPERTY_ENGINE_OIL_TEMP (0x00000304) -/** - * Engine rpm - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member engine_rpm - * @unit VEHICLE_UNIT_TYPE_RPM - */ -#define VEHICLE_PROPERTY_ENGINE_RPM (0x00000305) - -//==== Event Sensors ==== - -/** - * Currently selected gear - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member gear_selection - * @data_enum vehicle_gear - */ -#define VEHICLE_PROPERTY_GEAR_SELECTION (0x00000400) - -/** - * Current gear. In non-manual case, selected gear does not necessarily match the current gear - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member gear_current_gear - * @data_enum vehicle_gear - */ -#define VEHICLE_PROPERTY_CURRENT_GEAR (0x00000401) - -/** - * Parking brake state. - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member parking_brake - * @data_enum vehicle_boolean - */ -#define VEHICLE_PROPERTY_PARKING_BRAKE_ON (0x00000402) - -/** - * Driving status policy. - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member driving_status - * @data_enum vehicle_driving_status - */ -#define VEHICLE_PROPERTY_DRIVING_STATUS (0x00000404) - -/** - * Warning for fuel low level. - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member is_fuel_level_low - * @data_enum vehicle_boolean - */ -#define VEHICLE_PROPERTY_FUEL_LEVEL_LOW (0x00000405) - -/** - * Night mode or not. - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member night_mode - * @data_enum vehicle_boolean - */ -#define VEHICLE_PROPERTY_NIGHT_MODE (0x00000407) - - - -//==== HVAC Properties ==== - -/** - * Fan speed setting - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member hvac.fan_speed - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @allow_out_of_range_value : OFF - */ -#define VEHICLE_PROPERTY_HVAC_FAN_SPEED (0x00000500) - -/** - * Fan direction setting - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member hvac.fan_direction - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_enum vehicle_hvac_fan_direction - * @allow_out_of_range_value : OFF - */ -#define VEHICLE_PROPERTY_HVAC_FAN_DIRECTION (0x00000501) - -/* - * Bit flags for fan direction - */ -enum vehicle_hvac_fan_direction { - VEHICLE_HVAC_FAN_DIRECTION_FACE = 0x1, - VEHICLE_HVAC_FAN_DIRECTION_FLOOR = 0x2, - VEHICLE_HVAC_FAN_DIRECTION_FACE_AND_FLOOR = 0x3, - VEHICLE_HVAC_FAN_DIRECTION_DEFROST = 0x4, - VEHICLE_HVAC_FAN_DIRECTION_DEFROST_AND_FLOOR = 0x5 -}; - -/** - * HVAC current temperature. - * @value_type VEHICLE_VALUE_TYPE_ZONED_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.temperature_current - */ -#define VEHICLE_PROPERTY_HVAC_TEMPERATURE_CURRENT (0x00000502) - -/** - * HVAC, target temperature set. - * @value_type VEHICLE_VALUE_TYPE_ZONED_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.temperature_set - * @allow_out_of_range_value : MIN / MAX / OFF - */ -#define VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET (0x00000503) - -/** - * On/off defrost - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_WINDOW - * @data_member hvac.defrost_on - */ -#define VEHICLE_PROPERTY_HVAC_DEFROSTER (0x00000504) - -/** - * On/off AC - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_flags Supported zones - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.ac_on - */ -#define VEHICLE_PROPERTY_HVAC_AC_ON (0x00000505) - -/** - * On/off max AC - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.max_ac_on - */ -#define VEHICLE_PROPERTY_HVAC_MAX_AC_ON (0x00000506) - -/** - * On/off max defrost - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.max_defrost_on - */ -#define VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON (0x00000507) - -/** - * On/off re-circulation - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.max_recirc_on - */ -#define VEHICLE_PROPERTY_HVAC_RECIRC_ON (0x00000508) - -/** - * On/off dual. This will be defined per each row. - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.dual_on - */ -#define VEHICLE_PROPERTY_HVAC_DUAL_ON (0x00000509) - -/** - * On/off automatic mode - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.auto_on - */ -#define VEHICLE_PROPERTY_HVAC_AUTO_ON (0x0000050A) - -/** - * Seat temperature - * - * Negative values indicate cooling. - * 0 indicates off. - * Positive values indicate heating. - * - * Some vehicles may have multiple levels of heating and cooling. The min/max - * range defines the allowable range and number of steps in each direction. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE (0x0000050B) - -/** - * Side Mirror Heat - * - * Increase values denote higher heating levels for side mirrors. - * 0 indicates heating is turned off. - * - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_HVAC_SIDE_MIRROR_HEAT (0x0000050C) - -/** - * Steering Wheel Temperature - * - * Sets the temperature for the steering wheel - * Positive value indicates heating. - * Negative value indicates cooling. - * 0 indicates tempreature control is off. - * - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_HVAC_STEERING_WHEEL_TEMP (0x0000050D) - -/** - * Temperature units - * - * Indicates whether the temperature is in Celsius, Fahrenheit, or a different unit. - * This parameter affects all HVAC temperatures in the system. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_enum vehicle_unit_type - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_HVAC_TEMPERATURE_UNITS (0x0000050E) - -/** - * Actual fan speed - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @data_member hvac.fan_speed - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @allow_out_of_range_value : OFF - */ -#define VEHICLE_PROPERTY_HVAC_ACTUAL_FAN_SPEED_RPM (0x0000050F) - - -/** - * Represents power state for HVAC. Some HVAC properties will require matching power to be turned on - * to get out of OFF state. For non-zoned HVAC properties, VEHICLE_ALL_ZONE corresponds to - * global power state. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_string list of HVAC properties whose power is controlled by this property. Format is - * hexa-decimal number (0x...) separated by comma like "0x500,0x503". All zones - * defined in these affected properties should be available in the property. - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @data_member hvac.power_on - */ -#define VEHICLE_PROPERTY_HVAC_POWER_ON (0x00000510) - -/** - * Fan Positions Available - * - * This is a bit mask of fan positions available for the zone. Each entry in - * vehicle_hvac_fan_direction is selected by bit position. For instance, if - * only the FAN_DIRECTION_FACE (0x1) and FAN_DIRECTION_DEFROST (0x4) are available, - * then this value shall be set to 0x12. - * - * 0x12 = (1 << 1) | (1 << 4) - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC - * @access VEHICLE_PROP_ACCESS_READ - * @data_member int32_value - * @zone_type VEHICLE_ZONE_TYPE_ZONE - * @allow_out_of_range_value : OFF - */ -#define VEHICLE_PROPERTY_HVAC_FAN_DIRECTION_AVAILABLE (0x00000511) - -/** - * Outside temperature - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member outside_temperature - * @unit VEHICLE_UNIT_TYPE_CELCIUS - */ -#define VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE (0x00000703) - - -/** - * Cabin temperature - * @value_type VEHICLE_VALUE_TYPE_FLOAT - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE|VEHICLE_PROP_CHANGE_MODE_CONTINUOUS - * @access VEHICLE_PROP_ACCESS_READ - * @data_member cabin_temperature - * @unit VEHICLE_UNIT_TYPE_CELCIUS - */ -#define VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE (0x00000704) - - -/* - * Radio features. - */ -/** - * Radio presets stored on the Car radio module. The data type used is int32 - * array with the following fields: - * <ul> - * <li> int32_array[0]: Preset number </li> - * <li> int32_array[1]: Band type (see #RADIO_BAND_FM in - * system/core/include/system/radio.h). - * <li> int32_array[2]: Channel number </li> - * <li> int32_array[3]: Sub channel number </li> - * </ul> - * - * NOTE: When getting a current preset config ONLY set preset number (i.e. - * int32_array[0]). For setting a preset other fields are required. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC4 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_flags Number of presets supported - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_RADIO_PRESET (0x00000801) - -/** - * Constants relevant to radio. - */ -enum vehicle_radio_consts { - /** Minimum value for the radio preset */ - VEHICLE_RADIO_PRESET_MIN_VALUE = 1, -}; - -/** - * Represents audio focus state of Android side. Note that car's audio module will own audio - * focus and grant audio focus to Android side when requested by Android side. The focus has both - * per stream characteristics and global characteristics. - * - * Focus request (get of this property) will take the following form in int32_vec4: - * int32_array[0]: vehicle_audio_focus_request type - * int32_array[1]: bit flags of streams requested by this focus request. There can be up to - * 32 streams. - * int32_array[2]: External focus state flags. For request, only flag like - * VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG or - * VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG can be used. - * VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG is for case like radio where android - * side app still needs to hold focus but playback is done outside Android. - * VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG is for muting media channel - * including radio. VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG can be set even - * if android side releases focus (request type REQUEST_RELEASE). In that case, - * audio module should maintain mute state until user's explicit action to - * play some media. - * int32_array[3]: Currently active audio contexts. Use combination of flags from - * vehicle_audio_context_flag. - * This can be used as a hint to adjust audio policy or other policy decision. - * Note that there can be multiple context active at the same time. And android - * can send the same focus request type gain due to change in audio contexts. - * Note that each focus request can request multiple streams that is expected to be used for - * the current request. But focus request itself is global behavior as GAIN or GAIN_TRANSIENT - * expects all sounds played by car's audio module to stop. Note that stream already allocated to - * android before this focus request should not be affected by focus request. - * - * Focus response (set and subscription callback for this property) will take the following form: - * int32_array[0]: vehicle_audio_focus_state type - * int32_array[1]: bit flags of streams allowed. - * int32_array[2]: External focus state: bit flags of currently active audio focus in car - * side (outside Android). Active audio focus does not necessarily mean currently - * playing, but represents the state of having focus or waiting for focus - * (pause state). - * One or combination of flags from vehicle_audio_ext_focus_flag. - * 0 means no active audio focus holder outside Android. - * The state will have following values for each vehicle_audio_focus_state_type: - * GAIN: 0 or VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY when radio is active in - * Android side. - * GAIN_TRANSIENT: 0. Can be VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT or - * VEHICLE_AUDIO_EXT_FOCUS_CAR_TRANSIENT if android side has requested - * GAIN_TRANSIENT_MAY_DUCK and car side is ducking. - * LOSS: 0 when no focus is audio is active in car side. - * VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT when car side is playing something - * permanent. - * LOSS_TRANSIENT: always should be VEHICLE_AUDIO_EXT_FOCUS_CAR_TRANSIENT - * int32_array[3]: context requested by android side when responding to focus request. - * When car side is taking focus away, this should be zero. - * - * A focus response should be sent per each focus request even if there is no change in - * focus state. This can happen in case like focus request only involving context change - * where android side still needs matching focus response to confirm that audio module - * has made necessary changes. - * - * If car does not support VEHICLE_PROPERTY_AUDIO_FOCUS, focus is assumed to be granted always. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC4 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_AUDIO_FOCUS (0x00000900) - -enum vehicle_audio_focus_request { - VEHICLE_AUDIO_FOCUS_REQUEST_GAIN = 0x1, - VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT = 0x2, - VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK = 0x3, - /** - * This is for the case where android side plays sound like UI feedback - * and car side does not need to duck existing playback as long as - * requested stream is available. - */ - VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4, - VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE = 0x5, -}; - -enum vehicle_audio_focus_state { - /** - * Android side has permanent focus and can play allowed streams. - */ - VEHICLE_AUDIO_FOCUS_STATE_GAIN = 0x1, - /** - * Android side has transient focus and can play allowed streams. - */ - VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT = 0x2, - /** - * Car audio module is playing guidance kind of sound outside Android. Android side can - * still play through allowed streams with ducking. - */ - VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3, - /** - * Car audio module is playing transient sound outside Android. Android side should stop - * playing any sounds. - */ - VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT = 0x4, - /** - * Android side has lost focus and cannot play any sound. - */ - VEHICLE_AUDIO_FOCUS_STATE_LOSS = 0x5, - /** - * car audio module is playing safety critical sound, and Android side cannot request focus - * until the current state is finished. car audio module should restore it to the previous - * state when it can allow Android to play. - */ - VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6, -}; - -/** - * Flags to represent multiple streams by combining these. - */ -enum vehicle_audio_stream_flag { - VEHICLE_AUDIO_STREAM_STREAM0_FLAG = (0x1<<0), - VEHICLE_AUDIO_STREAM_STREAM1_FLAG = (0x1<<1), - VEHICLE_AUDIO_STREAM_STREAM2_FLAG = (0x1<<2), -}; - -/** - * Represents stream number (always 0 to N -1 where N is max number of streams). - * Can be used for audio related property expecting one stream. - */ -enum vehicle_audio_stream { - VEHICLE_AUDIO_STREAM0 = 0, - VEHICLE_AUDIO_STREAM1 = 1, -}; - -/** - * Flag to represent external focus state (outside Android). - */ -enum vehicle_audio_ext_focus_flag { - /** - * No external focus holder. - */ - VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG = 0x0, - /** - * Car side (outside Android) has component holding GAIN kind of focus state. - */ - VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG = 0x1, - /** - * Car side (outside Android) has component holding GAIN_TRANSIENT kind of focus state. - */ - VEHICLE_AUDIO_EXT_FOCUS_CAR_TRANSIENT_FLAG = 0x2, - /** - * Car side is expected to play something while focus is held by Android side. One example - * can be radio attached in car side. But Android's radio app still should have focus, - * and Android side should be in GAIN state, but media stream will not be allocated to Android - * side and car side can play radio any time while this flag is active. - */ - VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG = 0x4, - /** - * Car side should mute any media including radio. This can be used with any focus request - * including GAIN* and RELEASE. - */ - VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG = 0x8, -}; - -/** - * Index in int32_array for VEHICLE_PROPERTY_AUDIO_FOCUS property. - */ -enum vehicle_audio_focus_index { - VEHICLE_AUDIO_FOCUS_INDEX_FOCUS = 0, - VEHICLE_AUDIO_FOCUS_INDEX_STREAMS = 1, - VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE = 2, - VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS = 3, -}; - -/** - * Flags to tell the current audio context. - */ -enum vehicle_audio_context_flag { - /** Music playback is currently active. */ - VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG = 0x1, - /** Navigation is currently running. */ - VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG = 0x2, - /** Voice command session is currently running. */ - VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG = 0x4, - /** Voice call is currently active. */ - VEHICLE_AUDIO_CONTEXT_CALL_FLAG = 0x8, - /** Alarm is active. This may be only used in VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY. */ - VEHICLE_AUDIO_CONTEXT_ALARM_FLAG = 0x10, - /** - * Notification sound is active. This may be only used in VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY. - */ - VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG = 0x20, - /** - * Context unknown. Only used for VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY to represent default - * stream for unknown contents. - */ - VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG = 0x40, - /** Safety alert / warning is played. */ - VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG = 0x80, - /** CD / DVD kind of audio is played */ - VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG = 0x100, - /** Aux audio input is played */ - VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG = 0x200, - /** system sound like UI feedback */ - VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG = 0x400, - /** Radio is played */ - VEHICLE_AUDIO_CONTEXT_RADIO_FLAG = 0x800, - /** Ext source is played. This is for tagging generic ext sources. */ - VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG = 0x1000, -}; - -/** - * Property to control audio volume of each audio context. - * - * vehicle_prop_config_t - * config_array[0] : bit flags of all supported audio contexts. If this is 0, - * audio volume is controlled per physical stream - * config_array[1] : flags defined in vehicle_audio_volume_capability_flag to - * represent audio module's capability. - * - * Data type looks like: - * int32_array[0] : stream context as defined in vehicle_audio_context_flag. If only physical - stream is supported (config_array[0] == 0), this will represent physical - stream number. - * int32_array[1] : volume level, valid range is 0 to int32_max_value defined in config. - * 0 will be mute state. int32_min_value in config should be always 0. - * int32_array[2] : One of vehicle_audio_volume_state. - * - * This property requires per stream based get. HAL implementation should check stream number - * in get call to return the right volume. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC3 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_flags all audio contexts supported. - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_AUDIO_VOLUME (0x00000901) - - -/** - * flags to represent capability of audio volume property. - * used in config_array[1] of vehicle_prop_config_t. - */ -enum vehicle_audio_volume_capability_flag { - /** - * External audio module or vehicle hal has persistent storage - * to keep the volume level. This should be set only when per context - * volume level is supproted. When this is set, audio volume level per - * each context will be retrieved from the property when systen starts up. - * And external audio module is also expected to adjust volume automatically - * whenever there is an audio context change. - * When this flag is not set, android side will assume that there is no - * persistent storage and stored value in android side will be used to - * initialize the volume level. And android side will set volume level - * of each physical streams whenever there is an audio context change. - */ - VEHICLE_AUDIO_VOLUME_CAPABILITY_PERSISTENT_STORAGE = 0x1, - /** - * When this flag is set, the H/W can support only single master volume for all streams. - * There is no way to set volume level differently per each stream or context. - */ - VEHICLE_AUDIO_VOLUME_CAPABILITY_MASTER_VOLUME_ONLY = 0x2, -}; - - -/** - * enum to represent audio volume state. - */ -enum vehicle_audio_volume_state { - VEHICLE_AUDIO_VOLUME_STATE_OK = 0, - /** - * Audio volume has reached volume limit set in VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT - * and user's request to increase volume further is not allowed. - */ - VEHICLE_AUDIO_VOLUME_STATE_LIMIT_REACHED = 1, -}; - -/** - * Index in int32_array for VEHICLE_PROPERTY_AUDIO_VOLUME property. - */ -enum vehicle_audio_volume_index { - VEHICLE_AUDIO_VOLUME_INDEX_STREAM = 0, - VEHICLE_AUDIO_VOLUME_INDEX_VOLUME = 1, - VEHICLE_AUDIO_VOLUME_INDEX_STATE = 2, -}; - -/** - * Property for handling volume limit set by user. This limits maximum volume that can be set - * per each context or physical stream. - * - * vehicle_prop_config_t - * config_array[0] : bit flags of all supported audio contexts. If this is 0, - * audio volume is controlled per physical stream - * config_array[1] : flags defined in vehicle_audio_volume_capability_flag to - * represent audio module's capability. - * - * Data type looks like: - * int32_array[0] : stream context as defined in vehicle_audio_context_flag. If only physical - * stream is supported (config_array[0] == 0), this will represent physical - * stream number. - * int32_array[1] : maximum volume set to the stream. If there is no restriction, this value - * will be bigger than VEHICLE_PROPERTY_AUDIO_VOLUME's max value. - * - * If car does not support this feature, this property should not be populated by HAL. - * This property requires per stream based get. HAL implementation should check stream number - * in get call to return the right volume. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC2 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_flags all audio contexts supported. - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT (0x00000902) - -/** - * Index in int32_array for VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT property. - */ -enum vehicle_audio_volume_limit_index { - VEHICLE_AUDIO_VOLUME_LIMIT_INDEX_STREAM = 0, - VEHICLE_AUDIO_VOLUME_LIMIT_INDEX_MAX_VOLUME = 1, -}; - -/** - * Property to share audio routing policy of android side. This property is set at the beginning - * to pass audio policy in android side down to vehicle HAL and car audio module. - * This can be used as a hint to adjust audio policy or other policy decision. - * - * int32_array[0] : audio stream where the audio for the application context will be routed - * by default. Note that this is the default setting from system, but each app - * may still use different audio stream for whatever reason. - * int32_array[1] : All audio contexts that will be sent through the physical stream. Flag - * is defined in vehicle_audio_context_flag. - - * Setting of this property will be done for all available physical streams based on audio H/W - * variant information acquired from VEHICLE_PROPERTY_AUDIO_HW_VARIANT property. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC2 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_WRITE - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY (0x00000903) - -/** - * Index in int32_array for VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY property. - */ -enum vehicle_audio_routing_policy_index { - VEHICLE_AUDIO_ROUTING_POLICY_INDEX_STREAM = 0, - VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS = 1, -}; - -/** -* Property to return audio H/W variant type used in this car. This allows android side to -* support different audio policy based on H/W variant used. Note that other components like -* CarService may need overlay update to support additional variants. If this property does not -* exist, default audio policy will be used. -* -* @value_type VEHICLE_VALUE_TYPE_INT32 -* @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC -* @access VEHICLE_PROP_ACCESS_READ -* @config_flags Additional info on audio H/W. Should use vehicle_audio_hw_variant_config_flag for -* this. -* @data_member int32_value -*/ -#define VEHICLE_PROPERTY_AUDIO_HW_VARIANT (0x00000904) - -/** - * Flag to be used in vehicle_prop_config.config_flags for VEHICLE_PROPERTY_AUDIO_HW_VARIANT. - */ -enum vehicle_audio_hw_variant_config_flag { - /** - * Flag to tell that radio is internal to android and radio should - * be treated like other android stream like media. - * When this flag is not set or AUDIO_HW_VARIANT does not exist, - * radio is treated as external module. This brins some delta in audio focus - * handling as well. - */ - VEHICLE_AUDIO_HW_VARIANT_FLAG_INTERNAL_RADIO_FLAG = 0x1, -}; - -/** audio routing for AM/FM. This should be also be the value when there is only one - radio module in the system. */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_AM_FM "RADIO_AM_FM" -/** Should be added only when there is a separate radio module with HD capability. */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_AM_FM_HD "RADIO_AM_FM_HD" -/** For digial audio broadcasting type of radio */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_DAB "RADIO_DAB" -/** For satellite type of radio */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_SATELLITE "RADIO_SATELLITE" -/** For CD or DVD */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_CD_DVD "CD_DVD" -/** For 1st auxiliary input */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_AUX_IN0 "AUX_IN0" -/** For 2nd auxiliary input */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_AUX_IN1 "AUX_IN1" -/** For navigation guidance from outside android */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_NAV_GUIDANCE "EXT_NAV_GUIDANCE" -/** For voice command from outside android */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_VOICE_COMMAND "EXT_VOICE_COMMAND" -/** For voice call from outside android */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_VOICE_CALL "EXT_VOICE_CALL" -/** For safety alert from outside android */ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_SAFETY_ALERT "EXT_SAFETY_ALERT" - -/** -* Property to pass hint on external audio routing. When android side request focus -* with VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG flag, this property will be set -* before setting AUDIO_FOCUS property as a hint for external audio source routing. -* Note that setting this property alone should not trigger any change. -* Audio routing should be changed only when AUDIO_FOCUS property is set. Note that -* this property allows passing custom value as long as it is defined in config_string. -* This allows supporting non-standard routing options through this property. -* It is recommended to use separate name space for custom property to prevent conflict in future -* android releases. -* Enabling each external routing option is done by enabling each bit flag for the routing. -* This property can support up to 128 external routings. -* To give full flexibility, there is no standard definition for each bit flag and -* assigning each big flag to specific routing type is decided by config_string. -* config_string has format of each entry separated by ',' -* and each entry has format of bitFlagPositon:typeString[:physicalStreamNumber]. -* bitFlagPosition: represents which big flag will be set to enable this routing. 0 means -* LSB in int32_array[0]. 31 will be MSB in int32_array[0]. 127 will MSB in int32_array[3]. -* typeString: string representation of external routing. Some types are already defined -* in VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_* and use them first before adding something -* custom. Applications will find each routing using this string. -* physicalStreamNumber: This part is optional and represents physical stream to android -* which will be disabled when this routing is enabled. If not specified, this routing -* should not affect physical streams to android. -* As an example, let's assume a system with two physical streams, 0 for media and 1 for -* nav guidance. And let's assume external routing option of am fm radio, external navigation -* guidance, satellite radio, and one custom. Let's assume that radio and satellite replaces -* physical stream 0 and external navigation replaces physical stream 1. And bit flag will be -* assigned in the order listed above. This configuration will look like this in config_string: -* "0:RADIO_AM_FM:0,1:EXT_NAV_GUIDANCE:1,2:RADIO_SATELLITE:0,3:com.test.SOMETHING_CUSTOM" -* When android requests RADIO_AM_FM, int32_array[0] will be set to 0x1. -* When android requests RADIO_SATELLITE + EXT_NAV_GUIDANCE, int32_array[0] will be set to 0x2|0x4. -* -* @value_type VEHICLE_VALUE_TYPE_INT32_VEC4 -* @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE -* @access VEHICLE_PROP_ACCESS_WRITE|VEHICLE_PROP_ACCESS_READ_WRITE -* @config_string List of all avaiable external source in the system. -* @data_member int32_array -*/ -#define VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT (0x00000905) - -/** - * Property to control power state of application processor. - * - * It is assumed that AP's power state is controller by separate power controller. - * - * For configuration information, vehicle_prop_config.config_flags can have bit flag combining - * values in vehicle_ap_power_state_config_type. - * - * For get / notification, data type looks like this: - * int32_array[0] : vehicle_ap_power_state_type - * int32_array[1] : additional parameter relevant for each state. should be 0 if not used. - * For set, data type looks like this: - * int32_array[0] : vehicle_ap_power_state_set_type - * int32_array[1] : additional parameter relevant for each request. should be 0 if not used. - * - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC2 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_flags Additional info on power state. Should use vehicle_ap_power_state_config_flag. - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_AP_POWER_STATE (0x00000A00) - -enum vehicle_ap_power_state_config_flag { - /** - * AP can enter deep sleep state. If not set, AP will always shutdown from - * VEHICLE_AP_POWER_STATE_SHUTDOWN_PREPARE power state. - */ - VEHICLE_AP_POWER_STATE_CONFIG_ENABLE_DEEP_SLEEP_FLAG = 0x1, - - /** - * The power controller can power on AP from off state after timeout specified in - * VEHICLE_AP_POWER_SET_SHUTDOWN_READY message. - */ - VEHICLE_AP_POWER_STATE_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2, -}; - -enum vehicle_ap_power_state { - /** vehicle HAL will never publish this state to AP */ - VEHICLE_AP_POWER_STATE_OFF = 0, - /** vehicle HAL will never publish this state to AP */ - VEHICLE_AP_POWER_STATE_DEEP_SLEEP = 1, - /** AP is on but display should be off. */ - VEHICLE_AP_POWER_STATE_ON_DISP_OFF = 2, - /** AP is on with display on. This state allows full user interaction. */ - VEHICLE_AP_POWER_STATE_ON_FULL = 3, - /** - * The power controller has requested AP to shutdown. AP can either enter sleep state or start - * full shutdown. AP can also request postponing shutdown by sending - * VEHICLE_AP_POWER_SET_SHUTDOWN_POSTPONE message. The power controller should change power - * state to this state to shutdown system. - * - * int32_array[1] : one of enum_vehicle_ap_power_state_shutdown_param_type - */ - VEHICLE_AP_POWER_STATE_SHUTDOWN_PREPARE = 4, -}; - -enum vehicle_ap_power_state_shutdown_param { - /** AP should shutdown immediately. Postponing is not allowed. */ - VEHICLE_AP_POWER_SHUTDOWN_PARAM_SHUTDOWN_IMMEDIATELY = 1, - /** AP can enter deep sleep instead of shutting down completely. */ - VEHICLE_AP_POWER_SHUTDOWN_PARAM_CAN_SLEEP = 2, - /** AP can only shutdown with postponing allowed. */ - VEHICLE_AP_POWER_SHUTDOWN_PARAM_SHUTDOWN_ONLY = 3, -}; - -enum vehicle_ap_power_set_state { - /** - * AP has finished boot up, and can start shutdown if requested by power controller. - */ - VEHICLE_AP_POWER_SET_BOOT_COMPLETE = 0x1, - /** - * AP is entering deep sleep state. How this state is implemented may vary depending on - * each H/W, but AP's power should be kept in this state. - */ - VEHICLE_AP_POWER_SET_DEEP_SLEEP_ENTRY = 0x2, - /** - * AP is exiting from deep sleep state, and is in VEHICLE_AP_POWER_STATE_SHUTDOWN_PREPARE state. - * The power controller may change state to other ON states based on the current state. - */ - VEHICLE_AP_POWER_SET_DEEP_SLEEP_EXIT = 0x3, - /** - * int32_array[1]: Time to postpone shutdown in ms. Maximum value can be 5000 ms. - * If AP needs more time, it will send another POSTPONE message before - * the previous one expires. - */ - VEHICLE_AP_POWER_SET_SHUTDOWN_POSTPONE = 0x4, - /** - * AP is starting shutting down. When system completes shutdown, everything will stop in AP - * as kernel will stop all other contexts. It is responsibility of vehicle HAL or lower level - * to synchronize that state with external power controller. As an example, some kind of ping - * with timeout in power controller can be a solution. - * - * int32_array[1]: Time to turn on AP in secs. Power controller may turn on AP after specified - * time so that AP can run tasks like update. If it is set to 0, there is no - * wake up, and power controller may not necessarily support wake-up. - * If power controller turns on AP due to timer, it should start with - * VEHICLE_AP_POWER_STATE_ON_DISP_OFF state, and after receiving - * VEHICLE_AP_POWER_SET_BOOT_COMPLETE, it shall do state transition to - * VEHICLE_AP_POWER_STATE_SHUTDOWN_PREPARE. - */ - VEHICLE_AP_POWER_SET_SHUTDOWN_START = 0x5, - /** - * User has requested to turn off headunit's display, which is detected in android side. - * The power controller may change the power state to VEHICLE_AP_POWER_STATE_ON_DISP_OFF. - */ - VEHICLE_AP_POWER_SET_DISPLAY_OFF = 0x6, - /** - * User has requested to turn on headunit's display, most probably from power key input which - * is attached to headunit. The power controller may change the power state to - * VEHICLE_AP_POWER_STATE_ON_FULL. - */ - VEHICLE_AP_POWER_SET_DISPLAY_ON = 0x7, -}; - -/** - * Property to represent brightness of the display. Some cars have single control for - * the brightness of all displays and this property is to share change in that control. - * - * If this is writable, android side can set this value when user changes display brightness - * from Settings. If this is read only, user may still change display brightness from Settings, - * but that will not be reflected to other displays. - * - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ|VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32 - */ -#define VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS (0x00000A01) - - -/** - * Index in int32_array for VEHICLE_PROPERTY_AP_POWER_STATE property. - */ -enum vehicle_ap_power_state_index { - VEHICLE_AP_POWER_STATE_INDEX_STATE = 0, - VEHICLE_AP_POWER_STATE_INDEX_ADDITIONAL = 1, -}; - -/** -* Property to report bootup reason for the current power on. This is a static property that will -* not change for the whole duration until power off. For example, even if user presses power on -* button after automatic power on with door unlock, bootup reason should stay with -* VEHICLE_AP_POWER_BOOTUP_REASON_USER_UNLOCK. -* -* int32_value should be vehicle_ap_power_bootup_reason. -* -* @value_type VEHICLE_VALUE_TYPE_INT32 -* @change_mode VEHICLE_PROP_CHANGE_MODE_STATIC -* @access VEHICLE_PROP_ACCESS_READ -* @data_member int32_value -*/ -#define VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON (0x00000A02) - -/** - * Enum to represent bootup reason. - */ -enum vehicle_ap_power_bootup_reason { - /** - * Power on due to user's pressing of power key or rotating of ignition switch. - */ - VEHICLE_AP_POWER_BOOTUP_REASON_USER_POWER_ON = 0, - /** - * Automatic power on triggered by door unlock or any other kind of automatic user detection. - */ - VEHICLE_AP_POWER_BOOTUP_REASON_USER_UNLOCK = 1, - /** - * Automatic power on triggered by timer. This only happens when AP has asked wake-up after - * certain time through time specified in VEHICLE_AP_POWER_SET_SHUTDOWN_START. - */ - VEHICLE_AP_POWER_BOOTUP_REASON_TIMER = 2, -}; - - -/** - * Property to feed H/W input events to android - * - * int32_array[0] : action defined by vehicle_hw_key_input_action - * int32_array[1] : key code, should use standard android key code - * int32_array[2] : target display defined in vehicle_display. Events not tied - * to specific display should be sent to DISPLAY_MAIN. - * int32_array[3] : reserved for now. should be zero - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC4 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ - * @config_flags - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_HW_KEY_INPUT (0x00000A10) - -enum vehicle_hw_key_input_action { - /** Key down */ - VEHICLE_HW_KEY_INPUT_ACTION_DOWN = 0, - /** Key up */ - VEHICLE_HW_KEY_INPUT_ACTION_UP = 1, -}; - -enum vehicle_display { - /** center console */ - VEHICLE_DISPLAY_MAIN = 0, - VEHICLE_DISPLAY_INSTRUMENT_CLUSTER = 1, -}; - -/** - * Property to define instrument cluster information. - * For CLUSTER_TYPE_EXTERNAL_DISPLAY: - * READ: - * int32_array[0] : The current screen mode index. Screen mode is defined - * as a configuration in car service and represents which - * area of screen is renderable. - * int32_array[1] : Android can render to instrument cluster (=1) or not(=0). When this is 0, - * instrument cluster may be rendering some information in the area - * allocated for android and android side rendering is invisible. * - * int32_array[2..3] : should be zero - * WRITE from android: - * int32_array[0] : Preferred mode for android side. Depending on the app rendering to instrument - * cluster, preferred mode can change. Instrument cluster still needs to send - * event with new mode to trigger actual mode change. - * int32_array[1] : The current app context relevant for instrument cluster. Use the same flag - * with vehicle_audio_context_flag but this context represents active apps, not - * active audio. Instrument cluster side may change mode depending on the - * currently active contexts. - * int32_array[2..3] : should be zero - * When system boots up, Android side will write {0, 0, 0, 0} when it is ready to render to - * instrument cluster. Before this message, rendering from android should not be visible in the - * cluster. - * @value_type VEHICLE_VALUE_TYPE_INT32_VEC4 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @config_array 0:vehicle_instument_cluster_type 1:hw type - * @data_member int32_array - */ -#define VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO (0x00000A20) - -/** - * Represents instrument cluster type available in system - */ -enum vehicle_instument_cluster_type { - /** Android has no access to instument cluster */ - VEHICLE_INSTRUMENT_CLUSTER_TYPE_NONE = 0, - /** - * Instrument cluster can communicate through vehicle hal with additional - * properties to exchange meta-data - */ - VEHICLE_INSTRUMENT_CLUSTER_TYPE_HAL_INTERFACE = 1, - /** - * Instrument cluster is external display where android can render contents - */ - VEHICLE_INSTRUMENT_CLUSTER_TYPE_EXTERNAL_DISPLAY = 2, -}; - -/** - * Current date and time, encoded as Unix time. - * This value denotes the number of seconds that have elapsed since 1/1/1970. - * - * @value_type VEHICLE_VALUE_TYPE_INT64 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_SET - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int64_value - * @unit VEHICLE_UNIT_TYPE_SECS - */ -#define VEHICLE_PROPERTY_UNIX_TIME (0x00000A30) - -/** - * Current time only. - * Some vehicles may not keep track of date. This property only affects the current time, in - * seconds during the day. Thus, the max value for this parameter is 86,400 (24 * 60 * 60) - * - * @value_type VEHICLE_VALUE_TYPE_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_SET - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_value - * @unit VEHICLE_UNIT_TYPE_SECS - */ -#define VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS (0x00000A31) - - -//==== Car Cabin Properties ==== -/** - * Most Car Cabin properties have both a MOVE and POSITION parameter associated with them. - * - * The MOVE parameter will start moving the device in the indicated direction. The magnitude - * indicates the relative speed. For instance, setting the WINDOW_MOVE parameter to +1 will roll - * the window up. Setting it to +2 (if available) will roll it up faster. - * - * The POSITION parameter will move the device to the desired position. For instance, if the - * WINDOW_POS has a range of 0-100, then setting this parameter to 50 will open the window halfway. - * Depending upon the initial position, the window may move up or down to the 50% value. - * - * OEMs may choose to implement one or both of the MOVE/POSITION parameters depending upon the - * capability of the hardware. - */ - -// Doors -/** - * Door position - * - * This is an integer in case a door may be set to a particular position. Max - * value indicates fully open, min value (0) indicates fully closed. - * - * Some vehicles (minivans) can open the door electronically. Hence, the ability - * to write this property. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_DOOR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_DOOR_POS (0x00000B00) - -/** - * Door move - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_DOOR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_DOOR_MOVE (0x00000B01) - - -/** - * Door lock - * - * 'true' indicates door is locked - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_DOOR - * @data_member boolean_value - */ -#define VEHICLE_PROPERTY_DOOR_LOCK (0x00000B02) - -// Mirrors -/** - * Mirror Z Position - * - * Positive value indicates tilt upwards, negative value is downwards - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_MIRROR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_MIRROR_Z_POS (0x00000B40) - -/** - * Mirror Z Move - * - * Positive value indicates tilt upwards, negative value is downwards - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_MIRROR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_MIRROR_Z_MOVE (0x00000B41) - -/** - * Mirror Y Position - * - * Positive value indicate tilt right, negative value is left - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_MIRROR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_MIRROR_Y_POS (0x00000B42) - -/** - * Mirror Y Move - * - * Positive value indicate tilt right, negative value is left - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_MIRROR - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_MIRROR_Y_MOVE (0x00000B43) - -/** - * Mirror Lock - * - * True indicates mirror positions are locked and not changeable - * - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member boolean_value - */ -#define VEHICLE_PROPERTY_MIRROR_LOCK (0x00000B44) - -/** - * Mirror Fold - * - * True indicates mirrors are folded - * - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member boolean_value - */ -#define VEHICLE_PROPERTY_MIRROR_FOLD (0x00000B45) - -// Seats -/** - * Seat memory select - * - * This parameter selects the memory preset to use to select the seat position. - * The minValue is always 0, and the maxValue determines the number of seat - * positions available. - * - * For instance, if the driver's seat has 3 memory presets, the maxValue will be 3. - * When the user wants to select a preset, the desired preset number (1, 2, or 3) - * is set. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_MEMORY_SELECT (0x00000B80) - -/** - * Seat memory set - * - * This setting allows the user to save the current seat position settings into - * the selected preset slot. The maxValue for each seat position shall match - * the maxValue for VEHICLE_PROPERTY_SEAT_MEMORY_SELECT. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_MEMORY_SET (0x00000B81) - -/** - * Seatbelt buckled - * - * True indicates belt is buckled. - * - * Write access indicates automatic seat buckling capabilities. There are no known cars at this - * time, but you never know... - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member boolean_value - */ -#define VEHICLE_PROPERTY_SEAT_BELT_BUCKLED (0x00000B82) - -/** - * Seatbelt height position - * - * Adjusts the shoulder belt anchor point. - * Max value indicates highest position - * Min value indicates lowest position - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS (0x00000B83) - -/** - * Seatbelt height move - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE (0x00000B84) - -/** - * Seat fore/aft position - * - * Sets the seat position forward (closer to steering wheel) and backwards. - * Max value indicates closest to wheel, min value indicates most rearward - * position. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_FORE_AFT_POS (0x00000B85) - -/** - * Seat fore/aft move - * - * Moves the seat position forward and aft. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE (0x00000B86) - -/** - * Seat backrest angle 1 position - * - * Backrest angle 1 is the actuator closest to the bottom of the seat. - * Max value indicates angling forward towards the steering wheel. - * Min value indicates full recline. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS (0x00000B87) - -/** - * Seat backrest angle 1 move - * - * Moves the backrest forward or recline. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE (0x00000B88) - -/** - * Seat backrest angle 2 position - * - * Backrest angle 2 is the next actuator up from the bottom of the seat. - * Max value indicates angling forward towards the steering wheel. - * Min value indicates full recline. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS (0x00000B89) - -/** - * Seat backrest angle 2 move - * - * Moves the backrest forward or recline. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE (0x00000B8A) - -/** - * Seat height position - * - * Sets the seat height. - * Max value indicates highest position. - * Min value indicates lowest position. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEIGHT_POS (0x00000B8B) - -/** - * Seat height move - * - * Moves the seat height. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE (0x00000B8C) - -/** - * Seat depth position - * - * Sets the seat depth, distance from back rest to front edge of seat. - * Max value indicates longest depth position. - * Min value indicates shortest position. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_DEPTH_POS (0x00000B8D) - -/** - * Seat depth move - * - * Adjusts the seat depth. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_DEPTH_MOVE (0x00000B8E) - -/** - * Seat tilt position - * - * Sets the seat tilt. - * Max value indicates front edge of seat higher than back edge. - * Min value indicates front edge of seat lower than back edge. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_TILT_POS (0x00000B8F) - -/** - * Seat tilt move - * - * Tilts the seat. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_TILT_MOVE (0x00000B90) - -/** - * Lumber fore/aft position - * - * Pushes the lumbar support forward and backwards - * Max value indicates most forward position. - * Min value indicates most rearward position. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS (0x00000B91) - -/** - * Lumbar fore/aft move - * - * Adjusts the lumbar support. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE (0x00000B92) - -/** - * Lumbar side support position - * - * Sets the amount of lateral lumbar support. - * Max value indicates widest lumbar setting (i.e. least support) - * Min value indicates thinnest lumbar setting. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS (0x00000B93) - -/** - * Lumbar side support move - * - * Adjusts the amount of lateral lumbar support. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE (0x00000B94) - -/** - * Headrest height position - * - * Sets the headrest height. - * Max value indicates tallest setting. - * Min value indicates shortest setting. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS (0x00000B95) - -/** - * Headrest height move - * - * Moves the headrest up and down. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE (0x00000B96) - -/** - * Headrest angle position - * - * Sets the angle of the headrest. - * Max value indicates most upright angle. - * Min value indicates shallowest headrest angle. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS (0x00000B97) - -/** - * Headrest angle move - * - * Adjusts the angle of the headrest - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE (0x00000B98) - -/** - * Headrest fore/aft position - * - * Adjusts the headrest forwards and backwards. - * Max value indicates position closest to front of car. - * Min value indicates position closest to rear of car. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS (0x00000B99) - -/** - * Headrest fore/aft move - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @zone_type VEHICLE_ZONE_TYPE_SEAT - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE (0x00000B9A) - - -// Windows -/** - * Window Position - * - * Max = window up / closed - * Min = window down / open - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_WINDOW_POS (0x00000BC0) - -/** - * Window Move - * - * Max = window up / closed - * Min = window down / open - * Magnitude denotes relative speed. I.e. +2 is faster than +1 in raising the window. - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_WINDOW_MOVE (0x00000BC1) - -/** - * Window Vent Position - * - * This feature is used to control the vent feature on a sunroof. - * - * Max = vent open - * Min = vent closed - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_WINDOW_VENT_POS (0x00000BC2) - -/** - * Window Vent Move - * - * This feature is used to control the vent feature on a sunroof. - * - * Max = vent open - * Min = vent closed - * - * @value_type VEHICLE_VALUE_TYPE_ZONED_INT32 - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE|VEHICLE_PROP_ACCESS_WRITE - * @data_member int32_value - */ -#define VEHICLE_PROPERTY_WINDOW_VENT_MOVE (0x00000BC3) - -/** - * Window Lock - * - * True indicates windows are locked and can't be moved. - * - * @value_type VEHICLE_VALUE_TYPE_BOOLEAN - * @change_mode VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - * @access VEHICLE_PROP_ACCESS_READ_WRITE - * @data_member boolean_value - */ -#define VEHICLE_PROPERTY_WINDOW_LOCK (0x00000BC4) - - - -/** - * H/W specific, non-standard property can be added as necessary. Such property should use - * property number in range of [VEHICLE_PROPERTY_CUSTOM_START, VEHICLE_PROPERTY_CUSTOM_END]. - * Definition of property in this range is completely up to each HAL implementation. - * For such property, it is recommended to fill vehicle_prop_config.config_string with some - * additional information to help debugging. For example, company XYZ's custom extension may - * include config_string of "com.XYZ.some_further_details". - * @range_start - */ -#define VEHICLE_PROPERTY_CUSTOM_START (0x70000000) -/** @range_end */ -#define VEHICLE_PROPERTY_CUSTOM_END (0x73ffffff) - -/** - * Property range allocated for system's internal usage like testing. HAL should never declare - * property in this range. - * @range_start - */ -#define VEHICLE_PROPERTY_INTERNAL_START (0x74000000) -/** - * @range_end - */ -#define VEHICLE_PROPERTY_INTERNAL_END (0x74ffffff) - -/** - * Value types for various properties. - */ -enum vehicle_value_type { - VEHICLE_VALUE_TYPE_SHOUD_NOT_USE = 0x00, // value_type should never set to 0. - VEHICLE_VALUE_TYPE_STRING = 0x01, - VEHICLE_VALUE_TYPE_BYTES = 0x02, - VEHICLE_VALUE_TYPE_BOOLEAN = 0x03, - VEHICLE_VALUE_TYPE_ZONED_BOOLEAN = 0x04, - VEHICLE_VALUE_TYPE_INT64 = 0x05, - VEHICLE_VALUE_TYPE_FLOAT = 0x10, - VEHICLE_VALUE_TYPE_FLOAT_VEC2 = 0x11, - VEHICLE_VALUE_TYPE_FLOAT_VEC3 = 0x12, - VEHICLE_VALUE_TYPE_FLOAT_VEC4 = 0x13, - VEHICLE_VALUE_TYPE_INT32 = 0x20, - VEHICLE_VALUE_TYPE_INT32_VEC2 = 0x21, - VEHICLE_VALUE_TYPE_INT32_VEC3 = 0x22, - VEHICLE_VALUE_TYPE_INT32_VEC4 = 0x23, - VEHICLE_VALUE_TYPE_ZONED_FLOAT = 0x30, - VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2 = 0x31, - VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3 = 0x32, - VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4 = 0x33, - VEHICLE_VALUE_TYPE_ZONED_INT32 = 0x40, - VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2 = 0x41, - VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3 = 0x42, - VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4 = 0x43, -}; - -/** - * Units used for int or float type with no attached enum types. - */ -enum vehicle_unit_type { - VEHICLE_UNIT_TYPE_SHOULD_NOT_USE = 0x00000000, - // speed related items - VEHICLE_UNIT_TYPE_METER_PER_SEC = 0x00000001, - VEHICLE_UNIT_TYPE_RPM = 0x00000002, - VEHICLE_UNIT_TYPE_HZ = 0x00000003, - // kind of ratio - VEHICLE_UNIT_TYPE_PERCENTILE = 0x00000010, - // length - VEHICLE_UNIT_TYPE_MILLIMETER = 0x00000020, - VEHICLE_UNIT_TYPE_METER = 0x00000021, - VEHICLE_UNIT_TYPE_KILOMETER = 0x00000023, - // temperature - VEHICLE_UNIT_TYPE_CELSIUS = 0x00000030, - VEHICLE_UNIT_TYPE_FAHRENHEIT = 0x00000031, - VEHICLE_UNIT_TYPE_KELVIN = 0x00000032, - // volume - VEHICLE_UNIT_TYPE_MILLILITER = 0x00000040, - // time - VEHICLE_UNIT_TYPE_NANO_SECS = 0x00000050, - VEHICLE_UNIT_TYPE_SECS = 0x00000053, - VEHICLE_UNIT_TYPE_YEAR = 0x00000059, -}; - -/** - * This describes how value of property can change. - */ -enum vehicle_prop_change_mode { - /** - * Property of this type will *never* change. This property will not support subscription, but - * will support get - */ - VEHICLE_PROP_CHANGE_MODE_STATIC = 0x00, - /** - * Property of this type will be reported when there is a change. - * get call should return the current value. - * Set operation for this property is assumed to be asynchronous. When the property is read - * (get) after set, it may still return old value until underlying H/W backing this property - * has actually changed the state. Once state is changed, the property will dispatch changed - * value as event. - */ - VEHICLE_PROP_CHANGE_MODE_ON_CHANGE = 0x01, - /** - * Property of this type change continuously and requires fixed rate of sampling to retrieve - * the data. - */ - VEHICLE_PROP_CHANGE_MODE_CONTINUOUS = 0x02, - /** - * Property of this type may be polled to get the current value. - */ - VEHICLE_PROP_CHANGE_MODE_POLL = 0x03, - /** - * This is for property where change event should be sent only when the value is - * set from external component. Normal value change will not trigger event. - * For example, clock property can send change event only when it is set, outside android, - * for case like user setting time or time getting update. There is no need to send it - * per every value change. - */ - VEHICLE_PROP_CHANGE_MODE_ON_SET = 0x04, -}; - -/** - * Property config defines the capabilities of it. User of the API - * should first get the property config to understand the output from get() - * commands and also to ensure that set() or events commands are in sync with - * the expected output. - */ -enum vehicle_prop_access { - VEHICLE_PROP_ACCESS_READ = 0x01, - VEHICLE_PROP_ACCESS_WRITE = 0x02, - VEHICLE_PROP_ACCESS_READ_WRITE = 0x03 -}; - -/** - * These permissions define how the OEMs want to distribute their information and security they - * want to apply. On top of these restrictions, android will have additional - * 'app-level' permissions that the apps will need to ask the user before the apps have the - * information. - * This information should be kept in vehicle_prop_config.permission_model. - */ -enum vehicle_permission_model { - /** - * No special restriction, but each property can still require specific android app-level - * permission. - */ - VEHICLE_PERMISSION_NO_RESTRICTION = 0, - /** Signature only. Only APKs signed with OEM keys are allowed. */ - VEHICLE_PERMISSION_OEM_ONLY = 0x1, - /** System only. APKs built-in to system can access the property. */ - VEHICLE_PERMISSION_SYSTEM_APP_ONLY = 0x2, - /** Equivalent to “system|signature” */ - VEHICLE_PERMISSION_OEM_OR_SYSTEM_APP = 0x3 -}; - - -/** - * Special values for INT32/FLOAT (including ZONED types) - * These values represent special state, which is outside MIN/MAX range but can happen. - * For example, HVAC temperature may use out of range min / max to represent that - * it is working in full power although target temperature has separate min / max. - * OUT_OF_RANGE_OFF can represent a state where the property is powered off. - * Usually such property will have separate property to control power. - */ - -#define VEHICLE_INT_OUT_OF_RANGE_MAX (INT32_MAX) -#define VEHICLE_INT_OUT_OF_RANGE_MIN (INT32_MIN) -#define VEHICLE_INT_OUT_OF_RANGE_OFF (INT32_MIN + 1) - -#define VEHICLE_FLOAT_OUT_OF_RANGE_MAX (INFINITY) -#define VEHICLE_FLOAT_OUT_OF_RANGE_MIN (-INFINITY) -#define VEHICLE_FLOAT_OUT_OF_RANGE_OFF (NAN) - -/** - * Car states. - * - * The driving states determine what features of the UI will be accessible. - */ -enum vehicle_driving_status { - VEHICLE_DRIVING_STATUS_UNRESTRICTED = 0x00, - VEHICLE_DRIVING_STATUS_NO_VIDEO = 0x01, - VEHICLE_DRIVING_STATUS_NO_KEYBOARD_INPUT = 0x02, - VEHICLE_DRIVING_STATUS_NO_VOICE_INPUT = 0x04, - VEHICLE_DRIVING_STATUS_NO_CONFIG = 0x08, - VEHICLE_DRIVING_STATUS_LIMIT_MESSAGE_LEN = 0x10 -}; - -/** - * Various gears which can be selected by user and chosen in system. - */ -enum vehicle_gear { - // Gear selections present in both automatic and manual cars. - VEHICLE_GEAR_NEUTRAL = 0x0001, - VEHICLE_GEAR_REVERSE = 0x0002, - - // Gear selections (mostly) present only in automatic cars. - VEHICLE_GEAR_PARK = 0x0004, - VEHICLE_GEAR_DRIVE = 0x0008, - VEHICLE_GEAR_LOW = 0x0010, - - // Other possible gear selections (maybe present in manual or automatic - // cars). - VEHICLE_GEAR_1 = 0x0010, - VEHICLE_GEAR_2 = 0x0020, - VEHICLE_GEAR_3 = 0x0040, - VEHICLE_GEAR_4 = 0x0080, - VEHICLE_GEAR_5 = 0x0100, - VEHICLE_GEAR_6 = 0x0200, - VEHICLE_GEAR_7 = 0x0400, - VEHICLE_GEAR_8 = 0x0800, - VEHICLE_GEAR_9 = 0x1000 -}; - - -/** - * Various zones in the car. - * - * Zones are used for Air Conditioning purposes and divide the car into physical - * area zones. - */ -enum vehicle_zone { - VEHICLE_ZONE_ROW_1_LEFT = 0x00000001, - VEHICLE_ZONE_ROW_1_CENTER = 0x00000002, - VEHICLE_ZONE_ROW_1_RIGHT = 0x00000004, - VEHICLE_ZONE_ROW_1_ALL = 0x00000008, - VEHICLE_ZONE_ROW_2_LEFT = 0x00000010, - VEHICLE_ZONE_ROW_2_CENTER = 0x00000020, - VEHICLE_ZONE_ROW_2_RIGHT = 0x00000040, - VEHICLE_ZONE_ROW_2_ALL = 0x00000080, - VEHICLE_ZONE_ROW_3_LEFT = 0x00000100, - VEHICLE_ZONE_ROW_3_CENTER = 0x00000200, - VEHICLE_ZONE_ROW_3_RIGHT = 0x00000400, - VEHICLE_ZONE_ROW_3_ALL = 0x00000800, - VEHICLE_ZONE_ROW_4_LEFT = 0x00001000, - VEHICLE_ZONE_ROW_4_CENTER = 0x00002000, - VEHICLE_ZONE_ROW_4_RIGHT = 0x00004000, - VEHICLE_ZONE_ROW_4_ALL = 0x00008000, - VEHICLE_ZONE_ALL = 0x80000000, -}; - -/** - * Various Seats in the car. - */ -enum vehicle_seat { - VEHICLE_SEAT_ROW_1_LEFT = 0x0001, - VEHICLE_SEAT_ROW_1_CENTER = 0x0002, - VEHICLE_SEAT_ROW_1_RIGHT = 0x0004, - VEHICLE_SEAT_ROW_2_LEFT = 0x0010, - VEHICLE_SEAT_ROW_2_CENTER = 0x0020, - VEHICLE_SEAT_ROW_2_RIGHT = 0x0040, - VEHICLE_SEAT_ROW_3_LEFT = 0x0100, - VEHICLE_SEAT_ROW_3_CENTER = 0x0200, - VEHICLE_SEAT_ROW_3_RIGHT = 0x0400 -}; - -/** - * Various windshields/windows in the car. - */ -enum vehicle_window { - VEHICLE_WINDOW_FRONT_WINDSHIELD = 0x0001, - VEHICLE_WINDOW_REAR_WINDSHIELD = 0x0002, - VEHICLE_WINDOW_ROOF_TOP = 0x0004, - VEHICLE_WINDOW_ROW_1_LEFT = 0x0010, - VEHICLE_WINDOW_ROW_1_RIGHT = 0x0020, - VEHICLE_WINDOW_ROW_2_LEFT = 0x0100, - VEHICLE_WINDOW_ROW_2_RIGHT = 0x0200, - VEHICLE_WINDOW_ROW_3_LEFT = 0x1000, - VEHICLE_WINDOW_ROW_3_RIGHT = 0x2000, -}; - -enum vehicle_door { - VEHICLE_DOOR_ROW_1_LEFT = 0x00000001, - VEHICLE_DOOR_ROW_1_RIGHT = 0x00000004, - VEHICLE_DOOR_ROW_2_LEFT = 0x00000010, - VEHICLE_DOOR_ROW_2_RIGHT = 0x00000040, - VEHICLE_DOOR_ROW_3_LEFT = 0x00000100, - VEHICLE_DOOR_ROW_3_RIGHT = 0x00000400, - VEHICLE_DOOR_HOOD = 0x10000000, - VEHICLE_DOOR_REAR = 0x20000000, -}; - -enum vehicle_mirror { - VEHICLE_MIRROR_DRIVER_LEFT = 0x00000001, - VEHICLE_MIRROR_DRIVER_RIGHT = 0x00000002, - VEHICLE_MIRROR_DRIVER_CENTER = 0x00000004, -}; - -enum vehicle_turn_signal { - VEHICLE_SIGNAL_NONE = 0x00, - VEHICLE_SIGNAL_RIGHT = 0x01, - VEHICLE_SIGNAL_LEFT = 0x02, - VEHICLE_SIGNAL_EMERGENCY = 0x04 -}; - -enum vehicle_zone_type { - VEHICLE_ZONE_TYPE_NONE = 0x00, - VEHICLE_ZONE_TYPE_ZONE = 0x01, - VEHICLE_ZONE_TYPE_SEAT = 0x02, - VEHICLE_ZONE_TYPE_DOOR = 0x04, - VEHICLE_ZONE_TYPE_WINDOW = 0x10, - VEHICLE_ZONE_TYPE_MIRROR = 0x20, -}; - -/* - * Boolean type. - */ -enum vehicle_boolean { - VEHICLE_FALSE = 0x00, - VEHICLE_TRUE = 0x01 -}; - -typedef int32_t vehicle_boolean_t; - -/** - * Vehicle string. - * - * Defines a UTF8 encoded sequence of bytes that should be used for string - * representation throughout. - */ -typedef struct vehicle_str { - uint8_t* data; - int32_t len; -} vehicle_str_t; - -/** - * Vehicle byte array. - * This is for passing generic raw data. - */ -typedef vehicle_str_t vehicle_bytes_t; - -typedef struct vehicle_prop_config { - int32_t prop; - - /** - * Defines if the property is read or write. Value should be one of - * enum vehicle_prop_access. - */ - int32_t access; - - /** - * Defines if the property is continuous or on-change. Value should be one - * of enum vehicle_prop_change_mode. - */ - int32_t change_mode; - - /** - * Type of data used for this property. This type is fixed per each property. - * Check vehicle_value_type for allowed value. - */ - int32_t value_type; - - /** - * Define necessary permission model to access the data. - */ - int32_t permission_model; - - /** - * Some of the properties may have associated zones (such as hvac), in these - * cases the config should contain an ORed value for the associated zone. - */ - union { - /** - * The value is derived by ORing one or more of enum vehicle_zone members. - */ - int32_t vehicle_zone_flags; - /** The value is derived by ORing one or more of enum vehicle_seat members. */ - int32_t vehicle_seat_flags; - /** The value is derived by ORing one or more of enum vehicle_window members. */ - int32_t vehicle_window_flags; - }; - - /** - * Property specific configuration information. Usage of this will be defined per each property. - */ - union { - /** - * For generic configuration information - */ - int32_t config_flags; - /** The number of presets that are stored by the radio module. Pass 0 if - * there are no presets available. The range of presets is defined to be - * from 1 (see VEHICLE_RADIO_PRESET_MIN_VALUE) to vehicle_radio_num_presets. - */ - int32_t vehicle_radio_num_presets; - int32_t config_array[4]; - }; - - /** - * Some properties may require additional information passed over this string. Most properties - * do not need to set this and in that case, config_string.data should be NULL and - * config_string.len should be 0. - */ - vehicle_str_t config_string; - - /** - * Specify minimum allowed value for the property. This is necessary for property which does - * not have specified enum. - */ - union { - float float_min_value; - int32_t int32_min_value; - int64_t int64_min_value; - }; - - /** - * Specify maximum allowed value for the property. This is necessary for property which does - * not have specified enum. - */ - union { - float float_max_value; - int32_t int32_max_value; - int64_t int64_max_value; - }; - - /** - * Array of min values for zoned properties. Zoned property can specify min / max value in two - * different ways: - * 1. All zones having the same min / max value: *_min/max_value should be set and this - * array should be set to NULL. - * 2. All zones having separate min / max value: *_min/max_values array should be populated - * and its length should be the same as number of active zones specified by *_zone_flags. - * - * Should be NULL if each zone does not have separate max values. - */ - union { - float* float_min_values; - int32_t* int32_min_values; - int64_t* int64_min_values; - }; - - /** - * Array of max values for zoned properties. See above for its usage. - * Should be NULL if each zone does not have separate max values. - * If not NULL, length of array should match that of min_values. - */ - union { - float* float_max_values; - int32_t* int32_max_values; - int64_t* int64_max_values; - }; - - /** - * Min sample rate in Hz. Should be 0 for sensor type of VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - */ - float min_sample_rate; - /** - * Max sample rate in Hz. Should be 0 for sensor type of VEHICLE_PROP_CHANGE_MODE_ON_CHANGE - */ - float max_sample_rate; - /** - * Place holder for putting HAL implementation specific data. Usage is wholly up to HAL - * implementation. - */ - void* hal_data; -} vehicle_prop_config_t; - -/** - * HVAC property fields. - * - * Defines various HVAC properties which are packed into vehicle_hvac_t (see - * below). We define these properties outside in global scope so that HAL - * implementation and HAL users (JNI) can typecast vehicle_hvac correctly. - */ -typedef struct vehicle_hvac { - /** - * Define one structure for each possible HVAC property. - * NOTES: - * a) Fan speed is a number from (0 - 6) where 6 is the highest speed. (TODO define enum) - * b) Temperature is a floating point Celcius scale. - * c) Direction is defined in enum vehicle_fan_direction. - * - * The HAL should create #entries number of vehicle_hvac_properties and - * assign it to "properties" variable below. - */ - union { - int32_t fan_speed; - int32_t fan_direction; - vehicle_boolean_t ac_on; - vehicle_boolean_t max_ac_on; - vehicle_boolean_t max_defrost_on; - vehicle_boolean_t recirc_on; - vehicle_boolean_t dual_on; - vehicle_boolean_t auto_on; - vehicle_boolean_t power_on; - - float temperature_current; - float temperature_set; - - vehicle_boolean_t defrost_on; - }; -} vehicle_hvac_t; - -/* - * Defines how the values for various properties are represented. - * - * There are two ways to populate and access the fields: - * a) Using the individual fields. Use this mechanism (see - * info_manufacture_date, fuel_capacity fields etc). - * b) Using the union accessors (see uint32_value, float_value etc). - * - * To add a new field make sure that it does not exceed the total union size - * (defined in int_array) and it is one of the vehicle_value_type. Then add the - * field name with its unit to union. If the field type is not yet defined (as - * of this draft, we don't use int64_t) then add that type to vehicle_value_type - * and have an accessor (so for int64_t it will be int64_t int64_value). - */ -typedef union vehicle_value { - /** Define the max size of this structure. */ - int32_t int32_array[4]; - float float_array[4]; - - // Easy accessors for union members (HAL implementation SHOULD NOT USE these - // fields while populating, use the property specific fields below instead). - int32_t int32_value; - int64_t int64_value; - float float_value; - vehicle_str_t str_value; - vehicle_bytes_t bytes_value; - vehicle_boolean_t boolean_value; - - // Vehicle Information. - vehicle_str_t info_vin; - vehicle_str_t info_make; - vehicle_str_t info_model; - int32_t info_model_year; - - // Represented in milliliters. - float info_fuel_capacity; - - float vehicle_speed; - float odometer; - - // Engine sensors. - - // Represented in milliliters. - //float engine_coolant_level; - // Represented in celcius. - float engine_coolant_temperature; - // Represented in a percentage value. - //float engine_oil_level; - // Represented in celcius. - float engine_oil_temperature; - float engine_rpm; - - // Event sensors. - // Value should be one of enum vehicle_gear_selection. - int32_t gear_selection; - // Value should be one of enum vehicle_gear. - int32_t gear_current_gear; - // Value should be one of enum vehicle_boolean. - int32_t parking_brake; - // If cruise_set_speed > 0 then cruise is ON otherwise cruise is OFF. - // Unit: meters / second (m/s). - //int32_t cruise_set_speed; - // Value should be one of enum vehicle_boolean. - int32_t is_fuel_level_low; - // Value should be one of enum vehicle_driving_status. - int32_t driving_status; - int32_t night_mode; - // Value should be one of emum vehicle_turn_signal. - int32_t turn_signals; - // Value should be one of enum vehicle_boolean. - //int32_t engine_on; - - // HVAC properties. - vehicle_hvac_t hvac; - - float outside_temperature; - float cabin_temperature; - -} vehicle_value_t; - -/* - * Encapsulates the property name and the associated value. It - * is used across various API calls to set values, get values or to register for - * events. - */ -typedef struct vehicle_prop_value { - /* property identifier */ - int32_t prop; - - /* value type of property for quick conversion from union to appropriate - * value. The value must be one of enum vehicle_value_type. - */ - int32_t value_type; - - /** time is elapsed nanoseconds since boot */ - int64_t timestamp; - - /** - * Zone information for zoned property. For non-zoned property, this should be ignored. - */ - union { - int32_t zone; - int32_t seat; - int32_t window; - }; - - vehicle_value_t value; -} vehicle_prop_value_t; - -/* - * Event callback happens whenever a variable that the API user has subscribed - * to needs to be reported. This may be based purely on threshold and frequency - * (a regular subscription, see subscribe call's arguments) or when the set() - * command is executed and the actual change needs to be reported. - * - * event_data is OWNED by the HAL and should be copied before the callback - * finishes. - */ -typedef int (*vehicle_event_callback_fn)(const vehicle_prop_value_t *event_data); - - -/** - * Represent the operation where the current error has happened. - */ -enum vehicle_property_operation { - /** Generic error to this property which is not tied to any operation. */ - VEHICLE_OPERATION_GENERIC = 0, - /** Error happened while handling property set. */ - VEHICLE_OPERATION_SET = 1, - /** Error happened while handling property get. */ - VEHICLE_OPERATION_GET = 2, - /** Error happened while handling property subscription. */ - VEHICLE_OPERATION_SUBSCRIBE = 3, -}; - -/* - * Suggests that an error condition has occurred. - * - * @param error_code Error code. error_code should be standard error code with - * negative value like -EINVAL. - * @parm property Note a property where error has happened. If this is generic error, property - * should be VEHICLE_PROPERTY_INVALID. - * @param operation Represent the operation where the error has happened. Should be one of - * vehicle_property_operation. - */ -typedef int (*vehicle_error_callback_fn)(int32_t error_code, int32_t property, int32_t operation); - -/************************************************************************************/ - -/* - * 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 vehicle_module { - struct hw_module_t common; -} vehicle_module_t; - - -typedef struct vehicle_hw_device { - struct hw_device_t common; - - /** - * After calling open on device the user should register callbacks for event and error - * functions. - */ - int (*init)(struct vehicle_hw_device* device, - vehicle_event_callback_fn event_fn, vehicle_error_callback_fn err_fn); - /** - * Before calling close the user should destroy the registered callback - * functions. - * In case the unsubscribe() call is not called on all properties before - * release() then release() will unsubscribe the properties itself. - */ - int (*release)(struct vehicle_hw_device* device); - - /** - * Enumerate all available properties. The list is returned in "list". - * @param num_properties number of properties contained in the retuned array. - * @return array of property configs supported by this car. Note that returned data is const - * and caller cannot modify it. HAL implementation should keep this memory until HAL - * is released to avoid copying this again. - */ - vehicle_prop_config_t const *(*list_properties)(struct vehicle_hw_device* device, - int* num_properties); - - /** - * Get a vehicle property value immediately. data should be allocated - * properly. - * The caller of the API OWNS the data field. - * Caller will set data->prop, data->value_type, and optionally zone value for zoned property. - * But HAL implementation needs to fill all entries properly when returning. - * For pointer type, HAL implementation should allocate necessary memory and caller is - * responsible for calling release_memory_from_get, which allows HAL to release allocated - * memory. - * For VEHICLE_PROP_CHANGE_MODE_STATIC type of property, get should return the same value - * always. - * For VEHICLE_PROP_CHANGE_MODE_ON_CHANGE type of property, it should return the latest value. - * If there is no data available yet, which can happen during initial stage, this call should - * return immediately with error code of -EAGAIN. - */ - int (*get)(struct vehicle_hw_device* device, vehicle_prop_value_t *data); - - /** - * Release memory allocated to data in previous get call. get call for byte or string involves - * allocating necessary memory from vehicle hal. - * To be safe, memory allocated by vehicle hal should be released by vehicle hal and vehicle - * network service will call this when data from vehicle hal is no longer necessary. - * vehicle hal implementation should only release member of vehicle_prop_value_t like - * data->str_value.data or data->bytes_value.data but not data itself as data itself is - * allocated from vehicle network service. Once memory is freed, corresponding pointer should - * be set to NULL bu vehicle hal. - */ - void (*release_memory_from_get)(struct vehicle_hw_device* device, vehicle_prop_value_t *data); - - /** - * Set a vehicle property value. data should be allocated properly and not - * NULL. - * The caller of the API OWNS the data field. - * timestamp of data will be ignored for set operation. - * Setting some properties require having initial state available. Depending on the vehicle hal, - * such initial data may not be available for short time after init. In such case, set call - * can return -EAGAIN like get call. - * For a property with separate power control, set can fail if the property is not powered on. - * In such case, hal should return -ESHUTDOWN error. - */ - int (*set)(struct vehicle_hw_device* device, const vehicle_prop_value_t *data); - - /** - * Subscribe to events. - * Depending on output of list_properties if the property is: - * a) on-change: sample_rate should be set to 0. - * b) supports frequency: sample_rate should be set from min_sample_rate to - * max_sample_rate. - * For on-change type of properties, vehicle network service will make another get call to check - * the initial state. Due to this, vehicle hal implementation does not need to send initial - * state for on-change type of properties. - * @param device - * @param prop - * @param sample_rate - * @param zones All subscribed zones for zoned property. can be ignored for non-zoned property. - * 0 means all zones supported instead of no zone. - */ - int (*subscribe)(struct vehicle_hw_device* device, int32_t prop, float sample_rate, - int32_t zones); - - /** Cancel subscription on a property. */ - int (*unsubscribe)(struct vehicle_hw_device* device, int32_t prop); - - /** - * Print out debugging state for the vehicle hal. This will be called by - * the vehicle network service and will be included into the service' dump. - * - * The passed-in file descriptor can be used to write debugging text using - * dprintf() or write(). The text should be in ASCII encoding only. - * - * Performance requirements: - * - * This must be a non-blocking call. The HAL should return from this call - * in 1ms, must return from this call in 10ms. This call must avoid - * deadlocks, as it may be called at any point of operation. - * Any synchronization primitives used (such as mutex locks or semaphores) - * should be acquired with a timeout. - */ - int (*dump)(struct vehicle_hw_device* device, int fd); - -} vehicle_hw_device_t; - -__END_DECLS - -#endif // ANDROID_VEHICLE_INTERFACE_H diff --git a/modules/audio/audio_hw.c b/modules/audio/audio_hw.c index 8f73f118..041cd474 100644 --- a/modules/audio/audio_hw.c +++ b/modules/audio/audio_hw.c @@ -32,6 +32,15 @@ #include <hardware/hardware.h> #include <system/audio.h> +#define STUB_DEFAULT_SAMPLE_RATE 48000 +#define STUB_DEFAULT_AUDIO_FORMAT AUDIO_FORMAT_PCM_16_BIT + +#define STUB_INPUT_BUFFER_MILLISECONDS 20 +#define STUB_INPUT_DEFAULT_CHANNEL_MASK AUDIO_CHANNEL_IN_STEREO + +#define STUB_OUTPUT_BUFFER_MILLISECONDS 10 +#define STUB_OUTPUT_DEFAULT_CHANNEL_MASK AUDIO_CHANNEL_OUT_STEREO + struct stub_audio_device { struct audio_hw_device device; }; @@ -39,46 +48,71 @@ struct stub_audio_device { struct stub_stream_out { struct audio_stream_out stream; int64_t last_write_time_us; + uint32_t sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + size_t frame_count; }; struct stub_stream_in { struct audio_stream_in stream; int64_t last_read_time_us; + uint32_t sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + size_t frame_count; }; static uint32_t out_get_sample_rate(const struct audio_stream *stream) { - return 44100; + const struct stub_stream_out *out = (const struct stub_stream_out *)stream; + + ALOGV("out_get_sample_rate: %u", out->sample_rate); + return out->sample_rate; } static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - ALOGV("out_set_sample_rate: %d", 0); - return -ENOSYS; + struct stub_stream_out *out = (struct stub_stream_out *)stream; + + ALOGV("out_set_sample_rate: %d", rate); + out->sample_rate = rate; + return 0; } static size_t out_get_buffer_size(const struct audio_stream *stream) { - ALOGV("out_get_buffer_size: %d", 4096); - return 4096; + const struct stub_stream_out *out = (const struct stub_stream_out *)stream; + size_t buffer_size = out->frame_count * + audio_stream_out_frame_size(&out->stream); + + ALOGV("out_get_buffer_size: %zu", buffer_size); + return buffer_size; } static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) { - ALOGV("out_get_channels"); - return AUDIO_CHANNEL_OUT_STEREO; + const struct stub_stream_out *out = (const struct stub_stream_out *)stream; + + ALOGV("out_get_channels: %x", out->channel_mask); + return out->channel_mask; } static audio_format_t out_get_format(const struct audio_stream *stream) { - ALOGV("out_get_format"); - return AUDIO_FORMAT_PCM_16_BIT; + const struct stub_stream_out *out = (const struct stub_stream_out *)stream; + + ALOGV("out_get_format: %d", out->format); + return out->format; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { - ALOGV("out_set_format: %d",format); - return -ENOSYS; + struct stub_stream_out *out = (struct stub_stream_out *)stream; + + ALOGV("out_set_format: %d", format); + out->format = format; + return 0; } static int out_standby(struct audio_stream *stream) @@ -109,7 +143,7 @@ static char * out_get_parameters(const struct audio_stream *stream, const char * static uint32_t out_get_latency(const struct audio_stream_out *stream) { ALOGV("out_get_latency"); - return 0; + return STUB_OUTPUT_BUFFER_MILLISECONDS; } static int out_set_volume(struct audio_stream_out *stream, float left, @@ -182,36 +216,54 @@ static int out_get_next_write_timestamp(const struct audio_stream_out *stream, /** audio_stream_in implementation **/ static uint32_t in_get_sample_rate(const struct audio_stream *stream) { - ALOGV("in_get_sample_rate"); - return 8000; + const struct stub_stream_in *in = (const struct stub_stream_in *)stream; + + ALOGV("in_get_sample_rate: %u", in->sample_rate); + return in->sample_rate; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - ALOGV("in_set_sample_rate: %d", rate); - return -ENOSYS; + struct stub_stream_in *in = (struct stub_stream_in *)stream; + + ALOGV("in_set_sample_rate: %u", rate); + in->sample_rate = rate; + return 0; } static size_t in_get_buffer_size(const struct audio_stream *stream) { - ALOGV("in_get_buffer_size: %d", 320); - return 320; + const struct stub_stream_in *in = (const struct stub_stream_in *)stream; + size_t buffer_size = in->frame_count * + audio_stream_in_frame_size(&in->stream); + + ALOGV("in_get_buffer_size: %zu", buffer_size); + return buffer_size; } static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) { - ALOGV("in_get_channels: %d", AUDIO_CHANNEL_IN_MONO); - return AUDIO_CHANNEL_IN_MONO; + const struct stub_stream_in *in = (const struct stub_stream_in *)stream; + + ALOGV("in_get_channels: %x", in->channel_mask); + return in->channel_mask; } static audio_format_t in_get_format(const struct audio_stream *stream) { - return AUDIO_FORMAT_PCM_16_BIT; + const struct stub_stream_in *in = (const struct stub_stream_in *)stream; + + ALOGV("in_get_format: %d", in->format); + return in->format; } static int in_set_format(struct audio_stream *stream, audio_format_t format) { - return -ENOSYS; + struct stub_stream_in *in = (struct stub_stream_in *)stream; + + ALOGV("in_set_format: %d", format); + in->format = format; + return 0; } static int in_standby(struct audio_stream *stream) @@ -290,6 +342,13 @@ static int in_remove_audio_effect(const struct audio_stream *stream, effect_hand return 0; } +static size_t samples_per_milliseconds(size_t milliseconds, + uint32_t sample_rate, + size_t channel_count) +{ + return milliseconds * sample_rate * channel_count / 1000; +} + static int adev_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, @@ -323,7 +382,22 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; out->stream.get_next_write_timestamp = out_get_next_write_timestamp; - + out->sample_rate = config->sample_rate; + if (out->sample_rate == 0) + out->sample_rate = STUB_DEFAULT_SAMPLE_RATE; + out->channel_mask = config->channel_mask; + if (out->channel_mask == AUDIO_CHANNEL_NONE) + out->channel_mask = STUB_OUTPUT_DEFAULT_CHANNEL_MASK; + out->format = config->format; + if (out->format == AUDIO_FORMAT_DEFAULT) + out->format = STUB_DEFAULT_AUDIO_FORMAT; + out->frame_count = samples_per_milliseconds( + STUB_OUTPUT_BUFFER_MILLISECONDS, + out->sample_rate, 1); + + ALOGV("adev_open_output_stream: sample_rate: %u, channels: %x, format: %d," + " frames: %zu", out->sample_rate, out->channel_mask, out->format, + out->frame_count); *stream_out = &out->stream; return 0; } @@ -405,8 +479,21 @@ static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, const struct audio_config *config) { - ALOGV("adev_get_input_buffer_size: %d", 320); - return 320; + size_t buffer_size = samples_per_milliseconds( + STUB_INPUT_BUFFER_MILLISECONDS, + config->sample_rate, + audio_channel_count_from_in_mask( + config->channel_mask)); + + if (!audio_has_proportional_frames(config->format)) { + // Since the audio data is not proportional choose an arbitrary size for + // the buffer. + buffer_size *= 4; + } else { + buffer_size *= audio_bytes_per_sample(config->format); + } + ALOGV("adev_get_input_buffer_size: %zu", buffer_size); + return buffer_size; } static int adev_open_input_stream(struct audio_hw_device *dev, @@ -440,7 +527,21 @@ static int adev_open_input_stream(struct audio_hw_device *dev, in->stream.set_gain = in_set_gain; in->stream.read = in_read; in->stream.get_input_frames_lost = in_get_input_frames_lost; - + in->sample_rate = config->sample_rate; + if (in->sample_rate == 0) + in->sample_rate = STUB_DEFAULT_SAMPLE_RATE; + in->channel_mask = config->channel_mask; + if (in->channel_mask == AUDIO_CHANNEL_NONE) + in->channel_mask = STUB_INPUT_DEFAULT_CHANNEL_MASK; + in->format = config->format; + if (in->format == AUDIO_FORMAT_DEFAULT) + in->format = STUB_DEFAULT_AUDIO_FORMAT; + in->frame_count = samples_per_milliseconds( + STUB_INPUT_BUFFER_MILLISECONDS, in->sample_rate, 1); + + ALOGV("adev_open_input_stream: sample_rate: %u, channels: %x, format: %d," + "frames: %zu", in->sample_rate, in->channel_mask, in->format, + in->frame_count); *stream_in = &in->stream; return 0; } diff --git a/modules/audio_remote_submix/audio_hw.cpp b/modules/audio_remote_submix/audio_hw.cpp index eb6ae929..8c0c0971 100644 --- a/modules/audio_remote_submix/audio_hw.cpp +++ b/modules/audio_remote_submix/audio_hw.cpp @@ -52,9 +52,9 @@ extern "C" { namespace android { -// Set to 1 to enable extremely verbose logging in this module. -#define SUBMIX_VERBOSE_LOGGING 0 -#if SUBMIX_VERBOSE_LOGGING +// Uncomment to enable extremely verbose logging in this module. +// #define SUBMIX_VERBOSE_LOGGING +#if defined(SUBMIX_VERBOSE_LOGGING) #define SUBMIX_ALOGV(...) ALOGV(__VA_ARGS__) #define SUBMIX_ALOGE(...) ALOGE(__VA_ARGS__) #else @@ -205,7 +205,7 @@ struct submix_stream_in { int log_fd; #endif // LOG_STREAMS_TO_FILES - volatile int16_t read_error_count; + volatile uint16_t read_error_count; }; // Determine whether the specified sample rate is supported by the submix module. @@ -467,11 +467,9 @@ static void submix_audio_device_release_pipe_l(struct submix_audio_device * cons rsxadev->routes[route_idx].address); if (rsxadev->routes[route_idx].rsxSink != 0) { rsxadev->routes[route_idx].rsxSink.clear(); - rsxadev->routes[route_idx].rsxSink = 0; } if (rsxadev->routes[route_idx].rsxSource != 0) { rsxadev->routes[route_idx].rsxSource.clear(); - rsxadev->routes[route_idx].rsxSource = 0; } memset(rsxadev->routes[route_idx].address, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN); #ifdef ENABLE_RESAMPLING @@ -816,8 +814,8 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, static uint8_t flush_buffer[64]; const size_t flushBufferSizeFrames = sizeof(flush_buffer) / frame_size; size_t frames_to_flush_from_source = frames - availableToWrite; - SUBMIX_ALOGV("out_write(): flushing %d frames from the pipe to avoid blocking", - frames_to_flush_from_source); + SUBMIX_ALOGV("out_write(): flushing %llu frames from the pipe to avoid blocking", + (unsigned long long)frames_to_flush_from_source); while (frames_to_flush_from_source) { const size_t flush_size = min(frames_to_flush_from_source, flushBufferSizeFrames); frames_to_flush_from_source -= flush_size; @@ -898,7 +896,8 @@ static int out_get_presentation_position(const struct audio_stream_out *stream, } SUBMIX_ALOGV("out_get_presentation_position() got frames=%llu timestamp sec=%llu", - frames ? *frames : -1, timestamp ? timestamp->tv_sec : -1); + frames ? (unsigned long long)*frames : -1ULL, + timestamp ? (unsigned long long)timestamp->tv_sec : -1ULL); return ret; } @@ -1541,7 +1540,7 @@ static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, audio_bytes_per_sample(config->format); const size_t buffer_size = max_buffer_period_size_frames * frame_size_in_bytes; SUBMIX_ALOGV("adev_get_input_buffer_size() returns %zu bytes, %zu frames", - buffer_size, buffer_period_size_frames); + buffer_size, max_buffer_period_size_frames); return buffer_size; } return 0; @@ -1692,10 +1691,10 @@ static int adev_dump(const audio_hw_device_t *device, int fd) reinterpret_cast<const uint8_t *>(device) - offsetof(struct submix_audio_device, device)); char msg[100]; - int n = sprintf(msg, "\nReroute submix audio module:\n"); + int n = snprintf(msg, sizeof(msg), "\nReroute submix audio module:\n"); write(fd, &msg, n); for (int i=0 ; i < MAX_ROUTES ; i++) { - n = sprintf(msg, " route[%d] rate in=%d out=%d, addr=[%s]\n", i, + n = snprintf(msg, sizeof(msg), " route[%d] rate in=%d out=%d, addr=[%s]\n", i, rsxadev->routes[i].config.input_sample_rate, rsxadev->routes[i].config.output_sample_rate, rsxadev->routes[i].address); diff --git a/modules/vehicle/Android.bp b/modules/audio_remote_submix/tests/Android.bp index cb0406a9..8e4d42d0 100644 --- a/modules/vehicle/Android.bp +++ b/modules/audio_remote_submix/tests/Android.bp @@ -1,4 +1,4 @@ -// Copyright (C) 2012 The Android Open Source Project +// Copyright (C) 2018 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. @@ -12,23 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_shared { - name: "vehicle.default", +cc_test { + name: "r_submix_tests", + + srcs: ["remote_submix_tests.cpp"], - relative_install_path: "hw", - vendor: true, - srcs: [ - "vehicle.c", - "timeUtil.cpp", - ], - cflags: [ - "-Wall", - "-Werror", - ], - header_libs: ["libhardware_headers"], shared_libs: [ + "libhardware", "liblog", - "libcutils", "libutils", ], + + cflags: ["-Wall", "-Werror", "-O0", "-g",], + + header_libs: ["libaudiohal_headers"], } diff --git a/modules/audio_remote_submix/tests/remote_submix_tests.cpp b/modules/audio_remote_submix/tests/remote_submix_tests.cpp new file mode 100644 index 00000000..2c01c921 --- /dev/null +++ b/modules/audio_remote_submix/tests/remote_submix_tests.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2018 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. + */ + +// To run this test (as root): +// 1) Build it +// 2) adb push to /vendor/bin +// 3) adb shell /vendor/bin/r_submix_tests + +#define LOG_TAG "RemoteSubmixTest" + +#include <memory> + +#include <gtest/gtest.h> +#include <hardware/audio.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +using namespace android; + +static status_t load_audio_interface(const char* if_name, audio_hw_device_t **dev) +{ + const hw_module_t *mod; + int rc; + + rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); + if (rc) { + ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, + AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); + goto out; + } + rc = audio_hw_device_open(mod, dev); + if (rc) { + ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, + AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); + goto out; + } + if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { + ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); + rc = BAD_VALUE; + audio_hw_device_close(*dev); + goto out; + } + return OK; + +out: + *dev = NULL; + return rc; +} + +class RemoteSubmixTest : public testing::Test { + protected: + void SetUp() override; + void TearDown() override; + + void GenerateData(char* buffer, size_t bufferSize); + void OpenInputStream( + const char* address, bool mono, uint32_t sampleRate, audio_stream_in_t** streamIn); + void OpenOutputStream( + const char* address, bool mono, uint32_t sampleRate, audio_stream_out_t** streamOut); + void ReadFromStream(audio_stream_in_t* streamIn, char* buffer, size_t bufferSize); + void VerifyBufferAllZeroes(char* buffer, size_t bufferSize); + void VerifyBufferNotZeroes(char* buffer, size_t bufferSize); + void VerifyOutputInput( + audio_stream_out_t* streamOut, size_t outBufferSize, + audio_stream_in_t* streamIn, size_t inBufferSize, size_t repeats); + void WriteIntoStream(audio_stream_out_t* streamOut, const char* buffer, size_t bufferSize); + void WriteSomethingIntoStream(audio_stream_out_t* streamOut, size_t bufferSize, size_t repeats); + + audio_hw_device_t* mDev; +}; + +void RemoteSubmixTest::SetUp() { + mDev = nullptr; + ASSERT_EQ(OK, load_audio_interface(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, &mDev)); + ASSERT_NE(nullptr, mDev); +} + +void RemoteSubmixTest::TearDown() { + if (mDev != nullptr) { + int status = audio_hw_device_close(mDev); + mDev = nullptr; + ALOGE_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status)); + ASSERT_EQ(0, status); + } +} + +void RemoteSubmixTest::GenerateData(char* buffer, size_t bufferSize) { + for (size_t i = 0; i < bufferSize; ++i) { + buffer[i] = static_cast<char>(i & 0x7f); + } +} + +void RemoteSubmixTest::OpenInputStream( + const char* address, bool mono, uint32_t sampleRate, audio_stream_in_t** streamIn) { + *streamIn = nullptr; + struct audio_config configIn = {}; + configIn.channel_mask = mono ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO; + configIn.sample_rate = sampleRate; + status_t result = mDev->open_input_stream(mDev, + AUDIO_IO_HANDLE_NONE, AUDIO_DEVICE_NONE, &configIn, + streamIn, AUDIO_INPUT_FLAG_NONE, address, AUDIO_SOURCE_DEFAULT); + ASSERT_EQ(OK, result); + ASSERT_NE(nullptr, *streamIn); +} + +void RemoteSubmixTest::OpenOutputStream( + const char* address, bool mono, uint32_t sampleRate, audio_stream_out_t** streamOut) { + *streamOut = nullptr; + struct audio_config configOut = {}; + configOut.channel_mask = mono ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO; + configOut.sample_rate = sampleRate; + status_t result = mDev->open_output_stream(mDev, + AUDIO_IO_HANDLE_NONE, AUDIO_DEVICE_NONE, AUDIO_OUTPUT_FLAG_NONE, + &configOut, streamOut, address); + ASSERT_EQ(OK, result); + ASSERT_NE(nullptr, *streamOut); +} + +void RemoteSubmixTest::ReadFromStream( + audio_stream_in_t* streamIn, char* buffer, size_t bufferSize) { + ssize_t result = streamIn->read(streamIn, buffer, bufferSize); + EXPECT_EQ(bufferSize, static_cast<size_t>(result)); +} + +void RemoteSubmixTest::VerifyBufferAllZeroes(char* buffer, size_t bufferSize) { + for (size_t i = 0; i < bufferSize; ++i) { + if (buffer[i]) { + ADD_FAILURE(); + return; + } + } +} + +void RemoteSubmixTest::VerifyBufferNotZeroes(char* buffer, size_t bufferSize) { + for (size_t i = 0; i < bufferSize; ++i) { + if (buffer[i]) return; + } + ADD_FAILURE(); +} + +void RemoteSubmixTest::VerifyOutputInput( + audio_stream_out_t* streamOut, size_t outBufferSize, + audio_stream_in_t* streamIn, size_t inBufferSize, + size_t repeats) { + std::unique_ptr<char[]> outBuffer(new char[outBufferSize]), inBuffer(new char[inBufferSize]); + GenerateData(outBuffer.get(), outBufferSize); + for (size_t i = 0; i < repeats; ++i) { + WriteIntoStream(streamOut, outBuffer.get(), outBufferSize); + memset(inBuffer.get(), 0, inBufferSize); + ReadFromStream(streamIn, inBuffer.get(), inBufferSize); + if (inBufferSize == outBufferSize) { + ASSERT_EQ(0, memcmp(outBuffer.get(), inBuffer.get(), inBufferSize)); + } else { + VerifyBufferNotZeroes(inBuffer.get(), inBufferSize); + } + } +} + +void RemoteSubmixTest::WriteIntoStream( + audio_stream_out_t* streamOut, const char* buffer, size_t bufferSize) { + ssize_t result = streamOut->write(streamOut, buffer, bufferSize); + EXPECT_EQ(bufferSize, static_cast<size_t>(result)); +} + +void RemoteSubmixTest::WriteSomethingIntoStream( + audio_stream_out_t* streamOut, size_t bufferSize, size_t repeats) { + std::unique_ptr<char[]> buffer(new char[bufferSize]); + GenerateData(buffer.get(), bufferSize); + for (size_t i = 0; i < repeats; ++i) { + WriteIntoStream(streamOut, buffer.get(), bufferSize); + } +} + +TEST_F(RemoteSubmixTest, InitSuccess) { + // SetUp must finish with no assertions. +} + +// Verifies that when no input was opened, writing into an output stream does not block. +TEST_F(RemoteSubmixTest, OutputDoesNotBlockWhenNoInput) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + WriteSomethingIntoStream(streamOut, 1024, 16); + mDev->close_output_stream(mDev, streamOut); +} + +// Verifies that when input is opened but not reading, writing into an output stream does not block. +// !!! Currently does not finish because requires setting a parameter from another thread !!! +// TEST_F(RemoteSubmixTest, OutputDoesNotBlockWhenInputStuck) { +// const char* address = "1"; +// audio_stream_out_t* streamOut; +// OpenOutputStream(address, true /*mono*/, 48000, &streamOut); +// audio_stream_in_t* streamIn; +// OpenInputStream(address, true /*mono*/, 48000, &streamIn); +// WriteSomethingIntoStream(streamOut, 1024, 16); +// mDev->close_input_stream(mDev, streamIn); +// mDev->close_output_stream(mDev, streamOut); +// } + +TEST_F(RemoteSubmixTest, OutputAndInput) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + audio_stream_in_t* streamIn; + OpenInputStream(address, true /*mono*/, 48000, &streamIn); + const size_t bufferSize = 1024; + VerifyOutputInput(streamOut, bufferSize, streamIn, bufferSize, 16); + mDev->close_input_stream(mDev, streamIn); + mDev->close_output_stream(mDev, streamOut); +} + +// Verifies that reading and writing into a closed stream fails gracefully. +TEST_F(RemoteSubmixTest, OutputAndInputAfterClose) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + audio_stream_in_t* streamIn; + OpenInputStream(address, true /*mono*/, 48000, &streamIn); + mDev->close_input_stream(mDev, streamIn); + mDev->close_output_stream(mDev, streamOut); + const size_t bufferSize = 1024; + std::unique_ptr<char[]> buffer(new char[bufferSize]); + memset(buffer.get(), 0, bufferSize); + ASSERT_EQ(0, streamOut->write(streamOut, buffer.get(), bufferSize)); + ASSERT_EQ(static_cast<ssize_t>(bufferSize), streamIn->read(streamIn, buffer.get(), bufferSize)); + VerifyBufferAllZeroes(buffer.get(), bufferSize); +} + +TEST_F(RemoteSubmixTest, PresentationPosition) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + uint64_t frames; + struct timespec timestamp; + EXPECT_EQ(0, streamOut->get_presentation_position(streamOut, &frames, ×tamp)); + EXPECT_EQ(uint64_t{0}, frames); + uint64_t prevFrames = frames; + for (size_t i = 0; i < 16; ++i) { + WriteSomethingIntoStream(streamOut, 1024, 1); + EXPECT_EQ(0, streamOut->get_presentation_position(streamOut, &frames, ×tamp)); + EXPECT_LE(prevFrames, frames); + prevFrames = frames; + } + mDev->close_output_stream(mDev, streamOut); +} + +TEST_F(RemoteSubmixTest, RenderPosition) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + uint32_t frames; + EXPECT_EQ(0, streamOut->get_render_position(streamOut, &frames)); + EXPECT_EQ(0U, frames); + uint32_t prevFrames = frames; + for (size_t i = 0; i < 16; ++i) { + WriteSomethingIntoStream(streamOut, 1024, 1); + EXPECT_EQ(0, streamOut->get_render_position(streamOut, &frames)); + EXPECT_LE(prevFrames, frames); + prevFrames = frames; + } + mDev->close_output_stream(mDev, streamOut); +} + +// This requires ENABLE_CHANNEL_CONVERSION to be set in the HAL module +TEST_F(RemoteSubmixTest, MonoToStereoConversion) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + audio_stream_in_t* streamIn; + OpenInputStream(address, false /*mono*/, 48000, &streamIn); + const size_t bufferSize = 1024; + VerifyOutputInput(streamOut, bufferSize, streamIn, bufferSize * 2, 16); + mDev->close_input_stream(mDev, streamIn); + mDev->close_output_stream(mDev, streamOut); +} + +// This requires ENABLE_CHANNEL_CONVERSION to be set in the HAL module +TEST_F(RemoteSubmixTest, StereoToMonoConversion) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, false /*mono*/, 48000, &streamOut); + audio_stream_in_t* streamIn; + OpenInputStream(address, true /*mono*/, 48000, &streamIn); + const size_t bufferSize = 1024; + VerifyOutputInput(streamOut, bufferSize * 2, streamIn, bufferSize, 16); + mDev->close_input_stream(mDev, streamIn); + mDev->close_output_stream(mDev, streamOut); +} + +// This requires ENABLE_RESAMPLING to be set in the HAL module +TEST_F(RemoteSubmixTest, OutputAndInputResampling) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + audio_stream_in_t* streamIn; + OpenInputStream(address, true /*mono*/, 24000, &streamIn); + const size_t bufferSize = 1024; + VerifyOutputInput(streamOut, bufferSize * 2, streamIn, bufferSize, 16); + mDev->close_input_stream(mDev, streamIn); + mDev->close_output_stream(mDev, streamOut); +} + +// This requires ENABLE_LEGACY_INPUT_OPEN to be set in the HAL module +TEST_F(RemoteSubmixTest, OpenInputMultipleTimes) { + const char* address = "1"; + audio_stream_out_t* streamOut; + OpenOutputStream(address, true /*mono*/, 48000, &streamOut); + const size_t streamInCount = 3; + audio_stream_in_t* streamIn[streamInCount]; + for (size_t i = 0; i < streamInCount; ++i) { + OpenInputStream(address, true /*mono*/, 48000, &streamIn[i]); + } + const size_t bufferSize = 1024; + for (size_t i = 0; i < streamInCount; ++i) { + VerifyOutputInput(streamOut, bufferSize, streamIn[i], bufferSize, 16); + mDev->close_input_stream(mDev, streamIn[i]); + } + mDev->close_output_stream(mDev, streamOut); +} diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk index 05ddf5a9..6ecedaca 100644 --- a/modules/camera/3_4/Android.mk +++ b/modules/camera/3_4/Android.mk @@ -22,21 +22,31 @@ ifeq ($(USE_CAMERA_V4L2_HAL), true) v4l2_shared_libs := \ libbase \ + libchrome \ libcamera_client \ libcamera_metadata \ libcutils \ + libexif \ libhardware \ liblog \ libsync \ libutils \ -v4l2_static_libs := +v4l2_static_libs := \ + libyuv_static \ + libjpeg_static_ndk \ -v4l2_cflags := -fno-short-enums -Wall -Wno-error -Wextra -fvisibility=hidden +v4l2_cflags := -fno-short-enums -Wall -Wno-error -Wextra -fvisibility=hidden -DHAVE_JPEG -v4l2_c_includes := $(call include-path-for, camera) +v4l2_c_includes := $(call include-path-for, camera) \ + external/libyuv/files/include \ v4l2_src_files := \ + arc/cached_frame.cpp \ + arc/exif_utils.cpp \ + arc/frame_buffer.cpp \ + arc/image_processor.cpp \ + arc/jpeg_compressor.cpp \ camera.cpp \ capture_request.cpp \ format_metadata_factory.cpp \ @@ -49,7 +59,6 @@ v4l2_src_files := \ stream_format.cpp \ v4l2_camera.cpp \ v4l2_camera_hal.cpp \ - v4l2_gralloc.cpp \ v4l2_metadata_factory.cpp \ v4l2_wrapper.cpp \ diff --git a/modules/camera/3_4/README.md b/modules/camera/3_4/README.md index 73d0c13b..168ba97f 100644 --- a/modules/camera/3_4/README.md +++ b/modules/camera/3_4/README.md @@ -146,6 +146,3 @@ is underfeatured compared to the ideal/what is possible. * A variety of features are unimplemented: High speed capture, flash torch mode, hotplugging/unplugging. -* The HAL uses BGR for RGBA. Again, the HAL was designed for the Raspberry Pi -camera, which doesn't support RGB, but RGB is a common default format for -graphics stacks. diff --git a/modules/camera/3_4/arc/cached_frame.cpp b/modules/camera/3_4/arc/cached_frame.cpp new file mode 100644 index 00000000..0489be82 --- /dev/null +++ b/modules/camera/3_4/arc/cached_frame.cpp @@ -0,0 +1,218 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "arc/cached_frame.h" + +#include <errno.h> +#include <libyuv.h> + +#include "arc/common.h" +#include "arc/common_types.h" + +namespace arc { + +using android::CameraMetadata; + +CachedFrame::CachedFrame() + : source_frame_(nullptr), + cropped_buffer_capacity_(0), + yu12_frame_(new AllocatedFrameBuffer(0)) {} + +CachedFrame::~CachedFrame() { UnsetSource(); } + +int CachedFrame::SetSource(const FrameBuffer* frame, int rotate_degree) { + source_frame_ = frame; + int res = ConvertToYU12(); + if (res != 0) { + return res; + } + + if (rotate_degree > 0) { + res = CropRotateScale(rotate_degree); + } + return res; +} + +void CachedFrame::UnsetSource() { source_frame_ = nullptr; } + +uint8_t* CachedFrame::GetSourceBuffer() const { + return source_frame_->GetData(); +} + +size_t CachedFrame::GetSourceDataSize() const { + return source_frame_->GetDataSize(); +} + +uint32_t CachedFrame::GetSourceFourCC() const { + return source_frame_->GetFourcc(); +} + +uint8_t* CachedFrame::GetCachedBuffer() const { return yu12_frame_->GetData(); } + +uint32_t CachedFrame::GetCachedFourCC() const { + return yu12_frame_->GetFourcc(); +} + +uint32_t CachedFrame::GetWidth() const { return yu12_frame_->GetWidth(); } + +uint32_t CachedFrame::GetHeight() const { return yu12_frame_->GetHeight(); } + +size_t CachedFrame::GetConvertedSize(int fourcc) const { + return ImageProcessor::GetConvertedSize(fourcc, yu12_frame_->GetWidth(), + yu12_frame_->GetHeight()); +} + +int CachedFrame::Convert(const CameraMetadata& metadata, FrameBuffer* out_frame, + bool video_hack) { + if (video_hack && out_frame->GetFourcc() == V4L2_PIX_FMT_YVU420) { + out_frame->SetFourcc(V4L2_PIX_FMT_YUV420); + } + + FrameBuffer* source_frame = yu12_frame_.get(); + if (GetWidth() != out_frame->GetWidth() || + GetHeight() != out_frame->GetHeight()) { + size_t cache_size = ImageProcessor::GetConvertedSize( + yu12_frame_->GetFourcc(), out_frame->GetWidth(), + out_frame->GetHeight()); + if (cache_size == 0) { + return -EINVAL; + } else if (cache_size > scaled_frame_->GetBufferSize()) { + scaled_frame_.reset(new AllocatedFrameBuffer(cache_size)); + } + scaled_frame_->SetWidth(out_frame->GetWidth()); + scaled_frame_->SetHeight(out_frame->GetHeight()); + ImageProcessor::Scale(*yu12_frame_.get(), scaled_frame_.get()); + + source_frame = scaled_frame_.get(); + } + return ImageProcessor::ConvertFormat(metadata, *source_frame, out_frame); +} + +int CachedFrame::ConvertToYU12() { + size_t cache_size = ImageProcessor::GetConvertedSize( + V4L2_PIX_FMT_YUV420, source_frame_->GetWidth(), + source_frame_->GetHeight()); + if (cache_size == 0) { + return -EINVAL; + } + yu12_frame_->SetDataSize(cache_size); + yu12_frame_->SetFourcc(V4L2_PIX_FMT_YUV420); + yu12_frame_->SetWidth(source_frame_->GetWidth()); + yu12_frame_->SetHeight(source_frame_->GetHeight()); + + int res = ImageProcessor::ConvertFormat(CameraMetadata(), *source_frame_, + yu12_frame_.get()); + if (res) { + LOGF(ERROR) << "Convert from " << FormatToString(source_frame_->GetFourcc()) + << " to YU12 fails."; + return res; + } + return 0; +} + +int CachedFrame::CropRotateScale(int rotate_degree) { + // TODO(henryhsu): Move libyuv part to ImageProcessor. + if (yu12_frame_->GetHeight() % 2 != 0 || yu12_frame_->GetWidth() % 2 != 0) { + LOGF(ERROR) << "yu12_frame_ has odd dimension: " << yu12_frame_->GetWidth() + << "x" << yu12_frame_->GetHeight(); + return -EINVAL; + } + + if (yu12_frame_->GetHeight() > yu12_frame_->GetWidth()) { + LOGF(ERROR) << "yu12_frame_ is tall frame already: " + << yu12_frame_->GetWidth() << "x" << yu12_frame_->GetHeight(); + return -EINVAL; + } + + // Step 1: Crop and rotate + // + // Original frame Cropped frame Rotated frame + // -------------------- -------- + // | | | | | | --------------- + // | | | | | | | | + // | | | | =======>> | | =======>> | | + // | | | | | | --------------- + // | | | | | | + // -------------------- -------- + // + int cropped_width = yu12_frame_->GetHeight() * yu12_frame_->GetHeight() / + yu12_frame_->GetWidth(); + if (cropped_width % 2 == 1) { + // Make cropped_width to the closest even number. + cropped_width++; + } + int cropped_height = yu12_frame_->GetHeight(); + int margin = (yu12_frame_->GetWidth() - cropped_width) / 2; + + int rotated_height = cropped_width; + int rotated_width = cropped_height; + + int rotated_y_stride = rotated_width; + int rotated_uv_stride = rotated_width / 2; + size_t rotated_size = + rotated_y_stride * rotated_height + rotated_uv_stride * rotated_height; + if (rotated_size > cropped_buffer_capacity_) { + cropped_buffer_.reset(new uint8_t[rotated_size]); + cropped_buffer_capacity_ = rotated_size; + } + uint8_t* rotated_y_plane = cropped_buffer_.get(); + uint8_t* rotated_u_plane = + rotated_y_plane + rotated_y_stride * rotated_height; + uint8_t* rotated_v_plane = + rotated_u_plane + rotated_uv_stride * rotated_height / 2; + libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate90; + switch (rotate_degree) { + case 90: + rotation_mode = libyuv::RotationMode::kRotate90; + break; + case 270: + rotation_mode = libyuv::RotationMode::kRotate270; + break; + default: + LOGF(ERROR) << "Invalid rotation degree: " << rotate_degree; + return -EINVAL; + } + // This libyuv method first crops the frame and then rotates it 90 degrees + // clockwise. + int res = libyuv::ConvertToI420( + yu12_frame_->GetData(), yu12_frame_->GetDataSize(), rotated_y_plane, + rotated_y_stride, rotated_u_plane, rotated_uv_stride, rotated_v_plane, + rotated_uv_stride, margin, 0, yu12_frame_->GetWidth(), + yu12_frame_->GetHeight(), cropped_width, cropped_height, rotation_mode, + libyuv::FourCC::FOURCC_I420); + + if (res) { + LOGF(ERROR) << "ConvertToI420 failed: " << res; + return res; + } + + // Step 2: Scale + // + // Final frame + // Rotated frame --------------------- + // -------------- | | + // | | =====>> | | + // | | | | + // -------------- | | + // | | + // --------------------- + // + // + res = libyuv::I420Scale( + rotated_y_plane, rotated_y_stride, rotated_u_plane, rotated_uv_stride, + rotated_v_plane, rotated_uv_stride, rotated_width, rotated_height, + yu12_frame_->GetData(), yu12_frame_->GetWidth(), + yu12_frame_->GetData() + + yu12_frame_->GetWidth() * yu12_frame_->GetHeight(), + yu12_frame_->GetWidth() / 2, + yu12_frame_->GetData() + + yu12_frame_->GetWidth() * yu12_frame_->GetHeight() * 5 / 4, + yu12_frame_->GetWidth() / 2, yu12_frame_->GetWidth(), + yu12_frame_->GetHeight(), libyuv::FilterMode::kFilterNone); + LOGF_IF(ERROR, res) << "I420Scale failed: " << res; + return res; +} + +} // namespace arc diff --git a/modules/camera/3_4/arc/cached_frame.h b/modules/camera/3_4/arc/cached_frame.h new file mode 100644 index 00000000..fbfcb76b --- /dev/null +++ b/modules/camera/3_4/arc/cached_frame.h @@ -0,0 +1,86 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef HAL_USB_CACHED_FRAME_H_ +#define HAL_USB_CACHED_FRAME_H_ + +#include <memory> + +#include <camera/CameraMetadata.h> + +#include "arc/image_processor.h" + +namespace arc { + +// CachedFrame contains a source FrameBuffer and a cached, converted +// FrameBuffer. The incoming frames would be converted to YU12, the default +// format of libyuv, to allow convenient processing. +class CachedFrame { + public: + CachedFrame(); + ~CachedFrame(); + + // SetSource() doesn't take ownership of |frame|. The caller can only release + // |frame| after calling UnsetSource(). SetSource() immediately converts + // incoming frame into YU12. Return non-zero values if it encounters errors. + // If |rotate_degree| is 90 or 270, |frame| will be cropped, rotated by the + // specified amount and scaled. + // If |rotate_degree| is -1, |frame| will not be cropped, rotated, and scaled. + // This function will return an error if |rotate_degree| is not -1, 90, or + // 270. + int SetSource(const FrameBuffer* frame, int rotate_degree); + void UnsetSource(); + + uint8_t* GetSourceBuffer() const; + size_t GetSourceDataSize() const; + uint32_t GetSourceFourCC() const; + uint8_t* GetCachedBuffer() const; + uint32_t GetCachedFourCC() const; + + uint32_t GetWidth() const; + uint32_t GetHeight() const; + + // Calculate the output buffer size when converting to the specified pixel + // format. |fourcc| is defined as V4L2_PIX_FMT_* in linux/videodev2.h. Return + // 0 on error. + size_t GetConvertedSize(int fourcc) const; + + // Caller should fill everything except |data_size| and |fd| of |out_frame|. + // The function will do format conversion and scale to fit |out_frame| + // requirement. + // If |video_hack| is true, it outputs YU12 when |hal_pixel_format| is YV12 + // (swapping U/V planes). Caller should fill |fourcc|, |data|, and + // Return non-zero error code on failure; return 0 on success. + int Convert(const android::CameraMetadata& metadata, FrameBuffer* out_frame, + bool video_hack = false); + + private: + int ConvertToYU12(); + // When we have a landscape mounted camera and the current camera activity is + // portrait, the frames shown in the activity would be stretched. Therefore, + // we want to simulate a native portrait camera. That's why we want to crop, + // rotate |rotate_degree| clockwise and scale the frame. HAL would not change + // CameraInfo.orientation. Instead, framework would fake the + // CameraInfo.orientation. Framework would then tell HAL how much the frame + // needs to rotate clockwise by |rotate_degree|. + int CropRotateScale(int rotate_degree); + + const FrameBuffer* source_frame_; + // const V4L2FrameBuffer* source_frame_; + + // Temporary buffer for cropped and rotated results. + std::unique_ptr<uint8_t[]> cropped_buffer_; + size_t cropped_buffer_capacity_; + + // Cache YU12 decoded results. + std::unique_ptr<AllocatedFrameBuffer> yu12_frame_; + + // Temporary buffer for scaled results. + std::unique_ptr<AllocatedFrameBuffer> scaled_frame_; +}; + +} // namespace arc + +#endif // HAL_USB_CACHED_FRAME_H_ diff --git a/modules/camera/3_4/arc/common.h b/modules/camera/3_4/arc/common.h new file mode 100644 index 00000000..25e4eb58 --- /dev/null +++ b/modules/camera/3_4/arc/common.h @@ -0,0 +1,28 @@ +/* Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef INCLUDE_ARC_COMMON_H_ +#define INCLUDE_ARC_COMMON_H_ + +#include <string> + +#include <base/logging.h> + +#define LOGF(level) LOG(level) << __FUNCTION__ << "(): " +#define LOGFID(level, id) LOG(level) << __FUNCTION__ << "(): id: " << id << ": " +#define LOGF_IF(level, res) LOG_IF(level, res) << __FUNCTION__ << "(): " + +#define VLOGF(level) VLOG(level) << __FUNCTION__ << "(): " +#define VLOGFID(level, id) \ + VLOG(level) << __FUNCTION__ << "(): id: " << id << ": " + +#define VLOGF_ENTER() VLOGF(1) << "enter" +#define VLOGF_EXIT() VLOGF(1) << "exit" + +inline std::string FormatToString(int32_t format) { + return std::string(reinterpret_cast<char*>(&format), 4); +} + +#endif // INCLUDE_ARC_COMMON_H_ diff --git a/modules/camera/3_4/arc/common_types.h b/modules/camera/3_4/arc/common_types.h new file mode 100644 index 00000000..8f62ad6e --- /dev/null +++ b/modules/camera/3_4/arc/common_types.h @@ -0,0 +1,55 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef HAL_USB_COMMON_TYPES_H_ +#define HAL_USB_COMMON_TYPES_H_ + +#include <string> +#include <vector> + +namespace arc { + +struct DeviceInfo { + // ex: /dev/video0 + std::string device_path; + // USB vender id + std::string usb_vid; + // USB product id + std::string usb_pid; + // Some cameras need to wait several frames to output correct images. + uint32_t frames_to_skip_after_streamon; + + // Member definitions can be found in https://developer.android.com/ + // reference/android/hardware/camera2/CameraCharacteristics.html + uint32_t lens_facing; + int32_t sensor_orientation; + float horizontal_view_angle_16_9; + float horizontal_view_angle_4_3; + std::vector<float> lens_info_available_focal_lengths; + float lens_info_minimum_focus_distance; + float lens_info_optimal_focus_distance; + float vertical_view_angle_16_9; + float vertical_view_angle_4_3; +}; + +typedef std::vector<DeviceInfo> DeviceInfos; + +struct SupportedFormat { + uint32_t width; + uint32_t height; + uint32_t fourcc; + // All the supported frame rates in fps with given width, height, and + // pixelformat. This is not sorted. For example, suppose width, height, and + // fourcc are 640x480 YUYV. If frameRates are 15.0 and 30.0, the camera + // supports outputting 640X480 YUYV in 15fps or 30fps. + std::vector<float> frameRates; +}; + +typedef std::vector<SupportedFormat> SupportedFormats; + +} // namespace arc + +#endif // HAL_USB_COMMON_TYPES_H_ diff --git a/modules/camera/3_4/arc/exif_utils.cpp b/modules/camera/3_4/arc/exif_utils.cpp new file mode 100644 index 00000000..aec53c54 --- /dev/null +++ b/modules/camera/3_4/arc/exif_utils.cpp @@ -0,0 +1,513 @@ +/* + * Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "arc/exif_utils.h" + +#include <cstdlib> +#include <ctime> + +#include <libyuv.h> + +#include "arc/common.h" + +namespace std { + +template <> +struct default_delete<ExifEntry> { + inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); } +}; + +} // namespace std + +namespace arc { + +// This comes from the Exif Version 2.3 standard table 9. +const uint8_t gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, + 0x49, 0x0, 0x0, 0x0}; + +static void SetLatitudeOrLongitudeData(unsigned char* data, double num) { + // Take the integer part of |num|. + ExifLong degrees = static_cast<ExifLong>(num); + ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees)); + ExifLong microseconds = + static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0)); + exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1}); + exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {minutes, 1}); + exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {microseconds, 1000000}); +} + +ExifUtils::ExifUtils() + : yu12_buffer_(nullptr), + yu12_width_(0), + yu12_height_(0), + thumbnail_width_(0), + thumbnail_height_(0), + exif_data_(nullptr), + app1_buffer_(nullptr), + app1_length_(0) {} + +ExifUtils::~ExifUtils() { Reset(); } + +bool ExifUtils::Initialize(const uint8_t* buffer, uint16_t width, + uint16_t height, int quality) { + Reset(); + + if (width % 2 != 0 || height % 2 != 0) { + LOGF(ERROR) << "invalid image size " << width << "x" << height; + return false; + } + if (quality < 1 || quality > 100) { + LOGF(ERROR) << "invalid jpeg quality " << quality; + return false; + } + thumbnail_jpeg_quality_ = quality; + yu12_buffer_ = buffer; + yu12_width_ = width; + yu12_height_ = height; + + exif_data_ = exif_data_new(); + if (exif_data_ == nullptr) { + LOGF(ERROR) << "allocate memory for exif_data_ failed"; + return false; + } + // Set the image options. + exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); + exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED); + exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL); + + // Set image width and length. + SetImageWidth(width); + SetImageLength(height); + + return true; +} + +bool ExifUtils::SetMaker(const std::string& maker) { + size_t entrySize = maker.length() + 1; + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, entrySize, entrySize); + if (!entry) { + LOGF(ERROR) << "Adding Make exif entry failed"; + return false; + } + memcpy(entry->data, maker.c_str(), entrySize); + return true; +} + +bool ExifUtils::SetModel(const std::string& model) { + size_t entrySize = model.length() + 1; + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, entrySize, entrySize); + if (!entry) { + LOGF(ERROR) << "Adding Model exif entry failed"; + return false; + } + memcpy(entry->data, model.c_str(), entrySize); + return true; +} + +bool ExifUtils::SetDateTime(const struct tm& t) { + // The length is 20 bytes including NULL for termination in Exif standard. + char str[20]; + int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, + t.tm_min, t.tm_sec); + if (result != sizeof(str) - 1) { + LOGF(WARNING) << "Input time is invalid"; + return false; + } + std::unique_ptr<ExifEntry> entry = + AddVariableLengthEntry(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, + sizeof(str), sizeof(str)); + if (!entry) { + LOGF(ERROR) << "Adding DateTime exif entry failed"; + return false; + } + memcpy(entry->data, str, sizeof(str)); + return true; +} + +bool ExifUtils::SetFocalLength(uint32_t numerator, uint32_t denominator) { + std::unique_ptr<ExifEntry> entry = + AddEntry(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH); + if (!entry) { + LOGF(ERROR) << "Adding FocalLength exif entry failed"; + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, + {numerator, denominator}); + return true; +} + +bool ExifUtils::SetGpsLatitude(double latitude) { + const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF); + std::unique_ptr<ExifEntry> refEntry = + AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2); + if (!refEntry) { + LOGF(ERROR) << "Adding GPSLatitudeRef exif entry failed"; + return false; + } + if (latitude >= 0) { + memcpy(refEntry->data, "N", sizeof("N")); + } else { + memcpy(refEntry->data, "S", sizeof("S")); + latitude *= -1; + } + + const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE); + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + LOGF(ERROR) << "Adding GPSLatitude exif entry failed"; + return false; + } + SetLatitudeOrLongitudeData(entry->data, latitude); + + return true; +} + +bool ExifUtils::SetGpsLongitude(double longitude) { + ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF); + std::unique_ptr<ExifEntry> refEntry = + AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2); + if (!refEntry) { + LOGF(ERROR) << "Adding GPSLongitudeRef exif entry failed"; + return false; + } + if (longitude >= 0) { + memcpy(refEntry->data, "E", sizeof("E")); + } else { + memcpy(refEntry->data, "W", sizeof("W")); + longitude *= -1; + } + + ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE); + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + LOGF(ERROR) << "Adding GPSLongitude exif entry failed"; + return false; + } + SetLatitudeOrLongitudeData(entry->data, longitude); + + return true; +} + +bool ExifUtils::SetGpsAltitude(double altitude) { + ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF); + std::unique_ptr<ExifEntry> refEntry = + AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1); + if (!refEntry) { + LOGF(ERROR) << "Adding GPSAltitudeRef exif entry failed"; + return false; + } + if (altitude >= 0) { + *refEntry->data = 0; + } else { + *refEntry->data = 1; + altitude *= -1; + } + + ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE); + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + LOGF(ERROR) << "Adding GPSAltitude exif entry failed"; + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, + {static_cast<ExifLong>(altitude * 1000), 1000}); + + return true; +} + +bool ExifUtils::SetGpsTimestamp(const struct tm& t) { + const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP); + const size_t kGpsDateStampSize = 11; + std::unique_ptr<ExifEntry> entry = + AddVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII, + kGpsDateStampSize, kGpsDateStampSize); + if (!entry) { + LOGF(ERROR) << "Adding GPSDateStamp exif entry failed"; + return false; + } + int result = + snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize, + "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); + if (result != kGpsDateStampSize - 1) { + LOGF(WARNING) << "Input time is invalid"; + return false; + } + + const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP); + entry = AddVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3, + 3 * sizeof(ExifRational)); + if (!entry) { + LOGF(ERROR) << "Adding GPSTimeStamp exif entry failed"; + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, + {static_cast<ExifLong>(t.tm_hour), 1}); + exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {static_cast<ExifLong>(t.tm_min), 1}); + exif_set_rational(entry->data + 2 * sizeof(ExifRational), + EXIF_BYTE_ORDER_INTEL, + {static_cast<ExifLong>(t.tm_sec), 1}); + + return true; +} + +bool ExifUtils::SetGpsProcessingMethod(const std::string& method) { + ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD); + size_t size = sizeof(gExifAsciiPrefix) + method.length(); + std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_UNDEFINED, size, size); + if (!entry) { + LOGF(ERROR) << "Adding GPSProcessingMethod exif entry failed"; + return false; + } + memcpy(entry->data, gExifAsciiPrefix, sizeof(gExifAsciiPrefix)); + // Since the exif format is undefined, NULL termination is not necessary. + memcpy(entry->data + sizeof(gExifAsciiPrefix), method.c_str(), + method.length()); + + return true; +} + +bool ExifUtils::SetThumbnailSize(uint16_t width, uint16_t height) { + if (width % 2 != 0 || height % 2 != 0) { + LOGF(ERROR) << "Invalid thumbnail size " << width << "x" << height; + return false; + } + thumbnail_width_ = width; + thumbnail_height_ = height; + return true; +} + +bool ExifUtils::SetOrientation(uint16_t orientation) { + std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_ORIENTATION); + if (!entry) { + LOGF(ERROR) << "Adding Orientation exif entry failed"; + return false; + } + /* + * Orientation value: + * 1 2 3 4 5 6 7 8 + * + * 888888 888888 88 88 8888888888 88 88 8888888888 + * 88 88 88 88 88 88 88 88 88 88 88 88 + * 8888 8888 8888 8888 88 8888888888 8888888888 88 + * 88 88 88 88 + * 88 88 888888 888888 + */ + int value = 1; + switch (orientation) { + case 90: + value = 6; + break; + case 180: + value = 3; + break; + case 270: + value = 8; + break; + default: + break; + } + exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value); + return true; +} + +bool ExifUtils::GenerateApp1() { + DestroyApp1(); + if (thumbnail_width_ > 0 && thumbnail_height_ > 0) { + if (!GenerateThumbnail()) { + LOGF(ERROR) << "Generate thumbnail image failed"; + return false; + } + exif_data_->data = const_cast<uint8_t*>( + static_cast<const uint8_t*>(compressor_.GetCompressedImagePtr())); + exif_data_->size = compressor_.GetCompressedImageSize(); + } + // Save the result into |app1_buffer_|. + exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_); + if (!app1_length_) { + LOGF(ERROR) << "Allocate memory for app1_buffer_ failed"; + return false; + } + /* + * The JPEG segment size is 16 bits in spec. The size of APP1 segment should + * be smaller than 65533 because there are two bytes for segment size field. + */ + if (app1_length_ > 65533) { + DestroyApp1(); + LOGF(ERROR) << "The size of APP1 segment is too large"; + return false; + } + return true; +} + +const uint8_t* ExifUtils::GetApp1Buffer() { return app1_buffer_; } + +unsigned int ExifUtils::GetApp1Length() { return app1_length_; } + +void ExifUtils::Reset() { + yu12_buffer_ = nullptr; + yu12_width_ = 0; + yu12_height_ = 0; + thumbnail_width_ = 0; + thumbnail_height_ = 0; + DestroyApp1(); + if (exif_data_) { + /* + * Since we decided to ignore the original APP1, we are sure that there is + * no thumbnail allocated by libexif. |exif_data_->data| is actually + * allocated by JpegCompressor. Sets |exif_data_->data| to nullptr to + * prevent exif_data_unref() destroy it incorrectly. + */ + exif_data_->data = nullptr; + exif_data_->size = 0; + exif_data_unref(exif_data_); + exif_data_ = nullptr; + } +} + +std::unique_ptr<ExifEntry> ExifUtils::AddVariableLengthEntry( + ExifIfd ifd, ExifTag tag, ExifFormat format, uint64_t components, + unsigned int size) { + // Remove old entry if exists. + exif_content_remove_entry(exif_data_->ifd[ifd], + exif_content_get_entry(exif_data_->ifd[ifd], tag)); + ExifMem* mem = exif_mem_new_default(); + if (!mem) { + LOGF(ERROR) << "Allocate memory for exif entry failed"; + return nullptr; + } + std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem)); + if (!entry) { + LOGF(ERROR) << "Allocate memory for exif entry failed"; + exif_mem_unref(mem); + return nullptr; + } + void* tmpBuffer = exif_mem_alloc(mem, size); + if (!tmpBuffer) { + LOGF(ERROR) << "Allocate memory for exif entry failed"; + exif_mem_unref(mem); + return nullptr; + } + + entry->data = static_cast<unsigned char*>(tmpBuffer); + entry->tag = tag; + entry->format = format; + entry->components = components; + entry->size = size; + + exif_content_add_entry(exif_data_->ifd[ifd], entry.get()); + exif_mem_unref(mem); + + return entry; +} + +std::unique_ptr<ExifEntry> ExifUtils::AddEntry(ExifIfd ifd, ExifTag tag) { + std::unique_ptr<ExifEntry> entry( + exif_content_get_entry(exif_data_->ifd[ifd], tag)); + if (entry) { + // exif_content_get_entry() won't ref the entry, so we ref here. + exif_entry_ref(entry.get()); + return entry; + } + entry.reset(exif_entry_new()); + if (!entry) { + LOGF(ERROR) << "Allocate memory for exif entry failed"; + return nullptr; + } + entry->tag = tag; + exif_content_add_entry(exif_data_->ifd[ifd], entry.get()); + exif_entry_initialize(entry.get(), tag); + return entry; +} + +bool ExifUtils::SetImageWidth(uint16_t width) { + std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH); + if (!entry) { + LOGF(ERROR) << "Adding ImageWidth exif entry failed"; + return false; + } + exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, width); + return true; +} + +bool ExifUtils::SetImageLength(uint16_t length) { + std::unique_ptr<ExifEntry> entry = + AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH); + if (!entry) { + LOGF(ERROR) << "Adding ImageLength exif entry failed"; + return false; + } + exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, length); + return true; +} + +bool ExifUtils::GenerateThumbnail() { + // Resize yuv image to |thumbnail_width_| x |thumbnail_height_|. + std::vector<uint8_t> scaled_buffer; + if (!GenerateYuvThumbnail(&scaled_buffer)) { + LOGF(ERROR) << "Generate YUV thumbnail failed"; + return false; + } + + // Compress thumbnail to JPEG. + if (!compressor_.CompressImage(scaled_buffer.data(), thumbnail_width_, + thumbnail_height_, thumbnail_jpeg_quality_, + NULL, 0)) { + LOGF(ERROR) << "Compress thumbnail failed"; + return false; + } + return true; +} + +bool ExifUtils::GenerateYuvThumbnail(std::vector<uint8_t>* scaled_buffer) { + size_t y_plane_size = yu12_width_ * yu12_height_; + const uint8* y_plane = yu12_buffer_; + const uint8* u_plane = y_plane + y_plane_size; + const uint8* v_plane = u_plane + y_plane_size / 4; + + size_t scaled_y_plane_size = thumbnail_width_ * thumbnail_height_; + scaled_buffer->resize(scaled_y_plane_size * 3 / 2); + uint8* scaled_y_plane = scaled_buffer->data(); + uint8* scaled_u_plane = scaled_y_plane + scaled_y_plane_size; + uint8* scaled_v_plane = scaled_u_plane + scaled_y_plane_size / 4; + + int result = libyuv::I420Scale( + y_plane, yu12_width_, u_plane, yu12_width_ / 2, v_plane, yu12_width_ / 2, + yu12_width_, yu12_height_, scaled_y_plane, thumbnail_width_, + scaled_u_plane, thumbnail_width_ / 2, scaled_v_plane, + thumbnail_width_ / 2, thumbnail_width_, thumbnail_height_, + libyuv::kFilterNone); + if (result != 0) { + LOGF(ERROR) << "Scale I420 image failed"; + return false; + } + return true; +} + +void ExifUtils::DestroyApp1() { + /* + * Since there is no API to access ExifMem in ExifData->priv, we use free + * here, which is the default free function in libexif. See + * exif_data_save_data() for detail. + */ + free(app1_buffer_); + app1_buffer_ = nullptr; + app1_length_ = 0; +} + +} // namespace arc diff --git a/modules/camera/3_4/arc/exif_utils.h b/modules/camera/3_4/arc/exif_utils.h new file mode 100644 index 00000000..956ee1d2 --- /dev/null +++ b/modules/camera/3_4/arc/exif_utils.h @@ -0,0 +1,178 @@ +/* + * Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef INCLUDE_ARC_EXIF_UTILS_H_ +#define INCLUDE_ARC_EXIF_UTILS_H_ + +#include <cstddef> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +extern "C" { +#include <libexif/exif-data.h> +} + +#include "arc/jpeg_compressor.h" + +namespace arc { + +// ExifUtils can generate APP1 segment with tags which caller set. ExifUtils can +// also add a thumbnail in the APP1 segment if thumbnail size is specified. +// ExifUtils can be reused with different images by calling initialize(). +// +// Example of using this class : +// ExifUtils utils; +// utils.initialize(inputYU12Buffer, inputWidth, inputHeight, +// outputJpegQuality); +// ... +// // Call ExifUtils functions to set Exif tags. +// ... +// utils.generateApp1(); +// unsigned int app1Length = utils.getApp1Length(); +// uint8_t* app1Buffer = new uint8_t[app1Length]; +// memcpy(app1Buffer, utils.getApp1Buffer(), app1Length); +class ExifUtils { + public: + ExifUtils(); + ~ExifUtils(); + + // Sets input YU12 image |buffer| with |width| x |height|. |quality| is the + // compressed JPEG image quality. The caller should not release |buffer| until + // generateApp1() or the destructor is called. initialize() can be called + // multiple times. The setting of Exif tags will be cleared. + bool Initialize(const uint8_t* buffer, uint16_t width, uint16_t height, + int quality); + + // Sets the manufacturer of camera. + // Returns false if memory allocation fails. + bool SetMaker(const std::string& maker); + + // Sets the model number of camera. + // Returns false if memory allocation fails. + bool SetModel(const std::string& model); + + // Sets the date and time of image last modified. It takes local time. The + // name of the tag is DateTime in IFD0. + // Returns false if memory allocation fails. + bool SetDateTime(const struct tm& t); + + // Sets the focal length of lens used to take the image in millimeters. + // Returns false if memory allocation fails. + bool SetFocalLength(uint32_t numerator, uint32_t denominator); + + // Sets the latitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + bool SetGpsLatitude(double latitude); + + // Sets the longitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + bool SetGpsLongitude(double longitude); + + // Sets the altitude in meters. + // Returns false if memory allocation fails. + bool SetGpsAltitude(double altitude); + + // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time. + // Returns false if memory allocation fails. + bool SetGpsTimestamp(const struct tm& t); + + // Sets GPS processing method. + // Returns false if memory allocation fails. + bool SetGpsProcessingMethod(const std::string& method); + + // Since the size of APP1 segment is limited, it is recommended the + // resolution of thumbnail is equal to or smaller than 640x480. If the + // thumbnail is too big, generateApp1() will return false. + // Returns false if |width| or |height| is not even. + bool SetThumbnailSize(uint16_t width, uint16_t height); + + // Sets image orientation. + // Returns false if memory allocation fails. + bool SetOrientation(uint16_t orientation); + + // Generates APP1 segment. + // Returns false if generating APP1 segment fails. + bool GenerateApp1(); + + // Gets buffer of APP1 segment. This method must be called only after calling + // generateAPP1(). + const uint8_t* GetApp1Buffer(); + + // Gets length of APP1 segment. This method must be called only after calling + // generateAPP1(). + unsigned int GetApp1Length(); + + private: + // Resets the pointers and memories. + void Reset(); + + // Adds a variable length tag to |exif_data_|. It will remove the original one + // if the tag exists. + // Returns the entry of the tag. The reference count of returned ExifEntry is + // two. + std::unique_ptr<ExifEntry> AddVariableLengthEntry(ExifIfd ifd, ExifTag tag, + ExifFormat format, + uint64_t components, + unsigned int size); + + // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if + // the tag exists. + // Returns the entry of the tag. It adds one reference count to returned + // ExifEntry. + std::unique_ptr<ExifEntry> AddEntry(ExifIfd ifd, ExifTag tag); + + // Sets the width (number of columes) of main image. + // Returns false if memory allocation fails. + bool SetImageWidth(uint16_t width); + + // Sets the length (number of rows) of main image. + // Returns false if memory allocation fails. + bool SetImageLength(uint16_t length); + + // Generates a thumbnail. Calls compressor_.getCompressedImagePtr() to get the + // result image. + // Returns false if failed. + bool GenerateThumbnail(); + + // Resizes the thumbnail yuv image to |thumbnail_width_| x |thumbnail_height_| + // and stores in |scaled_buffer|. + // Returns false if scale image failed. + bool GenerateYuvThumbnail(std::vector<uint8_t>* scaled_buffer); + + // Destroys the buffer of APP1 segment if exists. + void DestroyApp1(); + + // The buffer pointer of yuv image (YU12). Not owned by this class. + const uint8_t* yu12_buffer_; + // The size of yuv image. + uint16_t yu12_width_; + uint16_t yu12_height_; + + // The size of thumbnail. + uint16_t thumbnail_width_; + uint16_t thumbnail_height_; + + // The Exif data (APP1). Owned by this class. + ExifData* exif_data_; + // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but + // owned by this class. + uint8_t* app1_buffer_; + // The length of |app1_buffer_|. + unsigned int app1_length_; + // The quality of compressed thumbnail image. The size of EXIF thumbnail has + // to be smaller than 64KB. If quality is 100, the size may be bigger than + // 64KB. + int thumbnail_jpeg_quality_; + + // The YU12 to Jpeg compressor. + JpegCompressor compressor_; +}; + +} // namespace arc + +#endif // INCLUDE_ARC_EXIF_UTILS_H_ diff --git a/modules/camera/3_4/arc/frame_buffer.cpp b/modules/camera/3_4/arc/frame_buffer.cpp new file mode 100644 index 00000000..4ae0fe39 --- /dev/null +++ b/modules/camera/3_4/arc/frame_buffer.cpp @@ -0,0 +1,188 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "arc/frame_buffer.h" + +#include <sys/mman.h> + +#include <utility> + +#include "arc/common.h" +#include "arc/image_processor.h" + +namespace arc { + +FrameBuffer::FrameBuffer() + : data_(nullptr), + data_size_(0), + buffer_size_(0), + width_(0), + height_(0), + fourcc_(0) {} + +FrameBuffer::~FrameBuffer() {} + +int FrameBuffer::SetDataSize(size_t data_size) { + if (data_size > buffer_size_) { + LOGF(ERROR) << "Buffer overflow: Buffer only has " << buffer_size_ + << ", but data needs " << data_size; + return -EINVAL; + } + data_size_ = data_size; + return 0; +} + +AllocatedFrameBuffer::AllocatedFrameBuffer(int buffer_size) { + buffer_.reset(new uint8_t[buffer_size]); + buffer_size_ = buffer_size; + data_ = buffer_.get(); +} + +AllocatedFrameBuffer::AllocatedFrameBuffer(uint8_t* buffer, int buffer_size) { + buffer_.reset(buffer); + buffer_size_ = buffer_size; + data_ = buffer_.get(); +} + +AllocatedFrameBuffer::~AllocatedFrameBuffer() {} + +int AllocatedFrameBuffer::SetDataSize(size_t size) { + if (size > buffer_size_) { + buffer_.reset(new uint8_t[size]); + buffer_size_ = size; + data_ = buffer_.get(); + } + data_size_ = size; + return 0; +} + +void AllocatedFrameBuffer::Reset() { memset(data_, 0, buffer_size_); } + +V4L2FrameBuffer::V4L2FrameBuffer(base::ScopedFD fd, int buffer_size, + uint32_t width, uint32_t height, + uint32_t fourcc) + : fd_(std::move(fd)), is_mapped_(false) { + buffer_size_ = buffer_size; + width_ = width; + height_ = height; + fourcc_ = fourcc; +} + +V4L2FrameBuffer::~V4L2FrameBuffer() { + if (Unmap()) { + LOGF(ERROR) << "Unmap failed"; + } +} + +int V4L2FrameBuffer::Map() { + base::AutoLock l(lock_); + if (is_mapped_) { + LOGF(ERROR) << "The buffer is already mapped"; + return -EINVAL; + } + void* addr = mmap(NULL, buffer_size_, PROT_READ, MAP_SHARED, fd_.get(), 0); + if (addr == MAP_FAILED) { + LOGF(ERROR) << "mmap() failed: " << strerror(errno); + return -EINVAL; + } + data_ = static_cast<uint8_t*>(addr); + is_mapped_ = true; + return 0; +} + +int V4L2FrameBuffer::Unmap() { + base::AutoLock l(lock_); + if (is_mapped_ && munmap(data_, buffer_size_)) { + LOGF(ERROR) << "mummap() failed: " << strerror(errno); + return -EINVAL; + } + is_mapped_ = false; + return 0; +} + +GrallocFrameBuffer::GrallocFrameBuffer(buffer_handle_t buffer, uint32_t width, + uint32_t height, uint32_t fourcc, + uint32_t device_buffer_length, + uint32_t stream_usage) + : buffer_(buffer), + is_mapped_(false), + device_buffer_length_(device_buffer_length), + stream_usage_(stream_usage) { + const hw_module_t* module = nullptr; + int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + if (ret || !module) { + LOGF(ERROR) << "Failed to get gralloc module."; + return; + } + gralloc_module_ = reinterpret_cast<const gralloc_module_t*>(module); + width_ = width; + height_ = height; + fourcc_ = fourcc; +} + +GrallocFrameBuffer::~GrallocFrameBuffer() { + if (Unmap()) { + LOGF(ERROR) << "Unmap failed"; + } +} + +int GrallocFrameBuffer::Map() { + base::AutoLock l(lock_); + if (is_mapped_) { + LOGF(ERROR) << "The buffer is already mapped"; + return -EINVAL; + } + + void* addr; + int ret = 0; + switch (fourcc_) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUYV: + android_ycbcr yuv_data; + ret = gralloc_module_->lock_ycbcr(gralloc_module_, buffer_, stream_usage_, + 0, 0, width_, height_, &yuv_data); + addr = yuv_data.y; + break; + case V4L2_PIX_FMT_JPEG: + ret = gralloc_module_->lock(gralloc_module_, buffer_, stream_usage_, 0, 0, + device_buffer_length_, 1, &addr); + break; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + ret = gralloc_module_->lock(gralloc_module_, buffer_, stream_usage_, 0, 0, + width_, height_, &addr); + break; + default: + return -EINVAL; + } + + if (ret) { + LOGF(ERROR) << "Failed to gralloc lock buffer: " << ret; + return ret; + } + + data_ = static_cast<uint8_t*>(addr); + if (fourcc_ == V4L2_PIX_FMT_YVU420 || fourcc_ == V4L2_PIX_FMT_YUV420 || + fourcc_ == V4L2_PIX_FMT_NV21 || fourcc_ == V4L2_PIX_FMT_RGB32 || + fourcc_ == V4L2_PIX_FMT_BGR32) { + buffer_size_ = ImageProcessor::GetConvertedSize(fourcc_, width_, height_); + } + + is_mapped_ = true; + return 0; +} + +int GrallocFrameBuffer::Unmap() { + base::AutoLock l(lock_); + if (is_mapped_ && gralloc_module_->unlock(gralloc_module_, buffer_)) { + LOGF(ERROR) << "Failed to unmap buffer: "; + return -EINVAL; + } + is_mapped_ = false; + return 0; +} + +} // namespace arc diff --git a/modules/camera/3_4/arc/frame_buffer.h b/modules/camera/3_4/arc/frame_buffer.h new file mode 100644 index 00000000..3efeb3ba --- /dev/null +++ b/modules/camera/3_4/arc/frame_buffer.h @@ -0,0 +1,135 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef HAL_USB_FRAME_BUFFER_H_ +#define HAL_USB_FRAME_BUFFER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include <base/files/scoped_file.h> +#include <base/synchronization/lock.h> + +#include <hardware/gralloc.h> + +namespace arc { + +class FrameBuffer { + public: + FrameBuffer(); + virtual ~FrameBuffer(); + + // If mapped successfully, the address will be assigned to |data_| and return + // 0. Otherwise, returns -EINVAL. + virtual int Map() = 0; + + // Unmaps the mapped address. Returns 0 for success. + virtual int Unmap() = 0; + + uint8_t* GetData() const { return data_; } + size_t GetDataSize() const { return data_size_; } + size_t GetBufferSize() const { return buffer_size_; } + uint32_t GetWidth() const { return width_; } + uint32_t GetHeight() const { return height_; } + uint32_t GetFourcc() const { return fourcc_; } + + void SetFourcc(uint32_t fourcc) { fourcc_ = fourcc; } + virtual int SetDataSize(size_t data_size); + + protected: + uint8_t* data_; + + // The number of bytes used in the buffer. + size_t data_size_; + + // The number of bytes allocated in the buffer. + size_t buffer_size_; + + // Frame resolution. + uint32_t width_; + uint32_t height_; + + // This is V4L2_PIX_FMT_* in linux/videodev2.h. + uint32_t fourcc_; +}; + +// AllocatedFrameBuffer is used for the buffer from hal malloc-ed. User should +// be aware to manage the memory. +class AllocatedFrameBuffer : public FrameBuffer { + public: + explicit AllocatedFrameBuffer(int buffer_size); + explicit AllocatedFrameBuffer(uint8_t* buffer, int buffer_size); + ~AllocatedFrameBuffer() override; + + // No-op for the two functions. + int Map() override { return 0; } + int Unmap() override { return 0; } + + void SetWidth(uint32_t width) { width_ = width; } + void SetHeight(uint32_t height) { height_ = height; } + int SetDataSize(size_t data_size) override; + void Reset(); + + private: + std::unique_ptr<uint8_t[]> buffer_; +}; + +// V4L2FrameBuffer is used for the buffer from V4L2CameraDevice. Maps the fd +// in constructor. Unmaps and closes the fd in destructor. +class V4L2FrameBuffer : public FrameBuffer { + public: + V4L2FrameBuffer(base::ScopedFD fd, int buffer_size, uint32_t width, + uint32_t height, uint32_t fourcc); + // Unmaps |data_| and closes |fd_|. + ~V4L2FrameBuffer(); + + int Map() override; + int Unmap() override; + int GetFd() const { return fd_.get(); } + + private: + // File descriptor of V4L2 frame buffer. + base::ScopedFD fd_; + + bool is_mapped_; + + // Lock to guard |is_mapped_|. + base::Lock lock_; +}; + +// GrallocFrameBuffer is used for the buffer from Android framework. Uses +// CameraBufferMapper to lock and unlock the buffer. +class GrallocFrameBuffer : public FrameBuffer { + public: + GrallocFrameBuffer(buffer_handle_t buffer, uint32_t width, uint32_t height, + uint32_t fourcc, uint32_t device_buffer_length, + uint32_t stream_usage); + ~GrallocFrameBuffer(); + + int Map() override; + int Unmap() override; + + private: + // The currently used buffer for |buffer_mapper_| operations. + buffer_handle_t buffer_; + + // Used to import gralloc buffer. + const gralloc_module_t* gralloc_module_; + + bool is_mapped_; + + // Lock to guard |is_mapped_|. + base::Lock lock_; + + // Camera stream and device buffer context. + uint32_t device_buffer_length_; + uint32_t stream_usage_; +}; + +} // namespace arc + +#endif // HAL_USB_FRAME_BUFFER_H_ diff --git a/modules/camera/3_4/arc/image_processor.cpp b/modules/camera/3_4/arc/image_processor.cpp new file mode 100644 index 00000000..f0fee918 --- /dev/null +++ b/modules/camera/3_4/arc/image_processor.cpp @@ -0,0 +1,491 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "arc/image_processor.h" + +#include <errno.h> +#include <libyuv.h> +#include <time.h> + +#include "arc/common.h" +#include "arc/common_types.h" +#include "arc/exif_utils.h" +#include "arc/jpeg_compressor.h" + +namespace arc { + +using android::CameraMetadata; + +/* + * Formats have different names in different header files. Here is the mapping + * table: + * + * android_pixel_format_t videodev2.h FOURCC in libyuv + * ----------------------------------------------------------------------------- + * HAL_PIXEL_FORMAT_YV12 = V4L2_PIX_FMT_YVU420 = FOURCC_YV12 + * HAL_PIXEL_FORMAT_YCrCb_420_SP = V4L2_PIX_FMT_NV21 = FOURCC_NV21 + * HAL_PIXEL_FORMAT_RGBA_8888 = V4L2_PIX_FMT_RGB32 = FOURCC_BGR4 + * HAL_PIXEL_FORMAT_YCbCr_422_I = V4L2_PIX_FMT_YUYV = FOURCC_YUYV + * = FOURCC_YUY2 + * V4L2_PIX_FMT_YUV420 = FOURCC_I420 + * = FOURCC_YU12 + * V4L2_PIX_FMT_MJPEG = FOURCC_MJPG + * + * Camera device generates FOURCC_YUYV and FOURCC_MJPG. + * Preview needs FOURCC_ARGB format. + * Software video encoder needs FOURCC_YU12. + * CTS requires FOURCC_YV12 and FOURCC_NV21 for applications. + * + * Android stride requirement: + * YV12 horizontal stride should be a multiple of 16 pixels. See + * android.graphics.ImageFormat.YV12. + * The stride of ARGB, YU12, and NV21 are always equal to the width. + * + * Conversion Path: + * MJPG/YUYV (from camera) -> YU12 -> ARGB (preview) + * -> NV21 (apps) + * -> YV12 (apps) + * -> YU12 (video encoder) + */ + +// YV12 horizontal stride should be a multiple of 16 pixels for each plane. +// |dst_stride_uv| is the pixel stride of u or v plane. +static int YU12ToYV12(const void* yv12, void* yu12, int width, int height, + int dst_stride_y, int dst_stride_uv); +static int YU12ToNV21(const void* yv12, void* nv21, int width, int height); +static bool ConvertToJpeg(const CameraMetadata& metadata, + const FrameBuffer& in_frame, FrameBuffer* out_frame); +static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils); + +// How precise the float-to-rational conversion for EXIF tags would be. +static const int kRationalPrecision = 10000; + +// Default JPEG quality settings. +static const int DEFAULT_JPEG_QUALITY = 80; + +inline static size_t Align16(size_t value) { return (value + 15) & ~15; } + +size_t ImageProcessor::GetConvertedSize(int fourcc, uint32_t width, + uint32_t height) { + if ((width % 2) || (height % 2)) { + LOGF(ERROR) << "Width or height is not even (" << width << " x " << height + << ")"; + return 0; + } + + switch (fourcc) { + case V4L2_PIX_FMT_YVU420: // YV12 + return Align16(width) * height + Align16(width / 2) * height; + case V4L2_PIX_FMT_YUV420: // YU12 + // Fall-through. + case V4L2_PIX_FMT_NV21: // NV21 + return width * height * 3 / 2; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + return width * height * 4; + default: + LOGF(ERROR) << "Pixel format " << FormatToString(fourcc) + << " is unsupported."; + return 0; + } +} + +bool ImageProcessor::SupportsConversion(uint32_t from_fourcc, + uint32_t to_fourcc) { + switch (from_fourcc) { + case V4L2_PIX_FMT_YUYV: + return (to_fourcc == V4L2_PIX_FMT_YUV420); + case V4L2_PIX_FMT_YUV420: + return ( + to_fourcc == V4L2_PIX_FMT_YUV420 || + to_fourcc == V4L2_PIX_FMT_YVU420 || to_fourcc == V4L2_PIX_FMT_NV21 || + to_fourcc == V4L2_PIX_FMT_RGB32 || to_fourcc == V4L2_PIX_FMT_BGR32 || + to_fourcc == V4L2_PIX_FMT_JPEG); + case V4L2_PIX_FMT_MJPEG: + return (to_fourcc == V4L2_PIX_FMT_YUV420); + default: + return false; + } +} + +int ImageProcessor::ConvertFormat(const CameraMetadata& metadata, + const FrameBuffer& in_frame, + FrameBuffer* out_frame) { + if ((in_frame.GetWidth() % 2) || (in_frame.GetHeight() % 2)) { + LOGF(ERROR) << "Width or height is not even (" << in_frame.GetWidth() + << " x " << in_frame.GetHeight() << ")"; + return -EINVAL; + } + + size_t data_size = GetConvertedSize( + out_frame->GetFourcc(), in_frame.GetWidth(), in_frame.GetHeight()); + + if (out_frame->SetDataSize(data_size)) { + LOGF(ERROR) << "Set data size failed"; + return -EINVAL; + } + + if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUYV) { + switch (out_frame->GetFourcc()) { + case V4L2_PIX_FMT_YUV420: // YU12 + { + int res = libyuv::YUY2ToI420( + in_frame.GetData(), /* src_yuy2 */ + in_frame.GetWidth() * 2, /* src_stride_yuy2 */ + out_frame->GetData(), /* dst_y */ + out_frame->GetWidth(), /* dst_stride_y */ + out_frame->GetData() + + out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */ + out_frame->GetWidth() / 2, /* dst_stride_u */ + out_frame->GetData() + out_frame->GetWidth() * + out_frame->GetHeight() * 5 / + 4, /* dst_v */ + out_frame->GetWidth() / 2, /* dst_stride_v */ + in_frame.GetWidth(), in_frame.GetHeight()); + LOGF_IF(ERROR, res) << "YUY2ToI420() for YU12 returns " << res; + return res ? -EINVAL : 0; + } + default: + LOGF(ERROR) << "Destination pixel format " + << FormatToString(out_frame->GetFourcc()) + << " is unsupported for YUYV source format."; + return -EINVAL; + } + } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420) { + // V4L2_PIX_FMT_YVU420 is YV12. I420 is usually referred to YU12 + // (V4L2_PIX_FMT_YUV420), and YV12 is similar to YU12 except that U/V + // planes are swapped. + switch (out_frame->GetFourcc()) { + case V4L2_PIX_FMT_YVU420: // YV12 + { + int ystride = Align16(in_frame.GetWidth()); + int uvstride = Align16(in_frame.GetWidth() / 2); + int res = YU12ToYV12(in_frame.GetData(), out_frame->GetData(), + in_frame.GetWidth(), in_frame.GetHeight(), ystride, + uvstride); + LOGF_IF(ERROR, res) << "YU12ToYV12() returns " << res; + return res ? -EINVAL : 0; + } + case V4L2_PIX_FMT_YUV420: // YU12 + { + memcpy(out_frame->GetData(), in_frame.GetData(), + in_frame.GetDataSize()); + return 0; + } + case V4L2_PIX_FMT_NV21: // NV21 + { + // TODO(henryhsu): Use libyuv::I420ToNV21. + int res = YU12ToNV21(in_frame.GetData(), out_frame->GetData(), + in_frame.GetWidth(), in_frame.GetHeight()); + LOGF_IF(ERROR, res) << "YU12ToNV21() returns " << res; + return res ? -EINVAL : 0; + } + case V4L2_PIX_FMT_BGR32: { + int res = libyuv::I420ToABGR( + in_frame.GetData(), /* src_y */ + in_frame.GetWidth(), /* src_stride_y */ + in_frame.GetData() + + in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */ + in_frame.GetWidth() / 2, /* src_stride_u */ + in_frame.GetData() + + in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */ + in_frame.GetWidth() / 2, /* src_stride_v */ + out_frame->GetData(), /* dst_abgr */ + out_frame->GetWidth() * 4, /* dst_stride_abgr */ + in_frame.GetWidth(), in_frame.GetHeight()); + LOGF_IF(ERROR, res) << "I420ToABGR() returns " << res; + return res ? -EINVAL : 0; + } + case V4L2_PIX_FMT_RGB32: { + int res = libyuv::I420ToARGB( + in_frame.GetData(), /* src_y */ + in_frame.GetWidth(), /* src_stride_y */ + in_frame.GetData() + + in_frame.GetWidth() * in_frame.GetHeight(), /* src_u */ + in_frame.GetWidth() / 2, /* src_stride_u */ + in_frame.GetData() + + in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, /* src_v */ + in_frame.GetWidth() / 2, /* src_stride_v */ + out_frame->GetData(), /* dst_argb */ + out_frame->GetWidth() * 4, /* dst_stride_argb */ + in_frame.GetWidth(), in_frame.GetHeight()); + LOGF_IF(ERROR, res) << "I420ToARGB() returns " << res; + return res ? -EINVAL : 0; + } + case V4L2_PIX_FMT_JPEG: { + bool res = ConvertToJpeg(metadata, in_frame, out_frame); + LOGF_IF(ERROR, !res) << "ConvertToJpeg() returns " << res; + return res ? -EINVAL : 0; + } + default: + LOGF(ERROR) << "Destination pixel format " + << FormatToString(out_frame->GetFourcc()) + << " is unsupported for YU12 source format."; + return -EINVAL; + } + } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_MJPEG) { + switch (out_frame->GetFourcc()) { + case V4L2_PIX_FMT_YUV420: // YU12 + { + int res = libyuv::MJPGToI420( + in_frame.GetData(), /* sample */ + in_frame.GetDataSize(), /* sample_size */ + out_frame->GetData(), /* dst_y */ + out_frame->GetWidth(), /* dst_stride_y */ + out_frame->GetData() + + out_frame->GetWidth() * out_frame->GetHeight(), /* dst_u */ + out_frame->GetWidth() / 2, /* dst_stride_u */ + out_frame->GetData() + out_frame->GetWidth() * + out_frame->GetHeight() * 5 / + 4, /* dst_v */ + out_frame->GetWidth() / 2, /* dst_stride_v */ + in_frame.GetWidth(), in_frame.GetHeight(), out_frame->GetWidth(), + out_frame->GetHeight()); + LOGF_IF(ERROR, res) << "MJPEGToI420() returns " << res; + return res ? -EINVAL : 0; + } + default: + LOGF(ERROR) << "Destination pixel format " + << FormatToString(out_frame->GetFourcc()) + << " is unsupported for MJPEG source format."; + return -EINVAL; + } + } else { + LOGF(ERROR) << "Convert format doesn't support source format " + << FormatToString(in_frame.GetFourcc()); + return -EINVAL; + } +} + +int ImageProcessor::Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame) { + if (in_frame.GetFourcc() != V4L2_PIX_FMT_YUV420) { + LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc()) + << " is unsupported."; + return -EINVAL; + } + + size_t data_size = GetConvertedSize( + in_frame.GetFourcc(), out_frame->GetWidth(), out_frame->GetHeight()); + + if (out_frame->SetDataSize(data_size)) { + LOGF(ERROR) << "Set data size failed"; + return -EINVAL; + } + out_frame->SetFourcc(in_frame.GetFourcc()); + + VLOGF(1) << "Scale image from " << in_frame.GetWidth() << "x" + << in_frame.GetHeight() << " to " << out_frame->GetWidth() << "x" + << out_frame->GetHeight(); + + int ret = libyuv::I420Scale( + in_frame.GetData(), in_frame.GetWidth(), + in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight(), + in_frame.GetWidth() / 2, + in_frame.GetData() + in_frame.GetWidth() * in_frame.GetHeight() * 5 / 4, + in_frame.GetWidth() / 2, in_frame.GetWidth(), in_frame.GetHeight(), + out_frame->GetData(), out_frame->GetWidth(), + out_frame->GetData() + out_frame->GetWidth() * out_frame->GetHeight(), + out_frame->GetWidth() / 2, + out_frame->GetData() + + out_frame->GetWidth() * out_frame->GetHeight() * 5 / 4, + out_frame->GetWidth() / 2, out_frame->GetWidth(), out_frame->GetHeight(), + libyuv::FilterMode::kFilterNone); + LOGF_IF(ERROR, ret) << "I420Scale failed: " << ret; + return ret; +} + +static int YU12ToYV12(const void* yu12, void* yv12, int width, int height, + int dst_stride_y, int dst_stride_uv) { + if ((width % 2) || (height % 2)) { + LOGF(ERROR) << "Width or height is not even (" << width << " x " << height + << ")"; + return -EINVAL; + } + if (dst_stride_y < width || dst_stride_uv < width / 2) { + LOGF(ERROR) << "Y plane stride (" << dst_stride_y + << ") or U/V plane stride (" << dst_stride_uv + << ") is invalid for width " << width; + return -EINVAL; + } + + const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12); + uint8_t* dst = reinterpret_cast<uint8_t*>(yv12); + const uint8_t* u_src = src + width * height; + uint8_t* u_dst = dst + dst_stride_y * height + dst_stride_uv * height / 2; + const uint8_t* v_src = src + width * height * 5 / 4; + uint8_t* v_dst = dst + dst_stride_y * height; + + return libyuv::I420Copy(src, width, u_src, width / 2, v_src, width / 2, dst, + dst_stride_y, u_dst, dst_stride_uv, v_dst, + dst_stride_uv, width, height); +} + +static int YU12ToNV21(const void* yu12, void* nv21, int width, int height) { + if ((width % 2) || (height % 2)) { + LOGF(ERROR) << "Width or height is not even (" << width << " x " << height + << ")"; + return -EINVAL; + } + + const uint8_t* src = reinterpret_cast<const uint8_t*>(yu12); + uint8_t* dst = reinterpret_cast<uint8_t*>(nv21); + const uint8_t* u_src = src + width * height; + const uint8_t* v_src = src + width * height * 5 / 4; + uint8_t* vu_dst = dst + width * height; + + memcpy(dst, src, width * height); + + for (int i = 0; i < height / 2; i++) { + for (int j = 0; j < width / 2; j++) { + *vu_dst++ = *v_src++; + *vu_dst++ = *u_src++; + } + } + return 0; +} + +static bool ConvertToJpeg(const CameraMetadata& metadata, + const FrameBuffer& in_frame, FrameBuffer* out_frame) { + ExifUtils utils; + int jpeg_quality, thumbnail_jpeg_quality; + camera_metadata_ro_entry entry; + + if (metadata.exists(ANDROID_JPEG_QUALITY)) { + entry = metadata.find(ANDROID_JPEG_QUALITY); + jpeg_quality = entry.data.u8[0]; + } else { + LOGF(ERROR) << "Could not find jpeg quality in metadata, defaulting to " + << DEFAULT_JPEG_QUALITY; + jpeg_quality = DEFAULT_JPEG_QUALITY; + } + if (metadata.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) { + entry = metadata.find(ANDROID_JPEG_THUMBNAIL_QUALITY); + thumbnail_jpeg_quality = entry.data.u8[0]; + } else { + thumbnail_jpeg_quality = jpeg_quality; + } + + if (!utils.Initialize(in_frame.GetData(), in_frame.GetWidth(), + in_frame.GetHeight(), thumbnail_jpeg_quality)) { + LOGF(ERROR) << "ExifUtils initialization failed."; + return false; + } + if (!SetExifTags(metadata, &utils)) { + LOGF(ERROR) << "Setting Exif tags failed."; + return false; + } + if (!utils.GenerateApp1()) { + LOGF(ERROR) << "Generating APP1 segment failed."; + return false; + } + JpegCompressor compressor; + if (!compressor.CompressImage(in_frame.GetData(), in_frame.GetWidth(), + in_frame.GetHeight(), jpeg_quality, + utils.GetApp1Buffer(), utils.GetApp1Length())) { + LOGF(ERROR) << "JPEG image compression failed"; + return false; + } + size_t buffer_length = compressor.GetCompressedImageSize(); + memcpy(out_frame->GetData(), compressor.GetCompressedImagePtr(), + buffer_length); + return true; +} + +static bool SetExifTags(const CameraMetadata& metadata, ExifUtils* utils) { + time_t raw_time = 0; + struct tm time_info; + bool time_available = time(&raw_time) != -1; + localtime_r(&raw_time, &time_info); + if (!utils->SetDateTime(time_info)) { + LOGF(ERROR) << "Setting data time failed."; + return false; + } + + float focal_length; + camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH); + if (entry.count) { + focal_length = entry.data.f[0]; + } else { + LOGF(ERROR) << "Cannot find focal length in metadata."; + return false; + } + if (!utils->SetFocalLength( + static_cast<uint32_t>(focal_length * kRationalPrecision), + kRationalPrecision)) { + LOGF(ERROR) << "Setting focal length failed."; + return false; + } + + if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) { + entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES); + if (entry.count < 3) { + LOGF(ERROR) << "Gps coordinates in metadata is not complete."; + return false; + } + if (!utils->SetGpsLatitude(entry.data.d[0])) { + LOGF(ERROR) << "Setting gps latitude failed."; + return false; + } + if (!utils->SetGpsLongitude(entry.data.d[1])) { + LOGF(ERROR) << "Setting gps longitude failed."; + return false; + } + if (!utils->SetGpsAltitude(entry.data.d[2])) { + LOGF(ERROR) << "Setting gps altitude failed."; + return false; + } + } + + if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) { + entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD); + std::string method_str(reinterpret_cast<const char*>(entry.data.u8)); + if (!utils->SetGpsProcessingMethod(method_str)) { + LOGF(ERROR) << "Setting gps processing method failed."; + return false; + } + } + + if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) { + entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP); + time_t timestamp = static_cast<time_t>(entry.data.i64[0]); + if (gmtime_r(×tamp, &time_info)) { + if (!utils->SetGpsTimestamp(time_info)) { + LOGF(ERROR) << "Setting gps timestamp failed."; + return false; + } + } else { + LOGF(ERROR) << "Time tranformation failed."; + return false; + } + } + + if (metadata.exists(ANDROID_JPEG_ORIENTATION)) { + entry = metadata.find(ANDROID_JPEG_ORIENTATION); + if (!utils->SetOrientation(entry.data.i32[0])) { + LOGF(ERROR) << "Setting orientation failed."; + return false; + } + } + + if (metadata.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) { + entry = metadata.find(ANDROID_JPEG_THUMBNAIL_SIZE); + if (entry.count < 2) { + LOGF(ERROR) << "Thumbnail size in metadata is not complete."; + return false; + } + int thumbnail_width = entry.data.i32[0]; + int thumbnail_height = entry.data.i32[1]; + if (thumbnail_width > 0 && thumbnail_height > 0) { + if (!utils->SetThumbnailSize(static_cast<uint16_t>(thumbnail_width), + static_cast<uint16_t>(thumbnail_height))) { + LOGF(ERROR) << "Setting thumbnail size failed."; + return false; + } + } + } + return true; +} + +} // namespace arc diff --git a/modules/camera/3_4/arc/image_processor.h b/modules/camera/3_4/arc/image_processor.h new file mode 100644 index 00000000..323680ab --- /dev/null +++ b/modules/camera/3_4/arc/image_processor.h @@ -0,0 +1,48 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef HAL_USB_IMAGE_PROCESSOR_H_ +#define HAL_USB_IMAGE_PROCESSOR_H_ + +#include <string> + +// FourCC pixel formats (defined as V4L2_PIX_FMT_*). +#include <linux/videodev2.h> +// Declarations of HAL_PIXEL_FORMAT_XXX. +#include <system/graphics.h> + +#include <camera/CameraMetadata.h> +#include "frame_buffer.h" + +namespace arc { + +// V4L2_PIX_FMT_YVU420(YV12) in ImageProcessor has alignment requirement. +// The stride of Y, U, and V planes should a multiple of 16 pixels. +struct ImageProcessor { + // Calculate the output buffer size when converting to the specified pixel + // format. |fourcc| is defined as V4L2_PIX_FMT_* in linux/videodev2.h. + // Return 0 on error. + static size_t GetConvertedSize(int fourcc, uint32_t width, uint32_t height); + + // Return whether this class supports the provided conversion. + static bool SupportsConversion(uint32_t from_fourcc, uint32_t to_fourcc); + + // Convert format from |in_frame.fourcc| to |out_frame->fourcc|. Caller should + // fill |data|, |buffer_size|, |width|, and |height| of |out_frame|. The + // function will fill |out_frame->data_size|. Return non-zero error code on + // failure; return 0 on success. + static int ConvertFormat(const android::CameraMetadata& metadata, + const FrameBuffer& in_frame, FrameBuffer* out_frame); + + // Scale image size according to |in_frame| and |out_frame|. Only support + // V4L2_PIX_FMT_YUV420 format. Caller should fill |data|, |width|, |height|, + // and |buffer_size| of |out_frame|. The function will fill |data_size| and + // |fourcc| of |out_frame|. + static int Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame); +}; + +} // namespace arc + +#endif // HAL_USB_IMAGE_PROCESSOR_H_ diff --git a/modules/camera/3_4/arc/jpeg_compressor.cpp b/modules/camera/3_4/arc/jpeg_compressor.cpp new file mode 100644 index 00000000..7c61b404 --- /dev/null +++ b/modules/camera/3_4/arc/jpeg_compressor.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "arc/jpeg_compressor.h" + +#include <memory> + +#include <errno.h> + +#include "arc/common.h" + +namespace arc { + +// The destination manager that can access |result_buffer_| in JpegCompressor. +struct destination_mgr { + public: + struct jpeg_destination_mgr mgr; + JpegCompressor* compressor; +}; + +JpegCompressor::JpegCompressor() {} + +JpegCompressor::~JpegCompressor() {} + +bool JpegCompressor::CompressImage(const void* image, int width, int height, + int quality, const void* app1Buffer, + unsigned int app1Size) { + if (width % 8 != 0 || height % 2 != 0) { + LOGF(ERROR) << "Image size can not be handled: " << width << "x" << height; + return false; + } + + result_buffer_.clear(); + if (!Encode(image, width, height, quality, app1Buffer, app1Size)) { + return false; + } + LOGF(INFO) << "Compressed JPEG: " << (width * height * 12) / 8 << "[" << width + << "x" << height << "] -> " << result_buffer_.size() << " bytes"; + return true; +} + +const void* JpegCompressor::GetCompressedImagePtr() { + return result_buffer_.data(); +} + +size_t JpegCompressor::GetCompressedImageSize() { + return result_buffer_.size(); +} + +void JpegCompressor::InitDestination(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->compressor->result_buffer_; + buffer.resize(kBlockSize); + dest->mgr.next_output_byte = &buffer[0]; + dest->mgr.free_in_buffer = buffer.size(); +} + +boolean JpegCompressor::EmptyOutputBuffer(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->compressor->result_buffer_; + size_t oldsize = buffer.size(); + buffer.resize(oldsize + kBlockSize); + dest->mgr.next_output_byte = &buffer[oldsize]; + dest->mgr.free_in_buffer = kBlockSize; + return true; +} + +void JpegCompressor::TerminateDestination(j_compress_ptr cinfo) { + destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest); + std::vector<JOCTET>& buffer = dest->compressor->result_buffer_; + buffer.resize(buffer.size() - dest->mgr.free_in_buffer); +} + +void JpegCompressor::OutputErrorMessage(j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message)(cinfo, buffer); + LOGF(ERROR) << buffer; +} + +bool JpegCompressor::Encode(const void* inYuv, int width, int height, + int jpegQuality, const void* app1Buffer, + unsigned int app1Size) { + jpeg_compress_struct cinfo; + jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + // Override output_message() to print error log with ALOGE(). + cinfo.err->output_message = &OutputErrorMessage; + jpeg_create_compress(&cinfo); + SetJpegDestination(&cinfo); + + SetJpegCompressStruct(width, height, jpegQuality, &cinfo); + jpeg_start_compress(&cinfo, TRUE); + + if (app1Buffer != nullptr && app1Size > 0) { + jpeg_write_marker(&cinfo, JPEG_APP0 + 1, + static_cast<const JOCTET*>(app1Buffer), app1Size); + } + + if (!Compress(&cinfo, static_cast<const uint8_t*>(inYuv))) { + return false; + } + jpeg_finish_compress(&cinfo); + return true; +} + +void JpegCompressor::SetJpegDestination(jpeg_compress_struct* cinfo) { + destination_mgr* dest = + static_cast<struct destination_mgr*>((*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(destination_mgr))); + dest->compressor = this; + dest->mgr.init_destination = &InitDestination; + dest->mgr.empty_output_buffer = &EmptyOutputBuffer; + dest->mgr.term_destination = &TerminateDestination; + cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest); +} + +void JpegCompressor::SetJpegCompressStruct(int width, int height, int quality, + jpeg_compress_struct* cinfo) { + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->input_components = 3; + cinfo->in_color_space = JCS_YCbCr; + jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); + jpeg_set_colorspace(cinfo, JCS_YCbCr); + cinfo->raw_data_in = TRUE; + cinfo->dct_method = JDCT_IFAST; + + // Configure sampling factors. The sampling factor is JPEG subsampling 420 + // because the source format is YUV420. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; +} + +bool JpegCompressor::Compress(jpeg_compress_struct* cinfo, const uint8_t* yuv) { + JSAMPROW y[kCompressBatchSize]; + JSAMPROW cb[kCompressBatchSize / 2]; + JSAMPROW cr[kCompressBatchSize / 2]; + JSAMPARRAY planes[3]{y, cb, cr}; + + size_t y_plane_size = cinfo->image_width * cinfo->image_height; + size_t uv_plane_size = y_plane_size / 4; + uint8_t* y_plane = const_cast<uint8_t*>(yuv); + uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size); + uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size); + std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); + memset(empty.get(), 0, cinfo->image_width); + + while (cinfo->next_scanline < cinfo->image_height) { + for (int i = 0; i < kCompressBatchSize; ++i) { + size_t scanline = cinfo->next_scanline + i; + if (scanline < cinfo->image_height) { + y[i] = y_plane + scanline * cinfo->image_width; + } else { + y[i] = empty.get(); + } + } + // cb, cr only have half scanlines + for (int i = 0; i < kCompressBatchSize / 2; ++i) { + size_t scanline = cinfo->next_scanline / 2 + i; + if (scanline < cinfo->image_height / 2) { + int offset = scanline * (cinfo->image_width / 2); + cb[i] = u_plane + offset; + cr[i] = v_plane + offset; + } else { + cb[i] = cr[i] = empty.get(); + } + } + + int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize); + if (processed != kCompressBatchSize) { + LOGF(ERROR) << "Number of processed lines does not equal input lines."; + return false; + } + } + return true; +} + +} // namespace arc diff --git a/modules/camera/3_4/arc/jpeg_compressor.h b/modules/camera/3_4/arc/jpeg_compressor.h new file mode 100644 index 00000000..378f3cda --- /dev/null +++ b/modules/camera/3_4/arc/jpeg_compressor.h @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef INCLUDE_ARC_JPEG_COMPRESSOR_H_ +#define INCLUDE_ARC_JPEG_COMPRESSOR_H_ + +// We must include cstdio before jpeglib.h. It is a requirement of libjpeg. +#include <cstdio> +#include <string> +#include <vector> + +extern "C" { +#include <jerror.h> +#include <jpeglib.h> +} + +namespace arc { + +// Encapsulates a converter from YU12 to JPEG format. This class is not +// thread-safe. +class JpegCompressor { + public: + JpegCompressor(); + ~JpegCompressor(); + + // Compresses YU12 image to JPEG format. After calling this method, call + // GetCompressedImagePtr() to get the image. |quality| is the resulted jpeg + // image quality. It ranges from 1 (poorest quality) to 100 (highest quality). + // |app1Buffer| is the buffer of APP1 segment (exif) which will be added to + // the compressed image. Returns false if errors occur during compression. + bool CompressImage(const void* image, int width, int height, int quality, + const void* app1Buffer, unsigned int app1Size); + + // Returns the compressed JPEG buffer pointer. This method must be called only + // after calling CompressImage(). + const void* GetCompressedImagePtr(); + + // Returns the compressed JPEG buffer size. This method must be called only + // after calling CompressImage(). + size_t GetCompressedImageSize(); + + private: + // InitDestination(), EmptyOutputBuffer() and TerminateDestination() are + // callback functions to be passed into jpeg library. + static void InitDestination(j_compress_ptr cinfo); + static boolean EmptyOutputBuffer(j_compress_ptr cinfo); + static void TerminateDestination(j_compress_ptr cinfo); + static void OutputErrorMessage(j_common_ptr cinfo); + + // Returns false if errors occur. + bool Encode(const void* inYuv, int width, int height, int jpegQuality, + const void* app1Buffer, unsigned int app1Size); + void SetJpegDestination(jpeg_compress_struct* cinfo); + void SetJpegCompressStruct(int width, int height, int quality, + jpeg_compress_struct* cinfo); + // Returns false if errors occur. + bool Compress(jpeg_compress_struct* cinfo, const uint8_t* yuv); + + // The block size for encoded jpeg image buffer. + static const int kBlockSize = 16384; + // Process 16 lines of Y and 16 lines of U/V each time. + // We must pass at least 16 scanlines according to libjpeg documentation. + static const int kCompressBatchSize = 16; + + // The buffer that holds the compressed result. + std::vector<JOCTET> result_buffer_; +}; + +} // namespace arc + +#endif // INCLUDE_ARC_JPEG_COMPRESSOR_H_ diff --git a/modules/camera/3_4/camera.cpp b/modules/camera/3_4/camera.cpp index 83905ea8..79dca0b8 100644 --- a/modules/camera/3_4/camera.cpp +++ b/modules/camera/3_4/camera.cpp @@ -455,6 +455,7 @@ int Camera::preprocessCaptureBuffer(camera3_stream_buffer_t *buffer) __func__, mId, strerror(-res), res); return res; } + ::close(buffer->acquire_fence); } // Acquire fence has been waited upon. diff --git a/modules/camera/3_4/format_metadata_factory.cpp b/modules/camera/3_4/format_metadata_factory.cpp index a08f9a8b..db03678d 100644 --- a/modules/camera/3_4/format_metadata_factory.cpp +++ b/modules/camera/3_4/format_metadata_factory.cpp @@ -16,6 +16,7 @@ #include "format_metadata_factory.h" +#include "arc/image_processor.h" #include "metadata/array_vector.h" #include "metadata/partial_metadata_factory.h" #include "metadata/property.h" @@ -35,6 +36,7 @@ static int GetHalFormats(const std::shared_ptr<V4L2Wrapper>& device, HAL_LOGE("Failed to get device formats."); return res; } + for (auto v4l2_format : v4l2_formats) { int32_t hal_format = StreamFormat::V4L2ToHalPixelFormat(v4l2_format); if (hal_format < 0) { @@ -44,19 +46,15 @@ static int GetHalFormats(const std::shared_ptr<V4L2Wrapper>& device, result_formats->insert(hal_format); } - // In addition to well-defined formats, there may be an - // "Implementation Defined" format chosen by the HAL (in this - // case what that means is managed by the StreamFormat class). + return 0; +} - // Get the V4L2 format for IMPLEMENTATION_DEFINED. - int v4l2_format = StreamFormat::HalToV4L2PixelFormat( - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); - // If it's available, add IMPLEMENTATION_DEFINED to the result set. - if (v4l2_format && v4l2_formats.count(v4l2_format) > 0) { - result_formats->insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); +static int FpsRangesCompare(std::array<int32_t, 2> a, + std::array<int32_t, 2> b) { + if (a[1] == b[1]) { + return a[0] > b[0]; } - - return 0; + return a[1] > b[1]; } int AddFormatComponents( @@ -71,20 +69,40 @@ int AddFormatComponents( return res; } - // Requirements check: need to support YCbCr_420_888, JPEG, - // and "Implementation Defined". + std::set<int32_t> unsupported_hal_formats; if (hal_formats.find(HAL_PIXEL_FORMAT_YCbCr_420_888) == hal_formats.end()) { - HAL_LOGE("YCbCr_420_888 not supported by device."); - return -ENODEV; - } else if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) { - HAL_LOGE("JPEG not supported by device."); - return -ENODEV; - } else if (hal_formats.find(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) == - hal_formats.end()) { - HAL_LOGE("HAL implementation defined format not supported by device."); - return -ENODEV; + HAL_LOGW("YCbCr_420_888 (0x%x) not directly supported by device.", + HAL_PIXEL_FORMAT_YCbCr_420_888); + hal_formats.insert(HAL_PIXEL_FORMAT_YCbCr_420_888); + unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_YCbCr_420_888); + } + if (hal_formats.find(HAL_PIXEL_FORMAT_BLOB) == hal_formats.end()) { + HAL_LOGW("JPEG (0x%x) not directly supported by device.", + HAL_PIXEL_FORMAT_BLOB); + hal_formats.insert(HAL_PIXEL_FORMAT_BLOB); + unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_BLOB); + } + + // As hal_formats is populated by reading and converting V4L2 formats to the + // matching HAL formats, we will never see an implementation defined format in + // the list. We populate it ourselves and map it to a qualified format. If no + // qualified formats exist, this will be the first available format. + hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); + unsupported_hal_formats.insert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); + + // Qualified formats are the set of formats supported by this camera that the + // image processor can translate into the YU12 format. We additionally check + // that the conversion from YU12 to the desired hal format is supported. + std::vector<uint32_t> qualified_formats; + res = device->GetQualifiedFormats(&qualified_formats); + if (res && unsupported_hal_formats.size() > 1) { + HAL_LOGE( + "Failed to retrieve qualified formats, cannot perform conversions."); + return res; } + HAL_LOGI("Supports %d qualified formats.", qualified_formats.size()); + // Find sizes and frame/stall durations for all formats. // We also want to find the smallest max frame duration amongst all formats, // And the largest min frame duration amongst YUV (i.e. largest max frame rate @@ -96,7 +114,7 @@ int AddFormatComponents( // Stall durations are {format, width, height, duration} (duration in ns). ArrayVector<int64_t, 4> stall_durations; int64_t min_max_frame_duration = std::numeric_limits<int64_t>::max(); - int64_t max_min_frame_duration_yuv = std::numeric_limits<int64_t>::min(); + std::vector<std::array<int32_t, 2>> fps_ranges; for (auto hal_format : hal_formats) { // Get the corresponding V4L2 format. uint32_t v4l2_format = StreamFormat::HalToV4L2PixelFormat(hal_format); @@ -105,6 +123,42 @@ int AddFormatComponents( // came from translating a bunch of V4L2 formats above. HAL_LOGE("Couldn't find V4L2 format for HAL format %d", hal_format); return -ENODEV; + } else if (unsupported_hal_formats.find(hal_format) != + unsupported_hal_formats.end()) { + if (hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + if (qualified_formats.size() != 0) { + v4l2_format = qualified_formats[0]; + } else if (unsupported_hal_formats.size() == 1) { + v4l2_format = StreamFormat::HalToV4L2PixelFormat( + HAL_PIXEL_FORMAT_YCbCr_420_888); + } else { + // No-op. If there are no qualified formats, and implementation + // defined is not the only unsupported format, then other unsupported + // formats will throw an error. + } + HAL_LOGW( + "Implementation-defined format is set to V4L2 pixel format 0x%x", + v4l2_format); + } else if (qualified_formats.size() == 0) { + HAL_LOGE( + "Camera does not support required format: 0x%x, and there are no " + "qualified" + "formats to transform from.", + hal_format); + return -ENODEV; + } else if (!arc::ImageProcessor::SupportsConversion(V4L2_PIX_FMT_YUV420, + v4l2_format)) { + HAL_LOGE( + "The image processor does not support conversion to required " + "format: 0x%x", + hal_format); + return -ENODEV; + } else { + v4l2_format = qualified_formats[0]; + HAL_LOGW( + "Hal format 0x%x will be converted from V4L2 pixel format 0x%x", + hal_format, v4l2_format); + } } // Get the available sizes for this format. @@ -160,39 +214,25 @@ int AddFormatComponents( if (size_max_frame_duration < min_max_frame_duration) { min_max_frame_duration = size_max_frame_duration; } - // We only care about the largest min frame duration - // (smallest max frame rate) for YUV sizes. - if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888 && - size_min_frame_duration > max_min_frame_duration_yuv) { - max_min_frame_duration_yuv = size_min_frame_duration; + // ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES will contain all + // the fps ranges for YUV_420_888 only since YUV_420_888 format is + // the default camera format by Android. + if (hal_format == HAL_PIXEL_FORMAT_YCbCr_420_888) { + // Convert from frame durations measured in ns. + // Min, max fps supported by all YUV formats. + const int32_t min_fps = 1000000000 / size_max_frame_duration; + const int32_t max_fps = 1000000000 / size_min_frame_duration; + if (std::find(fps_ranges.begin(), fps_ranges.end(), + std::array<int32_t, 2>{min_fps, max_fps}) == + fps_ranges.end()) { + fps_ranges.push_back({min_fps, max_fps}); + } } } } - // Convert from frame durations measured in ns. - // Min fps supported by all formats. - int32_t min_fps = 1000000000 / min_max_frame_duration; - if (min_fps > 15) { - HAL_LOGE("Minimum FPS %d is larger than HAL max allowable value of 15", - min_fps); - return -ENODEV; - } - // Max fps supported by all YUV formats. - int32_t max_yuv_fps = 1000000000 / max_min_frame_duration_yuv; - // ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES should be at minimum - // {mi, ma}, {ma, ma} where mi and ma are min and max frame rates for - // YUV_420_888. Min should be at most 15. - std::vector<std::array<int32_t, 2>> fps_ranges; - fps_ranges.push_back({{min_fps, max_yuv_fps}}); - - std::array<int32_t, 2> video_fps_range; - int32_t video_fps = 30; - if (video_fps >= max_yuv_fps) { - video_fps_range = {{max_yuv_fps, max_yuv_fps}}; - } else { - video_fps_range = {{video_fps, video_fps}}; - } - fps_ranges.push_back(video_fps_range); + // Sort fps ranges in descending order. + std::sort(fps_ranges.begin(), fps_ranges.end(), FpsRangesCompare); // Construct the metadata components. insertion_point = std::make_unique<Property<ArrayVector<int32_t, 4>>>( @@ -208,10 +248,9 @@ int AddFormatComponents( // TODO(b/31019725): This should probably not be a NoEffect control. insertion_point = NoEffectMenuControl<std::array<int32_t, 2>>( ANDROID_CONTROL_AE_TARGET_FPS_RANGE, - ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, - fps_ranges, - {{CAMERA3_TEMPLATE_VIDEO_RECORD, video_fps_range}, - {OTHER_TEMPLATES, fps_ranges[0]}}); + ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fps_ranges, + {{CAMERA3_TEMPLATE_VIDEO_RECORD, fps_ranges.front()}, + {OTHER_TEMPLATES, fps_ranges.back()}}); return 0; } diff --git a/modules/camera/3_4/format_metadata_factory.h b/modules/camera/3_4/format_metadata_factory.h index 4cf5952f..23c1777b 100644 --- a/modules/camera/3_4/format_metadata_factory.h +++ b/modules/camera/3_4/format_metadata_factory.h @@ -17,6 +17,7 @@ #ifndef V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_ #define V4L2_CAMERA_HAL_FORMAT_METADATA_FACTORY_H_ +#include <algorithm> #include <iterator> #include <memory> #include <set> diff --git a/modules/camera/3_4/format_metadata_factory_test.cpp b/modules/camera/3_4/format_metadata_factory_test.cpp index d37b09f7..fe5d67f5 100644 --- a/modules/camera/3_4/format_metadata_factory_test.cpp +++ b/modules/camera/3_4/format_metadata_factory_test.cpp @@ -46,10 +46,12 @@ class FormatMetadataFactoryTest : public Test { }; TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) { - std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420}; + std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YUYV}; std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{ {V4L2_PIX_FMT_JPEG, {{{10, 20}}, {{30, 60}}, {{120, 240}}}}, - {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}}}; + {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}}, + {V4L2_PIX_FMT_YUYV, {{{20, 40}}, {{80, 160}}, {{320, 640}}}}}; // These need to be on the correct order of magnitude, // as there is a check for min fps > 15. std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>> @@ -60,19 +62,24 @@ TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) { {V4L2_PIX_FMT_YUV420, {{{{1, 2}}, {{10000000000, 20000000000}}}, {{{3, 6}}, {{11000000000, 21000000000}}}, - {{{12, 24}}, {{10500000000, 19000000000}}}}}}; + {{{12, 24}}, {{10500000000, 19000000000}}}}}, + {V4L2_PIX_FMT_YUYV, + {{{{20, 40}}, {{11000000000, 22000000000}}}, + {{{80, 160}}, {{13000000000, 25000000000}}}, + {{{320, 640}}, {{10100000000, 19000000000}}}}}}; + // The camera must report at least one qualified format. + std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV}; // Device must support IMPLEMENTATION_DEFINED (as well as JPEG & YUV). - // Just duplicate the values from another format. - uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat( - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); - formats.insert(imp_defined_format); - sizes[imp_defined_format] = sizes[V4L2_PIX_FMT_YUV420]; - durations[imp_defined_format] = durations[V4L2_PIX_FMT_YUV420]; + // For USB cameras, we assume that this format will not be present, and it + // will default to a qualified format or one of the other required formats. EXPECT_CALL(*mock_device_, GetFormats(_)) .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0))); + EXPECT_CALL(*mock_device_, GetQualifiedFormats(_)) + .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0))); + for (auto format : formats) { std::set<std::array<int32_t, 2>> format_sizes = sizes[format]; EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _)) @@ -94,7 +101,7 @@ TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) { for (auto& component : components) { android::CameraMetadata metadata; component->PopulateStaticFields(&metadata); - ASSERT_EQ(metadata.entryCount(), 1); + ASSERT_EQ(metadata.entryCount(), 1u); int32_t tag = component->StaticTags()[0]; switch (tag) { case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: // Fall through. @@ -119,39 +126,71 @@ TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) { } } -TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingJpeg) { - uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat( - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); - std::set<uint32_t> formats{V4L2_PIX_FMT_YUV420, imp_defined_format}; - EXPECT_CALL(*mock_device_, GetFormats(_)) - .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0))); - PartialMetadataSet components; - ASSERT_EQ(AddFormatComponents(mock_device_, - std::inserter(components, components.end())), - -ENODEV); -} +TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingRequired) { + std::set<uint32_t> formats{V4L2_PIX_FMT_YUYV}; + std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{ + {V4L2_PIX_FMT_YUYV, {{{640, 480}}, {{320, 240}}}}}; + std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>> + durations{{V4L2_PIX_FMT_YUYV, + {{{{640, 480}}, {{100000000, 200000000}}}, + {{{320, 240}}, {{100000000, 200000000}}}}}}; -TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingYuv) { - uint32_t imp_defined_format = StreamFormat::HalToV4L2PixelFormat( - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); - std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, imp_defined_format}; EXPECT_CALL(*mock_device_, GetFormats(_)) .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0))); - PartialMetadataSet components; - ASSERT_EQ(AddFormatComponents(mock_device_, - std::inserter(components, components.end())), - -ENODEV); -} + // If a qualified format is present, we expect that required fields are + // populated as if they are supported. + std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV}; -TEST_F(FormatMetadataFactoryTest, - GetFormatMetadataMissingImplementationDefined) { - std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420}; - EXPECT_CALL(*mock_device_, GetFormats(_)) - .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0))); + EXPECT_CALL(*mock_device_, GetQualifiedFormats(_)) + .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0))); + + for (auto format : formats) { + std::set<std::array<int32_t, 2>> format_sizes = sizes[format]; + EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _)) + .Times(AtLeast(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(format_sizes), Return(0))); + for (auto size : format_sizes) { + EXPECT_CALL(*mock_device_, GetFormatFrameDurationRange(format, size, _)) + .Times(AtLeast(1)) + .WillRepeatedly( + DoAll(SetArgPointee<2>(durations[format][size]), Return(0))); + } + } + + // Check that all required formats are present. PartialMetadataSet components; ASSERT_EQ(AddFormatComponents(mock_device_, std::inserter(components, components.end())), - -ENODEV); + 0); + + std::vector<std::array<int32_t, 2>> target_fps_ranges{{{5, 10}}, {{10, 10}}}; + for (auto& component : components) { + android::CameraMetadata metadata; + component->PopulateStaticFields(&metadata); + ASSERT_EQ(metadata.entryCount(), 1u); + int32_t tag = component->StaticTags()[0]; + switch (tag) { + case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: // Fall through. + case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS: // Fall through. + case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS: // Fall through. + // Two sizes per format, four elements per config. + // # formats + 3 for YUV420, JPEG, IMPLEMENTATION_DEFINED. + ExpectMetadataTagCount(metadata, tag, (formats.size() + 3) * 2 * 4); + break; + case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION: + // The lowest max duration from above. + ExpectMetadataEq(metadata, tag, 200000000); + break; + case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES: + // 2 ranges ({min, max} and {max, max}), each with a min and max. + ExpectMetadataTagCount(metadata, tag, 4); + ExpectMetadataEq(metadata, tag, target_fps_ranges); + break; + default: + FAIL() << "Unexpected component created."; + break; + } + } } } // namespace v4l2_camera_hal diff --git a/modules/camera/3_4/metadata/control_test.cpp b/modules/camera/3_4/metadata/control_test.cpp index f76376cc..6284330e 100644 --- a/modules/camera/3_4/metadata/control_test.cpp +++ b/modules/camera/3_4/metadata/control_test.cpp @@ -64,15 +64,15 @@ class ControlTest : public Test { virtual void ExpectTags() { if (use_options_ && report_options_) { - ASSERT_EQ(control_->StaticTags().size(), 1); + ASSERT_EQ(control_->StaticTags().size(), 1u); EXPECT_EQ(control_->StaticTags()[0], options_tag_); } else { EXPECT_TRUE(control_->StaticTags().empty()); } // Controls use the same delgate, and thus tag, for getting and setting. - ASSERT_EQ(control_->ControlTags().size(), 1); + ASSERT_EQ(control_->ControlTags().size(), 1u); EXPECT_EQ(control_->ControlTags()[0], delegate_tag_); - ASSERT_EQ(control_->DynamicTags().size(), 1); + ASSERT_EQ(control_->DynamicTags().size(), 1u); EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_); } @@ -81,10 +81,10 @@ class ControlTest : public Test { android::CameraMetadata metadata; ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0); if (use_options_ && report_options_) { - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, options_tag_, options); } else { - EXPECT_EQ(metadata.entryCount(), 0); + EXPECT_EQ(metadata.entryCount(), 0u); // Shouldn't be expecting any options. EXPECT_TRUE(options.empty()); } @@ -93,7 +93,7 @@ class ControlTest : public Test { virtual void ExpectValue(uint8_t value) { android::CameraMetadata metadata; ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0); - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, delegate_tag_, value); } diff --git a/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp index 9ca3a383..3537ed24 100644 --- a/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp +++ b/modules/camera/3_4/metadata/partial_metadata_factory_test.cpp @@ -44,11 +44,11 @@ class PartialMetadataFactoryTest : public Test { } virtual void ExpectControlTags() { - ASSERT_EQ(control_->StaticTags().size(), 1); + ASSERT_EQ(control_->StaticTags().size(), 1u); EXPECT_EQ(control_->StaticTags()[0], options_tag_); - ASSERT_EQ(control_->ControlTags().size(), 1); + ASSERT_EQ(control_->ControlTags().size(), 1u); EXPECT_EQ(control_->ControlTags()[0], delegate_tag_); - ASSERT_EQ(control_->DynamicTags().size(), 1); + ASSERT_EQ(control_->DynamicTags().size(), 1u); EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_); } @@ -56,14 +56,14 @@ class PartialMetadataFactoryTest : public Test { // Options should be available. android::CameraMetadata metadata; ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0); - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, options_tag_, options); } virtual void ExpectControlValue(uint8_t value) { android::CameraMetadata metadata; ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0); - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, delegate_tag_, value); } @@ -84,14 +84,14 @@ TEST_F(PartialMetadataFactoryTest, FixedState) { uint8_t value = 13; std::unique_ptr<State<uint8_t>> state = FixedState(delegate_tag_, value); - ASSERT_EQ(state->StaticTags().size(), 0); - ASSERT_EQ(state->ControlTags().size(), 0); - ASSERT_EQ(state->DynamicTags().size(), 1); + ASSERT_EQ(state->StaticTags().size(), 0u); + ASSERT_EQ(state->ControlTags().size(), 0u); + ASSERT_EQ(state->DynamicTags().size(), 1u); EXPECT_EQ(state->DynamicTags()[0], delegate_tag_); android::CameraMetadata metadata; ASSERT_EQ(state->PopulateDynamicFields(&metadata), 0); - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, delegate_tag_, value); } diff --git a/modules/camera/3_4/metadata/property_test.cpp b/modules/camera/3_4/metadata/property_test.cpp index 8e947ea3..80f8eb8d 100644 --- a/modules/camera/3_4/metadata/property_test.cpp +++ b/modules/camera/3_4/metadata/property_test.cpp @@ -49,9 +49,9 @@ TEST_F(PropertyTest, Tags) { Property<int32_t> property(int_tag_, 1); // Should have only the single tag it was constructed with. - EXPECT_EQ(property.ControlTags().size(), 0); - EXPECT_EQ(property.DynamicTags().size(), 0); - ASSERT_EQ(property.StaticTags().size(), 1); + EXPECT_EQ(property.ControlTags().size(), 0u); + EXPECT_EQ(property.DynamicTags().size(), 0u); + ASSERT_EQ(property.StaticTags().size(), 1u); // The macro doesn't like the int_tag_ variable being passed in directly. int32_t expected_tag = int_tag_; EXPECT_EQ(property.StaticTags()[0], expected_tag); @@ -68,7 +68,7 @@ TEST_F(PropertyTest, PopulateStaticSingleNumber) { // Check the results. // Should only have added 1 entry. - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); // Should have added the right entry. ExpectMetadataEq(metadata, int_tag_, data); } @@ -86,7 +86,7 @@ TEST_F(PropertyTest, PopulateStaticVector) { // Check the results. // Should only have added 1 entry. - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); // Should have added the right entry. ExpectMetadataEq(metadata, float_tag_, data); } @@ -102,7 +102,7 @@ TEST_F(PropertyTest, PopulateStaticArray) { // Check the results. // Should only have added 1 entry. - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); // Should have added the right entry. ExpectMetadataEq(metadata, float_tag_, data); } @@ -120,7 +120,7 @@ TEST_F(PropertyTest, PopulateStaticArrayVector) { // Check the results. // Should only have added 1 entry. - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); // Should have added the right entry. ExpectMetadataEq(metadata, byte_tag_, data); } diff --git a/modules/camera/3_4/metadata/state_test.cpp b/modules/camera/3_4/metadata/state_test.cpp index 5c308bc9..ecc1d154 100644 --- a/modules/camera/3_4/metadata/state_test.cpp +++ b/modules/camera/3_4/metadata/state_test.cpp @@ -59,7 +59,7 @@ TEST_F(StateTest, Tags) { PrepareState(); EXPECT_TRUE(state_->StaticTags().empty()); EXPECT_TRUE(state_->ControlTags().empty()); - ASSERT_EQ(state_->DynamicTags().size(), 1); + ASSERT_EQ(state_->DynamicTags().size(), 1u); EXPECT_EQ(state_->DynamicTags()[0], tag_); } @@ -79,7 +79,7 @@ TEST_F(StateTest, PopulateDynamic) { android::CameraMetadata metadata; ASSERT_EQ(state_->PopulateDynamicFields(&metadata), 0); - EXPECT_EQ(metadata.entryCount(), 1); + EXPECT_EQ(metadata.entryCount(), 1u); ExpectMetadataEq(metadata, tag_, expected); } diff --git a/modules/camera/3_4/metadata/test_common.h b/modules/camera/3_4/metadata/test_common.h index 489990ff..42e44f0b 100644 --- a/modules/camera/3_4/metadata/test_common.h +++ b/modules/camera/3_4/metadata/test_common.h @@ -80,9 +80,9 @@ static void ExpectMetadataEq(const android::CameraMetadata& metadata, // Vector of arrays. template <typename T, size_t N> -static int ExpectMetadataEq(const android::CameraMetadata& metadata, - int32_t tag, - const std::vector<std::array<T, N>>& expected) { +static void ExpectMetadataEq(const android::CameraMetadata& metadata, + int32_t tag, + const std::vector<std::array<T, N>>& expected) { // Convert to array vector so we know all the elements are contiguous. ArrayVector<T, N> array_vector; for (const auto& array : expected) { diff --git a/modules/camera/3_4/stream_format.cpp b/modules/camera/3_4/stream_format.cpp index c85c26be..5f35e425 100644 --- a/modules/camera/3_4/stream_format.cpp +++ b/modules/camera/3_4/stream_format.cpp @@ -20,10 +20,21 @@ #include <system/graphics.h> +#include "arc/image_processor.h" #include "common.h" namespace v4l2_camera_hal { +using arc::SupportedFormat; +using arc::SupportedFormats; + +static const std::vector<uint32_t> GetSupportedFourCCs() { + // The preference of supported fourccs in the list is from high to low. + static const std::vector<uint32_t> kSupportedFourCCs = {V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_MJPEG}; + return kSupportedFourCCs; +} + StreamFormat::StreamFormat(int format, uint32_t width, uint32_t height) // TODO(b/30000211): multiplanar support. : type_(V4L2_BUF_TYPE_VIDEO_CAPTURE), @@ -42,6 +53,14 @@ StreamFormat::StreamFormat(const v4l2_format& format) bytes_per_line_(format.fmt.pix.bytesperline), min_buffer_size_(format.fmt.pix.sizeimage) {} +StreamFormat::StreamFormat(const arc::SupportedFormat& format) + : type_(V4L2_BUF_TYPE_VIDEO_CAPTURE), + v4l2_pixel_format_(format.fourcc), + width_(format.width), + height_(format.height), + bytes_per_line_(0), + min_buffer_size_(0) {} + void StreamFormat::FillFormatRequest(v4l2_format* format) const { memset(format, 0, sizeof(*format)); format->type = type_; @@ -79,47 +98,127 @@ bool StreamFormat::operator!=(const StreamFormat& other) const { int StreamFormat::V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format) { // Translate V4L2 format to HAL format. - int hal_pixel_format = -1; switch (v4l2_pixel_format) { + case V4L2_PIX_FMT_BGR32: + return HAL_PIXEL_FORMAT_RGBA_8888; case V4L2_PIX_FMT_JPEG: - hal_pixel_format = HAL_PIXEL_FORMAT_BLOB; - break; + return HAL_PIXEL_FORMAT_BLOB; + case V4L2_PIX_FMT_NV21: + return HAL_PIXEL_FORMAT_YCrCb_420_SP; case V4L2_PIX_FMT_YUV420: - hal_pixel_format = HAL_PIXEL_FORMAT_YCbCr_420_888; - break; - case V4L2_PIX_FMT_BGR32: - hal_pixel_format = HAL_PIXEL_FORMAT_RGBA_8888; - break; + return HAL_PIXEL_FORMAT_YCbCr_420_888; + case V4L2_PIX_FMT_YUYV: + return HAL_PIXEL_FORMAT_YCbCr_422_I; + case V4L2_PIX_FMT_YVU420: + return HAL_PIXEL_FORMAT_YV12; default: // Unrecognized format. HAL_LOGV("Unrecognized v4l2 pixel format %u", v4l2_pixel_format); break; } - return hal_pixel_format; + return -1; } uint32_t StreamFormat::HalToV4L2PixelFormat(int hal_pixel_format) { - // Translate HAL format to V4L2 format. - uint32_t v4l2_pixel_format = 0; switch (hal_pixel_format) { - case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // fall-through. + case HAL_PIXEL_FORMAT_BLOB: + return V4L2_PIX_FMT_JPEG; + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // Fall-through case HAL_PIXEL_FORMAT_RGBA_8888: - // Should be RGB32, but RPi doesn't support that. - // For now we accept that the colors will be off. - v4l2_pixel_format = V4L2_PIX_FMT_BGR32; - break; + return V4L2_PIX_FMT_BGR32; case HAL_PIXEL_FORMAT_YCbCr_420_888: - v4l2_pixel_format = V4L2_PIX_FMT_YUV420; - break; - case HAL_PIXEL_FORMAT_BLOB: - v4l2_pixel_format = V4L2_PIX_FMT_JPEG; - break; + // This is a flexible YUV format that depends on platform. Different + // platform may have different format. It can be YVU420 or NV12. Now we + // return YVU420 first. + // TODO(): call drm_drv.get_fourcc() to get correct format. + return V4L2_PIX_FMT_YUV420; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return V4L2_PIX_FMT_YUYV; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return V4L2_PIX_FMT_NV21; + case HAL_PIXEL_FORMAT_YV12: + return V4L2_PIX_FMT_YVU420; default: - // Unrecognized format. - HAL_LOGV("Unrecognized HAL pixel format %d", hal_pixel_format); + HAL_LOGV("Pixel format 0x%x is unsupported.", hal_pixel_format); break; } - return v4l2_pixel_format; + return -1; +} + +// Copy the qualified format into out_format and return true if there is a +// proper and fitting format in the given format lists. +bool StreamFormat::FindBestFitFormat(const SupportedFormats& supported_formats, + const SupportedFormats& qualified_formats, + uint32_t fourcc, uint32_t width, + uint32_t height, + SupportedFormat* out_format) { + // Match exact format and resolution if possible. + for (const auto& format : supported_formats) { + if (format.fourcc == fourcc && format.width == width && + format.height == height) { + if (out_format != NULL) { + *out_format = format; + } + return true; + } + } + // All conversions will be done through CachedFrame for now, which will + // immediately convert the qualified format into YU12 (YUV420). We check + // here that the conversion between YU12 and |fourcc| is supported. + if (!arc::ImageProcessor::SupportsConversion(V4L2_PIX_FMT_YUV420, fourcc)) { + HAL_LOGE("Conversion between YU12 and 0x%x not supported.", fourcc); + return false; + } + + // Choose the qualified format with a matching resolution. + for (const auto& format : qualified_formats) { + if (format.width == width && format.height == height) { + if (out_format != NULL) { + *out_format = format; + } + return true; + } + } + return false; +} + +// Copy corresponding format into out_format and return true by matching +// resolution |width|x|height| in |formats|. +bool StreamFormat::FindFormatByResolution(const SupportedFormats& formats, + uint32_t width, uint32_t height, + SupportedFormat* out_format) { + for (const auto& format : formats) { + if (format.width == width && format.height == height) { + if (out_format != NULL) { + *out_format = format; + } + return true; + } + } + return false; +} + +SupportedFormats StreamFormat::GetQualifiedFormats( + const SupportedFormats& supported_formats) { + // The preference of supported fourccs in the list is from high to low. + const std::vector<uint32_t> supported_fourccs = GetSupportedFourCCs(); + SupportedFormats qualified_formats; + for (const auto& supported_fourcc : supported_fourccs) { + for (const auto& supported_format : supported_formats) { + if (supported_format.fourcc != supported_fourcc) { + continue; + } + + // Skip if |qualified_formats| already has the same resolution with a more + // preferred fourcc. + if (FindFormatByResolution(qualified_formats, supported_format.width, + supported_format.height, NULL)) { + continue; + } + qualified_formats.push_back(supported_format); + } + } + return qualified_formats; } } // namespace v4l2_camera_hal diff --git a/modules/camera/3_4/stream_format.h b/modules/camera/3_4/stream_format.h index 66c59659..720e3800 100644 --- a/modules/camera/3_4/stream_format.h +++ b/modules/camera/3_4/stream_format.h @@ -21,6 +21,7 @@ #include <linux/videodev2.h> +#include "arc/common_types.h" #include "common.h" namespace v4l2_camera_hal { @@ -36,6 +37,7 @@ class StreamFormat { public: StreamFormat(int format, uint32_t width, uint32_t height); StreamFormat(const v4l2_format& format); + StreamFormat(const arc::SupportedFormat& format); virtual ~StreamFormat() = default; // Only uint32_t members, use default generated copy and assign. @@ -44,6 +46,9 @@ class StreamFormat { // Accessors. inline uint32_t type() const { return type_; }; + inline uint32_t width() const { return width_; }; + inline uint32_t height() const { return height_; }; + inline uint32_t v4l2_pixel_format() const { return v4l2_pixel_format_; } inline uint32_t bytes_per_line() const { return bytes_per_line_; }; bool operator==(const StreamFormat& other) const; @@ -55,6 +60,18 @@ class StreamFormat { // Returns -1 for unrecognized. static int V4L2ToHalPixelFormat(uint32_t v4l2_pixel_format); + // ARC++ SupportedFormat Helpers + static bool FindBestFitFormat(const arc::SupportedFormats& supported_formats, + const arc::SupportedFormats& qualified_formats, + uint32_t fourcc, uint32_t width, + uint32_t height, + arc::SupportedFormat* out_format); + static bool FindFormatByResolution(const arc::SupportedFormats& formats, + uint32_t width, uint32_t height, + arc::SupportedFormat* out_format); + static arc::SupportedFormats GetQualifiedFormats( + const arc::SupportedFormats& supported_formats); + private: uint32_t type_; uint32_t v4l2_pixel_format_; diff --git a/modules/camera/3_4/v4l2_camera.cpp b/modules/camera/3_4/v4l2_camera.cpp index 3e5b8590..98b80622 100644 --- a/modules/camera/3_4/v4l2_camera.cpp +++ b/modules/camera/3_4/v4l2_camera.cpp @@ -123,16 +123,7 @@ void V4L2Camera::disconnect() { int V4L2Camera::flushBuffers() { HAL_LOG_ENTER(); - - int res = device_->StreamOff(); - - // This is not strictly necessary, but prevents a buildup of aborted - // requests in the in_flight_ map. These should be cleared - // whenever the stream is turned off. - std::lock_guard<std::mutex> guard(in_flight_lock_); - in_flight_.clear(); - - return res; + return device_->StreamOff(); } int V4L2Camera::initStaticInfo(android::CameraMetadata* out) { @@ -262,65 +253,55 @@ bool V4L2Camera::enqueueRequestBuffers() { } // Actually enqueue the buffer for capture. - { - std::lock_guard<std::mutex> guard(in_flight_lock_); - - uint32_t index; - res = device_->EnqueueBuffer(&request->output_buffers[0], &index); - if (res) { - HAL_LOGE("Device failed to enqueue buffer."); - completeRequest(request, res); - return true; - } - - // Make sure the stream is on (no effect if already on). - res = device_->StreamOn(); - if (res) { - HAL_LOGE("Device failed to turn on stream."); - // Don't really want to send an error for only the request here, - // since this is a full device error. - // TODO: Should trigger full flush. - return true; - } + res = device_->EnqueueRequest(request); + if (res) { + HAL_LOGE("Device failed to enqueue buffer."); + completeRequest(request, res); + return true; + } - // Note: the request should be dequeued/flushed from the device - // before removal from in_flight_. - in_flight_.emplace(index, request); - buffers_in_flight_.notify_one(); + // Make sure the stream is on (no effect if already on). + res = device_->StreamOn(); + if (res) { + HAL_LOGE("Device failed to turn on stream."); + // Don't really want to send an error for only the request here, + // since this is a full device error. + // TODO: Should trigger full flush. + return true; } + std::unique_lock<std::mutex> lock(in_flight_lock_); + in_flight_buffer_count_++; + buffers_in_flight_.notify_one(); return true; } bool V4L2Camera::dequeueRequestBuffers() { // Dequeue a buffer. - uint32_t result_index; - int res = device_->DequeueBuffer(&result_index); - if (res) { - if (res == -EAGAIN) { - // EAGAIN just means nothing to dequeue right now. - // Wait until something is available before looping again. - std::unique_lock<std::mutex> lock(in_flight_lock_); - while (in_flight_.empty()) { - buffers_in_flight_.wait(lock); + std::shared_ptr<default_camera_hal::CaptureRequest> request; + int res; + + { + std::unique_lock<std::mutex> lock(in_flight_lock_); + res = device_->DequeueRequest(&request); + if (!res) { + if (request) { + completeRequest(request, res); + in_flight_buffer_count_--; } - } else { - HAL_LOGW("Device failed to dequeue buffer: %d", res); + return true; } - return true; } - // Find the associated request and complete it. - std::lock_guard<std::mutex> guard(in_flight_lock_); - auto index_request = in_flight_.find(result_index); - if (index_request != in_flight_.end()) { - completeRequest(index_request->second, 0); - in_flight_.erase(index_request); + if (res == -EAGAIN) { + // EAGAIN just means nothing to dequeue right now. + // Wait until something is available before looping again. + std::unique_lock<std::mutex> lock(in_flight_lock_); + while (in_flight_buffer_count_ == 0) { + buffers_in_flight_.wait(lock); + } } else { - HAL_LOGW( - "Dequeued non in-flight buffer index %d. " - "This buffer may have been flushed from the HAL but not the device.", - index_request->first); + HAL_LOGW("Device failed to dequeue buffer: %d", res); } return true; } @@ -346,10 +327,11 @@ int V4L2Camera::setupStreams(camera3_stream_configuration_t* stream_config) { std::lock_guard<std::mutex> guard(in_flight_lock_); // The framework should be enforcing this, but doesn't hurt to be safe. - if (!in_flight_.empty()) { + if (device_->GetInFlightBufferCount() != 0) { HAL_LOGE("Can't set device format while frames are in flight."); return -EINVAL; } + in_flight_buffer_count_ = 0; // stream_config should have been validated; assume at least 1 stream. camera3_stream_t* stream = stream_config->streams[0]; @@ -409,6 +391,11 @@ int V4L2Camera::setupStreams(camera3_stream_configuration_t* stream_config) { for (uint32_t i = 0; i < stream_config->num_streams; ++i) { stream = stream_config->streams[i]; + // Override HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED format. + if (stream->format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + stream->format = HAL_PIXEL_FORMAT_RGBA_8888; + } + // Max buffers as reported by the device. stream->max_buffers = max_buffers; diff --git a/modules/camera/3_4/v4l2_camera.h b/modules/camera/3_4/v4l2_camera.h index 1db8d40c..fc2adb3e 100644 --- a/modules/camera/3_4/v4l2_camera.h +++ b/modules/camera/3_4/v4l2_camera.h @@ -99,9 +99,7 @@ class V4L2Camera : public default_camera_hal::Camera { std::queue<std::shared_ptr<default_camera_hal::CaptureRequest>> request_queue_; std::mutex in_flight_lock_; - // Maps buffer index : request. - std::map<uint32_t, std::shared_ptr<default_camera_hal::CaptureRequest>> - in_flight_; + uint32_t in_flight_buffer_count_; // Threads require holding an Android strong pointer. android::sp<android::Thread> buffer_enqueuer_; android::sp<android::Thread> buffer_dequeuer_; diff --git a/modules/camera/3_4/v4l2_gralloc.cpp b/modules/camera/3_4/v4l2_gralloc.cpp deleted file mode 100644 index 1c8ca48b..00000000 --- a/modules/camera/3_4/v4l2_gralloc.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2016 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 "v4l2_gralloc.h" - -#include <linux/videodev2.h> - -#include <cerrno> -#include <cstdlib> - -#include <hardware/camera3.h> -#include <hardware/gralloc.h> -#include <system/graphics.h> - -#include "common.h" -#include "stream_format.h" - -namespace v4l2_camera_hal { - -// Copy |height| lines from |src| to |dest|, -// where |src| and |dest| may have different line lengths. -void copyWithPadding(uint8_t* dest, - const uint8_t* src, - size_t dest_stride, - size_t src_stride, - size_t height) { - size_t copy_stride = dest_stride; - if (copy_stride > src_stride) { - // Adding padding, not reducing. 0 out the extra memory. - memset(dest, 0, src_stride * height); - copy_stride = src_stride; - } - uint8_t* dest_line_start = dest; - const uint8_t* src_line_start = src; - for (size_t row = 0; row < height; - ++row, dest_line_start += dest_stride, src_line_start += src_stride) { - memcpy(dest_line_start, src_line_start, copy_stride); - } -} - -V4L2Gralloc* V4L2Gralloc::NewV4L2Gralloc() { - // Initialize and check the gralloc module. - const hw_module_t* module = nullptr; - int res = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); - if (res || !module) { - HAL_LOGE("Couldn't get gralloc module."); - return nullptr; - } - const gralloc_module_t* gralloc = - reinterpret_cast<const gralloc_module_t*>(module); - - // This class only supports Gralloc v0, not Gralloc V1. - if (gralloc->common.module_api_version > GRALLOC_MODULE_API_VERSION_0_3) { - HAL_LOGE( - "Invalid gralloc version %x. Only 0.3 (%x) " - "and below are supported by this HAL.", - gralloc->common.module_api_version, - GRALLOC_MODULE_API_VERSION_0_3); - return nullptr; - } - - return new V4L2Gralloc(gralloc); -} - -// Private. As checked by above factory, module will be non-null -// and a supported version. -V4L2Gralloc::V4L2Gralloc(const gralloc_module_t* module) : mModule(module) {} - -V4L2Gralloc::~V4L2Gralloc() { - // Unlock buffers that are still locked. - unlockAllBuffers(); -} - -int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer, - uint32_t bytes_per_line, - v4l2_buffer* device_buffer) { - // Lock the camera buffer (varies depending on if the buffer is YUV or not). - std::unique_ptr<BufferData> buffer_data( - new BufferData{camera_buffer, nullptr, bytes_per_line}); - buffer_handle_t buffer = *camera_buffer->buffer; - void* data; - camera3_stream_t* stream = camera_buffer->stream; - int ret = 0; - switch (StreamFormat::HalToV4L2PixelFormat(stream->format)) { - // TODO(b/30119452): support more YCbCr formats. - case V4L2_PIX_FMT_YUV420: - android_ycbcr yuv_data; - ret = mModule->lock_ycbcr(mModule, - buffer, - stream->usage, - 0, - 0, - stream->width, - stream->height, - &yuv_data); - if (ret) { - HAL_LOGE("Failed to lock ycbcr buffer: %d", ret); - return ret; - } - - // Check if gralloc format matches v4l2 format - // (same padding, not interleaved, contiguous). - if (yuv_data.ystride == bytes_per_line && - yuv_data.cstride == bytes_per_line / 2 && yuv_data.chroma_step == 1 && - (reinterpret_cast<uint8_t*>(yuv_data.cb) == - reinterpret_cast<uint8_t*>(yuv_data.y) + - (stream->height * yuv_data.ystride)) && - (reinterpret_cast<uint8_t*>(yuv_data.cr) == - reinterpret_cast<uint8_t*>(yuv_data.cb) + - (stream->height / 2 * yuv_data.cstride))) { - // If so, great, point to the beginning. - data = yuv_data.y; - } else { - // If not, allocate a contiguous buffer of appropriate size - // (to be transformed back upon unlock). - data = new uint8_t[device_buffer->length]; - // Make a dynamically-allocated copy of yuv_data, - // since it will be needed at transform time. - buffer_data->transform_dest.reset(new android_ycbcr(yuv_data)); - } - break; - case V4L2_PIX_FMT_JPEG: - // Jpeg buffers are just contiguous blobs; lock length * 1. - ret = mModule->lock(mModule, - buffer, - stream->usage, - 0, - 0, - device_buffer->length, - 1, - &data); - if (ret) { - HAL_LOGE("Failed to lock jpeg buffer: %d", ret); - return ret; - } - break; - case V4L2_PIX_FMT_BGR32: // Fall-through. - case V4L2_PIX_FMT_RGB32: - // RGB formats have nice agreed upon representation. Unless using android - // flex formats. - ret = mModule->lock(mModule, - buffer, - stream->usage, - 0, - 0, - stream->width, - stream->height, - &data); - if (ret) { - HAL_LOGE("Failed to lock RGB buffer: %d", ret); - return ret; - } - break; - default: - return -EINVAL; - } - - if (!data) { - ALOGE("Gralloc lock returned null ptr"); - return -ENODEV; - } - - // Set up the device buffer. - static_assert(sizeof(unsigned long) >= sizeof(void*), - "void* must be able to fit in the v4l2_buffer m.userptr " - "field (unsigned long) for this code to work"); - device_buffer->m.userptr = reinterpret_cast<unsigned long>(data); - - // Note the mapping of data:buffer info for when unlock is called. - mBufferMap.emplace(data, buffer_data.release()); - - return 0; -} - -int V4L2Gralloc::unlock(const v4l2_buffer* device_buffer) { - // TODO(b/30000211): support multi-planar data (video_capture_mplane). - if (device_buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - return -EINVAL; - } - - void* data = reinterpret_cast<void*>(device_buffer->m.userptr); - - // Find and pop the matching entry in the map. - auto map_entry = mBufferMap.find(data); - if (map_entry == mBufferMap.end()) { - HAL_LOGE("No matching buffer for data at %p", data); - return -EINVAL; - } - std::unique_ptr<const BufferData> buffer_data(map_entry->second); - mBufferMap.erase(map_entry); - - const camera3_stream_buffer_t* camera_buffer = buffer_data->camera_buffer; - const buffer_handle_t buffer = *camera_buffer->buffer; - - // Check for transform. - if (buffer_data->transform_dest) { - HAL_LOGV("Transforming V4L2 YUV to gralloc YUV."); - // In this case data was allocated by this class, put it in a unique_ptr - // to ensure it gets cleaned up no matter which way this function exits. - std::unique_ptr<uint8_t[]> data_cleanup(reinterpret_cast<uint8_t*>(data)); - - uint32_t bytes_per_line = buffer_data->v4l2_bytes_per_line; - android_ycbcr* yuv_data = buffer_data->transform_dest.get(); - - // Should only occur in error situations. - if (device_buffer->bytesused == 0) { - return -EINVAL; - } - - // Transform V4L2 to Gralloc, copying each plane to the correct place, - // adjusting padding, and interleaving if necessary. - uint32_t height = camera_buffer->stream->height; - // Y data first. - size_t y_len = bytes_per_line * height; - if (yuv_data->ystride == bytes_per_line) { - // Data should match exactly. - memcpy(yuv_data->y, data, y_len); - } else { - HAL_LOGV("Changing padding on Y plane from %u to %u.", - bytes_per_line, - yuv_data->ystride); - // Wrong padding from V4L2. - copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->y), - reinterpret_cast<uint8_t*>(data), - yuv_data->ystride, - bytes_per_line, - height); - } - // C data. - // TODO(b/30119452): These calculations assume YCbCr_420_888. - size_t c_len = y_len / 4; - uint32_t c_bytes_per_line = bytes_per_line / 2; - // V4L2 is packed, meaning the data is stored as contiguous {y, cb, cr}. - uint8_t* cb_device = reinterpret_cast<uint8_t*>(data) + y_len; - uint8_t* cr_device = cb_device + c_len; - size_t step = yuv_data->chroma_step; - if (step == 1) { - // Still planar. - if (yuv_data->cstride == c_bytes_per_line) { - // Data should match exactly. - memcpy(yuv_data->cb, cb_device, c_len); - memcpy(yuv_data->cr, cr_device, c_len); - } else { - HAL_LOGV("Changing padding on C plane from %u to %u.", - c_bytes_per_line, - yuv_data->cstride); - // Wrong padding from V4L2. - copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cb), - cb_device, - yuv_data->cstride, - c_bytes_per_line, - height / 2); - copyWithPadding(reinterpret_cast<uint8_t*>(yuv_data->cr), - cr_device, - yuv_data->cstride, - c_bytes_per_line, - height / 2); - } - } else { - // Desire semiplanar (cb and cr interleaved). - HAL_LOGV("Interleaving cb and cr. Padding going from %u to %u.", - c_bytes_per_line, - yuv_data->cstride); - uint32_t c_height = height / 2; - uint32_t c_width = camera_buffer->stream->width / 2; - // Zero out destination - uint8_t* cb_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cb); - uint8_t* cr_gralloc = reinterpret_cast<uint8_t*>(yuv_data->cr); - memset(cb_gralloc, 0, c_width * c_height * step); - - // Interleaving means we need to copy the cb and cr bytes one by one. - for (size_t line = 0; line < c_height; ++line, - cb_gralloc += yuv_data->cstride, - cr_gralloc += yuv_data->cstride, - cb_device += c_bytes_per_line, - cr_device += c_bytes_per_line) { - for (size_t i = 0; i < c_width; ++i) { - *(cb_gralloc + (i * step)) = *(cb_device + i); - *(cr_gralloc + (i * step)) = *(cr_device + i); - } - } - } - } - - // Unlock. - int res = mModule->unlock(mModule, buffer); - if (res) { - HAL_LOGE("Failed to unlock buffer at %p", buffer); - return -ENODEV; - } - - return 0; -} - -int V4L2Gralloc::unlockAllBuffers() { - HAL_LOG_ENTER(); - - bool failed = false; - for (auto const& entry : mBufferMap) { - int res = mModule->unlock(mModule, *entry.second->camera_buffer->buffer); - if (res) { - failed = true; - } - // When there is a transform to be made, the buffer returned by lock() - // is dynamically allocated (to hold the pre-transform data). - if (entry.second->transform_dest) { - delete[] reinterpret_cast<uint8_t*>(entry.first); - } - // The BufferData entry is always dynamically allocated in lock(). - delete entry.second; - } - mBufferMap.clear(); - - // If any unlock failed, return error. - if (failed) { - return -ENODEV; - } - - return 0; -} - -} // namespace default_camera_hal diff --git a/modules/camera/3_4/v4l2_gralloc.h b/modules/camera/3_4/v4l2_gralloc.h deleted file mode 100644 index 82129e37..00000000 --- a/modules/camera/3_4/v4l2_gralloc.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2016 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 V4L2_CAMERA_HAL_V4L2_GRALLOC_H_ -#define V4L2_CAMERA_HAL_V4L2_GRALLOC_H_ - -#include <linux/videodev2.h> - -#include <unordered_map> - -#include <hardware/camera3.h> -#include <hardware/gralloc.h> -#include <system/graphics.h> - -namespace v4l2_camera_hal { - -// Generously allow up to 6MB (the largest JPEG on the RPi camera is about 5MB). -static constexpr size_t V4L2_MAX_JPEG_SIZE = 6000000; - -// V4L2Gralloc is a wrapper around relevant parts of a gralloc module, -// with some assistive transformations. -class V4L2Gralloc { - public: - // Use this method to create V4L2Gralloc objects. Functionally equivalent - // to "new V4L2Gralloc", except that it may return nullptr in case of failure. - static V4L2Gralloc* NewV4L2Gralloc(); - virtual ~V4L2Gralloc(); - - // Lock a camera buffer. Uses device buffer length, sets user pointer. - int lock(const camera3_stream_buffer_t* camera_buffer, - uint32_t bytes_per_line, - v4l2_buffer* device_buffer); - // Unlock a buffer that was locked by this helper (equality determined - // based on buffer user pointer, not the specific object). - int unlock(const v4l2_buffer* device_buffer); - // Release all held locks. - int unlockAllBuffers(); - - private: - // Constructor is private to allow failing on bad input. - // Use NewV4L2Gralloc instead. - V4L2Gralloc(const gralloc_module_t* module); - - const gralloc_module_t* mModule; - - struct BufferData { - const camera3_stream_buffer_t* camera_buffer; - // Below fields only used when a ycbcr format transform is necessary. - std::unique_ptr<android_ycbcr> transform_dest; // nullptr if no transform. - uint32_t v4l2_bytes_per_line; - }; - // Map data pointer : BufferData about that buffer. - std::unordered_map<void*, const BufferData*> mBufferMap; -}; - -} // namespace default_camera_hal - -#endif // V4L2_CAMERA_HAL_V4L2_GRALLOC_H_ diff --git a/modules/camera/3_4/v4l2_metadata_factory.cpp b/modules/camera/3_4/v4l2_metadata_factory.cpp index 67556d9e..bc8806f7 100644 --- a/modules/camera/3_4/v4l2_metadata_factory.cpp +++ b/modules/camera/3_4/v4l2_metadata_factory.cpp @@ -27,7 +27,6 @@ #include "metadata/partial_metadata_factory.h" #include "metadata/property.h" #include "metadata/scaling_converter.h" -#include "v4l2_gralloc.h" namespace v4l2_camera_hal { @@ -37,6 +36,8 @@ const camera_metadata_rational_t kAeCompensationUnit = {1, 1000}; const int64_t kV4L2ExposureTimeStepNs = 100000; // According to spec, each unit of V4L2_CID_ISO_SENSITIVITY is ISO/1000. const int32_t kV4L2SensitivityDenominator = 1000; +// Generously allow up to 6MB (the largest size on the RPi Camera is about 5MB). +const size_t kV4L2MaxJpegSize = 6000000; int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device, std::unique_ptr<Metadata>* result) { @@ -433,10 +434,9 @@ int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device, ANDROID_JPEG_THUMBNAIL_SIZE, ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, {{{0, 0}}})); - // TODO(b/31022752): Get this from the device, - // not constant (from V4L2Gralloc.h). + // TODO(b/31022752): Get this from the device, not constant. components.insert(std::unique_ptr<PartialMetadataInterface>( - new Property<int32_t>(ANDROID_JPEG_MAX_SIZE, V4L2_MAX_JPEG_SIZE))); + new Property<int32_t>(ANDROID_JPEG_MAX_SIZE, kV4L2MaxJpegSize))); // TODO(b/31021672): Other JPEG controls (GPS, quality, orientation). // TODO(b/29939583): V4L2 can only support 1 stream at a time. // For now, just reporting minimum allowable for LIMITED devices. @@ -476,7 +476,7 @@ int GetV4L2Metadata(std::shared_ptr<V4L2Wrapper> device, new Property<uint8_t>(ANDROID_SCALER_CROPPING_TYPE, ANDROID_SCALER_CROPPING_TYPE_FREEFORM))); // Spoof pixel array size for now, eventually get from CROPCAP. - std::array<int32_t, 2> pixel_array_size = {{640, 480}}; + std::array<int32_t, 2> pixel_array_size = {{3280, 2464}}; components.insert(std::unique_ptr<PartialMetadataInterface>( new Property<std::array<int32_t, 2>>(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixel_array_size))); diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp index 3fafffd2..d715e7ec 100644 --- a/modules/camera/3_4/v4l2_wrapper.cpp +++ b/modules/camera/3_4/v4l2_wrapper.cpp @@ -29,29 +29,35 @@ #include <android-base/unique_fd.h> -#include "common.h" -#include "stream_format.h" -#include "v4l2_gralloc.h" +#include "arc/cached_frame.h" namespace v4l2_camera_hal { -const int32_t kStandardSizes[][2] = {{640, 480}, {320, 240}}; +using arc::AllocatedFrameBuffer; +using arc::SupportedFormat; +using arc::SupportedFormats; +using default_camera_hal::CaptureRequest; + +const int32_t kStandardSizes[][2] = { + {4096, 2160}, // 4KDCI (for USB camera) + {3840, 2160}, // 4KUHD (for USB camera) + {3280, 2464}, // 8MP + {2560, 1440}, // QHD + {1920, 1080}, // HD1080 + {1640, 1232}, // 2MP + {1280, 720}, // HD + {1024, 768}, // XGA + { 640, 480}, // VGA + { 320, 240}, // QVGA + { 176, 144} // QCIF +}; V4L2Wrapper* V4L2Wrapper::NewV4L2Wrapper(const std::string device_path) { - std::unique_ptr<V4L2Gralloc> gralloc(V4L2Gralloc::NewV4L2Gralloc()); - if (!gralloc) { - HAL_LOGE("Failed to initialize gralloc helper."); - return nullptr; - } - - return new V4L2Wrapper(device_path, std::move(gralloc)); + return new V4L2Wrapper(device_path); } -V4L2Wrapper::V4L2Wrapper(const std::string device_path, - std::unique_ptr<V4L2Gralloc> gralloc) - : device_path_(std::move(device_path)), - gralloc_(std::move(gralloc)), - connection_count_(0) {} +V4L2Wrapper::V4L2Wrapper(const std::string device_path) + : device_path_(std::move(device_path)), connection_count_(0) {} V4L2Wrapper::~V4L2Wrapper() {} @@ -85,6 +91,10 @@ int V4L2Wrapper::Connect() { // (Alternatively, better hotplugging support may make this unecessary // by disabling cameras that get disconnected and checking newly connected // cameras, so Connect() is never called on an unsupported camera) + + supported_formats_ = GetSupportedFormats(); + qualified_formats_ = StreamFormat::GetQualifiedFormats(supported_formats_); + return 0; } @@ -102,15 +112,16 @@ void V4L2Wrapper::Disconnect() { --connection_count_; if (connection_count_ > 0) { HAL_LOGV("Disconnected from camera device %s. %d connections remain.", - device_path_.c_str()); + device_path_.c_str(), connection_count_); return; } device_fd_.reset(-1); // Includes close(). format_.reset(); - buffers_.clear(); - // Closing the device releases all queued buffers back to the user. - gralloc_->unlockAllBuffers(); + { + std::lock_guard<std::mutex> buffer_lock(buffer_queue_lock_); + buffers_.clear(); + } } // Helper function. Should be used instead of ioctl throughout this class. @@ -134,7 +145,7 @@ int V4L2Wrapper::StreamOn() { int32_t type = format_->type(); if (IoctlLocked(VIDIOC_STREAMON, &type) < 0) { - HAL_LOGE("STREAMON fails: %s", strerror(errno)); + HAL_LOGE("STREAMON fails (%d): %s", errno, strerror(errno)); return -ENODEV; } @@ -152,20 +163,16 @@ int V4L2Wrapper::StreamOff() { int32_t type = format_->type(); int res = IoctlLocked(VIDIOC_STREAMOFF, &type); // Calling STREAMOFF releases all queued buffers back to the user. - int gralloc_res = gralloc_->unlockAllBuffers(); // No buffers in flight. - for (size_t i = 0; i < buffers_.size(); ++i) { - buffers_[i] = false; - } if (res < 0) { HAL_LOGE("STREAMOFF fails: %s", strerror(errno)); return -ENODEV; } - if (gralloc_res < 0) { - HAL_LOGE("Failed to unlock all buffers after turning stream off."); - return gralloc_res; + std::lock_guard<std::mutex> lock(buffer_queue_lock_); + for (auto& buffer : buffers_) { + buffer.active = false; + buffer.request.reset(); } - HAL_LOGV("Stream turned off."); return 0; } @@ -304,6 +311,36 @@ int V4L2Wrapper::SetControl(uint32_t control_id, return 0; } +const SupportedFormats V4L2Wrapper::GetSupportedFormats() { + SupportedFormats formats; + std::set<uint32_t> pixel_formats; + int res = GetFormats(&pixel_formats); + if (res) { + HAL_LOGE("Failed to get device formats."); + return formats; + } + + arc::SupportedFormat supported_format; + std::set<std::array<int32_t, 2>> frame_sizes; + + for (auto pixel_format : pixel_formats) { + supported_format.fourcc = pixel_format; + + frame_sizes.clear(); + res = GetFormatFrameSizes(pixel_format, &frame_sizes); + if (res) { + HAL_LOGE("Failed to get frame sizes for format: 0x%x", pixel_format); + continue; + } + for (auto frame_size : frame_sizes) { + supported_format.width = frame_size[0]; + supported_format.height = frame_size[1]; + formats.push_back(supported_format); + } + } + return formats; +} + int V4L2Wrapper::GetFormats(std::set<uint32_t>* v4l2_formats) { HAL_LOG_ENTER(); @@ -324,6 +361,22 @@ int V4L2Wrapper::GetFormats(std::set<uint32_t>* v4l2_formats) { return 0; } +int V4L2Wrapper::GetQualifiedFormats(std::vector<uint32_t>* v4l2_formats) { + HAL_LOG_ENTER(); + if (!connected()) { + HAL_LOGE( + "Device is not connected, qualified formats may not have been set."); + return -EINVAL; + } + v4l2_formats->clear(); + std::set<uint32_t> unique_fourccs; + for (auto& format : qualified_formats_) { + unique_fourccs.insert(format.fourcc); + } + v4l2_formats->assign(unique_fourccs.begin(), unique_fourccs.end()); + return 0; +} + int V4L2Wrapper::GetFormatFrameSizes(uint32_t v4l2_format, std::set<std::array<int32_t, 2>>* sizes) { v4l2_frmsizeenum size_query; @@ -364,7 +417,7 @@ int V4L2Wrapper::GetFormatFrameSizes(uint32_t v4l2_format, desired_height, v4l2_format); continue; - } else if (desired_width > size_query.stepwise.max_width && + } else if (desired_width > size_query.stepwise.max_width || desired_height > size_query.stepwise.max_height) { HAL_LOGV("Standard size %u x %u is too big for format %d", desired_width, @@ -447,8 +500,6 @@ int V4L2Wrapper::SetFormat(const StreamFormat& desired_format, return 0; } - // Not in the correct format, set the new one. - if (format_) { // If we had an old format, first request 0 buffers to inform the device // we're no longer using any previously "allocated" buffers from the old @@ -461,19 +512,34 @@ int V4L2Wrapper::SetFormat(const StreamFormat& desired_format, } } + // Select the matching format, or if not available, select a qualified format + // we can convert from. + SupportedFormat format; + if (!StreamFormat::FindBestFitFormat(supported_formats_, qualified_formats_, + desired_format.v4l2_pixel_format(), + desired_format.width(), + desired_format.height(), &format)) { + HAL_LOGE( + "Unable to find supported resolution in list, " + "width: %d, height: %d", + desired_format.width(), desired_format.height()); + return -EINVAL; + } + // Set the camera to the new format. v4l2_format new_format; - desired_format.FillFormatRequest(&new_format); + const StreamFormat resolved_format(format); + resolved_format.FillFormatRequest(&new_format); + // TODO(b/29334616): When async, this will need to check if the stream // is on, and if so, lock it off while setting format. - if (IoctlLocked(VIDIOC_S_FMT, &new_format) < 0) { HAL_LOGE("S_FMT failed: %s", strerror(errno)); return -ENODEV; } // Check that the driver actually set to the requested values. - if (desired_format != new_format) { + if (resolved_format != new_format) { HAL_LOGE("Device doesn't support desired stream configuration."); return -EINVAL; } @@ -500,28 +566,22 @@ int V4L2Wrapper::RequestBuffers(uint32_t num_requested) { int res = IoctlLocked(VIDIOC_REQBUFS, &req_buffers); // Calling REQBUFS releases all queued buffers back to the user. - int gralloc_res = gralloc_->unlockAllBuffers(); if (res < 0) { HAL_LOGE("REQBUFS failed: %s", strerror(errno)); return -ENODEV; } - if (gralloc_res < 0) { - HAL_LOGE("Failed to unlock all buffers when setting up new buffers."); - return gralloc_res; - } // V4L2 will set req_buffers.count to a number of buffers it can handle. if (num_requested > 0 && req_buffers.count < 1) { HAL_LOGE("REQBUFS claims it can't handle any buffers."); return -ENODEV; } - buffers_.resize(req_buffers.count, false); - + buffers_.resize(req_buffers.count); return 0; } -int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer, - uint32_t* enqueued_index) { +int V4L2Wrapper::EnqueueRequest( + std::shared_ptr<default_camera_hal::CaptureRequest> request) { if (!format_) { HAL_LOGE("Stream format must be set before enqueuing buffers."); return -ENODEV; @@ -533,8 +593,8 @@ int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer, int index = -1; { std::lock_guard<std::mutex> guard(buffer_queue_lock_); - for (int i = 0; i < buffers_.size(); ++i) { - if (!buffers_[i]) { + for (size_t i = 0; i < buffers_.size(); ++i) { + if (!buffers_[i].active) { index = i; break; } @@ -557,33 +617,42 @@ int V4L2Wrapper::EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer, // and fill out remaining fields. if (IoctlLocked(VIDIOC_QUERYBUF, &device_buffer) < 0) { HAL_LOGE("QUERYBUF fails: %s", strerror(errno)); + // Return buffer index. + std::lock_guard<std::mutex> guard(buffer_queue_lock_); + buffers_[index].active = false; return -ENODEV; } - // Lock the buffer for writing (fills in the user pointer field). - int res = - gralloc_->lock(camera_buffer, format_->bytes_per_line(), &device_buffer); - if (res) { - HAL_LOGE("Gralloc failed to lock buffer."); - return res; - } + // Setup our request context and fill in the user pointer field. + RequestContext* request_context; + void* data; + { + std::lock_guard<std::mutex> guard(buffer_queue_lock_); + request_context = &buffers_[index]; + request_context->camera_buffer->SetDataSize(device_buffer.length); + request_context->camera_buffer->Reset(); + request_context->camera_buffer->SetFourcc(format_->v4l2_pixel_format()); + request_context->camera_buffer->SetWidth(format_->width()); + request_context->camera_buffer->SetHeight(format_->height()); + request_context->request = request; + data = request_context->camera_buffer->GetData(); + } + device_buffer.m.userptr = reinterpret_cast<unsigned long>(data); + + // Pass the buffer to the camera. if (IoctlLocked(VIDIOC_QBUF, &device_buffer) < 0) { HAL_LOGE("QBUF fails: %s", strerror(errno)); - gralloc_->unlock(&device_buffer); return -ENODEV; } // Mark the buffer as in flight. std::lock_guard<std::mutex> guard(buffer_queue_lock_); - buffers_[index] = true; + request_context->active = true; - if (enqueued_index) { - *enqueued_index = index; - } return 0; } -int V4L2Wrapper::DequeueBuffer(uint32_t* dequeued_index) { +int V4L2Wrapper::DequeueRequest(std::shared_ptr<CaptureRequest>* request) { if (!format_) { HAL_LOGV( "Format not set, so stream can't be on, " @@ -607,23 +676,63 @@ int V4L2Wrapper::DequeueBuffer(uint32_t* dequeued_index) { } } - // Mark the buffer as no longer in flight. - { - std::lock_guard<std::mutex> guard(buffer_queue_lock_); - buffers_[buffer.index] = false; - } - - // Now that we're done painting the buffer, we can unlock it. - res = gralloc_->unlock(&buffer); + std::lock_guard<std::mutex> guard(buffer_queue_lock_); + RequestContext* request_context = &buffers_[buffer.index]; + + // Lock the camera stream buffer for painting. + const camera3_stream_buffer_t* stream_buffer = + &request_context->request->output_buffers[0]; + uint32_t fourcc = + StreamFormat::HalToV4L2PixelFormat(stream_buffer->stream->format); + + if (request) { + *request = request_context->request; + } + + // Note that the device buffer length is passed to the output frame. If the + // GrallocFrameBuffer does not have support for the transformation to + // |fourcc|, it will assume that the amount of data to lock is based on + // |buffer.length|, otherwise it will use the ImageProcessor::ConvertedSize. + arc::GrallocFrameBuffer output_frame( + *stream_buffer->buffer, stream_buffer->stream->width, + stream_buffer->stream->height, fourcc, buffer.length, + stream_buffer->stream->usage); + res = output_frame.Map(); if (res) { - HAL_LOGE("Gralloc failed to unlock buffer after dequeueing."); - return res; + HAL_LOGE("Failed to map output frame."); + request_context->request.reset(); + return -EINVAL; } - - if (dequeued_index) { - *dequeued_index = buffer.index; + if (request_context->camera_buffer->GetFourcc() == fourcc && + request_context->camera_buffer->GetWidth() == + stream_buffer->stream->width && + request_context->camera_buffer->GetHeight() == + stream_buffer->stream->height) { + // If no format conversion needs to be applied, directly copy the data over. + memcpy(output_frame.GetData(), request_context->camera_buffer->GetData(), + request_context->camera_buffer->GetDataSize()); + } else { + // Perform the format conversion. + arc::CachedFrame cached_frame; + cached_frame.SetSource(request_context->camera_buffer.get(), 0); + cached_frame.Convert(request_context->request->settings, &output_frame); } + + request_context->request.reset(); + // Mark the buffer as not in flight. + request_context->active = false; return 0; } +int V4L2Wrapper::GetInFlightBufferCount() { + int count = 0; + std::lock_guard<std::mutex> guard(buffer_queue_lock_); + for (auto& buffer : buffers_) { + if (buffer.active) { + count++; + } + } + return count; +} + } // namespace v4l2_camera_hal diff --git a/modules/camera/3_4/v4l2_wrapper.h b/modules/camera/3_4/v4l2_wrapper.h index c3ad2727..4a8d5170 100644 --- a/modules/camera/3_4/v4l2_wrapper.h +++ b/modules/camera/3_4/v4l2_wrapper.h @@ -26,11 +26,14 @@ #include <android-base/unique_fd.h> +#include "arc/common_types.h" +#include "arc/frame_buffer.h" +#include "capture_request.h" #include "common.h" #include "stream_format.h" -#include "v4l2_gralloc.h" namespace v4l2_camera_hal { + class V4L2Wrapper { public: // Use this method to create V4L2Wrapper objects. Functionally equivalent @@ -67,8 +70,10 @@ class V4L2Wrapper { int32_t* result = nullptr); // Manage format. virtual int GetFormats(std::set<uint32_t>* v4l2_formats); + virtual int GetQualifiedFormats(std::vector<uint32_t>* v4l2_formats); virtual int GetFormatFrameSizes(uint32_t v4l2_format, std::set<std::array<int32_t, 2>>* sizes); + // Durations are returned in ns. virtual int GetFormatFrameDurationRange( uint32_t v4l2_format, @@ -77,15 +82,16 @@ class V4L2Wrapper { virtual int SetFormat(const StreamFormat& desired_format, uint32_t* result_max_buffers); // Manage buffers. - virtual int EnqueueBuffer(const camera3_stream_buffer_t* camera_buffer, - uint32_t* enqueued_index = nullptr); - virtual int DequeueBuffer(uint32_t* dequeued_index = nullptr); + virtual int EnqueueRequest( + std::shared_ptr<default_camera_hal::CaptureRequest> request); + virtual int DequeueRequest( + std::shared_ptr<default_camera_hal::CaptureRequest>* request); + virtual int GetInFlightBufferCount(); private: // Constructor is private to allow failing on bad input. // Use NewV4L2Wrapper instead. - V4L2Wrapper(const std::string device_path, - std::unique_ptr<V4L2Gralloc> gralloc); + V4L2Wrapper(const std::string device_path); // Connect or disconnect to the device. Access by creating/destroying // a V4L2Wrapper::Connection object. @@ -99,20 +105,19 @@ class V4L2Wrapper { inline bool connected() { return device_fd_.get() >= 0; } + // Format management. + const arc::SupportedFormats GetSupportedFormats(); + // The camera device path. For example, /dev/video0. const std::string device_path_; // The opened device fd. android::base::unique_fd device_fd_; // The underlying gralloc module. - std::unique_ptr<V4L2Gralloc> gralloc_; + // std::unique_ptr<V4L2Gralloc> gralloc_; // Whether or not the device supports the extended control query. bool extended_query_supported_; // The format this device is set up for. std::unique_ptr<StreamFormat> format_; - // Map indecies to buffer status. True if the index is in-flight. - // |buffers_.size()| will always be the maximum number of buffers this device - // can handle in its current format. - std::vector<bool> buffers_; // Lock protecting use of the buffer tracker. std::mutex buffer_queue_lock_; // Lock protecting use of the device. @@ -121,6 +126,28 @@ class V4L2Wrapper { std::mutex connection_lock_; // Reference count connections. int connection_count_; + // Supported formats. + arc::SupportedFormats supported_formats_; + // Qualified formats. + arc::SupportedFormats qualified_formats_; + + class RequestContext { + public: + RequestContext() + : active(false), + camera_buffer(std::make_shared<arc::AllocatedFrameBuffer>(0)){}; + ~RequestContext(){}; + // Indicates whether this request context is in use. + bool active; + // Buffer handles of the context. + std::shared_ptr<arc::AllocatedFrameBuffer> camera_buffer; + std::shared_ptr<default_camera_hal::CaptureRequest> request; + }; + + // Map of in flight requests. + // |buffers_.size()| will always be the maximum number of buffers this device + // can handle in its current format. + std::vector<RequestContext> buffers_; friend class Connection; friend class V4L2WrapperMock; diff --git a/modules/camera/3_4/v4l2_wrapper_mock.h b/modules/camera/3_4/v4l2_wrapper_mock.h index d423cc59..f98bc948 100644 --- a/modules/camera/3_4/v4l2_wrapper_mock.h +++ b/modules/camera/3_4/v4l2_wrapper_mock.h @@ -27,7 +27,7 @@ namespace v4l2_camera_hal { class V4L2WrapperMock : public V4L2Wrapper { public: - V4L2WrapperMock() : V4L2Wrapper("", nullptr){}; + V4L2WrapperMock() : V4L2Wrapper(""){}; MOCK_METHOD0(StreamOn, int()); MOCK_METHOD0(StreamOff, int()); MOCK_METHOD2(QueryControl, @@ -36,17 +36,15 @@ class V4L2WrapperMock : public V4L2Wrapper { MOCK_METHOD3(SetControl, int(uint32_t control_id, int32_t desired, int32_t* result)); MOCK_METHOD1(GetFormats, int(std::set<uint32_t>*)); + MOCK_METHOD1(GetQualifiedFormats, int(std::vector<uint32_t>*)); MOCK_METHOD2(GetFormatFrameSizes, int(uint32_t, std::set<std::array<int32_t, 2>>*)); MOCK_METHOD3(GetFormatFrameDurationRange, int(uint32_t, const std::array<int32_t, 2>&, std::array<int64_t, 2>*)); - MOCK_METHOD4(SetFormat, - int(int format, - uint32_t width, - uint32_t height, - uint32_t* result_max_buffers)); + MOCK_METHOD2(SetFormat, int(const StreamFormat& desired_format, + uint32_t* result_max_buffers)); MOCK_METHOD2(EnqueueBuffer, int(const camera3_stream_buffer_t* camera_buffer, uint32_t* enqueued_index)); diff --git a/modules/gralloc/framebuffer.cpp b/modules/gralloc/framebuffer.cpp index 97964572..c1717115 100644 --- a/modules/gralloc/framebuffer.cpp +++ b/modules/gralloc/framebuffer.cpp @@ -332,6 +332,8 @@ int fb_device_open(hw_module_t const* module, const char* name, const_cast<int&>(dev->device.minSwapInterval) = 1; const_cast<int&>(dev->device.maxSwapInterval) = 1; *device = &dev->device.common; + } else { + free(dev); } } return status; diff --git a/modules/sensors/OWNERS b/modules/sensors/OWNERS index 1af35067..d4393d6a 100644 --- a/modules/sensors/OWNERS +++ b/modules/sensors/OWNERS @@ -1,2 +1,2 @@ -pengxu@google.com -ashutoshj@google.com +arthuri@google.com +bduddie@google.com diff --git a/modules/usbaudio/audio_hal.c b/modules/usbaudio/audio_hal.c index 9b606b75..81c9fd61 100644 --- a/modules/usbaudio/audio_hal.c +++ b/modules/usbaudio/audio_hal.c @@ -44,8 +44,6 @@ #include "alsa_device_proxy.h" #include "alsa_logging.h" -#define DEFAULT_INPUT_BUFFER_SIZE_MS 20 - /* Lock play & record samples rates at or above this threshold */ #define RATELOCK_THRESHOLD 96000 @@ -69,6 +67,8 @@ struct audio_device { bool mic_muted; bool standby; + + int32_t inputs_open; /* number of input streams currently open. */ }; struct stream_lock { @@ -85,7 +85,12 @@ struct stream_out { struct audio_device *adev; /* hardware information - only using this for the lock */ - alsa_device_profile * profile; /* Points to the alsa_device_profile in the audio_device */ + const alsa_device_profile *profile; /* Points to the alsa_device_profile in the audio_device. + * Const, so modifications go through adev->out_profile + * and thus should have the hardware lock and ensure + * stream is not active and no other open output streams. + */ + alsa_device_proxy proxy; /* state of the stream */ unsigned hal_channel_count; /* channel count exposed to AudioFlinger. @@ -116,7 +121,12 @@ struct stream_in { struct audio_device *adev; /* hardware information - only using this for the lock */ - alsa_device_profile * profile; /* Points to the alsa_device_profile in the audio_device */ + const alsa_device_profile *profile; /* Points to the alsa_device_profile in the audio_device. + * Const, so modifications go through adev->out_profile + * and thus should have the hardware lock and ensure + * stream is not active and no other open input streams. + */ + alsa_device_proxy proxy; /* state of the stream */ unsigned hal_channel_count; /* channel count exposed to AudioFlinger. @@ -232,7 +242,7 @@ static bool parse_card_device_params(const char *kvpairs, int *card, int *device return *card >= 0 && *device >= 0; } -static char * device_get_parameters(alsa_device_profile * profile, const char * keys) +static char *device_get_parameters(const alsa_device_profile *profile, const char * keys) { if (profile->card < 0 || profile->device < 0) { return strdup(""); @@ -383,12 +393,12 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) else { int saved_card = out->profile->card; int saved_device = out->profile->device; - out->profile->card = card; - out->profile->device = device; - ret_value = profile_read_device_info(out->profile) ? 0 : -EINVAL; + out->adev->out_profile.card = card; + out->adev->out_profile.device = device; + ret_value = profile_read_device_info(&out->adev->out_profile) ? 0 : -EINVAL; if (ret_value != 0) { - out->profile->card = saved_card; - out->profile->device = saved_device; + out->adev->out_profile.card = saved_card; + out->adev->out_profile.device = saved_device; } } } @@ -571,9 +581,9 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev, memset(&proxy_config, 0, sizeof(proxy_config)); /* Pull out the card/device pair */ - parse_card_device_params(address, &(out->profile->card), &(out->profile->device)); + parse_card_device_params(address, &out->adev->out_profile.card, &out->adev->out_profile.device); - profile_read_device_info(out->profile); + profile_read_device_info(&out->adev->out_profile); int ret = 0; @@ -780,18 +790,18 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) device_lock(in->adev); if (card >= 0 && device >= 0 && !profile_is_cached_for(in->profile, card, device)) { - /* cannot read pcm device info if playback is active */ - if (!in->standby) + /* cannot read pcm device info if playback is active, or more than one open stream */ + if (!in->standby || in->adev->inputs_open > 1) ret_value = -ENOSYS; else { int saved_card = in->profile->card; int saved_device = in->profile->device; - in->profile->card = card; - in->profile->device = device; - ret_value = profile_read_device_info(in->profile) ? 0 : -EINVAL; + in->adev->in_profile.card = card; + in->adev->in_profile.device = device; + ret_value = profile_read_device_info(&in->adev->in_profile) ? 0 : -EINVAL; if (ret_value != 0) { - in->profile->card = saved_card; - in->profile->device = saved_device; + in->adev->in_profile.card = saved_card; + in->adev->in_profile.device = saved_device; } } } @@ -931,10 +941,17 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev, ALOGV("adev_open_input_stream() rate:%" PRIu32 ", chanMask:0x%" PRIX32 ", fmt:%" PRIu8, config->sample_rate, config->channel_mask, config->format); - struct stream_in *in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); - int ret = 0; + /* Pull out the card/device pair */ + int32_t card, device; + if (!parse_card_device_params(address, &card, &device)) { + ALOGW("%s fail - invalid address %s", __func__, address); + *stream_in = NULL; + return -EINVAL; + } + struct stream_in * const in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); if (in == NULL) { + *stream_in = NULL; return -ENOMEM; } @@ -966,10 +983,29 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev, struct pcm_config proxy_config; memset(&proxy_config, 0, sizeof(proxy_config)); - /* Pull out the card/device pair */ - parse_card_device_params(address, &(in->profile->card), &(in->profile->device)); - - profile_read_device_info(in->profile); + int ret = 0; + /* Check if an input stream is already open */ + if (in->adev->inputs_open > 0) { + if (!profile_is_cached_for(in->profile, card, device)) { + ALOGW("%s fail - address card:%d device:%d doesn't match existing profile", + __func__, card, device); + ret = -EINVAL; + } + } else { + /* Read input profile only if necessary */ + in->adev->in_profile.card = card; + in->adev->in_profile.device = device; + if (!profile_read_device_info(&in->adev->in_profile)) { + ALOGW("%s fail - cannot read profile", __func__); + ret = -EINVAL; + } + } + if (ret != 0) { + device_unlock(in->adev); + free(in); + *stream_in = NULL; + return ret; + } /* Rate */ if (config->sample_rate == 0) { @@ -1074,6 +1110,10 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev, free(in); } + device_lock(in->adev); + ++in->adev->inputs_open; + device_unlock(in->adev); + return ret; } @@ -1085,6 +1125,12 @@ static void adev_close_input_stream(struct audio_hw_device *hw_dev, adev_remove_stream_from_list(in->adev, &in->list_node); + device_lock(in->adev); + --in->adev->inputs_open; + LOG_ALWAYS_FATAL_IF(in->adev->inputs_open < 0, + "invalid inputs_open: %d", in->adev->inputs_open); + device_unlock(in->adev); + /* Close the pcm device */ in_standby(&stream->common); diff --git a/modules/vehicle/timeUtil.cpp b/modules/vehicle/timeUtil.cpp deleted file mode 100644 index fb27dad6..00000000 --- a/modules/vehicle/timeUtil.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2015 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 <stdint.h> -#include <utils/SystemClock.h> - -extern "C" { - int64_t elapsedRealtimeNano() { - return android::elapsedRealtimeNano(); - } -} diff --git a/modules/vehicle/vehicle.c b/modules/vehicle/vehicle.c deleted file mode 100644 index 66a2137a..00000000 --- a/modules/vehicle/vehicle.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * Copyright (C) 2015 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 "vehicle_hw_default" -#define LOG_NDEBUG 1 -#define RADIO_PRESET_NUM 6 - -#define UNUSED __attribute__((__unused__)) - -#include <errno.h> -#include <inttypes.h> -#include <malloc.h> -#include <pthread.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/prctl.h> -#include <sys/time.h> -#include <time.h> - -#include <log/log.h> -#include <system/radio.h> -#include <hardware/hardware.h> -#include <hardware/vehicle.h> - -extern int64_t elapsedRealtimeNano(); - -static char VEHICLE_MAKE[] = "android_car"; - -typedef struct vehicle_device_impl { - vehicle_hw_device_t vehicle_device; - uint32_t initialized_; - vehicle_event_callback_fn event_fn_; - vehicle_error_callback_fn error_fn_; -} vehicle_device_impl_t ; - -static pthread_mutex_t lock_; - -typedef struct subscription { - // Each subscription has it's own thread. - pthread_t thread_id; - int32_t prop; - float sample_rate; - pthread_mutex_t lock; - // This field should be protected by the above mutex. - // TODO change this to something better as flag alone takes long time to finish. - uint32_t stop_thread; - vehicle_device_impl_t* impl; - pthread_t thread; - pthread_cond_t cond; - char name[100]; -} subscription_t; - -static vehicle_prop_config_t CONFIGS[] = { - { - .prop = VEHICLE_PROPERTY_INFO_MAKE, - .access = VEHICLE_PROP_ACCESS_READ, - .change_mode = VEHICLE_PROP_CHANGE_MODE_STATIC, - .value_type = VEHICLE_VALUE_TYPE_STRING, - .min_sample_rate = 0, - .max_sample_rate = 0, - .hal_data = NULL, - }, - { - .prop = VEHICLE_PROPERTY_GEAR_SELECTION, - .access = VEHICLE_PROP_ACCESS_READ, - .change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE, - .value_type = VEHICLE_VALUE_TYPE_INT32, - .min_sample_rate = 0, - .max_sample_rate = 0, - .hal_data = NULL, - }, - { - .prop = VEHICLE_PROPERTY_DRIVING_STATUS, - .access = VEHICLE_PROP_ACCESS_READ, - .change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE, - .value_type = VEHICLE_VALUE_TYPE_INT32, - .min_sample_rate = 0, - .max_sample_rate = 0, - .hal_data = NULL, - }, - { - .prop = VEHICLE_PROPERTY_PARKING_BRAKE_ON, - .access = VEHICLE_PROP_ACCESS_READ, - .change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE, - .value_type = VEHICLE_VALUE_TYPE_BOOLEAN, - .min_sample_rate = 0, - .max_sample_rate = 0, - .hal_data = NULL, - }, - { - .prop = VEHICLE_PROPERTY_PERF_VEHICLE_SPEED, - .access = VEHICLE_PROP_ACCESS_READ, - .change_mode = VEHICLE_PROP_CHANGE_MODE_CONTINUOUS, - .value_type = VEHICLE_VALUE_TYPE_FLOAT, - .min_sample_rate = 0.1, - .max_sample_rate = 10.0, - .hal_data = NULL, - }, - { - .prop = VEHICLE_PROPERTY_RADIO_PRESET, - .access = VEHICLE_PROP_ACCESS_READ_WRITE, - .change_mode = VEHICLE_PROP_CHANGE_MODE_ON_CHANGE, - .value_type = VEHICLE_VALUE_TYPE_INT32_VEC4, - .vehicle_radio_num_presets = RADIO_PRESET_NUM, - .min_sample_rate = 0, - .max_sample_rate = 0, - .hal_data = NULL, - }, -}; - -vehicle_prop_config_t* find_config(int prop) { - unsigned int i; - for (i = 0; i < sizeof(CONFIGS) / sizeof(vehicle_prop_config_t); i++) { - if (CONFIGS[i].prop == prop) { - return &CONFIGS[i]; - } - } - return NULL; -} - -static int alloc_vehicle_str_from_cstr(const char* string, vehicle_str_t* vehicle_str) { - int len = strlen(string); - vehicle_str->data = (uint8_t*) malloc(len); - if (vehicle_str->data == NULL) { - return -ENOMEM; - } - memcpy(vehicle_str->data, string, len); - vehicle_str->len = len; - return 0; -} - -static vehicle_prop_config_t const * vdev_list_properties(vehicle_hw_device_t* device UNUSED, - int* num_properties) { - ALOGD("vdev_list_properties."); - - *num_properties = sizeof(CONFIGS) / sizeof(vehicle_prop_config_t); - return CONFIGS; -} - -static int vdev_init(vehicle_hw_device_t* device, - vehicle_event_callback_fn event_callback_fn, - vehicle_error_callback_fn error_callback_fn) { - ALOGD("vdev_init."); - vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device; - pthread_mutex_lock(&lock_); - if (impl->initialized_) { - ALOGE("vdev_init: Callback and Error functions are already existing."); - pthread_mutex_unlock(&lock_); - return -EEXIST; - } - - impl->initialized_ = 1; - impl->event_fn_ = event_callback_fn; - impl->error_fn_ = error_callback_fn; - pthread_mutex_unlock(&lock_); - return 0; -} - -static int vdev_release(vehicle_hw_device_t* device) { - vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device; - pthread_mutex_lock(&lock_); - if (!impl->initialized_) { - ALOGD("vdev_release: Already released before, returning early."); - } else { - // unsubscribe_all() - impl->initialized_ = 0; - } - pthread_mutex_unlock(&lock_); - return 0; -} - -static int vdev_get(vehicle_hw_device_t* device UNUSED, vehicle_prop_value_t* data) { - ALOGD("vdev_get."); - //TODO all data supporting read should support get - if (!data) { - ALOGE("vdev_get: Data cannot be null."); - return -EINVAL; - } - vehicle_prop_config_t* config = find_config(data->prop); - if (config == NULL) { - ALOGE("vdev_get: cannot find config 0x%x", data->prop); - return -EINVAL; - } - data->value_type = config->value_type; - // for STATIC type, time can be just 0 instead - data->timestamp = elapsedRealtimeNano(); - int r; - switch (data->prop) { - case VEHICLE_PROPERTY_INFO_MAKE: - r = alloc_vehicle_str_from_cstr(VEHICLE_MAKE, &(data->value.str_value)); - if (r != 0) { - ALOGE("vdev_get: alloc failed"); - return r; - } - break; - - case VEHICLE_PROPERTY_RADIO_PRESET: { - int radio_preset = data->value.int32_array[0]; - if (radio_preset < VEHICLE_RADIO_PRESET_MIN_VALUE || - radio_preset >= RADIO_PRESET_NUM) { - ALOGE("%s Invalid radio preset: %d\n", __func__, radio_preset); - return -1; - } - ALOGD("%s Radio Preset number: %d", __func__, radio_preset); - int32_t selector = radio_preset % 2 == 0; - // Populate the channel and subchannel to be some variation of the - // preset number for mocking. - - // Restore the preset number. - data->value.int32_array[0] = radio_preset; - // Channel type values taken from - // system/core/include/system/radio.h - data->value.int32_array[1] = selector ? RADIO_BAND_FM : RADIO_BAND_AM; - // For FM set a value in Mhz and for AM set a value in Khz range - // (channel). - data->value.int32_array[2] = selector ? 99000000 : 100000; - // For FM we have a sub-channel and we care about it, for AM pass - // a dummy value. - data->value.int32_array[3] = selector ? radio_preset : -1; - break; - } - - default: - // actual implementation will be much complex than this. It should track proper last - // state. Here just fill with zero. - memset(&(data->value), 0, sizeof(data->value)); - break; - } - ALOGI("vdev_get, type 0x%x, time %" PRId64 ", value_type %d", data->prop, data->timestamp, - data->value_type); - return 0; -} - -static void vdev_release_memory_from_get(struct vehicle_hw_device* device UNUSED, - vehicle_prop_value_t *data) { - switch (data->value_type) { - case VEHICLE_VALUE_TYPE_STRING: - case VEHICLE_VALUE_TYPE_BYTES: - free(data->value.str_value.data); - data->value.str_value.data = NULL; - break; - default: - ALOGW("release_memory_from_get for property 0x%x which is not string or bytes type 0x%x" - , data->prop, data->value_type); - break; - } -} - -static int vdev_set(vehicle_hw_device_t* device UNUSED, const vehicle_prop_value_t* data) { - ALOGD("vdev_set."); - // Just print what data will be setting here. - ALOGD("Setting property %d with value type %d\n", data->prop, data->value_type); - vehicle_prop_config_t* config = find_config(data->prop); - if (config == NULL) { - ALOGE("vdev_set: cannot find config 0x%x", data->prop); - return -EINVAL; - } - if (config->value_type != data->value_type) { - ALOGE("vdev_set: type mismatch, passed 0x%x expecting 0x%x", data->value_type, - config->value_type); - return -EINVAL; - } - switch (data->value_type) { - case VEHICLE_VALUE_TYPE_FLOAT: - ALOGD("Value type: FLOAT\nValue: %f\n", data->value.float_value); - break; - case VEHICLE_VALUE_TYPE_INT32: - ALOGD("Value type: INT32\nValue: %" PRId32 "\n", data->value.int32_value); - break; - case VEHICLE_VALUE_TYPE_INT64: - ALOGD("Value type: INT64\nValue: %" PRId64 "\n", data->value.int64_value); - break; - case VEHICLE_VALUE_TYPE_BOOLEAN: - ALOGD("Value type: BOOLEAN\nValue: %d\n", data->value.boolean_value); - break; - case VEHICLE_VALUE_TYPE_STRING: - ALOGD("Value type: STRING\n Size: %d\n", data->value.str_value.len); - // NOTE: We only handle ASCII strings here. - // Print the UTF-8 string. - char *ascii_out = (char *) malloc ((data->value.str_value.len + 1) * sizeof (char)); - memcpy(ascii_out, data->value.str_value.data, data->value.str_value.len); - ascii_out[data->value.str_value.len] = '\0'; - ALOGD("Value: %s\n", ascii_out); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC4: - ALOGD("Value type: INT32_VEC4\nValue[0]: %d Value[1] %d Value[2] %d Value[3] %d", - data->value.int32_array[0], data->value.int32_array[1], - data->value.int32_array[2], data->value.int32_array[3]); - break; - default: - ALOGD("Value type not yet handled: %d.\n", data->value_type); - } - return 0; -} - -void print_subscribe_info(vehicle_device_impl_t* impl UNUSED) { - unsigned int i; - for (i = 0; i < sizeof(CONFIGS) / sizeof(vehicle_prop_config_t); i++) { - subscription_t* sub = (subscription_t*)CONFIGS[i].hal_data; - if (sub != NULL) { - ALOGD("prop: %d rate: %f", sub->prop, sub->sample_rate); - } - } -} - -// This should be run in a separate thread always. -void fake_event_thread(struct subscription *sub) { - if (!sub) { - ALOGE("oops! subscription object cannot be NULL."); - exit(-1); - } - prctl(PR_SET_NAME, (unsigned long)sub->name, 0, 0, 0); - // Emit values in a loop, every 2 seconds. - while (1) { - // Create a random value depending on the property type. - vehicle_prop_value_t event; - event.prop = sub->prop; - event.timestamp = elapsedRealtimeNano(); - switch (sub->prop) { - case VEHICLE_PROPERTY_GEAR_SELECTION: - event.value_type = VEHICLE_VALUE_TYPE_INT32; - switch ((event.timestamp & 0x30000000)>>28) { - case 0: - event.value.gear_selection = VEHICLE_GEAR_PARK; - break; - case 1: - event.value.gear_selection = VEHICLE_GEAR_NEUTRAL; - break; - case 2: - event.value.gear_selection = VEHICLE_GEAR_DRIVE; - break; - case 3: - event.value.gear_selection = VEHICLE_GEAR_REVERSE; - break; - } - break; - case VEHICLE_PROPERTY_PARKING_BRAKE_ON: - event.value_type = VEHICLE_VALUE_TYPE_BOOLEAN; - if (event.timestamp & 0x20000000) { - event.value.parking_brake = VEHICLE_FALSE; - } else { - event.value.parking_brake = VEHICLE_TRUE; - } - break; - case VEHICLE_PROPERTY_PERF_VEHICLE_SPEED: - event.value_type = VEHICLE_VALUE_TYPE_FLOAT; - event.value.vehicle_speed = (float) ((event.timestamp & 0xff000000)>>24); - break; - case VEHICLE_PROPERTY_RADIO_PRESET: - event.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4; - int presetInfo1[4] = {1 /* preset number */, 0 /* AM Band */, 1000, 0}; - int presetInfo2[4] = {2 /* preset number */, 1 /* FM Band */, 1000, 0}; - if (event.timestamp & 0x20000000) { - memcpy(event.value.int32_array, presetInfo1, sizeof(presetInfo1)); - } else { - memcpy(event.value.int32_array, presetInfo2, sizeof(presetInfo2)); - } - break; - default: // unsupported - if (sub->impl == NULL) { - ALOGE("subscription impl NULL"); - return; - } - if (sub->impl->error_fn_ != NULL) { - sub->impl->error_fn_(-EINVAL, VEHICLE_PROPERTY_INVALID, - VEHICLE_OPERATION_GENERIC); - } else { - ALOGE("Error function is null"); - } - ALOGE("Unsupported prop 0x%x, quit", sub->prop); - return; - } - if (sub->impl->event_fn_ != NULL) { - sub->impl->event_fn_(&event); - } else { - ALOGE("Event function is null"); - return; - } - pthread_mutex_lock(&sub->lock); - if (sub->stop_thread) { - ALOGD("exiting subscription request here."); - // Do any cleanup here. - pthread_mutex_unlock(&sub->lock); - return; - } - struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - now.tv_sec += 1; // sleep for one sec - pthread_cond_timedwait(&sub->cond, &sub->lock, &now); - pthread_mutex_unlock(&sub->lock); - } -} - -static int vdev_subscribe(vehicle_hw_device_t* device, int32_t prop, float sample_rate, - int32_t zones UNUSED) { - ALOGD("vdev_subscribe 0x%x, %f", prop, sample_rate); - vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device; - // Check that the device is initialized. - pthread_mutex_lock(&lock_); - if (!impl->initialized_) { - pthread_mutex_unlock(&lock_); - ALOGE("vdev_subscribe: have you called init()?"); - return -EINVAL; - } - vehicle_prop_config_t* config = find_config(prop); - if (config == NULL) { - pthread_mutex_unlock(&lock_); - ALOGE("vdev_subscribe not supported property 0x%x", prop); - return -EINVAL; - } - if ((config->access != VEHICLE_PROP_ACCESS_READ) && - (config->access != VEHICLE_PROP_ACCESS_READ_WRITE)) { - pthread_mutex_unlock(&lock_); - ALOGE("vdev_subscribe read not supported on the property 0x%x", prop); - return -EINVAL; - } - if (config->change_mode == VEHICLE_PROP_CHANGE_MODE_STATIC) { - pthread_mutex_unlock(&lock_); - ALOGE("vdev_subscribe cannot subscribe static property 0x%x", prop); - return -EINVAL; - } - if ((config->change_mode == VEHICLE_PROP_CHANGE_MODE_ON_CHANGE) && (sample_rate != 0)) { - pthread_mutex_unlock(&lock_); - ALOGE("vdev_subscribe on change type should have 0 sample rate, property 0x%x, sample rate %f", - prop, sample_rate); - return -EINVAL; - } - if ((config->max_sample_rate < sample_rate) || (config->min_sample_rate > sample_rate)) { - - ALOGE("vdev_subscribe property 0x%x, invalid sample rate %f, min:%f, max:%f", - prop, sample_rate, config->min_sample_rate, config->max_sample_rate); - pthread_mutex_unlock(&lock_); - return -EINVAL; - } - subscription_t* sub = (subscription_t*)config->hal_data; - if (sub == NULL) { - sub = calloc(1, sizeof(subscription_t)); - sub->prop = prop; - sub->sample_rate = sample_rate; - sub->stop_thread = 0; - sub->impl = impl; - pthread_mutex_init(&sub->lock, NULL); - pthread_cond_init(&sub->cond, NULL); - config->hal_data = sub; - sprintf(sub->name, "vhal0x%x", prop); - } else if (sub->sample_rate != sample_rate){ // sample rate changed - //TODO notify this to fake sensor thread - sub->sample_rate = sample_rate; - pthread_mutex_unlock(&lock_); - return 0; - } - int ret_code = pthread_create( - &sub->thread, NULL, (void *(*)(void*))fake_event_thread, sub); - if (ret_code != 0) { - return -ret_code; - } - print_subscribe_info(impl); - pthread_mutex_unlock(&lock_); - return 0; -} - -static int vdev_unsubscribe(vehicle_hw_device_t* device, int32_t prop) { - ALOGD("vdev_unsubscribe 0x%x", prop); - vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device; - pthread_mutex_lock(&lock_); - vehicle_prop_config_t* config = find_config(prop); - if (config == NULL) { - pthread_mutex_unlock(&lock_); - return -EINVAL; - } - subscription_t* sub = (subscription_t*)config->hal_data; - if (sub == NULL) { - pthread_mutex_unlock(&lock_); - return -EINVAL; - } - config->hal_data = NULL; - pthread_mutex_unlock(&lock_); - pthread_mutex_lock(&sub->lock); - sub->stop_thread = 1; - pthread_cond_signal(&sub->cond); - pthread_mutex_unlock(&sub->lock); - pthread_join(sub->thread, NULL); - pthread_cond_destroy(&sub->cond); - pthread_mutex_destroy(&sub->lock); - free(sub); - pthread_mutex_lock(&lock_); - print_subscribe_info(impl); - pthread_mutex_unlock(&lock_); - return 0; -} - -static int vdev_close(hw_device_t* device) { - vehicle_device_impl_t* impl = (vehicle_device_impl_t*)device; - if (impl) { - free(impl); - return 0; - } else { - return -1; - } -} - -static int vdev_dump(struct vehicle_hw_device* device UNUSED, int fd UNUSED) { - //TODO - return 0; -} - -/* - * The open function is provided as an interface in harwdare.h which fills in - * all the information about specific implementations and version specific - * informations in hw_device_t structure. After calling open() the client should - * use the hw_device_t to execute any Vehicle HAL device specific functions. - */ -static int vdev_open(const hw_module_t* module, const char* name UNUSED, - hw_device_t** device) { - ALOGD("vdev_open"); - - // Oops, out of memory! - vehicle_device_impl_t* vdev = calloc(1, sizeof(vehicle_device_impl_t)); - if (vdev == NULL) { - return -ENOMEM; - } - - // Common functions provided by harware.h to access module and device(s). - vdev->vehicle_device.common.tag = HARDWARE_DEVICE_TAG; - vdev->vehicle_device.common.version = VEHICLE_DEVICE_API_VERSION_1_0; - vdev->vehicle_device.common.module = (hw_module_t *) module; - vdev->vehicle_device.common.close = vdev_close; - - // Define the Vehicle HAL device specific functions. - vdev->vehicle_device.list_properties = vdev_list_properties; - vdev->vehicle_device.init = vdev_init; - vdev->vehicle_device.release = vdev_release; - vdev->vehicle_device.get = vdev_get; - vdev->vehicle_device.release_memory_from_get = vdev_release_memory_from_get; - vdev->vehicle_device.set = vdev_set; - vdev->vehicle_device.subscribe = vdev_subscribe; - vdev->vehicle_device.unsubscribe = vdev_unsubscribe; - vdev->vehicle_device.dump = vdev_dump; - - *device = (hw_device_t *) vdev; - return 0; -} - -static struct hw_module_methods_t hal_module_methods = { - .open = vdev_open, -}; - -/* - * This structure is mandatory to be implemented by each HAL implementation. It - * exposes the open method (see hw_module_methods_t above) which opens a device. - * The vehicle HAL is supposed to be used as a single device HAL hence all the - * functions should be implemented inside of the vehicle_hw_device_t struct (see - * the vehicle.h in include/ folder. - */ -vehicle_module_t HAL_MODULE_INFO_SYM = { - .common = { - .tag = HARDWARE_MODULE_TAG, - .module_api_version = VEHICLE_MODULE_API_VERSION_1_0, - .hal_api_version = HARDWARE_HAL_API_VERSION, - .id = VEHICLE_HARDWARE_MODULE_ID, - .name = "Default vehicle HW HAL", - .author = "", - .methods = &hal_module_methods, - }, -}; diff --git a/tests/vehicle/Android.bp b/tests/vehicle/Android.bp deleted file mode 100644 index cbe2a11d..00000000 --- a/tests/vehicle/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (C) 2015 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. - -// Build native tests. -cc_test { - name: "vehicle_tests", - srcs: ["vehicle_tests.cpp"], - - shared_libs: [ - "liblog", - "libhardware", - ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - ], -} - -// Build HAL command line utility. -cc_binary { - name: "vehicle-hal-tool", - srcs: ["vehicle-hal-tool.c"], - cflags: [ - "-Wall", - "-Wno-unused-parameter", - "-Werror", - ], - - shared_libs: [ - "libcutils", - "libhardware", - "liblog", - ], -} diff --git a/tests/vehicle/README b/tests/vehicle/README deleted file mode 100644 index 42d2210b..00000000 --- a/tests/vehicle/README +++ /dev/null @@ -1,73 +0,0 @@ -What does this document tell? - -This document details how to use the vehicle service if you are implementhing -HAL. It lists the various places to look for code and how to build and test the -code on your own dev device. - -This code also provides a simple command line utility for the target to test the -vehicle HAL. - -What is the code? - -The code is split into folowing logical components: -a) hardware/libhardware/include/hardware/vehicle.h - this is the main HAL -interface that will be required to be implemented by the OEMs. It includes all -documentation necessary to understand what vehicle subsystems are exposed, -various units, capabilities and any other relevant details about the HAL design -itself. - -b) hardware/libhardware/modules/vehicle/vehicle.c -This is a reference implementation for the OEMs to have a peek into getting -started with a barebones structure. There are implementation for each of the -critical HAL functions such as, get(), set() and subscribe(). - -c) hardware/libhardware/tests/vehicle/vehicle_test.cpp & vehicle_test_fixtures.h -These are native tests that can be run on the target to validate basic -features of HAL implementation. Things such as loading of HAL and -basic functions are implemented (by check if the returned functions are not NULL -pointers) can be asserted. It also checks if the subscribe function is doing its -job by spitting out data at continuous intervals and printed on the stdout. - -d) hardware/libhardware/tests/vehicle/vehicle-hal-tool.c -This tool will provide you with a simple utility which can set commands to the -HAL such as: -i) Getting a property (and printing its value). -ii) Setting a property (and the HAL will take some setting action). -iii) Subscribe to a property (and the HAL should send you values at some certain -intevals). - -See the usage() function in vehicle-hal-tool.c for details on how to use the -binary. - -How to build and run? - -You can build everything by issuing the following from the top of directory. It -is assumed that you have done a first run of make from the top level so that -intermediates are generated. - -$ croot -$ mmm hardware/libhardware - -This will generate the following binaries that we care about: -i) out/target/product/XXX/vendor/lib/hw/vehicle.default.so -ii) out/target/product/XXX/data/nativetest/vehicle_tests -iii) out/target/product/XXX/system/bin/vehicle-hal-tool - -The location for the first shared library would be: -$ adb push out/target/product/XXX/vendor/lib/hw/vehicle.default.so -/vendor/lib/hw -You can also use 'adb sync' if you like, although this is the easiest least -hassle way of putting it in place. - -The second binary is a native test - which is nothing but an executable for the -target device. You can load it anywhere in your /data directory and run it as: -$ adb push out/target/product/XXX/data/nativetest/vehicle_tests -/data/tmp/vehicle_tests -$ adb shell -$ ./data/tmp/vehicle_tests -<...output should be spitted with passing tests for atleast the reference -implementation ...> - -The last binary is the command line tool, to push the binary on your target do: -$ adb push out/target/product/XXX/system/bin/vehicle-hal-tool -/data/tmp/vehicle-hal-tool diff --git a/tests/vehicle/vehicle-hal-tool.c b/tests/vehicle/vehicle-hal-tool.c deleted file mode 100644 index 94624d34..00000000 --- a/tests/vehicle/vehicle-hal-tool.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (C) 2015 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 "vehicle-hal-tool" - -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <log/log.h> - -#include <hardware/hardware.h> -#include <hardware/vehicle.h> - -void usage() { - printf("Usage: " - "./vehicle-hal-tool [-l] [-m -p -t [-v]]\n" - "-l - List properties\n" - "-m - Mode (cannot be used with -l). Accepted strings: get, set or sub.\n" - "-p - Property (only used with -m)\n" - "-t - Type (only used with -m)\n" - "-w - Wait time in seconds (only used with -m set to sub)\n" - "-v - Value to which vehicle_prop_value is set\n" - "Depending on the type pass the value:\n" - "Int: pass a quoted integer\n" - "Float: pass a quoted float\n" - "Int array: pass a quoted space delimited int array, eg: \"1 2 3 4\" for\n:" - "setting int32_array's all 4 elements (see VEHICLE_VALUE_TYPE_INT32_VEC4\n" - "String: pass a normal string\n\n" - "The configurations to use the tool are as follows:\n" - "List Properties\n" - "---------------\n" - "./vehicle-hal-tool -l \n" - "Lists the various properties defined in HAL implementation. Use this to check if " - "the HAL implementation is correctly set up and exposing the capabilities correctly.\n" - - "Get Properties\n" - "---------------\n" - "./vehicle-hal-tool -m get -p <prop> -t <type> [-v <vehicle_prop_value>]\n" - "Example: ./vehicle-hal-tool -m get -p 1028 -t 3 # VEHICLE_PROPERTY_DRIVING_STATUS\n" - "./vehicle-hal-tool -m get -p 257 -t 1 # VEHICLE_PROPERTY_INFO_MAKE\n" - "./vehicle-hal-tool -m get -p 2049 -t 19 -v \"3 0 0 0\"\n" - " # VEHICLE_PROPERTY_RADIO_PRESET\n" - "with preset value set to 3.\n\n" - "Set properties\n" - "--------------\n" - "./vehicle-hal-tool -m set -p 10 -t 1 -v random_property\n" - "Set properties may not be applicable to most properties\n\n" - "Subscribe properties\n" - "--------------------\n" - "Subscribes to be notified about a property change (depending on whether\n" - "it is a on change property or a continuous property) for seconds provided\n" - "as -w paramter.\n" - "./vehicle-hal-tool -m sub -p 1028 -w 10\n" - ); -} - -void list_all_properties(vehicle_hw_device_t *device) { - int num_configs = -1; - const vehicle_prop_config_t *configs = device->list_properties(device, &num_configs); - if (num_configs < 0) { - printf("List configs error. %d", num_configs); - exit(1); - } - - printf("Listing configs\n--------------------\n"); - int i = 0; - for (i = 0; i < num_configs; i++) { - const vehicle_prop_config_t *config_temp = configs + i; - printf("Property ID: %d\n" - "Property config_flags: %d\n" - "Property change mode: %d\n" - "Property min sample rate: %f\n" - "Property max sample rate: %f\n", - config_temp->prop, config_temp->config_flags, config_temp->change_mode, - config_temp->min_sample_rate, config_temp->max_sample_rate); - } -} - -static void print_property(const vehicle_prop_value_t *data) { - switch (data->value_type) { - case VEHICLE_VALUE_TYPE_STRING: - printf("Value type: STRING\n Size: %d\n", data->value.str_value.len); - // This implementation only supports ASCII. - char *ascii_out = (char *) malloc((data->value.str_value.len + 1) * sizeof(char)); - memcpy(ascii_out, data->value.str_value.data, data->value.str_value.len); - ascii_out[data->value.str_value.len] = '\0'; - printf("Value Type: STRING %s\n", ascii_out); - free(ascii_out); - break; - case VEHICLE_VALUE_TYPE_BYTES: - printf("Value type: BYTES\n Size: %d", data->value.bytes_value.len); - for (int i = 0; i < data->value.bytes_value.len; i++) { - if ((i % 16) == 0) { - printf("\n %04X: ", i); - } - printf("%02X ", data->value.bytes_value.data[i]); - } - printf("\n"); - break; - case VEHICLE_VALUE_TYPE_BOOLEAN: - printf("Value type: BOOLEAN\nValue: %d\n", data->value.boolean_value); - break; - case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN: - printf("Value type: ZONED_BOOLEAN\nZone: %d\n", data->zone); - printf("Value: %d\n", data->value.boolean_value); - break; - case VEHICLE_VALUE_TYPE_INT64: - printf("Value type: INT64\nValue: %" PRId64 "\n", data->value.int64_value); - break; - case VEHICLE_VALUE_TYPE_FLOAT: - printf("Value type: FLOAT\nValue: %f\n", data->value.float_value); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC2: - printf("Value type: FLOAT_VEC2\nValue[0]: %f ", data->value.float_array[0]); - printf("Value[1]: %f\n", data->value.float_array[1]); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC3: - printf("Value type: FLOAT_VEC3\nValue[0]: %f ", data->value.float_array[0]); - printf("Value[1]: %f ", data->value.float_array[1]); - printf("Value[2]: %f\n", data->value.float_array[2]); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC4: - printf("Value type: FLOAT_VEC4\nValue[0]: %f ", data->value.float_array[0]); - printf("Value[1]: %f ", data->value.float_array[1]); - printf("Value[2]: %f ", data->value.float_array[2]); - printf("Value[3]: %f\n", data->value.float_array[3]); - break; - case VEHICLE_VALUE_TYPE_INT32: - printf("Value type: INT32\nValue: %d\n", data->value.int32_value); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC2: - printf("Value type: INT32_VEC2\nValue[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d\n", data->value.int32_array[1]); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC3: - printf("Value type: INT32_VEC3\nValue[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d ", data->value.int32_array[1]); - printf("Value[2]: %d\n", data->value.int32_array[2]); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC4: - printf("Value type: INT32_VEC4\nValue[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d ", data->value.int32_array[1]); - printf("Value[2]: %d ", data->value.int32_array[2]); - printf("Value[3]: %d\n", data->value.int32_array[3]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT: - printf("Value type: ZONED_FLOAT\nZone: %d ", data->zone); - printf("Value: %f\n", data->value.float_value); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: - printf("Value type: ZONED_FLOAT_VEC2\nZone: %d ", data->zone); - printf("Value[0]: %f", data->value.float_array[0]); - printf("Value[1]: %f\n", data->value.float_array[1]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: - printf("Value type: ZONED_FLOAT_VEC3\nZone: %d ", data->zone); - printf("Value[0]: %f ", data->value.float_array[0]); - printf("Value[1]: %f ", data->value.float_array[1]); - printf("Value[2]: %f\n", data->value.float_array[2]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4: - printf("Value type: ZONED_FLOAT_VEC4\nZone: %d ", data->zone); - printf("Value[0]: %f ", data->value.float_array[0]); - printf("Value[1]: %f ", data->value.float_array[1]); - printf("Value[2]: %f ", data->value.float_array[2]); - printf("Value[3]: %f\n", data->value.float_array[3]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32: - printf("Value type: ZONED_INT32\nZone: %d ", data->zone); - printf("Value: %d\n", data->value.int32_value); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: - printf("Value type: ZONED_INT32_VEC2\nZone: %d ", data->zone); - printf("Value[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d\n", data->value.int32_array[1]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: - printf("Value type: ZONED_INT32_VEC3\nZone: %d ", data->zone); - printf("Value[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d ", data->value.int32_array[1]); - printf("Value[2]: %d\n", data->value.int32_array[2]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: - printf("Value type: ZONED_INT32_VEC4\nZone: %d ", data->zone); - printf("Value[0]: %d ", data->value.int32_array[0]); - printf("Value[1]: %d ", data->value.int32_array[1]); - printf("Value[2]: %d ", data->value.int32_array[2]); - printf("Value[3]: %d\n", data->value.int32_array[3]); - break; - default: - printf("Value type not yet handled: %d.\n", data->value_type); - } -} - -void get_property( - vehicle_hw_device_t *device, int32_t property, int32_t type, char *value_string) { - vehicle_prop_value_t *data = (vehicle_prop_value_t *) malloc (sizeof(vehicle_prop_value_t)); - - // Parse the string according to type. - if (value_string != NULL && strlen(value_string) > 0) { - switch (type) { - case VEHICLE_VALUE_TYPE_INT32: - sscanf(value_string, "%d", &(data->value.int32_value)); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC4: - { - int32_t vec[4]; - sscanf(value_string, "%d %d %d %d", &vec[0], &vec[1], &vec[2], &vec[3]); - memcpy(data->value.int32_array, vec, sizeof(vec)); - break; - } - default: - printf("%s Setting value type not supported: %d\n", __func__, type); - exit(1); - } - } - - data->prop = property; - int ret_code = device->get(device, data); - if (ret_code != 0) { - printf("Cannot get property: %d\n", ret_code); - exit(1); - } - - // We simply convert the data into the type mentioned by the result of the - // get call. - printf("Get output\n------------\n"); - print_property(data); - free(data); -} - -void set_property(vehicle_hw_device_t *device, - int32_t property, - int32_t type, - char *data) { - vehicle_prop_value_t vehicle_data; - vehicle_data.prop = property; - vehicle_data.value_type = type; - int32_t zone = 0; - float value = 0.0; - switch (type) { - case VEHICLE_VALUE_TYPE_STRING: - // TODO: Make the code generic to UTF8 characters. - vehicle_data.value.str_value.len = strlen(data); - vehicle_data.value.str_value.data = - (uint8_t *) malloc (strlen(data) * sizeof(uint8_t)); - memcpy(vehicle_data.value.str_value.data, data, strlen(data) + 1); - break; - case VEHICLE_VALUE_TYPE_BYTES: { - int len = strlen(data); - int numBytes = (len + 1) / 3; - uint8_t *buf = calloc(numBytes, sizeof(uint8_t)); - char *byte = strtok(data, " "); - for (int i = 0; byte != NULL && i < numBytes; i++) { - buf[i] = strtol(data, NULL, 16); - byte = strtok(NULL, " "); - } - vehicle_data.value.bytes_value.len = numBytes; - vehicle_data.value.bytes_value.data = buf; - } - break; - case VEHICLE_VALUE_TYPE_BOOLEAN: - vehicle_data.value.boolean_value = atoi(data); - break; - case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN: - sscanf(data, "%d %d", &vehicle_data.zone, - &vehicle_data.value.boolean_value); - break; - case VEHICLE_VALUE_TYPE_INT64: - vehicle_data.value.int64_value = atoi(data); - break; - case VEHICLE_VALUE_TYPE_FLOAT: - vehicle_data.value.float_value = atof(data); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC2: - sscanf(data, "%f %f", &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1]); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC3: - sscanf(data, "%f %f %f", &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1], - &vehicle_data.value.float_array[2]); - break; - case VEHICLE_VALUE_TYPE_FLOAT_VEC4: - sscanf(data, "%f %f %f %f", &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1], - &vehicle_data.value.float_array[2], - &vehicle_data.value.float_array[3]); - break; - case VEHICLE_VALUE_TYPE_INT32: - vehicle_data.value.int32_value = atoi(data); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC2: - sscanf(data, "%d %d", &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1]); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC3: - sscanf(data, "%d %d %d", &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1], - &vehicle_data.value.int32_array[2]); - break; - case VEHICLE_VALUE_TYPE_INT32_VEC4: - sscanf(data, "%d %d %d %d", &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1], - &vehicle_data.value.int32_array[2], - &vehicle_data.value.int32_array[3]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT: - sscanf(data, "%d %f", &zone, &value); - vehicle_data.zone = zone; - vehicle_data.value.float_value = value; - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: - sscanf(data, "%d %f %f", &vehicle_data.zone, - &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: - sscanf(data, "%d %f %f %f", &vehicle_data.zone, - &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1], - &vehicle_data.value.float_array[2]); - break; - case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4: - sscanf(data, "%d %f %f %f %f", &vehicle_data.zone, - &vehicle_data.value.float_array[0], - &vehicle_data.value.float_array[1], - &vehicle_data.value.float_array[2], - &vehicle_data.value.float_array[3]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32: - sscanf(data, "%d %d", &vehicle_data.zone, - &vehicle_data.value.int32_value); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: - sscanf(data, "%d %d %d", &vehicle_data.zone, - &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: - sscanf(data, "%d %d %d %d", &vehicle_data.zone, - &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1], - &vehicle_data.value.int32_array[2]); - break; - case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: - sscanf(data, "%d %d %d %d %d", &vehicle_data.zone, - &vehicle_data.value.int32_array[0], - &vehicle_data.value.int32_array[1], - &vehicle_data.value.int32_array[2], - &vehicle_data.value.int32_array[3]); - break; - default: - printf("set_property: Value type not yet handled: %d\n", type); - exit(1); - } - printf("Setting Property id: %d\n", vehicle_data.prop); - print_property(&vehicle_data); - - int ret_code = device->set(device, &vehicle_data); - if (ret_code != 0) { - printf("Cannot set property: %d\n", ret_code); - exit(1); - } -} - -int vehicle_event_callback(const vehicle_prop_value_t *event_data) { - // Print what we got. - printf("Got some value from callback property: %d\n", event_data->prop); - printf("Timestamp: %" PRId64 "\n", event_data->timestamp); - print_property(event_data); - return 0; -} - -int vehicle_error_callback(int32_t error_code, int32_t property, int32_t operation) { - // Print what we got. - printf("Error code obtained: %d\n", error_code); - return 0; -} - -void subscribe_to_property( - vehicle_hw_device_t *device, - int32_t prop, - float sample_rate, - uint32_t wait_in_seconds) { - // Init the device with a callback. - int ret_code = device->subscribe(device, prop, 0, 0); - if (ret_code != 0) { - printf("Could not subscribe: %d\n", ret_code); - exit(1); - } - - // Callbacks will happen on one of the threads created by the HAL hence we - // can simply sleep here and see the output. - sleep(wait_in_seconds); - - // Unsubscribe and uninit. - ret_code = device->unsubscribe(device, prop); - if (ret_code != 0) { - printf("Error unsubscribing the HAL, still continuining to uninit HAL ..."); - } -} - -int main(int argc, char* argv[]) { - // Open the vehicle module and just ask for the list of properties. - const hw_module_t *hw_module = NULL; - int ret_code = hw_get_module(VEHICLE_HARDWARE_MODULE_ID, &hw_module); - if (ret_code != 0) { - printf("Cannot open the hw module. Does the HAL exist? %d\n", ret_code); - return -1; - } - - vehicle_module_t *vehicle_module = (vehicle_module_t *)(hw_module); - hw_device_t *device = NULL; - ret_code = vehicle_module->common.methods->open(hw_module, NULL, &device); - if (!device) { - printf("Cannot open the hw device: %d\n", ret_code); - return -1; - } - vehicle_hw_device_t *vehicle_device = (vehicle_hw_device_t *) (device); - printf("HAL Loaded!\n"); - - vehicle_device->init(vehicle_device, vehicle_event_callback, vehicle_error_callback); - - // If this is a list properties command - we check for -l command. - int list_properties = 0; - // Type of the property (see #defines in vehicle.h). - int property = -1; - // Type of the value of the property (see enum vehicle_value_type). - int type = -1; - // Whether the mode is "get" or "set". - char mode[100] = ""; - // Actual value as a string representation (supports only PODs for now). - // TODO: Support structures and complex types in the tool. - char value[100] = ""; - // Wait time for the subscribe type of calls. - // We keep a default in case the user does not specify one. - int wait_time_in_sec = 10; - // Sample rate for subscribe type of calls. - // Default value is 0 for onchange type of properties. - int sample_rate = 0; - // Int array string which represents the vehicle_value_t in array of - // numbers. See vehicle_prop_value_t.value.int32_array. - char int_array_string[1000]; int_array_string[0] = '\0'; - - int opt; - while ((opt = getopt(argc, argv, "lm:p:t:v:w:s:")) != -1) { - switch (opt) { - case 'l': - list_properties = 1; - break; - case 'm': - strcpy(mode, optarg); - break; - case 'p': - property = atoi(optarg); - break; - case 't': - type = atoi(optarg); - break; - case 'v': - strcpy(value, optarg); - break; - case 'w': - wait_time_in_sec = atoi(optarg); - break; - case 's': - sample_rate = atoi(optarg); - break; - } - } - - // We should have atleast one of list properties or mode (for get or set). - if (!list_properties && - !(!strcmp(mode, "get") || !strcmp(mode, "set") || !strcmp(mode, "sub"))) { - usage(); - exit(1); - } - - if (list_properties) { - printf("Listing properties...\n"); - list_all_properties(vehicle_device); - } else if (!strcmp(mode, "get")) { - printf("Getting property ...\n"); - if (property == -1) { - printf("Use -p to pass a valid Property.\n"); - usage(); - exit(1); - } - - int32_t int_array_list[4]; - int count = -1; - if (strlen(int_array_string) > 0) { - count = sscanf(int_array_string, "%d%d%d%d", - &int_array_list[0], &int_array_list[1], &int_array_list[2], &int_array_list[3]); - } - - get_property(vehicle_device, property, type, value); - } else if (!strcmp(mode, "set")) { - printf("Setting property ...\n"); - if (property == -1 || type == -1) { - printf("Use -p to pass a valid Property and -t to pass a valid Type.\n"); - usage(); - exit(1); - } - set_property(vehicle_device, property, type, value); - } else if (!strcmp(mode, "sub")) { - printf("Subscribing property ...\n"); - if (property == -1 || wait_time_in_sec <= 0) { - printf("Use -p to pass a valid property and -w to pass a valid wait time(s)\n"); - usage(); - exit(1); - } - subscribe_to_property(vehicle_device, property, sample_rate, wait_time_in_sec); - } - - ret_code = vehicle_device->release(vehicle_device); - if (ret_code != 0) { - printf("Error uniniting HAL, exiting anyways."); - } - return 0; -} diff --git a/tests/vehicle/vehicle_test_fixtures.h b/tests/vehicle/vehicle_test_fixtures.h deleted file mode 100644 index a9572baf..00000000 --- a/tests/vehicle/vehicle_test_fixtures.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2015 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_HAL_VEHICLE_TEST_ -#define __ANDROID_HAL_VEHICLE_TEST_ - -#include <gtest/gtest.h> -#include <hardware/hardware.h> -#include <hardware/vehicle.h> - -namespace tests { - -static const uint64_t kVersion = HARDWARE_DEVICE_API_VERSION_2(1, 0, 1); - -class VehicleModule : public testing::Test { -public: - VehicleModule() : - vehicle_module_(NULL) {} - ~VehicleModule() {} -protected: - virtual void SetUp() { - const hw_module_t *hw_module = NULL; - ASSERT_EQ(0, hw_get_module(VEHICLE_HARDWARE_MODULE_ID, &hw_module)) - << "Can't get vehicle module"; - ASSERT_TRUE(NULL != hw_module) - << "hw_get_module didn't return a valid hardware module"; - - vehicle_module_ = reinterpret_cast<const vehicle_module_t*>(hw_module); - } - const vehicle_module_t* vehicle_module() { return vehicle_module_; } -private: - const vehicle_module_t* vehicle_module_; -}; - - -int VehicleEventCallback(const vehicle_prop_value_t* event_data) { - // Print what we got. - std::cout << "got some value from callback: " - << event_data->prop - << " uint32 value: " - << event_data->value.int32_value << "\n"; - return 0; -} - - int VehicleErrorCallback(int32_t /*error_code*/, int32_t /*property*/, int32_t /*operation*/) { - // Do nothing. - return 0; -} - -class VehicleDevice : public VehicleModule { -public: - VehicleDevice() : - vehicle_device_(NULL) {} - ~VehicleDevice() {} -protected: - virtual void SetUp() { - VehicleModule::SetUp(); - hw_device_t *device = NULL; - ASSERT_TRUE(NULL != vehicle_module()->common.methods->open) - << "Vehicle open() is unimplemented"; - ASSERT_EQ(0, vehicle_module()->common.methods->open( - (const hw_module_t*)vehicle_module(), NULL, &device)) - << "Can't open vehicle device"; - ASSERT_TRUE(NULL != device) - << "Vehicle open() returned a NULL device"; - ASSERT_EQ(kVersion, device->version) - << "Unsupported version"; - vehicle_device_ = reinterpret_cast<vehicle_hw_device_t*>(device); - } - vehicle_hw_device_t* vehicle_device() { return vehicle_device_; } - vehicle_event_callback_fn callback_fn() { - return VehicleEventCallback; - } - vehicle_error_callback_fn error_fn() { - return VehicleErrorCallback; - } - - private: - vehicle_hw_device_t* vehicle_device_; -}; - -} // namespace tests - -#endif // __ANDROID_HAL_VEHICLE_TEST_ diff --git a/tests/vehicle/vehicle_tests.cpp b/tests/vehicle/vehicle_tests.cpp deleted file mode 100644 index 4c155709..00000000 --- a/tests/vehicle/vehicle_tests.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2015 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 <gtest/gtest.h> -#include "vehicle_test_fixtures.h" -#include "hardware/vehicle.h" - -namespace tests { - -// Check if list_properties command exists. -TEST_F(VehicleDevice, isThereListProperties) { - ASSERT_TRUE(NULL != vehicle_device()->list_properties) - << "list_properties() function is not implemented"; - std::cout << "Test succeeds.\n"; -} - -// HAL should provide atleast one property. The output of this command should be -// used to verify the vailidity of the function. -TEST_F(VehicleDevice, listPropertiesMoreThanOne) { - vehicle_prop_config_t const* config; - int num_configs = -1; - config = vehicle_device()->list_properties(vehicle_device(), &num_configs); - ASSERT_TRUE(num_configs > -1) << "list_properties() call failed."; - ASSERT_TRUE(num_configs > 0) << "list_properties() returned zero items."; - std::cout << "Number of properties reported: " << num_configs << "\n"; - for (int i = 0; i < num_configs; i++) { - // Print each of the properties. - const vehicle_prop_config_t& config_temp = config[i]; - std::cout << "Property ID: " << config_temp.prop << "\n"; - std::cout << "Property flags: " << config_temp.config_flags << "\n"; - std::cout << "Property change mode: " << config_temp.change_mode << "\n"; - std::cout << "Property min sample rate: " << config_temp.min_sample_rate << "\n"; - std::cout << "Property max sample rate: " << config_temp.max_sample_rate << "\n\n"; - } -} - -// Test get() command. -// The fields are hardcoded in the dummy implementation and here. -TEST_F(VehicleDevice, getDriveState) { - vehicle_prop_value_t data; - data.prop = VEHICLE_PROPERTY_DRIVING_STATUS; - // Set drive_state field to EINVAL so that we can check that its valid when - // it comes back. - data.value_type = -EINVAL; - data.value.driving_status = -EINVAL; - vehicle_device()->get(vehicle_device(), &data); - - // Check that retured values are not invalid. - ASSERT_NE(data.value_type, -EINVAL) << "Drive state value type should be integer."; - ASSERT_NE(data.value.driving_status, -EINVAL) << "Driving status should be positive."; - - std::cout << "Driving status value type: " << data.value_type << "\n" - << "Driving status: " << data.value.driving_status << "\n"; -} - -// Test the workflows for subscribe and init/release. -// Subscribe will return error before init() is called or after release() is -// called. -TEST_F(VehicleDevice, initTest) { - // Test that init on a new device works. When getting an instance, we are - // already calling 'open' on the device. - int ret_code = - vehicle_device()->init(vehicle_device(), callback_fn(), error_fn()); - ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code; - - // Trying to init again should return an error. - ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn()); - ASSERT_EQ(ret_code, -EEXIST) << "ret code: " << ret_code; - - // Uninit should always return 0. - ret_code = vehicle_device()->release(vehicle_device()); - ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code; - - // We should be able to init again. - ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn()); - ASSERT_EQ(ret_code, 0) << "ret code: " << ret_code; - - // Finally release. - ret_code = vehicle_device()->release(vehicle_device()); - ASSERT_EQ(ret_code, 0) << "ret_code: " << ret_code; -} - -// Test that subscribe works. -// We wait for 10 seconds while which the vehicle.c can post messages from -// within it's own thread. -TEST_F(VehicleDevice, subscribeTest) { - // If the device is not init subscribe should fail off the bat. - int ret_code = vehicle_device()->subscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS, - 0, 0); - ASSERT_EQ(ret_code, -EINVAL) << "Return code is: " << ret_code; - - // Let's init the device. - ret_code = vehicle_device()->init(vehicle_device(), callback_fn(), error_fn()); - ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code; - - // Subscribe should now go through. - ret_code = vehicle_device()->subscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS, 0, 0); - ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code; - - // We should start getting some messages thrown from the callback. Let's - // wait for 20 seconds before unsubscribing. - std::cout << "Sleeping for 20 seconds."; - sleep(20); - std::cout << "Waking from sleep."; - - // This property does not exist, so we should get -EINVAL. - ret_code = vehicle_device()->unsubscribe(vehicle_device(), VEHICLE_PROPERTY_INFO_VIN); - ASSERT_EQ(ret_code, -EINVAL) << "Return code is: " << ret_code; - - // This property exists, so we should get a success return code - also this - // will be a blocking call. - ret_code = vehicle_device()->unsubscribe(vehicle_device(), VEHICLE_PROPERTY_DRIVING_STATUS); - ASSERT_EQ(ret_code, 0) << "Return code is: " << ret_code; -} - -} // namespace tests |