am 0ab04716: (-s ours) Reconcile with jb-mr2-zeroday-release - do not merge
* commit '0ab04716dcb05f085edcc80bbed3ecefcbec9c46':
diff --git a/include/hardware/fused_location.h b/include/hardware/fused_location.h
new file mode 100644
index 0000000..4d6d5a0
--- /dev/null
+++ b/include/hardware/fused_location.h
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_HARDWARE_FUSED_LOCATION_H
+#define ANDROID_INCLUDE_HARDWARE_FUSED_LOCATION_H
+
+#include <hardware/hardware.h>
+
+
+/**
+ * This header file defines the interface of the Fused Location Provider.
+ * Fused Location Provider is designed to fuse data from various sources
+ * like GPS, Wifi, Cell, Sensors, Bluetooth etc to provide a fused location to the
+ * upper layers. The advantage of doing fusion in hardware is power savings.
+ * The goal is to do this without waking up the AP to get additional data.
+ * The software implementation of FLP will decide when to use
+ * the hardware fused location. Other location features like geofencing will
+ * also be implemented using fusion in hardware.
+ */
+__BEGIN_DECLS
+
+#define FLP_HEADER_VERSION 1
+#define FLP_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+#define FLP_DEVICE_API_VERSION_0_1 HARDWARE_DEVICE_API_VERSION_2(0, 1, FLP_HEADER_VERSION)
+
+/**
+ * The id of this module
+ */
+#define FUSED_LOCATION_HARDWARE_MODULE_ID "flp"
+
+/**
+ * Name for the FLP location interface
+ */
+#define FLP_LOCATION_INTERFACE "flp_location"
+
+/**
+ * Name for the FLP location interface
+ */
+#define FLP_DIAGNOSTIC_INTERFACE "flp_diagnostic"
+
+/**
+ * Name for the FLP_Geofencing interface.
+ */
+#define FLP_GEOFENCING_INTERFACE "flp_geofencing"
+
+/**
+ * Name for the FLP_device context interface.
+ */
+#define FLP_DEVICE_CONTEXT_INTERFACE "flp_device_context"
+
+/**
+ * Constants to indicate the various subsystems
+ * that will be used.
+ */
+#define FLP_TECH_MASK_GNSS (1U<<0)
+#define FLP_TECH_MASK_WIFI (1U<<1)
+#define FLP_TECH_MASK_SENSORS (1U<<2)
+#define FLP_TECH_MASK_CELL (1U<<3)
+#define FLP_TECH_MASK_BLUETOOTH (1U<<4)
+
+/**
+ * This constant is used with the batched locations
+ * APIs. Batching is mandatory when FLP implementation
+ * is supported. If the flag is set, the hardware implementation
+ * will wake up the application processor when the FIFO is full,
+ * If the flag is not set, the hardware implementation will drop
+ * the oldest data when the FIFO is full.
+ */
+#define FLP_BATCH_WAKEUP_ON_FIFO_FULL 0x0000001
+
+/**
+ * While batching, the implementation should not call the
+ * flp_location_callback on every location fix. However,
+ * sometimes in high power mode, the system might need
+ * a location callback every single time the location
+ * fix has been obtained. This flag controls that option.
+ * Its the responsibility of the upper layers (caller) to switch
+ * it off, if it knows that the AP might go to sleep.
+ * When this bit is on amidst a batching session, batching should
+ * continue while location fixes are reported in real time.
+ */
+#define FLP_BATCH_CALLBACK_ON_LOCATION_FIX 0x0000002
+
+/** Flags to indicate which values are valid in a FlpLocation. */
+typedef uint16_t FlpLocationFlags;
+
+// IMPORTANT: Note that the following values must match
+// constants in the corresponding java file.
+
+/** FlpLocation has valid latitude and longitude. */
+#define FLP_LOCATION_HAS_LAT_LONG (1U<<0)
+/** FlpLocation has valid altitude. */
+#define FLP_LOCATION_HAS_ALTITUDE (1U<<1)
+/** FlpLocation has valid speed. */
+#define FLP_LOCATION_HAS_SPEED (1U<<2)
+/** FlpLocation has valid bearing. */
+#define FLP_LOCATION_HAS_BEARING (1U<<4)
+/** FlpLocation has valid accuracy. */
+#define FLP_LOCATION_HAS_ACCURACY (1U<<8)
+
+
+typedef int64_t FlpUtcTime;
+
+/** Represents a location. */
+typedef struct {
+ /** set to sizeof(FlpLocation) */
+ size_t size;
+
+ /** Flags associated with the location object. */
+ FlpLocationFlags flags;
+
+ /** Represents latitude in degrees. */
+ double latitude;
+
+ /** Represents longitude in degrees. */
+ double longitude;
+
+ /**
+ * Represents altitude in meters above the WGS 84 reference
+ * ellipsoid. */
+ double altitude;
+
+ /** Represents speed in meters per second. */
+ float speed;
+
+ /** Represents heading in degrees. */
+ float bearing;
+
+ /** Represents expected accuracy in meters. */
+ float accuracy;
+
+ /** Timestamp for the location fix. */
+ FlpUtcTime timestamp;
+
+ /** Sources used, will be Bitwise OR of the FLP_TECH_MASK bits. */
+ uint32_t sources_used;
+} FlpLocation;
+
+typedef enum {
+ ASSOCIATE_JVM,
+ DISASSOCIATE_JVM,
+} ThreadEvent;
+
+/**
+ * Callback with location information.
+ * Can only be called from a thread created by create_thread_cb.
+ * Parameters:
+ * num_locations is the number of batched locations available.
+ * location is the pointer to an array of pointers to location objects.
+ */
+typedef void (*flp_location_callback)(int32_t num_locations, FlpLocation** location);
+
+/**
+ * Callback utility for acquiring a wakelock.
+ * This can be used to prevent the CPU from suspending while handling FLP events.
+ */
+typedef void (*flp_acquire_wakelock)();
+
+/**
+ * Callback utility for releasing the FLP wakelock.
+ */
+typedef void (*flp_release_wakelock)();
+
+/**
+ * Callback for creating a thread that can call into the Java framework code.
+ * This must be used to create any threads that report events up to the framework.
+ */
+typedef pthread_t (*flp_create_thread)(ThreadEvent event);
+
+/** FLP callback structure. */
+typedef struct {
+ /** set to sizeof(FlpCallbacks) */
+ size_t size;
+ flp_location_callback location_cb;
+ flp_acquire_wakelock acquire_wakelock_cb;
+ flp_release_wakelock release_wakelock_cb;
+ flp_create_thread create_thread_cb;
+} FlpCallbacks;
+
+
+/** Options with the batching FLP APIs */
+typedef struct {
+ /**
+ * Maximum power in mW that the underlying implementation
+ * can use for this batching call.
+ * If max_power_allocation_mW is 0, only fixes that are generated
+ * at no additional cost of power shall be reported.
+ */
+ double max_power_allocation_mW;
+
+ /** Bitwise OR of the FLP_TECH_MASKS to use */
+ uint32_t sources_to_use;
+
+ /**
+ * FLP_BATCH_WAKEUP_ON_FIFO_FULL - If set the hardware
+ * will wake up the AP when the buffer is full. If not set, the
+ * hardware will drop the oldest location object.
+ *
+ * FLP_BATCH_CALLBACK_ON_LOCATION_FIX - If set the location
+ * callback will be called every time there is a location fix.
+ * Its the responsibility of the upper layers (caller) to switch
+ * it off, if it knows that the AP might go to sleep. When this
+ * bit is on amidst a batching session, batching should continue
+ * while location fixes are reported in real time.
+ *
+ * Other flags to be bitwised ORed in the future.
+ */
+ uint32_t flags;
+
+ /**
+ * Frequency with which location needs to be batched in nano
+ * seconds.
+ */
+ int64_t period_ns;
+} FlpBatchOptions;
+
+#define FLP_RESULT_SUCCESS 0
+#define FLP_RESULT_ERROR -1
+#define FLP_RESULT_INSUFFICIENT_MEMORY -2
+#define FLP_RESULT_TOO_MANY_GEOFENCES -3
+#define FLP_RESULT_ID_EXISTS -4
+#define FLP_RESULT_ID_UNKNOWN -5
+#define FLP_RESULT_INVALID_GEOFENCE_TRANSITION -6
+
+/**
+ * Represents the standard FLP interface.
+ */
+typedef struct {
+ /**
+ * set to sizeof(FlpLocationInterface)
+ */
+ size_t size;
+
+ /**
+ * Opens the interface and provides the callback routines
+ * to the implemenation of this interface.
+ */
+ int (*init)(FlpCallbacks* callbacks );
+
+ /**
+ * Return the batch size (in number of FlpLocation objects)
+ * available in the hardware. Note, different HW implementations
+ * may have different sample sizes. This shall return number
+ * of samples defined in the format of FlpLocation.
+ * This will be used by the upper layer, to decide on the batching
+ * interval and whether the AP should be woken up or not.
+ */
+ int (*get_batch_size)();
+
+ /**
+ * Start batching locations. This API is primarily used when the AP is
+ * asleep and the device can batch locations in the hardware.
+ * flp_location_callback is used to return the locations. When the buffer
+ * is full and FLP_BATCH_WAKEUP_ON_FIFO_FULL is used, the AP is woken up.
+ * When the buffer is full and FLP_BATCH_WAKEUP_ON_FIFO_FULL is not set,
+ * the oldest location object is dropped. In this case the AP will not be
+ * woken up. The upper layer will use get_batched_location
+ * API to explicitly ask for the location.
+ * If FLP_BATCH_CALLBACK_ON_LOCATION_FIX is set, the implementation
+ * will call the flp_location_callback every single time there is a location
+ * fix. This overrides FLP_BATCH_WAKEUP_ON_FIFO_FULL flag setting.
+ * It's the responsibility of the upper layers (caller) to switch
+ * it off, if it knows that the AP might go to sleep. This is useful
+ * for nagivational applications when the system is in high power mode.
+ * Parameters:
+ * id - Id for the request.
+ * options - See FlpBatchOptions struct definition.
+ * Return value:
+ * FLP_RESULT_SUCCESS on success, FLP_RESULT_INSUFFICIENT_MEMORY,
+ * FLP_RESULT_ID_EXISTS, FLP_RESULT_ERROR on failure.
+ */
+ int (*start_batching)(int id, FlpBatchOptions* options);
+
+ /**
+ * Update FlpBatchOptions associated with a batching request.
+ * When a batching operation is in progress and a batching option
+ * such as FLP_BATCH_WAKEUP_ON_FIFO_FULL needs to be updated, this API
+ * will be used. For instance, this can happen when the AP is awake and
+ * the maps application is being used.
+ * Parameters:
+ * id - Id of an existing batch request.
+ * new_options - Updated FlpBatchOptions
+ * Return value:
+ * FLP_RESULT_SUCCESS on success, FLP_RESULT_ID_UNKNOWN,
+ * FLP_RESULT_ERROR on error.
+ */
+ int (*update_batching_options)(int id, FlpBatchOptions* new_options);
+
+ /**
+ * Stop batching.
+ * Parameters:
+ * id - Id for the request.
+ * Return Value:
+ * FLP_RESULT_SUCCESS on success, FLP_RESULT_ID_UNKNOWN or
+ * FLP_RESULT_ERROR on failure.
+ */
+ int (*stop_batching)(int id);
+
+ /**
+ * Closes the interface. If any batch operations are in progress,
+ * they should be stopped.
+ */
+ void (*cleanup)();
+
+ /**
+ * Get the fused location that was batched.
+ * flp_location_callback is used to return the location. The location object
+ * is dropped from the buffer only when the buffer is full. Do not remove it
+ * from the buffer just because it has been returned using the callback.
+ * In other words, when there is no new location object, two calls to
+ * get_batched_location(1) should return the same location object.
+ * Parameters:
+ * last_n_locations - Number of locations to get. This can be one or many.
+ * If the last_n_locations is 1, you get the latest location known to the
+ * hardware.
+ */
+ void (*get_batched_location)(int last_n_locations);
+
+ /**
+ * Injects current location from another location provider
+ * latitude and longitude are measured in degrees
+ * expected accuracy is measured in meters
+ * Parameters:
+ * location - The location object being injected.
+ * Return value: FLP_RESULT_SUCCESS or FLP_RESULT_ERROR.
+ */
+ int (*inject_location)(FlpLocation* location);
+
+ /**
+ * Get a pointer to extension information.
+ */
+ const void* (*get_extension)(const char* name);
+} FlpLocationInterface;
+
+struct flp_device_t {
+ struct hw_device_t common;
+
+ /**
+ * Get a handle to the FLP Interface.
+ */
+ const FlpLocationInterface* (*get_flp_interface)(struct flp_device_t* dev);
+};
+
+/**
+ * Callback for reports diagnostic data into the Java framework code.
+*/
+typedef void (*report_data)(char* data, int length);
+
+/**
+ * FLP diagnostic callback structure.
+ * Currently, not used - but this for future extension.
+ */
+typedef struct {
+ /** set to sizeof(FlpDiagnosticCallbacks) */
+ size_t size;
+
+ flp_create_thread create_thread_cb;
+
+ /** reports diagnostic data into the Java framework code */
+ report_data data_cb;
+} FlpDiagnosticCallbacks;
+
+/** Extended interface for diagnostic support. */
+typedef struct {
+ /** set to sizeof(FlpDiagnosticInterface) */
+ size_t size;
+
+ /**
+ * Opens the diagnostic interface and provides the callback routines
+ * to the implemenation of this interface.
+ */
+ void (*init)(FlpDiagnosticCallbacks* callbacks);
+
+ /**
+ * Injects diagnostic data into the FLP subsystem.
+ * Return 0 on success, -1 on error.
+ **/
+ int (*inject_data)(char* data, int length );
+} FlpDiagnosticInterface;
+
+/**
+ * Context setting information.
+ * All these settings shall be injected to FLP HAL at FLP init time.
+ * Following that, only the changed setting need to be re-injected
+ * upon changes.
+ */
+
+#define FLP_DEVICE_CONTEXT_GPS_ENABLED (1U<<0)
+#define FLP_DEVICE_CONTEXT_AGPS_ENABLED (1U<<1)
+#define FLP_DEVICE_CONTEXT_NETWORK_POSITIONING_ENABLED (1U<<2)
+#define FLP_DEVICE_CONTEXT_WIFI_CONNECTIVITY_ENABLED (1U<<3)
+#define FLP_DEVICE_CONTEXT_WIFI_POSITIONING_ENABLED (1U<<4)
+#define FIP_DEVICE_CONTEXT_HW_NETWORK_POSITIONING ENABLED (1U<<5)
+#define FLP_DEVICE_CONTEXT_AIRPLANE_MODE_ON (1U<<6)
+#define FLP_DEVICE_CONTEXT_DATA_ENABLED (1U<<7)
+#define FLP_DEVICE_CONTEXT_ROAMING_ENABLED (1U<<8)
+#define FLP_DEVICE_CONTEXT_CURRENTLY_ROAMING (1U<<9)
+#define FLP_DEVICE_CONTEXT_SENSOR_ENABLED (1U<<10)
+#define FLP_DEVICE_CONTEXT_BLUETOOTH_ENABLED (1U<<11)
+#define FLP_DEVICE_CONTEXT_CHARGER_ON (1U<<12)
+
+/** Extended interface for device context support. */
+typedef struct {
+ /** set to sizeof(FlpDeviceContextInterface) */
+ size_t size;
+
+ /**
+ * Injects debug data into the FLP subsystem.
+ * Return 0 on success, -1 on error.
+ **/
+ int (*inject_device_context)(uint32_t enabledMask);
+} FlpDeviceContextInterface;
+
+
+/**
+ * There are 3 states associated with a Geofence: Inside, Outside, Unknown.
+ * There are 3 transitions: ENTERED, EXITED, UNCERTAIN.
+ *
+ * An example state diagram with confidence level: 95% and Unknown time limit
+ * set as 30 secs is shown below. (confidence level and Unknown time limit are
+ * explained latter)
+ * ____________________________
+ * | Unknown (30 secs) |
+ * """"""""""""""""""""""""""""
+ * ^ | | ^
+ * UNCERTAIN| |ENTERED EXITED| |UNCERTAIN
+ * | v v |
+ * ________ EXITED _________
+ * | Inside | -----------> | Outside |
+ * | | <----------- | |
+ * """""""" ENTERED """""""""
+ *
+ * Inside state: We are 95% confident that the user is inside the geofence.
+ * Outside state: We are 95% confident that the user is outside the geofence
+ * Unknown state: Rest of the time.
+ *
+ * The Unknown state is better explained with an example:
+ *
+ * __________
+ * | c|
+ * | ___ | _______
+ * | |a| | | b |
+ * | """ | """""""
+ * | |
+ * """"""""""
+ * In the diagram above, "a" and "b" are 2 geofences and "c" is the accuracy
+ * circle reported by the FLP subsystem. Now with regard to "b", the system is
+ * confident that the user is outside. But with regard to "a" is not confident
+ * whether it is inside or outside the geofence. If the accuracy remains the
+ * same for a sufficient period of time, the UNCERTAIN transition would be
+ * triggered with the state set to Unknown. If the accuracy improves later, an
+ * appropriate transition should be triggered. This "sufficient period of time"
+ * is defined by the parameter in the add_geofence_area API.
+ * In other words, Unknown state can be interpreted as a state in which the
+ * FLP subsystem isn't confident enough that the user is either inside or
+ * outside the Geofence. It moves to Unknown state only after the expiry of the
+ * timeout.
+ *
+ * The geofence callback needs to be triggered for the ENTERED and EXITED
+ * transitions, when the FLP system is confident that the user has entered
+ * (Inside state) or exited (Outside state) the Geofence. An implementation
+ * which uses a value of 95% as the confidence is recommended. The callback
+ * should be triggered only for the transitions requested by the
+ * add_geofence_area call.
+ *
+ * Even though the diagram and explanation talks about states and transitions,
+ * the callee is only interested in the transistions. The states are mentioned
+ * here for illustrative purposes.
+ *
+ * Startup Scenario: When the device boots up, if an application adds geofences,
+ * and then we get an accurate FLP location fix, it needs to trigger the
+ * appropriate (ENTERED or EXITED) transition for every Geofence it knows about.
+ * By default, all the Geofences will be in the Unknown state.
+ *
+ * When the FLP system is unavailable, flp_geofence_status_callback should be
+ * called to inform the upper layers of the same. Similarly, when it becomes
+ * available the callback should be called. This is a global state while the
+ * UNKNOWN transition described above is per geofence.
+ *
+ */
+#define FLP_GEOFENCE_TRANSITION_ENTERED (1L<<0)
+#define FLP_GEOFENCE_TRANSITION_EXITED (1L<<1)
+#define FLP_GEOFENCE_TRANSITION_UNCERTAIN (1L<<2)
+
+#define FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE (1L<<0)
+#define FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE (1L<<1)
+
+/**
+ * The callback associated with the geofence.
+ * Parameters:
+ * geofence_id - The id associated with the add_geofence_area.
+ * location - The current location as determined by the FLP subsystem.
+ * transition - Can be one of FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED,
+ * FLP_GEOFENCE_TRANSITION_UNCERTAIN.
+ * timestamp - Timestamp when the transition was detected; -1 if not available.
+ * sources_used - Bitwise OR of FLP_TECH_MASK flags indicating which
+ * subsystems were used.
+ *
+ * The callback should only be called when the caller is interested in that
+ * particular transition. For instance, if the caller is interested only in
+ * ENTERED transition, then the callback should NOT be called with the EXITED
+ * transition.
+ *
+ * IMPORTANT: If a transition is triggered resulting in this callback, the
+ * subsystem will wake up the application processor, if its in suspend state.
+ */
+typedef void (*flp_geofence_transition_callback) (int32_t geofence_id, FlpLocation* location,
+ int32_t transition, FlpUtcTime timestamp, uint32_t sources_used);
+
+/**
+ * The callback associated with the availablity of one the sources used for geofence
+ * monitoring by the FLP sub-system For example, if the GPS system determines that it cannot
+ * monitor geofences because of lack of reliability or unavailability of the GPS signals,
+ * it will call this callback with FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE parameter and the
+ * source set to FLP_TECH_MASK_GNSS.
+ *
+ * Parameters:
+ * status - FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE or FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE.
+ * source - One of the FLP_TECH_MASKS
+ * last_location - Last known location.
+ */
+typedef void (*flp_geofence_monitor_status_callback) (int32_t status, uint32_t source,
+ FlpLocation* last_location);
+
+/**
+ * The callback associated with the add_geofence call.
+ *
+ * Parameter:
+ * geofence_id - Id of the geofence.
+ * result - FLP_RESULT_SUCCESS
+ * FLP_RESULT_ERROR_TOO_MANY_GEOFENCES - geofence limit has been reached.
+ * FLP_RESULT_ID_EXISTS - geofence with id already exists
+ * FLP_RESULT_INVALID_GEOFENCE_TRANSITION - the monitorTransition contains an
+ * invalid transition
+ * FLP_RESULT_ERROR - for other errors.
+ */
+typedef void (*flp_geofence_add_callback) (int32_t geofence_id, int32_t result);
+
+/**
+ * The callback associated with the remove_geofence call.
+ *
+ * Parameter:
+ * geofence_id - Id of the geofence.
+ * result - FLP_RESULT_SUCCESS
+ * FLP_RESULT_ID_UNKNOWN - for invalid id
+ * FLP_RESULT_ERROR for others.
+ */
+typedef void (*flp_geofence_remove_callback) (int32_t geofence_id, int32_t result);
+
+
+/**
+ * The callback associated with the pause_geofence call.
+ *
+ * Parameter:
+ * geofence_id - Id of the geofence.
+ * result - FLP_RESULT_SUCCESS
+ * FLP_RESULT__ID_UNKNOWN - for invalid id
+ * FLP_RESULT_INVALID_TRANSITION -
+ * when monitor_transitions is invalid
+ * FLP_RESULT_ERROR for others.
+ */
+typedef void (*flp_geofence_pause_callback) (int32_t geofence_id, int32_t result);
+
+/**
+ * The callback associated with the resume_geofence call.
+ *
+ * Parameter:
+ * geofence_id - Id of the geofence.
+ * result - FLP_RESULT_SUCCESS
+ * FLP_RESULT_ID_UNKNOWN - for invalid id
+ * FLP_RESULT_ERROR for others.
+ */
+typedef void (*flp_geofence_resume_callback) (int32_t geofence_id, int32_t result);
+
+typedef struct {
+ flp_geofence_transition_callback geofence_transition_callback;
+ flp_geofence_monitor_status_callback geofence_status_callback;
+ flp_geofence_add_callback geofence_add_callback;
+ flp_geofence_remove_callback geofence_remove_callback;
+ flp_geofence_pause_callback geofence_pause_callback;
+ flp_geofence_resume_callback geofence_resume_callback;
+ flp_create_thread create_thread_cb;
+} FlpGeofenceCallbacks;
+
+
+/** Type of geofence */
+typedef enum {
+ TYPE_CIRCLE = 0,
+} GeofenceType;
+
+/** Circular geofence is represented by lat / long / radius */
+typedef struct {
+ double latitude;
+ double longitude;
+ double radius_m;
+} GeofenceCircle;
+
+/** Represents the type of geofence and data */
+typedef struct {
+ GeofenceType type;
+ union {
+ GeofenceCircle circle;
+ } geofence;
+} GeofenceData;
+
+/** Geofence Options */
+typedef struct {
+ /**
+ * The current state of the geofence. For example, if
+ * the system already knows that the user is inside the geofence,
+ * this will be set to FLP_GEOFENCE_TRANSITION_ENTERED. In most cases, it
+ * will be FLP_GEOFENCE_TRANSITION_UNCERTAIN. */
+ int last_transition;
+
+ /**
+ * Transitions to monitor. Bitwise OR of
+ * FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED and
+ * FLP_GEOFENCE_TRANSITION_UNCERTAIN.
+ */
+ int monitor_transitions;
+
+ /**
+ * Defines the best-effort description
+ * of how soon should the callback be called when the transition
+ * associated with the Geofence is triggered. For instance, if set
+ * to 1000 millseconds with FLP_GEOFENCE_TRANSITION_ENTERED, the callback
+ * should be called 1000 milliseconds within entering the geofence.
+ * This parameter is defined in milliseconds.
+ * NOTE: This is not to be confused with the rate that the GPS is
+ * polled at. It is acceptable to dynamically vary the rate of
+ * sampling the GPS for power-saving reasons; thus the rate of
+ * sampling may be faster or slower than this.
+ */
+ int notification_responsivenes_ms;
+
+ /**
+ * The time limit after which the UNCERTAIN transition
+ * should be triggered. This paramter is defined in milliseconds.
+ */
+ int unknown_timer_ms;
+
+ /**
+ * The sources to use for monitoring geofences. Its a BITWISE-OR
+ * of FLP_TECH_MASK flags.
+ */
+ uint32_t sources_to_use;
+} GeofenceOptions;
+
+/** Geofence struct */
+typedef struct {
+ int32_t geofence_id;
+ GeofenceData* data;
+ GeofenceOptions* options;
+} Geofence;
+
+/** Extended interface for FLP_Geofencing support */
+typedef struct {
+ /** set to sizeof(FlpGeofencingInterface) */
+ size_t size;
+
+ /**
+ * Opens the geofence interface and provides the callback routines
+ * to the implemenation of this interface.
+ */
+ void (*init)( FlpGeofenceCallbacks* callbacks );
+
+ /**
+ * Add a list of geofences.
+ * Parameters:
+ * number_of_geofences - The number of geofences that needed to be added.
+ * geofences - Pointer to array of pointers to Geofence structure.
+ */
+ void (*add_geofences) (int32_t number_of_geofences, Geofence** geofences);
+
+ /**
+ * Pause monitoring a particular geofence.
+ * Parameters:
+ * geofence_id - The id for the geofence.
+ */
+ void (*pause_geofence) (int32_t geofence_id);
+
+ /**
+ * Resume monitoring a particular geofence.
+ * Parameters:
+ * geofence_id - The id for the geofence.
+ * monitor_transitions - Which transitions to monitor. Bitwise OR of
+ * FLP_GEOFENCE_TRANSITION_ENTERED, FLP_GEOFENCE_TRANSITION_EXITED and
+ * FLP_GEOFENCE_TRANSITION_UNCERTAIN.
+ * This supersedes the value associated provided in the
+ * add_geofence_area call.
+ */
+ void (*resume_geofence) (int32_t geofence_id, int monitor_transitions);
+
+ /**
+ * Modify a particular geofence option.
+ * Parameters:
+ * geofence_id - The id for the geofence.
+ * options - Various options associated with the geofence. See
+ * GeofenceOptions structure for details.
+ */
+ void (*modify_geofence_option) (int32_t geofence_id, GeofenceOptions* options);
+
+ /**
+ * Remove a list of geofences. After the function returns, no notifications
+ * should be sent.
+ * Parameter:
+ * number_of_geofences - The number of geofences that needed to be added.
+ * geofence_id - Pointer to array of geofence_ids to be removed.
+ */
+ void (*remove_geofences) (int32_t number_of_geofences, int32_t* geofence_id);
+} FlpGeofencingInterface;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_HARDWARE_FLP_H */
+
diff --git a/modules/camera/Android.mk b/modules/camera/Android.mk
index eebffc1..5d26934 100644
--- a/modules/camera/Android.mk
+++ b/modules/camera/Android.mk
@@ -26,11 +26,13 @@
LOCAL_SRC_FILES := \
CameraHAL.cpp \
Camera.cpp \
+ Stream.cpp \
LOCAL_SHARED_LIBRARIES := \
libcamera_metadata \
libcutils \
liblog \
+ libsync \
LOCAL_CFLAGS += -Wall -Wextra -fvisibility=hidden
diff --git a/modules/camera/Camera.cpp b/modules/camera/Camera.cpp
index 203b772..718d42b 100644
--- a/modules/camera/Camera.cpp
+++ b/modules/camera/Camera.cpp
@@ -17,7 +17,10 @@
#include <cstdlib>
#include <pthread.h>
#include <hardware/camera3.h>
+#include <sync/sync.h>
+#include <system/camera_metadata.h>
#include "CameraHAL.h"
+#include "Stream.h"
//#define LOG_NDEBUG 0
#define LOG_TAG "Camera"
@@ -25,9 +28,12 @@
#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
#include <cutils/trace.h>
+#include "ScopedTrace.h"
#include "Camera.h"
+#define CAMERA_SYNC_TIMEOUT 5000 // in msecs
+
namespace default_camera_hal {
extern "C" {
@@ -43,7 +49,10 @@
Camera::Camera(int id)
: mId(id),
mBusy(false),
- mCallbackOps(NULL)
+ mCallbackOps(NULL),
+ mStreams(NULL),
+ mNumStreams(0),
+ mSettings(NULL)
{
pthread_mutex_init(&mMutex,
NULL); // No pthread mutex attributes.
@@ -62,11 +71,10 @@
int Camera::open(const hw_module_t *module, hw_device_t **device)
{
ALOGI("%s:%d: Opening camera device", __func__, mId);
- ATRACE_BEGIN(__func__);
+ CAMTRACE_CALL();
pthread_mutex_lock(&mMutex);
if (mBusy) {
pthread_mutex_unlock(&mMutex);
- ATRACE_END();
ALOGE("%s:%d: Error! Camera device already opened", __func__, mId);
return -EBUSY;
}
@@ -77,18 +85,16 @@
*device = &mDevice.common;
pthread_mutex_unlock(&mMutex);
- ATRACE_END();
return 0;
}
int Camera::close()
{
ALOGI("%s:%d: Closing camera device", __func__, mId);
- ATRACE_BEGIN(__func__);
+ CAMTRACE_CALL();
pthread_mutex_lock(&mMutex);
if (!mBusy) {
pthread_mutex_unlock(&mMutex);
- ATRACE_END();
ALOGE("%s:%d: Error! Camera device not open", __func__, mId);
return -EINVAL;
}
@@ -97,7 +103,6 @@
mBusy = false;
pthread_mutex_unlock(&mMutex);
- ATRACE_END();
return 0;
}
@@ -108,18 +113,173 @@
return 0;
}
-int Camera::configureStreams(camera3_stream_configuration_t *stream_list)
+int Camera::configureStreams(camera3_stream_configuration_t *stream_config)
{
- ALOGV("%s:%d: stream_list=%p", __func__, mId, stream_list);
- // TODO: validate input, create internal stream representations
+ camera3_stream_t *astream;
+ Stream **newStreams = NULL;
+
+ CAMTRACE_CALL();
+ ALOGV("%s:%d: stream_config=%p", __func__, mId, stream_config);
+
+ if (stream_config == NULL) {
+ ALOGE("%s:%d: NULL stream configuration array", __func__, mId);
+ return -EINVAL;
+ }
+ if (stream_config->num_streams == 0) {
+ ALOGE("%s:%d: Empty stream configuration array", __func__, mId);
+ return -EINVAL;
+ }
+
+ // Create new stream array
+ newStreams = new Stream*[stream_config->num_streams];
+ ALOGV("%s:%d: Number of Streams: %d", __func__, mId,
+ stream_config->num_streams);
+
+ pthread_mutex_lock(&mMutex);
+
+ // Mark all current streams unused for now
+ for (int i = 0; i < mNumStreams; i++)
+ mStreams[i]->mReuse = false;
+ // Fill new stream array with reused streams and new streams
+ for (int i = 0; i < stream_config->num_streams; i++) {
+ astream = stream_config->streams[i];
+ if (astream->max_buffers > 0)
+ newStreams[i] = reuseStream(astream);
+ else
+ newStreams[i] = new Stream(mId, astream);
+
+ if (newStreams[i] == NULL) {
+ ALOGE("%s:%d: Error processing stream %d", __func__, mId, i);
+ goto err_out;
+ }
+ astream->priv = newStreams[i];
+ }
+
+ // Verify the set of streams in aggregate
+ if (!isValidStreamSet(newStreams, stream_config->num_streams)) {
+ ALOGE("%s:%d: Invalid stream set", __func__, mId);
+ goto err_out;
+ }
+
+ // Set up all streams (calculate usage/max_buffers for each)
+ setupStreams(newStreams, stream_config->num_streams);
+
+ // Destroy all old streams and replace stream array with new one
+ destroyStreams(mStreams, mNumStreams);
+ mStreams = newStreams;
+ mNumStreams = stream_config->num_streams;
+
+ // Clear out last seen settings metadata
+ setSettings(NULL);
+
+ pthread_mutex_unlock(&mMutex);
return 0;
+
+err_out:
+ // Clean up temporary streams, preserve existing mStreams/mNumStreams
+ destroyStreams(newStreams, stream_config->num_streams);
+ pthread_mutex_unlock(&mMutex);
+ return -EINVAL;
+}
+
+void Camera::destroyStreams(Stream **streams, int count)
+{
+ if (streams == NULL)
+ return;
+ for (int i = 0; i < count; i++) {
+ // Only destroy streams that weren't reused
+ if (streams[i] != NULL && !streams[i]->mReuse)
+ delete streams[i];
+ }
+ delete [] streams;
+}
+
+Stream *Camera::reuseStream(camera3_stream_t *astream)
+{
+ Stream *priv = reinterpret_cast<Stream*>(astream->priv);
+ // Verify the re-used stream's parameters match
+ if (!priv->isValidReuseStream(mId, astream)) {
+ ALOGE("%s:%d: Mismatched parameter in reused stream", __func__, mId);
+ return NULL;
+ }
+ // Mark stream to be reused
+ priv->mReuse = true;
+ return priv;
+}
+
+bool Camera::isValidStreamSet(Stream **streams, int count)
+{
+ int inputs = 0;
+ int outputs = 0;
+
+ if (streams == NULL) {
+ ALOGE("%s:%d: NULL stream configuration streams", __func__, mId);
+ return false;
+ }
+ if (count == 0) {
+ ALOGE("%s:%d: Zero count stream configuration streams", __func__, mId);
+ return false;
+ }
+ // Validate there is at most one input stream and at least one output stream
+ for (int i = 0; i < count; i++) {
+ // A stream may be both input and output (bidirectional)
+ if (streams[i]->isInputType())
+ inputs++;
+ if (streams[i]->isOutputType())
+ outputs++;
+ }
+ if (outputs < 1) {
+ ALOGE("%s:%d: Stream config must have >= 1 output", __func__, mId);
+ return false;
+ }
+ if (inputs > 1) {
+ ALOGE("%s:%d: Stream config must have <= 1 input", __func__, mId);
+ return false;
+ }
+ // TODO: check for correct number of Bayer/YUV/JPEG/Encoder streams
+ return true;
+}
+
+void Camera::setupStreams(Stream **streams, int count)
+{
+ /*
+ * This is where the HAL has to decide internally how to handle all of the
+ * streams, and then produce usage and max_buffer values for each stream.
+ * Note, the stream array has been checked before this point for ALL invalid
+ * conditions, so it must find a successful configuration for this stream
+ * array. The HAL may not return an error from this point.
+ *
+ * In this demo HAL, we just set all streams to be the same dummy values;
+ * real implementations will want to avoid USAGE_SW_{READ|WRITE}_OFTEN.
+ */
+ for (int i = 0; i < count; i++) {
+ uint32_t usage = 0;
+
+ if (streams[i]->isOutputType())
+ usage |= GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_CAMERA_WRITE;
+ if (streams[i]->isInputType())
+ usage |= GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_HW_CAMERA_READ;
+
+ streams[i]->setUsage(usage);
+ streams[i]->setMaxBuffers(1);
+ }
}
int Camera::registerStreamBuffers(const camera3_stream_buffer_set_t *buf_set)
{
ALOGV("%s:%d: buffer_set=%p", __func__, mId, buf_set);
- // TODO: register buffers with hardware
- return 0;
+ if (buf_set == NULL) {
+ ALOGE("%s:%d: NULL buffer set", __func__, mId);
+ return -EINVAL;
+ }
+ if (buf_set->stream == NULL) {
+ ALOGE("%s:%d: NULL stream handle", __func__, mId);
+ return -EINVAL;
+ }
+ Stream *stream = reinterpret_cast<Stream*>(buf_set->stream->priv);
+ return stream->registerBuffers(buf_set);
}
const camera_metadata_t* Camera::constructDefaultRequestSettings(int type)
@@ -131,17 +291,123 @@
int Camera::processCaptureRequest(camera3_capture_request_t *request)
{
+ camera3_capture_result result;
+
ALOGV("%s:%d: request=%p", __func__, mId, request);
- ATRACE_BEGIN(__func__);
+ CAMTRACE_CALL();
if (request == NULL) {
ALOGE("%s:%d: NULL request recieved", __func__, mId);
- ATRACE_END();
return -EINVAL;
}
- // TODO: verify request; submit request to hardware
- ATRACE_END();
+ ALOGV("%s:%d: Request Frame:%d Settings:%p", __func__, mId,
+ request->frame_number, request->settings);
+
+ // NULL indicates use last settings
+ if (request->settings == NULL) {
+ if (mSettings == NULL) {
+ ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p",
+ __func__, mId, request->frame_number, request);
+ return -EINVAL;
+ }
+ } else {
+ setSettings(request->settings);
+ }
+
+ if (request->input_buffer != NULL) {
+ ALOGV("%s:%d: Reprocessing input buffer %p", __func__, mId,
+ request->input_buffer);
+
+ if (!isValidReprocessSettings(request->settings)) {
+ ALOGE("%s:%d: Invalid settings for reprocess request: %p",
+ __func__, mId, request->settings);
+ return -EINVAL;
+ }
+ } else {
+ ALOGV("%s:%d: Capturing new frame.", __func__, mId);
+
+ if (!isValidCaptureSettings(request->settings)) {
+ ALOGE("%s:%d: Invalid settings for capture request: %p",
+ __func__, mId, request->settings);
+ return -EINVAL;
+ }
+ }
+
+ if (request->num_output_buffers <= 0) {
+ ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId,
+ request->num_output_buffers);
+ return -EINVAL;
+ }
+ result.num_output_buffers = request->num_output_buffers;
+ result.output_buffers = new camera3_stream_buffer_t[result.num_output_buffers];
+ for (unsigned int i = 0; i < request->num_output_buffers; i++) {
+ int res = processCaptureBuffer(&request->output_buffers[i],
+ const_cast<camera3_stream_buffer_t*>(&result.output_buffers[i]));
+ if (res)
+ goto err_out;
+ }
+
+ result.frame_number = request->frame_number;
+ // TODO: return actual captured/reprocessed settings
+ result.result = request->settings;
+ // TODO: asynchronously return results
+ mCallbackOps->process_capture_result(mCallbackOps, &result);
+
+ return 0;
+
+err_out:
+ delete [] result.output_buffers;
+ // TODO: this should probably be a total device failure; transient for now
+ return -EINVAL;
+}
+
+void Camera::setSettings(const camera_metadata_t *new_settings)
+{
+ if (mSettings != NULL) {
+ free_camera_metadata(mSettings);
+ mSettings = NULL;
+ }
+
+ if (new_settings != NULL)
+ mSettings = clone_camera_metadata(new_settings);
+}
+
+bool Camera::isValidCaptureSettings(const camera_metadata_t *settings)
+{
+ // TODO: reject settings that cannot be captured
+ return true;
+}
+
+bool Camera::isValidReprocessSettings(const camera_metadata_t *settings)
+{
+ // TODO: reject settings that cannot be reprocessed
+ // input buffers unimplemented, use this to reject reprocessing requests
+ ALOGE("%s:%d: Input buffer reprocessing not implemented", __func__, mId);
+ return false;
+}
+
+int Camera::processCaptureBuffer(const camera3_stream_buffer_t *in,
+ camera3_stream_buffer_t *out)
+{
+ int res = sync_wait(in->acquire_fence, CAMERA_SYNC_TIMEOUT);
+ if (res == -ETIME) {
+ ALOGE("%s:%d: Timeout waiting on buffer acquire fence", __func__, mId);
+ return res;
+ } else if (res) {
+ ALOGE("%s:%d: Error waiting on buffer acquire fence: %s(%d)",
+ __func__, mId, strerror(-res), res);
+ return res;
+ }
+
+ out->stream = in->stream;
+ out->buffer = in->buffer;
+ out->status = CAMERA3_BUFFER_STATUS_OK;
+ // TODO: use driver-backed release fences
+ out->acquire_fence = -1;
+ out->release_fence = -1;
+
+ // TODO: lock and software-paint buffer
return 0;
}
@@ -153,7 +419,7 @@
void Camera::dump(int fd)
{
- ALOGV("%s:%d: Dumping to fd %d", fd);
+ ALOGV("%s:%d: Dumping to fd %d", __func__, mId, fd);
// TODO: dprintf all relevant state to fd
}
diff --git a/modules/camera/Camera.h b/modules/camera/Camera.h
index f2ad093..4001453 100644
--- a/modules/camera/Camera.h
+++ b/modules/camera/Camera.h
@@ -20,6 +20,7 @@
#include <pthread.h>
#include <hardware/hardware.h>
#include <hardware/camera3.h>
+#include "Stream.h"
namespace default_camera_hal {
// Camera represents a physical camera on a device.
@@ -50,6 +51,24 @@
camera3_device_t mDevice;
private:
+ // Reuse a stream already created by this device
+ Stream *reuseStream(camera3_stream_t *astream);
+ // Destroy all streams in a stream array, and the array itself
+ void destroyStreams(Stream **array, int count);
+ // Verify a set of streams is valid in aggregate
+ bool isValidStreamSet(Stream **array, int count);
+ // Calculate usage and max_bufs of each stream
+ void setupStreams(Stream **array, int count);
+ // Copy new settings for re-use and clean up old settings.
+ void setSettings(const camera_metadata_t *new_settings);
+ // Verify settings are valid for a capture
+ bool isValidCaptureSettings(const camera_metadata_t *settings);
+ // Verify settings are valid for reprocessing an input buffer
+ bool isValidReprocessSettings(const camera_metadata_t *settings);
+ // Process an output buffer
+ int processCaptureBuffer(const camera3_stream_buffer_t *in,
+ camera3_stream_buffer_t *out);
+
// Identifier used by framework to distinguish cameras
const int mId;
// Busy flag indicates camera is in use
@@ -60,6 +79,12 @@
const camera3_callback_ops_t *mCallbackOps;
// Lock protecting the Camera object for modifications
pthread_mutex_t mMutex;
+ // Array of handles to streams currently in use by the device
+ Stream **mStreams;
+ // Number of streams in mStreams
+ int mNumStreams;
+ // Most recent request settings seen, memoized to be reused
+ camera_metadata_t *mSettings;
};
} // namespace default_camera_hal
diff --git a/modules/camera/CameraHAL.cpp b/modules/camera/CameraHAL.cpp
index 6cae7d2..05b1fad 100644
--- a/modules/camera/CameraHAL.cpp
+++ b/modules/camera/CameraHAL.cpp
@@ -90,7 +90,6 @@
{
int id;
char *nameEnd;
- Camera *cam;
ALOGV("%s: module=%p, name=%s, device=%p", __func__, mod, name, dev);
id = strtol(name, &nameEnd, 10);
diff --git a/modules/camera/ScopedTrace.h b/modules/camera/ScopedTrace.h
new file mode 100644
index 0000000..ed00570
--- /dev/null
+++ b/modules/camera/ScopedTrace.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAMERA_SCOPED_TRACE_H
+#define CAMERA_SCOPED_TRACE_H
+
+#include <stdint.h>
+#include <cutils/trace.h>
+
+// See <cutils/trace.h> for more tracing macros.
+
+// CAMTRACE_NAME traces the beginning and end of the current scope. To trace
+// the correct start and end times this macro should be declared first in the
+// scope body.
+#define CAMTRACE_NAME(name) ScopedTrace ___tracer(ATRACE_TAG, name)
+// CAMTRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define CAMTRACE_CALL() CAMTRACE_NAME(__FUNCTION__)
+
+namespace default_camera_hal {
+
+class ScopedTrace {
+public:
+inline ScopedTrace(uint64_t tag, const char* name)
+ : mTag(tag) {
+ atrace_begin(mTag,name);
+}
+
+inline ~ScopedTrace() {
+ atrace_end(mTag);
+}
+
+private:
+ uint64_t mTag;
+};
+
+}; // namespace default_camera_hal
+
+#endif // CAMERA_SCOPED_TRACE_H
diff --git a/modules/camera/Stream.cpp b/modules/camera/Stream.cpp
new file mode 100644
index 0000000..0834aee
--- /dev/null
+++ b/modules/camera/Stream.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Stream"
+#include <cutils/log.h>
+
+#define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
+#include <cutils/trace.h>
+#include "ScopedTrace.h"
+
+#include "Stream.h"
+
+namespace default_camera_hal {
+
+Stream::Stream(int id, camera3_stream_t *s)
+ : mReuse(false),
+ mId(id),
+ mStream(s),
+ mType(s->stream_type),
+ mWidth(s->width),
+ mHeight(s->height),
+ mFormat(s->format),
+ mUsage(0),
+ mMaxBuffers(0),
+ mRegistered(false),
+ mBuffers(0),
+ mNumBuffers(0)
+{
+ // NULL (default) pthread mutex attributes
+ pthread_mutex_init(&mMutex, NULL);
+}
+
+Stream::~Stream()
+{
+ pthread_mutex_lock(&mMutex);
+ unregisterBuffers_L();
+ pthread_mutex_unlock(&mMutex);
+}
+
+void Stream::setUsage(uint32_t usage)
+{
+ pthread_mutex_lock(&mMutex);
+ if (usage != mUsage) {
+ mUsage = usage;
+ unregisterBuffers_L();
+ }
+ pthread_mutex_unlock(&mMutex);
+}
+
+void Stream::setMaxBuffers(uint32_t max_buffers)
+{
+ pthread_mutex_lock(&mMutex);
+ if (max_buffers != mMaxBuffers) {
+ mMaxBuffers = max_buffers;
+ unregisterBuffers_L();
+ }
+ pthread_mutex_unlock(&mMutex);
+}
+
+int Stream::getType()
+{
+ return mType;
+}
+
+bool Stream::isInputType()
+{
+ return mType & (CAMERA3_STREAM_INPUT | CAMERA3_STREAM_BIDIRECTIONAL);
+}
+
+bool Stream::isOutputType()
+{
+ return mType & (CAMERA3_STREAM_OUTPUT | CAMERA3_STREAM_BIDIRECTIONAL);
+}
+
+bool Stream::isRegistered()
+{
+ return mRegistered;
+}
+
+bool Stream::isValidReuseStream(int id, camera3_stream_t *s)
+{
+ if (id != mId) {
+ ALOGE("%s:%d: Invalid camera id for reuse. Got %d expect %d",
+ __func__, mId, id, mId);
+ return false;
+ }
+ if (s != mStream) {
+ ALOGE("%s:%d: Invalid stream handle for reuse. Got %p expect %p",
+ __func__, mId, s, mStream);
+ return false;
+ }
+ if (s->stream_type != mType) {
+ // TODO: prettyprint type string
+ ALOGE("%s:%d: Mismatched type in reused stream. Got %d expect %d",
+ __func__, mId, s->stream_type, mType);
+ return false;
+ }
+ if (s->format != mFormat) {
+ // TODO: prettyprint format string
+ ALOGE("%s:%d: Mismatched format in reused stream. Got %d expect %d",
+ __func__, mId, s->format, mFormat);
+ return false;
+ }
+ if (s->width != mWidth) {
+ ALOGE("%s:%d: Mismatched width in reused stream. Got %d expect %d",
+ __func__, mId, s->width, mWidth);
+ return false;
+ }
+ if (s->height != mHeight) {
+ ALOGE("%s:%d: Mismatched height in reused stream. Got %d expect %d",
+ __func__, mId, s->height, mHeight);
+ return false;
+ }
+ return true;
+}
+
+int Stream::registerBuffers(const camera3_stream_buffer_set_t *buf_set)
+{
+ CAMTRACE_CALL();
+
+ if (buf_set->stream != mStream) {
+ ALOGE("%s:%d: Buffer set for invalid stream. Got %p expect %p",
+ __func__, mId, buf_set->stream, mStream);
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&mMutex);
+
+ mNumBuffers = buf_set->num_buffers;
+ mBuffers = new buffer_handle_t*[mNumBuffers];
+
+ for (unsigned int i = 0; i < mNumBuffers; i++) {
+ ALOGV("%s:%d: Registering buffer %p", __func__, mId,
+ buf_set->buffers[i]);
+ mBuffers[i] = buf_set->buffers[i];
+ // TODO: register buffers with hw, handle error cases
+ }
+ mRegistered = true;
+
+ pthread_mutex_unlock(&mMutex);
+
+ return 0;
+}
+
+// This must only be called with mMutex held
+void Stream::unregisterBuffers_L()
+{
+ mRegistered = false;
+ mNumBuffers = 0;
+ delete [] mBuffers;
+ // TODO: unregister buffers from hw
+}
+
+} // namespace default_camera_hal
diff --git a/modules/camera/Stream.h b/modules/camera/Stream.h
new file mode 100644
index 0000000..521362e
--- /dev/null
+++ b/modules/camera/Stream.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STREAM_H_
+#define STREAM_H_
+
+#include <hardware/camera3.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+namespace default_camera_hal {
+// Stream represents a single input or output stream for a camera device.
+class Stream {
+ public:
+ Stream(int id, camera3_stream_t *s);
+ ~Stream();
+
+ // validate that astream's parameters match this stream's parameters
+ bool isValidReuseStream(int id, camera3_stream_t *s);
+
+ // Register buffers with hardware
+ int registerBuffers(const camera3_stream_buffer_set_t *buf_set);
+
+ void setUsage(uint32_t usage);
+ void setMaxBuffers(uint32_t max_buffers);
+
+ int getType();
+ bool isInputType();
+ bool isOutputType();
+ bool isRegistered();
+
+ // This stream is being reused. Used in stream configuration passes
+ bool mReuse;
+
+ private:
+ // Clean up buffer state. must be called with mMutex held.
+ void unregisterBuffers_L();
+
+ // The camera device id this stream belongs to
+ const int mId;
+ // Handle to framework's stream, used as a cookie for buffers
+ const camera3_stream_t *mStream;
+ // Stream type: CAMERA3_STREAM_* (see <hardware/camera3.h>)
+ const int mType;
+ // Width in pixels of the buffers in this stream
+ const uint32_t mWidth;
+ // Height in pixels of the buffers in this stream
+ const uint32_t mHeight;
+ // Gralloc format: HAL_PIXEL_FORMAT_* (see <system/graphics.h>)
+ const int mFormat;
+ // Gralloc usage mask : GRALLOC_USAGE_* (see <hardware/gralloc.h>)
+ uint32_t mUsage;
+ // Max simultaneous in-flight buffers for this stream
+ uint32_t mMaxBuffers;
+ // Buffers have been registered for this stream and are ready
+ bool mRegistered;
+ // Array of handles to buffers currently in use by the stream
+ buffer_handle_t **mBuffers;
+ // Number of buffers in mBuffers
+ unsigned int mNumBuffers;
+ // Lock protecting the Stream object for modifications
+ pthread_mutex_t mMutex;
+};
+} // namespace default_camera_hal
+
+#endif // STREAM_H_
diff --git a/tests/hwc/Android.mk b/tests/hwc/Android.mk
new file mode 100644
index 0000000..4cae9eb
--- /dev/null
+++ b/tests/hwc/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcnativewindow
+LOCAL_SRC_FILES := cnativewindow.c util.c
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := hwc-test-arrows
+LOCAL_SRC_FILES := test-arrows.c
+LOCAL_STATIC_LIBRARIES := libcnativewindow
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+include $(BUILD_EXECUTABLE)
diff --git a/tests/hwc/cnativewindow.c b/tests/hwc/cnativewindow.c
new file mode 100644
index 0000000..474ceec
--- /dev/null
+++ b/tests/hwc/cnativewindow.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+#include <hardware/hwcomposer.h>
+
+#include <system/window.h>
+#include <cutils/native_handle.h>
+
+// normalize and shorten type names
+typedef struct android_native_base_t aBase;
+typedef struct ANativeWindowBuffer aBuffer;
+typedef struct ANativeWindow aWindow;
+
+static int trace_level = 1;
+
+#define _TRACE(n,fmt...) \
+ do { if (trace_level >= n) fprintf(stderr, "CNW: " fmt); } while (0)
+
+#define ERROR(fmt...) _TRACE(0, fmt)
+#define INFO(fmt...) _TRACE(1, fmt)
+#define LOG(fmt...) _TRACE(2, fmt)
+#define TRACE(fmt...) _TRACE(3, fmt)
+
+#define QCT_WORKAROUND 1
+
+typedef struct CNativeBuffer {
+ aBuffer base;
+ struct CNativeBuffer *next;
+ struct CNativeBuffer *prev;
+ int ffd;
+} CNativeBuffer;
+
+typedef struct CNativeWindow {
+ aWindow base;
+
+ hwc_composer_device_1_t *hwc;
+ framebuffer_device_t *fb;
+ alloc_device_t *gr;
+
+ pthread_mutex_t lock;
+ pthread_cond_t cvar;
+
+ aBuffer *front;
+ aBuffer *spare;
+
+ CNativeBuffer free_buffer_queue;
+
+ unsigned width;
+ unsigned height;
+ unsigned xdpi;
+ unsigned ydpi;
+ unsigned format;
+
+ hwc_display_contents_1_t *dclist[HWC_NUM_DISPLAY_TYPES];
+
+ hwc_display_contents_1_t dc;
+ hwc_layer_1_t layer[4];
+} CNativeWindow;
+
+static inline CNativeBuffer *from_abuffer(aBuffer *buf) {
+ return (CNativeBuffer*) buf;
+}
+
+static CNativeBuffer *get_front(struct CNativeBuffer *queue) {
+ CNativeBuffer *buf = queue->next;
+ if (buf == queue)
+ return 0;
+ buf->next->prev = queue;
+ queue->next = buf->next;
+ buf->next = buf->prev = 0;
+ return buf;
+}
+
+static void put_front(struct CNativeBuffer *queue, aBuffer *_buf) {
+ struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf;
+ buf->prev = queue;
+ buf->next = queue->next;
+ queue->next->prev = buf;
+ queue->next = buf;
+}
+
+static void put_back(struct CNativeBuffer *queue, aBuffer *_buf) {
+ struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf;
+ buf->next = queue;
+ buf->prev = queue->prev;
+ queue->prev->next = buf;
+ queue->prev = buf;
+}
+
+static void cnw_inc_ref(aBase *base) { TRACE("buf %p ref++\n",base); }
+static void cnw_dec_ref(aBase *base) { TRACE("buf %p ref--\n",base); }
+
+static inline CNativeWindow *from_base(aWindow *base) {
+ return (CNativeWindow *) base;
+}
+
+static inline CNativeWindow *from_base_const(const aWindow *base) {
+ return (CNativeWindow *) base;
+}
+
+static int cnw_set_swap_interval(aWindow *base, int interval) {
+ CNativeWindow *win = from_base(base);
+ if (win->fb && win->fb->setSwapInterval)
+ return win->fb->setSwapInterval(win->fb, interval);
+ return 0;
+}
+
+static int cnw_dequeue_buffer1(aWindow *base, aBuffer **buf, int *ffd) {
+ CNativeWindow *win = from_base(base);
+ CNativeBuffer *cnb;
+
+ pthread_mutex_lock(&win->lock);
+
+ while ((cnb = get_front(&win->free_buffer_queue)) == 0) {
+ pthread_cond_wait(&win->cvar, &win->lock);
+ }
+
+ *ffd = cnb->ffd;
+ *buf = &cnb->base;
+ cnb->ffd = -1;
+ LOG("<< dequeue buffer %p %d\n", *buf, *ffd);
+
+ pthread_mutex_unlock(&win->lock);
+ return 0;
+}
+
+static int cnw_lock_buffer0(aWindow *base, aBuffer *buffer) {
+ return 0;
+}
+
+static void set_layer(hwc_layer_1_t *dl, aBuffer *buf, int ffd) {
+ int right = buf->width;
+ int bottom = buf->height;
+
+ dl->compositionType = HWC_FRAMEBUFFER;
+ dl->hints = 0;
+ dl->flags = 0;
+
+ dl->handle = buf->handle;
+ dl->transform = 0;
+ dl->blending = HWC_BLENDING_NONE;
+ dl->sourceCrop.left = 0;
+ dl->sourceCrop.top = 0;
+ dl->sourceCrop.right = right;
+ dl->sourceCrop.bottom = bottom;
+ dl->displayFrame.left = 0;
+ dl->displayFrame.top = 0;
+ dl->displayFrame.right = right;
+ dl->displayFrame.bottom = bottom;
+ dl->visibleRegionScreen.numRects = 1;
+ dl->visibleRegionScreen.rects = &dl->displayFrame;
+
+ dl->acquireFenceFd = ffd;
+ dl->releaseFenceFd = -1;
+}
+
+static void hwc_post(CNativeWindow *win, aBuffer *buf, int ffd) {
+ hwc_composer_device_1_t *hwc = win->hwc;
+ hwc_display_contents_1_t *dc = &(win->dc);
+ hwc_layer_1_t *dl = win->dc.hwLayers;
+ int r, i;
+
+ dc->retireFenceFd = -1;
+ dc->outbufAcquireFenceFd = -1;
+ dc->flags = HWC_GEOMETRY_CHANGED;
+ dc->numHwLayers = 1;
+
+ // some hwcomposers fail if these are NULL
+ dc->dpy = (void*) 0xdeadbeef;
+ dc->sur = (void*) 0xdeadbeef;
+
+ set_layer(&dl[0], buf, ffd);
+
+ if (QCT_WORKAROUND) {
+ set_layer(&dl[1], win->spare, -1);
+ dl[1].compositionType = HWC_FRAMEBUFFER_TARGET;
+ dc->numHwLayers++;
+ }
+
+ r = hwc->prepare(hwc, HWC_NUM_DISPLAY_TYPES, win->dclist);
+ if (r) {
+ ERROR("hwc->prepare failed r=%d\n",r);
+ return;
+ }
+
+// for (i = 0; i < dc->numHwLayers; i++)
+// LOG("dl[%d] ctype=0x%08x hints=0x%08x flags=0x%08x\n", i,
+// dl[i].compositionType, dl[0].hints, dl[0].flags);
+
+ r = hwc->set(hwc, HWC_NUM_DISPLAY_TYPES, win->dclist);
+ if (r) {
+ ERROR("hwc->set failed, r=%d\n", r);
+ return;
+ }
+
+ if (dc->retireFenceFd != -1)
+ close(dc->retireFenceFd);
+ if (dl->releaseFenceFd != -1) {
+ CNativeBuffer *cnb = from_abuffer(buf);
+ cnb->ffd = dl->releaseFenceFd;
+ }
+ if (QCT_WORKAROUND)
+ if (dl[1].releaseFenceFd != -1)
+ close(dl[1].releaseFenceFd);
+}
+
+static int cnw_queue_buffer1(aWindow *base, aBuffer *buffer, int ffd) {
+ CNativeWindow *win = from_base(base);
+ int res;
+ LOG(">> queue buffer %p %d\n", buffer, ffd);
+ if (win->fb) {
+ res = win->fb->post(win->fb, buffer->handle);
+ if (ffd != -1)
+ close(ffd);
+ } else {
+ hwc_post(win, buffer, ffd);
+ res = 0;
+ }
+ pthread_mutex_lock(&win->lock);
+ if (win->front)
+ put_back(&win->free_buffer_queue, win->front);
+ win->front = buffer;
+ pthread_cond_signal(&win->cvar);
+ pthread_mutex_unlock(&win->lock);
+
+ return res;
+}
+
+static int cnw_cancel_buffer1(aWindow *base, aBuffer *buf, int ffd) {
+ CNativeWindow *win = from_base(base);
+ CNativeBuffer *cnb = from_abuffer(buf);
+ LOG("<< cancel buffer %p %d\n", buf, ffd);
+ cnb->ffd = ffd;
+ pthread_mutex_lock(&win->lock);
+ put_front(&win->free_buffer_queue, buf);
+ pthread_mutex_unlock(&win->lock);
+ return 0;
+}
+
+static int cnw_dequeue_buffer0(aWindow *base, aBuffer **buf) {
+ int ffd = -1;
+ int r;
+ r = cnw_dequeue_buffer1(base, buf, &ffd);
+ if (ffd != -1)
+ close(ffd);
+ return r;
+}
+
+static int cnw_queue_buffer0(aWindow *base, aBuffer *buf) {
+ return cnw_queue_buffer1(base, buf, -1);
+}
+
+static int cnw_cancel_buffer0(aWindow *base, aBuffer *buf) {
+ return cnw_cancel_buffer1(base, buf, -1);
+}
+
+static int cnw_query(const aWindow *base, int what, int *value) {
+ CNativeWindow *win = from_base_const(base);
+
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ case NATIVE_WINDOW_DEFAULT_WIDTH:
+ *value = win->width;
+ TRACE("query window width: %d\n", *value);
+ return 0;
+ case NATIVE_WINDOW_HEIGHT:
+ case NATIVE_WINDOW_DEFAULT_HEIGHT:
+ *value = win->height;
+ TRACE("query window height: %d\n", *value);
+ return 0;
+ case NATIVE_WINDOW_FORMAT:
+ *value = win->format;
+ TRACE("query window format: %d\n", *value);
+ return 0;
+ case NATIVE_WINDOW_TRANSFORM_HINT:
+ TRACE("query transform hint: 0\n");
+ *value = 0;
+ return 0;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ TRACE("query min undequeued buffers: 1\n");
+ *value = 1;
+ return 0;
+ default:
+ *value = 0;
+ ERROR("query %d unknown!\n", what);
+ return -EINVAL;
+ }
+}
+
+static int cnw_perform(aWindow *base, int op, ...) {
+ CNativeWindow *win = from_base(base);
+ va_list ap;
+ va_start(ap, op);
+
+ switch (op) {
+ case NATIVE_WINDOW_SET_USAGE:
+ TRACE("set usage %d\n", va_arg(ap,int));
+ return 0;
+ case NATIVE_WINDOW_CONNECT:
+ case NATIVE_WINDOW_DISCONNECT:
+ case NATIVE_WINDOW_API_CONNECT:
+ case NATIVE_WINDOW_API_DISCONNECT:
+ return 0;
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ TRACE("set buffers format %d\n", va_arg(ap,int));
+ return 0;
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ TRACE("set buffers transform %d\n", va_arg(ap,int));
+ return 0;
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ TRACE("set buffers timestamp %lld\n", va_arg(ap,long long));
+ return 0;
+ case NATIVE_WINDOW_SET_SCALING_MODE:
+ TRACE("set scaling mode %d\n", va_arg(ap,int));
+ return 0;
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
+ int w = va_arg(ap,int);
+ int h = va_arg(ap,int);
+ if ((w == win->width) && (h == win->height)) {
+ TRACE("set buffers dimensions %d x %d\n", w, h);
+ return 0;
+ }
+ ERROR("cannot resize buffers to %d x %d\n", w, h);
+ return -1;
+ }
+ default:
+ ERROR("perform %d unknown!\n", op);
+ return -ENODEV;
+ }
+}
+
+static void hwc_invalidate(const struct hwc_procs *procs) {}
+static void hwc_vsync(const struct hwc_procs *procs, int disp, int64_t ts) {}
+static void hwc_hotplug(const struct hwc_procs *procs, int disp, int conn) {}
+
+struct hwc_procs hprocs = {
+ .invalidate = hwc_invalidate,
+ .vsync = hwc_vsync,
+ .hotplug = hwc_hotplug,
+};
+
+uint32_t attrs[] = {
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_VSYNC_PERIOD,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_DPI_Y,
+ HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static int hwc_init(CNativeWindow *win) {
+ hw_module_t const* module;
+ hwc_composer_device_1_t *hwc;
+ unsigned i;
+ int r;
+ uint32_t configs[32];
+ uint32_t numconfigs = 32;
+ int32_t values[8];
+
+ if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
+ ERROR("cannot open hw composer module\n");
+ return -ENODEV;
+ }
+
+ if (hwc_open_1(module, &hwc)) {
+ ERROR("cannot open hwc device\n");
+ return -ENODEV;
+ }
+ win->hwc = hwc;
+
+ LOG("hwc version 0x%08x\n", hwc->common.version);
+
+ if ((hwc->common.version & 0xFFFF0000) < 0x01010000) {
+ ERROR("hwc version less than 1.1\n");
+ hwc_close_1(hwc);
+ return -ENODEV;
+ }
+
+ hwc->registerProcs(hwc, &hprocs);
+
+ if (hwc->getDisplayConfigs(hwc, 0, configs, &numconfigs)) {
+ ERROR("cannot get configs\n");
+ return -ENODEV;
+ }
+ for (i = 0; i < numconfigs; i++)
+ LOG("cfg[%d] = 0x%08x\n", i, configs[i]);
+
+ if ((r = hwc->getDisplayAttributes(hwc, 0, configs[0], attrs, values))) {
+ ERROR("cannot get attributes %d\n", r);
+ return -ENODEV;
+ }
+
+ win->width = values[0];
+ win->height = values[1];
+ win->xdpi = values[3];
+ win->ydpi = values[4];
+ win->format = HAL_PIXEL_FORMAT_RGBA_8888;
+
+ hwc->blank(hwc, 0, 0);
+
+ win->dclist[0] = &(win->dc);
+ return 0;
+}
+
+static aBuffer *cnw_alloc(CNativeWindow *win, unsigned format, unsigned usage) {
+ CNativeBuffer *cnb;
+ aBuffer *buf;
+ int err;
+
+ if (!(cnb = malloc(sizeof(CNativeBuffer))))
+ return 0;
+
+ buf = &cnb->base;
+ cnb->ffd = -1;
+
+ buf->common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+ buf->common.version = sizeof(aBuffer);
+ buf->common.incRef = cnw_inc_ref;
+ buf->common.decRef = cnw_dec_ref;
+
+ buf->width = win->width;
+ buf->height = win->height;
+ buf->format = format;
+ buf->usage = usage;
+
+ err = win->gr->alloc(win->gr, win->width, win->height,
+ format, usage, &buf->handle, &buf->stride);
+ if (err) {
+ ERROR("gralloc of %d x %d failed: err=%d\n",
+ win->width, win->height, err);
+ free(buf);
+ return 0;
+ }
+ INFO("alloc buffer %p %d x %d\n", buf, win->width, win->height);
+ return buf;
+}
+
+static int cnw_init(CNativeWindow *win) {
+ hw_module_t const* module;
+ framebuffer_device_t *fb = NULL;
+ alloc_device_t *gr;
+ int err, i, n;
+ unsigned usage, format;
+
+ memset(win, 0, sizeof(CNativeWindow));
+
+ win->free_buffer_queue.next = &(win->free_buffer_queue);
+ win->free_buffer_queue.prev = &(win->free_buffer_queue);
+
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) != 0) {
+ ERROR("cannot open gralloc module\n");
+ return -ENODEV;
+ }
+
+ if (hwc_init(win)) {
+ ERROR("cannot open hwcomposer, trying legacy fb HAL\n");
+ err = framebuffer_open(module, &fb);
+ if (err) {
+ ERROR("cannot open fb HAL (%s)", strerror(-err));
+ return -ENODEV;
+ }
+ win->width = fb->width;
+ win->height = fb->height;
+ win->format = fb->format;
+ win->xdpi = fb->xdpi;
+ win->ydpi = fb->ydpi;
+ win->fb = fb;
+ }
+
+ INFO("display %d x %d fmt=%d\n",
+ win->width, win->height, win->format);
+
+ err = gralloc_open(module, &gr);
+ if (err) {
+ ERROR("couldn't open gralloc HAL (%s)", strerror(-err));
+ return -ENODEV;
+ }
+ win->gr = gr;
+
+ usage = GRALLOC_USAGE_HW_FB |
+ GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_RENDER;
+
+ for (i = 0; i < 2; i++) {
+ aBuffer *buf = cnw_alloc(win, win->format, usage);
+ if (!buf)
+ return -ENOMEM;
+ put_back(&win->free_buffer_queue, buf);
+ }
+
+ if (!win->fb && QCT_WORKAROUND) {
+ win->spare = cnw_alloc(win, win->format, usage);
+ if (!win->spare)
+ return -ENOMEM;
+ }
+
+ // Disgusting, but we need to init these "const" fields
+ // and unlike C++ we can't use const_cast<>
+ *((float*) &win->base.xdpi) = win->xdpi;
+ *((float*) &win->base.ydpi) = win->ydpi;
+ *((int*) &win->base.minSwapInterval) = 1;
+ *((int*) &win->base.maxSwapInterval) = 1;
+
+ win->base.common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+ win->base.common.version = sizeof(aWindow);
+ win->base.common.incRef = cnw_inc_ref;
+ win->base.common.decRef = cnw_dec_ref;
+
+ win->base.setSwapInterval = cnw_set_swap_interval;
+ win->base.dequeueBuffer_DEPRECATED = cnw_dequeue_buffer0;
+ win->base.lockBuffer_DEPRECATED = cnw_lock_buffer0;
+ win->base.queueBuffer_DEPRECATED = cnw_queue_buffer0;
+ win->base.query = cnw_query;
+ win->base.perform = cnw_perform;
+ win->base.cancelBuffer_DEPRECATED = cnw_cancel_buffer0;
+ win->base.dequeueBuffer = cnw_dequeue_buffer1;
+ win->base.queueBuffer = cnw_queue_buffer1;
+ win->base.cancelBuffer = cnw_cancel_buffer1;
+
+ pthread_mutex_init(&win->lock, NULL);
+ pthread_cond_init(&win->cvar, NULL);
+
+ return 0;
+}
+
+void cnw_destroy(CNativeWindow *win) {
+ if (win->fb)
+ framebuffer_close(win->fb);
+ if (win->hwc)
+ hwc_close_1(win->hwc);
+ if (win->gr)
+ gralloc_close(win->gr);
+ free(win);
+}
+
+CNativeWindow *cnw_create(void) {
+ CNativeWindow *win;
+ char *x;
+ if ((x = getenv("CNWDEBUG")))
+ trace_level = atoi(x);
+ if (!(win = malloc(sizeof(CNativeWindow))))
+ return NULL;
+ if (cnw_init(win)) {
+ cnw_destroy(win);
+ return NULL;
+ }
+ return win;
+}
+
+void cnw_info(CNativeWindow *win, unsigned *w, unsigned *h, unsigned *fmt) {
+ *w = win->width;
+ *h = win->height;
+ *fmt = win->format;
+}
+
diff --git a/tests/hwc/test-arrows.c b/tests/hwc/test-arrows.c
new file mode 100644
index 0000000..a35faa7
--- /dev/null
+++ b/tests/hwc/test-arrows.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include "util.h"
+
+static const char gVertexShader[] =
+ "attribute vec4 aPosition;\n"
+ "uniform mat4 uTransform;\n"
+ "varying vec4 vTexCoord;\n"
+ "void main() {\n"
+ " gl_Position = aPosition * uTransform;\n"
+ " vTexCoord = aPosition * vec4(1.0/16.0,-1.0/16.0,0.0,0.0);\n"
+ "}\n";
+
+static const char gFragmentShader[] =
+ "precision mediump float;\n"
+ "uniform sampler2D uTexture;\n"
+ "uniform float uAnim;\n"
+ "varying vec4 vTexCoord;\n"
+ "void main() {\n"
+ " vec2 tc = vec2(vTexCoord.x, uAnim + vTexCoord.y);\n"
+ " gl_FragColor = texture2D(uTexture, tc);\n"
+ "}\n";
+
+static GLuint pgm;
+static GLint aPosition, uTransform, uTexture, uAnim;
+
+static GLfloat vtx[2 * 3 * 2];
+static GLfloat mtx[16];
+
+//#define R (0xFF0000FF)
+#define R (0xFF000000)
+#define G (0xFF00FF00)
+uint32_t t32[] = {
+ R, R, R, R, R, R, R, G, G, R, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, G, G, G, G, G, G, R, R, R, R, R,
+ R, R, R, R, G, G, G, G, G, G, G, G, R, R, R, R,
+ R, R, R, G, G, G, G, G, G, G, G, G, G, R, R, R,
+ R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R,
+ R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R,
+ R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R,
+ R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R,
+ R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R,
+};
+#undef R
+#undef G
+
+int prepare(int w, int h) {
+ GLuint texid;
+
+ int left = w / 4;
+ int top = h / 4;
+ int right = (w / 4) * 3;
+ int bottom = (h / 4) * 3;
+
+ vtx[0] = left;
+ vtx[1] = top;
+ vtx[2] = left;
+ vtx[3] = bottom;
+ vtx[4] = right;
+ vtx[5] = bottom;
+
+ vtx[6] = right;
+ vtx[7] = bottom;
+ vtx[8] = right;
+ vtx[9] = top;
+ vtx[10] = left;
+ vtx[11] = top;
+
+ matrix_init_ortho(mtx, w, h);
+
+ pgm = load_program(gVertexShader, gFragmentShader);
+ if (!pgm)
+ return -1;
+
+ aPosition = glGetAttribLocation(pgm, "aPosition");
+ uTexture = glGetUniformLocation(pgm, "uTexture");
+ uTransform = glGetUniformLocation(pgm, "uTransform");
+ uAnim = glGetUniformLocation(pgm, "uAnim");
+
+ glViewport(0, 0, w, h);
+
+ glGenTextures(1, &texid);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texid);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glEnable(GL_TEXTURE_2D);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, t32);
+
+ return 0;
+}
+
+static float anim = 0.0;
+
+void render() {
+ anim += 0.1;
+ if (anim >= 16.0) anim = 0.0;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ glUseProgram(pgm);
+ glUniform1i(uTexture, 0);
+ glUniform1f(uAnim, anim);
+ glUniformMatrix4fv(uTransform, 1, 0, mtx);
+ glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, vtx);
+ glEnableVertexAttribArray(aPosition);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+}
+
+int main(int argc, char **argv) {
+ EGLDisplay display;
+ EGLSurface surface;
+ int w, h, count;
+
+ if (argc > 1)
+ count = atoi(argv[1]);
+
+ if (egl_create(&display, &surface, &w, &h))
+ return -1;
+
+ if (prepare(w, h))
+ return -1;
+
+ for (;;) {
+ render();
+ eglSwapBuffers(display, surface);
+ if (count > 0)
+ if (--count == 0)
+ break;
+ }
+
+ egl_destroy(display, surface);
+ return 0;
+}
diff --git a/tests/hwc/util.c b/tests/hwc/util.c
new file mode 100644
index 0000000..8931305
--- /dev/null
+++ b/tests/hwc/util.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <system/graphics.h>
+
+#include "util.h"
+
+void matrix_init_ortho(GLfloat *m, float w, float h) {
+ m[0] = 2.0 / w;
+ m[1] = 0.0;
+ m[2] = 0.0;
+ m[3] = -1.0;
+ m[4] = 0.0;
+ m[5] = 2.0 / h;
+ m[6] = 0.0;
+ m[7] = -1.0;
+ m[8] = 0.0;
+ m[9] = 0.0;
+ m[10] -1.0;
+ m[11] = 0.0;
+ m[12] = 0.0;
+ m[13] = 0.0;
+ m[14] = 0.0;
+ m[15] = 1.0;
+}
+
+static GLuint load_shader(GLenum shaderType, const char *src) {
+ GLint status = 0, len = 0;
+ GLuint shader;
+
+ if (!(shader = glCreateShader(shaderType)))
+ return 0;
+
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+
+ if (status)
+ return shader;
+
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
+ if (len) {
+ char *msg = malloc(len);
+ if (msg) {
+ glGetShaderInfoLog(shader, len, NULL, msg);
+ msg[len-1] = 0;
+ fprintf(stderr, "error compiling shader:\n%s\n", msg);
+ free(msg);
+ }
+ }
+ glDeleteShader(shader);
+ return 0;
+}
+
+GLuint load_program(const char *vert_src, const char *frag_src) {
+ GLuint vert, frag, prog;
+ GLint status = 0, len = 0;
+
+ if (!(vert = load_shader(GL_VERTEX_SHADER, vert_src)))
+ return 0;
+ if (!(frag = load_shader(GL_FRAGMENT_SHADER, frag_src)))
+ goto fail_frag;
+ if (!(prog = glCreateProgram()))
+ goto fail_prog;
+
+ glAttachShader(prog, vert);
+ glAttachShader(prog, frag);
+ glLinkProgram(prog);
+
+ glGetProgramiv(prog, GL_LINK_STATUS, &status);
+ if (status)
+ return prog;
+
+ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
+ if (len) {
+ char *buf = (char*) malloc(len);
+ if (buf) {
+ glGetProgramInfoLog(prog, len, NULL, buf);
+ buf[len-1] = 0;
+ fprintf(stderr, "error linking program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(prog);
+fail_prog:
+ glDeleteShader(frag);
+fail_frag:
+ glDeleteShader(vert);
+ return 0;
+}
+
+int select_config_for_window(EGLDisplay dpy, EGLint *attr,
+ unsigned format, EGLConfig *config) {
+ EGLint R,G,B,A,r,g,b,a;
+ EGLint i, n, max;
+ EGLConfig *cfg;
+
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ R = G = B = A = 8;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ R = 5; G = 6; B = 5; A = 0;
+ break;
+ default:
+ fprintf(stderr, "unknown fb pixel format %d\n", format);
+ return -1;
+ }
+
+ if (eglGetConfigs(dpy, NULL, 0, &max) == EGL_FALSE) {
+ fprintf(stderr, "no EGL configurations available?!\n");
+ return -1;
+ }
+
+ cfg = (EGLConfig*) malloc(sizeof(EGLConfig) * max);
+ if (!cfg)
+ return -1;
+
+ if (eglChooseConfig(dpy, attr, cfg, max, &n) == EGL_FALSE) {
+ fprintf(stderr, "eglChooseConfig failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < n; i++) {
+ EGLint r,g,b,a;
+ eglGetConfigAttrib(dpy, cfg[i], EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(dpy, cfg[i], EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(dpy, cfg[i], EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(dpy, cfg[i], EGL_ALPHA_SIZE, &a);
+ if (r == R && g == G && b == B && a == A) {
+ *config = cfg[i];
+ free(cfg);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "cannot find matching config\n");
+ free(cfg);
+ return -1;
+}
+
+static struct CNativeWindow *_cnw = 0;
+
+int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h) {
+ EGLBoolean res;
+ EGLConfig config = { 0 };
+ EGLint context_attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLint config_attrs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE };
+ EGLint major, minor;
+ EGLContext context;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay display;
+ EGLNativeWindowType window;
+ unsigned width, height, format;
+ struct CNativeWindow *cnw;
+
+ display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY)
+ return -1;
+
+ if (!(res = eglInitialize(display, &major, &minor)))
+ return -1;
+
+ fprintf(stderr, "egl version: %d.%d\n", major, minor);
+
+ if ((cnw = cnw_create()) == 0)
+ return -1;
+
+ cnw_info(cnw, &width, &height, &format);
+ window = (EGLNativeWindowType) cnw;
+
+ if ((res = select_config_for_window(display, config_attrs, format, &config)))
+ goto fail;
+
+ surface = eglCreateWindowSurface(display, config, window, NULL);
+ if (surface == EGL_NO_SURFACE)
+ goto fail;
+
+ context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs);
+ if (context == EGL_NO_CONTEXT)
+ goto fail;
+
+ if (!(res = eglMakeCurrent(display, surface, surface, context)))
+ goto fail;
+
+ eglQuerySurface(display, surface, EGL_WIDTH, &w);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &h);
+
+ fprintf(stderr, "window: %d x %d\n", w, h);
+
+ *_display = display;
+ *_surface = surface;
+ *_w = w;
+ *_h = h;
+
+ _cnw = cnw;
+ return 0;
+
+fail:
+ cnw_destroy(cnw);
+ return -1;
+}
+
+void egl_destroy(EGLDisplay display, EGLSurface surface) {
+ if (_cnw) {
+ eglDestroySurface(display, surface);
+ eglTerminate(display);
+ cnw_destroy(_cnw);
+ _cnw = 0;
+ }
+}
diff --git a/tests/hwc/util.h b/tests/hwc/util.h
new file mode 100644
index 0000000..a0d38ce
--- /dev/null
+++ b/tests/hwc/util.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GL_UTIL_H_
+#define _GL_UTIL_H_
+
+/* convenience */
+
+GLuint load_program(const char *vert_src, const char *frag_src);
+void matrix_init_ortho(GLfloat *m, float w, float h);
+
+/* context setup / teardown */
+
+int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h);
+void egl_destroy(EGLDisplay display, EGLSurface surface);
+
+/* internals needed by util.c */
+
+struct CNativeWindow;
+struct CNativeWindow *cnw_create(void);
+void cnw_destroy(struct CNativeWindow *win);
+void cnw_info(struct CNativeWindow *win,
+ unsigned *w, unsigned *h, unsigned *fmt);
+
+#endif