diff options
77 files changed, 7613 insertions, 2488 deletions
diff --git a/include/hardware/audio_effect.h b/include/hardware/audio_effect.h index ee48e4ca..41cd2e61 100644 --- a/include/hardware/audio_effect.h +++ b/include/hardware/audio_effect.h @@ -344,9 +344,10 @@ struct effect_interface_s { // Output: // returned value: 0 successful operation // -EINVAL invalid interface handle or - // invalid command/reply size or format according to command code - // The return code should be restricted to indicate problems related to the this - // API specification. Status related to the execution of a particular command should be + // invalid command/reply size or format according to + // command code + // The return code should be restricted to indicate problems related to this API + // specification. Status related to the execution of a particular command should be // indicated as part of the reply field. // // *pReplyData updated with command response @@ -937,11 +938,12 @@ typedef struct audio_effect_library_s { // // Input: // uuid: pointer to the effect uuid. - // sessionId: audio session to which this effect instance will be attached. All effects - // created with the same session ID are connected in series and process the same signal - // stream. Knowing that two effects are part of the same effect chain can help the - // library implement some kind of optimizations. - // ioId: identifies the output or input stream this effect is directed to at audio HAL. + // sessionId: audio session to which this effect instance will be attached. + // All effects created with the same session ID are connected in series and process + // the same signal stream. Knowing that two effects are part of the same effect + // chain can help the library implement some kind of optimizations. + // ioId: identifies the output or input stream this effect is directed to in + // audio HAL. // For future use especially with tunneled HW accelerated effects // // Input/Output: diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h index 6f95684a..3427213d 100644 --- a/include/hardware/bluetooth.h +++ b/include/hardware/bluetooth.h @@ -318,7 +318,7 @@ typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state); /** Bluetooth Legacy PinKey Request callback */ typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr, - bt_bdname_t *bd_name, uint32_t cod); + bt_bdname_t *bd_name, uint32_t cod, bool min_16_digit); /** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/ /** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT & @@ -542,6 +542,12 @@ typedef struct { * Function is synchronous and |fd| is owned by caller. */ void (*dump)(int fd); + + /** + * Clear /data/misc/bt_config.conf and erase all stored connections + */ + int (*config_clear)(void); + } bt_interface_t; /** TODO: Need to add APIs for Service Discovery, Service authorization and diff --git a/include/hardware/bt_sdp.h b/include/hardware/bt_sdp.h index d298ad6a..8f39bc5b 100644 --- a/include/hardware/bt_sdp.h +++ b/include/hardware/bt_sdp.h @@ -26,12 +26,13 @@ __BEGIN_DECLS * These events are handled by the state machine */ typedef enum { - SDP_TYPE_RAW, // Used to carry raw SDP search data for unknown UUID's - SDP_TYPE_MAP_MAS, - SDP_TYPE_MAP_MNS, - SDP_TYPE_PBAP_PSE, - SDP_TYPE_PBAP_PCE, - SDP_TYPE_OPP_SERVER + SDP_TYPE_RAW, // Used to carry raw SDP search data for unknown UUIDs + SDP_TYPE_MAP_MAS, // Message Access Profile - Server + SDP_TYPE_MAP_MNS, // Message Access Profile - Client (Notification Server) + SDP_TYPE_PBAP_PSE, // Phone Book Profile - Server + SDP_TYPE_PBAP_PCE, // Phone Book Profile - Client + SDP_TYPE_OPP_SERVER, // Object Push Profile + SDP_TYPE_SAP_SERVER // SIM Access Profile } bluetooth_sdp_types; typedef struct _bluetooth_sdp_hdr { @@ -92,6 +93,10 @@ typedef struct _bluetooth_sdp_ops_record { uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; } bluetooth_sdp_ops_record; +typedef struct _bluetooth_sdp_sap_record { + bluetooth_sdp_hdr_overlay hdr; +} bluetooth_sdp_sap_record; + typedef union { bluetooth_sdp_hdr_overlay hdr; bluetooth_sdp_mas_record mas; @@ -99,6 +104,7 @@ typedef union { bluetooth_sdp_pse_record pse; bluetooth_sdp_pce_record pce; bluetooth_sdp_ops_record ops; + bluetooth_sdp_sap_record sap; } bluetooth_sdp_record; diff --git a/include/hardware/bt_sock.h b/include/hardware/bt_sock.h index 1c937d87..5d206d7f 100644 --- a/include/hardware/bt_sock.h +++ b/include/hardware/bt_sock.h @@ -21,6 +21,8 @@ __BEGIN_DECLS #define BTSOCK_FLAG_ENCRYPT 1 #define BTSOCK_FLAG_AUTH (1 << 1) #define BTSOCK_FLAG_NO_SDP (1 << 2) +#define BTSOCK_FLAG_AUTH_MITM (1 << 3) +#define BTSOCK_FLAG_AUTH_16_DIGIT (1 << 4) typedef enum { BTSOCK_RFCOMM = 1, @@ -34,11 +36,11 @@ typedef struct { bt_bdaddr_t bd_addr; int channel; int status; - + // The writer must make writes using a buffer of this maximum size // to avoid loosing data. (L2CAP only) unsigned short max_tx_packet_size; - + // The reader must read using a buffer of at least this size to avoid // loosing data. (L2CAP only) unsigned short max_rx_packet_size; diff --git a/include/hardware/camera3.h b/include/hardware/camera3.h index 2bb3ba1f..3ef6d6fa 100644 --- a/include/hardware/camera3.h +++ b/include/hardware/camera3.h @@ -21,7 +21,7 @@ #include "camera_common.h" /** - * Camera device HAL 3.2 [ CAMERA_DEVICE_API_VERSION_3_2 ] + * Camera device HAL 3.3 [ CAMERA_DEVICE_API_VERSION_3_3 ] * * This is the current recommended version of the camera device HAL. * @@ -29,9 +29,14 @@ * android.hardware.camera2 API in LIMITED or FULL modes. * * Camera devices that support this version of the HAL must return - * CAMERA_DEVICE_API_VERSION_3_2 in camera_device_t.common.version and in + * CAMERA_DEVICE_API_VERSION_3_3 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: + * Camera modules that may contain version 3.3 devices must implement at + * least version 2.2 of the camera module interface (as defined by + * camera_module_t.common.module_api_version). + * * CAMERA_DEVICE_API_VERSION_3_2: * Camera modules that may contain version 3.2 devices must implement at * least version 2.2 of the camera module interface (as defined by @@ -54,6 +59,7 @@ * S7. Key Performance Indicator (KPI) glossary * S8. Sample Use Cases * S9. Notes on Controls and Metadata + * S10. Reprocessing flow and controls */ /** @@ -119,6 +125,18 @@ * - change the input buffer return path. The buffer is returned in * process_capture_result instead of process_capture_request. * + * 3.3: Minor revision of expanded-capability HAL: + * + * - OPAQUE and YUV reprocessing API updates. + * + * - Basic support for depth output buffers. + * + * - Addition of data_space field to camera3_stream_t. + * + * - Addition of rotation field to camera3_stream_t. + * + * - Addition of camera3 stream configuration operation mode to camera3_stream_configuration_t + * */ /** @@ -178,8 +196,13 @@ * not-yet-registered streams. * * 9. When the capture of a request begins (sensor starts exposing for the - * capture), the HAL calls camera3_callback_ops_t->notify() with the SHUTTER - * event, including the frame number and the timestamp for start of exposure. + * capture) or processing a reprocess request begins, the HAL + * calls camera3_callback_ops_t->notify() with the SHUTTER event, including + * the frame number and the timestamp for start of exposure. For a reprocess + * request, the timestamp must be the start of exposure of the input image + * which can be looked up with android.sensor.timestamp from + * camera3_capture_request_t.settings when process_capture_request() is + * called. * * <= CAMERA_DEVICE_API_VERSION_3_1: * @@ -191,7 +214,8 @@ * The camera3_callback_ops_t->notify() call with the SHUTTER event should * be made as early as possible since the framework will be unable to * deliver gralloc buffers to the application layer (for that frame) until - * it has a valid timestamp for the start of exposure. + * it has a valid timestamp for the start of exposure (or the input image's + * start of exposure for a reprocess request). * * Both partial metadata results and the gralloc buffers may be sent to the * framework at any time before or after the SHUTTER event. @@ -1109,6 +1133,56 @@ * as input. * - And a HAL_PIXEL_FORMAT_BLOB (JPEG) output stream. * + * S8.2 ZSL (OPAQUE) reprocessing with CAMERA3_STREAM_INPUT stream. + * + * CAMERA_DEVICE_API_VERSION_3_3: + * When OPAQUE_REPROCESSING capability is supported by the camera device, the INPUT stream + * can be used for application/framework implemented use case like Zero Shutter Lag (ZSL). + * This kind of stream will be used by the framework as follows: + * + * 1. Application/framework configures an opaque (RAW or YUV based) format output stream that is + * used to produce the ZSL output buffers. The stream pixel format will be + * HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED. + * + * 2. Application/framework configures an opaque format input stream that is used to + * send the reprocessing ZSL buffers to the HAL. The stream pixel format will + * also be HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED. + * + * 3. Application/framework configures a YUV/JPEG output stream that is used to receive the + * reprocessed data. The stream pixel format will be YCbCr_420/HAL_PIXEL_FORMAT_BLOB. + * + * 4. Application/framework picks a ZSL buffer from the ZSL output stream when a ZSL capture is + * issued by the application, and sends the data back as an input buffer in a + * reprocessing request, then sends to the HAL for reprocessing. + * + * 5. The HAL sends back the output YUV/JPEG result to framework. + * + * The HAL can select the actual opaque buffer format and configure the ISP pipeline + * appropriately based on the HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED format and + * the gralloc usage flag GRALLOC_USAGE_HW_CAMERA_ZSL. + + * S8.3 YUV reprocessing with CAMERA3_STREAM_INPUT stream. + * + * When YUV reprocessing is supported by the HAL, the INPUT stream + * can be used for the YUV reprocessing use cases like lucky-shot and image fusion. + * This kind of stream will be used by the framework as follows: + * + * 1. Application/framework configures an YCbCr_420 format output stream that is + * used to produce the output buffers. + * + * 2. Application/framework configures an YCbCr_420 format input stream that is used to + * send the reprocessing YUV buffers to the HAL. + * + * 3. Application/framework configures a YUV/JPEG output stream that is used to receive the + * reprocessed data. The stream pixel format will be YCbCr_420/HAL_PIXEL_FORMAT_BLOB. + * + * 4. Application/framework processes the output buffers (could be as simple as picking + * an output buffer directly) from the output stream when a capture is issued, and sends + * the data back as an input buffer in a reprocessing request, then sends to the HAL + * for reprocessing. + * + * 5. The HAL sends back the output YUV/JPEG result to framework. + * */ /** @@ -1137,6 +1211,100 @@ * be included in the 'available modes' tag to represent this operating * mode. */ + +/** + * S10. Reprocessing flow and controls + * + * This section describes the OPAQUE and YUV reprocessing flow and controls. OPAQUE reprocessing + * uses an opaque format that is not directly application-visible, and the application can + * only select some of the output buffers and send back to HAL for reprocessing, while YUV + * reprocessing gives the application opportunity to process the buffers before reprocessing. + * + * S8 gives the stream configurations for the typical reprocessing uses cases, + * this section specifies the buffer flow and controls in more details. + * + * S10.1 OPAQUE (typically for ZSL use case) reprocessing flow and controls + * + * For OPAQUE reprocessing (e.g. ZSL) use case, after the application creates the specific + * output and input streams, runtime buffer flow and controls are specified as below: + * + * 1. Application starts output streaming by sending repeating requests for output + * opaque buffers and preview. The buffers are held by an application + * maintained circular buffer. The requests are based on CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG + * capture template, which should have all necessary settings that guarantee output + * frame rate is not slowed down relative to sensor output frame rate. + * + * 2. When a capture is issued, the application selects one output buffer based + * on application buffer selection logic, e.g. good AE and AF statistics etc. + * Application then creates an reprocess request based on the capture result associated + * with this selected buffer. The selected output buffer is now added to this reprocess + * request as an input buffer, the output buffer of this reprocess request should be + * either JPEG output buffer or YUV output buffer, or both, depending on the application + * choice. + * + * 3. Application then alters the reprocess settings to get best image quality. The HAL must + * support and only support below controls if the HAL support OPAQUE_REPROCESSING capability: + * - android.jpeg.* (if JPEG buffer is included as one of the output) + * - android.noiseReduction.mode (change to HIGH_QUALITY if it is supported) + * - android.edge.mode (change to HIGH_QUALITY if it is supported) + * All other controls must be ignored by the HAL. + * 4. HAL processed the input buffer and return the output buffers in the capture results + * as normal. + * + * S10.2 YUV reprocessing flow and controls + * + * The YUV reprocessing buffer flow is similar as OPAQUE reprocessing, with below difference: + * + * 1. Application may want to have finer granularity control of the intermediate YUV images + * (before reprocessing). For example, application may choose + * - android.noiseReduction.mode == MINIMAL + * to make sure the no YUV domain noise reduction has applied to the output YUV buffers, + * then it can do its own advanced noise reduction on them. For OPAQUE reprocessing case, this + * doesn't matter, as long as the final reprocessed image has the best quality. + * 2. Application may modify the YUV output buffer data. For example, for image fusion use + * case, where multiple output images are merged together to improve the signal-to-noise + * ratio (SNR). The input buffer may be generated from multiple buffers by the application. + * To avoid excessive amount of noise reduction and insufficient amount of edge enhancement + * being applied to the input buffer, the application can hint the HAL how much effective + * exposure time improvement has been done by the application, then the HAL can adjust the + * noise reduction and edge enhancement paramters to get best reprocessed image quality. + * Below tag can be used for this purpose: + * - android.reprocess.effectiveExposureFactor + * The value would be exposure time increase factor applied to the original output image, + * for example, if there are N image merged, the exposure time increase factor would be up + * to sqrt(N). See this tag spec for more details. + * + * S10.3 Reprocessing pipeline characteristics + * + * Reprocessing pipeline has below different characteristics comparing with normal output + * pipeline: + * + * 1. The reprocessing result can be returned ahead of the pending normal output results. But + * the FIFO ordering must be maintained for all reprocessing results. For example, there are + * below requests (A stands for output requests, B stands for reprocessing requests) + * being processed by the HAL: + * A1, A2, A3, A4, B1, A5, B2, A6... + * result of B1 can be returned before A1-A4, but result of B2 must be returned after B1. + * 2. Single input rule: For a given reprocessing request, all output buffers must be from the + * input buffer, rather than sensor output. For example, if a reprocess request include both + * JPEG and preview buffers, all output buffers must be produced from the input buffer + * included by the reprocessing request, rather than sensor. The HAL must not output preview + * buffers from sensor, while output JPEG buffer from the input buffer. + * 3. Input buffer will be from camera output directly (ZSL case) or indirectly(image fusion + * case). For the case where buffer is modified, the size will remain same. The HAL can + * notify CAMERA3_MSG_ERROR_REQUEST if buffer from unknown source is sent. + * 4. Result as reprocessing request: The HAL can expect that a reprocessing request is a copy + * of one of the output results with minor allowed setting changes. The HAL can notify + * CAMERA3_MSG_ERROR_REQUEST if a request from unknown source is issued. + * 5. Output buffers may not be used as inputs across the configure stream boundary, This is + * because an opaque stream like the ZSL output stream may have different actual image size + * inside of the ZSL buffer to save power and bandwidth for smaller resolution JPEG capture. + * The HAL may notify CAMERA3_MSG_ERROR_REQUEST if this case occurs. + * 6. HAL Reprocess requests error reporting during flush should follow the same rule specified + * by flush() method. + * + */ + __BEGIN_DECLS struct camera3_device; @@ -1184,6 +1352,9 @@ typedef enum camera3_stream_type { * quality images (that otherwise would cause a frame rate performance * loss), or to do off-line reprocessing. * + * CAMERA_DEVICE_API_VERSION_3_3: + * The typical use cases are OPAQUE (typically ZSL) and YUV reprocessing, + * see S8.2, S8.3 and S10 for more details. */ CAMERA3_STREAM_INPUT = 1, @@ -1209,6 +1380,102 @@ typedef enum camera3_stream_type { } camera3_stream_type_t; /** + * camera3_stream_rotation_t: + * + * The required counterclockwise rotation of camera stream. + */ +typedef enum camera3_stream_rotation { + /* No rotation */ + CAMERA3_STREAM_ROTATION_0 = 0, + + /* Rotate by 90 degree counterclockwise */ + CAMERA3_STREAM_ROTATION_90 = 1, + + /* Rotate by 180 degree counterclockwise */ + CAMERA3_STREAM_ROTATION_180 = 2, + + /* Rotate by 270 degree counterclockwise */ + CAMERA3_STREAM_ROTATION_270 = 3 +} camera3_stream_rotation_t; + +/** + * camera3_stream_configuration_mode_t: + * + * This defines the general operation mode for the HAL (for a given stream configuration), where + * modes besides NORMAL have different semantics, and usually limit the generality of the API in + * exchange for higher performance in some particular area. + */ +typedef enum camera3_stream_configuration_mode { + /** + * Normal stream configuration operation mode. This is the default camera operation mode, + * where all semantics of HAL APIs and metadata controls apply. + */ + CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE = 0, + + /** + * Special constrained high speed operation mode for devices that can not support high + * speed output in NORMAL mode. All streams in this configuration are operating at high speed + * mode and have different characteristics and limitations to achieve high speed output. + * The NORMAL mode can still be used for high speed output if the HAL can support high speed + * output while satisfying all the semantics of HAL APIs and metadata controls. It is + * recommended for the HAL to support high speed output in NORMAL mode (by advertising the high + * speed FPS ranges in android.control.aeAvailableTargetFpsRanges) if possible. + * + * This mode has below limitations/requirements: + * + * 1. The HAL must support up to 2 streams with sizes reported by + * android.control.availableHighSpeedVideoConfigurations. + * 2. In this mode, the HAL is expected to output up to 120fps or higher. This mode must + * support the targeted FPS range and size configurations reported by + * android.control.availableHighSpeedVideoConfigurations. + * 3. The HAL must support HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED output stream format. + * 4. To achieve efficient high speed streaming, the HAL may have to aggregate + * multiple frames together and send to camera device for processing where the request + * controls are same for all the frames in this batch (batch mode). The HAL must support + * max batch size and the max batch size requirements defined by + * android.control.availableHighSpeedVideoConfigurations. + * 5. In this mode, the HAL must override aeMode, awbMode, and afMode to ON, ON, and + * CONTINUOUS_VIDEO, respectively. All post-processing block mode controls must be + * overridden to be FAST. Therefore, no manual control of capture and post-processing + * parameters is possible. All other controls operate the same as when + * android.control.mode == AUTO. This means that all other android.control.* fields + * must continue to work, such as + * + * android.control.aeTargetFpsRange + * android.control.aeExposureCompensation + * android.control.aeLock + * android.control.awbLock + * android.control.effectMode + * android.control.aeRegions + * android.control.afRegions + * android.control.awbRegions + * android.control.afTrigger + * android.control.aePrecaptureTrigger + * + * Outside of android.control.*, the following controls must work: + * + * android.flash.mode (TORCH mode only, automatic flash for still capture will not work + * since aeMode is ON) + * android.lens.opticalStabilizationMode (if it is supported) + * android.scaler.cropRegion + * android.statistics.faceDetectMode (if it is supported) + * + * For more details about high speed stream requirements, see + * android.control.availableHighSpeedVideoConfigurations and CONSTRAINED_HIGH_SPEED_VIDEO + * capability defined in android.request.availableCapabilities. + * + * This mode only needs to be supported by HALs that include CONSTRAINED_HIGH_SPEED_VIDEO in + * the android.request.availableCapabilities static metadata. + */ + CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE = 1, + + /** + * First value for vendor-defined stream configuration modes. + */ + CAMERA3_VENDOR_STREAM_CONFIGURATION_MODE_START = 0x8000 +} camera3_stream_configuration_mode_t; + +/** * camera3_stream_t: * * A handle to a single camera input or output stream. A stream is defined by @@ -1326,6 +1593,61 @@ typedef struct camera3_stream { */ void *priv; + /** + * A field that describes the contents of the buffer. The format and buffer + * dimensions define the memory layout and structure of the stream buffers, + * while dataSpace defines the meaning of the data within the buffer. + * + * For most formats, dataSpace defines the color space of the image data. + * In addition, for some formats, dataSpace indicates whether image- or + * depth-based data is requested. See system/core/include/system/graphics.h + * for details of formats and valid dataSpace values for each format. + * + * Version information: + * + * < CAMERA_DEVICE_API_VERSION_3_3: + * + * Not defined and should not be accessed. dataSpace should be assumed to + * be HAL_DATASPACE_UNKNOWN, and the appropriate color space, etc, should + * be determined from the usage flags and the format. + * + * >= CAMERA_DEVICE_API_VERSION_3_3: + * + * Always set by the camera service. HAL must use this dataSpace to + * configure the stream to the correct colorspace, or to select between + * color and depth outputs if supported. + */ + android_dataspace_t data_space; + + /** + * The required output rotation of the stream, one of + * the camera3_stream_rotation_t values. This must be inspected by HAL along + * with stream width and height. For example, if the rotation is 90 degree + * and the stream width and height is 720 and 1280 respectively, camera service + * will supply buffers of size 720x1280, and HAL should capture a 1280x720 image + * and rotate the image by 90 degree counterclockwise. The rotation field is + * no-op when the stream type is input. Camera HAL must ignore the rotation + * field for an input stream. + * + * <= CAMERA_DEVICE_API_VERSION_3_2: + * + * Not defined and must not be accessed. HAL must not apply any rotation + * on output images. + * + * >= CAMERA_DEVICE_API_VERSION_3_3: + * + * Always set by camera service. HAL must inspect this field during stream + * configuration and returns -EINVAL if HAL cannot perform such rotation. + * HAL must always support CAMERA3_STREAM_ROTATION_0, so a + * configure_streams() call must not fail for unsupported rotation if + * rotation field of all streams is CAMERA3_STREAM_ROTATION_0. + * + */ + int rotation; + + /* reserved for future use */ + void *reserved[7]; + } camera3_stream_t; /** @@ -1355,6 +1677,19 @@ typedef struct camera3_stream_configuration { */ camera3_stream_t **streams; + /** + * >= CAMERA_DEVICE_API_VERSION_3_3: + * + * The operation mode of streams in this configuration, one of the value defined in + * camera3_stream_configuration_mode_t. + * The HAL can use this mode as an indicator to set the stream property (e.g., + * camera3_stream->max_buffers) appropriately. For example, if the configuration is + * CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE, the HAL may want to set aside more + * buffers for batch mode operation (see android.control.availableHighSpeedVideoConfigurations + * for batch mode definition). + * + */ + uint32_t operation_mode; } camera3_stream_configuration_t; /** @@ -1550,7 +1885,7 @@ typedef enum camera3_msg_type { CAMERA3_MSG_ERROR = 1, /** - * The exposure of a given request has + * The exposure of a given request or processing a reprocess request has * begun. camera3_notify_msg.message.shutter contains the information * the capture. */ @@ -1641,12 +1976,13 @@ typedef struct camera3_error_msg { */ typedef struct camera3_shutter_msg { /** - * Frame number of the request that has begun exposure + * Frame number of the request that has begun exposure or reprocessing. */ uint32_t frame_number; /** - * Timestamp for the start of capture. This must match the capture result + * Timestamp for the start of capture. For a reprocess request, this must + * be input image's start of capture. This must match the capture result * metadata's sensor exposure start timestamp. */ uint64_t timestamp; @@ -2120,9 +2456,10 @@ typedef struct camera3_callback_ops { * >= CAMERA_DEVICE_API_VERSION_3_2: * * Buffers delivered to the framework will not be dispatched to the - * application layer until a start of exposure timestamp has been received - * via a SHUTTER notify() call. It is highly recommended to - * dispatch this call as early as possible. + * application layer until a start of exposure timestamp (or input image's + * start of exposure timestamp for a reprocess request) has been received + * via a SHUTTER notify() call. It is highly recommended to dispatch this + * call as early as possible. * * ------------------------------------------------------------------------ * Performance requirements: @@ -2380,6 +2717,14 @@ typedef struct camera3_device_ops { * * - Including too many output streams of a certain format. * + * - Unsupported rotation configuration (only applies to + * devices with version >= CAMERA_DEVICE_API_VERSION_3_3) + * + * - Stream sizes/formats don't satisfy the + * camera3_stream_configuration_t->operation_mode requirements for non-NORMAL mode, + * or the requested operation_mode is not supported by the HAL. + * (only applies to devices with version >= CAMERA_DEVICE_API_VERSION_3_3) + * * Note that the framework submitting an invalid stream * configuration is not normal operation, since stream * configurations are checked before configure. An invalid @@ -2613,6 +2958,14 @@ typedef struct camera3_device_ops { * interruptible hardware blocks should be stopped, and any uninterruptible * blocks should be waited on. * + * flush() may be called concurrently to process_capture_request(), with the expectation that + * process_capture_request will return quickly and the request submitted in that + * process_capture_request call is treated like all other in-flight requests. Due to + * concurrency issues, it is possible that from the HAL's point of view, a + * process_capture_request() call may be started after flush has been invoked but has not + * returned yet. If such a call happens before flush() returns, the HAL should treat the new + * capture request like other in-flight pending requests (see #4 below). + * * More specifically, the HAL must follow below requirements for various cases: * * 1. For captures that are too late for the HAL to cancel/stop, and will be @@ -2657,6 +3010,12 @@ typedef struct camera3_device_ops { * 3.7. For fully-missing metadata, calling CAMERA3_MSG_ERROR_RESULT is sufficient, no * need to call process_capture_result with NULL metadata or equivalent. * + * 4. If a flush() is invoked while a process_capture_request() invocation is active, that + * process call should return as soon as possible. In addition, if a process_capture_request() + * call is made after flush() has been invoked but before flush() has returned, the + * capture request provided by the late process_capture_request call should be treated like + * a pending request in case #2 above. + * * flush() should only return when there are no more outstanding buffers or * requests left in the HAL. The framework may call configure_streams (as * the HAL state is now quiesced) or may issue new requests. diff --git a/include/hardware/camera_common.h b/include/hardware/camera_common.h index dadbc8f1..7658dd40 100644 --- a/include/hardware/camera_common.h +++ b/include/hardware/camera_common.h @@ -20,6 +20,7 @@ #define ANDROID_INCLUDE_CAMERA_COMMON_H #include <stdint.h> +#include <stdbool.h> #include <sys/cdefs.h> #include <sys/types.h> #include <cutils/native_handle.h> @@ -85,6 +86,37 @@ __BEGIN_DECLS * The standard hardware module open call (common.methods->open) continues * to open the camera device with the latest supported version, which is * also the version listed in camera_info_t.device_version. + * + ******************************************************************************* + * Version: 2.4 [CAMERA_MODULE_API_VERSION_2_4] + * + * This camera module version adds below API changes: + * + * 1. Torch mode support. The framework can use it to turn on torch mode for + * any camera device that has a flash unit, without opening a camera device. The + * camera device has a higher priority accessing the flash unit than the camera + * module; opening a camera device will turn off the torch if it had been enabled + * through the module interface. When there are any resource conflicts, such as + * open() is called to open a camera device, the camera HAL module must notify the + * framework through the torch mode status callback that the torch mode has been + * turned off. + * + * 2. External camera (e.g. USB hot-plug camera) support. The API updates specify that + * the camera static info is only available when camera is connected and ready to + * use for external hot-plug cameras. Calls to get static info will be invalid + * calls when camera status is not CAMERA_DEVICE_STATUS_PRESENT. The frameworks + * will only count on device status change callbacks to manage the available external + * camera list. + * + * 3. Camera arbitration hints. This module version adds support for explicitly + * indicating the number of camera devices that can be simultaneously opened and used. + * To specify valid combinations of devices, the resource_cost and conflicting_devices + * fields should always be set in the camera_info structure returned by the + * get_camera_info call. + * + * 4. Module initialization method. This will be called by the camera service + * right after the HAL module is loaded, to allow for one-time initialization + * of the HAL. It is called before any other module methods are invoked. */ /** @@ -100,8 +132,9 @@ __BEGIN_DECLS #define CAMERA_MODULE_API_VERSION_2_1 HARDWARE_MODULE_API_VERSION(2, 1) #define CAMERA_MODULE_API_VERSION_2_2 HARDWARE_MODULE_API_VERSION(2, 2) #define CAMERA_MODULE_API_VERSION_2_3 HARDWARE_MODULE_API_VERSION(2, 3) +#define CAMERA_MODULE_API_VERSION_2_4 HARDWARE_MODULE_API_VERSION(2, 4) -#define CAMERA_MODULE_API_VERSION_CURRENT CAMERA_MODULE_API_VERSION_2_3 +#define CAMERA_MODULE_API_VERSION_CURRENT CAMERA_MODULE_API_VERSION_2_4 /** * All device versions <= HARDWARE_DEVICE_API_VERSION(1, 0xFF) must be treated @@ -113,10 +146,11 @@ __BEGIN_DECLS #define CAMERA_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0) #define CAMERA_DEVICE_API_VERSION_3_1 HARDWARE_DEVICE_API_VERSION(3, 1) #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) -// Device version 3.2 is current, older HAL camera device versions are not +// Device version 3.3 is current, older HAL camera device versions are not // recommended for new devices. -#define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_3_2 +#define CAMERA_DEVICE_API_VERSION_CURRENT CAMERA_DEVICE_API_VERSION_3_3 /** * Defined in /system/media/camera/include/system/camera_metadata.h @@ -125,11 +159,19 @@ typedef struct camera_metadata camera_metadata_t; typedef struct camera_info { /** - * The direction that the camera faces to. It should be CAMERA_FACING_BACK - * or CAMERA_FACING_FRONT. + * The direction that the camera faces to. See system/core/include/system/camera.h + * for camera facing definitions. + * + * Version information (based on camera_module_t.common.module_api_version): * - * Version information: - * Valid in all camera_module versions + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * It should be CAMERA_FACING_BACK or CAMERA_FACING_FRONT. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * It should be CAMERA_FACING_BACK, CAMERA_FACING_FRONT or + * CAMERA_FACING_EXTERNAL. */ int facing; @@ -145,8 +187,16 @@ typedef struct camera_info { * top side of a front-facing camera sensor is aligned with the right of the * screen, the value should be 270. * - * Version information: - * Valid in all camera_module versions + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Valid in all camera_module versions. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * Valid if camera facing is CAMERA_FACING_BACK or CAMERA_FACING_FRONT, + * not valid if camera facing is CAMERA_FACING_EXTERNAL. */ int orientation; @@ -168,9 +218,9 @@ typedef struct camera_info { uint32_t device_version; /** - * The camera's fixed characteristics, which include all camera metadata in - * the android.*.info.* sections. This should be a sorted metadata buffer, - * and may not be modified or freed by the caller. The pointer should remain + * The camera's fixed characteristics, which include all static camera metadata + * specified in system/media/camera/docs/docs.html. This should be a sorted metadata + * buffer, and may not be modified or freed by the caller. The pointer should remain * valid for the lifetime of the camera module, and values in it may not * change after it is returned by get_camera_info(). * @@ -188,6 +238,185 @@ typedef struct camera_info { * */ const camera_metadata_t *static_camera_characteristics; + + /** + * The total resource "cost" of using this camera, represented as an integer + * value in the range [0, 100] where 100 represents total usage of the shared + * resource that is the limiting bottleneck of the camera subsystem. This may + * be a very rough estimate, and is used as a hint to the camera service to + * determine when to disallow multiple applications from simultaneously + * opening different cameras advertised by the camera service. + * + * The camera service must be able to simultaneously open and use any + * combination of camera devices exposed by the HAL where the sum of + * the resource costs of these cameras is <= 100. For determining cost, + * each camera device must be assumed to be configured and operating at + * the maximally resource-consuming framerate and stream size settings + * available in the configuration settings exposed for that device through + * the camera metadata. + * + * The camera service may still attempt to simultaneously open combinations + * of camera devices with a total resource cost > 100. This may succeed or + * fail. If this succeeds, combinations of configurations that are not + * supported due to resource constraints from having multiple open devices + * should fail during the configure calls. If the total resource cost is + * <= 100, open and configure should never fail for any stream configuration + * settings or other device capabilities that would normally succeed for a + * device when it is the only open camera device. + * + * This field will be used to determine whether background applications are + * allowed to use this camera device while other applications are using other + * camera devices. Note: multiple applications will never be allowed by the + * camera service to simultaneously open the same camera device. + * + * Example use cases: + * + * Ex. 1: Camera Device 0 = Back Camera + * Camera Device 1 = Front Camera + * - Using both camera devices causes a large framerate slowdown due to + * limited ISP bandwidth. + * + * Configuration: + * + * Camera Device 0 - resource_cost = 51 + * conflicting_devices = null + * Camera Device 1 - resource_cost = 51 + * conflicting_devices = null + * + * Result: + * + * Since the sum of the resource costs is > 100, if a higher-priority + * application has either device open, no lower-priority applications will be + * allowed by the camera service to open either device. If a lower-priority + * application is using a device that a higher-priority subsequently attempts + * to open, the lower-priority application will be forced to disconnect the + * the device. + * + * If the highest-priority application chooses, it may still attempt to open + * both devices (since these devices are not listed as conflicting in the + * conflicting_devices fields), but usage of these devices may fail in the + * open or configure calls. + * + * Ex. 2: Camera Device 0 = Left Back Camera + * Camera Device 1 = Right Back Camera + * Camera Device 2 = Combined stereo camera using both right and left + * back camera sensors used by devices 0, and 1 + * Camera Device 3 = Front Camera + * - Due to do hardware constraints, up to two cameras may be open at once. The + * combined stereo camera may never be used at the same time as either of the + * two back camera devices (device 0, 1), and typically requires too much + * bandwidth to use at the same time as the front camera (device 3). + * + * Configuration: + * + * Camera Device 0 - resource_cost = 50 + * conflicting_devices = { 2 } + * Camera Device 1 - resource_cost = 50 + * conflicting_devices = { 2 } + * Camera Device 2 - resource_cost = 100 + * conflicting_devices = { 0, 1 } + * Camera Device 3 - resource_cost = 50 + * conflicting_devices = null + * + * Result: + * + * Based on the conflicting_devices fields, the camera service guarantees that + * the following sets of open devices will never be allowed: { 1, 2 }, { 0, 2 }. + * + * Based on the resource_cost fields, if a high-priority foreground application + * is using camera device 0, a background application would be allowed to open + * camera device 1 or 3 (but would be forced to disconnect it again if the + * foreground application opened another device). + * + * The highest priority application may still attempt to simultaneously open + * devices 0, 2, and 3, but the HAL may fail in open or configure calls for + * this combination. + * + * Ex. 3: Camera Device 0 = Back Camera + * Camera Device 1 = Front Camera + * Camera Device 2 = Low-power Front Camera that uses the same + * sensor as device 1, but only exposes image stream + * resolutions that can be used in low-power mode + * - Using both front cameras (device 1, 2) at the same time is impossible due + * a shared physical sensor. Using the back and "high-power" front camera + * (device 1) may be impossible for some stream configurations due to hardware + * limitations, but the "low-power" front camera option may always be used as + * it has special dedicated hardware. + * + * Configuration: + * + * Camera Device 0 - resource_cost = 100 + * conflicting_devices = null + * Camera Device 1 - resource_cost = 100 + * conflicting_devices = { 2 } + * Camera Device 2 - resource_cost = 0 + * conflicting_devices = { 1 } + * Result: + * + * Based on the conflicting_devices fields, the camera service guarantees that + * the following sets of open devices will never be allowed: { 1, 2 }. + * + * Based on the resource_cost fields, only the highest priority application + * may attempt to open both device 0 and 1 at the same time. If a higher-priority + * application is not using device 1 or 2, a low-priority background application + * may open device 2 (but will be forced to disconnect it if a higher-priority + * application subsequently opens device 1 or 2). + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Not valid. Can be assumed to be 100. Do not read this field. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * Always valid. + */ + int resource_cost; + + /** + * An array of camera device IDs represented as NULL-terminated strings + * indicating other devices that cannot be simultaneously opened while this + * camera device is in use. + * + * This field is intended to be used to indicate that this camera device + * is a composite of several other camera devices, or otherwise has + * hardware dependencies that prohibit simultaneous usage. If there are no + * dependencies, a NULL may be returned in this field to indicate this. + * + * The camera service will never simultaneously open any of the devices + * in this list while this camera device is open. + * + * The strings pointed to in this field will not be cleaned up by the camera + * service, and must remain while this device is plugged in. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Not valid. Can be assumed to be NULL. Do not read this field. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * Always valid. + */ + char** conflicting_devices; + + /** + * The length of the array given in the conflicting_devices field. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Not valid. Can be assumed to be 0. Do not read this field. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * Always valid. + */ + size_t conflicting_devices_length; + } camera_info_t; /** @@ -211,33 +440,155 @@ typedef struct camera_info { typedef enum camera_device_status { /** * The camera device is not currently connected, and opening it will return - * failure. Calls to get_camera_info must still succeed, and provide the - * same information it would if the camera were connected + * failure. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Calls to get_camera_info must still succeed, and provide the same information + * it would if the camera were connected. + * + * CAMERA_MODULE_API_VERSION_2_4: + * + * The camera device at this status must return -EINVAL for get_camera_info call, + * as the device is not connected. */ CAMERA_DEVICE_STATUS_NOT_PRESENT = 0, /** - * The camera device is connected, and opening it will succeed. The - * information returned by get_camera_info cannot change due to this status - * change. By default, the framework will assume all devices are in this - * state. + * The camera device is connected, and opening it will succeed. + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * The information returned by get_camera_info cannot change due to this status + * change. By default, the framework will assume all devices are in this state. + * + * CAMERA_MODULE_API_VERSION_2_4: + * + * The information returned by get_camera_info will become valid after a device's + * status changes to this. By default, the framework will assume all devices are in + * this state. */ CAMERA_DEVICE_STATUS_PRESENT = 1, /** * The camera device is connected, but it is undergoing an enumeration and - * so opening the device will return -EBUSY. Calls to get_camera_info - * must still succeed, as if the camera was in the PRESENT status. + * so opening the device will return -EBUSY. + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * Calls to get_camera_info must still succeed, as if the camera was in the + * PRESENT status. + * + * CAMERA_MODULE_API_VERSION_2_4: + * + * The camera device at this status must return -EINVAL for get_camera_info for call, + * as the device is not ready. */ CAMERA_DEVICE_STATUS_ENUMERATING = 2, } camera_device_status_t; /** + * torch_mode_status_t: + * + * The current status of the torch mode, as provided by the HAL through the + * camera_module_callbacks.torch_mode_status_change() call. + * + * The torch mode status of a camera device is applicable only when the camera + * device is present. The framework will not call set_torch_mode() to turn on + * torch mode of a camera device if the camera device is not present. At module + * load time, the framework will assume torch modes are in the + * TORCH_MODE_STATUS_AVAILABLE_OFF state if the camera device is present and + * android.flash.info.available is reported as true via get_camera_info() call. + * + * The behaviors of the camera HAL module that the framework expects in the + * following situations when a camera device's status changes: + * 1. A previously-disconnected camera device becomes connected. + * After camera_module_callbacks::camera_device_status_change() is invoked + * to inform the framework that the camera device is present, the framework + * will assume the camera device's torch mode is in + * TORCH_MODE_STATUS_AVAILABLE_OFF state. The camera HAL module does not need + * to invoke camera_module_callbacks::torch_mode_status_change() unless the + * flash unit is unavailable to use by set_torch_mode(). + * + * 2. A previously-connected camera becomes disconnected. + * After camera_module_callbacks::camera_device_status_change() is invoked + * to inform the framework that the camera device is not present, the + * framework will not call set_torch_mode() for the disconnected camera + * device until its flash unit becomes available again. The camera HAL + * module does not need to invoke + * camera_module_callbacks::torch_mode_status_change() separately to inform + * that the flash unit has become unavailable. + * + * 3. open() is called to open a camera device. + * The camera HAL module must invoke + * camera_module_callbacks::torch_mode_status_change() for all flash units + * that have entered TORCH_MODE_STATUS_NOT_AVAILABLE state and can not be + * turned on by calling set_torch_mode() anymore due to this open() call. + * open() must not trigger TORCH_MODE_STATUS_AVAILABLE_OFF before + * TORCH_MODE_STATUS_NOT_AVAILABLE for all flash units that have become + * unavailable. + * + * 4. close() is called to close a camera device. + * The camera HAL module must invoke + * camera_module_callbacks::torch_mode_status_change() for all flash units + * that have entered TORCH_MODE_STATUS_AVAILABLE_OFF state and can be turned + * on by calling set_torch_mode() again because of enough resources freed + * up by this close() call. + * + * Note that the framework calling set_torch_mode() successfully must trigger + * TORCH_MODE_STATUS_AVAILABLE_OFF or TORCH_MODE_STATUS_AVAILABLE_ON callback + * for the given camera device. Additionally it must trigger + * TORCH_MODE_STATUS_AVAILABLE_OFF callbacks for other previously-on torch + * modes if HAL cannot keep multiple torch modes on simultaneously. + */ +typedef enum torch_mode_status { + + /** + * The flash unit is no longer available and the torch mode can not be + * turned on by calling set_torch_mode(). If the torch mode is on, it + * will be turned off by HAL before HAL calls torch_mode_status_change(). + */ + TORCH_MODE_STATUS_NOT_AVAILABLE = 0, + + /** + * A torch mode has become off and available to be turned on via + * set_torch_mode(). This may happen in the following + * cases: + * 1. After the resources to turn on the torch mode have become available. + * 2. After set_torch_mode() is called to turn off the torch mode. + * 3. After the framework turned on the torch mode of some other camera + * device and HAL had to turn off the torch modes of any camera devices + * that were previously on. + */ + TORCH_MODE_STATUS_AVAILABLE_OFF = 1, + + /** + * A torch mode has become on and available to be turned off via + * set_torch_mode(). This can happen only after set_torch_mode() is called + * to turn on the torch mode. + */ + TORCH_MODE_STATUS_AVAILABLE_ON = 2, + +} torch_mode_status_t; + +/** * Callback functions for the camera HAL module to use to inform the framework - * of changes to the camera subsystem. These are called only by HAL modules - * implementing version CAMERA_MODULE_API_VERSION_2_1 or higher of the HAL - * module API interface. + * of changes to the camera subsystem. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * Each callback is called only by HAL modules implementing the indicated + * version or higher of the HAL module API interface. + * + * CAMERA_MODULE_API_VERSION_2_1: + * camera_device_status_change() + * + * CAMERA_MODULE_API_VERSION_2_4: + * torch_mode_status_change() + */ typedef struct camera_module_callbacks { @@ -250,6 +601,8 @@ typedef struct camera_module_callbacks { * must call this method to inform the framework of any initially * NOT_PRESENT devices. * + * This callback is added for CAMERA_MODULE_API_VERSION_2_1. + * * camera_module_callbacks: The instance of camera_module_callbacks_t passed * to the module with set_callbacks. * @@ -263,6 +616,30 @@ typedef struct camera_module_callbacks { int camera_id, int new_status); + /** + * torch_mode_status_change: + * + * Callback to the framework to indicate that the state of the torch mode + * of the flash unit associated with a specific camera device has changed. + * At module load time, the framework will assume the torch modes are in + * the TORCH_MODE_STATUS_AVAILABLE_OFF state if android.flash.info.available + * is reported as true via get_camera_info() call. + * + * This callback is added for CAMERA_MODULE_API_VERSION_2_4. + * + * camera_module_callbacks: The instance of camera_module_callbacks_t + * passed to the module with set_callbacks. + * + * camera_id: The ID of camera device whose flash unit has a new torch mode + * status. + * + * new_status: The new status code, one of the torch_mode_status_t enums. + */ + void (*torch_mode_status_change)(const struct camera_module_callbacks*, + const char* camera_id, + int new_status); + + } camera_module_callbacks_t; typedef struct camera_module { @@ -304,8 +681,21 @@ typedef struct camera_module { * simply the number converted to a string. That is, "0" for camera ID 0, * "1" for camera ID 1. * - * The value here must be static, and cannot change after the first call to - * this method + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_3 or lower: + * + * The value here must be static, and cannot change after the first call + * to this method. + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * The value here must be static, and must count only built-in cameras, + * which have CAMERA_FACING_BACK or CAMERA_FACING_FRONT camera facing values + * (camera_info.facing). The HAL must not include the external cameras + * (camera_info.facing == CAMERA_FACING_EXTERNAL) into the return value + * of this call. Frameworks will use camera_device_status_change callback + * to manage number of external cameras. */ int (*get_number_of_cameras)(void); @@ -324,6 +714,14 @@ typedef struct camera_module { * * -EINVAL: The input arguments are invalid, i.e. the id is invalid, * and/or the module is invalid. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_2_4 or higher: + * + * When a camera is disconnected, its camera id becomes invalid. Calling this + * this method with this invalid camera id will get -EINVAL and NULL camera + * static metadata (camera_info.static_camera_characteristics). */ int (*get_camera_info)(int camera_id, struct camera_info *info); @@ -425,8 +823,92 @@ typedef struct camera_module { int (*open_legacy)(const struct hw_module_t* module, const char* id, uint32_t halVersion, struct hw_device_t** device); + /** + * set_torch_mode: + * + * Turn on or off the torch mode of the flash unit associated with a given + * camera ID. If the operation is successful, HAL must notify the framework + * torch state by invoking + * camera_module_callbacks.torch_mode_status_change() with the new state. + * + * The camera device has a higher priority accessing the flash unit. When + * there are any resource conflicts, such as open() is called to open a + * camera device, HAL module must notify the framework through + * camera_module_callbacks.torch_mode_status_change() that the + * torch mode has been turned off and the torch mode state has become + * TORCH_MODE_STATUS_NOT_AVAILABLE. When resources to turn on torch mode + * become available again, HAL module must notify the framework through + * camera_module_callbacks.torch_mode_status_change() that the torch mode + * state has become TORCH_MODE_STATUS_AVAILABLE_OFF for set_torch_mode() to + * be called. + * + * When the framework calls set_torch_mode() to turn on the torch mode of a + * flash unit, if HAL cannot keep multiple torch modes on simultaneously, + * HAL should turn off the torch mode that was turned on by + * a previous set_torch_mode() call and notify the framework that the torch + * mode state of that flash unit has become TORCH_MODE_STATUS_AVAILABLE_OFF. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_1_x/2_0/2_1/2_2/2_3: + * Not provided by HAL module. Framework will not call this function. + * + * CAMERA_MODULE_API_VERSION_2_4: + * Valid to be called by the framework. + * + * Return values: + * + * 0: On a successful operation. + * + * -ENOSYS: The camera device does not support this operation. It is + * returned if and only if android.flash.info.available is + * false. + * + * -EBUSY: The camera device is already in use. + * + * -EUSERS: The resources needed to turn on the torch mode are not + * available, typically because other camera devices are + * holding the resources to make using the flash unit not + * possible. + * + * -EINVAL: camera_id is invalid. + * + */ + int (*set_torch_mode)(const char* camera_id, bool enabled); + + /** + * init: + * + * This method is called by the camera service before any other methods + * are invoked, right after the camera HAL library has been successfully + * loaded. It may be left as NULL by the HAL module, if no initialization + * in needed. + * + * It can be used by HAL implementations to perform initialization and + * other one-time operations. + * + * Version information (based on camera_module_t.common.module_api_version): + * + * CAMERA_MODULE_API_VERSION_1_x/2_0/2_1/2_2/2_3: + * Not provided by HAL module. Framework will not call this function. + * + * CAMERA_MODULE_API_VERSION_2_4: + * If not NULL, will always be called by the framework once after the HAL + * module is loaded, before any other HAL module method is called. + * + * Return values: + * + * 0: On a successful operation. + * + * -ENODEV: Initialization cannot be completed due to an internal + * error. The HAL must be assumed to be in a nonfunctional + * state. + * + */ + int (*init)(); + /* reserved for future use */ - void* reserved[7]; + void* reserved[5]; } camera_module_t; __END_DECLS diff --git a/include/hardware/fingerprint.h b/include/hardware/fingerprint.h index 458ca2de..ac88c103 100644 --- a/include/hardware/fingerprint.h +++ b/include/hardware/fingerprint.h @@ -17,82 +17,97 @@ #ifndef ANDROID_INCLUDE_HARDWARE_FINGERPRINT_H #define ANDROID_INCLUDE_HARDWARE_FINGERPRINT_H +#include <hardware/hw_auth_token.h> + #define FINGERPRINT_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) +#define FINGERPRINT_MODULE_API_VERSION_2_0 HARDWARE_MODULE_API_VERSION(2, 0) #define FINGERPRINT_HARDWARE_MODULE_ID "fingerprint" typedef enum fingerprint_msg_type { FINGERPRINT_ERROR = -1, FINGERPRINT_ACQUIRED = 1, - FINGERPRINT_PROCESSED = 2, FINGERPRINT_TEMPLATE_ENROLLING = 3, - FINGERPRINT_TEMPLATE_REMOVED = 4 + FINGERPRINT_TEMPLATE_REMOVED = 4, + FINGERPRINT_AUTHENTICATED = 5 } fingerprint_msg_type_t; +/* + * Fingerprint errors are meant to tell the framework to terminate the current operation and ask + * for the user to correct the situation. These will almost always result in messaging and user + * interaction to correct the problem. + * + * For example, FINGERPRINT_ERROR_CANCELED should follow any acquisition message that results in + * a situation where the current operation can't continue without user interaction. For example, + * if the sensor is dirty during enrollment and no further enrollment progress can be made, + * send FINGERPRINT_ACQUIRED_IMAGER_DIRTY followed by FINGERPRINT_ERROR_CANCELED. + */ typedef enum fingerprint_error { - FINGERPRINT_ERROR_HW_UNAVAILABLE = 1, - FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2, - FINGERPRINT_ERROR_TIMEOUT = 3, - FINGERPRINT_ERROR_NO_SPACE = 4 /* No space available to store a template */ + FINGERPRINT_ERROR_HW_UNAVAILABLE = 1, /* The hardware has an error that can't be resolved. */ + FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2, /* Bad data; operation can't continue */ + FINGERPRINT_ERROR_TIMEOUT = 3, /* The operation has timed out waiting for user input. */ + FINGERPRINT_ERROR_NO_SPACE = 4, /* No space available to store a template */ + FINGERPRINT_ERROR_CANCELED = 5, /* The current operation can't proceed. See above. */ + FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6, /* fingerprint with given id can't be removed */ + FINGERPRINT_ERROR_VENDOR_BASE = 1000 /* vendor-specific error messages start here */ } fingerprint_error_t; +/* + * Fingerprint acquisition info is meant as feedback for the current operation. Anything but + * FINGERPRINT_ACQUIRED_GOOD will be shown to the user as feedback on how to take action on the + * current operation. For example, FINGERPRINT_ACQUIRED_IMAGER_DIRTY can be used to tell the user + * to clean the sensor. If this will cause the current operation to fail, an additional + * FINGERPRINT_ERROR_CANCELED can be sent to stop the operation in progress (e.g. enrollment). + * In general, these messages will result in a "Try again" message. + */ typedef enum fingerprint_acquired_info { FINGERPRINT_ACQUIRED_GOOD = 0, - FINGERPRINT_ACQUIRED_PARTIAL = 1, - FINGERPRINT_ACQUIRED_INSUFFICIENT = 2, - FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4, - FINGERPRINT_ACQUIRED_TOO_SLOW = 8, - FINGERPRINT_ACQUIRED_TOO_FAST = 16 + FINGERPRINT_ACQUIRED_PARTIAL = 1, /* sensor needs more data, i.e. longer swipe. */ + FINGERPRINT_ACQUIRED_INSUFFICIENT = 2, /* image doesn't contain enough detail for recognition*/ + FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3, /* sensor needs to be cleaned */ + FINGERPRINT_ACQUIRED_TOO_SLOW = 4, /* mostly swipe-type sensors; not enough data collected */ + FINGERPRINT_ACQUIRED_TOO_FAST = 5, /* for swipe and area sensors; tell user to slow down*/ + FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000 /* vendor-specific acquisition messages start here */ } fingerprint_acquired_info_t; +typedef struct fingerprint_finger_id { + uint32_t gid; + uint32_t fid; +} fingerprint_finger_id_t; + typedef struct fingerprint_enroll { - uint32_t id; + fingerprint_finger_id_t finger; /* samples_remaining goes from N (no data collected, but N scans needed) - * to 0 (no more data is needed to build a template). - * The progress indication may be augmented by a bitmap encoded indication - * of finger area that needs to be presented by the user. - * Bit numbers mapped to physical location: - * - * distal - * +-+-+-+ - * |2|1|0| - * |5|4|3| - * medial |8|7|6| lateral - * |b|a|9| - * |e|d|c| - * +-+-+-+ - * proximal - * - */ - uint16_t data_collected_bmp; - uint16_t samples_remaining; + * to 0 (no more data is needed to build a template). */ + uint32_t samples_remaining; + uint64_t msg; /* Vendor specific message. Used for user guidance */ } fingerprint_enroll_t; typedef struct fingerprint_removed { - uint32_t id; + fingerprint_finger_id_t finger; } fingerprint_removed_t; typedef struct fingerprint_acquired { fingerprint_acquired_info_t acquired_info; /* information about the image */ } fingerprint_acquired_t; -typedef struct fingerprint_processed { - uint32_t id; /* 0 is a special id and means no match */ -} fingerprint_processed_t; +typedef struct fingerprint_authenticated { + fingerprint_finger_id_t finger; + hw_auth_token_t hat; +} fingerprint_authenticated_t; typedef struct fingerprint_msg { fingerprint_msg_type_t type; union { - uint64_t raw; fingerprint_error_t error; fingerprint_enroll_t enroll; fingerprint_removed_t removed; fingerprint_acquired_t acquired; - fingerprint_processed_t processed; + fingerprint_authenticated_t authenticated; } data; } fingerprint_msg_t; /* Callback function type */ -typedef void (*fingerprint_notify_t)(fingerprint_msg_t msg); +typedef void (*fingerprint_notify_t)(const fingerprint_msg_t *msg); /* Synchronous operation */ typedef struct fingerprint_device { @@ -105,64 +120,137 @@ typedef struct fingerprint_device { struct hw_device_t common; /* + * Client provided callback function to receive notifications. + * Do not set by hand, use the function above instead. + */ + fingerprint_notify_t notify; + + /* + * Set notification callback: + * Registers a user function that would receive notifications from the HAL + * The call will block if the HAL state machine is in busy state until HAL + * leaves the busy state. + * + * Function return: 0 if callback function is successfuly registered + * or a negative number in case of error, generally from the errno.h set. + */ + int (*set_notify)(struct fingerprint_device *dev, fingerprint_notify_t notify); + + /* + * Fingerprint pre-enroll enroll request: + * Generates a unique token to upper layers to indicate the start of an enrollment transaction. + * This token will be wrapped by security for verification and passed to enroll() for + * verification before enrollment will be allowed. This is to ensure adding a new fingerprint + * template was preceded by some kind of credential confirmation (e.g. device password). + * + * Function return: 0 if function failed + * otherwise, a uint64_t of token + */ + uint64_t (*pre_enroll)(struct fingerprint_device *dev); + + /* * Fingerprint enroll request: * Switches the HAL state machine to collect and store a new fingerprint * template. Switches back as soon as enroll is complete * (fingerprint_msg.type == FINGERPRINT_TEMPLATE_ENROLLING && * fingerprint_msg.data.enroll.samples_remaining == 0) * or after timeout_sec seconds. + * The fingerprint template will be assigned to the group gid. User has a choice + * to supply the gid or set it to 0 in which case a unique group id will be generated. * * Function return: 0 if enrollment process can be successfully started - * -1 otherwise. A notify() function may be called - * indicating the error condition. + * or a negative number in case of error, generally from the errno.h set. + * A notify() function may be called indicating the error condition. */ - int (*enroll)(struct fingerprint_device *dev, uint32_t timeout_sec); + int (*enroll)(struct fingerprint_device *dev, const hw_auth_token_t *hat, + uint32_t gid, uint32_t timeout_sec); /* - * Cancel fingerprint enroll request: - * Switches the HAL state machine back to accept a fingerprint scan mode. - * (fingerprint_msg.type == FINGERPRINT_TEMPLATE_ENROLLING && - * fingerprint_msg.data.enroll.samples_remaining == 0) - * will indicate switch back to the scan mode. + * Finishes the enroll operation and invalidates the pre_enroll() generated challenge. + * This will be called at the end of a multi-finger enrollment session to indicate + * that no more fingers will be added. + * + * Function return: 0 if the request is accepted + * or a negative number in case of error, generally from the errno.h set. + */ + int (*post_enroll)(struct fingerprint_device *dev); + + /* + * get_authenticator_id: + * Returns a token associated with the current fingerprint set. This value will + * change whenever a new fingerprint is enrolled, thus creating a new fingerprint + * set. + * + * Function return: current authenticator id or 0 if function failed. + */ + uint64_t (*get_authenticator_id)(struct fingerprint_device *dev); + + /* + * Cancel pending enroll or authenticate, sending FINGERPRINT_ERROR_CANCELED + * to all running clients. Switches the HAL state machine back to the idle state. + * Unlike enroll_done() doesn't invalidate the pre_enroll() challenge. * * Function return: 0 if cancel request is accepted - * -1 otherwise. + * or a negative number in case of error, generally from the errno.h set. */ - int (*enroll_cancel)(struct fingerprint_device *dev); + int (*cancel)(struct fingerprint_device *dev); + + /* + * Enumerate all the fingerprint templates found in the directory set by + * set_active_group() + * This is a synchronous call. The function takes: + * - A pointer to an array of fingerprint_finger_id_t. + * - The size of the array provided, in fingerprint_finger_id_t elements. + * Max_size is a bi-directional parameter and returns the actual number + * of elements copied to the caller supplied array. + * In the absence of errors the function returns the total number of templates + * in the user directory. + * If the caller has no good guess on the size of the array he should call this + * function witn *max_size == 0 and use the return value for the array allocation. + * The caller of this function has a complete list of the templates when *max_size + * is the same as the function return. + * + * Function return: Total number of fingerprint templates in the current storage directory. + * or a negative number in case of error, generally from the errno.h set. + */ + int (*enumerate)(struct fingerprint_device *dev, fingerprint_finger_id_t *results, + uint32_t *max_size); /* * Fingerprint remove request: - * deletes a fingerprint template. - * If the fingerprint id is 0 the entire template database will be removed. - * notify() will be called for each template deleted with + * Deletes a fingerprint template. + * Works only within a path set by set_active_group(). + * notify() will be called with details on the template deleted. * fingerprint_msg.type == FINGERPRINT_TEMPLATE_REMOVED and - * fingerprint_msg.data.removed.id indicating each template id removed. + * fingerprint_msg.data.removed.id indicating the template id removed. * * Function return: 0 if fingerprint template(s) can be successfully deleted - * -1 otherwise. + * or a negative number in case of error, generally from the errno.h set. */ - int (*remove)(struct fingerprint_device *dev, uint32_t fingerprint_id); + int (*remove)(struct fingerprint_device *dev, uint32_t gid, uint32_t fid); /* - * Set notification callback: - * Registers a user function that would receive notifications from the HAL - * The call will block if the HAL state machine is in busy state until HAL - * leaves the busy state. + * Restricts the HAL operation to a set of fingerprints belonging to a + * group provided. + * The caller must provide a path to a storage location within the user's + * data directory. * - * Function return: 0 if callback function is successfuly registered - * -1 otherwise. + * Function return: 0 on success + * or a negative number in case of error, generally from the errno.h set. */ - int (*set_notify)(struct fingerprint_device *dev, - fingerprint_notify_t notify); + int (*set_active_group)(struct fingerprint_device *dev, uint32_t gid, + const char *store_path); /* - * Client provided callback function to receive notifications. - * Do not set by hand, use the function above instead. + * Authenticates an operation identifed by operation_id + * + * Function return: 0 on success + * or a negative number in case of error, generally from the errno.h set. */ - fingerprint_notify_t notify; + int (*authenticate)(struct fingerprint_device *dev, uint64_t operation_id, uint32_t gid); - /* Reserved for future use. Must be NULL. */ - void* reserved[8 - 4]; + /* Reserved for backward binary compatibility */ + void *reserved[4]; } fingerprint_device_t; typedef struct fingerprint_module { diff --git a/include/hardware/fused_location.h b/include/hardware/fused_location.h index 5c7821c8..73360a12 100644 --- a/include/hardware/fused_location.h +++ b/include/hardware/fused_location.h @@ -72,6 +72,37 @@ __BEGIN_DECLS #define FLP_TECH_MASK_BLUETOOTH (1U<<4) /** + * Set when your implementation can produce GNNS-derived locations, + * for use with flp_capabilities_callback. + * + * GNNS is a required capability for a particular feature to be used + * (batching or geofencing). If not supported that particular feature + * won't be used by the upper layer. + */ +#define CAPABILITY_GNSS (1U<<0) +/** + * Set when your implementation can produce WiFi-derived locations, for + * use with flp_capabilities_callback. + */ +#define CAPABILITY_WIFI (1U<<1) +/** + * Set when your implementation can produce cell-derived locations, for + * use with flp_capabilities_callback. + */ +#define CAPABILITY_CELL (1U<<3) + +/** + * Status to return in flp_status_callback when your implementation transitions + * from being unsuccessful in determining location to being successful. + */ +#define FLP_STATUS_LOCATION_AVAILABLE 0 +/** + * Status to return in flp_status_callback when your implementation transitions + * from being successful in determining location to being unsuccessful. + */ +#define FLP_STATUS_LOCATION_UNAVAILABLE 1 + +/** * This constant is used with the batched locations * APIs. Batching is mandatory when FLP implementation * is supported. If the flag is set, the hardware implementation @@ -183,6 +214,33 @@ typedef void (*flp_release_wakelock)(); */ typedef int (*flp_set_thread_event)(ThreadEvent event); +/** + * Callback for technologies supported by this implementation. + * + * Parameters: capabilities is a bitmask of FLP_CAPABILITY_* values describing + * which features your implementation supports. You should support + * CAPABILITY_GNSS at a minimum for your implementation to be utilized. You can + * return 0 in FlpGeofenceCallbacks to indicate you don't support geofencing, + * or 0 in FlpCallbacks to indicate you don't support location batching. + */ +typedef void (*flp_capabilities_callback)(int capabilities); + +/** + * Callback with status information on the ability to compute location. + * To avoid waking up the application processor you should only send + * changes in status (you shouldn't call this method twice in a row + * with the same status value). As a guideline you should not call this + * more frequently then the requested batch period set with period_ns + * in FlpBatchOptions. For example if period_ns is set to 5 minutes and + * the status changes many times in that interval, you should only report + * one status change every 5 minutes. + * + * Parameters: + * status is one of FLP_STATUS_LOCATION_AVAILABLE + * or FLP_STATUS_LOCATION_UNAVAILABLE. + */ +typedef void (*flp_status_callback)(int32_t status); + /** FLP callback structure. */ typedef struct { /** set to sizeof(FlpCallbacks) */ @@ -191,6 +249,8 @@ typedef struct { flp_acquire_wakelock acquire_wakelock_cb; flp_release_wakelock release_wakelock_cb; flp_set_thread_event set_thread_event_cb; + flp_capabilities_callback flp_capabilities_cb; + flp_status_callback flp_status_cb; } FlpCallbacks; @@ -228,6 +288,23 @@ typedef struct { * seconds. */ int64_t period_ns; + + /** + * The smallest displacement between reported locations in meters. + * + * If set to 0, then you should report locations at the requested + * interval even if the device is stationary. If positive, you + * can use this parameter as a hint to save power (e.g. throttling + * location period if the user hasn't traveled close to the displacement + * threshold). Even small positive values can be interpreted to mean + * that you don't have to compute location when the device is stationary. + * + * There is no need to filter location delivery based on this parameter. + * Locations can be delivered even if they have a displacement smaller than + * requested. This parameter can safely be ignored at the cost of potential + * power savings. + */ + float smallest_displacement_meters; } FlpBatchOptions; #define FLP_RESULT_SUCCESS 0 @@ -249,7 +326,9 @@ typedef struct { /** * Opens the interface and provides the callback routines - * to the implemenation of this interface. + * to the implementation of this interface. Once called you should respond + * by calling the flp_capabilities_callback in FlpCallbacks to + * specify the capabilities that your implementation supports. */ int (*init)(FlpCallbacks* callbacks ); @@ -346,6 +425,15 @@ typedef struct { * Get a pointer to extension information. */ const void* (*get_extension)(const char* name); + + /** + * Retrieve all batched locations currently stored and clear the buffer. + * flp_location_callback MUST be called in response, even if there are + * no locations to flush (in which case num_locations should be 0). + * Subsequent calls to get_batched_location or flush_batched_locations + * should not return any of the locations returned in this call. + */ + void (*flush_batched_locations)(); } FlpLocationInterface; struct flp_device_t { @@ -598,6 +686,7 @@ typedef struct { flp_geofence_pause_callback geofence_pause_callback; flp_geofence_resume_callback geofence_resume_callback; flp_set_thread_event set_thread_event_cb; + flp_capabilities_callback flp_capabilities_cb; } FlpGeofenceCallbacks; @@ -678,7 +767,9 @@ typedef struct { /** * Opens the geofence interface and provides the callback routines - * to the implemenation of this interface. + * to the implemenation of this interface. Once called you should respond + * by calling the flp_capabilities_callback in FlpGeofenceCallbacks to + * specify the capabilities that your implementation supports. */ void (*init)( FlpGeofenceCallbacks* callbacks ); diff --git a/include/hardware/gatekeeper.h b/include/hardware/gatekeeper.h index 6d2fb0b9..2bb2b08c 100644 --- a/include/hardware/gatekeeper.h +++ b/include/hardware/gatekeeper.h @@ -143,7 +143,36 @@ struct gatekeeper_device { const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll); + /* + * Deletes the enrolled_password_handle associated wth the uid. Once deleted + * the user cannot be verified anymore. + * This function is optional and should be set to NULL if it is not implemented. + * + * Parameters + * - dev: pointer to gatekeeper_device acquired via calls to gatekeeper_open + * - uid: the Android user identifier + * + * Returns: + * - 0 on success + * - An error code < 0 on failure + */ + int (*delete_user)(const struct gatekeeper_device *dev, uint32_t uid); + + /* + * Deletes all the enrolled_password_handles for all uid's. Once called, + * no users will be enrolled on the device. + * This function is optional and should be set to NULL if it is not implemented. + * + * Parameters + * - dev: pointer to gatekeeper_device acquired via calls to gatekeeper_open + * + * Returns: + * - 0 on success + * - An error code < 0 on failure + */ + int (*delete_all_users)(const struct gatekeeper_device *dev); }; + typedef struct gatekeeper_device gatekeeper_device_t; static inline int gatekeeper_open(const struct hw_module_t *module, diff --git a/include/hardware/gps.h b/include/hardware/gps.h index e264cf53..76b6cb7a 100644 --- a/include/hardware/gps.h +++ b/include/hardware/gps.h @@ -51,7 +51,10 @@ typedef uint32_t GpsPositionMode; #define GPS_POSITION_MODE_STANDALONE 0 /** AGPS MS-Based mode. */ #define GPS_POSITION_MODE_MS_BASED 1 -/** AGPS MS-Assisted mode. */ +/** + * AGPS MS-Assisted mode. This mode is not maintained by the platform anymore. + * It is strongly recommended to use GPS_POSITION_MODE_MS_BASE instead. + */ #define GPS_POSITION_MODE_MS_ASSISTED 2 /** Requested recurrence mode for GPS operation. */ @@ -293,6 +296,8 @@ typedef uint32_t GpsMeasurementFlags; #define GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY (1<<16) /** A valid 'used in fix' flag is stored in the data structure. */ #define GPS_MEASUREMENT_HAS_USED_IN_FIX (1<<17) +/** The value of 'pseudorange rate' is uncorrected. */ +#define GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE (1<<18) /** * Enumeration of the available values for the GPS Measurement's loss of lock. @@ -617,6 +622,12 @@ typedef struct { * min_interval represents the time between fixes in milliseconds. * preferred_accuracy represents the requested fix accuracy in meters. * preferred_time represents the requested time to first fix in milliseconds. + * + * 'mode' parameter should be one of GPS_POSITION_MODE_MS_BASE + * or GPS_POSITION_MODE_STANDALONE. + * It is allowed by the platform (and it is recommended) to fallback to + * GPS_POSITION_MODE_MS_BASE if GPS_POSITION_MODE_MS_ASSISTED is passed in, and + * GPS_POSITION_MODE_MS_BASED is supported. */ int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence, uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time); @@ -662,6 +673,12 @@ typedef struct { size_t (*get_internal_state)(char* buffer, size_t bufferSize); } GpsDebugInterface; +#pragma pack(push,4) +// We need to keep the alignment of this data structure to 4-bytes, to ensure that in 64-bit +// environments the size of this legacy definition does not collide with _v2. Implementations should +// be using _v2 and _v3, so it's OK to pay the 'unaligned' penalty in 64-bit if an old +// implementation is still in use. + /** Represents the status of AGPS. */ typedef struct { /** set to sizeof(AGpsStatus_v1) */ @@ -671,6 +688,8 @@ typedef struct { AGpsStatusValue status; } AGpsStatus_v1; +#pragma pack(pop) + /** Represents the status of AGPS augmented with a IPv4 address field. */ typedef struct { /** set to sizeof(AGpsStatus_v2) */ @@ -1353,6 +1372,9 @@ typedef struct { * * The value contains the 'drift uncertainty' in it. * If the data is available 'flags' must contain GPS_CLOCK_HAS_DRIFT. + * + * If GpsMeasurement's 'flags' field contains GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE, + * it is encouraged that this field is also provided. */ double drift_nsps; @@ -1416,11 +1438,15 @@ typedef struct { * * However, if there is any ambiguity in integer millisecond, * GPS_MEASUREMENT_STATE_MSEC_AMBIGUOUS should be set accordingly, in the 'state' field. + * + * This value must be populated if 'state' != GPS_MEASUREMENT_STATE_UNKNOWN. */ int64_t received_gps_tow_ns; /** * 1-Sigma uncertainty of the Received GPS Time-of-Week in nanoseconds. + * + * This value must be populated if 'state' != GPS_MEASUREMENT_STATE_UNKNOWN. */ int64_t received_gps_tow_uncertainty_ns; @@ -1434,11 +1460,23 @@ typedef struct { /** * Pseudorange rate at the timestamp in m/s. - * The value also includes the effects of the receiver clock frequency and satellite clock - * frequency errors. + * The correction of a given Pseudorange Rate value includes corrections for receiver and + * satellite clock frequency errors. + * + * If GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE is set in 'flags' field, this field must + * be populated with the 'uncorrected' reading. + * If GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE is not set in 'flags' field, this field + * must be populated with the 'corrected' reading. This is the default behavior. + * + * It is encouraged to provide the 'uncorrected' 'pseudorange rate', and provide GpsClock's + * 'drift' field as well. * * The value includes the 'pseudorange rate uncertainty' in it. - * A positive value indicates that the pseudorange is getting larger. + * A positive 'uncorrected' value indicates that the SV is moving away from the receiver. + * + * The sign of the 'uncorrected' 'pseudorange rate' and its relation to the sign of 'doppler + * shift' is given by the equation: + * pseudorange rate = -k * doppler shift (where k is a constant) * * This is a Mandatory value. */ @@ -1462,13 +1500,21 @@ typedef struct { /** * Accumulated delta range since the last channel reset in meters. - * The data is available if 'accumulated delta range state' != GPS_ADR_STATE_UNKNOWN. + * A positive value indicates that the SV is moving away from the receiver. + * + * The sign of the 'accumulated delta range' and its relation to the sign of 'carrier phase' + * is given by the equation: + * accumulated delta range = -k * carrier phase (where k is a constant) + * + * This value must be populated if 'accumulated delta range state' != GPS_ADR_STATE_UNKNOWN. + * However, it is expected that the data is only accurate when: + * 'accumulated delta range state' == GPS_ADR_STATE_VALID. */ double accumulated_delta_range_m; /** * 1-Sigma uncertainty of the accumulated delta range in meters. - * The data is available if 'accumulated delta range state' != GPS_ADR_STATE_UNKNOWN. + * This value must be populated if 'accumulated delta range state' != GPS_ADR_STATE_UNKNOWN. */ double accumulated_delta_range_uncertainty_m; diff --git a/include/hardware/hw_auth_token.h b/include/hardware/hw_auth_token.h index 40283cb2..f471d1ab 100644 --- a/include/hardware/hw_auth_token.h +++ b/include/hardware/hw_auth_token.h @@ -19,7 +19,7 @@ #ifndef ANDROID_HARDWARE_HW_AUTH_TOKEN_H #define ANDROID_HARDWARE_HW_AUTH_TOKEN_H -#ifndef __cplusplus +#ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -46,7 +46,7 @@ typedef struct __attribute__((__packed__)) { uint8_t hmac[32]; } hw_auth_token_t; -#ifndef __cplusplus +#ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/include/hardware/input.h b/include/hardware/input.h new file mode 100644 index 00000000..969b8ce5 --- /dev/null +++ b/include/hardware/input.h @@ -0,0 +1,549 @@ +/* + * 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_INCLUDE_HARDWARE_INPUT_H +#define ANDROID_INCLUDE_HARDWARE_INPUT_H + +#include <hardware/hardware.h> +#include <stdint.h> + +__BEGIN_DECLS + +#define INPUT_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) +#define INPUT_HARDWARE_MODULE_ID "input" + +#define INPUT_INSTANCE_EVDEV "evdev" + +typedef enum input_bus { + INPUT_BUS_BT, + INPUT_BUS_USB, + INPUT_BUS_SERIAL, + INPUT_BUS_BUILTIN +} input_bus_t; + +typedef struct input_host input_host_t; + +typedef struct input_device_handle input_device_handle_t; + +typedef struct input_device_identifier input_device_identifier_t; + +typedef struct input_device_definition input_device_definition_t; + +typedef struct input_report_definition input_report_definition_t; + +typedef struct input_report input_report_t; + +typedef struct input_collection input_collection_t; + +typedef struct input_property_map input_property_map_t; + +typedef struct input_property input_property_t; + +typedef enum { + // keycodes + INPUT_USAGE_KEYCODE_UNKNOWN, + INPUT_USAGE_KEYCODE_SOFT_LEFT, + INPUT_USAGE_KEYCODE_SOFT_RIGHT, + INPUT_USAGE_KEYCODE_HOME, + INPUT_USAGE_KEYCODE_BACK, + INPUT_USAGE_KEYCODE_CALL, + INPUT_USAGE_KEYCODE_ENDCALL, + INPUT_USAGE_KEYCODE_0, + INPUT_USAGE_KEYCODE_1, + INPUT_USAGE_KEYCODE_2, + INPUT_USAGE_KEYCODE_3, + INPUT_USAGE_KEYCODE_4, + INPUT_USAGE_KEYCODE_5, + INPUT_USAGE_KEYCODE_6, + INPUT_USAGE_KEYCODE_7, + INPUT_USAGE_KEYCODE_8, + INPUT_USAGE_KEYCODE_9, + INPUT_USAGE_KEYCODE_STAR, + INPUT_USAGE_KEYCODE_POUND, + INPUT_USAGE_KEYCODE_DPAD_UP, + INPUT_USAGE_KEYCODE_DPAD_DOWN, + INPUT_USAGE_KEYCODE_DPAD_LEFT, + INPUT_USAGE_KEYCODE_DPAD_RIGHT, + INPUT_USAGE_KEYCODE_DPAD_CENTER, + INPUT_USAGE_KEYCODE_VOLUME_UP, + INPUT_USAGE_KEYCODE_VOLUME_DOWN, + INPUT_USAGE_KEYCODE_POWER, + INPUT_USAGE_KEYCODE_CAMERA, + INPUT_USAGE_KEYCODE_CLEAR, + INPUT_USAGE_KEYCODE_A, + INPUT_USAGE_KEYCODE_B, + INPUT_USAGE_KEYCODE_C, + INPUT_USAGE_KEYCODE_D, + INPUT_USAGE_KEYCODE_E, + INPUT_USAGE_KEYCODE_F, + INPUT_USAGE_KEYCODE_G, + INPUT_USAGE_KEYCODE_H, + INPUT_USAGE_KEYCODE_I, + INPUT_USAGE_KEYCODE_J, + INPUT_USAGE_KEYCODE_K, + INPUT_USAGE_KEYCODE_L, + INPUT_USAGE_KEYCODE_M, + INPUT_USAGE_KEYCODE_N, + INPUT_USAGE_KEYCODE_O, + INPUT_USAGE_KEYCODE_P, + INPUT_USAGE_KEYCODE_Q, + INPUT_USAGE_KEYCODE_R, + INPUT_USAGE_KEYCODE_S, + INPUT_USAGE_KEYCODE_T, + INPUT_USAGE_KEYCODE_U, + INPUT_USAGE_KEYCODE_V, + INPUT_USAGE_KEYCODE_W, + INPUT_USAGE_KEYCODE_X, + INPUT_USAGE_KEYCODE_Y, + INPUT_USAGE_KEYCODE_Z, + INPUT_USAGE_KEYCODE_COMMA, + INPUT_USAGE_KEYCODE_PERIOD, + INPUT_USAGE_KEYCODE_ALT_LEFT, + INPUT_USAGE_KEYCODE_ALT_RIGHT, + INPUT_USAGE_KEYCODE_SHIFT_LEFT, + INPUT_USAGE_KEYCODE_SHIFT_RIGHT, + INPUT_USAGE_KEYCODE_TAB, + INPUT_USAGE_KEYCODE_SPACE, + INPUT_USAGE_KEYCODE_SYM, + INPUT_USAGE_KEYCODE_EXPLORER, + INPUT_USAGE_KEYCODE_ENVELOPE, + INPUT_USAGE_KEYCODE_ENTER, + INPUT_USAGE_KEYCODE_DEL, + INPUT_USAGE_KEYCODE_GRAVE, + INPUT_USAGE_KEYCODE_MINUS, + INPUT_USAGE_KEYCODE_EQUALS, + INPUT_USAGE_KEYCODE_LEFT_BRACKET, + INPUT_USAGE_KEYCODE_RIGHT_BRACKET, + INPUT_USAGE_KEYCODE_BACKSLASH, + INPUT_USAGE_KEYCODE_SEMICOLON, + INPUT_USAGE_KEYCODE_APOSTROPHE, + INPUT_USAGE_KEYCODE_SLASH, + INPUT_USAGE_KEYCODE_AT, + INPUT_USAGE_KEYCODE_NUM, + INPUT_USAGE_KEYCODE_HEADSETHOOK, + INPUT_USAGE_KEYCODE_FOCUS, // *Camera* focus + INPUT_USAGE_KEYCODE_PLUS, + INPUT_USAGE_KEYCODE_MENU, + INPUT_USAGE_KEYCODE_NOTIFICATION, + INPUT_USAGE_KEYCODE_SEARCH, + INPUT_USAGE_KEYCODE_MEDIA_PLAY_PAUSE, + INPUT_USAGE_KEYCODE_MEDIA_STOP, + INPUT_USAGE_KEYCODE_MEDIA_NEXT, + INPUT_USAGE_KEYCODE_MEDIA_PREVIOUS, + INPUT_USAGE_KEYCODE_MEDIA_REWIND, + INPUT_USAGE_KEYCODE_MEDIA_FAST_FORWARD, + INPUT_USAGE_KEYCODE_MUTE, + INPUT_USAGE_KEYCODE_PAGE_UP, + INPUT_USAGE_KEYCODE_PAGE_DOWN, + INPUT_USAGE_KEYCODE_PICTSYMBOLS, + INPUT_USAGE_KEYCODE_SWITCH_CHARSET, + INPUT_USAGE_KEYCODE_BUTTON_A, + INPUT_USAGE_KEYCODE_BUTTON_B, + INPUT_USAGE_KEYCODE_BUTTON_C, + INPUT_USAGE_KEYCODE_BUTTON_X, + INPUT_USAGE_KEYCODE_BUTTON_Y, + INPUT_USAGE_KEYCODE_BUTTON_Z, + INPUT_USAGE_KEYCODE_BUTTON_L1, + INPUT_USAGE_KEYCODE_BUTTON_R1, + INPUT_USAGE_KEYCODE_BUTTON_L2, + INPUT_USAGE_KEYCODE_BUTTON_R2, + INPUT_USAGE_KEYCODE_BUTTON_THUMBL, + INPUT_USAGE_KEYCODE_BUTTON_THUMBR, + INPUT_USAGE_KEYCODE_BUTTON_START, + INPUT_USAGE_KEYCODE_BUTTON_SELECT, + INPUT_USAGE_KEYCODE_BUTTON_MODE, + INPUT_USAGE_KEYCODE_ESCAPE, + INPUT_USAGE_KEYCODE_FORWARD_DEL, + INPUT_USAGE_KEYCODE_CTRL_LEFT, + INPUT_USAGE_KEYCODE_CTRL_RIGHT, + INPUT_USAGE_KEYCODE_CAPS_LOCK, + INPUT_USAGE_KEYCODE_SCROLL_LOCK, + INPUT_USAGE_KEYCODE_META_LEFT, + INPUT_USAGE_KEYCODE_META_RIGHT, + INPUT_USAGE_KEYCODE_FUNCTION, + INPUT_USAGE_KEYCODE_SYSRQ, + INPUT_USAGE_KEYCODE_BREAK, + INPUT_USAGE_KEYCODE_MOVE_HOME, + INPUT_USAGE_KEYCODE_MOVE_END, + INPUT_USAGE_KEYCODE_INSERT, + INPUT_USAGE_KEYCODE_FORWARD, + INPUT_USAGE_KEYCODE_MEDIA_PLAY, + INPUT_USAGE_KEYCODE_MEDIA_PAUSE, + INPUT_USAGE_KEYCODE_MEDIA_CLOSE, + INPUT_USAGE_KEYCODE_MEDIA_EJECT, + INPUT_USAGE_KEYCODE_MEDIA_RECORD, + INPUT_USAGE_KEYCODE_F1, + INPUT_USAGE_KEYCODE_F2, + INPUT_USAGE_KEYCODE_F3, + INPUT_USAGE_KEYCODE_F4, + INPUT_USAGE_KEYCODE_F5, + INPUT_USAGE_KEYCODE_F6, + INPUT_USAGE_KEYCODE_F7, + INPUT_USAGE_KEYCODE_F8, + INPUT_USAGE_KEYCODE_F9, + INPUT_USAGE_KEYCODE_F10, + INPUT_USAGE_KEYCODE_F11, + INPUT_USAGE_KEYCODE_F12, + INPUT_USAGE_KEYCODE_NUM_LOCK, + INPUT_USAGE_KEYCODE_NUMPAD_0, + INPUT_USAGE_KEYCODE_NUMPAD_1, + INPUT_USAGE_KEYCODE_NUMPAD_2, + INPUT_USAGE_KEYCODE_NUMPAD_3, + INPUT_USAGE_KEYCODE_NUMPAD_4, + INPUT_USAGE_KEYCODE_NUMPAD_5, + INPUT_USAGE_KEYCODE_NUMPAD_6, + INPUT_USAGE_KEYCODE_NUMPAD_7, + INPUT_USAGE_KEYCODE_NUMPAD_8, + INPUT_USAGE_KEYCODE_NUMPAD_9, + INPUT_USAGE_KEYCODE_NUMPAD_DIVIDE, + INPUT_USAGE_KEYCODE_NUMPAD_MULTIPLY, + INPUT_USAGE_KEYCODE_NUMPAD_SUBTRACT, + INPUT_USAGE_KEYCODE_NUMPAD_ADD, + INPUT_USAGE_KEYCODE_NUMPAD_DOT, + INPUT_USAGE_KEYCODE_NUMPAD_COMMA, + INPUT_USAGE_KEYCODE_NUMPAD_ENTER, + INPUT_USAGE_KEYCODE_NUMPAD_EQUALS, + INPUT_USAGE_KEYCODE_NUMPAD_LEFT_PAREN, + INPUT_USAGE_KEYCODE_NUMPAD_RIGHT_PAREN, + INPUT_USAGE_KEYCODE_VOLUME_MUTE, + INPUT_USAGE_KEYCODE_INFO, + INPUT_USAGE_KEYCODE_CHANNEL_UP, + INPUT_USAGE_KEYCODE_CHANNEL_DOWN, + INPUT_USAGE_KEYCODE_ZOOM_IN, + INPUT_USAGE_KEYCODE_ZOOM_OUT, + INPUT_USAGE_KEYCODE_TV, + INPUT_USAGE_KEYCODE_WINDOW, + INPUT_USAGE_KEYCODE_GUIDE, + INPUT_USAGE_KEYCODE_DVR, + INPUT_USAGE_KEYCODE_BOOKMARK, + INPUT_USAGE_KEYCODE_CAPTIONS, + INPUT_USAGE_KEYCODE_SETTINGS, + INPUT_USAGE_KEYCODE_TV_POWER, + INPUT_USAGE_KEYCODE_TV_INPUT, + INPUT_USAGE_KEYCODE_STB_POWER, + INPUT_USAGE_KEYCODE_STB_INPUT, + INPUT_USAGE_KEYCODE_AVR_POWER, + INPUT_USAGE_KEYCODE_AVR_INPUT, + INPUT_USAGE_KEYCODE_PROG_RED, + INPUT_USAGE_KEYCODE_PROG_GREEN, + INPUT_USAGE_KEYCODE_PROG_YELLOW, + INPUT_USAGE_KEYCODE_PROG_BLUE, + INPUT_USAGE_KEYCODE_APP_SWITCH, + INPUT_USAGE_KEYCODE_BUTTON_1, + INPUT_USAGE_KEYCODE_BUTTON_2, + INPUT_USAGE_KEYCODE_BUTTON_3, + INPUT_USAGE_KEYCODE_BUTTON_4, + INPUT_USAGE_KEYCODE_BUTTON_5, + INPUT_USAGE_KEYCODE_BUTTON_6, + INPUT_USAGE_KEYCODE_BUTTON_7, + INPUT_USAGE_KEYCODE_BUTTON_8, + INPUT_USAGE_KEYCODE_BUTTON_9, + INPUT_USAGE_KEYCODE_BUTTON_10, + INPUT_USAGE_KEYCODE_BUTTON_11, + INPUT_USAGE_KEYCODE_BUTTON_12, + INPUT_USAGE_KEYCODE_BUTTON_13, + INPUT_USAGE_KEYCODE_BUTTON_14, + INPUT_USAGE_KEYCODE_BUTTON_15, + INPUT_USAGE_KEYCODE_BUTTON_16, + INPUT_USAGE_KEYCODE_LANGUAGE_SWITCH, + INPUT_USAGE_KEYCODE_MANNER_MODE, + INPUT_USAGE_KEYCODE_3D_MODE, + INPUT_USAGE_KEYCODE_CONTACTS, + INPUT_USAGE_KEYCODE_CALENDAR, + INPUT_USAGE_KEYCODE_MUSIC, + INPUT_USAGE_KEYCODE_CALCULATOR, + INPUT_USAGE_KEYCODE_ZENKAKU_HANKAKU, + INPUT_USAGE_KEYCODE_EISU, + INPUT_USAGE_KEYCODE_MUHENKAN, + INPUT_USAGE_KEYCODE_HENKAN, + INPUT_USAGE_KEYCODE_KATAKANA_HIRAGANA, + INPUT_USAGE_KEYCODE_YEN, + INPUT_USAGE_KEYCODE_RO, + INPUT_USAGE_KEYCODE_KANA, + INPUT_USAGE_KEYCODE_ASSIST, + INPUT_USAGE_KEYCODE_BRIGHTNESS_DOWN, + INPUT_USAGE_KEYCODE_BRIGHTNESS_UP, + INPUT_USAGE_KEYCODE_MEDIA_AUDIO_TRACK, + INPUT_USAGE_KEYCODE_SLEEP, + INPUT_USAGE_KEYCODE_WAKEUP, + INPUT_USAGE_KEYCODE_PAIRING, + INPUT_USAGE_KEYCODE_MEDIA_TOP_MENU, + INPUT_USAGE_KEYCODE_11, + INPUT_USAGE_KEYCODE_12, + INPUT_USAGE_KEYCODE_LAST_CHANNEL, + INPUT_USAGE_KEYCODE_TV_DATA_SERVICE, + INPUT_USAGE_KEYCODE_VOICE_ASSIST, + INPUT_USAGE_KEYCODE_TV_RADIO_SERVICE, + INPUT_USAGE_KEYCODE_TV_TELETEXT, + INPUT_USAGE_KEYCODE_TV_NUMBER_ENTRY, + INPUT_USAGE_KEYCODE_TV_TERRESTRIAL_ANALOG, + INPUT_USAGE_KEYCODE_TV_TERRESTRIAL_DIGITAL, + INPUT_USAGE_KEYCODE_TV_SATELLITE, + INPUT_USAGE_KEYCODE_TV_SATELLITE_BS, + INPUT_USAGE_KEYCODE_TV_SATELLITE_CS, + INPUT_USAGE_KEYCODE_TV_SATELLITE_SERVICE, + INPUT_USAGE_KEYCODE_TV_NETWORK, + INPUT_USAGE_KEYCODE_TV_ANTENNA_CABLE, + INPUT_USAGE_KEYCODE_TV_INPUT_HDMI_1, + INPUT_USAGE_KEYCODE_TV_INPUT_HDMI_2, + INPUT_USAGE_KEYCODE_TV_INPUT_HDMI_3, + INPUT_USAGE_KEYCODE_TV_INPUT_HDMI_4, + INPUT_USAGE_KEYCODE_TV_INPUT_COMPOSITE_1, + INPUT_USAGE_KEYCODE_TV_INPUT_COMPOSITE_2, + INPUT_USAGE_KEYCODE_TV_INPUT_COMPONENT_1, + INPUT_USAGE_KEYCODE_TV_INPUT_COMPONENT_2, + INPUT_USAGE_KEYCODE_TV_INPUT_VGA_1, + INPUT_USAGE_KEYCODE_TV_AUDIO_DESCRIPTION, + INPUT_USAGE_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP, + INPUT_USAGE_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN, + INPUT_USAGE_KEYCODE_TV_ZOOM_MODE, + INPUT_USAGE_KEYCODE_TV_CONTENTS_MENU, + INPUT_USAGE_KEYCODE_TV_MEDIA_CONTEXT_MENU, + INPUT_USAGE_KEYCODE_TV_TIMER_PROGRAMMING, + INPUT_USAGE_KEYCODE_HELP, + + // axes + INPUT_USAGE_AXIS_X, + INPUT_USAGE_AXIS_Y, + INPUT_USAGE_AXIS_PRESSURE, + INPUT_USAGE_AXIS_SIZE, + INPUT_USAGE_AXIS_TOUCH_MAJOR, + INPUT_USAGE_AXIS_TOUCH_MINOR, + INPUT_USAGE_AXIS_TOOL_MAJOR, + INPUT_USAGE_AXIS_TOOL_MINOR, + INPUT_USAGE_AXIS_ORIENTATION, + INPUT_USAGE_AXIS_VSCROLL, + INPUT_USAGE_AXIS_HSCROLL, + INPUT_USAGE_AXIS_Z, + INPUT_USAGE_AXIS_RX, + INPUT_USAGE_AXIS_RY, + INPUT_USAGE_AXIS_RZ, + INPUT_USAGE_AXIS_HAT_X, + INPUT_USAGE_AXIS_HAT_Y, + INPUT_USAGE_AXIS_LTRIGGER, + INPUT_USAGE_AXIS_RTRIGGER, + INPUT_USAGE_AXIS_THROTTLE, + INPUT_USAGE_AXIS_RUDDER, + INPUT_USAGE_AXIS_WHEEL, + INPUT_USAGE_AXIS_GAS, + INPUT_USAGE_AXIS_BRAKE, + INPUT_USAGE_AXIS_DISTANCE, + INPUT_USAGE_AXIS_TILT, + INPUT_USAGE_AXIS_GENERIC_1, + INPUT_USAGE_AXIS_GENERIC_2, + INPUT_USAGE_AXIS_GENERIC_3, + INPUT_USAGE_AXIS_GENERIC_4, + INPUT_USAGE_AXIS_GENERIC_5, + INPUT_USAGE_AXIS_GENERIC_6, + INPUT_USAGE_AXIS_GENERIC_7, + INPUT_USAGE_AXIS_GENERIC_8, + INPUT_USAGE_AXIS_GENERIC_9, + INPUT_USAGE_AXIS_GENERIC_10, + INPUT_USAGE_AXIS_GENERIC_11, + INPUT_USAGE_AXIS_GENERIC_12, + INPUT_USAGE_AXIS_GENERIC_13, + INPUT_USAGE_AXIS_GENERIC_14, + INPUT_USAGE_AXIS_GENERIC_15, + INPUT_USAGE_AXIS_GENERIC_16, + + // leds + INPUT_USAGE_LED_NUM_LOCK, + INPUT_USAGE_LED_CAPS_LOCK, + INPUT_USAGE_LED_SCROLL_LOCK, + INPUT_USAGE_LED_COMPOSE, + INPUT_USAGE_LED_KANA, + INPUT_USAGE_LED_SLEEP, + INPUT_USAGE_LED_SUSPEND, + INPUT_USAGE_LED_MUTE, + INPUT_USAGE_LED_MISC, + INPUT_USAGE_LED_MAIL, + INPUT_USAGE_LED_CHARGING, + INPUT_USAGE_LED_CONTROLLER_1, + INPUT_USAGE_LED_CONTROLLER_2, + INPUT_USAGE_LED_CONTROLLER_3, + INPUT_USAGE_LED_CONTROLLER_4, +} input_usage_t; + +typedef enum { + INPUT_COLLECTION_ID_TOUCH, + INPUT_COLLECTION_ID_KEYBOARD, + INPUT_COLLECTION_ID_MOUSE, + INPUT_COLLECTION_ID_TOUCHPAD, + // etc +} input_collection_id_t; + +typedef struct input_message input_message_t; + +typedef struct input_host_callbacks { + + /** + * Creates a device identifier with the given properties. + * The unique ID should be a string that precisely identifies a given piece of hardware. For + * example, an input device connected via Bluetooth could use its MAC address as its unique ID. + */ + input_device_identifier_t* (*create_device_identifier)(input_host_t* host, + const char* name, int32_t product_id, int32_t vendor_id, + input_bus_t bus, const char* unique_id); + + /** + * Allocates the device definition which will describe the input capabilities of a device. A + * device definition may be used to register as many devices as desired. + */ + input_device_definition_t* (*create_device_definition)(input_host_t* host); + + /** + * Allocate either an input report, which the HAL will use to tell the host of incoming input + * events, or an output report, which the host will use to tell the HAL of desired state + * changes (e.g. setting an LED). + */ + input_report_definition_t* (*create_input_report_definition)(input_host_t* host); + input_report_definition_t* (*create_output_report_definition)(input_host_t* host); + + /** + * Append the report to the given input device. + */ + void (*input_device_definition_add_report)(input_host_t* host, + input_device_definition_t* d, input_report_definition_t* r); + + /** + * Add a collection with the given arity and ID. A collection describes a set + * of logically grouped properties such as the X and Y coordinates of a single finger touch or + * the set of keys on a keyboard. The arity declares how many repeated instances of this + * collection will appear in whatever report it is attached to. The ID describes the type of + * grouping being represented by the collection. For example, a touchscreen capable of + * reporting up to 2 fingers simultaneously might have a collection with the X and Y + * coordinates, an arity of 2, and an ID of INPUT_COLLECTION_USAGE_TOUCHSCREEN. Any given ID + * may only be present once for a given report. + */ + void (*input_report_definition_add_collection)(input_host_t* host, + input_report_definition_t* report, input_collection_id_t id, int32_t arity); + + /** + * Declare an int usage with the given properties. The report and collection defines where the + * usage is being declared. + */ + void (*input_report_definition_declare_usage_int)(input_host_t* host, + input_report_definition_t* report, input_collection_id_t id, + input_usage_t usage, int32_t min, int32_t max, float resolution); + + /** + * Declare a set of boolean usages with the given properties. The report and collection + * defines where the usages are being declared. + */ + void (*input_report_definition_declare_usages_bool)(input_host_t* host, + input_report_definition_t* report, input_collection_id_t id, + input_usage_t* usage, size_t usage_count); + + + /** + * Register a given input device definition. This notifies the host that an input device has + * been connected and gives a description of all its capabilities. + */ + input_device_handle_t* (*register_device)(input_host_t* host, + input_device_identifier_t* id, input_device_definition_t* d); + + /** Unregister the given device */ + void (*unregister_device)(input_host_t* host, input_device_handle_t* handle); + + /** + * Allocate a report that will contain all of the state as described by the given report. + */ + input_report_t* (*input_allocate_report)(input_host_t* host, input_report_definition_t* r); + + /** + * Add an int usage value to a report. + */ + void (*input_report_set_usage_int)(input_host_t* host, input_report_t* r, + input_collection_id_t id, input_usage_t usage, int32_t value, int32_t arity_index); + + /** + * Add a boolean usage value to a report. + */ + void (*input_report_set_usage_bool)(input_host_t* host, input_report_t* r, + input_collection_id_t id, input_usage_t usage, bool value, int32_t arity_index); + + void (*report_event)(input_host_t* host, input_device_handle_t* d, input_report_t* report); + + /** + * Retrieve the set of properties for the device. The returned + * input_property_map_t* may be used to query specific properties via the + * input_get_device_property callback. + */ + input_property_map_t* (*input_get_device_property_map)(input_host_t* host, + input_device_identifier_t* id); + /** + * Retrieve a property for the device with the given key. Returns NULL if + * the key does not exist, or an input_property_t* that must be freed using + * input_free_device_property(). Using an input_property_t after the + * corresponding input_property_map_t is freed is undefined. + */ + input_property_t* (*input_get_device_property)(input_host_t* host, + input_property_map_t* map, const char* key); + + /** + * Get the key for the input property. Returns NULL if the property is NULL. + * The returned const char* is owned by the input_property_t. + */ + const char* (*input_get_property_key)(input_host_t* host, input_property_t* property); + + /** + * Get the value for the input property. Returns NULL if the property is + * NULL. The returned const char* is owned by the input_property_t. + */ + const char* (*input_get_property_value)(input_host_t* host, input_property_t* property); + + /** + * Frees the input_property_t*. + */ + void (*input_free_device_property)(input_host_t* host, input_property_t* property); + + /** + * Frees the input_property_map_t*. + */ + void (*input_free_device_property_map)(input_host_t* host, input_property_map_t* map); +} input_host_callbacks_t; + +typedef struct input_module input_module_t; + +struct input_module { + /** + * Common methods of the input module. This *must* be the first member + * of input_module as users of this structure will cast a hw_module_t + * to input_module pointer in contexts where it's known + * the hw_module_t references a input_module. + */ + struct hw_module_t common; + + /** + * Initialize the module with host callbacks. At this point the HAL should start up whatever + * infrastructure it needs to in order to process input events. + */ + void (*init)(const input_module_t* module, input_host_t* host, input_host_callbacks_t cb); + + /** + * Sends an output report with a new set of state the host would like the given device to + * assume. + */ + void (*notify_report)(const input_module_t* module, input_report_t* report); +}; + +static inline int input_open(const struct hw_module_t** module, const char* type) { + return hw_get_module_by_class(INPUT_HARDWARE_MODULE_ID, type, module); +} + +__END_DECLS + +#endif /* ANDROID_INCLUDE_HARDWARE_INPUT_H */ diff --git a/include/hardware/radio.h b/include/hardware/radio.h new file mode 100644 index 00000000..145deb57 --- /dev/null +++ b/include/hardware/radio.h @@ -0,0 +1,298 @@ +/* + * 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 <system/radio.h> +#include <hardware/hardware.h> + +#ifndef ANDROID_RADIO_HAL_H +#define ANDROID_RADIO_HAL_H + + +__BEGIN_DECLS + +/** + * The id of this module + */ +#define RADIO_HARDWARE_MODULE_ID "radio" + +/** + * Name of the audio devices to open + */ +#define RADIO_HARDWARE_DEVICE "radio_hw_device" + +#define RADIO_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0) +#define RADIO_MODULE_API_VERSION_CURRENT RADIO_MODULE_API_VERSION_1_0 + + +#define RADIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0) +#define RADIO_DEVICE_API_VERSION_CURRENT RADIO_DEVICE_API_VERSION_1_0 + +/** + * List of known radio HAL modules. This is the base name of the radio HAL + * library composed of the "radio." prefix, one of the base names below and + * a suffix specific to the device. + * E.g: radio.fm.default.so + */ + +#define RADIO_HARDWARE_MODULE_ID_FM "fm" /* corresponds to RADIO_CLASS_AM_FM */ +#define RADIO_HARDWARE_MODULE_ID_SAT "sat" /* corresponds to RADIO_CLASS_SAT */ +#define RADIO_HARDWARE_MODULE_ID_DT "dt" /* corresponds to RADIO_CLASS_DT */ + + +/** + * 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. + */ +struct radio_module { + struct hw_module_t common; +}; + +/* + * Callback function called by the HAL when one of the following occurs: + * - event RADIO_EVENT_HW_FAILURE: radio chip of driver failure requiring + * closing and reopening of the tuner interface. + * - event RADIO_EVENT_CONFIG: new configuration applied in response to open_tuner(), + * or set_configuration(). The event status is 0 (no error) if the configuration has been applied, + * -EINVAL is not or -ETIMEDOUT in case of time out. + * - event RADIO_EVENT_TUNED: tune locked on new station/frequency following scan(), + * step(), tune() or auto AF switching. The event status is 0 (no error) if in tune, + * -EINVAL is not tuned and data in radio_program_info is not valid or -ETIMEDOUT if scan() + * timed out. + * - event RADIO_EVENT_TA: at the beginning and end of traffic announcement if current + * configuration enables TA. + * - event RADIO_EVENT_AF: after automatic switching to alternate frequency if current + * configuration enables AF switching. + * - event RADIO_EVENT_ANTENNA: when the antenna is connected or disconnected. + * - event RADIO_EVENT_METADATA: when new meta data are received from the tuned station. + * The callback MUST NOT be called synchronously while executing a HAL function but from + * a separate thread. + */ +typedef void (*radio_callback_t)(radio_hal_event_t *event, void *cookie); + +/* control interface for a radio tuner */ +struct radio_tuner { + /* + * Apply current radio band configuration (band, range, channel spacing ...). + * + * arguments: + * - config: the band configuration to apply + * + * returns: + * 0 if configuration could be applied + * -EINVAL if configuration requested is invalid + * + * Automatically cancels pending scan, step or tune. + * + * Callback function with event RADIO_EVENT_CONFIG MUST be called once the + * configuration is applied or a failure occurs or after a time out. + */ + int (*set_configuration)(const struct radio_tuner *tuner, + const radio_hal_band_config_t *config); + + /* + * Retrieve current radio band configuration. + * + * arguments: + * - config: where to return the band configuration + * + * returns: + * 0 if valid configuration is returned + * -EINVAL if invalid arguments are passed + */ + int (*get_configuration)(const struct radio_tuner *tuner, + radio_hal_band_config_t *config); + + /* + * Start scanning up to next valid station. + * Must be called when a valid configuration has been applied. + * + * arguments: + * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN + * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels + * (e.g SPS for HD radio). + * + * returns: + * 0 if scan successfully started + * -ENOSYS if called out of sequence + * -ENODEV if another error occurs + * + * Automatically cancels pending scan, step or tune. + * + * Callback function with event RADIO_EVENT_TUNED MUST be called once + * locked on a station or after a time out or full frequency scan if + * no station found. The event status should indicate if a valid station + * is tuned or not. + */ + int (*scan)(const struct radio_tuner *tuner, + radio_direction_t direction, bool skip_sub_channel); + + /* + * Move one channel spacing up or down. + * Must be called when a valid configuration has been applied. + * + * arguments: + * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN + * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels + * (e.g SPS for HD radio). + * + * returns: + * 0 if step successfully started + * -ENOSYS if called out of sequence + * -ENODEV if another error occurs + * + * Automatically cancels pending scan, step or tune. + * + * Callback function with event RADIO_EVENT_TUNED MUST be called once + * step completed or after a time out. The event status should indicate + * if a valid station is tuned or not. + */ + int (*step)(const struct radio_tuner *tuner, + radio_direction_t direction, bool skip_sub_channel); + + /* + * Tune to specified frequency. + * Must be called when a valid configuration has been applied. + * + * arguments: + * - channel: channel to tune to. A frequency in kHz for AM/FM/HD Radio bands. + * - sub_channel: valid for HD radio or digital radios only: (e.g SPS number for HD radio). + * + * returns: + * 0 if tune successfully started + * -ENOSYS if called out of sequence + * -EINVAL if invalid arguments are passed + * -ENODEV if another error occurs + * + * Automatically cancels pending scan, step or tune. + * + * Callback function with event RADIO_EVENT_TUNED MUST be called once + * tuned or after a time out. The event status should indicate + * if a valid station is tuned or not. + */ + int (*tune)(const struct radio_tuner *tuner, + unsigned int channel, unsigned int sub_channel); + + /* + * Cancel a scan, step or tune operation. + * Must be called while a scan, step or tune operation is pending + * (callback not yet sent). + * + * returns: + * 0 if successful + * -ENOSYS if called out of sequence + * -ENODEV if another error occurs + * + * The callback is not sent. + */ + int (*cancel)(const struct radio_tuner *tuner); + + /* + * Retrieve current station information. + * + * arguments: + * - info: where to return the program info. + * If info->metadata is NULL. no meta data should be returned. + * If meta data must be returned, they should be added to or cloned to + * info->metadata, not passed from a newly created meta data buffer. + * + * returns: + * 0 if tuned and information available + * -EINVAL if invalid arguments are passed + * -ENODEV if another error occurs + */ + int (*get_program_information)(const struct radio_tuner *tuner, + radio_program_info_t *info); +}; + +struct radio_hw_device { + struct hw_device_t common; + + /* + * Retrieve implementation properties. + * + * arguments: + * - properties: where to return the module properties + * + * returns: + * 0 if no error + * -EINVAL if invalid arguments are passed + */ + int (*get_properties)(const struct radio_hw_device *dev, + radio_hal_properties_t *properties); + + /* + * Open a tuner interface for the requested configuration. + * If no other tuner is opened, this will activate the radio module. + * + * arguments: + * - config: the band configuration to apply + * - audio: this tuner will be used for live radio listening and should be connected to + * the radio audio source. + * - callback: the event callback + * - cookie: the cookie to pass when calling the callback + * - tuner: where to return the tuner interface + * + * returns: + * 0 if HW was powered up and configuration could be applied + * -EINVAL if configuration requested is invalid + * -ENOSYS if called out of sequence + * + * Callback function with event RADIO_EVENT_CONFIG MUST be called once the + * configuration is applied or a failure occurs or after a time out. + */ + int (*open_tuner)(const struct radio_hw_device *dev, + const radio_hal_band_config_t *config, + bool audio, + radio_callback_t callback, + void *cookie, + const struct radio_tuner **tuner); + + /* + * Close a tuner interface. + * If the last tuner is closed, the radio module is deactivated. + * + * arguments: + * - tuner: the tuner interface to close + * + * returns: + * 0 if powered down successfully. + * -EINVAL if an invalid argument is passed + * -ENOSYS if called out of sequence + */ + int (*close_tuner)(const struct radio_hw_device *dev, const struct radio_tuner *tuner); + +}; + +typedef struct radio_hw_device radio_hw_device_t; + +/** convenience API for opening and closing a supported device */ + +static inline int radio_hw_device_open(const struct hw_module_t* module, + struct radio_hw_device** device) +{ + return module->methods->open(module, RADIO_HARDWARE_DEVICE, + (struct hw_device_t**)device); +} + +static inline int radio_hw_device_close(const struct radio_hw_device* device) +{ + return device->common.close((struct hw_device_t *)&device->common); +} + +__END_DECLS + +#endif // ANDROID_RADIO_HAL_H diff --git a/include/hardware/sensors.h b/include/hardware/sensors.h index e917c0a8..b368ee6f 100644 --- a/include/hardware/sensors.h +++ b/include/hardware/sensors.h @@ -35,6 +35,7 @@ __BEGIN_DECLS #define SENSORS_DEVICE_API_VERSION_1_1 HARDWARE_DEVICE_API_VERSION_2(1, 1, SENSORS_HEADER_VERSION) #define SENSORS_DEVICE_API_VERSION_1_2 HARDWARE_DEVICE_API_VERSION_2(1, 2, SENSORS_HEADER_VERSION) #define SENSORS_DEVICE_API_VERSION_1_3 HARDWARE_DEVICE_API_VERSION_2(1, 3, SENSORS_HEADER_VERSION) +#define SENSORS_DEVICE_API_VERSION_1_4 HARDWARE_DEVICE_API_VERSION_2(1, 4, SENSORS_HEADER_VERSION) /** * Please see the Sensors section of source.android.com for an @@ -93,6 +94,29 @@ enum { #define SENSOR_PERMISSION_BODY_SENSORS "android.permission.BODY_SENSORS" /* + * Availability: SENSORS_DEVICE_API_VERSION_1_4 + * Sensor HAL modes used in set_operation_mode method + */ +enum { + /* + * Operating modes for the HAL. + */ + + /* + * Normal mode operation. This is the default state of operation. + * The HAL shall initialize into this mode on device startup. + */ + SENSOR_HAL_NORMAL_MODE = 0, + + /* + * Data Injection mode. In this mode, the device shall not source data from the + * physical sensors as it would in normal mode. Instead sensor data is + * injected by the sensor service. + */ + SENSOR_HAL_DATA_INJECTION_MODE = 0x1 +}; + +/* * Availability: SENSORS_DEVICE_API_VERSION_1_3 * Sensor flags used in sensor_t.flags. */ @@ -114,7 +138,16 @@ enum { SENSOR_FLAG_CONTINUOUS_MODE = 0, // 0000 SENSOR_FLAG_ON_CHANGE_MODE = 0x2, // 0010 SENSOR_FLAG_ONE_SHOT_MODE = 0x4, // 0100 - SENSOR_FLAG_SPECIAL_REPORTING_MODE = 0x6 // 0110 + SENSOR_FLAG_SPECIAL_REPORTING_MODE = 0x6, // 0110 + + /* + * Set this flag if the sensor supports data_injection mode and allows data to be injected + * from the SensorService. When in data_injection ONLY sensors with this flag set are injected + * sensor data and only sensors with this flag set are activated. Eg: Accelerometer and Step + * Counter sensors can be set with this flag and SensorService will inject accelerometer data + * and read the corresponding step counts. + */ + SENSOR_FLAG_SUPPORTS_DATA_INJECTION = 0x8 // 1000 }; /* @@ -124,6 +157,12 @@ enum { #define REPORTING_MODE_SHIFT (1) /* + * Mask and shift for data_injection mode sensor flags defined above. + */ +#define DATA_INJECTION_MASK (0x10) +#define DATA_INJECTION_SHIFT (4) + +/* * Sensor type * * Each sensor has a type which defines what this sensor measures and how @@ -807,6 +846,18 @@ struct sensors_module_t { */ int (*get_sensors_list)(struct sensors_module_t* module, struct sensor_t const** list); + + /** + * Place the module in a specific mode. The following modes are defined + * + * 0 - Normal operation. Default state of the module. + * 1 - Loopback mode. Data is injected for the the supported + * sensors by the sensor service in this mode. + * @return 0 on success + * -EINVAL if requested mode is not supported + * -EPERM if operation is not allowed + */ + int (*set_operation_mode)(unsigned int mode); }; struct sensor_t { @@ -1004,7 +1055,16 @@ typedef struct sensors_poll_device_1 { */ int (*flush)(struct sensors_poll_device_1* dev, int sensor_handle); - void (*reserved_procs[8])(void); + /* + * Inject a single sensor sample to be to this device. + * data points to the sensor event to be injected + * @return 0 on success + * -EPERM if operation is not allowed + * -EINVAL if sensor event cannot be injected + */ + int (*inject_sensor_data)(struct sensors_poll_device_1 *dev, const sensors_event_t *data); + + void (*reserved_procs[7])(void); } sensors_poll_device_1_t; diff --git a/modules/Android.mk b/modules/Android.mk index 0725d3e3..9f7e5f0d 100644 --- a/modules/Android.mk +++ b/modules/Android.mk @@ -1,4 +1,4 @@ hardware_modules := gralloc hwcomposer audio nfc nfc-nci local_time \ - power usbaudio audio_remote_submix camera consumerir sensors vibrator \ - tv_input fingerprint + power usbaudio audio_remote_submix camera usbcamera consumerir sensors vibrator \ + tv_input fingerprint input include $(call all-named-subdir-makefiles,$(hardware_modules)) diff --git a/modules/audio/Android.mk b/modules/audio/Android.mk index a31c85f3..ef4b8f5c 100644 --- a/modules/audio/Android.mk +++ b/modules/audio/Android.mk @@ -31,6 +31,23 @@ LOCAL_CFLAGS := -Wno-unused-parameter include $(BUILD_SHARED_LIBRARY) +# The stub audio HAL module, identical to the default audio hal, but with +# different name to be loaded concurrently with other audio HALs if necessary. +# This can also be used as skeleton for new implementations +# +# The format of the name is audio.<type>.<hardware/etc>.so where the only +# required type is 'primary'. Other possibilites are 'a2dp', 'usb', etc. +include $(CLEAR_VARS) + +LOCAL_MODULE := audio.stub.default +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SRC_FILES := audio_hw.c +LOCAL_SHARED_LIBRARIES := liblog libcutils +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wno-unused-parameter + +include $(BUILD_SHARED_LIBRARY) + # The stub audio policy HAL module that can be used as a skeleton for # new implementations. include $(CLEAR_VARS) diff --git a/modules/audio/audio_hw.c b/modules/audio/audio_hw.c index 637e3f4b..a1a322ff 100644 --- a/modules/audio/audio_hw.c +++ b/modules/audio/audio_hw.c @@ -48,65 +48,78 @@ static uint32_t out_get_sample_rate(const struct audio_stream *stream) static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - return 0; + ALOGV("out_set_sample_rate: %d", 0); + return -ENOSYS; } static size_t out_get_buffer_size(const struct audio_stream *stream) { + ALOGV("out_get_buffer_size: %d", 4096); return 4096; } static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) { + ALOGV("out_get_channels"); return AUDIO_CHANNEL_OUT_STEREO; } static audio_format_t out_get_format(const struct audio_stream *stream) { + ALOGV("out_get_format"); return AUDIO_FORMAT_PCM_16_BIT; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { - return 0; + ALOGV("out_set_format: %d",format); + return -ENOSYS; } static int out_standby(struct audio_stream *stream) { + ALOGV("out_standby"); + return 0; } static int out_dump(const struct audio_stream *stream, int fd) { + ALOGV("out_dump"); return 0; } static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { + ALOGV("out_set_parameters"); return 0; } static char * out_get_parameters(const struct audio_stream *stream, const char *keys) { + ALOGV("out_get_parameters"); return strdup(""); } static uint32_t out_get_latency(const struct audio_stream_out *stream) { + ALOGV("out_get_latency"); return 0; } static int out_set_volume(struct audio_stream_out *stream, float left, float right) { + ALOGV("out_set_volume: Left:%f Right:%f", left, right); return 0; } static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes) { + ALOGV("out_write: bytes: %d", bytes); /* XXX: fake timing for audio output */ - usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) / + usleep((int64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) / out_get_sample_rate(&stream->common)); return bytes; } @@ -114,43 +127,53 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) { + *dsp_frames = 0; + ALOGV("out_get_render_position: dsp_frames: %p", dsp_frames); return -EINVAL; } static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { + ALOGV("out_add_audio_effect: %p", effect); return 0; } static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { + ALOGV("out_remove_audio_effect: %p", effect); return 0; } static int out_get_next_write_timestamp(const struct audio_stream_out *stream, int64_t *timestamp) { + *timestamp = 0; + ALOGV("out_get_next_write_timestamp: %ld", (long int)(*timestamp)); return -EINVAL; } /** audio_stream_in implementation **/ static uint32_t in_get_sample_rate(const struct audio_stream *stream) { + ALOGV("in_get_sample_rate"); return 8000; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - return 0; + ALOGV("in_set_sample_rate: %d", rate); + return -ENOSYS; } static size_t in_get_buffer_size(const struct audio_stream *stream) { + ALOGV("in_get_buffer_size: %d", 320); return 320; } 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; } @@ -161,7 +184,7 @@ static audio_format_t in_get_format(const struct audio_stream *stream) static int in_set_format(struct audio_stream *stream, audio_format_t format) { - return 0; + return -ENOSYS; } static int in_standby(struct audio_stream *stream) @@ -193,9 +216,11 @@ static int in_set_gain(struct audio_stream_in *stream, float gain) static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes) { + ALOGV("in_read: bytes %d", bytes); /* XXX: fake timing for audio input */ - usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) / + usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) / in_get_sample_rate(&stream->common)); + memset(buffer, 0, bytes); return bytes; } @@ -222,6 +247,8 @@ static int adev_open_output_stream(struct audio_hw_device *dev, struct audio_stream_out **stream_out, const char *address __unused) { + ALOGV("adev_open_output_stream..."); + struct stub_audio_device *ladev = (struct stub_audio_device *)dev; struct stub_stream_out *out; int ret; @@ -260,68 +287,81 @@ err_open: static void adev_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream) { + ALOGV("adev_close_output_stream..."); free(stream); } static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { + ALOGV("adev_set_parameters"); return -ENOSYS; } static char * adev_get_parameters(const struct audio_hw_device *dev, const char *keys) { - return NULL; + ALOGV("adev_get_parameters"); + return strdup(""); } static int adev_init_check(const struct audio_hw_device *dev) { + ALOGV("adev_init_check"); return 0; } static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) { + ALOGV("adev_set_voice_volume: %f", volume); return -ENOSYS; } static int adev_set_master_volume(struct audio_hw_device *dev, float volume) { + ALOGV("adev_set_master_volume: %f", volume); return -ENOSYS; } static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) { + ALOGV("adev_get_master_volume: %f", *volume); return -ENOSYS; } static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) { + ALOGV("adev_set_master_mute: %d", muted); return -ENOSYS; } static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) { + ALOGV("adev_get_master_mute: %d", *muted); return -ENOSYS; } static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) { + ALOGV("adev_set_mode: %d", mode); return 0; } static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) { + ALOGV("adev_set_mic_mute: %d",state); return -ENOSYS; } static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) { + ALOGV("adev_get_mic_mute"); return -ENOSYS; } 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; } @@ -334,6 +374,8 @@ static int adev_open_input_stream(struct audio_hw_device *dev, const char *address __unused, audio_source_t source __unused) { + ALOGV("adev_open_input_stream..."); + struct stub_audio_device *ladev = (struct stub_audio_device *)dev; struct stub_stream_in *in; int ret; @@ -370,16 +412,19 @@ err_open: static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *in) { + ALOGV("adev_close_input_stream..."); return; } static int adev_dump(const audio_hw_device_t *device, int fd) { + ALOGV("adev_dump"); return 0; } static int adev_close(hw_device_t *device) { + ALOGV("adev_close"); free(device); return 0; } @@ -387,6 +432,8 @@ static int adev_close(hw_device_t *device) static int adev_open(const hw_module_t* module, const char* name, hw_device_t** device) { + ALOGV("adev_open: %s", name); + struct stub_audio_device *adev; int ret; diff --git a/modules/audio_remote_submix/audio_hw.cpp b/modules/audio_remote_submix/audio_hw.cpp index b9dcf7ac..20c0fab9 100644 --- a/modules/audio_remote_submix/audio_hw.cpp +++ b/modules/audio_remote_submix/audio_hw.cpp @@ -25,6 +25,7 @@ #include <sys/time.h> #include <sys/limits.h> +#include <cutils/compiler.h> #include <cutils/log.h> #include <cutils/properties.h> #include <cutils/str_parms.h> @@ -62,7 +63,7 @@ namespace android { #endif // SUBMIX_VERBOSE_LOGGING // NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe(). -#define DEFAULT_PIPE_SIZE_IN_FRAMES (1024*8) +#define DEFAULT_PIPE_SIZE_IN_FRAMES (1024*4) // Value used to divide the MonoPipe() buffer into segments that are written to the source and // read from the sink. The maximum latency of the device is the size of the MonoPipe's buffer // the minimum latency is the MonoPipe buffer size divided by this value. @@ -178,6 +179,7 @@ struct submix_stream_out { struct submix_audio_device *dev; int route_handle; bool output_standby; + uint64_t write_counter_frames; #if LOG_STREAMS_TO_FILES int log_fd; #endif // LOG_STREAMS_TO_FILES @@ -189,11 +191,10 @@ struct submix_stream_in { int route_handle; bool input_standby; bool output_standby_rec_thr; // output standby state as seen from record thread - // wall clock when recording starts struct timespec record_start_time; // how many frames have been requested to be read - int64_t read_counter_frames; + uint64_t read_counter_frames; #if ENABLE_LEGACY_INPUT_OPEN // Number of references to this input stream. @@ -699,6 +700,7 @@ static int out_standby(struct audio_stream *stream) pthread_mutex_lock(&rsxadev->lock); out->output_standby = true; + out->write_counter_frames = 0; pthread_mutex_unlock(&rsxadev->lock); @@ -852,6 +854,9 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, pthread_mutex_lock(&rsxadev->lock); sink.clear(); + if (written_frames > 0) { + out->write_counter_frames += written_frames; + } pthread_mutex_unlock(&rsxadev->lock); if (written_frames < 0) { @@ -863,12 +868,51 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, return written_bytes; } +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) +{ + const submix_stream_out * out = reinterpret_cast<const struct submix_stream_out *> + (reinterpret_cast<const uint8_t *>(stream) - + offsetof(struct submix_stream_out, stream)); + struct submix_audio_device * const rsxadev = out->dev; + int ret = 0; + + pthread_mutex_lock(&rsxadev->lock); + + if (frames) { + const ssize_t frames_in_pipe = + rsxadev->routes[out->route_handle].rsxSource->availableToRead(); + if (CC_UNLIKELY(frames_in_pipe < 0)) { + *frames = out->write_counter_frames; + } else { + *frames = out->write_counter_frames > (uint64_t) frames_in_pipe ? + out->write_counter_frames - frames_in_pipe : 0; + } + } + if (timestamp) { + clock_gettime(CLOCK_MONOTONIC, timestamp); + } + + pthread_mutex_unlock(&rsxadev->lock); + + SUBMIX_ALOGV("out_get_presentation_position() got frames=%llu timestamp sec=%llu", + frames ? *frames : -1, timestamp ? timestamp->tv_sec : -1); + + return ret; +} + static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) { - (void)stream; - (void)dsp_frames; - return -EINVAL; + if (!dsp_frames) { + return -EINVAL; + } + uint64_t frames = 0; + int ret = out_get_presentation_position(stream, &frames, NULL); + if ((ret == 0) && dsp_frames) { + *dsp_frames = (uint32_t) frames; + } + return ret; } static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) @@ -1335,6 +1379,9 @@ 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->stream.get_presentation_position = out_get_presentation_position; + + out->write_counter_frames = 0; #if ENABLE_RESAMPLING // Recreate the pipe with the correct sample rate so that MonoPipe.write() rate limits diff --git a/modules/camera/CameraHAL.cpp b/modules/camera/CameraHAL.cpp index 6f64a0d9..62ee6d41 100644 --- a/modules/camera/CameraHAL.cpp +++ b/modules/camera/CameraHAL.cpp @@ -185,6 +185,8 @@ camera_module_t HAL_MODULE_INFO_SYM __attribute__ ((visibility("default"))) = { set_callbacks : set_callbacks, get_vendor_tag_ops : get_vendor_tag_ops, open_legacy : NULL, + set_torch_mode : NULL, + init : NULL, reserved : {0}, }; } // extern "C" diff --git a/modules/camera/ExampleCamera.cpp b/modules/camera/ExampleCamera.cpp index ca28b993..d8733217 100644 --- a/modules/camera/ExampleCamera.cpp +++ b/modules/camera/ExampleCamera.cpp @@ -92,7 +92,7 @@ camera_metadata_t *ExampleCamera::initStaticInfo() /* android.scaler */ int32_t android_scaler_available_formats[] = { - HAL_PIXEL_FORMAT_RAW_SENSOR, + HAL_PIXEL_FORMAT_RAW16, HAL_PIXEL_FORMAT_BLOB, HAL_PIXEL_FORMAT_RGBA_8888, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, diff --git a/modules/camera/Stream.cpp b/modules/camera/Stream.cpp index 90ad30b9..e0099b6e 100644 --- a/modules/camera/Stream.cpp +++ b/modules/camera/Stream.cpp @@ -117,10 +117,6 @@ const char* Stream::formatToString(int format) return "RGB 888"; case HAL_PIXEL_FORMAT_RGB_565: return "RGB 565"; - case HAL_PIXEL_FORMAT_sRGB_A_8888: - return "sRGB A 8888"; - case HAL_PIXEL_FORMAT_sRGB_X_8888: - return "sRGB B 8888"; case HAL_PIXEL_FORMAT_Y8: return "Y8"; case HAL_PIXEL_FORMAT_Y16: @@ -133,8 +129,10 @@ const char* Stream::formatToString(int format) return "NV21"; case HAL_PIXEL_FORMAT_YCbCr_422_I: return "YUY2"; - case HAL_PIXEL_FORMAT_RAW_SENSOR: - return "RAW SENSOR"; + case HAL_PIXEL_FORMAT_RAW10: + return "RAW10"; + case HAL_PIXEL_FORMAT_RAW16: + return "RAW16"; case HAL_PIXEL_FORMAT_BLOB: return "BLOB"; case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: diff --git a/modules/fingerprint/fingerprint.c b/modules/fingerprint/fingerprint.c index 03465182..08b112b6 100644 --- a/modules/fingerprint/fingerprint.c +++ b/modules/fingerprint/fingerprint.c @@ -32,13 +32,38 @@ static int fingerprint_close(hw_device_t *dev) } } + +static uint64_t fingerprint_pre_enroll(struct fingerprint_device __unused *dev) { + return FINGERPRINT_ERROR; +} + static int fingerprint_enroll(struct fingerprint_device __unused *dev, + const hw_auth_token_t __unused *hat, + uint32_t __unused gid, uint32_t __unused timeout_sec) { return FINGERPRINT_ERROR; } +static uint64_t fingerprint_get_auth_id(struct fingerprint_device __unused *dev) { + return FINGERPRINT_ERROR; +} + +static int fingerprint_cancel(struct fingerprint_device __unused *dev) { + return FINGERPRINT_ERROR; +} + static int fingerprint_remove(struct fingerprint_device __unused *dev, - uint32_t __unused fingerprint_id) { + uint32_t __unused gid, uint32_t __unused fid) { + return FINGERPRINT_ERROR; +} + +static int fingerprint_set_active_group(struct fingerprint_device __unused *dev, + uint32_t __unused gid, const char __unused *store_path) { + return FINGERPRINT_ERROR; +} + +static int fingerprint_authenticate(struct fingerprint_device __unused *dev, + uint64_t __unused operation_id, __unused uint32_t gid) { return FINGERPRINT_ERROR; } @@ -61,12 +86,17 @@ static int fingerprint_open(const hw_module_t* module, const char __unused *id, memset(dev, 0, sizeof(fingerprint_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; - dev->common.version = HARDWARE_MODULE_API_VERSION(1, 0); + dev->common.version = FINGERPRINT_MODULE_API_VERSION_2_0; dev->common.module = (struct hw_module_t*) module; dev->common.close = fingerprint_close; + dev->pre_enroll = fingerprint_pre_enroll; dev->enroll = fingerprint_enroll; + dev->get_authenticator_id = fingerprint_get_auth_id; + dev->cancel = fingerprint_cancel; dev->remove = fingerprint_remove; + dev->set_active_group = fingerprint_set_active_group; + dev->authenticate = fingerprint_authenticate; dev->set_notify = set_notify_callback; dev->notify = NULL; @@ -81,7 +111,7 @@ static struct hw_module_methods_t fingerprint_module_methods = { fingerprint_module_t HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, - .module_api_version = FINGERPRINT_MODULE_API_VERSION_1_0, + .module_api_version = FINGERPRINT_MODULE_API_VERSION_2_0, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = FINGERPRINT_HARDWARE_MODULE_ID, .name = "Demo Fingerprint HAL", diff --git a/modules/gralloc/gralloc.cpp b/modules/gralloc/gralloc.cpp index bdc789dd..a9fbc809 100644 --- a/modules/gralloc/gralloc.cpp +++ b/modules/gralloc/gralloc.cpp @@ -217,7 +217,7 @@ static int gralloc_alloc(alloc_device_t* dev, bpp = 3; break; case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: bpp = 2; break; default: diff --git a/modules/input/Android.mk b/modules/input/Android.mk new file mode 100644 index 00000000..3011b2e2 --- /dev/null +++ b/modules/input/Android.mk @@ -0,0 +1,19 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/modules/input/evdev/Android.mk b/modules/input/evdev/Android.mk new file mode 100644 index 00000000..d3c49e7d --- /dev/null +++ b/modules/input/evdev/Android.mk @@ -0,0 +1,57 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +# Evdev module implementation +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + InputHub.cpp \ + InputDevice.cpp \ + InputDeviceManager.cpp \ + InputHost.cpp + +LOCAL_SHARED_LIBRARIES := \ + libhardware_legacy \ + liblog \ + libutils + +LOCAL_CLANG := true +LOCAL_CPPFLAGS += -std=c++14 -Wno-unused-parameter + +LOCAL_MODULE := libinput_evdev +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +# HAL module +include $(CLEAR_VARS) + +LOCAL_MODULE := input.evdev.default +LOCAL_MODULE_RELATIVE_PATH := hw + +LOCAL_SRC_FILES := \ + EvdevModule.cpp + +LOCAL_SHARED_LIBRARIES := \ + libinput_evdev \ + liblog + +LOCAL_CLANG := true +LOCAL_CPPFLAGS += -std=c++14 -Wno-unused-parameter + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/modules/input/evdev/EvdevModule.cpp b/modules/input/evdev/EvdevModule.cpp new file mode 100644 index 00000000..e9c82227 --- /dev/null +++ b/modules/input/evdev/EvdevModule.cpp @@ -0,0 +1,134 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "EvdevModule" + +#include <memory> +#include <string> +#include <thread> + +#include <assert.h> +#include <hardware/hardware.h> +#include <hardware/input.h> + +#include <utils/Log.h> + +#include "InputHub.h" +#include "InputDeviceManager.h" +#include "InputHost.h" + +namespace android { + +static const char kDevInput[] = "/dev/input"; + +class EvdevModule { +public: + explicit EvdevModule(InputHost inputHost); + + void init(); + void notifyReport(input_report_t* r); + +private: + void loop(); + + InputHost mInputHost; + std::shared_ptr<InputDeviceManager> mDeviceManager; + std::shared_ptr<InputHub> mInputHub; + std::thread mPollThread; +}; + +static std::shared_ptr<EvdevModule> gEvdevModule; + +EvdevModule::EvdevModule(InputHost inputHost) : + mInputHost(inputHost), + mDeviceManager(std::make_shared<InputDeviceManager>()), + mInputHub(std::make_shared<InputHub>(mDeviceManager)) {} + +void EvdevModule::init() { + ALOGV("%s", __func__); + + mInputHub->registerDevicePath(kDevInput); + mPollThread = std::thread(&EvdevModule::loop, this); +} + +void EvdevModule::notifyReport(input_report_t* r) { + ALOGV("%s", __func__); + + // notifyReport() will be called from an arbitrary thread within the input + // host. Since InputHub is not threadsafe, this is how I expect this to + // work: + // * notifyReport() will queue up the output report in the EvdevModule and + // call wake() on the InputHub. + // * In the main loop thread, after returning from poll(), the queue will + // be processed with any pending work. +} + +void EvdevModule::loop() { + ALOGV("%s", __func__); + for (;;) { + mInputHub->poll(); + + // TODO: process any pending work, like notify reports + } +} + +extern "C" { + +static int dummy_open(const hw_module_t __unused *module, const char __unused *id, + hw_device_t __unused **device) { + ALOGW("open not implemented in the input HAL!"); + return 0; +} + +static void input_init(const input_module_t* module, + input_host_t* host, input_host_callbacks_t cb) { + LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0); + InputHost inputHost = {host, cb}; + gEvdevModule = std::make_shared<EvdevModule>(inputHost); + gEvdevModule->init(); +} + +static void input_notify_report(const input_module_t* module, input_report_t* r) { + LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0); + LOG_ALWAYS_FATAL_IF(gEvdevModule == nullptr); + gEvdevModule->notifyReport(r); +} + +static struct hw_module_methods_t input_module_methods = { + .open = dummy_open, +}; + +input_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = INPUT_MODULE_API_VERSION_1_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = INPUT_HARDWARE_MODULE_ID, + .name = "Input evdev HAL", + .author = "The Android Open Source Project", + .methods = &input_module_methods, + .dso = NULL, + .reserved = {0}, + }, + + .init = input_init, + .notify_report = input_notify_report, +}; + +} // extern "C" + +} // namespace input diff --git a/modules/input/evdev/InputDevice.cpp b/modules/input/evdev/InputDevice.cpp new file mode 100644 index 00000000..c0b59d7f --- /dev/null +++ b/modules/input/evdev/InputDevice.cpp @@ -0,0 +1,102 @@ +/* + * 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 "InputDevice" +#define LOG_NDEBUG 0 + +#include <linux/input.h> + +#define __STDC_FORMAT_MACROS +#include <cinttypes> +#include <string> + +#include <utils/Log.h> +#include <utils/Timers.h> + +#include "InputHub.h" +#include "InputDevice.h" + +#define MSC_ANDROID_TIME_SEC 0x6 +#define MSC_ANDROID_TIME_USEC 0x7 + +namespace android { + +EvdevDevice::EvdevDevice(std::shared_ptr<InputDeviceNode> node) : + mDeviceNode(node) {} + +void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) { + std::string log; + log.append("---InputEvent for device %s---\n"); + log.append(" when: %" PRId64 "\n"); + log.append(" type: %d\n"); + log.append(" code: %d\n"); + log.append(" value: %d\n"); + ALOGV(log.c_str(), mDeviceNode->getPath().c_str(), event.when, event.type, event.code, + event.value); + + if (event.type == EV_MSC) { + if (event.code == MSC_ANDROID_TIME_SEC) { + mOverrideSec = event.value; + } else if (event.code == MSC_ANDROID_TIME_USEC) { + mOverrideUsec = event.value; + } + return; + } + + if (mOverrideSec || mOverrideUsec) { + event.when = s2ns(mOverrideSec) + us2ns(mOverrideUsec); + ALOGV("applied override time %d.%06d", mOverrideSec, mOverrideUsec); + + if (event.type == EV_SYN && event.code == SYN_REPORT) { + mOverrideSec = 0; + mOverrideUsec = 0; + } + } + + // Bug 7291243: Add a guard in case the kernel generates timestamps + // that appear to be far into the future because they were generated + // using the wrong clock source. + // + // This can happen because when the input device is initially opened + // it has a default clock source of CLOCK_REALTIME. Any input events + // enqueued right after the device is opened will have timestamps + // generated using CLOCK_REALTIME. We later set the clock source + // to CLOCK_MONOTONIC but it is already too late. + // + // Invalid input event timestamps can result in ANRs, crashes and + // and other issues that are hard to track down. We must not let them + // propagate through the system. + // + // Log a warning so that we notice the problem and recover gracefully. + if (event.when >= currentTime + s2ns(10)) { + // Double-check. Time may have moved on. + auto time = systemTime(SYSTEM_TIME_MONOTONIC); + if (event.when > time) { + ALOGW("An input event from %s has a timestamp that appears to have " + "been generated using the wrong clock source (expected " + "CLOCK_MONOTONIC): event time %" PRId64 ", current time %" PRId64 + ", call time %" PRId64 ". Using current time instead.", + mDeviceNode->getPath().c_str(), event.when, time, currentTime); + event.when = time; + } else { + ALOGV("Event time is ok but failed the fast path and required an extra " + "call to systemTime: event time %" PRId64 ", current time %" PRId64 + ", call time %" PRId64 ".", event.when, time, currentTime); + } + } +} + +} // namespace android diff --git a/modules/input/evdev/InputDevice.h b/modules/input/evdev/InputDevice.h new file mode 100644 index 00000000..3aa16cca --- /dev/null +++ b/modules/input/evdev/InputDevice.h @@ -0,0 +1,60 @@ +/* + * 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_INPUT_DEVICE_H_ +#define ANDROID_INPUT_DEVICE_H_ + +#include <memory> + +#include <utils/Timers.h> + +#include "InputHub.h" + +namespace android { + +/** + * InputDeviceInterface represents an input device in the HAL. It processes + * input events before passing them to the input host. + */ +class InputDeviceInterface { +public: + virtual void processInput(InputEvent& event, nsecs_t currentTime) = 0; + +protected: + InputDeviceInterface() = default; + virtual ~InputDeviceInterface() = default; +}; + +/** + * EvdevDevice is an input device backed by a Linux evdev node. + */ +class EvdevDevice : public InputDeviceInterface { +public: + explicit EvdevDevice(std::shared_ptr<InputDeviceNode> node); + virtual ~EvdevDevice() override = default; + + virtual void processInput(InputEvent& event, nsecs_t currentTime) override; + +private: + std::shared_ptr<InputDeviceNode> mDeviceNode; + + int32_t mOverrideSec = 0; + int32_t mOverrideUsec = 0; +}; + +} // namespace android + +#endif // ANDROID_INPUT_DEVICE_H_ diff --git a/modules/input/evdev/InputDeviceManager.cpp b/modules/input/evdev/InputDeviceManager.cpp new file mode 100644 index 00000000..ceddd90e --- /dev/null +++ b/modules/input/evdev/InputDeviceManager.cpp @@ -0,0 +1,50 @@ +/* + * 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 "InputDeviceManager" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> + +#include "InputDevice.h" +#include "InputDeviceManager.h" + +namespace android { + +void InputDeviceManager::onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event, + nsecs_t event_time) { + if (mDevices[node] == nullptr) { + ALOGE("got input event for unknown node %s", node->getPath().c_str()); + return; + } + mDevices[node]->processInput(event, event_time); +} + +void InputDeviceManager::onDeviceAdded(std::shared_ptr<InputDeviceNode> node) { + mDevices[node] = std::make_shared<EvdevDevice>(node); +} + +void InputDeviceManager::onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) { + if (mDevices[node] == nullptr) { + ALOGE("could not remove unknown node %s", node->getPath().c_str()); + return; + } + // TODO: tell the InputDevice and InputDeviceNode that they are being + // removed so they can run any cleanup. + mDevices.erase(node); +} + +} // namespace android diff --git a/modules/input/evdev/InputDeviceManager.h b/modules/input/evdev/InputDeviceManager.h new file mode 100644 index 00000000..b6521550 --- /dev/null +++ b/modules/input/evdev/InputDeviceManager.h @@ -0,0 +1,53 @@ +/* + * 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_INPUT_DEVICE_MANAGER_H_ +#define ANDROID_INPUT_DEVICE_MANAGER_H_ + +#include <memory> +#include <unordered_map> + +#include <utils/Timers.h> + +#include "InputDevice.h" +#include "InputHub.h" + +namespace android { + +/** + * InputDeviceManager keeps the mapping of InputDeviceNodes to + * InputDeviceInterfaces and handles the callbacks from the InputHub, delegating + * them to the appropriate InputDeviceInterface. + */ +class InputDeviceManager : public InputCallbackInterface { +public: + virtual ~InputDeviceManager() override = default; + + virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event, + nsecs_t event_time) override; + virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) override; + virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) override; + +private: + template<class T, class U> + using DeviceMap = std::unordered_map<std::shared_ptr<T>, std::shared_ptr<U>>; + + DeviceMap<InputDeviceNode, InputDeviceInterface> mDevices; +}; + +} // namespace android + +#endif // ANDROID_INPUT_DEVICE_MANAGER_H_ diff --git a/modules/input/evdev/InputHost.cpp b/modules/input/evdev/InputHost.cpp new file mode 100644 index 00000000..6a65fcd0 --- /dev/null +++ b/modules/input/evdev/InputHost.cpp @@ -0,0 +1,104 @@ +/* + * 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 "InputHost.h" + +namespace android { + +void InputReport::reportEvent(InputDeviceHandle d) { + mCallbacks.report_event(mHost, d, mReport); +} + +void InputReportDefinition::addCollection(InputCollectionId id, int32_t arity) { + mCallbacks.input_report_definition_add_collection(mHost, mReportDefinition, id, arity); +} + +void InputReportDefinition::declareUsage(InputCollectionId id, InputUsage usage, + int32_t min, int32_t max, float resolution) { + mCallbacks.input_report_definition_declare_usage_int(mHost, mReportDefinition, + id, usage, min, max, resolution); +} + +void InputReportDefinition::declareUsage(InputCollectionId id, InputUsage* usage, + size_t usageCount) { + mCallbacks.input_report_definition_declare_usages_bool(mHost, mReportDefinition, + id, usage, usageCount); +} + +InputReport InputReportDefinition::allocateReport() { + return InputReport(mHost, mCallbacks, + mCallbacks.input_allocate_report(mHost, mReportDefinition)); +} + +void InputDeviceDefinition::addReport(InputReportDefinition r) { + mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, r); +} + +InputProperty::~InputProperty() { + mCallbacks.input_free_device_property(mHost, mProperty); +} + +const char* InputProperty::getKey() { + return mCallbacks.input_get_property_key(mHost, mProperty); +} + +const char* InputProperty::getValue() { + return mCallbacks.input_get_property_value(mHost, mProperty); +} + +InputPropertyMap::~InputPropertyMap() { + mCallbacks.input_free_device_property_map(mHost, mMap); +} + +InputProperty InputPropertyMap::getDeviceProperty(const char* key) { + return InputProperty(mHost, mCallbacks, + mCallbacks.input_get_device_property(mHost, mMap, key)); +} + +InputDeviceIdentifier InputHost::createDeviceIdentifier(const char* name, int32_t productId, + int32_t vendorId, InputBus bus, const char* uniqueId) { + return mCallbacks.create_device_identifier(mHost, name, productId, vendorId, bus, uniqueId); +} + +InputDeviceDefinition InputHost::createDeviceDefinition() { + return InputDeviceDefinition(mHost, mCallbacks, mCallbacks.create_device_definition(mHost)); +} + +InputReportDefinition InputHost::createInputReportDefinition() { + return InputReportDefinition(mHost, mCallbacks, + mCallbacks.create_input_report_definition(mHost)); +} + +InputReportDefinition InputHost::createOutputReportDefinition() { + return InputReportDefinition(mHost, mCallbacks, + mCallbacks.create_output_report_definition(mHost)); +} + +InputDeviceHandle InputHost::registerDevice(InputDeviceIdentifier id, + InputDeviceDefinition d) { + return mCallbacks.register_device(mHost, id, d); +} + +void InputHost::unregisterDevice(InputDeviceHandle handle) { + return mCallbacks.unregister_device(mHost, handle); +} + +InputPropertyMap InputHost::getDevicePropertyMap(InputDeviceIdentifier id) { + return InputPropertyMap(mHost, mCallbacks, + mCallbacks.input_get_device_property_map(mHost, id)); +} + +} // namespace android diff --git a/modules/input/evdev/InputHost.h b/modules/input/evdev/InputHost.h new file mode 100644 index 00000000..98ce26f5 --- /dev/null +++ b/modules/input/evdev/InputHost.h @@ -0,0 +1,192 @@ +/* + * 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_INPUT_HOST_H_ +#define ANDROID_INPUT_HOST_H_ + +#include <memory> + +#include <hardware/input.h> + +namespace android { + +/** + * Classes in this file wrap the corresponding interfaces in the Input HAL. They + * are intended to be lightweight and passed by value. It is still important not + * to use an object after a HAL-specific method has freed the underlying + * representation. + * + * See hardware/input.h for details about each of these methods. + */ + +using InputBus = input_bus_t; +using InputCollectionId = input_collection_id_t; +using InputDeviceHandle = input_device_handle_t*; +using InputDeviceIdentifier = input_device_identifier_t*; +using InputUsage = input_usage_t; + +class InputHostBase { +protected: + InputHostBase(input_host_t* host, input_host_callbacks_t cb) : mHost(host), mCallbacks(cb) {} + virtual ~InputHostBase() = default; + + input_host_t* mHost; + input_host_callbacks_t mCallbacks; +}; + +class InputReport : private InputHostBase { +public: + virtual ~InputReport() = default; + + InputReport(const InputReport& rhs) = default; + InputReport& operator=(const InputReport& rhs) = default; + operator input_report_t*() const { return mReport; } + + void reportEvent(InputDeviceHandle d); + +private: + friend class InputReportDefinition; + + InputReport(input_host_t* host, input_host_callbacks_t cb, input_report_t* r) : + InputHostBase(host, cb), mReport(r) {} + + input_report_t* mReport; +}; + +class InputReportDefinition : private InputHostBase { +public: + virtual ~InputReportDefinition() = default; + + InputReportDefinition(const InputReportDefinition& rhs) = default; + InputReportDefinition& operator=(const InputReportDefinition& rhs) = default; + operator input_report_definition_t*() { return mReportDefinition; } + + void addCollection(InputCollectionId id, int32_t arity); + void declareUsage(InputCollectionId id, InputUsage usage, int32_t min, int32_t max, + float resolution); + void declareUsage(InputCollectionId id, InputUsage* usage, size_t usageCount); + + InputReport allocateReport(); + +private: + friend class InputHost; + + InputReportDefinition( + input_host_t* host, input_host_callbacks_t cb, input_report_definition_t* r) : + InputHostBase(host, cb), mReportDefinition(r) {} + + input_report_definition_t* mReportDefinition; +}; + +class InputDeviceDefinition : private InputHostBase { +public: + virtual ~InputDeviceDefinition() = default; + + InputDeviceDefinition(const InputDeviceDefinition& rhs) = default; + InputDeviceDefinition& operator=(const InputDeviceDefinition& rhs) = default; + operator input_device_definition_t*() { return mDeviceDefinition; } + + void addReport(InputReportDefinition r); + +private: + friend class InputHost; + + InputDeviceDefinition( + input_host_t* host, input_host_callbacks_t cb, input_device_definition_t* d) : + InputHostBase(host, cb), mDeviceDefinition(d) {} + + input_device_definition_t* mDeviceDefinition; +}; + +class InputProperty : private InputHostBase { +public: + virtual ~InputProperty(); + + operator input_property_t*() { return mProperty; } + + const char* getKey(); + const char* getValue(); + + // Default move constructor transfers ownership of the input_property_t + // pointer. + InputProperty(InputProperty&& rhs) = default; + + // Prevent copy/assign because of the ownership of the underlying + // input_property_t pointer. + InputProperty(const InputProperty& rhs) = delete; + InputProperty& operator=(const InputProperty& rhs) = delete; + +private: + friend class InputPropertyMap; + + InputProperty( + input_host_t* host, input_host_callbacks_t cb, input_property_t* p) : + InputHostBase(host, cb), mProperty(p) {} + + input_property_t* mProperty; +}; + +class InputPropertyMap : private InputHostBase { +public: + virtual ~InputPropertyMap(); + + operator input_property_map_t*() { return mMap; } + + InputProperty getDeviceProperty(const char* key); + + // Default move constructor transfers ownership of the input_property_map_t + // pointer. + InputPropertyMap(InputPropertyMap&& rhs) = default; + + // Prevent copy/assign because of the ownership of the underlying + // input_property_map_t pointer. + InputPropertyMap(const InputPropertyMap& rhs) = delete; + InputPropertyMap& operator=(const InputPropertyMap& rhs) = delete; + +private: + friend class InputHost; + + InputPropertyMap( + input_host_t* host, input_host_callbacks_t cb, input_property_map_t* m) : + InputHostBase(host, cb), mMap(m) {} + + input_property_map_t* mMap; +}; + +class InputHost : private InputHostBase { +public: + InputHost(input_host_t* host, input_host_callbacks_t cb) : InputHostBase(host, cb) {} + virtual ~InputHost() = default; + + InputHost(const InputHost& rhs) = default; + InputHost& operator=(const InputHost& rhs) = default; + + InputDeviceIdentifier createDeviceIdentifier(const char* name, int32_t productId, + int32_t vendorId, InputBus bus, const char* uniqueId); + + InputDeviceDefinition createDeviceDefinition(); + InputReportDefinition createInputReportDefinition(); + InputReportDefinition createOutputReportDefinition(); + + InputDeviceHandle registerDevice(InputDeviceIdentifier id, InputDeviceDefinition d); + void unregisterDevice(InputDeviceHandle handle); + + InputPropertyMap getDevicePropertyMap(InputDeviceIdentifier id); +}; + +} // namespace android + +#endif // ANDROID_INPUT_HOST_H_ diff --git a/modules/input/evdev/InputHub.cpp b/modules/input/evdev/InputHub.cpp new file mode 100644 index 00000000..e72ac2e9 --- /dev/null +++ b/modules/input/evdev/InputHub.cpp @@ -0,0 +1,802 @@ +/* + * 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 "InputHub" +#define LOG_NDEBUG 0 + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/inotify.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> + +#include <vector> + +#include "InputHub.h" + +#include <android/input.h> +#include <hardware_legacy/power.h> +#include <linux/input.h> + +#include <utils/Log.h> + +namespace android { + +static const char WAKE_LOCK_ID[] = "KeyEvents"; +static const int NO_TIMEOUT = -1; +static const int EPOLL_MAX_EVENTS = 16; +static const int INPUT_MAX_EVENTS = 128; + +static constexpr bool testBit(int bit, const uint8_t arr[]) { + return arr[bit / 8] & (1 << (bit % 8)); +} + +static constexpr size_t sizeofBitArray(size_t bits) { + return (bits + 7) / 8; +} + +static void getLinuxRelease(int* major, int* minor) { + struct utsname info; + if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) { + *major = 0, *minor = 0; + ALOGE("Could not get linux version: %s", strerror(errno)); + } +} + +static bool processHasCapability(int capability) { + LOG_ALWAYS_FATAL_IF(!cap_valid(capability), "invalid linux capability: %d", capability); + struct __user_cap_header_struct cap_header_data; + struct __user_cap_data_struct cap_data_data[2]; + cap_user_header_t caphdr = &cap_header_data; + cap_user_data_t capdata = cap_data_data; + caphdr->pid = 0; + caphdr->version = _LINUX_CAPABILITY_VERSION_3; + LOG_ALWAYS_FATAL_IF(capget(caphdr, capdata) != 0, + "Could not get process capabilities. errno=%d", errno); + ALOGV("effective capabilities: %08x %08x", capdata[0].effective, capdata[1].effective); + int idx = CAP_TO_INDEX(capability); + return capdata[idx].effective & CAP_TO_MASK(capability); +} + +class EvdevDeviceNode : public InputDeviceNode { +public: + static EvdevDeviceNode* openDeviceNode(const std::string& path); + + virtual ~EvdevDeviceNode() { + ALOGV("closing %s (fd=%d)", mPath.c_str(), mFd); + if (mFd >= 0) { + ::close(mFd); + } + } + + virtual int getFd() const { return mFd; } + virtual const std::string& getPath() const override { return mPath; } + virtual const std::string& getName() const override { return mName; } + virtual const std::string& getLocation() const override { return mLocation; } + virtual const std::string& getUniqueId() const override { return mUniqueId; } + + virtual uint16_t getBusType() const override { return mBusType; } + virtual uint16_t getVendorId() const override { return mVendorId; } + virtual uint16_t getProductId() const override { return mProductId; } + virtual uint16_t getVersion() const override { return mVersion; } + + virtual bool hasKey(int32_t key) const override; + virtual bool hasRelativeAxis(int axis) const override; + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override; + virtual bool hasInputProperty(int property) const override; + + virtual int32_t getKeyState(int32_t key) const override; + virtual int32_t getSwitchState(int32_t sw) const override; + virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const override; + + virtual void vibrate(nsecs_t duration) override; + virtual void cancelVibrate(int32_t deviceId) override; + + virtual void disableDriverKeyRepeat() override; + +private: + EvdevDeviceNode(const std::string& path, int fd) : + mFd(fd), mPath(path) {} + + status_t queryProperties(); + void queryAxisInfo(); + + int mFd; + std::string mPath; + + std::string mName; + std::string mLocation; + std::string mUniqueId; + + uint16_t mBusType; + uint16_t mVendorId; + uint16_t mProductId; + uint16_t mVersion; + + uint8_t mKeyBitmask[KEY_CNT / 8]; + uint8_t mAbsBitmask[ABS_CNT / 8]; + uint8_t mRelBitmask[REL_CNT / 8]; + uint8_t mSwBitmask[SW_CNT / 8]; + uint8_t mLedBitmask[LED_CNT / 8]; + uint8_t mFfBitmask[FF_CNT / 8]; + uint8_t mPropBitmask[INPUT_PROP_CNT / 8]; + + std::unordered_map<uint32_t, std::unique_ptr<AbsoluteAxisInfo>> mAbsInfo; + + bool mFfEffectPlaying = false; + int16_t mFfEffectId = -1; +}; + +EvdevDeviceNode* EvdevDeviceNode::openDeviceNode(const std::string& path) { + auto fd = TEMP_FAILURE_RETRY(::open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)); + if (fd < 0) { + ALOGE("could not open evdev device %s. err=%d", path.c_str(), errno); + return nullptr; + } + + // Tell the kernel that we want to use the monotonic clock for reporting + // timestamps associated with input events. This is important because the + // input system uses the timestamps extensively and assumes they were + // recorded using the monotonic clock. + // + // The EVIOCSCLOCKID ioctl was introduced in Linux 3.4. + int clockId = CLOCK_MONOTONIC; + if (TEMP_FAILURE_RETRY(ioctl(fd, EVIOCSCLOCKID, &clockId)) < 0) { + ALOGW("Could not set input clock id to CLOCK_MONOTONIC. errno=%d", errno); + } + + auto node = new EvdevDeviceNode(path, fd); + status_t ret = node->queryProperties(); + if (ret != OK) { + ALOGE("could not open evdev device %s: failed to read properties. errno=%d", + path.c_str(), ret); + delete node; + return nullptr; + } + return node; +} + +status_t EvdevDeviceNode::queryProperties() { + char buffer[80]; + + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGNAME(sizeof(buffer) - 1), buffer)) < 1) { + ALOGV("could not get device name for %s.", mPath.c_str()); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + mName = buffer; + } + + int driverVersion; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGVERSION, &driverVersion))) { + ALOGE("could not get driver version for %s. err=%d", mPath.c_str(), errno); + return -errno; + } + + struct input_id inputId; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGID, &inputId))) { + ALOGE("could not get device input id for %s. err=%d", mPath.c_str(), errno); + return -errno; + } + mBusType = inputId.bustype; + mVendorId = inputId.vendor; + mProductId = inputId.product; + mVersion = inputId.version; + + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGPHYS(sizeof(buffer) - 1), buffer)) < 1) { + ALOGV("could not get location for %s.", mPath.c_str()); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + mLocation = buffer; + } + + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGUNIQ(sizeof(buffer) - 1), buffer)) < 1) { + ALOGV("could not get unique id for %s.", mPath.c_str()); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + mUniqueId = buffer; + } + + ALOGV("add device %s", mPath.c_str()); + ALOGV(" bus: %04x\n" + " vendor: %04x\n" + " product: %04x\n" + " version: %04x\n", + mBusType, mVendorId, mProductId, mVersion); + ALOGV(" name: \"%s\"\n" + " location: \"%s\"\n" + " unique_id: \"%s\"\n" + " descriptor: (TODO)\n" + " driver: v%d.%d.%d", + mName.c_str(), mLocation.c_str(), mUniqueId.c_str(), + driverVersion >> 16, (driverVersion >> 8) & 0xff, (driverVersion >> 16) & 0xff); + + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_KEY, sizeof(mKeyBitmask)), mKeyBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_ABS, sizeof(mAbsBitmask)), mAbsBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_REL, sizeof(mRelBitmask)), mRelBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_SW, sizeof(mSwBitmask)), mSwBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_LED, sizeof(mLedBitmask)), mLedBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_FF, sizeof(mFfBitmask)), mFfBitmask)); + TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGPROP(sizeof(mPropBitmask)), mPropBitmask)); + + queryAxisInfo(); + + return OK; +} + +void EvdevDeviceNode::queryAxisInfo() { + for (int32_t axis = 0; axis < ABS_MAX; ++axis) { + if (testBit(axis, mAbsBitmask)) { + struct input_absinfo info; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGABS(axis), &info))) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", + axis, mPath.c_str(), mFd, errno); + continue; + } + + mAbsInfo[axis] = std::unique_ptr<AbsoluteAxisInfo>(new AbsoluteAxisInfo{ + .minValue = info.minimum, + .maxValue = info.maximum, + .flat = info.flat, + .fuzz = info.fuzz, + .resolution = info.resolution + }); + } + } +} + +bool EvdevDeviceNode::hasKey(int32_t key) const { + if (key >= 0 && key <= KEY_MAX) { + return testBit(key, mKeyBitmask); + } + return false; +} + +bool EvdevDeviceNode::hasRelativeAxis(int axis) const { + if (axis >= 0 && axis <= REL_MAX) { + return testBit(axis, mRelBitmask); + } + return false; +} + +const AbsoluteAxisInfo* EvdevDeviceNode::getAbsoluteAxisInfo(int32_t axis) const { + if (axis < 0 || axis > ABS_MAX) { + return nullptr; + } + + const auto absInfo = mAbsInfo.find(axis); + if (absInfo != mAbsInfo.end()) { + return absInfo->second.get(); + } + return nullptr; +} + +bool EvdevDeviceNode::hasInputProperty(int property) const { + if (property >= 0 && property <= INPUT_PROP_MAX) { + return testBit(property, mPropBitmask); + } + return false; +} + +int32_t EvdevDeviceNode::getKeyState(int32_t key) const { + if (key >= 0 && key <= KEY_MAX) { + if (testBit(key, mKeyBitmask)) { + uint8_t keyState[sizeofBitArray(KEY_CNT)]; + memset(keyState, 0, sizeof(keyState)); + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGKEY(sizeof(keyState)), keyState)) >= 0) { + return testBit(key, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + } + } + return AKEY_STATE_UNKNOWN; +} + +int32_t EvdevDeviceNode::getSwitchState(int32_t sw) const { + if (sw >= 0 && sw <= SW_MAX) { + if (testBit(sw, mSwBitmask)) { + uint8_t swState[sizeofBitArray(SW_CNT)]; + memset(swState, 0, sizeof(swState)); + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGSW(sizeof(swState)), swState)) >= 0) { + return testBit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + } + } + return AKEY_STATE_UNKNOWN; +} + +status_t EvdevDeviceNode::getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const { + *outValue = 0; + + if (axis >= 0 && axis <= ABS_MAX) { + if (testBit(axis, mAbsBitmask)) { + struct input_absinfo info; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGABS(axis), &info))) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", + axis, mPath.c_str(), mFd, errno); + return -errno; + } + + *outValue = info.value; + return OK; + } + } + return -1; +} + +void EvdevDeviceNode::vibrate(nsecs_t duration) { + ff_effect effect{}; + effect.type = FF_RUMBLE; + effect.id = mFfEffectId; + effect.u.rumble.strong_magnitude = 0xc000; + effect.u.rumble.weak_magnitude = 0xc000; + effect.replay.length = (duration + 999'999LL) / 1'000'000LL; + effect.replay.delay = 0; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCSFF, &effect))) { + ALOGW("Could not upload force feedback effect to device %s due to error %d.", + mPath.c_str(), errno); + return; + } + mFfEffectId = effect.id; + + struct input_event ev{}; + ev.type = EV_FF; + ev.code = mFfEffectId; + ev.value = 1; + size_t written = TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(ev))); + if (written != sizeof(ev)) { + ALOGW("Could not start force feedback effect on device %s due to error %d.", + mPath.c_str(), errno); + return; + } + mFfEffectPlaying = true; +} + +void EvdevDeviceNode::cancelVibrate(int32_t deviceId) { + if (mFfEffectPlaying) { + mFfEffectPlaying = false; + + struct input_event ev{}; + ev.type = EV_FF; + ev.code = mFfEffectId; + ev.value = 0; + size_t written = TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(ev))); + if (written != sizeof(ev)) { + ALOGW("Could not stop force feedback effect on device %s due to error %d.", + mPath.c_str(), errno); + return; + } + } +} + +void EvdevDeviceNode::disableDriverKeyRepeat() { + unsigned int repeatRate[] = {0, 0}; + if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCSREP, repeatRate))) { + ALOGW("Unable to disable kernel key repeat for %s due to error %d.", + mPath.c_str(), errno); + } +} + +InputHub::InputHub(std::shared_ptr<InputCallbackInterface> cb) : + mInputCallback(cb) { + // Determine the type of suspend blocking we can do on this device. There + // are 3 options, in decreasing order of preference: + // 1) EPOLLWAKEUP: introduced in Linux kernel 3.5, this flag can be set on + // an epoll event to indicate that a wake lock should be held from the + // time an fd has data until the next epoll_wait (or the epoll fd is + // closed). + // 2) EVIOCSSUSPENDBLOCK: introduced into the Android kernel's evdev + // driver, this ioctl blocks suspend while the event queue for the fd is + // not empty. This was never accepted into the mainline kernel, and it was + // replaced by EPOLLWAKEUP. + // 3) explicit wake locks: use acquire_wake_lock to manage suspend + // blocking explicitly in the InputHub code. + // + // (1) can be checked by simply observing the Linux kernel version. (2) + // requires an fd from an evdev node, which cannot be done in the InputHub + // constructor. So we assume (3) unless (1) is true, and we can verify + // whether (2) is true once we have an evdev fd (and we're not in (1)). + int major, minor; + getLinuxRelease(&major, &minor); + if (major > 3 || (major == 3 && minor >= 5)) { + ALOGI("Using EPOLLWAKEUP to block suspend while processing input events."); + mWakeupMechanism = WakeMechanism::EPOLL_WAKEUP; + mNeedToCheckSuspendBlockIoctl = false; + } + if (manageWakeLocks()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + } + + // epoll_create argument is ignored, but it must be > 0. + mEpollFd = epoll_create(1); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + mINotifyFd = inotify_init(); + LOG_ALWAYS_FATAL_IF(mINotifyFd < 0, "Could not create inotify instance. errno=%d", errno); + + struct epoll_event eventItem; + memset(&eventItem, 0, sizeof(eventItem)); + eventItem.events = EPOLLIN; + if (mWakeupMechanism == WakeMechanism::EPOLL_WAKEUP) { + eventItem.events |= EPOLLWAKEUP; + } + eventItem.data.u32 = mINotifyFd; + int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); + + int wakeFds[2]; + result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeEventFd = eventfd(0, EFD_NONBLOCK); + LOG_ALWAYS_FATAL_IF(mWakeEventFd == -1, "Could not create wake event fd. errno=%d", errno); + + eventItem.data.u32 = mWakeEventFd; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance. errno=%d", errno); +} + +InputHub::~InputHub() { + ::close(mEpollFd); + ::close(mINotifyFd); + ::close(mWakeEventFd); + + if (manageWakeLocks()) { + release_wake_lock(WAKE_LOCK_ID); + } +} + +status_t InputHub::registerDevicePath(const std::string& path) { + ALOGV("registering device path %s", path.c_str()); + int wd = inotify_add_watch(mINotifyFd, path.c_str(), IN_DELETE | IN_CREATE); + if (wd < 0) { + ALOGE("Could not add %s to INotify watch. errno=%d", path.c_str(), errno); + return -errno; + } + mWatchedPaths[wd] = path; + scanDir(path); + return OK; +} + +status_t InputHub::unregisterDevicePath(const std::string& path) { + int wd = -1; + for (auto pair : mWatchedPaths) { + if (pair.second == path) { + wd = pair.first; + break; + } + } + + if (wd == -1) { + return BAD_VALUE; + } + mWatchedPaths.erase(wd); + if (inotify_rm_watch(mINotifyFd, wd) != 0) { + return -errno; + } + return OK; +} + +status_t InputHub::poll() { + bool deviceChange = false; + + if (manageWakeLocks()) { + // Mind the wake lock dance! + // If we're relying on wake locks, we hold a wake lock at all times + // except during epoll_wait(). This works due to some subtle + // choreography. When a device driver has pending (unread) events, it + // acquires a kernel wake lock. However, once the last pending event + // has been read, the device driver will release the kernel wake lock. + // To prevent the system from going to sleep when this happens, the + // InputHub holds onto its own user wake lock while the client is + // processing events. Thus the system can only sleep if there are no + // events pending or currently being processed. + release_wake_lock(WAKE_LOCK_ID); + } + + struct epoll_event pendingEventItems[EPOLL_MAX_EVENTS]; + int pollResult = epoll_wait(mEpollFd, pendingEventItems, EPOLL_MAX_EVENTS, NO_TIMEOUT); + + if (manageWakeLocks()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + } + + if (pollResult == 0) { + ALOGW("epoll_wait should not return 0 with no timeout"); + return UNKNOWN_ERROR; + } + if (pollResult < 0) { + // An error occurred. Return even if it's EINTR, and let the caller + // restart the poll. + ALOGE("epoll_wait returned with errno=%d", errno); + return -errno; + } + + // pollResult > 0: there are events to process + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + std::vector<int> removedDeviceFds; + int inputFd = -1; + std::shared_ptr<InputDeviceNode> deviceNode; + for (int i = 0; i < pollResult; ++i) { + const struct epoll_event& eventItem = pendingEventItems[i]; + + int dataFd = static_cast<int>(eventItem.data.u32); + if (dataFd == mINotifyFd) { + if (eventItem.events & EPOLLIN) { + deviceChange = true; + } else { + ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); + } + continue; + } + + if (dataFd == mWakeEventFd) { + if (eventItem.events & EPOLLIN) { + ALOGV("awoken after wake()"); + uint64_t u; + ssize_t nRead = TEMP_FAILURE_RETRY(read(mWakeEventFd, &u, sizeof(uint64_t))); + if (nRead != sizeof(uint64_t)) { + ALOGW("Could not read event fd; waking anyway."); + } + } else { + ALOGW("Received unexpected epoll event 0x%08x for wake event.", + eventItem.events); + } + continue; + } + + // Update the fd and device node when the fd changes. When several + // events are read back-to-back with the same fd, this saves many reads + // from the hash table. + if (inputFd != dataFd) { + inputFd = dataFd; + deviceNode = mDeviceNodes[inputFd]; + } + if (deviceNode == nullptr) { + ALOGE("could not find device node for fd %d", inputFd); + continue; + } + if (eventItem.events & EPOLLIN) { + struct input_event ievs[INPUT_MAX_EVENTS]; + for (;;) { + ssize_t readSize = TEMP_FAILURE_RETRY(read(inputFd, ievs, sizeof(ievs))); + if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { + ALOGW("could not get event, removed? (fd: %d, size: %d errno: %d)", + inputFd, readSize, errno); + + removedDeviceFds.push_back(inputFd); + break; + } else if (readSize < 0) { + if (errno != EAGAIN && errno != EINTR) { + ALOGW("could not get event. errno=%d", errno); + } + break; + } else if (readSize % sizeof(input_event) != 0) { + ALOGE("could not get event. wrong size=%d", readSize); + break; + } else { + size_t count = static_cast<size_t>(readSize) / sizeof(struct input_event); + for (size_t i = 0; i < count; ++i) { + auto& iev = ievs[i]; + auto when = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + InputEvent inputEvent = { when, iev.type, iev.code, iev.value }; + mInputCallback->onInputEvent(deviceNode, inputEvent, now); + } + } + } + } else if (eventItem.events & EPOLLHUP) { + ALOGI("Removing device fd %d due to epoll hangup event.", inputFd); + removedDeviceFds.push_back(inputFd); + } else { + ALOGW("Received unexpected epoll event 0x%08x for device fd %d", + eventItem.events, inputFd); + } + } + + if (removedDeviceFds.size()) { + for (auto deviceFd : removedDeviceFds) { + auto deviceNode = mDeviceNodes[deviceFd]; + if (deviceNode != nullptr) { + status_t ret = closeNodeByFd(deviceFd); + if (ret != OK) { + ALOGW("Could not close device with fd %d. errno=%d", deviceFd, ret); + } else { + mInputCallback->onDeviceRemoved(deviceNode); + } + } + } + } + + if (deviceChange) { + readNotify(); + } + + return OK; +} + +status_t InputHub::wake() { + ALOGV("wake() called"); + + uint64_t u = 1; + ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &u, sizeof(uint64_t))); + + if (nWrite != sizeof(uint64_t) && errno != EAGAIN) { + ALOGW("Could not write wake signal, errno=%d", errno); + return -errno; + } + return OK; +} + +void InputHub::dump(String8& dump) { + // TODO +} + +status_t InputHub::readNotify() { + char event_buf[512]; + struct inotify_event* event; + + ssize_t res = TEMP_FAILURE_RETRY(read(mINotifyFd, event_buf, sizeof(event_buf))); + if (res < static_cast<int>(sizeof(*event))) { + ALOGW("could not get inotify event, %s\n", strerror(errno)); + return -errno; + } + + size_t event_pos = 0; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (res >= static_cast<int>(sizeof(*event))) { + event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos); + if (event->len) { + std::string path = mWatchedPaths[event->wd]; + path.append("/").append(event->name); + ALOGV("inotify event for path %s", path.c_str()); + + if (event->mask & IN_CREATE) { + std::shared_ptr<InputDeviceNode> deviceNode; + status_t res = openNode(path, &deviceNode); + if (res != OK) { + ALOGE("could not open device node %s. err=%d", path.c_str(), res); + } else { + mInputCallback->onDeviceAdded(deviceNode); + } + } else { + auto deviceNode = findNodeByPath(path); + if (deviceNode != nullptr) { + status_t ret = closeNode(deviceNode); + if (ret != OK) { + ALOGW("Could not close device %s. errno=%d", path.c_str(), ret); + } else { + mInputCallback->onDeviceRemoved(deviceNode); + } + } else { + ALOGW("could not find device node for %s", path.c_str()); + } + } + } + int event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + + return OK; +} + +status_t InputHub::scanDir(const std::string& path) { + auto dir = ::opendir(path.c_str()); + if (dir == nullptr) { + ALOGE("could not open device path %s to scan for devices. err=%d", path.c_str(), errno); + return -errno; + } + + while (auto dirent = readdir(dir)) { + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) { + continue; + } + std::string filename = path + "/" + dirent->d_name; + std::shared_ptr<InputDeviceNode> node; + if (openNode(filename, &node) != OK) { + ALOGE("could not open device node %s", filename.c_str()); + } else { + mInputCallback->onDeviceAdded(node); + } + } + ::closedir(dir); + return OK; +} + +status_t InputHub::openNode(const std::string& path, + std::shared_ptr<InputDeviceNode>* outNode) { + ALOGV("opening %s...", path.c_str()); + auto evdevNode = std::shared_ptr<EvdevDeviceNode>(EvdevDeviceNode::openDeviceNode(path)); + if (evdevNode == nullptr) { + return UNKNOWN_ERROR; + } + + auto fd = evdevNode->getFd(); + ALOGV("opened %s with fd %d", path.c_str(), fd); + *outNode = std::static_pointer_cast<InputDeviceNode>(evdevNode); + mDeviceNodes[fd] = *outNode; + struct epoll_event eventItem{}; + eventItem.events = EPOLLIN; + if (mWakeupMechanism == WakeMechanism::EPOLL_WAKEUP) { + eventItem.events |= EPOLLWAKEUP; + } + eventItem.data.u32 = fd; + if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { + ALOGE("Could not add device fd to epoll instance. errno=%d", errno); + return -errno; + } + + if (mNeedToCheckSuspendBlockIoctl) { +#ifndef EVIOCSSUSPENDBLOCK + // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels + // will use an epoll flag instead, so as long as we want to support this + // feature, we need to be prepared to define the ioctl ourselves. +#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) +#endif + if (TEMP_FAILURE_RETRY(ioctl(fd, EVIOCSSUSPENDBLOCK, 1))) { + // no wake mechanism, continue using explicit wake locks + ALOGI("Using explicit wakelocks to block suspend while processing input events."); + } else { + mWakeupMechanism = WakeMechanism::LEGACY_EVDEV_SUSPENDBLOCK_IOCTL; + // release any held wakelocks since we won't need them anymore + release_wake_lock(WAKE_LOCK_ID); + ALOGI("Using EVIOCSSUSPENDBLOCK to block suspend while processing input events."); + } + mNeedToCheckSuspendBlockIoctl = false; + } + + return OK; +} + +status_t InputHub::closeNode(const std::shared_ptr<InputDeviceNode>& node) { + for (auto pair : mDeviceNodes) { + if (pair.second.get() == node.get()) { + return closeNodeByFd(pair.first); + } + } + return BAD_VALUE; +} + +status_t InputHub::closeNodeByFd(int fd) { + status_t ret = OK; + if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL)) { + ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); + ret = -errno; + } + mDeviceNodes.erase(fd); + ::close(fd); + return ret; +} + +std::shared_ptr<InputDeviceNode> InputHub::findNodeByPath(const std::string& path) { + for (auto pair : mDeviceNodes) { + if (pair.second->getPath() == path) return pair.second; + } + return nullptr; +} + +bool InputHub::manageWakeLocks() const { + return mWakeupMechanism != WakeMechanism::EPOLL_WAKEUP; +} + +} // namespace android diff --git a/modules/input/evdev/InputHub.h b/modules/input/evdev/InputHub.h new file mode 100644 index 00000000..bec327ac --- /dev/null +++ b/modules/input/evdev/InputHub.h @@ -0,0 +1,204 @@ +/* + * 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_INPUT_HUB_H_ +#define ANDROID_INPUT_HUB_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include <utils/String8.h> +#include <utils/Timers.h> + +namespace android { + +/** + * InputEvent represents an event from the kernel. The fields largely mirror + * those found in linux/input.h. + */ +struct InputEvent { + nsecs_t when; + + int32_t type; + int32_t code; + int32_t value; +}; + +/** Describes an absolute axis. */ +struct AbsoluteAxisInfo { + int32_t minValue = 0; // minimum value + int32_t maxValue = 0; // maximum value + int32_t flat = 0; // center flat position, e.g. flat == 8 means center is between -8 and 8 + int32_t fuzz = 0; // error tolerance, e.g. fuzz == 4 means value is +/- 4 due to noise + int32_t resolution = 0; // resolution in units per mm or radians per mm +}; + +/** + * An InputDeviceNode represents a device node in the Linux system. It can be + * used to interact with the device, setting and getting property values. + * + * An InputDeviceNode should only be used on the same thread that is polling for + * input events. + */ +class InputDeviceNode { +public: + virtual const std::string& getPath() const = 0; + + virtual const std::string& getName() const = 0; + virtual const std::string& getLocation() const = 0; + virtual const std::string& getUniqueId() const = 0; + + virtual uint16_t getBusType() const = 0; + virtual uint16_t getVendorId() const = 0; + virtual uint16_t getProductId() const = 0; + virtual uint16_t getVersion() const = 0; + + virtual bool hasKey(int32_t key) const = 0; + virtual bool hasRelativeAxis(int axis) const = 0; + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const = 0; + virtual bool hasInputProperty(int property) const = 0; + + virtual int32_t getKeyState(int32_t key) const = 0; + virtual int32_t getSwitchState(int32_t sw) const = 0; + virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const = 0; + + virtual void vibrate(nsecs_t duration) = 0; + virtual void cancelVibrate(int32_t deviceId) = 0; + + virtual void disableDriverKeyRepeat() = 0; + +protected: + InputDeviceNode() = default; + virtual ~InputDeviceNode() = default; +}; + +/** Callback interface for receiving input events, including device changes. */ +class InputCallbackInterface { +public: + virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event, + nsecs_t event_time) = 0; + virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) = 0; + virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) = 0; + +protected: + InputCallbackInterface() = default; + virtual ~InputCallbackInterface() = default; +}; + +/** + * InputHubInterface is responsible for monitoring a set of device paths and + * executing callbacks when events occur. Before calling poll(), you should set + * the device and input callbacks, and register your device path(s). + */ +class InputHubInterface { +public: + virtual status_t registerDevicePath(const std::string& path) = 0; + virtual status_t unregisterDevicePath(const std::string& path) = 0; + + virtual status_t poll() = 0; + virtual status_t wake() = 0; + + virtual void dump(String8& dump) = 0; + +protected: + InputHubInterface() = default; + virtual ~InputHubInterface() = default; +}; + +/** + * An implementation of InputHubInterface that uses epoll to wait for events. + * + * This class is not threadsafe. Any functions called on the InputHub should be + * called on the same thread that is used to call poll(). The only exception is + * wake(), which may be used to return from poll() before an input or device + * event occurs. + */ +class InputHub : public InputHubInterface { +public: + explicit InputHub(std::shared_ptr<InputCallbackInterface> cb); + virtual ~InputHub() override; + + virtual status_t registerDevicePath(const std::string& path) override; + virtual status_t unregisterDevicePath(const std::string& path) override; + + virtual status_t poll() override; + virtual status_t wake() override; + + virtual void dump(String8& dump) override; + +private: + status_t readNotify(); + status_t scanDir(const std::string& path); + status_t openNode(const std::string& path, std::shared_ptr<InputDeviceNode>* outNode); + status_t closeNode(const std::shared_ptr<InputDeviceNode>& node); + status_t closeNodeByFd(int fd); + std::shared_ptr<InputDeviceNode> findNodeByPath(const std::string& path); + + enum class WakeMechanism { + /** + * The kernel supports the EPOLLWAKEUP flag for epoll_ctl. + * + * When using this mechanism, epoll_wait will internally acquire a wake + * lock whenever one of the FDs it is monitoring becomes ready. The wake + * lock is held automatically by the kernel until the next call to + * epoll_wait. + * + * This mechanism only exists in Linux kernel 3.5+. + */ + EPOLL_WAKEUP, + /** + * The kernel evdev driver supports the EVIOCSSUSPENDBLOCK ioctl. + * + * When using this mechanism, the InputHub asks evdev to acquire and + * hold a wake lock whenever its buffer is non-empty. We must take care + * to acquire our own userspace wake lock before draining the buffer to + * prevent actually going back into suspend before we have fully + * processed all of the events. + * + * This mechanism only exists in older Android Linux kernels. + */ + LEGACY_EVDEV_SUSPENDBLOCK_IOCTL, + /** + * The kernel doesn't seem to support any special wake mechanism. + * + * We explicitly acquire and release wake locks when processing input + * events. + */ + LEGACY_EVDEV_EXPLICIT_WAKE_LOCKS, + }; + WakeMechanism mWakeupMechanism = WakeMechanism::LEGACY_EVDEV_EXPLICIT_WAKE_LOCKS; + bool manageWakeLocks() const; + bool mNeedToCheckSuspendBlockIoctl = true; + + int mEpollFd; + int mINotifyFd; + int mWakeEventFd; + int mWakeReadPipeFd; + int mWakeWritePipeFd; + + // Callback for input events + std::shared_ptr<InputCallbackInterface> mInputCallback; + + // Map from watch descriptors to watched paths + std::unordered_map<int, std::string> mWatchedPaths; + // Map from file descriptors to InputDeviceNodes + std::unordered_map<int, std::shared_ptr<InputDeviceNode>> mDeviceNodes; +}; + +} // namespace android + +#endif // ANDROID_INPUT_HUB_H_ diff --git a/modules/radio/Android.mk b/modules/radio/Android.mk new file mode 100644 index 00000000..f433c853 --- /dev/null +++ b/modules/radio/Android.mk @@ -0,0 +1,27 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +# Stub radio HAL module, used for tests +include $(CLEAR_VARS) + +LOCAL_MODULE := radio.fm.default +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SRC_FILES := radio_hw.c +LOCAL_SHARED_LIBRARIES := liblog libcutils libradio_metadata +LOCAL_MODULE_TAGS := optional +LOCAL_32_BIT_ONLY := true + +include $(BUILD_SHARED_LIBRARY) diff --git a/modules/radio/radio_hw.c b/modules/radio/radio_hw.c new file mode 100644 index 00000000..9c0f22c2 --- /dev/null +++ b/modules/radio/radio_hw.c @@ -0,0 +1,744 @@ +/* + * 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 "radio_hw_stub" +#define LOG_NDEBUG 0 + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include <sys/prctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <cutils/log.h> +#include <cutils/list.h> +#include <system/radio.h> +#include <system/radio_metadata.h> +#include <hardware/hardware.h> +#include <hardware/radio.h> + +static const radio_hal_properties_t hw_properties = { + .class_id = RADIO_CLASS_AM_FM, + .implementor = "The Android Open Source Project", + .product = "Radio stub HAL", + .version = "0.1", + .serial = "0123456789", + .num_tuners = 1, + .num_audio_sources = 1, + .supports_capture = false, + .num_bands = 2, + .bands = { + { + .type = RADIO_BAND_FM, + .antenna_connected = false, + .lower_limit = 87900, + .upper_limit = 107900, + .num_spacings = 1, + .spacings = { 200 }, + .fm = { + .deemphasis = RADIO_DEEMPHASIS_75, + .stereo = true, + .rds = RADIO_RDS_US, + .ta = false, + .af = false, + } + }, + { + .type = RADIO_BAND_AM, + .antenna_connected = true, + .lower_limit = 540, + .upper_limit = 1610, + .num_spacings = 1, + .spacings = { 10 }, + .am = { + .stereo = true, + } + } + } +}; + +struct stub_radio_tuner { + struct radio_tuner interface; + struct stub_radio_device *dev; + radio_callback_t callback; + void *cookie; + radio_hal_band_config_t config; + radio_program_info_t program; + bool audio; + pthread_t callback_thread; + pthread_mutex_t lock; + pthread_cond_t cond; + struct listnode command_list; +}; + +struct stub_radio_device { + struct radio_hw_device device; + struct stub_radio_tuner *tuner; + pthread_mutex_t lock; +}; + + +typedef enum { + CMD_EXIT, + CMD_CONFIG, + CMD_STEP, + CMD_SCAN, + CMD_TUNE, + CMD_CANCEL, + CMD_METADATA, +} thread_cmd_type_t; + +struct thread_command { + struct listnode node; + thread_cmd_type_t type; + struct timespec ts; + union { + unsigned int param; + radio_hal_band_config_t config; + }; +}; + +/* must be called with out->lock locked */ +static int send_command_l(struct stub_radio_tuner *tuner, + thread_cmd_type_t type, + unsigned int delay_ms, + void *param) +{ + struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command)); + struct timespec ts; + + if (cmd == NULL) + return -ENOMEM; + + ALOGV("%s %d delay_ms %d", __func__, type, delay_ms); + + cmd->type = type; + if (param != NULL) { + if (cmd->type == CMD_CONFIG) { + cmd->config = *(radio_hal_band_config_t *)param; + ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type); + } else + cmd->param = *(unsigned int *)param; + } + + clock_gettime(CLOCK_REALTIME, &ts); + + ts.tv_sec += delay_ms/1000; + ts.tv_nsec += (delay_ms%1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + cmd->ts = ts; + list_add_tail(&tuner->command_list, &cmd->node); + pthread_cond_signal(&tuner->cond); + return 0; +} + +#define BITMAP_FILE_PATH "/data/misc/media/android.png" + +static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key, + const char *source) +{ + int fd; + ssize_t ret = 0; + struct stat info; + void *data = NULL; + size_t size; + + fd = open(source, O_RDONLY); + if (fd < 0) + return -EPIPE; + + fstat(fd, &info); + size = info.st_size; + data = malloc(size); + if (data == NULL) { + ret = -ENOMEM; + goto exit; + } + ret = read(fd, data, size); + if (ret < 0) + goto exit; + ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size); + +exit: + close(fd); + free(data); + ALOGE_IF(ret != 0, "%s error %d", __func__, ret); + return (int)ret; +} + +static int prepare_metadata(struct stub_radio_tuner *tuner, + radio_metadata_t **metadata, bool program) +{ + int ret = 0; + char text[RADIO_STRING_LEN_MAX]; + struct timespec ts; + + if (metadata == NULL) + return -EINVAL; + + if (*metadata != NULL) + radio_metadata_deallocate(*metadata); + + *metadata = NULL; + + ret = radio_metadata_allocate(metadata, tuner->program.channel, 0); + if (ret != 0) + return ret; + + if (program) { + ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5); + if (ret != 0) + goto exit; + ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand"); + if (ret != 0) + goto exit; + ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH); + if (ret != 0) + goto exit; + } else { + ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH); + if (ret != 0) + goto exit; + } + + clock_gettime(CLOCK_REALTIME, &ts); + snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10); + ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text); + if (ret != 0) + goto exit; + + snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10); + ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text); + if (ret != 0) + goto exit; + + return 0; + +exit: + radio_metadata_deallocate(*metadata); + *metadata = NULL; + return ret; +} + +static void *callback_thread_loop(void *context) +{ + struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context; + struct timespec ts = {0, 0}; + + ALOGI("%s", __func__); + + prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0); + + pthread_mutex_lock(&tuner->lock); + + while (true) { + struct thread_command *cmd = NULL; + struct listnode *item; + struct listnode *tmp; + struct timespec cur_ts; + bool got_cancel = false; + bool send_meta_data = false; + + if (list_empty(&tuner->command_list) || ts.tv_sec != 0) { + ALOGV("%s SLEEPING", __func__); + if (ts.tv_sec != 0) { + ALOGV("%s SLEEPING with timeout", __func__); + pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts); + } else { + ALOGV("%s SLEEPING forever", __func__); + pthread_cond_wait(&tuner->cond, &tuner->lock); + } + ts.tv_sec = 0; + ALOGV("%s RUNNING", __func__); + } + + clock_gettime(CLOCK_REALTIME, &cur_ts); + + list_for_each_safe(item, tmp, &tuner->command_list) { + cmd = node_to_item(item, struct thread_command, node); + + if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN || + cmd->type == CMD_TUNE || cmd->type == CMD_METADATA)) { + list_remove(item); + free(cmd); + continue; + } + + if ((cmd->ts.tv_sec < cur_ts.tv_sec) || + ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) { + radio_hal_event_t event; + radio_metadata_t *metadata = NULL; + + event.type = RADIO_EVENT_HW_FAILURE; + list_remove(item); + + ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec, + cmd->ts.tv_nsec); + + switch (cmd->type) { + default: + case CMD_EXIT: + free(cmd); + goto exit; + + case CMD_CONFIG: { + tuner->config = cmd->config; + event.type = RADIO_EVENT_CONFIG; + event.config = tuner->config; + ALOGV("%s CMD_CONFIG type %d low %d up %d", + __func__, tuner->config.type, + tuner->config.lower_limit, tuner->config.upper_limit); + if (tuner->config.type == RADIO_BAND_FM) { + ALOGV(" - stereo %d\n - rds %d\n - ta %d\n - af %d", + tuner->config.fm.stereo, tuner->config.fm.rds, + tuner->config.fm.ta, tuner->config.fm.af); + } else { + ALOGV(" - stereo %d", tuner->config.am.stereo); + } + } break; + + case CMD_STEP: { + int frequency; + frequency = tuner->program.channel; + if (cmd->param == RADIO_DIRECTION_UP) { + frequency += tuner->config.spacings[0]; + } else { + frequency -= tuner->config.spacings[0]; + } + if (frequency > (int)tuner->config.upper_limit) { + frequency = tuner->config.lower_limit; + } + if (frequency < (int)tuner->config.lower_limit) { + frequency = tuner->config.upper_limit; + } + tuner->program.channel = frequency; + tuner->program.tuned = (frequency / (tuner->config.spacings[0] * 5)) % 2; + tuner->program.signal_strength = 20; + if (tuner->config.type == RADIO_BAND_FM) + tuner->program.stereo = false; + else + tuner->program.stereo = false; + + event.type = RADIO_EVENT_TUNED; + event.info = tuner->program; + } break; + + case CMD_SCAN: { + int frequency; + frequency = tuner->program.channel; + if (cmd->param == RADIO_DIRECTION_UP) { + frequency += tuner->config.spacings[0] * 25; + } else { + frequency -= tuner->config.spacings[0] * 25; + } + if (frequency > (int)tuner->config.upper_limit) { + frequency = tuner->config.lower_limit; + } + if (frequency < (int)tuner->config.lower_limit) { + frequency = tuner->config.upper_limit; + } + tuner->program.channel = (unsigned int)frequency; + tuner->program.tuned = true; + if (tuner->config.type == RADIO_BAND_FM) + tuner->program.stereo = tuner->config.fm.stereo; + else + tuner->program.stereo = tuner->config.am.stereo; + tuner->program.signal_strength = 50; + + event.type = RADIO_EVENT_TUNED; + event.info = tuner->program; + send_meta_data = true; + } break; + + case CMD_TUNE: { + tuner->program.channel = cmd->param; + tuner->program.tuned = (tuner->program.channel / + (tuner->config.spacings[0] * 5)) % 2; + + if (tuner->program.tuned) { + prepare_metadata(tuner, &tuner->program.metadata, true); + send_command_l(tuner, CMD_METADATA, 5000, NULL); + } else { + if (tuner->program.metadata != NULL) + radio_metadata_deallocate(tuner->program.metadata); + tuner->program.metadata = NULL; + } + tuner->program.signal_strength = 100; + if (tuner->config.type == RADIO_BAND_FM) + tuner->program.stereo = + tuner->program.tuned ? tuner->config.fm.stereo : false; + else + tuner->program.stereo = + tuner->program.tuned ? tuner->config.am.stereo : false; + event.type = RADIO_EVENT_TUNED; + event.info = tuner->program; + send_meta_data = true; + } break; + + case CMD_METADATA: { + int ret = prepare_metadata(tuner, &metadata, false); + if (ret == 0) { + event.type = RADIO_EVENT_METADATA; + event.metadata = metadata; + } + send_meta_data = true; + } break; + + case CMD_CANCEL: { + got_cancel = true; + } break; + + } + if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) { + pthread_mutex_unlock(&tuner->lock); + tuner->callback(&event, tuner->cookie); + pthread_mutex_lock(&tuner->lock); + if (event.type == RADIO_EVENT_METADATA && metadata != NULL) { + radio_metadata_deallocate(metadata); + metadata = NULL; + } + } + ALOGV("%s processed command %d", __func__, cmd->type); + free(cmd); + } else { + if ((ts.tv_sec == 0) || + (cmd->ts.tv_sec < ts.tv_sec) || + ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) { + ts.tv_sec = cmd->ts.tv_sec; + ts.tv_nsec = cmd->ts.tv_nsec; + } + } + } + + if (send_meta_data) { + list_for_each_safe(item, tmp, &tuner->command_list) { + cmd = node_to_item(item, struct thread_command, node); + if (cmd->type == CMD_METADATA) { + list_remove(item); + free(cmd); + } + } + send_command_l(tuner, CMD_METADATA, 100, NULL); + } + } + +exit: + pthread_mutex_unlock(&tuner->lock); + + ALOGV("%s Exiting", __func__); + + return NULL; +} + + +static int tuner_set_configuration(const struct radio_tuner *tuner, + const radio_hal_band_config_t *config) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + int status = 0; + + ALOGI("%s stub_tuner %p", __func__, stub_tuner); + pthread_mutex_lock(&stub_tuner->lock); + if (config == NULL) { + status = -EINVAL; + goto exit; + } + send_command_l(stub_tuner, CMD_CANCEL, 0, NULL); + send_command_l(stub_tuner, CMD_CONFIG, 500, (void *)config); + +exit: + pthread_mutex_unlock(&stub_tuner->lock); + return status; +} + +static int tuner_get_configuration(const struct radio_tuner *tuner, + radio_hal_band_config_t *config) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + int status = 0; + struct listnode *item; + radio_hal_band_config_t *src_config; + + ALOGI("%s stub_tuner %p", __func__, stub_tuner); + pthread_mutex_lock(&stub_tuner->lock); + src_config = &stub_tuner->config; + + if (config == NULL) { + status = -EINVAL; + goto exit; + } + list_for_each(item, &stub_tuner->command_list) { + struct thread_command *cmd = node_to_item(item, struct thread_command, node); + if (cmd->type == CMD_CONFIG) { + src_config = &cmd->config; + } + } + *config = *src_config; + +exit: + pthread_mutex_unlock(&stub_tuner->lock); + return status; +} + +static int tuner_step(const struct radio_tuner *tuner, + radio_direction_t direction, bool skip_sub_channel) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + + ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d", + __func__, stub_tuner, direction, skip_sub_channel); + + pthread_mutex_lock(&stub_tuner->lock); + send_command_l(stub_tuner, CMD_STEP, 20, &direction); + pthread_mutex_unlock(&stub_tuner->lock); + return 0; +} + +static int tuner_scan(const struct radio_tuner *tuner, + radio_direction_t direction, bool skip_sub_channel) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + + ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d", + __func__, stub_tuner, direction, skip_sub_channel); + + pthread_mutex_lock(&stub_tuner->lock); + send_command_l(stub_tuner, CMD_SCAN, 200, &direction); + pthread_mutex_unlock(&stub_tuner->lock); + return 0; +} + +static int tuner_tune(const struct radio_tuner *tuner, + unsigned int channel, unsigned int sub_channel) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + + ALOGI("%s stub_tuner %p channel %d, sub_channel %d", + __func__, stub_tuner, channel, sub_channel); + + pthread_mutex_lock(&stub_tuner->lock); + if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) { + pthread_mutex_unlock(&stub_tuner->lock); + ALOGI("%s channel out of range", __func__); + return -EINVAL; + } + send_command_l(stub_tuner, CMD_TUNE, 100, &channel); + pthread_mutex_unlock(&stub_tuner->lock); + return 0; +} + +static int tuner_cancel(const struct radio_tuner *tuner) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + + ALOGI("%s stub_tuner %p", __func__, stub_tuner); + + pthread_mutex_lock(&stub_tuner->lock); + send_command_l(stub_tuner, CMD_CANCEL, 0, NULL); + pthread_mutex_unlock(&stub_tuner->lock); + return 0; +} + +static int tuner_get_program_information(const struct radio_tuner *tuner, + radio_program_info_t *info) +{ + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + int status = 0; + radio_metadata_t *metadata; + + ALOGI("%s stub_tuner %p", __func__, stub_tuner); + pthread_mutex_lock(&stub_tuner->lock); + if (info == NULL) { + status = -EINVAL; + goto exit; + } + metadata = info->metadata; + *info = stub_tuner->program; + info->metadata = metadata; + if (metadata != NULL && stub_tuner->program.metadata != NULL) + radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata); + +exit: + pthread_mutex_unlock(&stub_tuner->lock); + return status; +} + +static int rdev_get_properties(const struct radio_hw_device *dev, + radio_hal_properties_t *properties) +{ + struct stub_radio_device *rdev = (struct stub_radio_device *)dev; + + ALOGI("%s", __func__); + if (properties == NULL) + return -EINVAL; + memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t)); + return 0; +} + +static int rdev_open_tuner(const struct radio_hw_device *dev, + const radio_hal_band_config_t *config, + bool audio, + radio_callback_t callback, + void *cookie, + const struct radio_tuner **tuner) +{ + struct stub_radio_device *rdev = (struct stub_radio_device *)dev; + int status = 0; + + ALOGI("%s rdev %p", __func__, rdev); + pthread_mutex_lock(&rdev->lock); + + if (rdev->tuner != NULL) { + status = -ENOSYS; + goto exit; + } + + if (config == NULL || callback == NULL || tuner == NULL) { + status = -EINVAL; + goto exit; + } + + rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner)); + if (rdev->tuner == NULL) { + status = -ENOMEM; + goto exit; + } + + rdev->tuner->interface.set_configuration = tuner_set_configuration; + rdev->tuner->interface.get_configuration = tuner_get_configuration; + rdev->tuner->interface.scan = tuner_scan; + rdev->tuner->interface.step = tuner_step; + rdev->tuner->interface.tune = tuner_tune; + rdev->tuner->interface.cancel = tuner_cancel; + rdev->tuner->interface.get_program_information = tuner_get_program_information; + + rdev->tuner->audio = audio; + rdev->tuner->callback = callback; + rdev->tuner->cookie = cookie; + + rdev->tuner->dev = rdev; + + pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL); + pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL); + pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL, + callback_thread_loop, rdev->tuner); + list_init(&rdev->tuner->command_list); + + pthread_mutex_lock(&rdev->tuner->lock); + send_command_l(rdev->tuner, CMD_CONFIG, 500, (void *)config); + pthread_mutex_unlock(&rdev->tuner->lock); + + *tuner = &rdev->tuner->interface; + +exit: + pthread_mutex_unlock(&rdev->lock); + ALOGI("%s DONE", __func__); + return status; +} + +static int rdev_close_tuner(const struct radio_hw_device *dev, + const struct radio_tuner *tuner) +{ + struct stub_radio_device *rdev = (struct stub_radio_device *)dev; + struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; + int status = 0; + + ALOGI("%s tuner %p", __func__, tuner); + pthread_mutex_lock(&rdev->lock); + + if (tuner == NULL) { + status = -EINVAL; + goto exit; + } + + pthread_mutex_lock(&stub_tuner->lock); + stub_tuner->callback = NULL; + send_command_l(stub_tuner, CMD_EXIT, 0, NULL); + pthread_mutex_unlock(&stub_tuner->lock); + pthread_join(stub_tuner->callback_thread, (void **) NULL); + + if (stub_tuner->program.metadata != NULL) + radio_metadata_deallocate(stub_tuner->program.metadata); + + free(stub_tuner); + rdev->tuner = NULL; + +exit: + pthread_mutex_unlock(&rdev->lock); + return status; +} + +static int rdev_close(hw_device_t *device) +{ + struct stub_radio_device *rdev = (struct stub_radio_device *)device; + if (rdev != NULL) { + free(rdev->tuner); + } + free(rdev); + return 0; +} + +static int rdev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct stub_radio_device *rdev; + int ret; + + if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0) + return -EINVAL; + + rdev = calloc(1, sizeof(struct stub_radio_device)); + if (!rdev) + return -ENOMEM; + + rdev->device.common.tag = HARDWARE_DEVICE_TAG; + rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0; + rdev->device.common.module = (struct hw_module_t *) module; + rdev->device.common.close = rdev_close; + rdev->device.get_properties = rdev_get_properties; + rdev->device.open_tuner = rdev_open_tuner; + rdev->device.close_tuner = rdev_close_tuner; + + pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL); + + *device = &rdev->device.common; + + return 0; +} + + +static struct hw_module_methods_t hal_module_methods = { + .open = rdev_open, +}; + +struct radio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = RADIO_MODULE_API_VERSION_1_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = RADIO_HARDWARE_MODULE_ID, + .name = "Stub radio HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; diff --git a/modules/usbaudio/Android.mk b/modules/usbaudio/Android.mk index ec8a8c0c..9df1e792 100644 --- a/modules/usbaudio/Android.mk +++ b/modules/usbaudio/Android.mk @@ -19,15 +19,12 @@ include $(CLEAR_VARS) LOCAL_MODULE := audio.usb.default LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_SRC_FILES := \ - audio_hw.c \ - alsa_device_profile.c \ - alsa_device_proxy.c \ - logging.c \ - format.c + audio_hal.c LOCAL_C_INCLUDES += \ external/tinyalsa/include \ - $(call include-path-for, audio-utils) -LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils + $(call include-path-for, audio-utils) \ + $(call include-path-for, alsa-utils) +LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libalsautils LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := -Wno-unused-parameter diff --git a/modules/usbaudio/alsa_device_profile.c b/modules/usbaudio/alsa_device_profile.c deleted file mode 100644 index 8e844713..00000000 --- a/modules/usbaudio/alsa_device_profile.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (C) 2014 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 "alsa_device_profile" -/*#define LOG_NDEBUG 0*/ -/*#define LOG_PCM_PARAMS 0*/ - -#include <errno.h> -#include <inttypes.h> -#include <stdint.h> -#include <stdlib.h> - -#include <log/log.h> - -#include "alsa_device_profile.h" -#include "format.h" -#include "logging.h" - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/*TODO - Evaluate if this value should/can be retrieved from a device-specific property */ -#define BUFF_DURATION_MS 5 - -#define DEFAULT_PERIOD_SIZE 1024 - -static const char * const format_string_map[] = { - "AUDIO_FORMAT_PCM_16_BIT", /* "PCM_FORMAT_S16_LE", */ - "AUDIO_FORMAT_PCM_32_BIT", /* "PCM_FORMAT_S32_LE", */ - "AUDIO_FORMAT_PCM_8_BIT", /* "PCM_FORMAT_S8", */ - "AUDIO_FORMAT_PCM_8_24_BIT", /* "PCM_FORMAT_S24_LE", */ - "AUDIO_FORMAT_PCM_24_BIT_PACKED"/* "PCM_FORMAT_S24_3LE" */ -}; - -static const unsigned const format_byte_size_map[] = { - 2, /* PCM_FORMAT_S16_LE */ - 4, /* PCM_FORMAT_S32_LE */ - 1, /* PCM_FORMAT_S8 */ - 4, /* PCM_FORMAT_S24_LE */ - 3, /* PCM_FORMAT_S24_3LE */ -}; - -extern int8_t const pcm_format_value_map[50]; - -/* sort these highest -> lowest (to default to best quality) */ -static const unsigned std_sample_rates[] = - {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; - -static void profile_reset(alsa_device_profile* profile) -{ - profile->card = profile->device = -1; - - /* Fill the attribute arrays with invalid values */ - size_t index; - for (index = 0; index < ARRAY_SIZE(profile->formats); index++) { - profile->formats[index] = PCM_FORMAT_INVALID; - } - - for (index = 0; index < ARRAY_SIZE(profile->sample_rates); index++) { - profile->sample_rates[index] = 0; - } - - for (index = 0; index < ARRAY_SIZE(profile->channel_counts); index++) { - profile->channel_counts[index] = 0; - } - - profile->min_period_size = profile->max_period_size = 0; - profile->min_channel_count = profile->max_channel_count = DEFAULT_CHANNEL_COUNT; - - profile->is_valid = false; -} - -void profile_init(alsa_device_profile* profile, int direction) -{ - profile->direction = direction; - profile_reset(profile); -} - -bool profile_is_initialized(alsa_device_profile* profile) -{ - return profile->card >= 0 && profile->device >= 0; -} - -bool profile_is_valid(alsa_device_profile* profile) { - return profile->is_valid; -} - -bool profile_is_cached_for(alsa_device_profile* profile, int card, int device) { - return card == profile->card && device == profile->device; -} - -void profile_decache(alsa_device_profile* profile) { - profile_reset(profile); -} - -/* - * Returns the supplied value rounded up to the next even multiple of 16 - */ -static unsigned int round_to_16_mult(unsigned int size) -{ - return (size + 15) & ~15; // 0xFFFFFFF0; -} - -/* - * Returns the system defined minimum period size based on the supplied sample rate. - */ -unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate) -{ - ALOGV("profile_calc_min_period_size(%p, rate:%d)", profile, sample_rate); - if (profile == NULL) { - return DEFAULT_PERIOD_SIZE; - } else { - unsigned num_sample_frames = (sample_rate * BUFF_DURATION_MS) / 1000; - if (num_sample_frames < profile->min_period_size) { - num_sample_frames = profile->min_period_size; - } - return round_to_16_mult(num_sample_frames) * 2; - } -} - -unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate) -{ - // return profile->default_config.period_size; - unsigned int period_size = profile_calc_min_period_size(profile, sample_rate); - ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size); - return period_size; -} - -/* - * Sample Rate - */ -unsigned profile_get_default_sample_rate(alsa_device_profile* profile) -{ - /* - * TODO this won't be right in general. we should store a preferred rate as we are scanning. - * But right now it will return the highest rate, which may be correct. - */ - return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE; -} - -bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate) -{ - if (profile_is_valid(profile)) { - size_t index; - for (index = 0; profile->sample_rates[index] != 0; index++) { - if (profile->sample_rates[index] == rate) { - return true; - } - } - - return false; - } else { - return rate == DEFAULT_SAMPLE_RATE; - } -} - -/* - * Format - */ -enum pcm_format profile_get_default_format(alsa_device_profile* profile) -{ - /* - * TODO this won't be right in general. we should store a preferred format as we are scanning. - */ - return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT; -} - -bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt) { - if (profile_is_valid(profile)) { - size_t index; - for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) { - if (profile->formats[index] == fmt) { - return true; - } - } - - return false; - } else { - return fmt == DEFAULT_SAMPLE_FORMAT; - } -} - -/* - * Channels - */ -unsigned profile_get_default_channel_count(alsa_device_profile* profile) -{ - return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT; -} - -bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count) -{ - if (profile_is_initialized(profile)) { - return count >= profile->min_channel_count && count <= profile->max_channel_count; - } else { - return count == DEFAULT_CHANNEL_COUNT; - } -} - -static bool profile_test_sample_rate(alsa_device_profile* profile, unsigned rate) -{ - struct pcm_config config = profile->default_config; - config.rate = rate; - - bool works = false; /* let's be pessimistic */ - struct pcm * pcm = pcm_open(profile->card, profile->device, - profile->direction, &config); - - if (pcm != NULL) { - works = pcm_is_ready(pcm); - pcm_close(pcm); - } - - return works; -} - -static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max) -{ - unsigned num_entries = 0; - unsigned index; - - for (index = 0; index < ARRAY_SIZE(std_sample_rates) && - num_entries < ARRAY_SIZE(profile->sample_rates) - 1; - index++) { - if (std_sample_rates[index] >= min && std_sample_rates[index] <= max - && profile_test_sample_rate(profile, std_sample_rates[index])) { - profile->sample_rates[num_entries++] = std_sample_rates[index]; - } - } - - return num_entries; /* return # of supported rates */ -} - -static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask) -{ - const int num_slots = ARRAY_SIZE(mask->bits); - const int bits_per_slot = sizeof(mask->bits[0]) * 8; - - const int table_size = ARRAY_SIZE(pcm_format_value_map); - - int slot_index, bit_index, table_index; - table_index = 0; - int num_written = 0; - for (slot_index = 0; slot_index < num_slots && table_index < table_size; - slot_index++) { - unsigned bit_mask = 1; - for (bit_index = 0; - bit_index < bits_per_slot && table_index < table_size; - bit_index++) { - if ((mask->bits[slot_index] & bit_mask) != 0) { - enum pcm_format format = pcm_format_value_map[table_index]; - /* Never return invalid (unrecognized) or 8-bit */ - if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) { - profile->formats[num_written++] = format; - if (num_written == ARRAY_SIZE(profile->formats) - 1) { - /* leave at least one PCM_FORMAT_INVALID at the end */ - return num_written; - } - } - } - bit_mask <<= 1; - table_index++; - } - } - - return num_written; -} - -static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min, unsigned max) -{ - static const unsigned std_channel_counts[] = {8, 4, 2, 1}; - - unsigned num_counts = 0; - unsigned index; - /* TODO write a profile_test_channel_count() */ - /* Ensure there is at least one invalid channel count to terminate the channel counts array */ - for (index = 0; index < ARRAY_SIZE(std_channel_counts) && - num_counts < ARRAY_SIZE(profile->channel_counts) - 1; - index++) { - /* TODO Do we want a channel counts test? */ - if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* && - profile_test_channel_count(profile, channel_counts[index])*/) { - profile->channel_counts[num_counts++] = std_channel_counts[index]; - } - } - - return num_counts; /* return # of supported counts */ -} - -/* - * Reads and decodes configuration info from the specified ALSA card/device. - */ -static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config) -{ - ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)", - profile->card, profile->device, profile->direction); - - if (profile->card < 0 || profile->device < 0) { - return -EINVAL; - } - - struct pcm_params * alsa_hw_params = - pcm_params_get(profile->card, profile->device, profile->direction); - if (alsa_hw_params == NULL) { - return -EINVAL; - } - - profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE); - profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE); - - profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS); - profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS); - - int ret = 0; - - /* - * This Logging will be useful when testing new USB devices. - */ -#ifdef LOG_PCM_PARAMS - log_pcm_params(alsa_hw_params); -#endif - - config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS); - config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE); - config->period_size = profile_calc_min_period_size(profile, config->rate); - config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS); - config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); -#ifdef LOG_PCM_PARAMS - log_pcm_config(config, "read_alsa_device_config"); -#endif - if (config->format == PCM_FORMAT_INVALID) { - ret = -EINVAL; - } - - pcm_params_free(alsa_hw_params); - - return ret; -} - -bool profile_read_device_info(alsa_device_profile* profile) -{ - if (!profile_is_initialized(profile)) { - return false; - } - - /* let's get some defaults */ - read_alsa_device_config(profile, &profile->default_config); - ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d", - profile->default_config.channels, profile->default_config.rate, - profile->default_config.format, profile->default_config.period_count, - profile->default_config.period_size); - - struct pcm_params * alsa_hw_params = pcm_params_get(profile->card, - profile->device, - profile->direction); - if (alsa_hw_params == NULL) { - return false; - } - - /* Formats */ - struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT); - profile_enum_sample_formats(profile, format_mask); - - /* Channels */ - profile_enum_channel_counts( - profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS)); - - /* Sample Rates */ - profile_enum_sample_rates( - profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE)); - - profile->is_valid = true; - - return true; -} - -char * profile_get_sample_rate_strs(alsa_device_profile* profile) -{ - char buffer[128]; - buffer[0] = '\0'; - int buffSize = ARRAY_SIZE(buffer); - - char numBuffer[32]; - - int numEntries = 0; - unsigned index; - for (index = 0; profile->sample_rates[index] != 0; index++) { - if (numEntries++ != 0) { - strncat(buffer, "|", buffSize); - } - snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]); - strncat(buffer, numBuffer, buffSize); - } - - return strdup(buffer); -} - -char * profile_get_format_strs(alsa_device_profile* profile) -{ - /* TODO remove this hack when we have support for input in non PCM16 formats */ - if (profile->direction == PCM_IN) { - return strdup("AUDIO_FORMAT_PCM_16_BIT"); - } - - char buffer[128]; - buffer[0] = '\0'; - int buffSize = ARRAY_SIZE(buffer); - - int numEntries = 0; - unsigned index = 0; - for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) { - if (numEntries++ != 0) { - strncat(buffer, "|", buffSize); - } - strncat(buffer, format_string_map[profile->formats[index]], buffSize); - } - - return strdup(buffer); -} - -char * profile_get_channel_count_strs(alsa_device_profile* profile) -{ - static const char * const out_chans_strs[] = { - /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */ - /* 1 */"AUDIO_CHANNEL_OUT_MONO", - /* 2 */"AUDIO_CHANNEL_OUT_STEREO", - /* 3 */ /* "AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL, - /* 4 */"AUDIO_CHANNEL_OUT_QUAD", - /* 5 */ /* "AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER" */ NULL, - /* 6 */"AUDIO_CHANNEL_OUT_5POINT1", - /* 7 */ /* "AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER" */ NULL, - /* 8 */"AUDIO_CHANNEL_OUT_7POINT1", - /* channel counts greater than this not considered */ - }; - - static const char * const in_chans_strs[] = { - /* 0 */"AUDIO_CHANNEL_NONE", /* will never be taken as this is a terminator */ - /* 1 */"AUDIO_CHANNEL_IN_MONO", - /* 2 */"AUDIO_CHANNEL_IN_STEREO", - /* channel counts greater than this not considered */ - }; - - const bool isOutProfile = profile->direction == PCM_OUT; - - const char * const * const names_array = isOutProfile ? out_chans_strs : in_chans_strs; - const size_t names_size = isOutProfile ? ARRAY_SIZE(out_chans_strs) - : ARRAY_SIZE(in_chans_strs); - - char buffer[256]; /* caution, may need to be expanded */ - buffer[0] = '\0'; - const int buffer_size = ARRAY_SIZE(buffer); - int num_entries = 0; - /* We currently support MONO and STEREO, and always report STEREO but some (many) - * USB Audio Devices may only announce support for MONO (a headset mic for example), or - * The total number of output channels. SO, if the device itself doesn't explicitly - * support STEREO, append to the channel config strings we are generating. - */ - bool stereo_present = false; - unsigned index; - unsigned channel_count; - - for (index = 0; (channel_count = profile->channel_counts[index]) != 0; index++) { - stereo_present = stereo_present || channel_count == 2; - if (channel_count < names_size && names_array[channel_count] != NULL) { - if (num_entries++ != 0) { - strncat(buffer, "|", buffer_size); - } - strncat(buffer, names_array[channel_count], buffer_size); - } - } - - /* emulated modes: - * always expose stereo as we can emulate it for PCM_OUT - */ - if (!stereo_present) { - if (num_entries++ != 0) { - strncat(buffer, "|", buffer_size); - } - strncat(buffer, names_array[2], buffer_size); /* stereo */ - } - - return strdup(buffer); -} diff --git a/modules/usbaudio/alsa_device_profile.h b/modules/usbaudio/alsa_device_profile.h deleted file mode 100644 index 2c0da394..00000000 --- a/modules/usbaudio/alsa_device_profile.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2014 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_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H -#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H - -#include <stdbool.h> - -#include <tinyalsa/asoundlib.h> - -#define MAX_PROFILE_FORMATS 6 /* We long support the 5 standard formats defined - * in asound.h, so we just need this to be 1 more - * than that */ -#define MAX_PROFILE_SAMPLE_RATES 10 /* this number needs to be 1 more than the number of - * standard formats in std_sample_rates[] - * (in alsa_device_profile.c) */ -#define MAX_PROFILE_CHANNEL_COUNTS 5 /* this number need to be 1 more than the number of - * standard channel formats in std_channel_counts[] - * (in alsa_device_profile.c) */ - -#define DEFAULT_SAMPLE_RATE 44100 -#define DEFAULT_SAMPLE_FORMAT PCM_FORMAT_S16_LE -#define DEFAULT_CHANNEL_COUNT 2 - -typedef struct { - int card; - int device; - int direction; /* PCM_OUT or PCM_IN */ - - enum pcm_format formats[MAX_PROFILE_FORMATS]; - - unsigned sample_rates[MAX_PROFILE_SAMPLE_RATES]; - - unsigned channel_counts[MAX_PROFILE_CHANNEL_COUNTS]; - - bool is_valid; - - /* read from the hardware device */ - struct pcm_config default_config; - - unsigned min_period_size; - unsigned max_period_size; - - unsigned min_channel_count; - unsigned max_channel_count; -} alsa_device_profile; - -void profile_init(alsa_device_profile* profile, int direction); -bool profile_is_initialized(alsa_device_profile* profile); -bool profile_is_valid(alsa_device_profile* profile); -bool profile_is_cached_for(alsa_device_profile* profile, int card, int device); -void profile_decache(alsa_device_profile* profile); - -bool profile_read_device_info(alsa_device_profile* profile); - -/* Audio Config Strings Methods */ -char * profile_get_sample_rate_strs(alsa_device_profile* profile); -char * profile_get_format_strs(alsa_device_profile* profile); -char * profile_get_channel_count_strs(alsa_device_profile* profile); - -/* Sample Rate Methods */ -unsigned profile_get_default_sample_rate(alsa_device_profile* profile); -bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate); - -/* Format Methods */ -enum pcm_format profile_get_default_format(alsa_device_profile* profile); -bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt); - -/* Channel Methods */ -unsigned profile_get_default_channel_count(alsa_device_profile* profile); -bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count); - -/* Utility */ -unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate); -unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate); - -#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H */ diff --git a/modules/usbaudio/alsa_device_proxy.c b/modules/usbaudio/alsa_device_proxy.c deleted file mode 100644 index 676f2889..00000000 --- a/modules/usbaudio/alsa_device_proxy.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2014 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 "alsa_device_proxy" -/*#define LOG_NDEBUG 0*/ -/*#define LOG_PCM_PARAMS 0*/ - -#include <log/log.h> - -#include <errno.h> - -#include "alsa_device_proxy.h" - -#include "logging.h" - -#define DEFAULT_PERIOD_SIZE 1024 -#define DEFAULT_PERIOD_COUNT 2 - -void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile, - struct pcm_config * config) -{ - ALOGV("proxy_prepare()"); - - proxy->profile = profile; - -#ifdef LOG_PCM_PARAMS - log_pcm_config(config, "proxy_setup()"); -#endif - - proxy->alsa_config.format = - config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format) - ? config->format : profile->default_config.format; - proxy->alsa_config.rate = - config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate) - ? config->rate : profile->default_config.rate; - proxy->alsa_config.channels = - config->channels != 0 && profile_is_channel_count_valid(profile, config->channels) - ? config->channels : profile->default_config.channels; - - proxy->alsa_config.period_count = profile->default_config.period_count; - proxy->alsa_config.period_size = - profile_get_period_size(proxy->profile, proxy->alsa_config.rate); - - // Hack for USB accessory audio. - // Here we set the correct value for period_count if tinyalsa fails to get it from the - // f_audio_source driver. - if (proxy->alsa_config.period_count == 0) { - proxy->alsa_config.period_count = 4; - } - - proxy->pcm = NULL; -} - -int proxy_open(alsa_device_proxy * proxy) -{ - alsa_device_profile* profile = proxy->profile; - ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device, - profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN"); - - proxy->pcm = pcm_open(profile->card, profile->device, profile->direction, &proxy->alsa_config); - if (proxy->pcm == NULL) { - return -ENOMEM; - } - - if (!pcm_is_ready(proxy->pcm)) { - ALOGE("[%s] proxy_open() pcm_open() failed: %s", LOG_TAG, pcm_get_error(proxy->pcm)); -#ifdef LOG_PCM_PARAMS - log_pcm_config(&proxy->alsa_config, "config"); -#endif - pcm_close(proxy->pcm); - proxy->pcm = NULL; - return -ENOMEM; - } - - return 0; -} - -void proxy_close(alsa_device_proxy * proxy) -{ - ALOGV("proxy_close() [pcm:%p]", proxy->pcm); - - if (proxy->pcm != NULL) { - pcm_close(proxy->pcm); - proxy->pcm = NULL; - } -} - -/* - * Sample Rate - */ -unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy) -{ - return proxy->alsa_config.rate; -} - -/* - * Format - */ -enum pcm_format proxy_get_format(const alsa_device_proxy * proxy) -{ - return proxy->alsa_config.format; -} - -/* - * Channel Count - */ -unsigned proxy_get_channel_count(const alsa_device_proxy * proxy) -{ - return proxy->alsa_config.channels; -} - -/* - * Other - */ -unsigned int proxy_get_period_size(const alsa_device_proxy * proxy) -{ - return proxy->alsa_config.period_size; -} - -unsigned int proxy_get_period_count(const alsa_device_proxy * proxy) -{ - return proxy->alsa_config.period_count; -} - -unsigned proxy_get_latency(const alsa_device_proxy * proxy) -{ - return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000) - / proxy_get_sample_rate(proxy); -} - -/* - * I/O - */ -int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count) -{ - return pcm_write(proxy->pcm, data, count); -} - -int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count) -{ - return pcm_read(proxy->pcm, data, count); -} diff --git a/modules/usbaudio/alsa_device_proxy.h b/modules/usbaudio/alsa_device_proxy.h deleted file mode 100644 index f090c562..00000000 --- a/modules/usbaudio/alsa_device_proxy.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014 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_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H -#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H - -#include <tinyalsa/asoundlib.h> - -#include "alsa_device_profile.h" - -typedef struct { - alsa_device_profile* profile; - - struct pcm_config alsa_config; - - struct pcm * pcm; -} alsa_device_proxy; - -void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile * profile, - struct pcm_config * config); - -unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy); -enum pcm_format proxy_get_format(const alsa_device_proxy * proxy); -unsigned proxy_get_channel_count(const alsa_device_proxy * proxy); - -unsigned int proxy_get_period_size(const alsa_device_proxy * proxy); - -unsigned proxy_get_latency(const alsa_device_proxy * proxy); - -int proxy_open(alsa_device_proxy * proxy); -void proxy_close(alsa_device_proxy * proxy); - -int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count); -int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count); - -#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H */ diff --git a/modules/usbaudio/audio_hw.c b/modules/usbaudio/audio_hal.c index ad018335..38fea869 100644 --- a/modules/usbaudio/audio_hw.c +++ b/modules/usbaudio/audio_hal.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "usb_audio_hw" +#define LOG_TAG "modules.usbaudio.audio_hal" /*#define LOG_NDEBUG 0*/ #include <errno.h> @@ -51,10 +51,15 @@ static const unsigned k_force_channels = 0; #include "alsa_device_profile.h" #include "alsa_device_proxy.h" -#include "logging.h" +#include "alsa_logging.h" #define DEFAULT_INPUT_BUFFER_SIZE_MS 20 +// stereo channel count +#define FCC_2 2 +// fixed channel count of 8 limitation (for data processing in AudioFlinger) +#define FCC_8 8 + struct audio_device { struct audio_hw_device hw_device; @@ -75,11 +80,12 @@ struct stream_out { struct audio_stream_out stream; pthread_mutex_t lock; /* see note below on mutex acquisition order */ + pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */ bool standby; struct audio_device *dev; /* hardware information - only using this for the lock */ - alsa_device_profile * profile; + alsa_device_profile * profile; /* Points to the alsa_device_profile in the audio_device */ alsa_device_proxy proxy; /* state of the stream */ unsigned hal_channel_count; /* channel count exposed to AudioFlinger. @@ -87,6 +93,8 @@ struct stream_out { * the device is not compatible with AudioFlinger * capabilities, e.g. exposes too many channels or * too few channels. */ + audio_channel_mask_t hal_channel_mask; /* channel mask exposed to AudioFlinger. */ + void * conversion_buffer; /* any conversions are put into here * they could come from here too if * there was a previous conversion */ @@ -96,12 +104,13 @@ struct stream_out { struct stream_in { struct audio_stream_in stream; - pthread_mutex_t lock; /* see note below on mutex acquisition order */ + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by capture thread */ bool standby; struct audio_device *dev; /* hardware information - only using this for the lock */ - alsa_device_profile * profile; + alsa_device_profile * profile; /* Points to the alsa_device_profile in the audio_device */ alsa_device_proxy proxy; /* state of the stream */ unsigned hal_channel_count; /* channel count exposed to AudioFlinger. @@ -109,6 +118,8 @@ struct stream_in { * the device is not compatible with AudioFlinger * capabilities, e.g. exposes too many channels or * too few channels. */ + audio_channel_mask_t hal_channel_mask; /* channel mask exposed to AudioFlinger. */ + /* We may need to read more data from the device in order to data reduce to 16bit, 4chan */ void * conversion_buffer; /* any conversions are put into here * they could come from here too if @@ -117,77 +128,49 @@ struct stream_in { }; /* - * Data Conversions + * NOTE: when multiple mutexes have to be acquired, always take the + * stream_in or stream_out mutex first, followed by the audio_device mutex. + * stream pre_lock is always acquired before stream lock to prevent starvation of control thread by + * higher priority playback or capture thread. */ + /* - * Convert a buffer of packed (3-byte) PCM24LE samples to PCM16LE samples. - * in_buff points to the buffer of PCM24LE samples - * num_in_samples size of input buffer in SAMPLES - * out_buff points to the buffer to receive converted PCM16LE LE samples. - * returns - * the number of BYTES of output data. - * We are doing this since we *always* present to The Framework as A PCM16LE device, but need to - * support PCM24_3LE (24-bit, packed). - * NOTE: - * This conversion is safe to do in-place (in_buff == out_buff). - * TODO Move this to a utilities module. + * Extract the card and device numbers from the supplied key/value pairs. + * kvpairs A null-terminated string containing the key/value pairs or card and device. + * i.e. "card=1;device=42" + * card A pointer to a variable to receive the parsed-out card number. + * device A pointer to a variable to receive the parsed-out device number. + * NOTE: The variables pointed to by card and device return -1 (undefined) if the + * associated key/value pair is not found in the provided string. + * Return true if the kvpairs string contain a card/device spec, false otherwise. */ -static size_t convert_24_3_to_16(const unsigned char * in_buff, size_t num_in_samples, - short * out_buff) +static bool parse_card_device_params(const char *kvpairs, int *card, int *device) { - /* - * Move from front to back so that the conversion can be done in-place - * i.e. in_buff == out_buff - */ - /* we need 2 bytes in the output for every 3 bytes in the input */ - unsigned char* dst_ptr = (unsigned char*)out_buff; - const unsigned char* src_ptr = in_buff; - size_t src_smpl_index; - for (src_smpl_index = 0; src_smpl_index < num_in_samples; src_smpl_index++) { - src_ptr++; /* lowest-(skip)-byte */ - *dst_ptr++ = *src_ptr++; /* low-byte */ - *dst_ptr++ = *src_ptr++; /* high-byte */ - } + struct str_parms * parms = str_parms_create_str(kvpairs); + char value[32]; + int param_val; - /* return number of *bytes* generated: */ - return num_in_samples * 2; -} + // initialize to "undefined" state. + *card = -1; + *device = -1; -/* - * Convert a buffer of packed (3-byte) PCM32 samples to PCM16LE samples. - * in_buff points to the buffer of PCM32 samples - * num_in_samples size of input buffer in SAMPLES - * out_buff points to the buffer to receive converted PCM16LE LE samples. - * returns - * the number of BYTES of output data. - * We are doing this since we *always* present to The Framework as A PCM16LE device, but need to - * support PCM_FORMAT_S32_LE (32-bit). - * NOTE: - * This conversion is safe to do in-place (in_buff == out_buff). - * TODO Move this to a utilities module. - */ -static size_t convert_32_to_16(const int32_t * in_buff, size_t num_in_samples, short * out_buff) -{ - /* - * Move from front to back so that the conversion can be done in-place - * i.e. in_buff == out_buff - */ + param_val = str_parms_get_str(parms, "card", value, sizeof(value)); + if (param_val >= 0) { + *card = atoi(value); + } - short * dst_ptr = out_buff; - const int32_t* src_ptr = in_buff; - size_t src_smpl_index; - for (src_smpl_index = 0; src_smpl_index < num_in_samples; src_smpl_index++) { - *dst_ptr++ = *src_ptr++ >> 16; + param_val = str_parms_get_str(parms, "device", value, sizeof(value)); + if (param_val >= 0) { + *device = atoi(value); } - /* return number of *bytes* generated: */ - return num_in_samples * 2; + str_parms_destroy(parms); + + return *card >= 0 && *device >= 0; } static char * device_get_parameters(alsa_device_profile * profile, const char * keys) { - ALOGV("usb:audio_hw::device_get_parameters() keys:%s", keys); - if (profile->card < 0 || profile->device < 0) { return strdup(""); } @@ -224,11 +207,25 @@ static char * device_get_parameters(alsa_device_profile * profile, const char * char* result_str = str_parms_to_str(result); str_parms_destroy(result); - ALOGV("usb:audio_hw::device_get_parameters = %s", result_str); + ALOGV("device_get_parameters = %s", result_str); return result_str; } +void lock_input_stream(struct stream_in *in) +{ + pthread_mutex_lock(&in->pre_lock); + pthread_mutex_lock(&in->lock); + pthread_mutex_unlock(&in->pre_lock); +} + +void lock_output_stream(struct stream_out *out) +{ + pthread_mutex_lock(&out->pre_lock); + pthread_mutex_lock(&out->lock); + pthread_mutex_unlock(&out->pre_lock); +} + /* * HAl Functions */ @@ -263,7 +260,7 @@ static size_t out_get_buffer_size(const struct audio_stream *stream) static uint32_t out_get_channels(const struct audio_stream *stream) { const struct stream_out *out = (const struct stream_out*)stream; - return audio_channel_out_mask_from_count(out->hal_channel_count); + return out->hal_channel_mask; } static audio_format_t out_get_format(const struct audio_stream *stream) @@ -286,16 +283,14 @@ static int out_standby(struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; - pthread_mutex_lock(&out->dev->lock); - pthread_mutex_lock(&out->lock); - + lock_output_stream(out); if (!out->standby) { + pthread_mutex_lock(&out->dev->lock); proxy_close(&out->proxy); + pthread_mutex_unlock(&out->dev->lock); out->standby = true; } - pthread_mutex_unlock(&out->lock); - pthread_mutex_unlock(&out->dev->lock); return 0; } @@ -307,30 +302,25 @@ static int out_dump(const struct audio_stream *stream, int fd) static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { - ALOGV("usb:audio_hw::out out_set_parameters() keys:%s", kvpairs); + ALOGV("out_set_parameters() keys:%s", kvpairs); struct stream_out *out = (struct stream_out *)stream; - char value[32]; - int param_val; int routing = 0; int ret_value = 0; int card = -1; int device = -1; - struct str_parms * parms = str_parms_create_str(kvpairs); - pthread_mutex_lock(&out->dev->lock); - pthread_mutex_lock(&out->lock); - - param_val = str_parms_get_str(parms, "card", value, sizeof(value)); - if (param_val >= 0) - card = atoi(value); + if (!parse_card_device_params(kvpairs, &card, &device)) { + // nothing to do + return ret_value; + } - param_val = str_parms_get_str(parms, "device", value, sizeof(value)); - if (param_val >= 0) - device = atoi(value); + lock_output_stream(out); + /* Lock the device because that is where the profile lives */ + pthread_mutex_lock(&out->dev->lock); - if (card >= 0 && device >= 0 && !profile_is_cached_for(out->profile, card, device)) { + if (!profile_is_cached_for(out->profile, card, device)) { /* cannot read pcm device info if playback is active */ if (!out->standby) ret_value = -ENOSYS; @@ -347,9 +337,8 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) } } - pthread_mutex_unlock(&out->lock); pthread_mutex_unlock(&out->dev->lock); - str_parms_destroy(parms); + pthread_mutex_unlock(&out->lock); return ret_value; } @@ -357,8 +346,8 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) static char * out_get_parameters(const struct audio_stream *stream, const char *keys) { struct stream_out *out = (struct stream_out *)stream; + lock_output_stream(out); pthread_mutex_lock(&out->dev->lock); - pthread_mutex_lock(&out->lock); char * params_str = device_get_parameters(out->profile, keys); @@ -382,8 +371,7 @@ static int out_set_volume(struct audio_stream_out *stream, float left, float rig /* must be called with hw device and output stream mutexes locked */ static int start_output_stream(struct stream_out *out) { - ALOGV("usb:audio_hw::out start_output_stream(card:%d device:%d)", - out->profile->card, out->profile->device); + ALOGV("start_output_stream(card:%d device:%d)", out->profile->card, out->profile->device); return proxy_open(&out->proxy); } @@ -393,17 +381,16 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si int ret; struct stream_out *out = (struct stream_out *)stream; - pthread_mutex_lock(&out->dev->lock); - pthread_mutex_lock(&out->lock); + lock_output_stream(out); if (out->standby) { + pthread_mutex_lock(&out->dev->lock); ret = start_output_stream(out); + pthread_mutex_unlock(&out->dev->lock); if (ret != 0) { - pthread_mutex_unlock(&out->dev->lock); goto err; } out->standby = false; } - pthread_mutex_unlock(&out->dev->lock); alsa_device_proxy* proxy = &out->proxy; const void * write_buff = buffer; @@ -455,8 +442,16 @@ static int out_get_render_position(const struct audio_stream_out *stream, uint32 static int out_get_presentation_position(const struct audio_stream_out *stream, uint64_t *frames, struct timespec *timestamp) { - /* FIXME - This needs to be implemented */ - return -EINVAL; + struct stream_out *out = (struct stream_out *)stream; // discard const qualifier + lock_output_stream(out); + + const alsa_device_proxy *proxy = &out->proxy; + const int ret = proxy_get_presentation_position(proxy, frames, timestamp); + + pthread_mutex_unlock(&out->lock); + ALOGV("out_get_presentation_position() status:%d frames:%llu", + ret, (unsigned long long)*frames); + return ret; } static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) @@ -480,10 +475,10 @@ static int adev_open_output_stream(struct audio_hw_device *dev, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, - const char *address __unused) + const char *address /*__unused*/) { - ALOGV("usb:audio_hw::out adev_open_output_stream() handle:0x%X, device:0x%X, flags:0x%X", - handle, devices, flags); + ALOGV("adev_open_output_stream() handle:0x%X, device:0x%X, flags:0x%X, addr:%s", + handle, devices, flags, address); struct audio_device *adev = (struct audio_device *)dev; @@ -513,14 +508,24 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->stream.get_presentation_position = out_get_presentation_position; out->stream.get_next_write_timestamp = out_get_next_write_timestamp; - out->dev = adev; + pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL); + pthread_mutex_init(&out->pre_lock, (const pthread_mutexattr_t *) NULL); + out->dev = adev; + pthread_mutex_lock(&adev->lock); out->profile = &adev->out_profile; // build this to hand to the alsa_device_proxy struct pcm_config proxy_config; memset(&proxy_config, 0, sizeof(proxy_config)); + /* Pull out the card/device pair */ + parse_card_device_params(address, &(out->profile->card), &(out->profile->device)); + + profile_read_device_info(out->profile); + + pthread_mutex_unlock(&adev->lock); + int ret = 0; /* Rate */ @@ -549,15 +554,28 @@ static int adev_open_output_stream(struct audio_hw_device *dev, } /* Channels */ - unsigned proposed_channel_count = profile_get_default_channel_count(out->profile); + unsigned proposed_channel_count = 0; if (k_force_channels) { proposed_channel_count = k_force_channels; - } else if (config->channel_mask != AUDIO_CHANNEL_NONE) { - proposed_channel_count = audio_channel_count_from_out_mask(config->channel_mask); + } else if (config->channel_mask == AUDIO_CHANNEL_NONE) { + proposed_channel_count = profile_get_default_channel_count(out->profile); } - /* we can expose any channel count mask, and emulate internally. */ - config->channel_mask = audio_channel_out_mask_from_count(proposed_channel_count); - out->hal_channel_count = proposed_channel_count; + if (proposed_channel_count != 0) { + if (proposed_channel_count <= FCC_2) { + // use channel position mask for mono and stereo + config->channel_mask = audio_channel_out_mask_from_count(proposed_channel_count); + } else { + // use channel index mask for multichannel + config->channel_mask = + audio_channel_mask_for_index_assignment_from_count(proposed_channel_count); + } + out->hal_channel_count = proposed_channel_count; + } else { + out->hal_channel_count = audio_channel_count_from_out_mask(config->channel_mask); + } + /* we can expose any channel mask, and emulate internally based on channel count. */ + out->hal_channel_mask = config->channel_mask; + /* no validity checks are needed as proxy_prepare() forces channel_count to be valid. * and we emulate any channel count discrepancies in out_write(). */ proxy_config.channels = proposed_channel_count; @@ -585,8 +603,8 @@ err_open: static void adev_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream) { - ALOGV("usb:audio_hw::out adev_close_output_stream()"); struct stream_out *out = (struct stream_out *)stream; + ALOGV("adev_close_output_stream(c:%d d:%d)", out->profile->card, out->profile->device); /* Close the pcm device */ out_standby(&stream->common); @@ -631,21 +649,14 @@ static size_t in_get_buffer_size(const struct audio_stream *stream) static uint32_t in_get_channels(const struct audio_stream *stream) { const struct stream_in *in = (const struct stream_in*)stream; - return audio_channel_in_mask_from_count(in->hal_channel_count); + return in->hal_channel_mask; } static audio_format_t in_get_format(const struct audio_stream *stream) { - /* TODO Here is the code we need when we support arbitrary input formats - * alsa_device_proxy * proxy = ((struct stream_in*)stream)->proxy; - * audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy)); - * ALOGV("in_get_format() = %d", format); - * return format; - */ - /* Input only supports PCM16 */ - /* TODO When AudioPolicyManager & AudioFlinger supports arbitrary input formats - rewrite this to return the ACTUAL channel format (above) */ - return AUDIO_FORMAT_PCM_16_BIT; + alsa_device_proxy *proxy = &((struct stream_in*)stream)->proxy; + audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy)); + return format; } static int in_set_format(struct audio_stream *stream, audio_format_t format) @@ -659,16 +670,15 @@ static int in_standby(struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; - pthread_mutex_lock(&in->dev->lock); - pthread_mutex_lock(&in->lock); - + lock_input_stream(in); if (!in->standby) { + pthread_mutex_lock(&in->dev->lock); proxy_close(&in->proxy); + pthread_mutex_unlock(&in->dev->lock); in->standby = true; } pthread_mutex_unlock(&in->lock); - pthread_mutex_unlock(&in->dev->lock); return 0; } @@ -680,7 +690,7 @@ static int in_dump(const struct audio_stream *stream, int fd) static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) { - ALOGV("usb: audio_hw::in in_set_parameters() keys:%s", kvpairs); + ALOGV("in_set_parameters() keys:%s", kvpairs); struct stream_in *in = (struct stream_in *)stream; @@ -691,19 +701,13 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) int card = -1; int device = -1; - struct str_parms * parms = str_parms_create_str(kvpairs); + if (!parse_card_device_params(kvpairs, &card, &device)) { + // nothing to do + return ret_value; + } + lock_input_stream(in); pthread_mutex_lock(&in->dev->lock); - pthread_mutex_lock(&in->lock); - - /* Device Connection Message ("card=1,device=0") */ - param_val = str_parms_get_str(parms, "card", value, sizeof(value)); - if (param_val >= 0) - card = atoi(value); - - param_val = str_parms_get_str(parms, "device", value, sizeof(value)); - if (param_val >= 0) - device = atoi(value); if (card >= 0 && device >= 0 && !profile_is_cached_for(in->profile, card, device)) { /* cannot read pcm device info if playback is active */ @@ -722,10 +726,8 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) } } - pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); - - str_parms_destroy(parms); + pthread_mutex_unlock(&in->lock); return ret_value; } @@ -734,13 +736,13 @@ static char * in_get_parameters(const struct audio_stream *stream, const char *k { struct stream_in *in = (struct stream_in *)stream; + lock_input_stream(in); pthread_mutex_lock(&in->dev->lock); - pthread_mutex_lock(&in->lock); char * params_str = device_get_parameters(in->profile, keys); - pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); + pthread_mutex_unlock(&in->lock); return params_str; } @@ -763,8 +765,7 @@ static int in_set_gain(struct audio_stream_in *stream, float gain) /* must be called with hw device and output stream mutexes locked */ static int start_input_stream(struct stream_in *in) { - ALOGV("usb:audio_hw::start_input_stream(card:%d device:%d)", - in->profile->card, in->profile->device); + ALOGV("ustart_input_stream(card:%d device:%d)", in->profile->card, in->profile->device); return proxy_open(&in->proxy); } @@ -779,17 +780,16 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte struct stream_in * in = (struct stream_in *)stream; - pthread_mutex_lock(&in->dev->lock); - pthread_mutex_lock(&in->lock); + lock_input_stream(in); if (in->standby) { - if (start_input_stream(in) != 0) { - pthread_mutex_unlock(&in->dev->lock); + pthread_mutex_lock(&in->dev->lock); + ret = start_input_stream(in); + pthread_mutex_unlock(&in->dev->lock); + if (ret != 0) { goto err; } in->standby = false; } - pthread_mutex_unlock(&in->dev->lock); - alsa_device_profile * profile = in->profile; @@ -798,22 +798,13 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte * number of bytes in the HAL format (16-bit, stereo). */ num_read_buff_bytes = bytes; - int num_device_channels = proxy_get_channel_count(&in->proxy); - int num_req_channels = in->hal_channel_count; + int num_device_channels = proxy_get_channel_count(&in->proxy); /* what we told Alsa */ + int num_req_channels = in->hal_channel_count; /* what we told AudioFlinger */ if (num_device_channels != num_req_channels) { num_read_buff_bytes = (num_device_channels * num_read_buff_bytes) / num_req_channels; } - enum pcm_format format = proxy_get_format(&in->proxy); - if (format == PCM_FORMAT_S24_3LE) { - /* 24-bit USB device */ - num_read_buff_bytes = (3 * num_read_buff_bytes) / 2; - } else if (format == PCM_FORMAT_S32_LE) { - /* 32-bit USB device */ - num_read_buff_bytes = num_read_buff_bytes * 2; - } - /* Setup/Realloc the conversion buffer (if necessary). */ if (num_read_buff_bytes != bytes) { if (num_read_buff_bytes > in->conversion_buffer_size) { @@ -827,29 +818,6 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte ret = proxy_read(&in->proxy, read_buff, num_read_buff_bytes); if (ret == 0) { - /* - * Do any conversions necessary to send the data in the format specified to/by the HAL - * (but different from the ALSA format), such as 24bit ->16bit, or 4chan -> 2chan. - */ - if (format != PCM_FORMAT_S16_LE) { - /* we need to convert */ - if (num_device_channels != num_req_channels) { - out_buff = read_buff; - } - - if (format == PCM_FORMAT_S24_3LE) { - num_read_buff_bytes = - convert_24_3_to_16(read_buff, num_read_buff_bytes / 3, out_buff); - } else if (format == PCM_FORMAT_S32_LE) { - num_read_buff_bytes = - convert_32_to_16(read_buff, num_read_buff_bytes / 4, out_buff); - } else { - LOG_ALWAYS_FATAL("Unsupported format"); - num_read_buff_bytes = 0; - goto err; - } - } - if (num_device_channels != num_req_channels) { // ALOGV("chans dev:%d req:%d", num_device_channels, num_req_channels); @@ -890,10 +858,10 @@ static int adev_open_input_stream(struct audio_hw_device *dev, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags __unused, - const char *address __unused, + const char *address /*__unused*/, audio_source_t source __unused) { - ALOGV("usb: in adev_open_input_stream() rate:%" PRIu32 ", chanMask:0x%" PRIX32 ", fmt:%" PRIu8, + ALOGV("in 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)); @@ -920,13 +888,23 @@ static int adev_open_input_stream(struct audio_hw_device *dev, in->stream.read = in_read; in->stream.get_input_frames_lost = in_get_input_frames_lost; + pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); + pthread_mutex_init(&in->pre_lock, (const pthread_mutexattr_t *) NULL); + in->dev = (struct audio_device *)dev; + pthread_mutex_lock(&in->dev->lock); in->profile = &in->dev->in_profile; 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); + pthread_mutex_unlock(&in->dev->lock); + /* Rate */ if (config->sample_rate == 0) { proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(in->profile); @@ -938,35 +916,39 @@ static int adev_open_input_stream(struct audio_hw_device *dev, } /* Format */ - /* until the framework supports format conversion, just take what it asks for - * i.e. AUDIO_FORMAT_PCM_16_BIT */ if (config->format == AUDIO_FORMAT_DEFAULT) { - /* just return AUDIO_FORMAT_PCM_16_BIT until the framework supports other input - * formats */ - config->format = AUDIO_FORMAT_PCM_16_BIT; - proxy_config.format = PCM_FORMAT_S16_LE; - } else if (config->format == AUDIO_FORMAT_PCM_16_BIT) { - /* Always accept AUDIO_FORMAT_PCM_16_BIT until the framework supports other input - * formats */ - proxy_config.format = PCM_FORMAT_S16_LE; + proxy_config.format = profile_get_default_format(in->profile); + config->format = audio_format_from_pcm_format(proxy_config.format); } else { - /* When the framework support other formats, validate here */ - config->format = AUDIO_FORMAT_PCM_16_BIT; - proxy_config.format = PCM_FORMAT_S16_LE; - ret = -EINVAL; + enum pcm_format fmt = pcm_format_from_audio_format(config->format); + if (profile_is_format_valid(in->profile, fmt)) { + proxy_config.format = fmt; + } else { + proxy_config.format = profile_get_default_format(in->profile); + config->format = audio_format_from_pcm_format(proxy_config.format); + ret = -EINVAL; + } } /* Channels */ - unsigned proposed_channel_count = profile_get_default_channel_count(in->profile); + unsigned proposed_channel_count = 0; if (k_force_channels) { proposed_channel_count = k_force_channels; - } else if (config->channel_mask != AUDIO_CHANNEL_NONE) { - proposed_channel_count = audio_channel_count_from_in_mask(config->channel_mask); + } else if (config->channel_mask == AUDIO_CHANNEL_NONE) { + proposed_channel_count = profile_get_default_channel_count(in->profile); } + if (proposed_channel_count != 0) { + config->channel_mask = audio_channel_in_mask_from_count(proposed_channel_count); + if (config->channel_mask == AUDIO_CHANNEL_INVALID) + config->channel_mask = + audio_channel_mask_for_index_assignment_from_count(proposed_channel_count); + in->hal_channel_count = proposed_channel_count; + } else { + in->hal_channel_count = audio_channel_count_from_in_mask(config->channel_mask); + } + /* we can expose any channel mask, and emulate internally based on channel count. */ + in->hal_channel_mask = config->channel_mask; - /* we can expose any channel count mask, and emulate internally. */ - config->channel_mask = audio_channel_in_mask_from_count(proposed_channel_count); - in->hal_channel_count = proposed_channel_count; proxy_config.channels = profile_get_default_channel_count(in->profile); proxy_prepare(&in->proxy, in->profile, &proxy_config); @@ -997,43 +979,6 @@ static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_st */ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { - ALOGV("audio_hw:usb adev_set_parameters(%s)", kvpairs); - - struct audio_device * adev = (struct audio_device *)dev; - - char value[32]; - int param_val; - - struct str_parms * parms = str_parms_create_str(kvpairs); - - /* Check for the "disconnect" message */ - param_val = str_parms_get_str(parms, "disconnect", value, sizeof(value)); - if (param_val >= 0) { - audio_devices_t device = (audio_devices_t)atoi(value); - - param_val = str_parms_get_str(parms, "card", value, sizeof(value)); - int alsa_card = param_val >= 0 ? atoi(value) : -1; - - param_val = str_parms_get_str(parms, "device", value, sizeof(value)); - int alsa_device = param_val >= 0 ? atoi(value) : -1; - - if (alsa_card >= 0 && alsa_device >= 0) { - /* "decache" the profile */ - pthread_mutex_lock(&adev->lock); - if (device == AUDIO_DEVICE_OUT_USB_DEVICE && - profile_is_cached_for(&adev->out_profile, alsa_card, alsa_device)) { - profile_decache(&adev->out_profile); - } - if (device == AUDIO_DEVICE_IN_USB_DEVICE && - profile_is_cached_for(&adev->in_profile, alsa_card, alsa_device)) { - profile_decache(&adev->in_profile); - } - pthread_mutex_unlock(&adev->lock); - } - } - - str_parms_destroy(parms); - return 0; } diff --git a/modules/usbaudio/format.c b/modules/usbaudio/format.c deleted file mode 100644 index 6aac1d39..00000000 --- a/modules/usbaudio/format.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2014 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 "usb_profile" -/*#define LOG_NDEBUG 0*/ - -#include "format.h" - -#include <tinyalsa/asoundlib.h> - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/* - * Maps from bit position in pcm_mask to AUDIO_ format constants. - */ -static audio_format_t const format_value_map[] = { - AUDIO_FORMAT_PCM_8_BIT, /* 00 - SNDRV_PCM_FORMAT_S8 */ - AUDIO_FORMAT_PCM_8_BIT, /* 01 - SNDRV_PCM_FORMAT_U8 */ - AUDIO_FORMAT_PCM_16_BIT, /* 02 - SNDRV_PCM_FORMAT_S16_LE */ - AUDIO_FORMAT_INVALID, /* 03 - SNDRV_PCM_FORMAT_S16_BE */ - AUDIO_FORMAT_INVALID, /* 04 - SNDRV_PCM_FORMAT_U16_LE */ - AUDIO_FORMAT_INVALID, /* 05 - SNDRV_PCM_FORMAT_U16_BE */ - AUDIO_FORMAT_INVALID, /* 06 - SNDRV_PCM_FORMAT_S24_LE */ - AUDIO_FORMAT_INVALID, /* 07 - SNDRV_PCM_FORMAT_S24_BE */ - AUDIO_FORMAT_INVALID, /* 08 - SNDRV_PCM_FORMAT_U24_LE */ - AUDIO_FORMAT_INVALID, /* 09 - SNDRV_PCM_FORMAT_U24_BE */ - AUDIO_FORMAT_PCM_32_BIT, /* 10 - SNDRV_PCM_FORMAT_S32_LE */ - AUDIO_FORMAT_INVALID, /* 11 - SNDRV_PCM_FORMAT_S32_BE */ - AUDIO_FORMAT_INVALID, /* 12 - SNDRV_PCM_FORMAT_U32_LE */ - AUDIO_FORMAT_INVALID, /* 13 - SNDRV_PCM_FORMAT_U32_BE */ - AUDIO_FORMAT_PCM_FLOAT, /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */ - AUDIO_FORMAT_INVALID, /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */ - AUDIO_FORMAT_INVALID, /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */ - AUDIO_FORMAT_INVALID, /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */ - AUDIO_FORMAT_INVALID, /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */ - AUDIO_FORMAT_INVALID, /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */ - AUDIO_FORMAT_INVALID, /* 20 - SNDRV_PCM_FORMAT_MU_LAW */ - AUDIO_FORMAT_INVALID, /* 21 - SNDRV_PCM_FORMAT_A_LAW */ - AUDIO_FORMAT_INVALID, /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */ - AUDIO_FORMAT_INVALID, /* 23 - SNDRV_PCM_FORMAT_MPEG */ - AUDIO_FORMAT_INVALID, /* 24 - SNDRV_PCM_FORMAT_GSM */ - AUDIO_FORMAT_INVALID, /* 25 -> 30 (not assigned) */ - AUDIO_FORMAT_INVALID, - AUDIO_FORMAT_INVALID, - AUDIO_FORMAT_INVALID, - AUDIO_FORMAT_INVALID, - AUDIO_FORMAT_INVALID, - AUDIO_FORMAT_INVALID, /* 31 - SNDRV_PCM_FORMAT_SPECIAL */ - AUDIO_FORMAT_PCM_24_BIT_PACKED, /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ - AUDIO_FORMAT_INVALID, /* 33 - SNDRV_PCM_FORMAT_S24_3BE */ - AUDIO_FORMAT_INVALID, /* 34 - SNDRV_PCM_FORMAT_U24_3LE */ - AUDIO_FORMAT_INVALID, /* 35 - SNDRV_PCM_FORMAT_U24_3BE */ - AUDIO_FORMAT_INVALID, /* 36 - SNDRV_PCM_FORMAT_S20_3LE */ - AUDIO_FORMAT_INVALID, /* 37 - SNDRV_PCM_FORMAT_S20_3BE */ - AUDIO_FORMAT_INVALID, /* 38 - SNDRV_PCM_FORMAT_U20_3LE */ - AUDIO_FORMAT_INVALID, /* 39 - SNDRV_PCM_FORMAT_U20_3BE */ - AUDIO_FORMAT_INVALID, /* 40 - SNDRV_PCM_FORMAT_S18_3LE */ - AUDIO_FORMAT_INVALID, /* 41 - SNDRV_PCM_FORMAT_S18_3BE */ - AUDIO_FORMAT_INVALID, /* 42 - SNDRV_PCM_FORMAT_U18_3LE */ - AUDIO_FORMAT_INVALID, /* 43 - SNDRV_PCM_FORMAT_U18_3BE */ - AUDIO_FORMAT_INVALID, /* 44 - SNDRV_PCM_FORMAT_G723_24 */ - AUDIO_FORMAT_INVALID, /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */ - AUDIO_FORMAT_INVALID, /* 46 - SNDRV_PCM_FORMAT_G723_40 */ - AUDIO_FORMAT_INVALID, /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */ - AUDIO_FORMAT_INVALID, /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */ - AUDIO_FORMAT_INVALID /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */ -}; - -audio_format_t get_format_for_mask(struct pcm_mask* mask) -{ - int num_slots = sizeof(mask->bits) / sizeof(mask->bits[0]); - int bits_per_slot = sizeof(mask->bits[0]) * 8; - - int table_size = sizeof(format_value_map) / sizeof(format_value_map[0]); - - int slot_index, bit_index, table_index; - table_index = 0; - int num_written = 0; - for (slot_index = 0; slot_index < num_slots; slot_index++) { - unsigned bit_mask = 1; - for (bit_index = 0; bit_index < bits_per_slot; bit_index++) { - /* don't return b-bit formats even if they are supported */ - if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) { - /* just return the first one */ - return table_index < table_size - ? format_value_map[table_index] - : AUDIO_FORMAT_INVALID; - } - bit_mask <<= 1; - table_index++; - } - } - - return AUDIO_FORMAT_INVALID; -} - -/* - * Maps from bit position in pcm_mask to PCM_ format constants. - */ -int8_t const pcm_format_value_map[50] = { - PCM_FORMAT_S8, /* 00 - SNDRV_PCM_FORMAT_S8 */ - PCM_FORMAT_INVALID, /* 01 - SNDRV_PCM_FORMAT_U8 */ - PCM_FORMAT_S16_LE, /* 02 - SNDRV_PCM_FORMAT_S16_LE */ - PCM_FORMAT_INVALID, /* 03 - SNDRV_PCM_FORMAT_S16_BE */ - PCM_FORMAT_INVALID, /* 04 - SNDRV_PCM_FORMAT_U16_LE */ - PCM_FORMAT_INVALID, /* 05 - SNDRV_PCM_FORMAT_U16_BE */ - PCM_FORMAT_S24_3LE, /* 06 - SNDRV_PCM_FORMAT_S24_LE */ - PCM_FORMAT_INVALID, /* 07 - SNDRV_PCM_FORMAT_S24_BE */ - PCM_FORMAT_INVALID, /* 08 - SNDRV_PCM_FORMAT_U24_LE */ - PCM_FORMAT_INVALID, /* 09 - SNDRV_PCM_FORMAT_U24_BE */ - PCM_FORMAT_S32_LE, /* 10 - SNDRV_PCM_FORMAT_S32_LE */ - PCM_FORMAT_INVALID, /* 11 - SNDRV_PCM_FORMAT_S32_BE */ - PCM_FORMAT_INVALID, /* 12 - SNDRV_PCM_FORMAT_U32_LE */ - PCM_FORMAT_INVALID, /* 13 - SNDRV_PCM_FORMAT_U32_BE */ - PCM_FORMAT_INVALID, /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */ - PCM_FORMAT_INVALID, /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */ - PCM_FORMAT_INVALID, /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */ - PCM_FORMAT_INVALID, /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */ - PCM_FORMAT_INVALID, /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */ - PCM_FORMAT_INVALID, /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */ - PCM_FORMAT_INVALID, /* 20 - SNDRV_PCM_FORMAT_MU_LAW */ - PCM_FORMAT_INVALID, /* 21 - SNDRV_PCM_FORMAT_A_LAW */ - PCM_FORMAT_INVALID, /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */ - PCM_FORMAT_INVALID, /* 23 - SNDRV_PCM_FORMAT_MPEG */ - PCM_FORMAT_INVALID, /* 24 - SNDRV_PCM_FORMAT_GSM */ - PCM_FORMAT_INVALID, /* 25 -> 30 (not assigned) */ - PCM_FORMAT_INVALID, - PCM_FORMAT_INVALID, - PCM_FORMAT_INVALID, - PCM_FORMAT_INVALID, - PCM_FORMAT_INVALID, - PCM_FORMAT_INVALID, /* 31 - SNDRV_PCM_FORMAT_SPECIAL */ - PCM_FORMAT_S24_3LE, /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ /* ??? */ - PCM_FORMAT_INVALID, /* 33 - SNDRV_PCM_FORMAT_S24_3BE */ - PCM_FORMAT_INVALID, /* 34 - SNDRV_PCM_FORMAT_U24_3LE */ - PCM_FORMAT_INVALID, /* 35 - SNDRV_PCM_FORMAT_U24_3BE */ - PCM_FORMAT_INVALID, /* 36 - SNDRV_PCM_FORMAT_S20_3LE */ - PCM_FORMAT_INVALID, /* 37 - SNDRV_PCM_FORMAT_S20_3BE */ - PCM_FORMAT_INVALID, /* 38 - SNDRV_PCM_FORMAT_U20_3LE */ - PCM_FORMAT_INVALID, /* 39 - SNDRV_PCM_FORMAT_U20_3BE */ - PCM_FORMAT_INVALID, /* 40 - SNDRV_PCM_FORMAT_S18_3LE */ - PCM_FORMAT_INVALID, /* 41 - SNDRV_PCM_FORMAT_S18_3BE */ - PCM_FORMAT_INVALID, /* 42 - SNDRV_PCM_FORMAT_U18_3LE */ - PCM_FORMAT_INVALID, /* 43 - SNDRV_PCM_FORMAT_U18_3BE */ - PCM_FORMAT_INVALID, /* 44 - SNDRV_PCM_FORMAT_G723_24 */ - PCM_FORMAT_INVALID, /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */ - PCM_FORMAT_INVALID, /* 46 - SNDRV_PCM_FORMAT_G723_40 */ - PCM_FORMAT_INVALID, /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */ - PCM_FORMAT_INVALID, /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */ - PCM_FORMAT_INVALID /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */ -}; - -/* - * Scans the provided format mask and returns the first non-8 bit sample - * format supported by the devices. - */ -enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask) -{ - int num_slots = ARRAY_SIZE(mask->bits); - int bits_per_slot = sizeof(mask->bits[0]) * 8; - - int table_size = ARRAY_SIZE(pcm_format_value_map); - - int slot_index, bit_index, table_index; - table_index = 0; - int num_written = 0; - for (slot_index = 0; slot_index < num_slots && table_index < table_size; slot_index++) { - unsigned bit_mask = 1; - for (bit_index = 0; bit_index < bits_per_slot && table_index < table_size; bit_index++) { - /* skip any 8-bit formats */ - if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) { - /* just return the first one which will be at least 16-bit */ - return (int)pcm_format_value_map[table_index]; - } - bit_mask <<= 1; - table_index++; - } - } - - return PCM_FORMAT_INVALID; -} diff --git a/modules/usbaudio/format.h b/modules/usbaudio/format.h deleted file mode 100644 index e23935ec..00000000 --- a/modules/usbaudio/format.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2014 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_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H -#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H - -#include <system/audio.h> - -#include <tinyalsa/asoundlib.h> - -audio_format_t get_format_for_mask(struct pcm_mask* mask); -enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask); - -#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H */ diff --git a/modules/usbaudio/logging.c b/modules/usbaudio/logging.c deleted file mode 100644 index 0a055119..00000000 --- a/modules/usbaudio/logging.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2014 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 "usb_logging" -/*#define LOG_NDEBUG 0*/ - -#include <string.h> - -#include <log/log.h> - -#include "logging.h" - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/* - * Logging - */ -void log_pcm_mask(const char* mask_name, struct pcm_mask* mask) -{ - const size_t num_slots = ARRAY_SIZE(mask->bits); - const size_t bits_per_slot = (sizeof(mask->bits[0]) * 8); - const size_t chars_per_slot = (bits_per_slot + 1); /* comma */ - - const size_t BUFF_SIZE = - (num_slots * chars_per_slot + 2 + 1); /* brackets and null-terminator */ - char buff[BUFF_SIZE]; - buff[0] = '\0'; - - size_t slot_index, bit_index; - strcat(buff, "["); - for (slot_index = 0; slot_index < num_slots; slot_index++) { - unsigned bit_mask = 1; - for (bit_index = 0; bit_index < bits_per_slot; bit_index++) { - strcat(buff, (mask->bits[slot_index] & bit_mask) != 0 ? "1" : "0"); - bit_mask <<= 1; - } - if (slot_index < num_slots - 1) { - strcat(buff, ","); - } - } - strcat(buff, "]"); - - ALOGV("%s: mask:%s", mask_name, buff); -} - -void log_pcm_params(struct pcm_params * alsa_hw_params) -{ - ALOGV("usb:audio_hw - PCM_PARAM_SAMPLE_BITS min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_SAMPLE_BITS), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_SAMPLE_BITS)); - ALOGV("usb:audio_hw - PCM_PARAM_FRAME_BITS min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_FRAME_BITS), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_FRAME_BITS)); - log_pcm_mask("PCM_PARAM_FORMAT", - pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); - log_pcm_mask("PCM_PARAM_SUBFORMAT", - pcm_params_get_mask(alsa_hw_params, PCM_PARAM_SUBFORMAT)); - ALOGV("usb:audio_hw - PCM_PARAM_CHANNELS min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS)); - ALOGV("usb:audio_hw - PCM_PARAM_RATE min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE)); - ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_TIME min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_TIME), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_TIME)); - ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_SIZE min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE)); - ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_BYTES min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_BYTES), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_BYTES)); - ALOGV("usb:audio_hw - PCM_PARAM_PERIODS min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIODS)); - ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_TIME min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_TIME), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_TIME)); - ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_SIZE min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_SIZE), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_SIZE)); - ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_BYTES min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_BYTES), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_BYTES)); - ALOGV("usb:audio_hw - PCM_PARAM_TICK_TIME min:%u, max:%u", - pcm_params_get_min(alsa_hw_params, PCM_PARAM_TICK_TIME), - pcm_params_get_max(alsa_hw_params, PCM_PARAM_TICK_TIME)); -} - -void log_pcm_config(struct pcm_config * config, const char* label) { - ALOGV("log_pcm_config() - %s", label); - ALOGV(" channels:%d", config->channels); - ALOGV(" rate:%d", config->rate); - ALOGV(" period_size:%d", config->period_size); - ALOGV(" period_count:%d", config->period_count); - ALOGV(" format:%d", config->format); -#if 0 - /* Values to use for the ALSA start, stop and silence thresholds. Setting - * any one of these values to 0 will cause the default tinyalsa values to be - * used instead. Tinyalsa defaults are as follows. - * - * start_threshold : period_count * period_size - * stop_threshold : period_count * period_size - * silence_threshold : 0 - */ - unsigned int start_threshold; - unsigned int stop_threshold; - unsigned int silence_threshold; - - /* Minimum number of frames available before pcm_mmap_write() will actually - * write into the kernel buffer. Only used if the stream is opened in mmap mode - * (pcm_open() called with PCM_MMAP flag set). Use 0 for default. - */ - int avail_min; -#endif -} diff --git a/modules/usbaudio/logging.h b/modules/usbaudio/logging.h deleted file mode 100644 index b5640eda..00000000 --- a/modules/usbaudio/logging.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2014 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_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H -#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H - -#include <tinyalsa/asoundlib.h> - -void log_pcm_mask(const char* mask_name, struct pcm_mask* mask); -void log_pcm_params(struct pcm_params * alsa_hw_params); -void log_pcm_config(struct pcm_config * config, const char* label); - -#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H */ diff --git a/modules/usbcamera/Android.mk b/modules/usbcamera/Android.mk new file mode 100644 index 00000000..162b1585 --- /dev/null +++ b/modules/usbcamera/Android.mk @@ -0,0 +1,45 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := camera.usb.default +LOCAL_MODULE_RELATIVE_PATH := hw + +LOCAL_C_INCLUDES += \ + system/core/include \ + system/media/camera/include \ + +LOCAL_SRC_FILES := \ + CameraHAL.cpp \ + Camera.cpp \ + UsbCamera.cpp \ + Metadata.cpp \ + Stream.cpp \ + HotplugThread.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libcamera_metadata \ + libcutils \ + liblog \ + libsync \ + libutils \ + +LOCAL_CFLAGS += -Wall -Wextra -fvisibility=hidden + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/modules/usbcamera/Camera.cpp b/modules/usbcamera/Camera.cpp new file mode 100644 index 00000000..cf62f7f4 --- /dev/null +++ b/modules/usbcamera/Camera.cpp @@ -0,0 +1,537 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "Camera" +#include <cutils/log.h> + +#include <cstdlib> +#include <stdio.h> +#include <hardware/camera3.h> +#include <system/camera_metadata.h> +#include <system/graphics.h> +#include <utils/Mutex.h> +#include "CameraHAL.h" +#include "Metadata.h" +#include "Stream.h" + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <utils/Trace.h> + +#include "Camera.h" + +namespace usb_camera_hal { + +extern "C" { +// Shim passed to the framework to close an opened device. +static int close_device(hw_device_t* dev) { + camera3_device_t* cam_dev = reinterpret_cast<camera3_device_t*>(dev); + Camera* cam = static_cast<Camera*>(cam_dev->priv); + return cam->close(); +} + +// Get handle to camera from device priv data +static Camera *camdev_to_camera(const camera3_device_t *dev) { + return reinterpret_cast<Camera*>(dev->priv); +} + +static int initialize(const camera3_device_t *dev, + const camera3_callback_ops_t *callback_ops) { + return camdev_to_camera(dev)->initialize(callback_ops); +} + +static int configure_streams(const camera3_device_t *dev, + camera3_stream_configuration_t *stream_list) { + return camdev_to_camera(dev)->configureStreams(stream_list); +} + +static const camera_metadata_t *construct_default_request_settings( + const camera3_device_t *dev, int type) { + return camdev_to_camera(dev)->constructDefaultRequestSettings(type); +} + +static int process_capture_request(const camera3_device_t *dev, + camera3_capture_request_t *request) { + return camdev_to_camera(dev)->processCaptureRequest(request); +} + +static void dump(const camera3_device_t *dev, int fd) { + camdev_to_camera(dev)->dump(fd); +} + +static int flush(const camera3_device_t *dev) { + return camdev_to_camera(dev)->flush(); +} + +} // extern "C" + +const camera3_device_ops_t Camera::sOps = { + .initialize = usb_camera_hal::initialize, + .configure_streams = usb_camera_hal::configure_streams, + .register_stream_buffers = NULL, + .construct_default_request_settings + = usb_camera_hal::construct_default_request_settings, + .process_capture_request = usb_camera_hal::process_capture_request, + .get_metadata_vendor_tag_ops = NULL, + .dump = usb_camera_hal::dump, + .flush = usb_camera_hal::flush, + .reserved = {0}, +}; + +Camera::Camera(int id) + : mId(id), + mStaticInfo(NULL), + mBusy(false), + mCallbackOps(NULL), + mSettings(NULL), + mIsInitialized(false) { + memset(&mTemplates, 0, sizeof(mTemplates)); + memset(&mDevice, 0, sizeof(mDevice)); + mDevice.common.tag = HARDWARE_DEVICE_TAG; + // TODO: Upgrade to HAL3.3 + mDevice.common.version = CAMERA_DEVICE_API_VERSION_3_2; + mDevice.common.close = close_device; + mDevice.ops = const_cast<camera3_device_ops_t*>(&sOps); + mDevice.priv = this; +} + +Camera::~Camera() { + if (mStaticInfo != NULL) { + free_camera_metadata(mStaticInfo); + } + + for (int i = 0; i < CAMERA3_TEMPLATE_COUNT; i++) { + free_camera_metadata(mTemplates[i]); + } + + if (mSettings != NULL) { + free_camera_metadata(mSettings); + } +} + +int Camera::open(const hw_module_t *module, hw_device_t **device) { + ALOGI("%s:%d: Opening camera device", __func__, mId); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + + if (mBusy) { + ALOGE("%s:%d: Error! Camera device already opened", __func__, mId); + return -EBUSY; + } + + mBusy = true; + mDevice.common.module = const_cast<hw_module_t*>(module); + *device = &mDevice.common; + return openDevice(); +} + +int Camera::getInfo(struct camera_info *info) { + android::Mutex::Autolock al(mStaticInfoLock); + + // TODO: update to CAMERA_FACING_EXTERNAL once the HAL API changes are merged. + info->facing = CAMERA_FACING_FRONT; + info->orientation = 0; + info->device_version = mDevice.common.version; + if (mStaticInfo == NULL) { + initStaticInfo(); + } + info->static_camera_characteristics = mStaticInfo; + return 0; +} + +void Camera::updateInfo() { + android::Mutex::Autolock al(mStaticInfoLock); + initStaticInfo(); +} + +int Camera::close() { + ALOGI("%s:%d: Closing camera device", __func__, mId); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + + if (!mBusy) { + ALOGE("%s:%d: Error! Camera device not open", __func__, mId); + return -EINVAL; + } + + mBusy = false; + mIsInitialized = false; + return closeDevice(); +} + +int Camera::initialize(const camera3_callback_ops_t *callback_ops) { + int res; + + ALOGV("%s:%d: callback_ops=%p", __func__, mId, callback_ops); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + + mCallbackOps = callback_ops; + // per-device specific initialization + res = initDevice(); + if (res != 0) { + ALOGE("%s:%d: Failed to initialize device!", __func__, mId); + return res; + } + + mIsInitialized = true; + return 0; +} + +int Camera::configureStreams(camera3_stream_configuration_t *stream_config) { + camera3_stream_t *astream; + android::Vector<Stream *> newStreams; + + ALOGV("%s:%d: stream_config=%p", __func__, mId, stream_config); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + if (!mIsInitialized) { + ALOGE("Device is not initialized yet"); + return -EINVAL; + } + + if (stream_config == NULL) { + ALOGE("%s:%d: NULL stream configuration array", __func__, mId); + return -EINVAL; + } + if (stream_config->num_streams == 0) { + ALOGE("%s:%d: Empty stream configuration array", __func__, mId); + return -EINVAL; + } + + ALOGV("%s:%d: Number of Streams: %d", __func__, mId, + stream_config->num_streams); + // Mark all current streams unused for now + for (size_t i = 0; i < mStreams.size(); i++) { + mStreams[i]->mReuse = false; + } + // Fill new stream array with reused streams and new streams + for (unsigned int i = 0; i < stream_config->num_streams; i++) { + astream = stream_config->streams[i]; + if (astream->max_buffers > 0) { + ALOGV("%s:%d: Reusing stream %d", __func__, mId, i); + newStreams.add(reuseStreamLocked(astream)); + } else { + ALOGV("%s:%d: Creating new stream %d", __func__, mId, i); + newStreams.add(new Stream(mId, astream)); + } + + if (newStreams[i] == NULL) { + ALOGE("%s:%d: Error processing stream %d", __func__, mId, i); + goto err_out; + } + astream->priv = reinterpret_cast<void *>(newStreams[i]); + } + + // Verify the set of streams in aggregate + if (!isValidStreamSetLocked(newStreams)) { + ALOGE("%s:%d: Invalid stream set", __func__, mId); + goto err_out; + } + + // Set up all streams (calculate usage/max_buffers for each) + setupStreamsLocked(newStreams); + + // Destroy all old streams and replace stream array with new one + destroyStreamsLocked(mStreams); + mStreams = newStreams; + + // Clear out last seen settings metadata + updateSettingsLocked(NULL); + return 0; + +err_out: + // Clean up temporary streams, preserve existing mStreams + destroyStreamsLocked(newStreams); + return -EINVAL; +} + +void Camera::destroyStreamsLocked(android::Vector<Stream *> &streams) { + for (size_t i = 0; i < streams.size(); i++) { + delete streams[i]; + } + streams.clear(); +} + +Stream *Camera::reuseStreamLocked(camera3_stream_t *astream) { + Stream *priv = reinterpret_cast<Stream*>(astream->priv); + // Verify the re-used stream's parameters match + if (!priv->isValidReuseStream(mId, astream)) { + ALOGE("%s:%d: Mismatched parameter in reused stream", __func__, mId); + return NULL; + } + // Mark stream to be reused + priv->mReuse = true; + return priv; +} + +bool Camera::isValidStreamSetLocked(const android::Vector<Stream *> &streams) { + int inputs = 0; + int outputs = 0; + + if (streams.isEmpty()) { + ALOGE("%s:%d: Zero count stream configuration streams", __func__, mId); + return false; + } + // Validate there is at most one input stream and at least one output stream + for (size_t i = 0; i < streams.size(); i++) { + // A stream may be both input and output (bidirectional) + if (streams[i]->isInputType()) + inputs++; + if (streams[i]->isOutputType()) + outputs++; + } + ALOGV("%s:%d: Configuring %d output streams and %d input streams", + __func__, mId, outputs, inputs); + if (outputs < 1) { + ALOGE("%s:%d: Stream config must have >= 1 output", __func__, mId); + return false; + } + if (inputs > 1) { + ALOGE("%s:%d: Stream config must have <= 1 input", __func__, mId); + return false; + } + // TODO: check for correct number of Bayer/YUV/JPEG/Encoder streams + return true; +} + +void Camera::setupStreamsLocked(android::Vector<Stream *> &streams) { + /* + * This is where the HAL has to decide internally how to handle all of the + * streams, and then produce usage and max_buffer values for each stream. + * Note, the stream vector has been checked before this point for ALL invalid + * conditions, so it must find a successful configuration for this stream + * array. The HAL may not return an error from this point. + * + * TODO: we just set all streams to be the same dummy values; + * real implementations will want to avoid USAGE_SW_{READ|WRITE}_OFTEN. + */ + for (size_t i = 0; i < streams.size(); i++) { + uint32_t usage = 0; + + if (streams[i]->isOutputType()) + usage |= GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_CAMERA_WRITE; + if (streams[i]->isInputType()) + usage |= GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_HW_CAMERA_READ; + + streams[i]->setUsage(usage); + streams[i]->setMaxBuffers(1); + } +} + +bool Camera::isValidTemplateType(int type) { + return type >= 1 && type < CAMERA3_TEMPLATE_COUNT; +} + +const camera_metadata_t* Camera::constructDefaultRequestSettings(int type) { + ALOGV("%s:%d: type=%d", __func__, mId, type); + android::Mutex::Autolock al(mDeviceLock); + + if (!isValidTemplateType(type)) { + ALOGE("%s:%d: Invalid template request type: %d", __func__, mId, type); + return NULL; + } + + // DO NOT try to initialize the device here, it will be guaranteed deadlock. + if (!mIsInitialized) { + ALOGE("Device is not initialized yet"); + return NULL; + } + + return mTemplates[type]; +} + +// This implementation is a copy-paste, probably we should override (or move) this to +// device specific class. +int Camera::processCaptureRequest(camera3_capture_request_t *request) { + camera3_capture_result result; + ALOGV("%s:%d: request=%p", __func__, mId, request); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + + if (request == NULL) { + ALOGE("%s:%d: NULL request recieved", __func__, mId); + return -EINVAL; + } + + ALOGV("%s:%d: Request Frame:%d Settings:%p", __func__, mId, + request->frame_number, request->settings); + + // NULL indicates use last settings + if (request->settings == NULL) { + if (mSettings == NULL) { + ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p", + __func__, mId, request->frame_number, request); + return -EINVAL; + } + } else { + updateSettingsLocked(request->settings); + } + + if (request->input_buffer != NULL) { + ALOGV("%s:%d: Reprocessing input buffer is not supported yet", __func__, mId); + return -EINVAL; + } else { + ALOGV("%s:%d: Capturing new frame.", __func__, mId); + + if (!isValidCaptureSettings(request->settings)) { + ALOGE("%s:%d: Invalid settings for capture request: %p", + __func__, mId, request->settings); + return -EINVAL; + } + } + + if (request->num_output_buffers <= 0) { + ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId, + request->num_output_buffers); + return -EINVAL; + } + result.num_output_buffers = request->num_output_buffers; + result.output_buffers = new camera3_stream_buffer_t[result.num_output_buffers]; + for (unsigned int i = 0; i < request->num_output_buffers; i++) { + int res = processCaptureBuffer(&request->output_buffers[i], + const_cast<camera3_stream_buffer_t*>(&result.output_buffers[i])); + if (res) { + delete [] result.output_buffers; + // TODO: this should probably be a total device failure; transient for now + return -EINVAL; + } + } + + result.frame_number = request->frame_number; + // TODO: return actual captured/reprocessed settings + result.result = request->settings; + // TODO: asynchronously return results + notifyShutter(request->frame_number, 0); + mCallbackOps->process_capture_result(mCallbackOps, &result); + + // Free up capture result related resources, HAL owns the capture result, and it + // is only valid during the process_capture_result call. + delete[] result.output_buffers; + + return 0; +} + +int Camera::flush() { + int res; + + ALOGV("%s:%d: flush device", __func__, mId); + // per-device specific flush + res = flushDevice(); + if (res != 0) { + ALOGE("%s:%d: Failed to flush device!", __func__, mId); + return res; + } + return 0; +} + +void Camera::updateSettingsLocked(const camera_metadata_t *new_settings) { + if (mSettings != NULL) { + free_camera_metadata(mSettings); + mSettings = NULL; + } + + if (new_settings != NULL) + mSettings = clone_camera_metadata(new_settings); +} + +void Camera::notifyShutter(uint32_t frame_number, uint64_t timestamp) { + int res; + struct timespec ts; + + // If timestamp is 0, get timestamp from right now instead + if (timestamp == 0) { + ALOGW("%s:%d: No timestamp provided, using CLOCK_BOOTTIME", + __func__, mId); + res = clock_gettime(CLOCK_BOOTTIME, &ts); + if (res == 0) { + timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec; + } else { + ALOGE("%s:%d: No timestamp and failed to get CLOCK_BOOTTIME %s(%d)", + __func__, mId, strerror(errno), errno); + } + } + camera3_notify_msg_t m; + memset(&m, 0, sizeof(m)); + m.type = CAMERA3_MSG_SHUTTER; + m.message.shutter.frame_number = frame_number; + m.message.shutter.timestamp = timestamp; + mCallbackOps->notify(mCallbackOps, &m); +} + +void Camera::dump(int fd) { + ALOGV("%s:%d: Dumping to fd %d", __func__, mId, fd); + ATRACE_CALL(); + android::Mutex::Autolock al(mDeviceLock); + + dprintf(fd, "Camera ID: %d (Busy: %d)\n", mId, mBusy); + + // TODO: dump all settings + dprintf(fd, "Most Recent Settings: (%p)\n", mSettings); + + dprintf(fd, "Number of streams: %d\n", mStreams.size()); + for (size_t i = 0; i < mStreams.size(); i++) { + dprintf(fd, "Stream %d/%d:\n", i, mStreams.size()); + mStreams[i]->dump(fd); + } +} + +const char* Camera::templateToString(int type) { + switch (type) { + case CAMERA3_TEMPLATE_PREVIEW: + return "CAMERA3_TEMPLATE_PREVIEW"; + case CAMERA3_TEMPLATE_STILL_CAPTURE: + return "CAMERA3_TEMPLATE_STILL_CAPTURE"; + case CAMERA3_TEMPLATE_VIDEO_RECORD: + return "CAMERA3_TEMPLATE_VIDEO_RECORD"; + case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT: + return "CAMERA3_TEMPLATE_VIDEO_SNAPSHOT"; + case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG: + return "CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG"; + case CAMERA3_TEMPLATE_MANUAL: + return "CAMERA3_TEMPLATE_MANUAL"; + } + + return "Invalid template type!"; +} + +int Camera::setTemplate(int type, camera_metadata_t *settings) { + android::Mutex::Autolock al(mDeviceLock); + + if (!isValidTemplateType(type)) { + ALOGE("%s:%d: Invalid template request type: %d", __func__, mId, type); + return -EINVAL; + } + + if (mTemplates[type] != NULL) { + ALOGE("%s:%d: Setting already constructed template type %s(%d)", + __func__, mId, templateToString(type), type); + return -EINVAL; + } + + // Make a durable copy of the underlying metadata + mTemplates[type] = clone_camera_metadata(settings); + if (mTemplates[type] == NULL) { + ALOGE("%s:%d: Failed to clone metadata %p for template type %s(%d)", + __func__, mId, settings, templateToString(type), type); + return -EINVAL; + } + return 0; +} + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/Camera.h b/modules/usbcamera/Camera.h new file mode 100644 index 00000000..6419c7d1 --- /dev/null +++ b/modules/usbcamera/Camera.h @@ -0,0 +1,131 @@ +/* + * 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 CAMERA_H_ +#define CAMERA_H_ + +#include <hardware/hardware.h> +#include <hardware/camera3.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> +#include "Metadata.h" +#include <sync/sync.h> +#include "Stream.h" + +#define CAMERA_SYNC_TIMEOUT_MS 5000 + +namespace usb_camera_hal { +// Camera represents a physical camera on a device. +// This is constructed when the HAL module is loaded, one per physical camera. +// It is opened by the framework, and must be closed before it can be opened +// again. +// This is an abstract class, containing all logic and data shared between all +// camera devices. +class Camera { + public: + // id is used to distinguish cameras. 0 <= id < NUM_CAMERAS. + // module is a handle to the HAL module, used when the device is opened. + Camera(int id); + virtual ~Camera(); + + // Common Camera Device Operations (see <hardware/camera_common.h>) + int open(const hw_module_t *module, hw_device_t **device); + int getInfo(struct camera_info *info); + int close(); + + // Camera v3 Device Operations (see <hardware/camera3.h>) + int initialize(const camera3_callback_ops_t *callback_ops); + int configureStreams(camera3_stream_configuration_t *stream_list); + const camera_metadata_t *constructDefaultRequestSettings(int type); + int processCaptureRequest(camera3_capture_request_t *request); + int flush(); + void dump(int fd); + + // Update static camera characteristics. This method could be called by + // HAL hotplug thread when camera is plugged. + void updateInfo(); + + protected: + // Initialize static camera characteristics. + virtual int initStaticInfo() = 0; + // Verify settings are valid for a capture + virtual bool isValidCaptureSettings(const camera_metadata_t *) = 0; + // Separate open method for individual devices + virtual int openDevice() = 0; + // Separate initialization method for individual devices when opened + virtual int initDevice() = 0; + // Flush camera pipeline for each individual device + virtual int flushDevice() = 0; + // Separate close method for individual devices + virtual int closeDevice() = 0; + // Capture and file an output buffer for an input buffer. + virtual int processCaptureBuffer(const camera3_stream_buffer_t *in, + camera3_stream_buffer_t *out) = 0; + // Accessor method used by initDevice() to set the templates' metadata + int setTemplate(int type, camera_metadata_t *settings); + // Prettyprint template names + const char* templateToString(int type); + // Process an output buffer + + // Identifier used by framework to distinguish cameras + const int mId; + // Metadata containing persistent camera characteristics + Metadata mMetadata; + // camera_metadata structure containing static characteristics + camera_metadata_t *mStaticInfo; + + private: + // Camera device handle returned to framework for use + camera3_device_t mDevice; + // Reuse a stream already created by this device. Must be called with mDeviceLock held. + Stream *reuseStreamLocked(camera3_stream_t *astream); + // Destroy all streams in a stream array, and the array itself. Must be called with + // mDeviceLock held. + void destroyStreamsLocked(android::Vector<Stream *> &streams); + // Verify a set of streams is valid in aggregate. Must be called with mDeviceLock held. + bool isValidStreamSetLocked(const android::Vector<Stream *> &streams); + // Calculate usage and max_bufs of each stream. Must be called with mDeviceLock held. + void setupStreamsLocked(android::Vector<Stream *> &streams); + // Update new settings for re-use and clean up old settings. Must be called with + // mDeviceLock held. + void updateSettingsLocked(const camera_metadata_t *new_settings); + // Send a shutter notify message with start of exposure time + void notifyShutter(uint32_t frame_number, uint64_t timestamp); + // Is type a valid template type (and valid index into mTemplates) + bool isValidTemplateType(int type); + + // Busy flag indicates camera is in use + bool mBusy; + // Camera device operations handle shared by all devices + const static camera3_device_ops_t sOps; + // Methods used to call back into the framework + const camera3_callback_ops_t *mCallbackOps; + // Lock protecting the Camera object for modifications + android::Mutex mDeviceLock; + // Lock protecting only static camera characteristics, which may + // be accessed without the camera device open + android::Mutex mStaticInfoLock; + // Array of handles to streams currently in use by the device + android::Vector<Stream *> mStreams; + // Static array of standard camera settings templates + camera_metadata_t *mTemplates[CAMERA3_TEMPLATE_COUNT]; + // Most recent request settings seen, memoized to be reused + camera_metadata_t *mSettings; + bool mIsInitialized; +}; +} // namespace usb_camera_hal + +#endif // CAMERA_H_ diff --git a/modules/usbcamera/CameraHAL.cpp b/modules/usbcamera/CameraHAL.cpp new file mode 100644 index 00000000..652e937b --- /dev/null +++ b/modules/usbcamera/CameraHAL.cpp @@ -0,0 +1,155 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "UsbCameraHAL" +#include <cutils/log.h> + +#include <cstdlib> +#include <hardware/camera_common.h> +#include <hardware/hardware.h> +#include "UsbCamera.h" +#include "CameraHAL.h" + +/* + * This file serves as the entry point to the HAL. It contains the module + * structure and functions used by the framework to load and interface to this + * HAL, as well as the handles to the individual camera devices. + */ + +namespace usb_camera_hal { + +static CameraHAL gCameraHAL; + +CameraHAL::CameraHAL() + : mCallbacks(NULL) { + // Should not allocate the camera devices for now, as it is unclear if the device is plugged. + + // Start hotplug thread + mHotplugThread = new HotplugThread(this); + mHotplugThread->run("usb-camera-hotplug"); +} + +CameraHAL::~CameraHAL() { + // Stop hotplug thread + { + android::Mutex::Autolock al(mModuleLock); + if (mHotplugThread != NULL) { + mHotplugThread->requestExit(); + } + + // Delete camera device from mCameras + } + + // Joining done without holding mLock, otherwise deadlocks may ensue + // as the threads try to access parent states. + if (mHotplugThread != NULL) { + mHotplugThread->join(); + } + + delete mHotplugThread; +} + +int CameraHAL::getNumberOfCameras() { + android::Mutex::Autolock al(mModuleLock); + ALOGV("%s: %d", __func__, mCameras.size()); + return static_cast<int>(mCameras.size()); +} + +int CameraHAL::getCameraInfo(int id, struct camera_info* info) { + android::Mutex::Autolock al(mModuleLock); + ALOGV("%s: camera id %d: info=%p", __func__, id, info); + if (id < 0 || id >= static_cast<int>(mCameras.size())) { + ALOGE("%s: Invalid camera id %d", __func__, id); + return -ENODEV; + } + + return mCameras[id]->getInfo(info); +} + +int CameraHAL::setCallbacks(const camera_module_callbacks_t *callbacks) { + ALOGV("%s : callbacks=%p", __func__, callbacks); + mCallbacks = callbacks; + return 0; +} + +int CameraHAL::open(const hw_module_t* mod, const char* name, hw_device_t** dev) { + int id; + char *nameEnd; + + android::Mutex::Autolock al(mModuleLock); + ALOGV("%s: module=%p, name=%s, device=%p", __func__, mod, name, dev); + if (*name == '\0') { + ALOGE("%s: Invalid camera id name is NULL", __func__); + return -EINVAL; + } + id = strtol(name, &nameEnd, 10); + if (*nameEnd != '\0') { + ALOGE("%s: Invalid camera id name %s", __func__, name); + return -EINVAL; + } else if (id < 0 || id >= static_cast<int>(mCameras.size())) { + ALOGE("%s: Invalid camera id %d", __func__, id); + return -ENODEV; + } + return mCameras[id]->open(mod, dev); +} + +extern "C" { + +static int get_number_of_cameras() { + return gCameraHAL.getNumberOfCameras(); +} + +static int get_camera_info(int id, struct camera_info* info) { + return gCameraHAL.getCameraInfo(id, info); +} + +static int set_callbacks(const camera_module_callbacks_t *callbacks) { + return gCameraHAL.setCallbacks(callbacks); +} + +static int open_dev(const hw_module_t* mod, const char* name, hw_device_t** dev) { + return gCameraHAL.open(mod, name, dev); +} + +static hw_module_methods_t gCameraModuleMethods = { + open : open_dev +}; + +camera_module_t HAL_MODULE_INFO_SYM __attribute__ ((visibility("default"))) = { + common : { + tag : HARDWARE_MODULE_TAG, + module_api_version : CAMERA_MODULE_API_VERSION_2_4, + hal_api_version : HARDWARE_HAL_API_VERSION, + id : CAMERA_HARDWARE_MODULE_ID, + name : "Default USB Camera HAL", + author : "The Android Open Source Project", + methods : &gCameraModuleMethods, + dso : NULL, + reserved : {0}, + }, + get_number_of_cameras : get_number_of_cameras, + get_camera_info : get_camera_info, + set_callbacks : set_callbacks, + get_vendor_tag_ops : NULL, + open_legacy : NULL, + set_torch_mode : NULL, + init : NULL, + reserved : {0}, +}; +} // extern "C" + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/CameraHAL.h b/modules/usbcamera/CameraHAL.h new file mode 100644 index 00000000..1770d957 --- /dev/null +++ b/modules/usbcamera/CameraHAL.h @@ -0,0 +1,60 @@ +/* + * 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 CAMERA_HAL_H_ +#define CAMERA_HAL_H_ + +#include <hardware/hardware.h> +#include <hardware/camera_common.h> +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include "HotplugThread.h" +#include "Camera.h" + +namespace usb_camera_hal { + +class HotplugThread; + +/** + * CameraHAL contains all module state that isn't specific to an individual camera device + */ +class CameraHAL { + public: + CameraHAL(); + ~CameraHAL(); + + // Camera Module Interface (see <hardware/camera_common.h>) + int getNumberOfCameras(); + int getCameraInfo(int camera_id, struct camera_info *info); + int setCallbacks(const camera_module_callbacks_t *callbacks); + void getVendorTagOps(vendor_tag_ops_t* ops); + + // Hardware Module Interface (see <hardware/hardware.h>) + int open(const hw_module_t* mod, const char* name, hw_device_t** dev); + + private: + // Callback handle + const camera_module_callbacks_t *mCallbacks; + android::Vector<Camera*> mCameras; + // Lock to protect the module method calls. + android::Mutex mModuleLock; + // Hot plug thread managing camera hot plug. + HotplugThread *mHotplugThread; + +}; +} // namespace usb_camera_hal + +#endif // CAMERA_HAL_H_ diff --git a/modules/usbcamera/HotplugThread.cpp b/modules/usbcamera/HotplugThread.cpp new file mode 100644 index 00000000..6c650861 --- /dev/null +++ b/modules/usbcamera/HotplugThread.cpp @@ -0,0 +1,59 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "HotplugThread" +#include <cutils/log.h> + +#include "HotplugThread.h" + +namespace usb_camera_hal { + +HotplugThread::HotplugThread(CameraHAL *hal) + : mModule(hal) { + +} + +HotplugThread::~HotplugThread() { + +} + +void HotplugThread::requestExit() { + // Call parent to set up shutdown + Thread::requestExit(); + + // Cleanup other states? +} + +bool HotplugThread::threadLoop() { + + /** + * Check camera connection status change, if connected, do below: + * 1. Create camera device, add to mCameras. + * 2. Init static info (mCameras[id]->initStaticInfo()) + * 3. Notify on_status_change callback + * + * If unconnected, similarly, do below: + * 1. Destroy camera device and remove it from mCameras. + * 2. Notify on_status_change callback + * + * DO NOT have a tight polling loop here, to avoid excessive CPU utilization. + */ + + return true; +} + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/HotplugThread.h b/modules/usbcamera/HotplugThread.h new file mode 100644 index 00000000..a13adb79 --- /dev/null +++ b/modules/usbcamera/HotplugThread.h @@ -0,0 +1,52 @@ +/* + * 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 HOTPLUG_THREAD_H_ +#define HOTPLUG_THREAD_H_ + +#include <utils/Thread.h> +#include "CameraHAL.h" + +namespace usb_camera_hal { +/** + * Thread for managing usb camera hotplug. It does below: + * 1. Monitor camera hotplug status, and notify the status changes by calling + * module callback methods. + * 2. When camera is plugged, create camera device instance, initialize the camera + * static info. When camera is unplugged, destroy the camera device instance and + * static metadata. As an optimization option, the camera device instance (including + * the static info) could be cached when the same camera plugged/unplugged multiple + * times. + */ + +class CameraHAL; + +class HotplugThread : public android::Thread { + + public: + HotplugThread(CameraHAL *hal); + ~HotplugThread(); + + // Override below two methods for proper cleanup. + virtual bool threadLoop(); + virtual void requestExit(); + + private: + CameraHAL *mModule; +}; + +} // namespace usb_camera_hal + +#endif // HOTPLUG_THREAD_H_ diff --git a/modules/usbcamera/Metadata.cpp b/modules/usbcamera/Metadata.cpp new file mode 100644 index 00000000..f243834f --- /dev/null +++ b/modules/usbcamera/Metadata.cpp @@ -0,0 +1,160 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "Metadata" +#include <cutils/log.h> + +#include <system/camera_metadata.h> + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <utils/Trace.h> + +#include "Metadata.h" + +namespace usb_camera_hal { + +Metadata::Metadata(): + mData(NULL) { +} + +Metadata::~Metadata() { + replace(NULL); +} + +void Metadata::replace(camera_metadata_t *m) { + if (m == mData) { + return; + } + if (mData) + free_camera_metadata(mData); + mData = m; +} + +int Metadata::init(const camera_metadata_t *metadata) { + camera_metadata_t* tmp; + + if (!validate_camera_metadata_structure(metadata, NULL)) + return -EINVAL; + + tmp = clone_camera_metadata(metadata); + if (tmp == NULL) + return -EINVAL; + + replace(tmp); + return 0; +} + +int Metadata::addUInt8(uint32_t tag, int count, const uint8_t *data) { + if (!validate(tag, TYPE_BYTE, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::add1UInt8(uint32_t tag, const uint8_t data) { + return addUInt8(tag, 1, &data); +} + +int Metadata::addInt32(uint32_t tag, int count, const int32_t *data) { + if (!validate(tag, TYPE_INT32, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addFloat(uint32_t tag, int count, const float *data) { + if (!validate(tag, TYPE_FLOAT, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addInt64(uint32_t tag, int count, const int64_t *data) { + if (!validate(tag, TYPE_INT64, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addDouble(uint32_t tag, int count, const double *data) { + if (!validate(tag, TYPE_DOUBLE, count)) return -EINVAL; + return add(tag, count, data); +} + +int Metadata::addRational(uint32_t tag, int count, + const camera_metadata_rational_t *data) { + if (!validate(tag, TYPE_RATIONAL, count)) return -EINVAL; + return add(tag, count, data); +} + +bool Metadata::validate(uint32_t tag, int tag_type, int count) { + if (get_camera_metadata_tag_type(tag) < 0) { + ALOGE("%s: Invalid metadata entry tag: %d", __func__, tag); + return false; + } + if (tag_type < 0 || tag_type >= NUM_TYPES) { + ALOGE("%s: Invalid metadata entry tag type: %d", __func__, tag_type); + return false; + } + if (tag_type != get_camera_metadata_tag_type(tag)) { + ALOGE("%s: Tag %d called with incorrect type: %s(%d)", __func__, tag, + camera_metadata_type_names[tag_type], tag_type); + return false; + } + if (count < 1) { + ALOGE("%s: Invalid metadata entry count: %d", __func__, count); + return false; + } + return true; +} + +int Metadata::add(uint32_t tag, int count, const void *tag_data) { + // Opportunistically attempt to add if metadata has room for it + if (!add_camera_metadata_entry(mData, tag, tag_data, count)) { + return 0; + } + + int res; + camera_metadata_t* tmp; + int tag_type = get_camera_metadata_tag_type(tag); + size_t size = calculate_camera_metadata_entry_data_size(tag_type, count); + size_t entry_capacity = get_camera_metadata_entry_count(mData) + 1; + size_t data_capacity = get_camera_metadata_data_count(mData) + size; + + // Double new dimensions to minimize future reallocations + tmp = allocate_camera_metadata(entry_capacity * 2, data_capacity * 2); + if (tmp == NULL) { + ALOGE("%s: Failed to allocate new metadata with %zu entries, %zu data", + __func__, entry_capacity, data_capacity); + return -ENOMEM; + } + // Append the current metadata to the new (empty) metadata + res = append_camera_metadata(tmp, mData); + if (res) { + ALOGE("%s: Failed to append old metadata %p to new %p", + __func__, mData, tmp); + return res; + } + // Add the remaining new item + res = add_camera_metadata_entry(tmp, tag, tag_data, count); + if (res) { + ALOGE("%s: Failed to add new entry (%d, %p, %d) to metadata %p", + __func__, tag, tag_data, count, tmp); + return res; + } + + replace(tmp); + return 0; +} + +camera_metadata_t* Metadata::get() { + return mData; +} + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/Metadata.h b/modules/usbcamera/Metadata.h new file mode 100644 index 00000000..288db16a --- /dev/null +++ b/modules/usbcamera/Metadata.h @@ -0,0 +1,59 @@ +/* + * 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 METADATA_H_ +#define METADATA_H_ + +#include <stdint.h> +#include <hardware/camera3.h> +#include <system/camera_metadata.h> + +namespace usb_camera_hal { +// Metadata is a convenience class for dealing with libcamera_metadata +class Metadata { + public: + Metadata(); + ~Metadata(); + // Initialize with framework metadata + int init(const camera_metadata_t *metadata); + + // Parse and add an entry. Allocates and copies new storage for *data. + int addUInt8(uint32_t tag, int count, const uint8_t *data); + int add1UInt8(uint32_t tag, const uint8_t data); + int addInt32(uint32_t tag, int count, const int32_t *data); + int addFloat(uint32_t tag, int count, const float *data); + int addInt64(uint32_t tag, int count, const int64_t *data); + int addDouble(uint32_t tag, int count, const double *data); + int addRational(uint32_t tag, int count, + const camera_metadata_rational_t *data); + + // Get a handle to the current metadata + // This is not a durable handle, and may be destroyed by add*/init + camera_metadata_t* get(); + + private: + // Actual internal storage + camera_metadata_t* mData; + // Destroy old metadata and replace with new + void replace(camera_metadata_t *m); + // Validate the tag, type and count for a metadata entry + bool validate(uint32_t tag, int tag_type, int count); + // Add a verified tag with data + int add(uint32_t tag, int count, const void *tag_data); +}; +} // namespace usb_camera_hal + +#endif // METADATA_H_ diff --git a/modules/usbcamera/Stream.cpp b/modules/usbcamera/Stream.cpp new file mode 100644 index 00000000..f56866ea --- /dev/null +++ b/modules/usbcamera/Stream.cpp @@ -0,0 +1,180 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "Stream" +#include <cutils/log.h> + +#include <stdio.h> +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <system/graphics.h> +#include <utils/Mutex.h> + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <utils/Trace.h> + +#include "Stream.h" + +namespace usb_camera_hal { + +Stream::Stream(int id, camera3_stream_t *s) + : mReuse(false), + mId(id), + mStream(s){ +} + +Stream::~Stream() { + for (size_t i = 0; i < mBuffers.size(); i++) { + delete mBuffers[i]; + } + + mBuffers.clear(); +} + +void Stream::setUsage(uint32_t usage) { + android::Mutex::Autolock al(mLock); + if (usage != mStream->usage) { + mStream->usage = usage; + } +} + +void Stream::setMaxBuffers(uint32_t max_buffers) { + android::Mutex::Autolock al(mLock); + if (max_buffers != mStream->max_buffers) { + mStream->max_buffers = max_buffers; + } +} + +int Stream::getType() { + return mStream->stream_type; +} + +bool Stream::isInputType() { + return mStream->stream_type == CAMERA3_STREAM_INPUT || + mStream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL; +} + +bool Stream::isOutputType() { + return mStream->stream_type == CAMERA3_STREAM_OUTPUT || + mStream->stream_type == CAMERA3_STREAM_BIDIRECTIONAL; +} + +const char* Stream::typeToString(int type) { + switch (type) { + case CAMERA3_STREAM_INPUT: + return "CAMERA3_STREAM_INPUT"; + case CAMERA3_STREAM_OUTPUT: + return "CAMERA3_STREAM_OUTPUT"; + case CAMERA3_STREAM_BIDIRECTIONAL: + return "CAMERA3_STREAM_BIDIRECTIONAL"; + } + return "Invalid stream type!"; +} + +const char* Stream::formatToString(int format) { + // See <system/graphics.h> for full list + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + return "BGRA 8888"; + case HAL_PIXEL_FORMAT_RGBA_8888: + return "RGBA 8888"; + case HAL_PIXEL_FORMAT_RGBX_8888: + return "RGBX 8888"; + case HAL_PIXEL_FORMAT_RGB_888: + return "RGB 888"; + case HAL_PIXEL_FORMAT_RGB_565: + return "RGB 565"; + case HAL_PIXEL_FORMAT_Y8: + return "Y8"; + case HAL_PIXEL_FORMAT_Y16: + return "Y16"; + case HAL_PIXEL_FORMAT_YV12: + return "YV12"; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + return "NV16"; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return "NV21"; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return "YUY2"; + case HAL_PIXEL_FORMAT_RAW10: + return "RAW10"; + case HAL_PIXEL_FORMAT_RAW16: + return "RAW16"; + case HAL_PIXEL_FORMAT_BLOB: + return "BLOB"; + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + return "IMPLEMENTATION DEFINED"; + case HAL_PIXEL_FORMAT_YCbCr_420_888: + return "FLEXIBLE YCbCr 420 888"; + } + return "Invalid stream format!"; +} + +bool Stream::isValidReuseStream(int id, camera3_stream_t *s) { + if (id != mId) { + ALOGE("%s:%d: Invalid camera id for reuse. Got %d expect %d", + __func__, mId, id, mId); + return false; + } + if (s != mStream) { + ALOGE("%s:%d: Invalid stream handle for reuse. Got %p expect %p", + __func__, mId, s, mStream); + return false; + } + if (s->stream_type != mStream->stream_type) { + ALOGE("%s:%d: Mismatched type in reused stream. Got %s(%d) " + "expect %s(%d)", __func__, mId, typeToString(s->stream_type), + s->stream_type, typeToString(mStream->stream_type), mStream->stream_type); + return false; + } + if (s->format != mStream->format) { + ALOGE("%s:%d: Mismatched format in reused stream. Got %s(%d) " + "expect %s(%d)", __func__, mId, formatToString(s->format), + s->format, formatToString(mStream->format), mStream->format); + return false; + } + if (s->width != mStream->width) { + ALOGE("%s:%d: Mismatched width in reused stream. Got %d expect %d", + __func__, mId, s->width, mStream->width); + return false; + } + if (s->height != mStream->height) { + ALOGE("%s:%d: Mismatched height in reused stream. Got %d expect %d", + __func__, mId, s->height, mStream->height); + return false; + } + return true; +} + +void Stream::dump(int fd) { + android::Mutex::Autolock al(mLock); + + dprintf(fd, "Stream ID: %d (%p)\n", mId, mStream); + dprintf(fd, "Stream Type: %s (%d)\n", typeToString(mStream->stream_type), mStream->stream_type); + dprintf(fd, "Width: %" PRIu32 " Height: %" PRIu32 "\n", mStream->width, mStream->height); + dprintf(fd, "Stream Format: %s (%d)", formatToString(mStream->format), mStream->format); + // ToDo: prettyprint usage mask flags + dprintf(fd, "Gralloc Usage Mask: %#" PRIx32 "\n", mStream->usage); + dprintf(fd, "Max Buffer Count: %" PRIu32 "\n", mStream->max_buffers); + dprintf(fd, "Number of Buffers in use by HAL: %" PRIu32 "\n", mBuffers.size()); + for (size_t i = 0; i < mBuffers.size(); i++) { + dprintf(fd, "Buffer %" PRIu32 "/%" PRIu32 ": %p\n", i, mBuffers.size(), + mBuffers[i]); + } +} + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/Stream.h b/modules/usbcamera/Stream.h new file mode 100644 index 00000000..022ca9f9 --- /dev/null +++ b/modules/usbcamera/Stream.h @@ -0,0 +1,61 @@ +/* + * 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 STREAM_H_ +#define STREAM_H_ + +#include <hardware/camera3.h> +#include <hardware/gralloc.h> +#include <system/graphics.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> + +namespace usb_camera_hal { +// Stream represents a single input or output stream for a camera device. +class Stream { + public: + Stream(int id, camera3_stream_t *s); + ~Stream(); + + // validate that a stream's parameters match this stream's parameters + bool isValidReuseStream(int id, camera3_stream_t *s); + + void setUsage(uint32_t usage); + void setMaxBuffers(uint32_t max_buffers); + + int getType(); + bool isInputType(); + bool isOutputType(); + const char* typeToString(int type); + const char* formatToString(int format); + void dump(int fd); + + // This stream is being reused. Used in stream configuration passes + bool mReuse; + + private: + // The camera device id this stream belongs to + const int mId; + // Handle to framework's stream, used as a cookie for buffers + camera3_stream_t *mStream; + // Array of handles to buffers currently in use by the stream + android::Vector<buffer_handle_t *> mBuffers; + // Lock protecting the Stream object for modifications + android::Mutex mLock; +}; +} // namespace usb_camera_hal + +#endif // STREAM_H_ diff --git a/modules/usbcamera/UsbCamera.cpp b/modules/usbcamera/UsbCamera.cpp new file mode 100644 index 00000000..82a1145c --- /dev/null +++ b/modules/usbcamera/UsbCamera.cpp @@ -0,0 +1,321 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "UsbCameraDevice" +#include <cutils/log.h> + +#include <system/camera_metadata.h> + +#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) +#include <utils/Trace.h> + +#include "Camera.h" +#include "UsbCamera.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +namespace usb_camera_hal { + +UsbCamera::UsbCamera(int id) : Camera(id) { +} + +UsbCamera::~UsbCamera() { +} + +int UsbCamera::initStaticInfo() { + /* + * Setup static camera info. This will have to customized per camera + * device. + * TODO: this is just some sample code, need tailor for USB cameras. + */ + if (mStaticInfo != NULL) { + free_camera_metadata(mStaticInfo); + } + + Metadata m; + + /* android.control */ + int32_t android_control_ae_available_target_fps_ranges[] = {30, 30}; + m.addInt32(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, + ARRAY_SIZE(android_control_ae_available_target_fps_ranges), + android_control_ae_available_target_fps_ranges); + + int32_t android_control_ae_compensation_range[] = {-4, 4}; + m.addInt32(ANDROID_CONTROL_AE_COMPENSATION_RANGE, + ARRAY_SIZE(android_control_ae_compensation_range), + android_control_ae_compensation_range); + + camera_metadata_rational_t android_control_ae_compensation_step[] = {{2,1}}; + m.addRational(ANDROID_CONTROL_AE_COMPENSATION_STEP, + ARRAY_SIZE(android_control_ae_compensation_step), + android_control_ae_compensation_step); + + int32_t android_control_max_regions[] = {/*AE*/ 1,/*AWB*/ 1,/*AF*/ 1}; + m.addInt32(ANDROID_CONTROL_MAX_REGIONS, + ARRAY_SIZE(android_control_max_regions), + android_control_max_regions); + + /* android.jpeg */ + int32_t android_jpeg_available_thumbnail_sizes[] = {0, 0, 128, 96}; + m.addInt32(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, + ARRAY_SIZE(android_jpeg_available_thumbnail_sizes), + android_jpeg_available_thumbnail_sizes); + + int32_t android_jpeg_max_size[] = {13 * 1024 * 1024}; // 13MB + m.addInt32(ANDROID_JPEG_MAX_SIZE, + ARRAY_SIZE(android_jpeg_max_size), + android_jpeg_max_size); + + /* android.lens */ + float android_lens_info_available_focal_lengths[] = {1.0}; + m.addFloat(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, + ARRAY_SIZE(android_lens_info_available_focal_lengths), + android_lens_info_available_focal_lengths); + + /* android.request */ + int32_t android_request_max_num_output_streams[] = {0, 3, 1}; + m.addInt32(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, + ARRAY_SIZE(android_request_max_num_output_streams), + android_request_max_num_output_streams); + + /* android.scaler */ + int32_t android_scaler_available_formats[] = { + HAL_PIXEL_FORMAT_RAW16, + HAL_PIXEL_FORMAT_BLOB, + HAL_PIXEL_FORMAT_RGBA_8888, + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + // These are handled by YCbCr_420_888 + // HAL_PIXEL_FORMAT_YV12, + // HAL_PIXEL_FORMAT_YCrCb_420_SP, + HAL_PIXEL_FORMAT_YCbCr_420_888}; + m.addInt32(ANDROID_SCALER_AVAILABLE_FORMATS, + ARRAY_SIZE(android_scaler_available_formats), + android_scaler_available_formats); + + int64_t android_scaler_available_jpeg_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_jpeg_min_durations), + android_scaler_available_jpeg_min_durations); + + int32_t android_scaler_available_jpeg_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, + ARRAY_SIZE(android_scaler_available_jpeg_sizes), + android_scaler_available_jpeg_sizes); + + float android_scaler_available_max_digital_zoom[] = {1}; + m.addFloat(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, + ARRAY_SIZE(android_scaler_available_max_digital_zoom), + android_scaler_available_max_digital_zoom); + + int64_t android_scaler_available_processed_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_processed_min_durations), + android_scaler_available_processed_min_durations); + + int32_t android_scaler_available_processed_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, + ARRAY_SIZE(android_scaler_available_processed_sizes), + android_scaler_available_processed_sizes); + + int64_t android_scaler_available_raw_min_durations[] = {1}; + m.addInt64(ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS, + ARRAY_SIZE(android_scaler_available_raw_min_durations), + android_scaler_available_raw_min_durations); + + int32_t android_scaler_available_raw_sizes[] = {640, 480}; + m.addInt32(ANDROID_SCALER_AVAILABLE_RAW_SIZES, + ARRAY_SIZE(android_scaler_available_raw_sizes), + android_scaler_available_raw_sizes); + + /* android.sensor */ + + int32_t android_sensor_info_active_array_size[] = {0, 0, 640, 480}; + m.addInt32(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, + ARRAY_SIZE(android_sensor_info_active_array_size), + android_sensor_info_active_array_size); + + int32_t android_sensor_info_sensitivity_range[] = + {100, 1600}; + m.addInt32(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE, + ARRAY_SIZE(android_sensor_info_sensitivity_range), + android_sensor_info_sensitivity_range); + + int64_t android_sensor_info_max_frame_duration[] = {30000000000}; + m.addInt64(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, + ARRAY_SIZE(android_sensor_info_max_frame_duration), + android_sensor_info_max_frame_duration); + + float android_sensor_info_physical_size[] = {3.2, 2.4}; + m.addFloat(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, + ARRAY_SIZE(android_sensor_info_physical_size), + android_sensor_info_physical_size); + + int32_t android_sensor_info_pixel_array_size[] = {640, 480}; + m.addInt32(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, + ARRAY_SIZE(android_sensor_info_pixel_array_size), + android_sensor_info_pixel_array_size); + + int32_t android_sensor_orientation[] = {0}; + m.addInt32(ANDROID_SENSOR_ORIENTATION, + ARRAY_SIZE(android_sensor_orientation), + android_sensor_orientation); + + /* End of static camera characteristics */ + + mStaticInfo = clone_camera_metadata(m.get()); + + return 0; +} + +int UsbCamera::openDevice() { + // TODO: implement usb camera device open sequence: open device nodes etc. + + return 0; +} + +int UsbCamera::closeDevice() { + // TODO: implement usb camera device close sequence: close device nodes etc. + + return 0; +} + +int UsbCamera::processCaptureBuffer(const camera3_stream_buffer_t *in, + camera3_stream_buffer_t *out) { + if (in->acquire_fence != -1) { + int res = sync_wait(in->acquire_fence, CAMERA_SYNC_TIMEOUT_MS); + if (res == -ETIME) { + ALOGE("%s:%d: Timeout waiting on buffer acquire fence", + __func__, mId); + return res; + } else if (res) { + ALOGE("%s:%d: Error waiting on buffer acquire fence: %s(%d)", + __func__, mId, strerror(-res), res); + return res; + } + } + + out->stream = in->stream; + out->buffer = in->buffer; + out->status = CAMERA3_BUFFER_STATUS_OK; + // TODO: use driver-backed release fences + out->acquire_fence = -1; + out->release_fence = -1; + + // TODO: lock and software-paint buffer + return 0; +} + +int UsbCamera::initDevice() { + int res; + Metadata base; + + // Create standard settings templates from copies of base metadata + res = base.add1UInt8(ANDROID_CONTROL_MODE, ANDROID_CONTROL_MODE_OFF); + if (res) + return res; + + // Use base settings to create all other templates and set them. This is just some samples, + // More initialization may be needed. + res = initPreviewTemplate(base); + if (res) + return res; + res = initStillTemplate(base); + if (res) + return res; + res = initRecordTemplate(base); + if (res) + return res; + res = initSnapshotTemplate(base); + if (res) + return res; + res = initZslTemplate(base); + if (res) + return res; + res = initManualTemplate(base); + if (res) + return res; + + return 0; +} + +int UsbCamera::initPreviewTemplate(Metadata m) { + // Setup default preview controls + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW); + + if (res) + return res; + // TODO: set fast auto-focus, auto-whitebalance, auto-exposure, auto flash + return setTemplate(CAMERA3_TEMPLATE_PREVIEW, m.get()); +} + +int UsbCamera::initStillTemplate(Metadata m) { + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE); + // Setup default still capture controls + if (res) + return res; + // TODO: set fast auto-focus, auto-whitebalance, auto-exposure, auto flash + return setTemplate(CAMERA3_TEMPLATE_STILL_CAPTURE, m.get()); +} + +int UsbCamera::initRecordTemplate(Metadata m) { + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + // Setup default video record controls + if (res) + return res; + // TODO: set slow auto-focus, auto-whitebalance, auto-exposure, flash off + return setTemplate(CAMERA3_TEMPLATE_VIDEO_RECORD, m.get()); +} + +int UsbCamera::initSnapshotTemplate(Metadata m) { + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT); + // Setup default video snapshot controls + if (res) + return res; + // TODO: set slow auto-focus, auto-whitebalance, auto-exposure, flash off + return setTemplate(CAMERA3_TEMPLATE_VIDEO_SNAPSHOT, m.get()); +} + +int UsbCamera::initZslTemplate(Metadata m) { + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG); + // Setup default zero shutter lag controls + if (res) + return res; + // TODO: set reprocessing parameters for zsl input queue + return setTemplate(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, m.get()); +} + +int UsbCamera::initManualTemplate(Metadata m) { + int res = m.add1UInt8(ANDROID_CONTROL_CAPTURE_INTENT, + ANDROID_CONTROL_CAPTURE_INTENT_MANUAL); + // Setup manual controls + if (res) + return res; + // TODO: set reprocessing parameters for zsl input queue + return setTemplate(CAMERA3_TEMPLATE_MANUAL, m.get()); +} + +bool UsbCamera::isValidCaptureSettings(const camera_metadata_t* settings) { + // TODO: reject settings that cannot be captured + return true; +} + +} // namespace usb_camera_hal diff --git a/modules/usbcamera/UsbCamera.h b/modules/usbcamera/UsbCamera.h new file mode 100644 index 00000000..fe52ade3 --- /dev/null +++ b/modules/usbcamera/UsbCamera.h @@ -0,0 +1,55 @@ +/* + * 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 EXAMPLE_CAMERA_H_ +#define EXAMPLE_CAMERA_H_ + +#include <system/camera_metadata.h> +#include "Camera.h" + +namespace usb_camera_hal { +/** + * UsbCamera is an example for a specific camera device. The Camera instance contains + * a specific camera device (e.g. UsbCamera) holds all specific metadata and logic about + * that device. + */ +class UsbCamera : public Camera { + public: + UsbCamera(int id); + ~UsbCamera(); + + private: + // Initialize static camera characteristics for individual device + int initStaticInfo(); + int openDevice(); + // Initialize whole device (templates/etc) when opened + int initDevice(); + int flushDevice(); + int closeDevice(); + int processCaptureBuffer(const camera3_stream_buffer_t *in, camera3_stream_buffer_t *out); + // Initialize each template metadata controls + int initPreviewTemplate(Metadata m); + int initStillTemplate(Metadata m); + int initRecordTemplate(Metadata m); + int initSnapshotTemplate(Metadata m); + int initZslTemplate(Metadata m); + int initManualTemplate(Metadata m); + // Verify settings are valid for a capture with this device + bool isValidCaptureSettings(const camera_metadata_t* settings); +}; +} // namespace usb_camera_hal + +#endif // CAMERA_H_ diff --git a/tests/camera2/Android.mk b/tests/camera2/Android.mk index 11285227..e45f467c 100644 --- a/tests/camera2/Android.mk +++ b/tests/camera2/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - camera2.cpp \ camera2_utils.cpp \ main.cpp \ CameraMetadataTests.cpp \ diff --git a/tests/camera2/CameraMetadataTests.cpp b/tests/camera2/CameraMetadataTests.cpp index 94fa911a..da5b7483 100644 --- a/tests/camera2/CameraMetadataTests.cpp +++ b/tests/camera2/CameraMetadataTests.cpp @@ -169,7 +169,7 @@ TEST_F(CameraMetadataTest, SaneResolutions) { if (rawResolutionsCount > 0) { EXPECT_TRUE( HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS, - HAL_PIXEL_FORMAT_RAW_SENSOR)); + HAL_PIXEL_FORMAT_RAW16)); } // Required processed sizes. diff --git a/tests/camera2/CameraModuleFixture.h b/tests/camera2/CameraModuleFixture.h index 0bb0e7d7..3a2df69d 100644 --- a/tests/camera2/CameraModuleFixture.h +++ b/tests/camera2/CameraModuleFixture.h @@ -22,6 +22,7 @@ #include "hardware/hardware.h" #include "hardware/camera2.h" +#include <common/CameraModule.h> #include <device2/Camera2Device.h> #include <device3/Camera3Device.h> @@ -54,15 +55,17 @@ struct CameraModuleFixture { void SetUp() { TEST_EXTENSION_FORKING_SET_UP; + camera_module_t *rawModule; ASSERT_LE(0, hw_get_module(CAMERA_HARDWARE_MODULE_ID, - (const hw_module_t **)&mModule)) << "Could not load camera module"; - ASSERT_NE((void*)0, mModule); + (const hw_module_t **)&rawModule)) << "Could not load camera module"; + ASSERT_NE((void*)0, rawModule); + mModule = new CameraModule(rawModule); - mNumberOfCameras = mModule->get_number_of_cameras(); + mNumberOfCameras = mModule->getNumberOfCameras(); ASSERT_LE(0, mNumberOfCameras); ASSERT_LE( - CAMERA_MODULE_API_VERSION_2_0, mModule->common.module_api_version) + CAMERA_MODULE_API_VERSION_2_0, mModule->getModuleApiVersion()) << "Wrong module API version"; /* For using this fixture in other tests only */ @@ -72,6 +75,7 @@ struct CameraModuleFixture { void TearDown() { TEST_EXTENSION_FORKING_TEAR_DOWN; + delete mModule; TearDownMixin(); /* important: device must be destructed before closing module, @@ -79,14 +83,14 @@ struct CameraModuleFixture { mDevice.clear(); if (!TEST_EXTENSION_FORKING_ENABLED) { - ASSERT_EQ(0, HWModuleHelpers::closeModule(&mModule->common)) + ASSERT_EQ(0, HWModuleHelpers::closeModule(mModule->getDso())) << "Failed to close camera HAL module"; } } void CreateCamera(int cameraID, /*out*/ sp<CameraDeviceBase> *device) { struct camera_info info; - ASSERT_EQ(OK, mModule->get_camera_info(cameraID, &info)); + ASSERT_EQ(OK, mModule->getCameraInfo(cameraID, &info)); ASSERT_GE((int)info.device_version, CAMERA_DEVICE_API_VERSION_2_0) << "Device version too old for camera " << cameraID << ". Version: " << @@ -116,7 +120,7 @@ struct CameraModuleFixture { int getDeviceVersion(int cameraId, status_t* status = NULL) { camera_info info; status_t res; - res = mModule->get_camera_info(cameraId, &info); + res = mModule->getCameraInfo(cameraId, &info); if (status != NULL) *status = res; return info.device_version; @@ -147,7 +151,7 @@ private: protected: int mNumberOfCameras; - camera_module_t *mModule; + CameraModule *mModule; sp<CameraDeviceBase> mDevice; private: diff --git a/tests/camera2/CameraModuleTests.cpp b/tests/camera2/CameraModuleTests.cpp index 828c56a1..2b6c7576 100644 --- a/tests/camera2/CameraModuleTests.cpp +++ b/tests/camera2/CameraModuleTests.cpp @@ -93,11 +93,7 @@ TEST_F(CameraModuleTest, LoadModuleBadIndices) { for (unsigned i = 0; i < sizeof(idx)/sizeof(idx[0]); ++i) { String8 deviceName = String8::format("%d", idx[i]); - status_t res = - mModule->common.methods->open( - &mModule->common, - deviceName, - &device); + status_t res = mModule->open(deviceName, &device); EXPECT_NE(OK, res); EXPECT_EQ(-ENODEV, res) << "Incorrect error code when trying to open camera with invalid id " @@ -111,7 +107,7 @@ TEST_F(CameraModuleTest, GetCameraInfo) { for (int i = 0; i < mNumberOfCameras; ++i) { struct camera_info info; - ASSERT_EQ(OK, mModule->get_camera_info(i, &info)); + ASSERT_EQ(OK, mModule->getCameraInfo(i, &info)); } } @@ -123,8 +119,8 @@ TEST_F(CameraModuleTest, GetCameraInfoBadIndices) { int idx[] = { -1, mNumberOfCameras, mNumberOfCameras + 1 }; for (unsigned i = 0; i < sizeof(idx)/sizeof(idx[0]); ++i) { struct camera_info info; - EXPECT_NE(OK, mModule->get_camera_info(idx[i], &info)); - EXPECT_EQ(-ENODEV, mModule->get_camera_info(idx[i], &info)) + EXPECT_NE(OK, mModule->getCameraInfo(idx[i], &info)); + EXPECT_EQ(-EINVAL, mModule->getCameraInfo(idx[i], &info)) << "Incorrect error code for get_camera_info idx= " << idx[i]; } diff --git a/tests/camera2/CameraMultiStreamTests.cpp b/tests/camera2/CameraMultiStreamTests.cpp index df8e623b..3e29ad62 100644 --- a/tests/camera2/CameraMultiStreamTests.cpp +++ b/tests/camera2/CameraMultiStreamTests.cpp @@ -92,7 +92,7 @@ public: sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; - void CreateOnScreenSurface(sp<ANativeWindow>& surface) { + void CreateOnScreenSurface(sp<Surface>& surface) { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); @@ -175,7 +175,7 @@ public: int width, int height, const sp<CameraDeviceBase>& device, - CameraStreamParams param, sp<ANativeWindow> surface, + CameraStreamParams param, sp<Surface> surface, bool useCpuConsumer) : mDevice(device), mWidth(width), @@ -188,11 +188,11 @@ public: mCpuConsumer = new CpuConsumer(consumer, param.mHeapCount); mCpuConsumer->setName(String8( "CameraMultiStreamTest::mCpuConsumer")); - mNativeWindow = new Surface(producer); + mSurface = new Surface(producer); } else { // Render the stream to screen. mCpuConsumer = NULL; - mNativeWindow = surface; + mSurface = surface; } mFrameListener = new FrameListener(); @@ -207,9 +207,9 @@ public: */ void SetUp() { ASSERT_EQ(OK, - mDevice->createStream(mNativeWindow, - mWidth, mHeight, mFormat, - &mStreamId)); + mDevice->createStream(mSurface, + mWidth, mHeight, mFormat, HAL_DATASPACE_UNKNOWN, + CAMERA3_STREAM_ROTATION_0, &mStreamId)); ASSERT_NE(-1, mStreamId); } @@ -225,14 +225,14 @@ public: mDevice->deleteStream(mStreamId); } // Clear producer before consumer. - mNativeWindow.clear(); + mSurface.clear(); mCpuConsumer.clear(); } private: sp<FrameListener> mFrameListener; sp<CpuConsumer> mCpuConsumer; - sp<ANativeWindow> mNativeWindow; + sp<Surface> mSurface; sp<CameraDeviceBase> mDevice; int mStreamId; int mWidth; @@ -334,7 +334,7 @@ public: int height, const sp<CameraDeviceBase>& device, CameraStreamParams param = DEFAULT_STREAM_PARAMETERS, - sp<ANativeWindow> surface = NULL, + sp<Surface> surface = NULL, bool useCpuConsumer = true) { param.mFormat = MapAutoFormat(param.mFormat); return new CameraStream(width, height, device, @@ -528,7 +528,8 @@ TEST_F(CameraMultiStreamTest, DISABLED_MultiBurst) { // Find the right sizes for preview, metering, and capture streams int64_t minFrameDuration = DEFAULT_FRAME_DURATION; - Size processedMinSize, processedMaxSize, jpegMaxSize; + Size processedMinSize = {0, 0}, processedMaxSize = {0, 0}; + Size jpegMaxSize = {0, 0}; int32_t minIdx, maxIdx; GetMinSize(implDefData, implDefCount, &processedMinSize, &minIdx); @@ -582,7 +583,7 @@ TEST_F(CameraMultiStreamTest, DISABLED_MultiBurst) { // Preview stream: small resolution, render on the screen. sp<CameraStream> previewStream; { - sp<ANativeWindow> surface; + sp<Surface> surface; ASSERT_NO_FATAL_FAILURE(CreateOnScreenSurface(/*out*/surface)); previewStream = CreateStream( previewSize.width, diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h index cc131695..fc5fb368 100644 --- a/tests/camera2/CameraStreamFixture.h +++ b/tests/camera2/CameraStreamFixture.h @@ -111,7 +111,7 @@ private: mHeight = entry.data.i32[1]; } else { buildOutputResolutions(); - const int32_t *implDefResolutions; + const int32_t *implDefResolutions = NULL; size_t implDefResolutionsCount; int format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; @@ -131,7 +131,7 @@ private: CameraModuleFixture::TearDown(); deleteOutputResolutions(); - mNativeWindow.clear(); + mSurface.clear(); mCpuConsumer.clear(); mFrameListener.clear(); } @@ -250,13 +250,15 @@ protected: mCpuConsumer = new CpuConsumer(consumer, p.mHeapCount); mCpuConsumer->setName(String8("CameraStreamTest::mCpuConsumer")); - mNativeWindow = new Surface(producer); + mSurface = new Surface(producer); int format = MapAutoFormat(p.mFormat); ASSERT_EQ(OK, - device->createStream(mNativeWindow, + device->createStream(mSurface, mWidth, mHeight, format, + HAL_DATASPACE_UNKNOWN, + CAMERA3_STREAM_ROTATION_0, &mStreamId)); ASSERT_NE(-1, mStreamId); @@ -362,7 +364,7 @@ protected: android::sp<FrameListener> mFrameListener; android::sp<CpuConsumer> mCpuConsumer; - android::sp<ANativeWindow> mNativeWindow; + android::sp<Surface> mSurface; KeyedVector<int32_t, Vector<int32_t>* > mOutputResolutions; private: diff --git a/tests/camera2/CameraStreamTests.cpp b/tests/camera2/CameraStreamTests.cpp index 69ee274d..a3b8c474 100644 --- a/tests/camera2/CameraStreamTests.cpp +++ b/tests/camera2/CameraStreamTests.cpp @@ -166,15 +166,15 @@ static CameraStreamParams TestParameters[] = { /*mHeapCount*/ 3 }, { - /*mFormat*/ HAL_PIXEL_FORMAT_RAW_SENSOR, + /*mFormat*/ HAL_PIXEL_FORMAT_RAW16, /*mHeapCount*/ 1 }, { - /*mFormat*/ HAL_PIXEL_FORMAT_RAW_SENSOR, + /*mFormat*/ HAL_PIXEL_FORMAT_RAW16, /*mHeapCount*/ 2 }, { - /*mFormat*/ HAL_PIXEL_FORMAT_RAW_SENSOR, + /*mFormat*/ HAL_PIXEL_FORMAT_RAW16, /*mHeapCount*/ 3 }, }; diff --git a/tests/camera2/camera2.cpp b/tests/camera2/camera2.cpp deleted file mode 100644 index 72b7b610..00000000 --- a/tests/camera2/camera2.cpp +++ /dev/null @@ -1,872 +0,0 @@ -/* - * Copyright (C) 2012 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 "Camera2_test" -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include <gtest/gtest.h> -#include <iostream> -#include <fstream> - -#include <utils/Vector.h> -#include <utils/KeyedVector.h> -#include <gui/CpuConsumer.h> -#include <ui/PixelFormat.h> -#include <system/camera_metadata.h> - -#include "camera2_utils.h" -#include "TestExtensions.h" - -namespace android { -namespace camera2 { -namespace tests { - -class Camera2Test: public testing::Test { - public: - void SetUpModule() { - int res; - - hw_module_t *module = NULL; - res = hw_get_module(CAMERA_HARDWARE_MODULE_ID, - (const hw_module_t **)&module); - - ASSERT_EQ(0, res) - << "Failure opening camera hardware module: " << res; - ASSERT_TRUE(NULL != module) - << "No camera module was set by hw_get_module"; - - IF_ALOGV() { - std::cout << " Camera module name: " - << module->name << std::endl; - std::cout << " Camera module author: " - << module->author << std::endl; - std::cout << " Camera module API version: 0x" << std::hex - << module->module_api_version << std::endl; - std::cout << " Camera module HAL API version: 0x" << std::hex - << module->hal_api_version << std::endl; - } - - int16_t version2_0 = CAMERA_MODULE_API_VERSION_2_0; - ASSERT_LE(version2_0, module->module_api_version) - << "Camera module version is 0x" - << std::hex << module->module_api_version - << ", should be at least 2.0. (0x" - << std::hex << CAMERA_MODULE_API_VERSION_2_0 << ")"; - - sCameraModule = reinterpret_cast<camera_module_t*>(module); - - sNumCameras = sCameraModule->get_number_of_cameras(); - ASSERT_LT(0, sNumCameras) << "No camera devices available!"; - - IF_ALOGV() { - std::cout << " Camera device count: " << sNumCameras << std::endl; - } - - sCameraSupportsHal2 = new bool[sNumCameras]; - - for (int i = 0; i < sNumCameras; i++) { - camera_info info; - res = sCameraModule->get_camera_info(i, &info); - ASSERT_EQ(0, res) - << "Failure getting camera info for camera " << i; - IF_ALOGV() { - std::cout << " Camera device: " << std::dec - << i << std::endl;; - std::cout << " Facing: " << std::dec - << info.facing << std::endl; - std::cout << " Orientation: " << std::dec - << info.orientation << std::endl; - std::cout << " Version: 0x" << std::hex << - info.device_version << std::endl; - } - if (info.device_version >= CAMERA_DEVICE_API_VERSION_2_0 && - info.device_version < CAMERA_DEVICE_API_VERSION_3_0) { - sCameraSupportsHal2[i] = true; - ASSERT_TRUE(NULL != info.static_camera_characteristics); - IF_ALOGV() { - std::cout << " Static camera metadata:" << std::endl; - dump_indented_camera_metadata(info.static_camera_characteristics, - 0, 1, 6); - } - } else { - sCameraSupportsHal2[i] = false; - } - } - } - - void TearDownModule() { - hw_module_t *module = reinterpret_cast<hw_module_t*>(sCameraModule); - ASSERT_EQ(0, HWModuleHelpers::closeModule(module)); - } - - static const camera_module_t *getCameraModule() { - return sCameraModule; - } - - static int getNumCameras() { - return sNumCameras; - } - - static bool isHal2Supported(int id) { - return sCameraSupportsHal2[id]; - } - - static camera2_device_t *openCameraDevice(int id) { - ALOGV("Opening camera %d", id); - if (NULL == sCameraSupportsHal2) return NULL; - if (id >= sNumCameras) return NULL; - if (!sCameraSupportsHal2[id]) return NULL; - - hw_device_t *device = NULL; - const camera_module_t *cam_module = getCameraModule(); - if (cam_module == NULL) { - return NULL; - } - - char camId[10]; - int res; - - snprintf(camId, 10, "%d", id); - res = cam_module->common.methods->open( - (const hw_module_t*)cam_module, - camId, - &device); - if (res != NO_ERROR || device == NULL) { - return NULL; - } - camera2_device_t *cam_device = - reinterpret_cast<camera2_device_t*>(device); - return cam_device; - } - - static status_t configureCameraDevice(camera2_device_t *dev, - MetadataQueue &requestQueue, - MetadataQueue &frameQueue, - NotifierListener &listener) { - - status_t err; - - err = dev->ops->set_request_queue_src_ops(dev, - requestQueue.getToConsumerInterface()); - if (err != OK) return err; - - requestQueue.setFromConsumerInterface(dev); - - err = dev->ops->set_frame_queue_dst_ops(dev, - frameQueue.getToProducerInterface()); - if (err != OK) return err; - - err = listener.getNotificationsFrom(dev); - if (err != OK) return err; - - return OK; - } - - static status_t closeCameraDevice(camera2_device_t **cam_dev) { - int res; - if (*cam_dev == NULL ) return OK; - - ALOGV("Closing camera %p", cam_dev); - - hw_device_t *dev = reinterpret_cast<hw_device_t *>(*cam_dev); - res = dev->close(dev); - *cam_dev = NULL; - return res; - } - - void setUpCamera(int id) { - ASSERT_GT(sNumCameras, id); - status_t res; - - if (mDevice != NULL) { - closeCameraDevice(&mDevice); - } - mId = id; - mDevice = openCameraDevice(mId); - ASSERT_TRUE(NULL != mDevice) << "Failed to open camera device"; - - camera_info info; - res = sCameraModule->get_camera_info(id, &info); - ASSERT_EQ(OK, res); - - mDeviceVersion = info.device_version; - mStaticInfo = info.static_camera_characteristics; - buildOutputResolutions(); - - res = configureCameraDevice(mDevice, - mRequests, - mFrames, - mNotifications); - ASSERT_EQ(OK, res) << "Failure to configure camera device"; - - } - - void setUpStream(sp<IGraphicBufferProducer> consumer, - int width, int height, int format, int *id) { - status_t res; - - StreamAdapter* stream = new StreamAdapter(consumer); - - ALOGV("Creating stream, format 0x%x, %d x %d", format, width, height); - res = stream->connectToDevice(mDevice, width, height, format); - ASSERT_EQ(NO_ERROR, res) << "Failed to connect to stream: " - << strerror(-res); - mStreams.push_back(stream); - - *id = stream->getId(); - } - - void disconnectStream(int id) { - status_t res; - unsigned int i=0; - for (; i < mStreams.size(); i++) { - if (mStreams[i]->getId() == id) { - res = mStreams[i]->disconnect(); - ASSERT_EQ(NO_ERROR, res) << - "Failed to disconnect stream " << id; - break; - } - } - ASSERT_GT(mStreams.size(), i) << "Stream id not found:" << id; - } - - void buildOutputResolutions() { - status_t res; - if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) { - return; - } - if (mOutputResolutions.isEmpty()) { - camera_metadata_ro_entry_t availableStrmConfigs; - res = find_camera_metadata_ro_entry(mStaticInfo, - ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, - &availableStrmConfigs); - ASSERT_EQ(OK, res); - ASSERT_EQ(0u, availableStrmConfigs.count % 4); - for (uint32_t i = 0; i < availableStrmConfigs.count; i += 4) { - int32_t format = availableStrmConfigs.data.i32[i]; - int32_t width = availableStrmConfigs.data.i32[i + 1]; - int32_t height = availableStrmConfigs.data.i32[i + 2]; - int32_t inOrOut = availableStrmConfigs.data.i32[i + 3]; - if (inOrOut == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) { - int index = mOutputResolutions.indexOfKey(format); - if (index < 0) { - index = mOutputResolutions.add(format, new Vector<int32_t>()); - ASSERT_TRUE(index >= 0); - } - Vector<int32_t> *resolutions = mOutputResolutions.editValueAt(index); - resolutions->add(width); - resolutions->add(height); - } - } - } - } - - void deleteOutputResolutions() { - for (uint32_t i = 0; i < mOutputResolutions.size(); i++) { - Vector<int32_t>* resolutions = mOutputResolutions.editValueAt(i); - delete resolutions; - } - mOutputResolutions.clear(); - } - - void getResolutionList(int32_t format, - const int32_t **list, - size_t *count) { - status_t res; - ALOGV("Getting resolutions for format %x", format); - if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) { - if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { - camera_metadata_ro_entry_t availableFormats; - res = find_camera_metadata_ro_entry(mStaticInfo, - ANDROID_SCALER_AVAILABLE_FORMATS, - &availableFormats); - ASSERT_EQ(OK, res); - - uint32_t formatIdx; - for (formatIdx=0; formatIdx < availableFormats.count; formatIdx++) { - if (availableFormats.data.i32[formatIdx] == format) break; - } - ASSERT_NE(availableFormats.count, formatIdx) - << "No support found for format 0x" << std::hex << format; - } - - camera_metadata_ro_entry_t availableSizes; - if (format == HAL_PIXEL_FORMAT_RAW_SENSOR) { - res = find_camera_metadata_ro_entry(mStaticInfo, - ANDROID_SCALER_AVAILABLE_RAW_SIZES, - &availableSizes); - } else if (format == HAL_PIXEL_FORMAT_BLOB) { - res = find_camera_metadata_ro_entry(mStaticInfo, - ANDROID_SCALER_AVAILABLE_JPEG_SIZES, - &availableSizes); - } else { - res = find_camera_metadata_ro_entry(mStaticInfo, - ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, - &availableSizes); - } - ASSERT_EQ(OK, res); - - *list = availableSizes.data.i32; - *count = availableSizes.count; - } else { - int index = mOutputResolutions.indexOfKey(format); - ASSERT_TRUE(index >= 0); - Vector<int32_t>* resolutions = mOutputResolutions.valueAt(index); - *list = resolutions->array(); - *count = resolutions->size(); - } - } - - status_t waitUntilDrained() { - static const uint32_t kSleepTime = 50000; // 50 ms - static const uint32_t kMaxSleepTime = 10000000; // 10 s - ALOGV("%s: Camera %d: Starting wait", __FUNCTION__, mId); - - // TODO: Set up notifications from HAL, instead of sleeping here - uint32_t totalTime = 0; - while (mDevice->ops->get_in_progress_count(mDevice) > 0) { - usleep(kSleepTime); - totalTime += kSleepTime; - if (totalTime > kMaxSleepTime) { - ALOGE("%s: Waited %d us, %d requests still in flight", __FUNCTION__, - mDevice->ops->get_in_progress_count(mDevice), totalTime); - return TIMED_OUT; - } - } - ALOGV("%s: Camera %d: HAL is idle", __FUNCTION__, mId); - return OK; - } - - virtual void SetUp() { - TEST_EXTENSION_FORKING_SET_UP; - - SetUpModule(); - - const ::testing::TestInfo* const testInfo = - ::testing::UnitTest::GetInstance()->current_test_info(); - (void)testInfo; - - ALOGV("*** Starting test %s in test case %s", testInfo->name(), - testInfo->test_case_name()); - mDevice = NULL; - } - - virtual void TearDown() { - TEST_EXTENSION_FORKING_TEAR_DOWN; - - for (unsigned int i = 0; i < mStreams.size(); i++) { - delete mStreams[i]; - } - if (mDevice != NULL) { - closeCameraDevice(&mDevice); - } - - deleteOutputResolutions(); - TearDownModule(); - } - - int mId; - camera2_device *mDevice; - uint32_t mDeviceVersion; - const camera_metadata_t *mStaticInfo; - - MetadataQueue mRequests; - MetadataQueue mFrames; - NotifierListener mNotifications; - - Vector<StreamAdapter*> mStreams; - KeyedVector<int32_t, Vector<int32_t>* > mOutputResolutions; - - private: - static camera_module_t *sCameraModule; - static int sNumCameras; - static bool *sCameraSupportsHal2; - -}; - -camera_module_t *Camera2Test::sCameraModule = NULL; -bool *Camera2Test::sCameraSupportsHal2 = NULL; -int Camera2Test::sNumCameras = 0; - -static const nsecs_t USEC = 1000; -static const nsecs_t MSEC = 1000*USEC; -static const nsecs_t SEC = 1000*MSEC; - - -TEST_F(Camera2Test, OpenClose) { - - TEST_EXTENSION_FORKING_INIT; - - status_t res; - - for (int id = 0; id < getNumCameras(); id++) { - if (!isHal2Supported(id)) continue; - - camera2_device_t *d = openCameraDevice(id); - ASSERT_TRUE(NULL != d) << "Failed to open camera device"; - - res = closeCameraDevice(&d); - ASSERT_EQ(NO_ERROR, res) << "Failed to close camera device"; - } -} - -TEST_F(Camera2Test, Capture1Raw) { - - TEST_EXTENSION_FORKING_INIT; - - status_t res; - - for (int id = 0; id < getNumCameras(); id++) { - if (!isHal2Supported(id)) continue; - - ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - - sp<IGraphicBufferProducer> bqProducer; - sp<IGraphicBufferConsumer> bqConsumer; - BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - sp<CpuConsumer> rawConsumer = new CpuConsumer(bqConsumer, 1); - sp<FrameWaiter> rawWaiter = new FrameWaiter(); - rawConsumer->setFrameAvailableListener(rawWaiter); - - const int32_t *rawResolutions; - size_t rawResolutionsCount; - - int format = HAL_PIXEL_FORMAT_RAW_SENSOR; - - getResolutionList(format, - &rawResolutions, &rawResolutionsCount); - - if (rawResolutionsCount <= 0) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - std::cerr << "Skipping test " - << test_info->test_case_name() << "." - << test_info->name() - << " because the optional format was not available: " - << "RAW_SENSOR" << std::endl; - return; - } - - ASSERT_LT((size_t)0, rawResolutionsCount); - - // Pick first available raw resolution - int width = rawResolutions[0]; - int height = rawResolutions[1]; - - int streamId; - ASSERT_NO_FATAL_FAILURE( - setUpStream(bqProducer, width, height, format, &streamId) ); - - camera_metadata_t *request; - request = allocate_camera_metadata(20, 2000); - - uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL; - add_camera_metadata_entry(request, - ANDROID_REQUEST_METADATA_MODE, - (void**)&metadataMode, 1); - uint32_t outputStreams = streamId; - add_camera_metadata_entry(request, - ANDROID_REQUEST_OUTPUT_STREAMS, - (void**)&outputStreams, 1); - - uint64_t exposureTime = 10*MSEC; - add_camera_metadata_entry(request, - ANDROID_SENSOR_EXPOSURE_TIME, - (void**)&exposureTime, 1); - uint64_t frameDuration = 30*MSEC; - add_camera_metadata_entry(request, - ANDROID_SENSOR_FRAME_DURATION, - (void**)&frameDuration, 1); - uint32_t sensitivity = 100; - add_camera_metadata_entry(request, - ANDROID_SENSOR_SENSITIVITY, - (void**)&sensitivity, 1); - uint8_t requestType = ANDROID_REQUEST_TYPE_CAPTURE; - add_camera_metadata_entry(request, - ANDROID_REQUEST_TYPE, - (void**)&requestType, 1); - - uint32_t hourOfDay = 12; - add_camera_metadata_entry(request, - 0x80000000, // EMULATOR_HOUROFDAY - &hourOfDay, 1); - - IF_ALOGV() { - std::cout << "Input request: " << std::endl; - dump_indented_camera_metadata(request, 0, 1, 2); - } - - res = mRequests.enqueue(request); - ASSERT_EQ(NO_ERROR, res) << "Can't enqueue request: " << strerror(-res); - - res = mFrames.waitForBuffer(exposureTime + SEC); - ASSERT_EQ(NO_ERROR, res) << "No frame to get: " << strerror(-res); - - camera_metadata_t *frame; - res = mFrames.dequeue(&frame); - ASSERT_EQ(NO_ERROR, res); - ASSERT_TRUE(frame != NULL); - - IF_ALOGV() { - std::cout << "Output frame:" << std::endl; - dump_indented_camera_metadata(frame, 0, 1, 2); - } - - res = rawWaiter->waitForFrame(exposureTime + SEC); - ASSERT_EQ(NO_ERROR, res); - - CpuConsumer::LockedBuffer buffer; - res = rawConsumer->lockNextBuffer(&buffer); - ASSERT_EQ(NO_ERROR, res); - - IF_ALOGV() { - const char *dumpname = - "/data/local/tmp/camera2_test-capture1raw-dump.raw"; - ALOGV("Dumping raw buffer to %s", dumpname); - // Write to file - std::ofstream rawFile(dumpname); - size_t bpp = 2; - for (unsigned int y = 0; y < buffer.height; y++) { - rawFile.write( - (const char *)(buffer.data + y * buffer.stride * bpp), - buffer.width * bpp); - } - rawFile.close(); - } - - res = rawConsumer->unlockBuffer(buffer); - ASSERT_EQ(NO_ERROR, res); - - ASSERT_EQ(OK, waitUntilDrained()); - ASSERT_NO_FATAL_FAILURE(disconnectStream(streamId)); - - res = closeCameraDevice(&mDevice); - ASSERT_EQ(NO_ERROR, res) << "Failed to close camera device"; - - } -} - -TEST_F(Camera2Test, CaptureBurstRaw) { - - TEST_EXTENSION_FORKING_INIT; - - status_t res; - - for (int id = 0; id < getNumCameras(); id++) { - if (!isHal2Supported(id)) continue; - - ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - - sp<IGraphicBufferProducer> bqProducer; - sp<IGraphicBufferConsumer> bqConsumer; - BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - sp<CpuConsumer> rawConsumer = new CpuConsumer(bqConsumer, 1); - sp<FrameWaiter> rawWaiter = new FrameWaiter(); - rawConsumer->setFrameAvailableListener(rawWaiter); - - const int32_t *rawResolutions; - size_t rawResolutionsCount; - - int format = HAL_PIXEL_FORMAT_RAW_SENSOR; - - getResolutionList(format, - &rawResolutions, &rawResolutionsCount); - - if (rawResolutionsCount <= 0) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - std::cerr << "Skipping test " - << test_info->test_case_name() << "." - << test_info->name() - << " because the optional format was not available: " - << "RAW_SENSOR" << std::endl; - return; - } - - ASSERT_LT((uint32_t)0, rawResolutionsCount); - - // Pick first available raw resolution - int width = rawResolutions[0]; - int height = rawResolutions[1]; - - int streamId; - ASSERT_NO_FATAL_FAILURE( - setUpStream(bqProducer, width, height, format, &streamId) ); - - camera_metadata_t *request; - request = allocate_camera_metadata(20, 2000); - - uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL; - add_camera_metadata_entry(request, - ANDROID_REQUEST_METADATA_MODE, - (void**)&metadataMode, 1); - uint32_t outputStreams = streamId; - add_camera_metadata_entry(request, - ANDROID_REQUEST_OUTPUT_STREAMS, - (void**)&outputStreams, 1); - - uint64_t frameDuration = 30*MSEC; - add_camera_metadata_entry(request, - ANDROID_SENSOR_FRAME_DURATION, - (void**)&frameDuration, 1); - uint32_t sensitivity = 100; - add_camera_metadata_entry(request, - ANDROID_SENSOR_SENSITIVITY, - (void**)&sensitivity, 1); - uint8_t requestType = ANDROID_REQUEST_TYPE_CAPTURE; - add_camera_metadata_entry(request, - ANDROID_REQUEST_TYPE, - (void**)&requestType, 1); - - uint32_t hourOfDay = 12; - add_camera_metadata_entry(request, - 0x80000000, // EMULATOR_HOUROFDAY - &hourOfDay, 1); - - IF_ALOGV() { - std::cout << "Input request template: " << std::endl; - dump_indented_camera_metadata(request, 0, 1, 2); - } - - int numCaptures = 10; - - // Enqueue numCaptures requests with increasing exposure time - - uint64_t exposureTime = 100 * USEC; - for (int reqCount = 0; reqCount < numCaptures; reqCount++ ) { - camera_metadata_t *req; - req = allocate_camera_metadata(20, 2000); - append_camera_metadata(req, request); - - add_camera_metadata_entry(req, - ANDROID_SENSOR_EXPOSURE_TIME, - (void**)&exposureTime, 1); - exposureTime *= 2; - - res = mRequests.enqueue(req); - ASSERT_EQ(NO_ERROR, res) << "Can't enqueue request: " - << strerror(-res); - } - - // Get frames and image buffers one by one - uint64_t expectedExposureTime = 100 * USEC; - for (int frameCount = 0; frameCount < 10; frameCount++) { - res = mFrames.waitForBuffer(SEC + expectedExposureTime); - ASSERT_EQ(NO_ERROR, res) << "No frame to get: " << strerror(-res); - - camera_metadata_t *frame; - res = mFrames.dequeue(&frame); - ASSERT_EQ(NO_ERROR, res); - ASSERT_TRUE(frame != NULL); - - camera_metadata_entry_t frameNumber; - res = find_camera_metadata_entry(frame, - ANDROID_REQUEST_FRAME_COUNT, - &frameNumber); - ASSERT_EQ(NO_ERROR, res); - ASSERT_EQ(frameCount, *frameNumber.data.i32); - - res = rawWaiter->waitForFrame(SEC + expectedExposureTime); - ASSERT_EQ(NO_ERROR, res) << - "Never got raw data for capture " << frameCount; - - CpuConsumer::LockedBuffer buffer; - res = rawConsumer->lockNextBuffer(&buffer); - ASSERT_EQ(NO_ERROR, res); - - IF_ALOGV() { - char dumpname[60]; - snprintf(dumpname, 60, - "/data/local/tmp/camera2_test-" - "captureBurstRaw-dump_%d.raw", - frameCount); - ALOGV("Dumping raw buffer to %s", dumpname); - // Write to file - std::ofstream rawFile(dumpname); - for (unsigned int y = 0; y < buffer.height; y++) { - rawFile.write( - (const char *)(buffer.data + y * buffer.stride * 2), - buffer.width * 2); - } - rawFile.close(); - } - - res = rawConsumer->unlockBuffer(buffer); - ASSERT_EQ(NO_ERROR, res); - - expectedExposureTime *= 2; - } - } -} - -TEST_F(Camera2Test, ConstructDefaultRequests) { - - TEST_EXTENSION_FORKING_INIT; - - status_t res; - - for (int id = 0; id < getNumCameras(); id++) { - if (!isHal2Supported(id)) continue; - - ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - - for (int i = CAMERA2_TEMPLATE_PREVIEW; i < CAMERA2_TEMPLATE_COUNT; - i++) { - camera_metadata_t *request = NULL; - res = mDevice->ops->construct_default_request(mDevice, - i, - &request); - EXPECT_EQ(NO_ERROR, res) << - "Unable to construct request from template type " << i; - EXPECT_TRUE(request != NULL); - EXPECT_LT((size_t)0, get_camera_metadata_entry_count(request)); - EXPECT_LT((size_t)0, get_camera_metadata_data_count(request)); - - IF_ALOGV() { - std::cout << " ** Template type " << i << ":"<<std::endl; - dump_indented_camera_metadata(request, 0, 2, 4); - } - - free_camera_metadata(request); - } - } -} - -TEST_F(Camera2Test, Capture1Jpeg) { - status_t res; - - for (int id = 0; id < getNumCameras(); id++) { - if (!isHal2Supported(id)) continue; - - ASSERT_NO_FATAL_FAILURE(setUpCamera(id)); - - sp<IGraphicBufferProducer> bqProducer; - sp<IGraphicBufferConsumer> bqConsumer; - BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - sp<CpuConsumer> jpegConsumer = new CpuConsumer(bqConsumer, 1); - sp<FrameWaiter> jpegWaiter = new FrameWaiter(); - jpegConsumer->setFrameAvailableListener(jpegWaiter); - - const int32_t *jpegResolutions; - size_t jpegResolutionsCount; - - int format = HAL_PIXEL_FORMAT_BLOB; - - getResolutionList(format, - &jpegResolutions, &jpegResolutionsCount); - ASSERT_LT((size_t)0, jpegResolutionsCount); - - // Pick first available JPEG resolution - int width = jpegResolutions[0]; - int height = jpegResolutions[1]; - - int streamId; - ASSERT_NO_FATAL_FAILURE( - setUpStream(bqProducer, width, height, format, &streamId) ); - - camera_metadata_t *request; - request = allocate_camera_metadata(20, 2000); - - uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL; - add_camera_metadata_entry(request, - ANDROID_REQUEST_METADATA_MODE, - (void**)&metadataMode, 1); - uint32_t outputStreams = streamId; - add_camera_metadata_entry(request, - ANDROID_REQUEST_OUTPUT_STREAMS, - (void**)&outputStreams, 1); - - uint64_t exposureTime = 10*MSEC; - add_camera_metadata_entry(request, - ANDROID_SENSOR_EXPOSURE_TIME, - (void**)&exposureTime, 1); - uint64_t frameDuration = 30*MSEC; - add_camera_metadata_entry(request, - ANDROID_SENSOR_FRAME_DURATION, - (void**)&frameDuration, 1); - uint32_t sensitivity = 100; - add_camera_metadata_entry(request, - ANDROID_SENSOR_SENSITIVITY, - (void**)&sensitivity, 1); - uint8_t requestType = ANDROID_REQUEST_TYPE_CAPTURE; - add_camera_metadata_entry(request, - ANDROID_REQUEST_TYPE, - (void**)&requestType, 1); - - uint32_t hourOfDay = 12; - add_camera_metadata_entry(request, - 0x80000000, // EMULATOR_HOUROFDAY - &hourOfDay, 1); - - IF_ALOGV() { - std::cout << "Input request: " << std::endl; - dump_indented_camera_metadata(request, 0, 1, 4); - } - - res = mRequests.enqueue(request); - ASSERT_EQ(NO_ERROR, res) << "Can't enqueue request: " << strerror(-res); - - res = mFrames.waitForBuffer(exposureTime + SEC); - ASSERT_EQ(NO_ERROR, res) << "No frame to get: " << strerror(-res); - - camera_metadata_t *frame; - res = mFrames.dequeue(&frame); - ASSERT_EQ(NO_ERROR, res); - ASSERT_TRUE(frame != NULL); - - IF_ALOGV() { - std::cout << "Output frame:" << std::endl; - dump_indented_camera_metadata(frame, 0, 1, 4); - } - - res = jpegWaiter->waitForFrame(exposureTime + SEC); - ASSERT_EQ(NO_ERROR, res); - - CpuConsumer::LockedBuffer buffer; - res = jpegConsumer->lockNextBuffer(&buffer); - ASSERT_EQ(NO_ERROR, res); - - IF_ALOGV() { - const char *dumpname = - "/data/local/tmp/camera2_test-capture1jpeg-dump.jpeg"; - ALOGV("Dumping raw buffer to %s", dumpname); - // Write to file - std::ofstream jpegFile(dumpname); - size_t bpp = 1; - for (unsigned int y = 0; y < buffer.height; y++) { - jpegFile.write( - (const char *)(buffer.data + y * buffer.stride * bpp), - buffer.width * bpp); - } - jpegFile.close(); - } - - res = jpegConsumer->unlockBuffer(buffer); - ASSERT_EQ(NO_ERROR, res); - - ASSERT_EQ(OK, waitUntilDrained()); - ASSERT_NO_FATAL_FAILURE(disconnectStream(streamId)); - - res = closeCameraDevice(&mDevice); - ASSERT_EQ(NO_ERROR, res) << "Failed to close camera device"; - - } -} - -} // namespace tests -} // namespace camera2 -} // namespace android diff --git a/tests/camera2/camera2_utils.cpp b/tests/camera2/camera2_utils.cpp index 9cc6c905..d9629396 100644 --- a/tests/camera2/camera2_utils.cpp +++ b/tests/camera2/camera2_utils.cpp @@ -580,14 +580,13 @@ void FrameWaiter::onFrameAvailable(const BufferItem& /* item */) { mCondition.signal(); } -int HWModuleHelpers::closeModule(hw_module_t* module) { +int HWModuleHelpers::closeModule(void *dso) { int status; - - if (!module) { + if (!dso) { return -EINVAL; } - status = dlclose(module->dso); + status = dlclose(dso); if (status != 0) { char const *err_str = dlerror(); ALOGE("%s dlclose failed, error: %s", __func__, err_str ?: "unknown"); diff --git a/tests/camera2/camera2_utils.h b/tests/camera2/camera2_utils.h index c1d1e724..ab1fcfb2 100644 --- a/tests/camera2/camera2_utils.h +++ b/tests/camera2/camera2_utils.h @@ -240,7 +240,7 @@ class FrameWaiter : public CpuConsumer::FrameAvailableListener { struct HWModuleHelpers { /* attempt to unload the library with dlclose */ - static int closeModule(hw_module_t* module); + static int closeModule(void* dso); }; } diff --git a/tests/fingerprint/fingerprint_tests.cpp b/tests/fingerprint/fingerprint_tests.cpp index 44637510..dbb248fe 100644 --- a/tests/fingerprint/fingerprint_tests.cpp +++ b/tests/fingerprint/fingerprint_tests.cpp @@ -24,11 +24,36 @@ TEST_F(FingerprintDevice, isThereEnroll) { << "enroll() function is not implemented"; } +TEST_F(FingerprintDevice, isTherePreEnroll) { + ASSERT_TRUE(NULL != fp_device()->pre_enroll) + << "pre_enroll() function is not implemented"; +} + +TEST_F(FingerprintDevice, isThereGetAuthenticatorId) { + ASSERT_TRUE(NULL != fp_device()->get_authenticator_id) + << "get_authenticator_id() function is not implemented"; +} + +TEST_F(FingerprintDevice, isThereCancel) { + ASSERT_TRUE(NULL != fp_device()->cancel) + << "cancel() function is not implemented"; +} + TEST_F(FingerprintDevice, isThereRemove) { ASSERT_TRUE(NULL != fp_device()->remove) << "remove() function is not implemented"; } +TEST_F(FingerprintDevice, isThereAuthenticate) { + ASSERT_TRUE(NULL != fp_device()->authenticate) + << "authenticate() function is not implemented"; +} + +TEST_F(FingerprintDevice, isThereSetActiveGroup) { + ASSERT_TRUE(NULL != fp_device()->set_active_group) + << "set_active_group() function is not implemented"; +} + TEST_F(FingerprintDevice, isThereSetNotify) { ASSERT_TRUE(NULL != fp_device()->set_notify) << "set_notify() function is not implemented"; diff --git a/tests/hardware/struct-offset.cpp b/tests/hardware/struct-offset.cpp index a7ff797b..10c08957 100644 --- a/tests/hardware/struct-offset.cpp +++ b/tests/hardware/struct-offset.cpp @@ -115,7 +115,8 @@ void CheckOffsets(void) { CHECK_MEMBER_AT(sensors_poll_device_1_t, poll, 72, 136); CHECK_MEMBER_AT(sensors_poll_device_1_t, batch, 76, 144); CHECK_MEMBER_AT(sensors_poll_device_1_t, flush, 80, 152); - CHECK_MEMBER_AT(sensors_poll_device_1_t, reserved_procs, 84, 160); + CHECK_MEMBER_AT(sensors_poll_device_1_t, inject_sensor_data, 84, 160); + CHECK_MEMBER_AT(sensors_poll_device_1_t, reserved_procs, 88, 168); //Types defined in fb.h CHECK_MEMBER_AT(framebuffer_device_t, common, 0, 0); @@ -212,7 +213,9 @@ void CheckOffsets(void) { CHECK_MEMBER_AT(camera_module_t, set_callbacks, 136, 264); CHECK_MEMBER_AT(camera_module_t, get_vendor_tag_ops, 140, 272); CHECK_MEMBER_AT(camera_module_t, open_legacy, 144, 280); - CHECK_MEMBER_AT(camera_module_t, reserved, 148, 288); + CHECK_MEMBER_AT(camera_module_t, set_torch_mode, 148, 288); + CHECK_MEMBER_AT(camera_module_t, init, 152, 296); + CHECK_MEMBER_AT(camera_module_t, reserved, 156, 304); //Types defined in camera3.h CHECK_MEMBER_AT(camera3_device_ops_t, initialize, 0, 0); diff --git a/tests/input/Android.mk b/tests/input/Android.mk new file mode 100644 index 00000000..3011b2e2 --- /dev/null +++ b/tests/input/Android.mk @@ -0,0 +1,19 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/input/evdev/Android.mk b/tests/input/evdev/Android.mk new file mode 100644 index 00000000..167cbc28 --- /dev/null +++ b/tests/input/evdev/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES += hardware/libhardware/modules/input/evdev + +LOCAL_SRC_FILES:= \ + InputDevice_test.cpp \ + InputHub_test.cpp \ + TestHelpers.cpp + +LOCAL_SHARED_LIBRARIES := \ + libinput_evdev \ + liblog \ + libutils + +LOCAL_CLANG := true +LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter +LOCAL_CPPFLAGS += -std=c++14 + +LOCAL_MODULE := libinput_evdevtests +LOCAL_MODULE_TAGS := tests + +include $(BUILD_NATIVE_TEST) diff --git a/tests/input/evdev/InputDevice_test.cpp b/tests/input/evdev/InputDevice_test.cpp new file mode 100644 index 00000000..a96d664a --- /dev/null +++ b/tests/input/evdev/InputDevice_test.cpp @@ -0,0 +1,134 @@ +/* + * 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 "InputHub_test" +//#define LOG_NDEBUG 0 + +#include <linux/input.h> + +#include <gtest/gtest.h> + +#include <utils/Timers.h> + +#include "InputDevice.h" +#include "InputHub.h" + +// # of milliseconds to allow for timing measurements +#define TIMING_TOLERANCE_MS 25 + +#define MSC_ANDROID_TIME_SEC 0x6 +#define MSC_ANDROID_TIME_USEC 0x7 + +namespace android { +namespace tests { + +class MockInputDeviceNode : public InputDeviceNode { + virtual const std::string& getPath() const override { return mPath; } + + virtual const std::string& getName() const override { return mName; } + virtual const std::string& getLocation() const override { return mLocation; } + virtual const std::string& getUniqueId() const override { return mUniqueId; } + + virtual uint16_t getBusType() const override { return 0; } + virtual uint16_t getVendorId() const override { return 0; } + virtual uint16_t getProductId() const override { return 0; } + virtual uint16_t getVersion() const override { return 0; } + + virtual bool hasKey(int32_t key) const { return false; } + virtual bool hasRelativeAxis(int axis) const { return false; } + virtual bool hasInputProperty(int property) const { return false; } + + virtual int32_t getKeyState(int32_t key) const { return 0; } + virtual int32_t getSwitchState(int32_t sw) const { return 0; } + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const { return nullptr; } + virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const { return 0; } + + virtual void vibrate(nsecs_t duration) {} + virtual void cancelVibrate(int32_t deviceId) {} + + virtual void disableDriverKeyRepeat() {} + +private: + std::string mPath = "/test"; + std::string mName = "Test Device"; + std::string mLocation = "test/0"; + std::string mUniqueId = "test-id"; +}; + +TEST(EvdevDeviceTest, testOverrideTime) { + auto node = std::make_shared<MockInputDeviceNode>(); + auto device = std::make_unique<EvdevDevice>(node); + ASSERT_TRUE(device != nullptr); + + // Send two timestamp override events before an input event. + nsecs_t when = 2ULL; + InputEvent msc1 = { when, EV_MSC, MSC_ANDROID_TIME_SEC, 1 }; + InputEvent msc2 = { when, EV_MSC, MSC_ANDROID_TIME_USEC, 900000 }; + + // Send a key down and syn. Should get the overridden timestamp. + InputEvent keyDown = { when, EV_KEY, KEY_HOME, 1 }; + InputEvent syn = { when, EV_SYN, SYN_REPORT, 0 }; + + // Send a key up, which should be at the reported timestamp. + InputEvent keyUp = { when, EV_KEY, KEY_HOME, 0 }; + + device->processInput(msc1, when); + device->processInput(msc2, when); + device->processInput(keyDown, when); + device->processInput(syn, when); + device->processInput(keyUp, when); + + nsecs_t expectedWhen = s2ns(1) + us2ns(900000); + EXPECT_EQ(expectedWhen, keyDown.when); + EXPECT_EQ(expectedWhen, syn.when); + EXPECT_EQ(when, keyUp.when); +} + +TEST(EvdevDeviceTest, testWrongClockCorrection) { + auto node = std::make_shared<MockInputDeviceNode>(); + auto device = std::make_unique<EvdevDevice>(node); + ASSERT_TRUE(device != nullptr); + + auto now = systemTime(SYSTEM_TIME_MONOTONIC); + + // Input event that supposedly comes from 1 minute in the future. In + // reality, the timestamps would be much further off. + InputEvent event = { now + s2ns(60), EV_KEY, KEY_HOME, 1 }; + + device->processInput(event, now); + + EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS)); +} + +TEST(EvdevDeviceTest, testClockCorrectionOk) { + auto node = std::make_shared<MockInputDeviceNode>(); + auto device = std::make_unique<EvdevDevice>(node); + ASSERT_TRUE(device != nullptr); + + auto now = systemTime(SYSTEM_TIME_MONOTONIC); + + // Input event from now, but will be reported as if it came early. + InputEvent event = { now, EV_KEY, KEY_HOME, 1 }; + + // event_time parameter is 11 seconds in the past, so it looks like we used + // the wrong clock. + device->processInput(event, now - s2ns(11)); + + EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS)); +} + +} // namespace tests +} // namespace android diff --git a/tests/input/evdev/InputHub_test.cpp b/tests/input/evdev/InputHub_test.cpp new file mode 100644 index 00000000..f2c8edf9 --- /dev/null +++ b/tests/input/evdev/InputHub_test.cpp @@ -0,0 +1,259 @@ +/* + * 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 "InputHub_test" +//#define LOG_NDEBUG 0 + +#include <linux/input.h> + +#include <chrono> +#include <memory> +#include <mutex> + +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <utils/StopWatch.h> +#include <utils/Timers.h> + +#include "InputHub.h" +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 +#define NO_TIMEOUT (-1) + +namespace android { +namespace tests { + +using namespace std::literals::chrono_literals; + +using InputCbFunc = std::function<void(std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t)>; +using DeviceCbFunc = std::function<void(std::shared_ptr<InputDeviceNode>)>; + +static const InputCbFunc kNoopInputCb = [](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t){}; +static const DeviceCbFunc kNoopDeviceCb = [](std::shared_ptr<InputDeviceNode>){}; + +class TestInputCallback : public InputCallbackInterface { +public: + TestInputCallback() : + mInputCb(kNoopInputCb), mDeviceAddedCb(kNoopDeviceCb), mDeviceRemovedCb(kNoopDeviceCb) {} + virtual ~TestInputCallback() = default; + + void setInputCallback(InputCbFunc cb) { mInputCb = cb; } + void setDeviceAddedCallback(DeviceCbFunc cb) { mDeviceAddedCb = cb; } + void setDeviceRemovedCallback(DeviceCbFunc cb) { mDeviceRemovedCb = cb; } + + virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event, + nsecs_t event_time) override { + mInputCb(node, event, event_time); + } + virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) override { + mDeviceAddedCb(node); + } + virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) override { + mDeviceRemovedCb(node); + } + +private: + InputCbFunc mInputCb; + DeviceCbFunc mDeviceAddedCb; + DeviceCbFunc mDeviceRemovedCb; +}; + +class InputHubTest : public ::testing::Test { + protected: + virtual void SetUp() { + mCallback = std::make_shared<TestInputCallback>(); + mInputHub = std::make_shared<InputHub>(mCallback); + } + + std::shared_ptr<TestInputCallback> mCallback; + std::shared_ptr<InputHub> mInputHub; +}; + +TEST_F(InputHubTest, testWake) { + // Call wake() after 100ms. + auto f = delay_async(100ms, [&]() { EXPECT_EQ(OK, mInputHub->wake()); }); + + StopWatch stopWatch("poll"); + EXPECT_EQ(OK, mInputHub->poll()); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); +} + +TEST_F(InputHubTest, DISABLED_testDeviceAdded) { + auto tempDir = std::make_shared<TempDir>(); + std::string pathname; + // Expect that this callback will run and set handle and pathname. + mCallback->setDeviceAddedCallback( + [&](std::shared_ptr<InputDeviceNode> node) { + pathname = node->getPath(); + }); + + ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); + + // Create a new file in tempDir after 100ms. + std::unique_ptr<TempFile> tempFile; + std::mutex tempFileMutex; + auto f = delay_async(100ms, + [&]() { + std::lock_guard<std::mutex> lock(tempFileMutex); + tempFile.reset(tempDir->newTempFile()); + }); + + StopWatch stopWatch("poll"); + EXPECT_EQ(OK, mInputHub->poll()); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); + std::lock_guard<std::mutex> lock(tempFileMutex); + EXPECT_EQ(tempFile->getName(), pathname); +} + +TEST_F(InputHubTest, DISABLED_testDeviceRemoved) { + // Create a temp dir and file. Save its name and handle (to be filled in + // once InputHub scans the dir). + auto tempDir = std::make_unique<TempDir>(); + auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); + std::string tempFileName(deviceFile->getName()); + + std::shared_ptr<InputDeviceNode> tempNode; + // Expect that these callbacks will run for the above device file. + mCallback->setDeviceAddedCallback( + [&](std::shared_ptr<InputDeviceNode> node) { + tempNode = node; + }); + mCallback->setDeviceRemovedCallback( + [&](std::shared_ptr<InputDeviceNode> node) { + EXPECT_EQ(tempNode, node); + }); + + ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); + // Ensure that tempDir was scanned to find the device. + ASSERT_TRUE(tempNode != nullptr); + + auto f = delay_async(100ms, [&]() { deviceFile.reset(); }); + + StopWatch stopWatch("poll"); + EXPECT_EQ(OK, mInputHub->poll()); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); +} + +TEST_F(InputHubTest, DISABLED_testInputEvent) { + // Create a temp dir and file. Save its name and handle (to be filled in + // once InputHub scans the dir.) + auto tempDir = std::make_unique<TempDir>(); + auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); + std::string tempFileName(deviceFile->getName()); + + // Send a key event corresponding to HOME. + struct input_event iev; + iev.time = { 1, 0 }; + iev.type = EV_KEY; + iev.code = KEY_HOME; + iev.value = 0x01; + + auto inputDelayMs = 100ms; + auto f = delay_async(inputDelayMs, [&] { + ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile->getFd(), &iev, sizeof(iev))); + + ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " + << deviceFile->getFd() << ". errno: " << errno; + }); + + // Expect this callback to run when the input event is read. + nsecs_t expectedWhen = systemTime(CLOCK_MONOTONIC) + ms2ns(inputDelayMs.count()); + mCallback->setInputCallback( + [&](std::shared_ptr<InputDeviceNode> node, InputEvent& event, nsecs_t event_time) { + EXPECT_NEAR(expectedWhen, event_time, ms2ns(TIMING_TOLERANCE_MS)); + EXPECT_EQ(s2ns(1), event.when); + EXPECT_EQ(tempFileName, node->getPath()); + EXPECT_EQ(EV_KEY, event.type); + EXPECT_EQ(KEY_HOME, event.code); + EXPECT_EQ(0x01, event.value); + }); + ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); + + StopWatch stopWatch("poll"); + EXPECT_EQ(OK, mInputHub->poll()); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); +} + +TEST_F(InputHubTest, DISABLED_testCallbackOrder) { + // Create two "devices": one to receive input and the other to go away. + auto tempDir = std::make_unique<TempDir>(); + auto deviceFile1 = std::unique_ptr<TempFile>(tempDir->newTempFile()); + auto deviceFile2 = std::unique_ptr<TempFile>(tempDir->newTempFile()); + std::string tempFileName(deviceFile2->getName()); + + bool inputCallbackFinished = false, deviceCallbackFinished = false; + + // Setup the callback for input events. Should run before the device + // callback. + mCallback->setInputCallback( + [&](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t) { + ASSERT_FALSE(deviceCallbackFinished); + inputCallbackFinished = true; + }); + + // Setup the callback for device removal. Should run after the input + // callback. + mCallback->setDeviceRemovedCallback( + [&](std::shared_ptr<InputDeviceNode> node) { + ASSERT_TRUE(inputCallbackFinished) + << "input callback did not run before device changed callback"; + // Make sure the correct device was removed. + EXPECT_EQ(tempFileName, node->getPath()); + deviceCallbackFinished = true; + }); + ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); + + auto f = delay_async(100ms, + [&]() { + // Delete the second device file first. + deviceFile2.reset(); + + // Then inject an input event into the first device. + struct input_event iev; + iev.time = { 1, 0 }; + iev.type = EV_KEY; + iev.code = KEY_HOME; + iev.value = 0x01; + + ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile1->getFd(), &iev, sizeof(iev))); + + ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " + << deviceFile1->getFd() << ". errno: " << errno; + }); + + StopWatch stopWatch("poll"); + EXPECT_EQ(OK, mInputHub->poll()); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); + EXPECT_TRUE(inputCallbackFinished); + EXPECT_TRUE(deviceCallbackFinished); +} + +} // namespace tests +} // namespace android diff --git a/tests/input/evdev/TestHelpers.cpp b/tests/input/evdev/TestHelpers.cpp new file mode 100644 index 00000000..63b579e1 --- /dev/null +++ b/tests/input/evdev/TestHelpers.cpp @@ -0,0 +1,93 @@ +/* + * 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 "TestHelpers" +#define LOG_NDEBUG 0 + +#include <dirent.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <utils/Log.h> + +#include "TestHelpers.h" + +namespace android { + +static const char kTmpDirTemplate[] = "/data/local/tmp/XXXXXX"; + +TempFile::TempFile(const char* path) { + bool needTrailingSlash = path[strlen(path) - 1] != '/'; + // name = path + XXXXXX + \0 + size_t nameLen = strlen(path) + 6 + 1; + if (needTrailingSlash) nameLen++; + + mName = new char[nameLen]; + strcpy(mName, path); + if (needTrailingSlash) { + strcat(mName, "/"); + } + strcat(mName, "XXXXXX"); + mName = mktemp(mName); + LOG_FATAL_IF(mName == nullptr, "could not create temp filename %s. errno=%d", mName, errno); + + int result = TEMP_FAILURE_RETRY(mkfifo(mName, S_IRWXU)); + LOG_FATAL_IF(result < 0, "could not create fifo %s. errno=%d", mName, errno); + + mFd = TEMP_FAILURE_RETRY(open(mName, O_RDWR | O_NONBLOCK)); + LOG_FATAL_IF(mFd < 0, "could not open fifo %s. errno=%d", mName, errno); +} + +TempFile::~TempFile() { + if (unlink(mName) < 0) { + ALOGE("could not unlink %s. errno=%d", mName, errno); + } + if (close(mFd) < 0) { + ALOGE("could not close %d. errno=%d", mFd, errno); + } + delete[] mName; +} + +TempDir::TempDir() { + mName = new char[sizeof(kTmpDirTemplate)]; + strcpy(mName, kTmpDirTemplate); + mName = mkdtemp(mName); + LOG_FATAL_IF(mName == nullptr, "could not allocate tempdir %s", mName); +} + +TempDir::~TempDir() { + auto tmpDir = opendir(mName); + while (auto entry = readdir(tmpDir)) { + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) { + continue; + } + ALOGD("stale file %s, removing", entry->d_name); + unlink(entry->d_name); + } + closedir(tmpDir); + rmdir(mName); + delete mName; +} + +TempFile* TempDir::newTempFile() { + return new TempFile(mName); +} + +} // namespace android diff --git a/tests/input/evdev/TestHelpers.h b/tests/input/evdev/TestHelpers.h new file mode 100644 index 00000000..461db048 --- /dev/null +++ b/tests/input/evdev/TestHelpers.h @@ -0,0 +1,83 @@ +/* + * 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_TEST_HELPERS_H_ +#define ANDROID_TEST_HELPERS_H_ + +#include <future> +#include <thread> + +namespace android { + +/** + * Runs the given function after the specified delay. + * NOTE: if the std::future returned from std::async is not bound, this function + * will block until the task completes. This is almost certainly NOT what you + * want, so save the return value from delay_async into a variable: + * + * auto f = delay_async(100ms, []{ ALOGD("Hello world"); }); + */ +template<class Function, class Duration> +decltype(auto) delay_async(Duration&& delay, Function&& task) +{ + return std::async(std::launch::async, [=]{ std::this_thread::sleep_for(delay); task(); }); +} + +/** + * Creates and opens a temporary file at the given path. The file is unlinked + * and closed in the destructor. + */ +class TempFile { +public: + TempFile(const char* path); + ~TempFile(); + + // No copy or assign + TempFile(const TempFile&) = delete; + TempFile& operator=(const TempFile&) = delete; + + const char* getName() const { return mName; } + int getFd() const { return mFd; } + +private: + char* mName; + int mFd; +}; + +/** + * Creates a temporary directory that can create temporary files. The directory + * is emptied and deleted in the destructor. + */ +class TempDir { +public: + TempDir(); + ~TempDir(); + + // No copy or assign + TempDir(const TempDir&) = delete; + TempDir& operator=(const TempDir&) = delete; + + const char* getName() const { return mName; } + + TempFile* newTempFile(); + +private: + char* mName; +}; + +} // namespace android + +#endif // ANDROID_TEST_HELPERS_H_ |