Merge "Creating audio.stub.default for default HAL audio output"
diff --git a/include/hardware/camera3.h b/include/hardware/camera3.h
index 2c47ef6..fd3a45d 100644
--- a/include/hardware/camera3.h
+++ b/include/hardware/camera3.h
@@ -129,10 +129,12 @@
*
* - OPAQUE and YUV reprocessing API updates.
*
- * - Basic support for depth output buffers
+ * - Basic support for depth output buffers.
*
* - Addition of data_space field to camera3_stream_t.
*
+ * - Addition of rotation field to camera3_stream_t.
+ *
*/
/**
@@ -1370,6 +1372,25 @@
} 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_t:
*
* A handle to a single camera input or output stream. A stream is defined by
@@ -1513,8 +1534,34 @@
*/
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[8];
+ void *reserved[7];
} camera3_stream_t;
@@ -2570,6 +2617,9 @@
*
* - Including too many output streams of a certain format.
*
+ * - Unsupported rotation configuration (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
diff --git a/include/hardware/camera_common.h b/include/hardware/camera_common.h
index fa22c47..c2d4536 100644
--- a/include/hardware/camera_common.h
+++ b/include/hardware/camera_common.h
@@ -236,10 +236,12 @@
const camera_metadata_t *static_camera_characteristics;
/**
- * The total resource "cost" of using this 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.
+ * 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
@@ -249,12 +251,112 @@
* available in the configuration settings exposed for that device through
* the camera metadata.
*
- * Note: 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 should fail during the configure calls. If the
- * total resource cost is <= 100, configuration should never fail due to
- * resource constraints.
+ * 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):
*
@@ -281,6 +383,9 @@
* 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:
diff --git a/include/hardware/gatekeeper.h b/include/hardware/gatekeeper.h
new file mode 100644
index 0000000..9150e70
--- /dev/null
+++ b/include/hardware/gatekeeper.h
@@ -0,0 +1,99 @@
+/*
+ * 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_HARDWARE_GATEKEEPER_H
+#define ANDROID_HARDWARE_GATEKEEPER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+#define GATEKEEPER_HARDWARE_MODULE_ID "gatekeeper"
+
+#define GATEKEEPER_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+
+#define HARDWARE_GATEKEEPER "gatekeeper"
+
+struct gatekeeper_module {
+ /**
+ * Comon methods of the gatekeeper module. This *must* be the first member of
+ * gatekeeper_module as users of this structure will cast a hw_module_t to
+ * a gatekeeper_module pointer in the appropriate context.
+ */
+ hw_module_t common;
+};
+
+struct gatekeeper_device {
+ /**
+ * Common methods of the gatekeeper device. As above, this must be the first
+ * member of keymaster_device.
+ */
+ hw_device_t common;
+
+ /**
+ * Enrolls desired_password, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * If there was already a password enrolled, it should be provided in
+ * current_password_handle, along with the current password in current_password
+ * that should validate against current_password_handle.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ int (*enroll)(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *current_password_handle, size_t current_password_handle_length,
+ const uint8_t *current_password, size_t current_password_length,
+ const uint8_t *desired_password, size_t desired_password_length,
+ uint8_t **enrolled_password_handle, size_t *enrolled_password_handle_length);
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ int (*verify)(const struct gatekeeper_device *dev, uint32_t uid,
+ const uint8_t *enrolled_password_handle, size_t enrolled_password_handle_length,
+ const uint8_t *provided_password, size_t provided_password_length,
+ uint8_t **auth_token, size_t *auth_token_length);
+
+};
+typedef struct gatekeeper_device gatekeeper_device_t;
+
+static inline int gatekeeper_open(const struct hw_module_t *module,
+ gatekeeper_device_t **device) {
+ return module->methods->open(module, HARDWARE_GATEKEEPER,
+ (struct hw_device_t **) device);
+}
+
+static inline int gatekeeper_close(gatekeeper_device_t *device) {
+ return device->common.close(&device->common);
+}
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_GATEKEEPER_H
diff --git a/include/hardware/hw_auth_token.h b/include/hardware/hw_auth_token.h
new file mode 100644
index 0000000..154c1fd
--- /dev/null
+++ b/include/hardware/hw_auth_token.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+
+#ifndef ANDROID_HARDWARE_HW_AUTH_TOKEN_H
+#define ANDROID_HARDWARE_HW_AUTH_TOKEN_H
+
+#ifndef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef enum {
+ HW_AUTH_NONE = 0,
+ HW_AUTH_PASSWORD = 1 << 1,
+ HW_AUTH_FINGERPRINT = 1 << 2,
+ // Additional entries should be powers of 2.
+ HW_AUTH_ANY = UINT32_MAX,
+} hw_authenticator_type_t;
+
+/**
+ * Data format for an authentication record used to prove successful authentication.
+ */
+typedef struct __attribute__((__packed__)) {
+ uint8_t version; // Current version is 0
+ uint64_t challenge;
+ uint64_t user_id; // secure user ID, not Android user ID
+ uint64_t authenticator_id; // secure authenticator ID
+ uint32_t authenticator_type; // hw_authenticator_type_t, in network order
+ uint32_t timestamp; // in network order
+ uint8_t hmac[32];
+} hw_auth_token_t;
+
+#ifndef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // ANDROID_HARDWARE_HW_AUTH_TOKEN_H
diff --git a/include/hardware/keyguard.h b/include/hardware/keyguard.h
deleted file mode 100644
index 88dcad1..0000000
--- a/include/hardware/keyguard.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_KEYGUARD_H
-#define ANDROID_HARDWARE_KEYGUARD_H
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <hardware/hardware.h>
-
-__BEGIN_DECLS
-
-#define KEYGUARD_HARDWARE_MODULE_ID "keyguard"
-
-#define KEYGUARD_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
-
-#define HARDWARE_KEYGUARD "keyguard"
-
-struct keyguard_module {
- /**
- * Comon methods of the keyguard module. This *must* be the first member of
- * keyguard_module as users of this structure will cast a hw_module_t to
- * a keyguard_module pointer in the appropriate context.
- */
- hw_module_t common;
-};
-
-struct keyguard_device {
- /**
- * Common methods of the keyguard device. As above, this must be the first
- * member of keymaster_device.
- */
- hw_device_t common;
-
- /**
- * Enrolls password_payload, which should be derived from a user selected pin or password,
- * with the authentication factor private key used only for enrolling authentication
- * factor data.
- *
- * Returns: 0 on success or an error code less than 0 on error.
- * On error, enrolled_password_handle will not be allocated.
- */
- int (*enroll)(const struct keyguard_device *dev, uint32_t uid,
- const uint8_t *password_payload, size_t password_payload_length,
- uint8_t **enrolled_password_handle, size_t *enrolled_password_handle_length);
-
- /**
- * Verifies provided_password matches enrolled_password_handle.
- *
- * Implementations of this module may retain the result of this call
- * to attest to the recency of authentication.
- *
- * On success, writes the address of a verification token to verification_token,
- * usable to attest password verification to other trusted services. Clients
- * may pass NULL for this value.
- *
- * Returns: 0 on success or an error code less than 0 on error
- * On error, verification token will not be allocated
- */
- int (*verify)(const struct keyguard_device *dev, uint32_t uid,
- const uint8_t *enrolled_password_handle, size_t enrolled_password_handle_length,
- const uint8_t *provided_password, size_t provided_password_length,
- uint8_t **verification_token, size_t *verification_token_length);
-
-};
-typedef struct keyguard_device keyguard_device_t;
-
-static inline int keyguard_open(const struct hw_module_t *module,
- keyguard_device_t **device) {
- return module->methods->open(module, HARDWARE_KEYGUARD,
- (struct hw_device_t **) device);
-}
-
-static inline int keyguard_close(keyguard_device_t *device) {
- return device->common.close(&device->common);
-}
-
-__END_DECLS
-
-#endif // ANDROID_HARDWARE_KEYGUARD_H
diff --git a/include/hardware/keymaster_defs.h b/include/hardware/keymaster_defs.h
index 2e93dc6..664d3a7 100644
--- a/include/hardware/keymaster_defs.h
+++ b/include/hardware/keymaster_defs.h
@@ -21,9 +21,9 @@
#include <stdlib.h>
#include <string.h>
-#if defined(__cplusplus)
+#ifndef __cplusplus
extern "C" {
-#endif // defined(__cplusplus)
+#endif // __cplusplus
/**
* Authorization tags each have an associated type. This enumeration facilitates tagging each with
@@ -128,6 +128,9 @@
KM_TAG_NONCE = KM_BYTES | 1001, /* Nonce or Initialization Vector */
KM_TAG_CHUNK_LENGTH = KM_INT | 1002, /* AEAD mode chunk size, in bytes. 0 means no limit,
which requires KM_TAG_RETURN_UNAUTHED. */
+ KM_TAG_AUTH_TOKEN = KM_BYTES | 1003, /* Authentication token that proves secure user
+ authentication has been performed. Structure
+ defined in hw_auth_token_t in hw_auth_token.h. */
} keymaster_tag_t;
/**
@@ -371,6 +374,11 @@
KM_ERROR_SECURE_HW_BUSY = -48,
KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49,
KM_ERROR_UNSUPPORTED_EC_FIELD = -50,
+ KM_ERROR_MISSING_NONCE = -51,
+ KM_ERROR_INVALID_NONCE = -52,
+ KM_ERROR_UNSUPPORTED_CHUNK_LENGTH = -53,
+ KM_ERROR_RESCOPABLE_KEY_NOT_USABLE = -54,
+
KM_ERROR_UNIMPLEMENTED = -100,
KM_ERROR_VERSION_MISMATCH = -101,
@@ -461,6 +469,54 @@
return param;
}
+#define KEYMASTER_SIMPLE_COMPARE(a, b) (a < b) ? -1 : ((a > b) ? 1 : 0)
+inline int keymaster_param_compare(const keymaster_key_param_t* a, const keymaster_key_param_t* b) {
+ int retval = KEYMASTER_SIMPLE_COMPARE(a->tag, b->tag);
+ if (retval != 0)
+ return retval;
+
+ switch (keymaster_tag_get_type(a->tag)) {
+ case KM_INVALID:
+ case KM_BOOL:
+ return 0;
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ return KEYMASTER_SIMPLE_COMPARE(a->enumerated, b->enumerated);
+ case KM_INT:
+ case KM_INT_REP:
+ return KEYMASTER_SIMPLE_COMPARE(a->integer, b->integer);
+ case KM_LONG:
+ return KEYMASTER_SIMPLE_COMPARE(a->long_integer, b->long_integer);
+ case KM_DATE:
+ return KEYMASTER_SIMPLE_COMPARE(a->date_time, b->date_time);
+ case KM_BIGNUM:
+ case KM_BYTES:
+ // Handle the empty cases.
+ if (a->blob.data_length != 0 && b->blob.data_length == 0)
+ return -1;
+ if (a->blob.data_length == 0 && b->blob.data_length == 0)
+ return 0;
+ if (a->blob.data_length == 0 && b->blob.data_length > 0)
+ return 1;
+
+ retval = memcmp(a->blob.data, b->blob.data, a->blob.data_length < b->blob.data_length
+ ? a->blob.data_length
+ : b->blob.data_length);
+ if (retval != 0)
+ return retval;
+ else if (a->blob.data_length != b->blob.data_length) {
+ // Equal up to the common length; longer one is larger.
+ if (a->blob.data_length < b->blob.data_length)
+ return -1;
+ if (a->blob.data_length > b->blob.data_length)
+ return 1;
+ };
+ }
+
+ return 0;
+}
+#undef KEYMASTER_SIMPLE_COMPARE
+
inline void keymaster_free_param_values(keymaster_key_param_t* param, size_t param_count) {
while (param_count-- > 0) {
switch (keymaster_tag_get_type(param->tag)) {
@@ -492,8 +548,8 @@
}
}
-#if defined(__cplusplus)
+#ifndef __cplusplus
} // extern "C"
-#endif // defined(__cplusplus)
+#endif // __cplusplus
#endif // ANDROID_HARDWARE_KEYMASTER_DEFS_H
diff --git a/include/hardware/radio.h b/include/hardware/radio.h
new file mode 100644
index 0000000..145deb5
--- /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/modules/radio/Android.mk b/modules/radio/Android.mk
new file mode 100644
index 0000000..f433c85
--- /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 0000000..b1a4d26
--- /dev/null
+++ b/modules/radio/radio_hw.c
@@ -0,0 +1,726 @@
+/*
+ * 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;
+
+ 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 ((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;
+
+ 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;
+ if (tuner->program.metadata != NULL)
+ radio_metadata_deallocate(tuner->program.metadata);
+ tuner->program.metadata = NULL;
+ send_command_l(tuner, CMD_METADATA, 2000, NULL);
+ } 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;
+ } break;
+
+ case CMD_METADATA: {
+ prepare_metadata(tuner, &tuner->program.metadata, false);
+ event.type = RADIO_EVENT_METADATA;
+ event.metadata = tuner->program.metadata;
+ } break;
+
+ case CMD_CANCEL: {
+ struct listnode *tmp2;
+ list_for_each_safe(item, tmp2, &tuner->command_list) {
+ cmd = node_to_item(item, struct thread_command, node);
+ if (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
+ cmd->type == CMD_TUNE || cmd->type == CMD_METADATA) {
+ list_remove(item);
+ free(cmd);
+ }
+ }
+ } 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);
+ }
+ 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;
+ }
+ }
+ }
+ }
+
+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)
+ 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/tests/camera2/CameraMultiStreamTests.cpp b/tests/camera2/CameraMultiStreamTests.cpp
index 1edc28e..bfadfea 100644
--- a/tests/camera2/CameraMultiStreamTests.cpp
+++ b/tests/camera2/CameraMultiStreamTests.cpp
@@ -209,7 +209,7 @@
ASSERT_EQ(OK,
mDevice->createStream(mNativeWindow,
mWidth, mHeight, mFormat, HAL_DATASPACE_UNKNOWN,
- &mStreamId));
+ CAMERA3_STREAM_ROTATION_0, &mStreamId));
ASSERT_NE(-1, mStreamId);
}
diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h
index ff110d8..8a8c27d 100644
--- a/tests/camera2/CameraStreamFixture.h
+++ b/tests/camera2/CameraStreamFixture.h
@@ -258,6 +258,7 @@
device->createStream(mNativeWindow,
mWidth, mHeight, format,
HAL_DATASPACE_UNKNOWN,
+ CAMERA3_STREAM_ROTATION_0,
&mStreamId));
ASSERT_NE(-1, mStreamId);