Merge "Create NeuralNetworks HAL v1.1 for new OperationTypes"
diff --git a/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc b/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
index 79edad6..c02db08 100644
--- a/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
+++ b/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
@@ -1,4 +1,4 @@
-service vendor.evs-hal-mock /vendor/bin/hw/android.hardware.automotive.audiocontrol@1.0-service
+service vendor.audiocontrol-hal-1.0 /vendor/bin/hw/android.hardware.automotive.audiocontrol@1.0-service
class hal
user audioserver
group system
diff --git a/automotive/vehicle/2.0/Android.bp b/automotive/vehicle/2.0/Android.bp
index a0d20f3..bdc44ef 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/2.0/Android.bp
@@ -48,7 +48,6 @@
"VehicleHvacFanDirection",
"VehicleHwKeyInputAction",
"VehicleIgnitionState",
- "VehicleInstrumentClusterType",
"VehiclePropConfig",
"VehiclePropValue",
"VehicleProperty",
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
index 3f98a94..40dd56e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
@@ -47,6 +47,7 @@
dest->prop = src.prop;
dest->areaId = src.areaId;
+ dest->status = src.status;
dest->timestamp = src.timestamp;
copyVehicleRawValue(&dest->value, src.value);
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
index 34a6380..5b6816e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
@@ -114,6 +114,7 @@
void shallowCopy(VehiclePropValue* dest, const VehiclePropValue& src) {
dest->prop = src.prop;
dest->areaId = src.areaId;
+ dest->status = src.status;
dest->timestamp = src.timestamp;
shallowCopyHidlVec(&dest->value.int32Values, src.value.int32Values);
shallowCopyHidlVec(&dest->value.int64Values, src.value.int64Values);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 16d2b0b..5118b18 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -363,6 +363,7 @@
updatedPropValue->prop = propId;
updatedPropValue->areaId = 0; // Add area support if necessary.
updatedPropValue->timestamp = elapsedRealtimeNano();
+ updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
mPropStore->writeValue(*updatedPropValue);
auto changeMode = mPropStore->getConfigOrDie(propId)->changeMode;
if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index fca8e9e..bf7be09 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -138,6 +138,7 @@
VehiclePropValue val = {
.prop = protoVal.prop(),
.areaId = protoVal.area_id(),
+ .status = (VehiclePropertyStatus)protoVal.status(),
.timestamp = elapsedRealtimeNano(),
};
@@ -288,6 +289,7 @@
protoVal->set_prop(val->prop);
protoVal->set_value_type(toInt(getPropType(val->prop)));
protoVal->set_timestamp(val->timestamp);
+ protoVal->set_status((emulator::VehiclePropStatus)(val->status));
protoVal->set_area_id(val->areaId);
// Copy value data if it is set.
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
index 86433f5..2ef64fb 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
@@ -46,6 +46,12 @@
ERROR_INVALID_OPERATION = 8;
}
+enum VehiclePropStatus {
+ AVAILABLE = 0;
+ UNAVAILABLE = 1;
+ ERROR = 2;
+}
+
message VehicleAreaConfig {
required int32 area_id = 1;
optional sint32 min_int32_value = 2;
@@ -61,7 +67,7 @@
optional int32 access = 2;
optional int32 change_mode = 3;
optional int32 value_type = 4;
- optional int32 supported_areas = 5;
+ optional int32 supported_areas = 5; // Deprecated - DO NOT USE
repeated VehicleAreaConfig area_configs = 6;
optional int32 config_flags = 7;
repeated int32 config_array = 8;
@@ -75,6 +81,7 @@
required int32 prop = 1;
optional int32 value_type = 2;
optional int64 timestamp = 3; // required for valid data from HAL, skipped for set
+ optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set
// values
optional int32 area_id = 4;
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index f673d1a..be98a56 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -988,41 +988,6 @@
| VehicleArea:GLOBAL),
/**
- * Property to define instrument cluster information.
- * For VehicleInstrumentClusterType:EXTERNAL_DISPLAY:
- * READ:
- * int32Values[0] : The current screen mode index. Screen mode is defined
- * as a configuration in car service and represents
- * which area of screen is renderable.
- * int32Values[1] : Android can render to instrument cluster (=1) or
- * not(=0). When this is 0, instrument cluster may be
- * rendering some information in the area allocated for
- * android and android side rendering is invisible.
- * WRITE from android:
- * int32Values[0] : Preferred mode for android side. Depending on the app
- * rendering to instrument cluster, preferred mode can
- * change. Instrument cluster still needs to send
- * event with new mode to trigger actual mode change.
- * int32Values[1] : The current app context relevant for instrument
- * cluster. Use the same flag with
- * VehicleAudioContextFlag but this context represents
- * active apps, not active audio. Instrument cluster
- * side may change mode depending on the currently
- * active contexts.
- * When system boots up, Android side will write {0, 0, 0, 0} when it is
- * ready to render to instrument cluster. Before this message, rendering
- * from android must not be visible in the cluster.
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
- * @configArray 0:VehicleInstrumentClusterType 1:hw type
- */
- INSTRUMENT_CLUSTER_INFO = (
- 0x0A20
- | VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:INT32_VEC
- | VehicleArea:GLOBAL),
-
- /**
* Current date and time, encoded as Unix time.
* This value denotes the number of seconds that have elapsed since
* 1/1/1970.
@@ -2067,25 +2032,6 @@
};
/**
- * Represents instrument cluster type available in system
- */
-enum VehicleInstrumentClusterType : int32_t {
- /** Android has no access to instument cluster */
- NONE = 0,
-
- /**
- * Instrument cluster can communicate through vehicle hal with additional
- * properties to exchange meta-data
- */
- HAL_INTERFACE = 1,
-
- /**
- * Instrument cluster is external display where android can render contents
- */
- EXTERNAL_DISPLAY = 2,
-};
-
-/**
* Units used for int or float type with no attached enum types.
*/
enum VehicleUnit : int32_t {
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp
index 6209cb8..21f81f5 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/1.0/default/Android.bp
@@ -7,7 +7,9 @@
"CameraMetadata.cpp",
"CameraParameters.cpp",
"VendorTagDescriptor.cpp",
- "HandleImporter.cpp"],
+ "HandleImporter.cpp",
+ "Exif.cpp"
+ ],
cflags: [
"-Werror",
"-Wextra",
@@ -17,7 +19,9 @@
"liblog",
"libhardware",
"libcamera_metadata",
- "android.hardware.graphics.mapper@2.0"],
+ "android.hardware.graphics.mapper@2.0",
+ "libexif",
+ ],
include_dirs: ["system/media/private/camera/include"],
export_include_dirs : ["include"]
}
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/1.0/default/Exif.cpp
new file mode 100644
index 0000000..3e894f9
--- /dev/null
+++ b/camera/common/1.0/default/Exif.cpp
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CamComm1.0-Exif"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "Exif.h"
+
+extern "C" {
+#include <libexif/exif-data.h>
+}
+
+namespace std {
+
+template <>
+struct default_delete<ExifEntry> {
+ inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
+};
+
+} // namespace std
+
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+
+class ExifUtilsImpl : public ExifUtils {
+ public:
+ ExifUtilsImpl();
+
+ virtual ~ExifUtilsImpl();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize();
+
+ // set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight);
+
+ // sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator);
+
+ // sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space);
+
+ // sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration);
+
+ // sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression);
+
+ // sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast);
+
+ // sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t);
+
+ // sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description);
+
+ // sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator);
+
+ // sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator);
+
+ // sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode);
+
+ // sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program);
+
+ // sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator);
+
+ // sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash);
+
+ // sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator);
+
+ // sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator);
+
+ // sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control);
+
+ // sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude);
+
+ // sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude);
+
+ // sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude);
+
+ // sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method);
+
+ // sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t);
+
+ // sets the length (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length);
+
+ // sets the width (number of columes) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width);
+
+ // sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings);
+
+ // sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source);
+
+ // sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode);
+
+ // sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation);
+
+ // sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit);
+
+ // sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation);
+
+ // sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type);
+
+ // sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness);
+
+ // sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator);
+
+ // sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator);
+
+ // sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time);
+
+ // sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance);
+
+ // sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning);
+
+ // sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make);
+
+ // sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model);
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1(const void* thumbnail_buffer, uint32_t size);
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer();
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length();
+
+ protected:
+ // sets the version of this standard supported.
+ // Returns false if memory allocation fails.
+ virtual bool setExifVersion(const std::string& exif_version);
+
+
+ // Resets the pointers and memories.
+ virtual void reset();
+
+ // Adds a variable length tag to |exif_data_|. It will remove the original one
+ // if the tag exists.
+ // Returns the entry of the tag. The reference count of returned ExifEntry is
+ // two.
+ virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ uint64_t components,
+ unsigned int size);
+
+ // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if
+ // the tag exists.
+ // Returns the entry of the tag. It adds one reference count to returned
+ // ExifEntry.
+ virtual std::unique_ptr<ExifEntry> addEntry(ExifIfd ifd, ExifTag tag);
+
+ // Helpe functions to add exif data with different types.
+ virtual bool setShort(ExifIfd ifd,
+ ExifTag tag,
+ uint16_t value,
+ const std::string& msg);
+
+ virtual bool setLong(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t value,
+ const std::string& msg);
+
+ virtual bool setRational(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t numerator,
+ uint32_t denominator,
+ const std::string& msg);
+
+ virtual bool setSRational(ExifIfd ifd,
+ ExifTag tag,
+ int32_t numerator,
+ int32_t denominator,
+ const std::string& msg);
+
+ virtual bool setString(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ const std::string& buffer,
+ const std::string& msg);
+
+ // Destroys the buffer of APP1 segment if exists.
+ virtual void destroyApp1();
+
+ // The Exif data (APP1). Owned by this class.
+ ExifData* exif_data_;
+ // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but
+ // owned by this class.
+ uint8_t* app1_buffer_;
+ // The length of |app1_buffer_|.
+ unsigned int app1_length_;
+
+};
+
+#define SET_SHORT(ifd, tag, value) \
+ do { \
+ if (setShort(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_LONG(ifd, tag, value) \
+ do { \
+ if (setLong(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_RATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_SRATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setSRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_STRING(ifd, tag, format, buffer) \
+ do { \
+ if (setString(ifd, tag, format, buffer, #tag) == false) \
+ return false; \
+ } while (0);
+
+// This comes from the Exif Version 2.2 standard table 6.
+const char gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0};
+
+static void setLatitudeOrLongitudeData(unsigned char* data, double num) {
+ // Take the integer part of |num|.
+ ExifLong degrees = static_cast<ExifLong>(num);
+ ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
+ ExifLong microseconds =
+ static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
+ exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
+ exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {minutes, 1});
+ exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {microseconds, 1000000});
+}
+
+ExifUtils *ExifUtils::create() {
+ return new ExifUtilsImpl();
+}
+
+ExifUtils::~ExifUtils() {
+}
+
+ExifUtilsImpl::ExifUtilsImpl()
+ : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}
+
+ExifUtilsImpl::~ExifUtilsImpl() {
+ reset();
+}
+
+
+bool ExifUtilsImpl::initialize() {
+ reset();
+ exif_data_ = exif_data_new();
+ if (exif_data_ == nullptr) {
+ ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__);
+ return false;
+ }
+ // set the image options.
+ exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+ exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);
+
+ // set exif version to 2.2.
+ if (!setExifVersion("0220")) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setColorSpace(uint16_t color_space) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE, color_space);
+ return true;
+}
+
+bool ExifUtilsImpl::setComponentsConfiguration(
+ const std::string& components_configuration) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION,
+ EXIF_FORMAT_UNDEFINED, components_configuration);
+ return true;
+}
+
+bool ExifUtilsImpl::setCompression(uint16_t compression) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
+ return true;
+}
+
+bool ExifUtilsImpl::setContrast(uint16_t contrast) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast);
+ return true;
+}
+
+bool ExifUtilsImpl::setDateTime(const struct tm& t) {
+ // The length is 20 bytes including NULL for termination in Exif standard.
+ char str[20];
+ int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
+ t.tm_min, t.tm_sec);
+ if (result != sizeof(str) - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+ std::string buffer(str);
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
+ buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
+ buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setDescription(const std::string& description) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII,
+ description);
+ return true;
+}
+
+bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFlash(uint16_t flash) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash);
+ return true;
+}
+
+bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setGainControl(uint16_t gain_control) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsAltitude(double altitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSAltitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (altitude >= 0) {
+ *refEntry->data = 0;
+ } else {
+ *refEntry->data = 1;
+ altitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSAltitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(altitude * 1000), 1000});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLatitude(double latitude) {
+ const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLatitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (latitude >= 0) {
+ memcpy(refEntry->data, "N", sizeof("N"));
+ } else {
+ memcpy(refEntry->data, "S", sizeof("S"));
+ latitude *= -1;
+ }
+
+ const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLatitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, latitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLongitude(double longitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLongitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (longitude >= 0) {
+ memcpy(refEntry->data, "E", sizeof("E"));
+ } else {
+ memcpy(refEntry->data, "W", sizeof("W"));
+ longitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLongitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, longitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) {
+ std::string buffer =
+ std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
+ SET_STRING(EXIF_IFD_GPS, static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD),
+ EXIF_FORMAT_UNDEFINED, buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) {
+ const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
+ const size_t kGpsDateStampSize = 11;
+ std::unique_ptr<ExifEntry> entry =
+ addVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII,
+ kGpsDateStampSize, kGpsDateStampSize);
+ if (!entry) {
+ ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ int result =
+ snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
+ "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
+ if (result != kGpsDateStampSize - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+
+ const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
+ entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
+ 3 * sizeof(ExifRational));
+ if (!entry) {
+ ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_hour), 1});
+ exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_min), 1});
+ exif_set_rational(entry->data + 2 * sizeof(ExifRational),
+ EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_sec), 1});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setImageHeight(uint32_t length) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, length);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, length);
+ return true;
+}
+
+bool ExifUtilsImpl::setImageWidth(uint32_t width) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, width);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width);
+ return true;
+}
+
+bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso_speed_ratings);
+ return true;
+}
+
+bool ExifUtilsImpl::setLightSource(uint16_t light_source) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source);
+ return true;
+}
+
+bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setOrientation(uint16_t orientation) {
+ /*
+ * Orientation value:
+ * 1 2 3 4 5 6 7 8
+ *
+ * 888888 888888 88 88 8888888888 88 88 8888888888
+ * 88 88 88 88 88 88 88 88 88 88 88 88
+ * 8888 8888 8888 8888 88 8888888888 8888888888 88
+ * 88 88 88 88
+ * 88 88 888888 888888
+ */
+ int value = 1;
+ switch (orientation) {
+ case 90:
+ value = 6;
+ break;
+ case 180:
+ value = 3;
+ break;
+ case 270:
+ value = 8;
+ break;
+ default:
+ break;
+ }
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit);
+ return true;
+}
+
+bool ExifUtilsImpl::setSaturation(uint16_t saturation) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation);
+ return true;
+}
+
+bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type);
+ return true;
+}
+
+bool ExifUtilsImpl::setSharpness(uint16_t sharpness) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness);
+ return true;
+}
+
+bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII,
+ subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
+ subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
+ subsec_time);
+ return true;
+}
+
+bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance);
+ return true;
+}
+
+bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning);
+ return true;
+}
+
+bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::generateApp1(const void* thumbnail_buffer, uint32_t size) {
+ destroyApp1();
+ exif_data_->data = const_cast<uint8_t*>(static_cast<const uint8_t*>(thumbnail_buffer));
+ exif_data_->size = size;
+ // Save the result into |app1_buffer_|.
+ exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
+ if (!app1_length_) {
+ ALOGE("%s: Allocate memory for app1_buffer_ failed", __FUNCTION__);
+ return false;
+ }
+ /*
+ * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
+ * be smaller than 65533 because there are two bytes for segment size field.
+ */
+ if (app1_length_ > 65533) {
+ destroyApp1();
+ ALOGE("%s: The size of APP1 segment is too large", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+const uint8_t* ExifUtilsImpl::getApp1Buffer() {
+ return app1_buffer_;
+}
+
+unsigned int ExifUtilsImpl::getApp1Length() {
+ return app1_length_;
+}
+
+bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, exif_version);
+ return true;
+}
+
+bool ExifUtilsImpl::setMake(const std::string& make) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);
+ return true;
+}
+
+bool ExifUtilsImpl::setModel(const std::string& model) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);
+ return true;
+}
+
+void ExifUtilsImpl::reset() {
+ destroyApp1();
+ if (exif_data_) {
+ /*
+ * Since we decided to ignore the original APP1, we are sure that there is
+ * no thumbnail allocated by libexif. |exif_data_->data| is actually
+ * allocated by JpegCompressor. sets |exif_data_->data| to nullptr to
+ * prevent exif_data_unref() destroy it incorrectly.
+ */
+ exif_data_->data = nullptr;
+ exif_data_->size = 0;
+ exif_data_unref(exif_data_);
+ exif_data_ = nullptr;
+ }
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ uint64_t components,
+ unsigned int size) {
+ // Remove old entry if exists.
+ exif_content_remove_entry(exif_data_->ifd[ifd],
+ exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ ExifMem* mem = exif_mem_new_default();
+ if (!mem) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+ void* tmpBuffer = exif_mem_alloc(mem, size);
+ if (!tmpBuffer) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+
+ entry->data = static_cast<unsigned char*>(tmpBuffer);
+ entry->tag = tag;
+ entry->format = format;
+ entry->components = components;
+ entry->size = size;
+
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_mem_unref(mem);
+
+ return entry;
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addEntry(ExifIfd ifd, ExifTag tag) {
+ std::unique_ptr<ExifEntry> entry(exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ if (entry) {
+ // exif_content_get_entry() won't ref the entry, so we ref here.
+ exif_entry_ref(entry.get());
+ return entry;
+ }
+ entry.reset(exif_entry_new());
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ entry->tag = tag;
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_entry_initialize(entry.get(), tag);
+ return entry;
+}
+
+bool ExifUtilsImpl::setShort(ExifIfd ifd,
+ ExifTag tag,
+ uint16_t value,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setLong(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t value,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_long(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setRational(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t numerator,
+ uint32_t denominator,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setSRational(ExifIfd ifd,
+ ExifTag tag,
+ int32_t numerator,
+ int32_t denominator,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setString(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ const std::string& buffer,
+ const std::string& msg) {
+ size_t entry_size = buffer.length();
+ // Since the exif format is undefined, NULL termination is not necessary.
+ if (format == EXIF_FORMAT_ASCII) {
+ entry_size++;
+ }
+ std::unique_ptr<ExifEntry> entry =
+ addVariableLengthEntry(ifd, tag, format, entry_size, entry_size);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ memcpy(entry->data, buffer.c_str(), entry_size);
+ return true;
+}
+
+void ExifUtilsImpl::destroyApp1() {
+ /*
+ * Since there is no API to access ExifMem in ExifData->priv, we use free
+ * here, which is the default free function in libexif. See
+ * exif_data_save_data() for detail.
+ */
+ free(app1_buffer_);
+ app1_buffer_ = nullptr;
+ app1_length_ = 0;
+}
+
+bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight) {
+ // How precise the float-to-rational conversion for EXIF tags would be.
+ constexpr int kRationalPrecision = 10000;
+ if (!setImageWidth(imageWidth) ||
+ !setImageHeight(imageHeight)) {
+ ALOGE("%s: setting image resolution failed.", __FUNCTION__);
+ return false;
+ }
+
+ struct timespec tp;
+ struct tm time_info;
+ bool time_available = clock_gettime(CLOCK_REALTIME, &tp) != -1;
+ localtime_r(&tp.tv_sec, &time_info);
+ if (!setDateTime(time_info)) {
+ ALOGE("%s: setting data time failed.", __FUNCTION__);
+ return false;
+ }
+
+ float focal_length;
+ camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
+ if (entry.count) {
+ focal_length = entry.data.f[0];
+ } else {
+ ALOGE("%s: Cannot find focal length in metadata.", __FUNCTION__);
+ return false;
+ }
+ if (!setFocalLength(
+ static_cast<uint32_t>(focal_length * kRationalPrecision),
+ kRationalPrecision)) {
+ ALOGE("%s: setting focal length failed.", __FUNCTION__);
+ return false;
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES);
+ if (entry.count < 3) {
+ ALOGE("%s: Gps coordinates in metadata is not complete.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLatitude(entry.data.d[0])) {
+ ALOGE("%s: setting gps latitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLongitude(entry.data.d[1])) {
+ ALOGE("%s: setting gps longitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsAltitude(entry.data.d[2])) {
+ ALOGE("%s: setting gps altitude failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD);
+ std::string method_str(reinterpret_cast<const char*>(entry.data.u8));
+ if (!setGpsProcessingMethod(method_str)) {
+ ALOGE("%s: setting gps processing method failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP);
+ time_t timestamp = static_cast<time_t>(entry.data.i64[0]);
+ if (gmtime_r(×tamp, &time_info)) {
+ if (!setGpsTimestamp(time_info)) {
+ ALOGE("%s: setting gps timestamp failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Time tranformation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_ORIENTATION)) {
+ entry = metadata.find(ANDROID_JPEG_ORIENTATION);
+ if (!setOrientation(entry.data.i32[0])) {
+ ALOGE("%s: setting orientation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
+ entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME);
+ // int64_t of nanoseconds
+ if (!setExposureTime(entry.data.i64[0],1000000000u)) {
+ ALOGE("%s: setting exposure time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_LENS_APERTURE)) {
+ const int kAperturePrecision = 10000;
+ entry = metadata.find(ANDROID_LENS_APERTURE);
+ if (!setFNumber(entry.data.f[0] * kAperturePrecision,
+ kAperturePrecision)) {
+ ALOGE("%s: setting F number failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) {
+ entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
+ if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) {
+ const uint32_t kNoFlashFunction = 0x20;
+ if (!setFlash(kNoFlashFunction)) {
+ ALOGE("%s: setting flash failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) {
+ entry = metadata.find(ANDROID_CONTROL_AWB_MODE);
+ if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) {
+ const uint16_t kAutoWhiteBalance = 0;
+ if (!setWhiteBalance(kAutoWhiteBalance)) {
+ ALOGE("%s: setting white balance failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (time_available) {
+ char str[4];
+ if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) {
+ ALOGE("%s: Subsec is invalid: %ld", __FUNCTION__, tp.tv_nsec);
+ return false;
+ }
+ if (!setSubsecTime(std::string(str))) {
+ ALOGE("%s: setting subsec time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index e9741ef..21706a8 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -134,6 +134,38 @@
}
}
+void* HandleImporter::lock(
+ buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
+ Mutex::Autolock lock(mLock);
+ void *ret = 0;
+ IMapper::Rect accessRegion { 0, 0, static_cast<int>(size), 1 };
+
+ if (!mInitialized) {
+ initializeLocked();
+ }
+
+ if (mMapper == nullptr) {
+ ALOGE("%s: mMapper is null!", __FUNCTION__);
+ return ret;
+ }
+
+ hidl_handle acquireFenceHandle;
+ auto buffer = const_cast<native_handle_t*>(buf);
+ mMapper->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpPtr) {
+ if (tmpError == MapperError::NONE) {
+ ret = tmpPtr;
+ } else {
+ ALOGE("%s: failed to lock error %d!",
+ __FUNCTION__, tmpError);
+ }
+ });
+
+ ALOGV("%s: ptr %p size: %zu", __FUNCTION__, ret, size);
+ return ret;
+}
+
+
YCbCrLayout HandleImporter::lockYCbCr(
buffer_handle_t& buf, uint64_t cpuUsage,
const IMapper::Rect& accessRegion) {
diff --git a/camera/common/1.0/default/include/Exif.h b/camera/common/1.0/default/include/Exif.h
new file mode 100644
index 0000000..dc31679
--- /dev/null
+++ b/camera/common/1.0/default/include/Exif.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
+#define ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
+
+#include "CameraMetadata.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+
+// This is based on the original ChromeOS ARC implementation of a V4L2 HAL
+
+// ExifUtils can generate APP1 segment with tags which caller set. ExifUtils can
+// also add a thumbnail in the APP1 segment if thumbnail size is specified.
+// ExifUtils can be reused with different images by calling initialize().
+//
+// Example of using this class :
+// std::unique_ptr<ExifUtils> utils(ExifUtils::Create());
+// utils->initialize();
+// ...
+// // Call ExifUtils functions to set Exif tags.
+// ...
+// utils->GenerateApp1(thumbnail_buffer, thumbnail_size);
+// unsigned int app1Length = utils->GetApp1Length();
+// uint8_t* app1Buffer = new uint8_t[app1Length];
+// memcpy(app1Buffer, utils->GetApp1Buffer(), app1Length);
+class ExifUtils {
+
+ public:
+ virtual ~ExifUtils();
+
+ static ExifUtils* create();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize() = 0;
+
+ // Set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight) = 0;
+
+ // Sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space) = 0;
+
+ // Sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration) = 0;
+
+ // Sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression) = 0;
+
+ // Sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast) = 0;
+
+ // Sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t) = 0;
+
+ // Sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description) = 0;
+
+ // Sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode) = 0;
+
+ // Sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program) = 0;
+
+ // Sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash) = 0;
+
+ // Sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control) = 0;
+
+ // Sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude) = 0;
+
+ // Sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude) = 0;
+
+ // Sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude) = 0;
+
+ // Sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method) = 0;
+
+ // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t) = 0;
+
+ // Sets the height (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length) = 0;
+
+ // Sets the width (number of columns) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width) = 0;
+
+ // Sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings) = 0;
+
+ // Sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source) = 0;
+
+ // Sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode) = 0;
+
+ // Sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation) = 0;
+
+ // Sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit) = 0;
+
+ // Sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation) = 0;
+
+ // Sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type) = 0;
+
+ // Sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness) = 0;
+
+ // Sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time) = 0;
+
+ // Sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance) = 0;
+
+ // Sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning) = 0;
+
+ // Sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make) = 0;
+
+ // Sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model) = 0;
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1(const void* thumbnail_buffer, uint32_t size) = 0;
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer() = 0;
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length() = 0;
+};
+
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+
+#endif // ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
index 443362d..f9cd9fb 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -45,6 +45,9 @@
void closeFence(int fd) const;
// Assume caller has done waiting for acquire fences
+ void* lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size);
+
+ // Assume caller has done waiting for acquire fences
YCbCrLayout lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
const IMapper::Rect& accessRegion);
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index ae275ae..975fb01 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -1198,26 +1198,19 @@
return Void();
}
-/**
- * Static callback forwarding methods from HAL to instance
- */
-void CameraDeviceSession::sProcessCaptureResult(
- const camera3_callback_ops *cb,
- const camera3_capture_result *hal_result) {
- CameraDeviceSession *d =
- const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
-
+void CameraDeviceSession::constructCaptureResult(CaptureResult& result,
+ const camera3_capture_result *hal_result) {
uint32_t frameNumber = hal_result->frame_number;
bool hasInputBuf = (hal_result->input_buffer != nullptr);
size_t numOutputBufs = hal_result->num_output_buffers;
size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
if (numBufs > 0) {
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
if (hasInputBuf) {
int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
// validate if buffer is inflight
auto key = std::make_pair(streamId, frameNumber);
- if (d->mInflightBuffers.count(key) != 1) {
+ if (mInflightBuffers.count(key) != 1) {
ALOGE("%s: input buffer for stream %d frame %d is not inflight!",
__FUNCTION__, streamId, frameNumber);
return;
@@ -1228,7 +1221,7 @@
int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
// validate if buffer is inflight
auto key = std::make_pair(streamId, frameNumber);
- if (d->mInflightBuffers.count(key) != 1) {
+ if (mInflightBuffers.count(key) != 1) {
ALOGE("%s: output buffer for stream %d frame %d is not inflight!",
__FUNCTION__, streamId, frameNumber);
return;
@@ -1237,64 +1230,63 @@
}
// We don't need to validate/import fences here since we will be passing them to camera service
// within the scope of this function
- CaptureResult result;
result.frameNumber = frameNumber;
result.fmqResultSize = 0;
result.partialResult = hal_result->partial_result;
convertToHidl(hal_result->result, &result.result);
if (nullptr != hal_result->result) {
bool resultOverriden = false;
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
// Derive some new keys for backward compatibility
- if (d->mDerivePostRawSensKey) {
+ if (mDerivePostRawSensKey) {
camera_metadata_ro_entry entry;
if (find_camera_metadata_ro_entry(hal_result->result,
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, &entry) == 0) {
- d->mInflightRawBoostPresent[frameNumber] = true;
+ mInflightRawBoostPresent[frameNumber] = true;
} else {
- auto entry = d->mInflightRawBoostPresent.find(frameNumber);
- if (d->mInflightRawBoostPresent.end() == entry) {
- d->mInflightRawBoostPresent[frameNumber] = false;
+ auto entry = mInflightRawBoostPresent.find(frameNumber);
+ if (mInflightRawBoostPresent.end() == entry) {
+ mInflightRawBoostPresent[frameNumber] = false;
}
}
- if ((hal_result->partial_result == d->mNumPartialResults)) {
- if (!d->mInflightRawBoostPresent[frameNumber]) {
+ if ((hal_result->partial_result == mNumPartialResults)) {
+ if (!mInflightRawBoostPresent[frameNumber]) {
if (!resultOverriden) {
- d->mOverridenResult.clear();
- d->mOverridenResult.append(hal_result->result);
+ mOverridenResult.clear();
+ mOverridenResult.append(hal_result->result);
resultOverriden = true;
}
int32_t defaultBoost[1] = {100};
- d->mOverridenResult.update(
+ mOverridenResult.update(
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
defaultBoost, 1);
}
- d->mInflightRawBoostPresent.erase(frameNumber);
+ mInflightRawBoostPresent.erase(frameNumber);
}
}
- auto entry = d->mInflightAETriggerOverrides.find(frameNumber);
- if (d->mInflightAETriggerOverrides.end() != entry) {
+ auto entry = mInflightAETriggerOverrides.find(frameNumber);
+ if (mInflightAETriggerOverrides.end() != entry) {
if (!resultOverriden) {
- d->mOverridenResult.clear();
- d->mOverridenResult.append(hal_result->result);
+ mOverridenResult.clear();
+ mOverridenResult.append(hal_result->result);
resultOverriden = true;
}
- d->overrideResultForPrecaptureCancelLocked(entry->second,
- &d->mOverridenResult);
- if (hal_result->partial_result == d->mNumPartialResults) {
- d->mInflightAETriggerOverrides.erase(frameNumber);
+ overrideResultForPrecaptureCancelLocked(entry->second,
+ &mOverridenResult);
+ if (hal_result->partial_result == mNumPartialResults) {
+ mInflightAETriggerOverrides.erase(frameNumber);
}
}
if (resultOverriden) {
const camera_metadata_t *metaBuffer =
- d->mOverridenResult.getAndLock();
+ mOverridenResult.getAndLock();
convertToHidl(metaBuffer, &result.result);
- d->mOverridenResult.unlock(metaBuffer);
+ mOverridenResult.unlock(metaBuffer);
}
}
if (hasInputBuf) {
@@ -1335,24 +1327,38 @@
// configure_streams right after the processCaptureResult call so we need to finish
// updating inflight queues first
if (numBufs > 0) {
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
if (hasInputBuf) {
int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
auto key = std::make_pair(streamId, frameNumber);
- d->mInflightBuffers.erase(key);
+ mInflightBuffers.erase(key);
}
for (size_t i = 0; i < numOutputBufs; i++) {
int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
auto key = std::make_pair(streamId, frameNumber);
- d->mInflightBuffers.erase(key);
+ mInflightBuffers.erase(key);
}
- if (d->mInflightBuffers.empty()) {
+ if (mInflightBuffers.empty()) {
ALOGV("%s: inflight buffer queue is now empty!", __FUNCTION__);
}
}
+}
+
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+void CameraDeviceSession::sProcessCaptureResult(
+ const camera3_callback_ops *cb,
+ const camera3_capture_result *hal_result) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+ CaptureResult result;
+ d->constructCaptureResult(result, hal_result);
+
d->mResultBatcher.processCaptureResult(result);
}
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index dd73b39..61db671 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -190,7 +190,7 @@
void notify(NotifyMsg& msg);
void processCaptureResult(CaptureResult& result);
- private:
+ protected:
struct InflightBatch {
// Protect access to entire struct. Acquire this lock before read/write any data or
// calling any methods. processCaptureResult and notify will compete for this lock
@@ -235,7 +235,6 @@
bool mRemoved = false;
};
- static const int NOT_BATCHED = -1;
// Get the batch index and pointer to InflightBatch (nullptrt if the frame is not batched)
// Caller must acquire the InflightBatch::mLock before accessing the InflightBatch
@@ -245,6 +244,16 @@
// This method will hold ResultBatcher::mLock briefly
std::pair<int, std::shared_ptr<InflightBatch>> getBatch(uint32_t frameNumber);
+ static const int NOT_BATCHED = -1;
+
+ // move/push function avoids "hidl_handle& operator=(hidl_handle&)", which clones native
+ // handle
+ void moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst);
+ void pushStreamBuffer(StreamBuffer&& src, std::vector<StreamBuffer>& dst);
+
+ void sendBatchMetadataLocked(
+ std::shared_ptr<InflightBatch> batch, uint32_t lastPartialResultIdx);
+
// Check if the first batch in mInflightBatches is ready to be removed, and remove it if so
// This method will hold ResultBatcher::mLock briefly
void checkAndRemoveFirstBatch();
@@ -257,9 +266,7 @@
// send buffers for specified streams
void sendBatchBuffersLocked(
std::shared_ptr<InflightBatch> batch, const std::vector<int>& streams);
- void sendBatchMetadataLocked(
- std::shared_ptr<InflightBatch> batch, uint32_t lastPartialResultIdx);
- // End of sendXXXX methods
+ // End of sendXXXX methods
// helper methods
void freeReleaseFences(hidl_vec<CaptureResult>&);
@@ -267,11 +274,6 @@
void processOneCaptureResult(CaptureResult& result);
void invokeProcessCaptureResultCallback(hidl_vec<CaptureResult> &results, bool tryWriteFmq);
- // move/push function avoids "hidl_handle& operator=(hidl_handle&)", which clones native
- // handle
- void moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst);
- void pushStreamBuffer(StreamBuffer&& src, std::vector<StreamBuffer>& dst);
-
// Protect access to mInflightBatches, mNumPartialResults and mStreamsToBatch
// processCaptureRequest, processCaptureResult, notify will compete for this lock
// Do NOT issue HIDL IPCs while holding this lock (except when HAL reports error)
@@ -325,6 +327,8 @@
static callbacks_process_capture_result_t sProcessCaptureResult;
static callbacks_notify_t sNotify;
+ void constructCaptureResult(CaptureResult& result,
+ const camera3_capture_result *hal_result);
private:
struct TrampolineSessionInterface_3_2 : public ICameraDeviceSession {
diff --git a/camera/device/3.4/Android.bp b/camera/device/3.4/Android.bp
index b3757c0..87acd25 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"types.hal",
+ "ICameraDeviceCallback.hal",
"ICameraDeviceSession.hal",
],
interfaces: [
@@ -19,8 +20,10 @@
],
types: [
"CaptureRequest",
+ "CaptureResult",
"HalStream",
"HalStreamConfiguration",
+ "PhysicalCameraMetadata",
"PhysicalCameraSetting",
"RequestTemplate",
"Stream",
diff --git a/camera/device/3.4/ICameraDeviceCallback.hal b/camera/device/3.4/ICameraDeviceCallback.hal
new file mode 100644
index 0000000..8ce8d4b
--- /dev/null
+++ b/camera/device/3.4/ICameraDeviceCallback.hal
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.device@3.4;
+
+import @3.2::ICameraDeviceCallback;
+
+/**
+ *
+ * Callback methods for the HAL to call into the framework.
+ *
+ * These methods are used to return metadata and image buffers for a completed
+ * or failed captures, and to notify the framework of asynchronous events such
+ * as errors.
+ *
+ * The framework must not call back into the HAL from within these callbacks,
+ * and these calls must not block for extended periods.
+ *
+ */
+interface ICameraDeviceCallback extends @3.2::ICameraDeviceCallback {
+ /**
+ * processCaptureResult_3_4:
+ *
+ * Identical to @3.2::ICameraDeviceCallback.processCaptureResult, except
+ * that it takes a list of @3.4::CaptureResult, which could contain
+ * physical camera metadata for logical multi-camera.
+ *
+ */
+ processCaptureResult_3_4(vec<@3.4::CaptureResult> results);
+};
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index 61ac244..a936dae 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -34,7 +34,7 @@
srcs: [
"CameraDevice.cpp",
"CameraDeviceSession.cpp",
- "convert.cpp",
+ "convert.cpp"
],
shared_libs: [
"libhidlbase",
@@ -89,6 +89,8 @@
"libfmq",
"libsync",
"libyuv",
+ "libjpeg",
+ "libexif",
],
static_libs: [
"android.hardware.camera.common@1.0-helper",
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index d054788..f6c6b2b 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -34,7 +34,23 @@
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) :
- V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback) {
+ V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback),
+ mResultBatcher_3_4(callback) {
+
+ mHasCallback_3_4 = false;
+
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ sp<ICameraDeviceCallback> callback3_4 = castResult;
+ if (callback3_4 != nullptr) {
+ process_capture_result = sProcessCaptureResult_3_4;
+ notify = sNotify_3_4;
+ mHasCallback_3_4 = true;
+ if (!mInitFail) {
+ mResultBatcher_3_4.setResultMetadataQueue(mResultMetadataQueue);
+ }
+ }
+ }
}
CameraDeviceSession::~CameraDeviceSession() {
@@ -54,6 +70,18 @@
Status status = initStatus();
HalStreamConfiguration outStreams;
+ // If callback is 3.2, make sure no physical stream is configured
+ if (!mHasCallback_3_4) {
+ for (size_t i = 0; i < requestedConfiguration.streams.size(); i++) {
+ if (requestedConfiguration.streams[i].physicalCameraId.size() > 0) {
+ ALOGE("%s: trying to configureStreams with physical camera id with V3.2 callback",
+ __FUNCTION__);
+ _hidl_cb(Status::INTERNAL_ERROR, outStreams);
+ return Void();
+ }
+ }
+ }
+
// hold the inflight lock for entire configureStreams scope since there must not be any
// inflight request/results during stream configuration.
Mutex::Autolock _l(mInflightLock);
@@ -205,7 +233,7 @@
mVideoStreamIds.push_back(stream.v3_2.id);
}
}
- mResultBatcher.setBatchedStreams(mVideoStreamIds);
+ mResultBatcher_3_4.setBatchedStreams(mVideoStreamIds);
}
Return<void> CameraDeviceSession::processCaptureRequest_3_4(
@@ -224,7 +252,7 @@
}
if (s == Status::OK && requests.size() > 1) {
- mResultBatcher.registerBatch(requests[0].v3_2.frameNumber, requests.size());
+ mResultBatcher_3_4.registerBatch(requests[0].v3_2.frameNumber, requests.size());
}
_hidl_cb(s, numRequestProcessed);
@@ -237,6 +265,14 @@
ALOGE("%s: camera init failed or disconnected", __FUNCTION__);
return status;
}
+ // If callback is 3.2, make sure there are no physical settings.
+ if (!mHasCallback_3_4) {
+ if (request.physicalCameraSettings.size() > 0) {
+ ALOGE("%s: trying to call processCaptureRequest_3_4 with physical camera id "
+ "and V3.2 callback", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
camera3_capture_request_t halRequest;
halRequest.frame_number = request.v3_2.frameNumber;
@@ -407,6 +443,228 @@
return Status::OK;
}
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+void CameraDeviceSession::sProcessCaptureResult_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_capture_result *hal_result) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+ CaptureResult result;
+ d->constructCaptureResult(result.v3_2, hal_result);
+ result.physicalCameraMetadata.resize(hal_result->num_physcam_metadata);
+ for (uint32_t i = 0; i < hal_result->num_physcam_metadata; i++) {
+ std::string physicalId = hal_result->physcam_ids[i];
+ V3_2::CameraMetadata physicalMetadata;
+ V3_2::implementation::convertToHidl(hal_result->physcam_metadata[i], &physicalMetadata);
+ PhysicalCameraMetadata physicalCameraMetadata = {
+ .fmqMetadataSize = 0,
+ .physicalCameraId = physicalId,
+ .metadata = physicalMetadata };
+ result.physicalCameraMetadata[i] = physicalCameraMetadata;
+ }
+ d->mResultBatcher_3_4.processCaptureResult_3_4(result);
+}
+
+void CameraDeviceSession::sNotify_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_notify_msg *msg) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+ V3_2::NotifyMsg hidlMsg;
+ V3_2::implementation::convertToHidl(msg, &hidlMsg);
+
+ if (hidlMsg.type == (V3_2::MsgType) CAMERA3_MSG_ERROR &&
+ hidlMsg.msg.error.errorStreamId != -1) {
+ if (d->mStreamMap.count(hidlMsg.msg.error.errorStreamId) != 1) {
+ ALOGE("%s: unknown stream ID %d reports an error!",
+ __FUNCTION__, hidlMsg.msg.error.errorStreamId);
+ return;
+ }
+ }
+
+ if (static_cast<camera3_msg_type_t>(hidlMsg.type) == CAMERA3_MSG_ERROR) {
+ switch (hidlMsg.msg.error.errorCode) {
+ case V3_2::ErrorCode::ERROR_DEVICE:
+ case V3_2::ErrorCode::ERROR_REQUEST:
+ case V3_2::ErrorCode::ERROR_RESULT: {
+ Mutex::Autolock _l(d->mInflightLock);
+ auto entry = d->mInflightAETriggerOverrides.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightAETriggerOverrides.end() != entry) {
+ d->mInflightAETriggerOverrides.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ auto boostEntry = d->mInflightRawBoostPresent.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightRawBoostPresent.end() != boostEntry) {
+ d->mInflightRawBoostPresent.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ }
+ break;
+ case V3_2::ErrorCode::ERROR_BUFFER:
+ default:
+ break;
+ }
+
+ }
+
+ d->mResultBatcher_3_4.notify(hidlMsg);
+}
+
+CameraDeviceSession::ResultBatcher_3_4::ResultBatcher_3_4(
+ const sp<V3_2::ICameraDeviceCallback>& callback) :
+ V3_3::implementation::CameraDeviceSession::ResultBatcher(callback) {
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ mCallback_3_4 = castResult;
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processCaptureResult_3_4(CaptureResult& result) {
+ auto pair = getBatch(result.v3_2.frameNumber);
+ int batchIdx = pair.first;
+ if (batchIdx == NOT_BATCHED) {
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+ std::shared_ptr<InflightBatch> batch = pair.second;
+ {
+ Mutex::Autolock _l(batch->mLock);
+ // Check if the batch is removed (mostly by notify error) before lock was acquired
+ if (batch->mRemoved) {
+ // Fall back to non-batch path
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+
+ // queue metadata
+ if (result.v3_2.result.size() != 0) {
+ // Save a copy of metadata
+ batch->mResultMds[result.v3_2.partialResult].mMds.push_back(
+ std::make_pair(result.v3_2.frameNumber, result.v3_2.result));
+ }
+
+ // queue buffer
+ std::vector<int> filledStreams;
+ std::vector<V3_2::StreamBuffer> nonBatchedBuffers;
+ for (auto& buffer : result.v3_2.outputBuffers) {
+ auto it = batch->mBatchBufs.find(buffer.streamId);
+ if (it != batch->mBatchBufs.end()) {
+ InflightBatch::BufferBatch& bb = it->second;
+ pushStreamBuffer(std::move(buffer), bb.mBuffers);
+ filledStreams.push_back(buffer.streamId);
+ } else {
+ pushStreamBuffer(std::move(buffer), nonBatchedBuffers);
+ }
+ }
+
+ // send non-batched buffers up
+ if (nonBatchedBuffers.size() > 0 || result.v3_2.inputBuffer.streamId != -1) {
+ CaptureResult nonBatchedResult;
+ nonBatchedResult.v3_2.frameNumber = result.v3_2.frameNumber;
+ nonBatchedResult.v3_2.fmqResultSize = 0;
+ nonBatchedResult.v3_2.outputBuffers.resize(nonBatchedBuffers.size());
+ for (size_t i = 0; i < nonBatchedBuffers.size(); i++) {
+ moveStreamBuffer(
+ std::move(nonBatchedBuffers[i]), nonBatchedResult.v3_2.outputBuffers[i]);
+ }
+ moveStreamBuffer(std::move(result.v3_2.inputBuffer), nonBatchedResult.v3_2.inputBuffer);
+ nonBatchedResult.v3_2.partialResult = 0; // 0 for buffer only results
+ processOneCaptureResult_3_4(nonBatchedResult);
+ }
+
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ // Send data up
+ if (result.v3_2.partialResult > 0) {
+ sendBatchMetadataLocked(batch, result.v3_2.partialResult);
+ }
+ // send buffer up
+ if (filledStreams.size() > 0) {
+ sendBatchBuffersLocked(batch, filledStreams);
+ }
+ }
+ } // end of batch lock scope
+
+ // see if the batch is complete
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ checkAndRemoveFirstBatch();
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processOneCaptureResult_3_4(CaptureResult& result) {
+ hidl_vec<CaptureResult> results;
+ results.resize(1);
+ results[0] = std::move(result);
+ invokeProcessCaptureResultCallback_3_4(results, /* tryWriteFmq */true);
+ freeReleaseFences_3_4(results);
+ return;
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::invokeProcessCaptureResultCallback_3_4(
+ hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+ if (mProcessCaptureResultLock.tryLock() != OK) {
+ ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+ if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
+ ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+ __FUNCTION__);
+ return;
+ }
+ }
+ if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+ for (CaptureResult &result : results) {
+ if (result.v3_2.result.size() > 0) {
+ if (mResultMetadataQueue->write(result.v3_2.result.data(),
+ result.v3_2.result.size())) {
+ result.v3_2.fmqResultSize = result.v3_2.result.size();
+ result.v3_2.result.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ result.v3_2.fmqResultSize = 0;
+ }
+ }
+
+ for (auto& onePhysMetadata : result.physicalCameraMetadata) {
+ if (mResultMetadataQueue->write(onePhysMetadata.metadata.data(),
+ onePhysMetadata.metadata.size())) {
+ onePhysMetadata.fmqMetadataSize = onePhysMetadata.metadata.size();
+ onePhysMetadata.metadata.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ onePhysMetadata.fmqMetadataSize = 0;
+ }
+ }
+ }
+ }
+ mCallback_3_4->processCaptureResult_3_4(results);
+ mProcessCaptureResultLock.unlock();
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::freeReleaseFences_3_4(hidl_vec<CaptureResult>& results) {
+ for (auto& result : results) {
+ if (result.v3_2.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ result.v3_2.inputBuffer.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ for (auto& buf : result.v3_2.outputBuffers) {
+ if (buf.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ buf.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return;
+}
+
} // namespace implementation
} // namespace V3_4
} // namespace device
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index 4ad1768..e6e0ae3 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -47,7 +47,7 @@
// Also make sure that can be done without editing source code
// TODO: b/72261675: make it dynamic since this affects memory usage
-const int kMaxJpegSize = {13 * 1024 * 1024}; // 13MB
+const int kMaxJpegSize = {5 * 1024 * 1024}; // 5MB
} // anonymous namespace
ExternalCameraDevice::ExternalCameraDevice(const std::string& cameraId) :
@@ -227,8 +227,7 @@
status_t ExternalCameraDevice::initDefaultCharsKeys(
::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
- // TODO: changed to HARDWARELEVEL_EXTERNAL later
- const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+ const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
UPDATE(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &hardware_level, 1);
// android.colorCorrection
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 507f092..ff55489 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -30,6 +30,9 @@
#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
#include <libyuv.h>
+#include <jpeglib.h>
+
+
namespace android {
namespace hardware {
namespace camera {
@@ -57,8 +60,9 @@
HandleImporter ExternalCameraDeviceSession::sHandleImporter;
bool isAspectRatioClose(float ar1, float ar2) {
- const float kAspectRatioMatchThres = 0.01f; // This threshold is good enough to distinguish
+ const float kAspectRatioMatchThres = 0.025f; // This threshold is good enough to distinguish
// 4:3/16:9/20:9
+ // 1.33 / 1.78 / 2
return (std::abs(ar1 - ar2) < kAspectRatioMatchThres);
}
@@ -72,7 +76,9 @@
mV4l2Fd(std::move(v4l2Fd)),
mSupportedFormats(sortFormats(supportedFormats)),
mCroppingType(initCroppingType(mSupportedFormats)),
- mOutputThread(new OutputThread(this, mCroppingType)) {
+ mOutputThread(new OutputThread(this, mCroppingType)),
+ mMaxThumbResolution(getMaxThumbResolution()),
+ mMaxJpegResolution(getMaxJpegResolution()) {
mInitFail = initialize();
}
@@ -93,8 +99,8 @@
const std::vector<SupportedV4L2Format>& sortedFmts) {
const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
float maxSizeAr = ASPECT_RATIO(maxSize);
- float minAr = kMinAspectRatio;
- float maxAr = kMaxAspectRatio;
+ float minAr = kMaxAspectRatio;
+ float maxAr = kMinAspectRatio;
for (const auto& fmt : sortedFmts) {
float ar = ASPECT_RATIO(fmt);
if (ar < minAr) {
@@ -724,11 +730,24 @@
ALOGE("%s: out is null", __FUNCTION__);
return -1;
}
+
uint32_t inW = inSize.width;
uint32_t inH = inSize.height;
uint32_t outW = outSize.width;
uint32_t outH = outSize.height;
+ // Handle special case where aspect ratio is close to input but scaled
+ // dimension is slightly larger than input
+ float arIn = ASPECT_RATIO(inSize);
+ float arOut = ASPECT_RATIO(outSize);
+ if (isAspectRatioClose(arIn, arOut)) {
+ out->left = 0;
+ out->top = 0;
+ out->width = inW;
+ out->height = inH;
+ return 0;
+ }
+
if (ct == VERTICAL) {
uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
if (scaledOutH > inH) {
@@ -765,9 +784,9 @@
}
int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
- sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf, YCbCrLayout* out) {
+ sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
Size inSz = {in->mWidth, in->mHeight};
- Size outSz = {halBuf.width, halBuf.height};
+
int ret;
if (inSz == outSz) {
ret = in->getLayout(out);
@@ -855,6 +874,152 @@
return 0;
}
+
+int ExternalCameraDeviceSession::OutputThread::cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size &outSz, YCbCrLayout* out) {
+ Size inSz {in->mWidth, in->mHeight};
+
+ if ((outSz.width * outSz.height) >
+ (mYu12ThumbFrame->mWidth * mYu12ThumbFrame->mHeight)) {
+ ALOGE("%s: Requested thumbnail size too big (%d,%d) > (%d,%d)",
+ __FUNCTION__, outSz.width, outSz.height,
+ mYu12ThumbFrame->mWidth, mYu12ThumbFrame->mHeight);
+ return -1;
+ }
+
+ int ret;
+
+ /* This will crop-and-zoom the input YUV frame to the thumbnail size
+ * Based on the following logic:
+ * 1) Square pixels come in, square pixels come out, therefore single
+ * scale factor is computed to either make input bigger or smaller
+ * depending on if we are upscaling or downscaling
+ * 2) That single scale factor would either make height too tall or width
+ * too wide so we need to crop the input either horizontally or vertically
+ * but not both
+ */
+
+ /* Convert the input and output dimensions into floats for ease of math */
+ float fWin = static_cast<float>(inSz.width);
+ float fHin = static_cast<float>(inSz.height);
+ float fWout = static_cast<float>(outSz.width);
+ float fHout = static_cast<float>(outSz.height);
+
+ /* Compute the one scale factor from (1) above, it will be the smaller of
+ * the two possibilities. */
+ float scaleFactor = std::min( fHin / fHout, fWin / fWout );
+
+ /* Since we are crop-and-zooming (as opposed to letter/pillar boxing) we can
+ * simply multiply the output by our scaleFactor to get the cropped input
+ * size. Note that at least one of {fWcrop, fHcrop} is going to wind up
+ * being {fWin, fHin} respectively because fHout or fWout cancels out the
+ * scaleFactor calculation above.
+ *
+ * Specifically:
+ * if ( fHin / fHout ) < ( fWin / fWout ) we crop the sides off
+ * input, in which case
+ * scaleFactor = fHin / fHout
+ * fWcrop = fHin / fHout * fWout
+ * fHcrop = fHin
+ *
+ * Note that fWcrop <= fWin ( because ( fHin / fHout ) * fWout < fWin, which
+ * is just the inequality above with both sides multiplied by fWout
+ *
+ * on the other hand if ( fWin / fWout ) < ( fHin / fHout) we crop the top
+ * and the bottom off of input, and
+ * scaleFactor = fWin / fWout
+ * fWcrop = fWin
+ * fHCrop = fWin / fWout * fHout
+ */
+ float fWcrop = scaleFactor * fWout;
+ float fHcrop = scaleFactor * fHout;
+
+ /* Convert to integer and truncate to an even number */
+ Size cropSz = { 2*static_cast<uint32_t>(fWcrop/2.0f),
+ 2*static_cast<uint32_t>(fHcrop/2.0f) };
+
+ /* Convert to a centered rectange with even top/left */
+ IMapper::Rect inputCrop {
+ 2*static_cast<int32_t>((inSz.width - cropSz.width)/4),
+ 2*static_cast<int32_t>((inSz.height - cropSz.height)/4),
+ static_cast<int32_t>(cropSz.width),
+ static_cast<int32_t>(cropSz.height) };
+
+ if ((inputCrop.top < 0) ||
+ (inputCrop.top >= static_cast<int32_t>(inSz.height)) ||
+ (inputCrop.left < 0) ||
+ (inputCrop.left >= static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.width <= 0) ||
+ (inputCrop.width + inputCrop.left > static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.height <= 0) ||
+ (inputCrop.height + inputCrop.top > static_cast<int32_t>(inSz.height)))
+ {
+ ALOGE("%s: came up with really wrong crop rectangle",__FUNCTION__);
+ ALOGE("%s: input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return -1;
+ }
+
+ YCbCrLayout inputLayout;
+ ret = in->getCroppedLayout(inputCrop, &inputLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to crop input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return ret;
+ }
+ ALOGV("%s: crop input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGV("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+
+
+ // Scale
+ YCbCrLayout outFullLayout;
+
+ ret = mYu12ThumbFrame->getLayout(&outFullLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to get output buffer layout", __FUNCTION__);
+ return ret;
+ }
+
+
+ ret = libyuv::I420Scale(
+ static_cast<uint8_t*>(inputLayout.y),
+ inputLayout.yStride,
+ static_cast<uint8_t*>(inputLayout.cb),
+ inputLayout.cStride,
+ static_cast<uint8_t*>(inputLayout.cr),
+ inputLayout.cStride,
+ inputCrop.width,
+ inputCrop.height,
+ static_cast<uint8_t*>(outFullLayout.y),
+ outFullLayout.yStride,
+ static_cast<uint8_t*>(outFullLayout.cb),
+ outFullLayout.cStride,
+ static_cast<uint8_t*>(outFullLayout.cr),
+ outFullLayout.cStride,
+ outSz.width,
+ outSz.height,
+ libyuv::FilterMode::kFilterNone);
+
+ if (ret != 0) {
+ ALOGE("%s: failed to scale buffer from %dx%d to %dx%d. Ret %d",
+ __FUNCTION__, inputCrop.width, inputCrop.height,
+ outSz.width, outSz.height, ret);
+ return ret;
+ }
+
+ *out = outFullLayout;
+ return 0;
+}
+
int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
int ret = 0;
@@ -937,6 +1102,436 @@
return 0;
}
+int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12(
+ const Size & inSz, const YCbCrLayout& inLayout,
+ int jpegQuality, const void *app1Buffer, size_t app1Size,
+ void *out, const size_t maxOutSize, size_t &actualCodeSize)
+{
+ /* libjpeg is a C library so we use C-style "inheritance" by
+ * putting libjpeg's jpeg_destination_mgr first in our custom
+ * struct. This allows us to cast jpeg_destination_mgr* to
+ * CustomJpegDestMgr* when we get it passed to us in a callback */
+ struct CustomJpegDestMgr {
+ struct jpeg_destination_mgr mgr;
+ JOCTET *mBuffer;
+ size_t mBufferSize;
+ size_t mEncodedSize;
+ bool mSuccess;
+ } dmgr;
+
+ jpeg_compress_struct cinfo = {};
+ jpeg_error_mgr jerr;
+
+ /* Initialize error handling with standard callbacks, but
+ * then override output_message (to print to ALOG) and
+ * error_exit to set a flag and print a message instead
+ * of killing the whole process */
+ cinfo.err = jpeg_std_error(&jerr);
+
+ cinfo.err->output_message = [](j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("libjpeg error: %s", buffer);
+ };
+ cinfo.err->error_exit = [](j_common_ptr cinfo) {
+ (*cinfo->err->output_message)(cinfo);
+ if(cinfo->client_data) {
+ auto & dmgr =
+ *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
+ dmgr.mSuccess = false;
+ }
+ };
+ /* Now that we initialized some callbacks, let's create our compressor */
+ jpeg_create_compress(&cinfo);
+
+ /* Initialize our destination manager */
+ dmgr.mBuffer = static_cast<JOCTET*>(out);
+ dmgr.mBufferSize = maxOutSize;
+ dmgr.mEncodedSize = 0;
+ dmgr.mSuccess = true;
+ cinfo.client_data = static_cast<void*>(&dmgr);
+
+ /* These lambdas become C-style function pointers and as per C++11 spec
+ * may not capture anything */
+ dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mgr.next_output_byte = dmgr.mBuffer;
+ dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
+ ALOGV("%s:%d jpeg start: %p [%zu]",
+ __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
+ };
+
+ dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
+ ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
+ return 0;
+ };
+
+ dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
+ ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
+ };
+ cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
+
+ /* We are going to be using JPEG in raw data mode, so we are passing
+ * straight subsampled planar YCbCr and it will not touch our pixel
+ * data or do any scaling or anything */
+ cinfo.image_width = inSz.width;
+ cinfo.image_height = inSz.height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_YCbCr;
+
+ /* Initialize defaults and then override what we want */
+ jpeg_set_defaults(&cinfo);
+
+ jpeg_set_quality(&cinfo, jpegQuality, 1);
+ jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+ cinfo.raw_data_in = 1;
+ cinfo.dct_method = JDCT_IFAST;
+
+ /* Configure sampling factors. The sampling factor is JPEG subsampling 420
+ * because the source format is YUV420. Note that libjpeg sampling factors
+ * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
+ * 1 V value for each 2 Y values */
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+
+ /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+ int maxVSampFactor = std::max( {
+ cinfo.comp_info[0].v_samp_factor,
+ cinfo.comp_info[1].v_samp_factor,
+ cinfo.comp_info[2].v_samp_factor
+ });
+ int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
+ cinfo.comp_info[1].v_samp_factor;
+
+ /* Start the compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Compute our macroblock height, so we can pad our input to be vertically
+ * macroblock aligned.
+ * TODO: Does it need to be horizontally MCU aligned too? */
+
+ size_t mcuV = DCTSIZE*maxVSampFactor;
+ size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
+
+ /* libjpeg uses arrays of row pointers, which makes it really easy to pad
+ * data vertically (unfortunately doesn't help horizontally) */
+ std::vector<JSAMPROW> yLines (paddedHeight);
+ std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
+ std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
+
+ uint8_t *py = static_cast<uint8_t*>(inLayout.y);
+ uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
+ uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
+
+ for(uint32_t i = 0; i < paddedHeight; i++)
+ {
+ /* Once we are in the padding territory we still point to the last line
+ * effectively replicating it several times ~ CLAMP_TO_EDGE */
+ int li = std::min(i, inSz.height - 1);
+ yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
+ if(i < paddedHeight / cVSubSampling)
+ {
+ crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
+ cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+ }
+ }
+
+ /* If APP1 data was passed in, use it */
+ if(app1Buffer && app1Size)
+ {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(app1Buffer), app1Size);
+ }
+
+ /* While we still have padded height left to go, keep giving it one
+ * macroblock at a time. */
+ while (cinfo.next_scanline < cinfo.image_height) {
+ const uint32_t batchSize = DCTSIZE * maxVSampFactor;
+ const uint32_t nl = cinfo.next_scanline;
+ JSAMPARRAY planes[3]{ &yLines[nl],
+ &cbLines[nl/cVSubSampling],
+ &crLines[nl/cVSubSampling] };
+
+ uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
+
+ if (done != batchSize) {
+ ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
+ __FUNCTION__, done, batchSize, cinfo.next_scanline,
+ cinfo.image_height);
+ return -1;
+ }
+ }
+
+ /* This will flush everything */
+ jpeg_finish_compress(&cinfo);
+
+ /* Grab the actual code size and set it */
+ actualCodeSize = dmgr.mEncodedSize;
+
+ return 0;
+}
+
+/*
+ * TODO: There needs to be a mechanism to discover allocated buffer size
+ * in the HAL.
+ *
+ * This is very fragile because it is duplicated computation from:
+ * frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
+ *
+ */
+
+/* This assumes mSupportedFormats have all been declared as supporting
+ * HAL_PIXEL_FORMAT_BLOB to the framework */
+Size ExternalCameraDeviceSession::getMaxJpegResolution() const {
+ Size ret { 0, 0 };
+ for(auto & fmt : mSupportedFormats) {
+ if(fmt.width * fmt.height > ret.width * ret.height) {
+ ret = Size { fmt.width, fmt.height };
+ }
+ }
+ return ret;
+}
+
+Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
+ }
+
+ return thumbSize;
+}
+
+
+ssize_t ExternalCameraDeviceSession::getJpegBufferSize(
+ uint32_t width, uint32_t height) const {
+ // Constant from camera3.h
+ const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(CameraBlob);
+ // Get max jpeg size (area-wise).
+ if (mMaxJpegResolution.width == 0) {
+ ALOGE("%s: Do not have a single supported JPEG stream",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // Get max jpeg buffer size
+ ssize_t maxJpegBufferSize = 0;
+ camera_metadata_ro_entry jpegBufMaxSize =
+ mCameraCharacteristics.find(ANDROID_JPEG_MAX_SIZE);
+ if (jpegBufMaxSize.count == 0) {
+ ALOGE("%s: Can't find maximum JPEG size in static metadata!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ maxJpegBufferSize = jpegBufMaxSize.data.i32[0];
+
+ if (maxJpegBufferSize <= kMinJpegBufferSize) {
+ ALOGE("%s: ANDROID_JPEG_MAX_SIZE (%zd) <= kMinJpegBufferSize (%zd)",
+ __FUNCTION__, maxJpegBufferSize, kMinJpegBufferSize);
+ return BAD_VALUE;
+ }
+
+ // Calculate final jpeg buffer size for the given resolution.
+ float scaleFactor = ((float) (width * height)) /
+ (mMaxJpegResolution.width * mMaxJpegResolution.height);
+ ssize_t jpegBufferSize = scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) +
+ kMinJpegBufferSize;
+ if (jpegBufferSize > maxJpegBufferSize) {
+ jpegBufferSize = maxJpegBufferSize;
+ }
+
+ return jpegBufferSize;
+}
+
+int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
+ HalStreamBuffer &halBuf,
+ HalRequest &req)
+{
+ int ret;
+ auto lfail = [&](auto... args) {
+ ALOGE(args...);
+
+ return 1;
+ };
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return 1;
+ }
+
+ ALOGV("%s: HAL buffer sid: %d bid: %" PRIu64 " w: %u h: %u",
+ __FUNCTION__, halBuf.streamId, static_cast<uint64_t>(halBuf.bufferId),
+ halBuf.width, halBuf.height);
+ ALOGV("%s: HAL buffer fmt: %x usage: %" PRIx64 " ptr: %p",
+ __FUNCTION__, halBuf.format, static_cast<uint64_t>(halBuf.usage),
+ halBuf.bufPtr);
+ ALOGV("%s: YV12 buffer %d x %d",
+ __FUNCTION__,
+ mYu12Frame->mWidth, mYu12Frame->mHeight);
+
+ int jpegQuality, thumbQuality;
+ Size thumbSize;
+
+ if (req.setting.exists(ANDROID_JPEG_QUALITY)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_QUALITY);
+ jpegQuality = entry.data.u8[0];
+ } else {
+ return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__);
+ }
+
+ if (req.setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+ thumbQuality = entry.data.u8[0];
+ } else {
+ return lfail(
+ "%s: ANDROID_JPEG_THUMBNAIL_QUALITY not set",
+ __FUNCTION__);
+ }
+
+ if (req.setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
+ static_cast<uint32_t>(entry.data.i32[1])
+ };
+ } else {
+ return lfail(
+ "%s: ANDROID_JPEG_THUMBNAIL_SIZE not set", __FUNCTION__);
+ }
+
+ /* Cropped and scaled YU12 buffer for main and thumbnail */
+ YCbCrLayout yu12Main;
+ Size jpegSize { halBuf.width, halBuf.height };
+
+ /* Compute temporary buffer sizes accounting for the following:
+ * thumbnail can't exceed APP1 size of 64K
+ * main image needs to hold APP1, headers, and at most a poorly
+ * compressed image */
+ const ssize_t maxThumbCodeSize = 64 * 1024;
+ const ssize_t maxJpegCodeSize = parent->getJpegBufferSize(jpegSize.width,
+ jpegSize.height);
+
+ /* Check that getJpegBufferSize did not return an error */
+ if (maxJpegCodeSize < 0) {
+ return lfail(
+ "%s: getJpegBufferSize returned %zd",__FUNCTION__,maxJpegCodeSize);
+ }
+
+
+ /* Hold actual thumbnail and main image code sizes */
+ size_t thumbCodeSize = 0, jpegCodeSize = 0;
+ /* Temporary thumbnail code buffer */
+ std::vector<uint8_t> thumbCode(maxThumbCodeSize);
+
+ YCbCrLayout yu12Thumb;
+ ret = cropAndScaleThumbLocked(mYu12Frame, thumbSize, &yu12Thumb);
+
+ if (ret != 0) {
+ return lfail(
+ "%s: crop and scale thumbnail failed!", __FUNCTION__);
+ }
+
+ /* Scale and crop main jpeg */
+ ret = cropAndScaleLocked(mYu12Frame, jpegSize, &yu12Main);
+
+ if (ret != 0) {
+ return lfail("%s: crop and scale main failed!", __FUNCTION__);
+ }
+
+ /* Encode the thumbnail image */
+ ret = encodeJpegYU12(thumbSize, yu12Thumb,
+ thumbQuality, 0, 0,
+ &thumbCode[0], maxThumbCodeSize, thumbCodeSize);
+
+ if (ret != 0) {
+ return lfail("%s: encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+ }
+
+ /* Combine camera characteristics with request settings to form EXIF
+ * metadata */
+ common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics);
+ meta.append(req.setting);
+
+ /* Generate EXIF object */
+ std::unique_ptr<ExifUtils> utils(ExifUtils::create());
+ /* Make sure it's initialized */
+ utils->initialize();
+
+ utils->setFromMetadata(meta, jpegSize.width, jpegSize.height);
+
+ /* Check if we made a non-zero-sized thumbnail. Currently not possible
+ * that we got this far and the code is size 0, but if this code moves
+ * around it might become relevant again */
+
+ ret = utils->generateApp1(thumbCodeSize ? &thumbCode[0] : 0, thumbCodeSize);
+
+ if (!ret) {
+ return lfail("%s: generating APP1 failed", __FUNCTION__);
+ }
+
+ /* Get internal buffer */
+ size_t exifDataSize = utils->getApp1Length();
+ const uint8_t* exifData = utils->getApp1Buffer();
+
+ /* Lock the HAL jpeg code buffer */
+ void *bufPtr = sHandleImporter.lock(
+ *(halBuf.bufPtr), halBuf.usage, maxJpegCodeSize);
+
+ if (!bufPtr) {
+ return lfail("%s: could not lock %zu bytes", __FUNCTION__, maxJpegCodeSize);
+ }
+
+ /* Encode the main jpeg image */
+ ret = encodeJpegYU12(jpegSize, yu12Main,
+ jpegQuality, exifData, exifDataSize,
+ bufPtr, maxJpegCodeSize, jpegCodeSize);
+
+ /* TODO: Not sure this belongs here, maybe better to pass jpegCodeSize out
+ * and do this when returning buffer to parent */
+ CameraBlob blob { CameraBlobId::JPEG, static_cast<uint32_t>(jpegCodeSize) };
+ void *blobDst =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(bufPtr) +
+ maxJpegCodeSize -
+ sizeof(CameraBlob));
+ memcpy(blobDst, &blob, sizeof(CameraBlob));
+
+ /* Unlock the HAL jpeg code buffer */
+ int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+ if (relFence > 0) {
+ halBuf.acquireFence = relFence;
+ }
+
+ /* Check if our JPEG actually succeeded */
+ if (ret != 0) {
+ return lfail(
+ "%s: encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+ }
+
+ ALOGV("%s: encoded JPEG (ret:%d) with Q:%d max size: %zu",
+ __FUNCTION__, ret, jpegQuality, maxJpegCodeSize);
+
+ return 0;
+}
+
bool ExternalCameraDeviceSession::OutputThread::threadLoop() {
HalRequest req;
auto parent = mParent.promote();
@@ -1017,9 +1612,21 @@
// Gralloc lockYCbCr the buffer
switch (halBuf.format) {
- case PixelFormat::BLOB:
- // TODO: b/72261675 implement JPEG output path
- break;
+ case PixelFormat::BLOB: {
+ int ret = createJpegLocked(halBuf, req);
+
+ if(ret != 0) {
+ ALOGE("%s: createJpegLocked failed with %d",
+ __FUNCTION__, ret);
+ lk.unlock();
+ parent->notifyError(
+ /*frameNum*/req.frameNumber,
+ /*stream*/-1,
+ ErrorCode::ERROR_DEVICE);
+
+ return false;
+ }
+ } break;
case PixelFormat::YCBCR_420_888:
case PixelFormat::YV12: {
IMapper::Rect outRect {0, 0,
@@ -1041,7 +1648,9 @@
YCbCrLayout cropAndScaled;
int ret = cropAndScaleLocked(
- mYu12Frame, halBuf, &cropAndScaled);
+ mYu12Frame,
+ Size { halBuf.width, halBuf.height },
+ &cropAndScaled);
if (ret != 0) {
ALOGE("%s: crop and scale failed!", __FUNCTION__);
lk.unlock();
@@ -1087,7 +1696,8 @@
}
Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers(
- const Size& v4lSize, const hidl_vec<Stream>& streams) {
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams) {
std::lock_guard<std::mutex> lk(mLock);
if (mScaledYu12Frames.size() != 0) {
ALOGE("%s: intermediate buffer pool has %zu inflight buffers! (expect 0)",
@@ -1107,6 +1717,19 @@
}
}
+ // Allocating intermediate YU12 thumbnail frame
+ if (mYu12ThumbFrame == nullptr ||
+ mYu12ThumbFrame->mWidth != thumbSize.width ||
+ mYu12ThumbFrame->mHeight != thumbSize.height) {
+ mYu12ThumbFrame.clear();
+ mYu12ThumbFrame = new AllocatedFrame(thumbSize.width, thumbSize.height);
+ int ret = mYu12ThumbFrame->allocate(&mYu12ThumbFrameLayout);
+ if (ret != 0) {
+ ALOGE("%s: allocating YU12 thumb frame failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+
// Allocating scaled buffers
for (const auto& stream : streams) {
Size sz = {stream.width, stream.height};
@@ -1646,7 +2269,24 @@
}
Size v4lSize = {v4l2Fmt.width, v4l2Fmt.height};
- status = mOutputThread->allocateIntermediateBuffers(v4lSize, config.streams);
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGE("%s: non-zero thumbnail size not available", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ status = mOutputThread->allocateIntermediateBuffers(v4lSize,
+ mMaxThumbResolution, config.streams);
if (status != Status::OK) {
ALOGE("%s: allocating intermediate buffers failed!", __FUNCTION__);
return status;
@@ -1667,6 +2307,7 @@
switch (config.streams[i].format) {
case PixelFormat::BLOB:
case PixelFormat::YCBCR_420_888:
+ case PixelFormat::YV12: // Used by SurfaceTexture
// No override
out->streams[i].v3_2.overrideFormat = config.streams[i].format;
break;
@@ -1679,7 +2320,7 @@
mStreamMap[config.streams[i].id].format = out->streams[i].v3_2.overrideFormat;
break;
default:
- ALOGE("%s: unsupported format %x", __FUNCTION__, config.streams[i].format);
+ ALOGE("%s: unsupported format 0x%x", __FUNCTION__, config.streams[i].format);
return Status::ILLEGAL_ARGUMENT;
}
}
@@ -1805,8 +2446,8 @@
intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
break;
default:
- ALOGE("%s: unknown template type %d", __FUNCTION__, type);
- return BAD_VALUE;
+ ALOGV("%s: unsupported RequestTemplate type %d", __FUNCTION__, type);
+ continue;
}
UPDATE(mdCopy, ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
@@ -1833,15 +2474,14 @@
const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-
- // TODO: b/72261912 AF should stay LOCKED until cancel is seen
- bool afTrigger = false;
+ bool afTrigger = mAfTrigger;
if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) {
+ Mutex::Autolock _l(mLock);
camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER);
if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) {
- afTrigger = true;
+ mAfTrigger = afTrigger = true;
} else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) {
- afTrigger = false;
+ mAfTrigger = afTrigger = false;
}
}
@@ -1871,6 +2511,9 @@
return -EINVAL;
}
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
+
// android.scaler
const int32_t crop_region[] = {
active_array_size.data.i32[0], active_array_size.data.i32[1],
diff --git a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
index 913bd78..9cd7da7 100644
--- a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
+++ b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
@@ -19,6 +19,7 @@
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
#include <../../3.3/default/CameraDeviceSession.h>
#include <../../3.3/default/include/convert.h>
#include <fmq/MessageQueue.h>
@@ -46,6 +47,7 @@
using ::android::hardware::camera::device::V3_4::StreamConfiguration;
using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_4::ICameraDeviceCallback;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
using ::android::hardware::kSynchronizedReadWrite;
@@ -94,6 +96,25 @@
Status processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request);
std::map<int, std::string> mPhysicalCameraIdMap;
+
+ static V3_2::implementation::callbacks_process_capture_result_t sProcessCaptureResult_3_4;
+ static V3_2::implementation::callbacks_notify_t sNotify_3_4;
+
+ class ResultBatcher_3_4 : public V3_3::implementation::CameraDeviceSession::ResultBatcher {
+ public:
+ ResultBatcher_3_4(const sp<V3_2::ICameraDeviceCallback>& callback);
+ void processCaptureResult_3_4(CaptureResult& result);
+ private:
+ void freeReleaseFences_3_4(hidl_vec<CaptureResult>&);
+ void processOneCaptureResult_3_4(CaptureResult& result);
+ void invokeProcessCaptureResultCallback_3_4(hidl_vec<CaptureResult> &results,
+ bool tryWriteFmq);
+
+ sp<ICameraDeviceCallback> mCallback_3_4;
+ } mResultBatcher_3_4;
+
+ // Whether this camera device session is created with version 3.4 callback.
+ bool mHasCallback_3_4;
private:
struct TrampolineSessionInterface_3_4 : public ICameraDeviceSession {
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index d8a17f6..5856306 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -30,6 +30,7 @@
#include <unordered_set>
#include "CameraMetadata.h"
#include "HandleImporter.h"
+#include "Exif.h"
#include "utils/KeyedVector.h"
#include "utils/Mutex.h"
#include "utils/Thread.h"
@@ -58,10 +59,13 @@
using ::android::hardware::camera::device::V3_2::StreamRotation;
using ::android::hardware::camera::device::V3_2::StreamType;
using ::android::hardware::camera::device::V3_2::DataspaceFlags;
+using ::android::hardware::camera::device::V3_2::CameraBlob;
+using ::android::hardware::camera::device::V3_2::CameraBlobId;
using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::helper::ExifUtils;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::Dataspace;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
@@ -272,13 +276,19 @@
hidl_vec<CaptureResult> &results, bool tryWriteFmq);
static void freeReleaseFences(hidl_vec<CaptureResult>&);
+ Size getMaxJpegResolution() const;
+ Size getMaxThumbResolution() const;
+
+ ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
+
class OutputThread : public android::Thread {
public:
OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
~OutputThread();
Status allocateIntermediateBuffers(
- const Size& v4lSize, const hidl_vec<Stream>& streams);
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams);
Status submitRequest(const HalRequest&);
void flush();
virtual bool threadLoop() override;
@@ -296,12 +306,24 @@
void waitForNextRequest(HalRequest* out);
int cropAndScaleLocked(
- sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf,
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
YCbCrLayout* out);
int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
Size sz, uint32_t format);
+ static int encodeJpegYU12(const Size &inSz,
+ const YCbCrLayout& inLayout, int jpegQuality,
+ const void *app1Buffer, size_t app1Size,
+ void *out, size_t maxOutSize,
+ size_t &actualCodeSize);
+
+ int createJpegLocked(HalStreamBuffer &halBuf, HalRequest &req);
+
mutable std::mutex mLock;
std::condition_variable mRequestCond;
wp<ExternalCameraDeviceSession> mParent;
@@ -312,9 +334,11 @@
// (Scale)-> mScaledYu12Frames
// (Format convert) -> output gralloc frames
sp<AllocatedFrame> mYu12Frame;
+ sp<AllocatedFrame> mYu12ThumbFrame;
std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
YCbCrLayout mYu12FrameLayout;
+ YCbCrLayout mYu12ThumbFrameLayout;
};
// Protect (most of) HIDL interface methods from synchronized-entering
@@ -359,6 +383,8 @@
// Stream ID -> circulating buffers map
std::map<int, CirculatingBuffers> mCirculatingBuffers;
+ bool mAfTrigger = false;
+
static HandleImporter sHandleImporter;
/* Beginning of members not changed after initialize() */
@@ -371,6 +397,9 @@
Mutex mProcessCaptureResultLock;
std::unordered_map<RequestTemplate, CameraMetadata> mDefaultRequests;
+
+ const Size mMaxThumbResolution;
+ const Size mMaxJpegResolution;
/* End of members not changed after initialize() */
private:
diff --git a/camera/device/3.4/types.hal b/camera/device/3.4/types.hal
index 429db3e..d80ab67 100644
--- a/camera/device/3.4/types.hal
+++ b/camera/device/3.4/types.hal
@@ -22,6 +22,7 @@
import @3.3::HalStream;
import @3.2::CameraMetadata;
import @3.2::CaptureRequest;
+import @3.2::CaptureResult;
/**
* Stream:
@@ -30,7 +31,7 @@
* by the framework by its buffer resolution and format, and additionally by the
* HAL with the gralloc usage flags and the maximum in-flight buffer count.
*
- * This version extends the @3.2 Stream with the physicalCameraId field.
+ * This version extends the @3.2 Stream with the physicalCameraId and bufferSize field.
*/
struct Stream {
/**
@@ -59,6 +60,21 @@
* instance names returned by getCameraIdList().
*/
string physicalCameraId;
+
+ /**
+ * The size of a buffer from this Stream, in bytes.
+ *
+ * For non PixelFormat::BLOB formats, this entry must be 0 and HAL should use
+ * android.hardware.graphics.mapper lockYCbCr API to get buffer layout.
+ *
+ * For BLOB format with dataSpace Dataspace::DEPTH, this must be zero and and HAL must
+ * determine the buffer size based on ANDROID_DEPTH_MAX_DEPTH_SAMPLES.
+ *
+ * For BLOB format with dataSpace Dataspace::JFIF, this must be non-zero and represent the
+ * maximal size HAL can lock using android.hardware.graphics.mapper lock API.
+ *
+ */
+ uint32_t bufferSize;
};
/**
@@ -226,3 +242,66 @@
*/
vec<PhysicalCameraSetting> physicalCameraSettings;
};
+
+/**
+ * PhysicalCameraMetadata:
+ *
+ * Individual camera metadata for a physical camera as part of a logical
+ * multi-camera. Camera HAL should return one such metadata for each physical
+ * camera being requested on.
+ */
+struct PhysicalCameraMetadata {
+ /**
+ * If non-zero, read metadata from result metadata queue instead
+ * (see ICameraDeviceSession.getCaptureResultMetadataQueue).
+ * If zero, read metadata from .metadata field.
+ */
+ uint64_t fmqMetadataSize;
+
+ /**
+ * Contains the physical device camera id. As long as the corresponding
+ * processCaptureRequest requests on a particular physical camera stream,
+ * the metadata for that physical camera should be generated for the capture
+ * result. */
+ string physicalCameraId;
+
+ /**
+ * If fmqMetadataSize is zero, the metadata buffer contains the metadata
+ * for the physical device with physicalCameraId.
+ *
+ * The v3_2 CaptureResult metadata is read first from the FMQ, followed by
+ * the physical cameras' metadata starting from index 0.
+ */
+ CameraMetadata metadata;
+};
+
+/**
+ * CaptureResult:
+ *
+ * Identical to @3.2::CaptureResult, except that it contains a list of
+ * physical camera metadata.
+ *
+ * Physical camera metadata needs to be generated if and only if a
+ * request is pending on a stream from that physical camera. For example,
+ * if the processCaptureRequest call doesn't request on physical camera
+ * streams, the physicalCameraMetadata field of the CaptureResult being returned
+ * should be an 0-size vector. If the processCaptureRequest call requests on
+ * streams from one of the physical camera, the physicalCameraMetadata field
+ * should contain one metadata describing the capture from that physical camera.
+ *
+ * For a CaptureResult that contains physical camera metadata, its
+ * partialResult field must be android.request.partialResultCount. In other
+ * words, the physicalCameraMetadata must only be contained in a final capture
+ * result.
+ */
+struct CaptureResult {
+ /**
+ * The definition of CaptureResult from the prior version.
+ */
+ @3.2::CaptureResult v3_2;
+
+ /**
+ * The physical metadata for logical multi-camera.
+ */
+ vec<PhysicalCameraMetadata> physicalCameraMetadata;
+};
diff --git a/drm/1.1/IDrmPlugin.hal b/drm/1.1/IDrmPlugin.hal
index c32d2b5..7dd397a 100644
--- a/drm/1.1/IDrmPlugin.hal
+++ b/drm/1.1/IDrmPlugin.hal
@@ -23,6 +23,8 @@
import @1.1::DrmMetricGroup;
import @1.1::HdcpLevel;
import @1.1::KeyRequestType;
+import @1.0::SecureStopId;
+import @1.1::SecureStopRelease;
import @1.1::SecurityLevel;
/**
@@ -176,4 +178,56 @@
* plugin.
*/
getMetrics() generates (Status status, vec<DrmMetricGroup> metric_groups);
+
+ /**
+ * Get the IDs of all secure stops on the device
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * IDs cannot be returned.
+ * @return secureStopIds a list of the IDs
+ */
+ getSecureStopIds() generates
+ (Status status, vec<SecureStopId> secureStopIds);
+
+ /**
+ * Release secure stops given a release message from the key server
+ *
+ * @param ssRelease the secure stop release message identifying one or more
+ * secure stops to release. ssRelease is opaque, it is passed directly from
+ * a DRM license server through the app and media framework to the vendor
+ * HAL module. The format and content of ssRelease must be defined by the
+ * DRM scheme being implemented according to this HAL. The DRM scheme
+ * can be identified by its UUID which can be queried using
+ * IDrmFactory::isCryptoSchemeSupported.
+ *
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: BAD_VALUE if ssRelease is invalid or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * cannot be released.
+ */
+ releaseSecureStops(SecureStopRelease ssRelease) generates (Status status);
+
+ /**
+ * Remove a secure stop given its secure stop ID, without requiring
+ * a secure stop release response message from the key server.
+ *
+ * @param secureStopId the ID of the secure stop to release.
+ *
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: BAD_VALUE if the secureStopId is invalid or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * cannot be released.
+ */
+ removeSecureStop(SecureStopId secureStopId) generates (Status status);
+
+ /**
+ * Remove all secure stops on the device without requiring a secure
+ * stop release response message from the key server.
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure
+ * stops cannot be removed.
+ */
+ removeAllSecureStops() generates (Status status);
};
diff --git a/drm/1.1/types.hal b/drm/1.1/types.hal
index 94a6e66..015f1b7 100644
--- a/drm/1.1/types.hal
+++ b/drm/1.1/types.hal
@@ -210,3 +210,10 @@
HW_SECURE_ALL,
};
+/**
+ * Encapsulates a secure stop release opaque object
+ */
+struct SecureStopRelease {
+ vec<uint8_t> opaqueData;
+};
+
diff --git a/gnss/1.1/IGnss.hal b/gnss/1.1/IGnss.hal
index 0c3d876..096f251 100644
--- a/gnss/1.1/IGnss.hal
+++ b/gnss/1.1/IGnss.hal
@@ -17,6 +17,7 @@
package android.hardware.gnss@1.1;
import @1.0::IGnss;
+import @1.0::GnssLocation;
import IGnssCallback;
import IGnssConfiguration;
@@ -78,4 +79,16 @@
* @return gnssMeasurementIface Handle to the IGnssMeasurement interface.
*/
getExtensionGnssMeasurement_1_1() generates (IGnssMeasurement gnssMeasurementIface);
+
+ /**
+ * Injects current location from the best available location provider.
+ *
+ * Unlike injectLocation, this method may inject a recent GNSS location from the HAL
+ * implementation, if that is the best available location known to the framework.
+ *
+ * @param location Location information from the best available location provider.
+ *
+ * @return success Returns true if successful.
+ */
+ injectBestLocation(GnssLocation location) generates (bool success);
};
\ No newline at end of file
diff --git a/gnss/1.1/IGnssCallback.hal b/gnss/1.1/IGnssCallback.hal
index 7a2849e..9fd71ae 100644
--- a/gnss/1.1/IGnssCallback.hal
+++ b/gnss/1.1/IGnssCallback.hal
@@ -35,4 +35,17 @@
* @param name String providing the name of the GNSS HAL implementation
*/
gnssNameCb(string name);
+
+ /**
+ * Callback for requesting Location.
+ *
+ * HAL implementation shall call this when it wants the framework to provide location to assist
+ * with GNSS HAL operation. For example, to assist with time to first fix, and/or error
+ * recovery, it may ask for a location that is independent from GNSS (e.g. from the "network"
+ * LocationProvier), or to provide a Device-Based-Hybrid location to supplement A-GPS/GNSS
+ * emergency call flows managed by the GNSS HAL.
+ *
+ * @param independentFromGnss True if requesting a location that is independent from GNSS.
+ */
+ gnssRequestLocationCb(bool independentFromGnss);
};
\ No newline at end of file
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.h b/gnss/1.1/vts/functional/gnss_hal_test.h
index a06db5d..6aab3cb 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.h
+++ b/gnss/1.1/vts/functional/gnss_hal_test.h
@@ -74,6 +74,9 @@
}
Return<void> gnssAcquireWakelockCb() override { return Void(); }
Return<void> gnssReleaseWakelockCb() override { return Void(); }
+ Return<void> gnssRequestLocationCb(bool /* independentFromGnss */) override {
+ return Void();
+ }
Return<void> gnssRequestTimeCb() override { return Void(); }
// Actual (test) callback handlers
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index c9e36a9..b4bad2c 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -23,6 +23,8 @@
using android::hardware::hidl_vec;
using android::hardware::gnss::V1_0::GnssConstellationType;
+using android::hardware::gnss::V1_0::GnssLocation;
+using android::hardware::gnss::V1_0::IGnssDebug;
using android::hardware::gnss::V1_1::IGnssConfiguration;
using android::hardware::gnss::V1_1::IGnssMeasurement;
@@ -363,4 +365,83 @@
result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
-}
\ No newline at end of file
+}
+
+/*
+ * InjectBestLocation
+ *
+ * Ensure successfully injecting a location.
+ */
+TEST_F(GnssHalTest, InjectBestLocation) {
+ GnssLocation gnssLocation = {.gnssLocationFlags = 0, // set below
+ .latitudeDegrees = 43.0,
+ .longitudeDegrees = -180,
+ .altitudeMeters = 1000,
+ .speedMetersPerSec = 0,
+ .bearingDegrees = 0,
+ .horizontalAccuracyMeters = 0.1,
+ .verticalAccuracyMeters = 0.1,
+ .speedAccuracyMetersPerSecond = 0.1,
+ .bearingAccuracyDegrees = 0.1,
+ .timestamp = 1534567890123L};
+ gnssLocation.gnssLocationFlags |=
+ GnssLocationFlags::HAS_LAT_LONG | GnssLocationFlags::HAS_ALTITUDE |
+ GnssLocationFlags::HAS_SPEED | GnssLocationFlags::HAS_HORIZONTAL_ACCURACY |
+ GnssLocationFlags::HAS_VERTICAL_ACCURACY | GnssLocationFlags::HAS_SPEED_ACCURACY |
+ GnssLocationFlags::HAS_BEARING | GnssLocationFlags::HAS_BEARING_ACCURACY;
+
+ CheckLocation(gnssLocation, true);
+
+ auto result = gnss_hal_->injectBestLocation(gnssLocation);
+
+ ASSERT_TRUE(result.isOk());
+ EXPECT_TRUE(result);
+}
+
+/*
+ * GnssDebugValuesSanityTest:
+ * Ensures that GnssDebug values make sense.
+ */
+TEST_F(GnssHalTest, GnssDebugValuesSanityTest) {
+ auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
+ ASSERT_TRUE(gnssDebug.isOk());
+ if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
+ sp<IGnssDebug> iGnssDebug = gnssDebug;
+ EXPECT_NE(iGnssDebug, nullptr);
+
+ IGnssDebug::DebugData data;
+ iGnssDebug->getDebugData(
+ [&data](const IGnssDebug::DebugData& debugData) { data = debugData; });
+
+ if (data.position.valid) {
+ EXPECT_GE(data.position.latitudeDegrees, -90);
+ EXPECT_LE(data.position.latitudeDegrees, 90);
+
+ EXPECT_GE(data.position.longitudeDegrees, -180);
+ EXPECT_LE(data.position.longitudeDegrees, 180);
+
+ EXPECT_GE(data.position.altitudeMeters, -1000); // Dead Sea: -414m
+ EXPECT_LE(data.position.altitudeMeters, 20000); // Mount Everest: 8850m
+
+ EXPECT_GE(data.position.speedMetersPerSec, 0);
+ EXPECT_LE(data.position.speedMetersPerSec, 600);
+
+ EXPECT_GE(data.position.bearingDegrees, -360);
+ EXPECT_LE(data.position.bearingDegrees, 360);
+
+ EXPECT_GE(data.position.horizontalAccuracyMeters, 0);
+ EXPECT_LE(data.position.horizontalAccuracyMeters, 20000000);
+
+ EXPECT_GE(data.position.verticalAccuracyMeters, 0);
+ EXPECT_LE(data.position.verticalAccuracyMeters, 20000);
+
+ EXPECT_GE(data.position.speedAccuracyMetersPerSecond, 0);
+ EXPECT_LE(data.position.speedAccuracyMetersPerSecond, 500);
+
+ EXPECT_GE(data.position.bearingAccuracyDegrees, 0);
+ EXPECT_LE(data.position.bearingAccuracyDegrees, 180);
+
+ EXPECT_GE(data.position.ageSeconds, 0);
+ }
+ }
+}
diff --git a/health/2.0/README b/health/2.0/README
index a0a5f08..49b2b1e 100644
--- a/health/2.0/README
+++ b/health/2.0/README
@@ -96,4 +96,4 @@
# device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
# Add device specific permissions to hal_health_default domain, especially
-# if Step 6.2 or Step 7.2 is done.
+# if Step 6.1 or Step 7.2 is done.
diff --git a/keymaster/4.0/support/Android.bp b/keymaster/4.0/support/Android.bp
index 6b8dcdc..ccd1b56 100644
--- a/keymaster/4.0/support/Android.bp
+++ b/keymaster/4.0/support/Android.bp
@@ -26,6 +26,8 @@
"attestation_record.cpp",
"authorization_set.cpp",
"key_param_output.cpp",
+ "keymaster_utils.cpp",
+ "Keymaster.cpp",
"Keymaster3.cpp",
"Keymaster4.cpp",
],
diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.0/support/Keymaster.cpp
new file mode 100644
index 0000000..bf52c47
--- /dev/null
+++ b/keymaster/4.0/support/Keymaster.cpp
@@ -0,0 +1,90 @@
+/*
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <keymasterV4_0/Keymaster.h>
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <keymasterV4_0/Keymaster3.h>
+#include <keymasterV4_0/Keymaster4.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+using ::android::sp;
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+template <typename Wrapper>
+std::vector<std::unique_ptr<Keymaster>> enumerateDevices(
+ const sp<IServiceManager>& serviceManager) {
+ std::vector<std::unique_ptr<Keymaster>> result;
+
+ bool foundDefault = false;
+ auto& descriptor = Wrapper::WrappedIKeymasterDevice::descriptor;
+ serviceManager->listByInterface(descriptor, [&](const hidl_vec<hidl_string>& names) {
+ for (auto& name : names) {
+ if (name == "default") foundDefault = true;
+ auto device = Wrapper::WrappedIKeymasterDevice::getService();
+ CHECK(device) << "Failed to get service for " << descriptor << " with interface name "
+ << name;
+ result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, name)));
+ }
+ });
+
+ if (!foundDefault) {
+ // "default" wasn't provided by listByInterface. Maybe there's a passthrough
+ // implementation.
+ auto device = Wrapper::WrappedIKeymasterDevice::getService("default");
+ if (device) result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, "default")));
+ }
+
+ return result;
+}
+
+std::vector<std::unique_ptr<Keymaster>> Keymaster::enumerateAvailableDevices() {
+ auto serviceManager = IServiceManager::getService();
+ CHECK(serviceManager) << "Could not retrieve ServiceManager";
+
+ auto km4s = enumerateDevices<Keymaster4>(serviceManager);
+ auto km3s = enumerateDevices<Keymaster3>(serviceManager);
+
+ auto result = std::move(km4s);
+ result.insert(result.end(), std::make_move_iterator(km3s.begin()),
+ std::make_move_iterator(km3s.end()));
+
+ std::sort(result.begin(), result.end(),
+ [](auto& a, auto& b) { return a->halVersion() > b->halVersion(); });
+
+ size_t i = 1;
+ LOG(INFO) << "List of Keymaster HALs found:";
+ for (auto& hal : result) {
+ auto& version = hal->halVersion();
+ LOG(INFO) << "Keymaster HAL #" << i << ": " << version.keymasterName << " from "
+ << version.authorName << " SecurityLevel: " << toString(version.securityLevel)
+ << " HAL : " << hal->descriptor() << " instance " << hal->instanceName();
+ }
+
+ return result;
+}
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+}; // namespace android
diff --git a/keymaster/4.0/support/Keymaster3.cpp b/keymaster/4.0/support/Keymaster3.cpp
index 6dfe85b..b2cdbd9 100644
--- a/keymaster/4.0/support/Keymaster3.cpp
+++ b/keymaster/4.0/support/Keymaster3.cpp
@@ -18,7 +18,7 @@
#include <keymasterV4_0/Keymaster3.h>
#include <android-base/logging.h>
-#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/keymaster_utils.h>
namespace android {
namespace hardware {
@@ -82,35 +82,6 @@
return std::copy(value_ptr, value_ptr + sizeof(value), dest);
}
-constexpr size_t kHmacSize = 32;
-
-inline static hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
- static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
- "HardwareAuthToken content size does not match hw_auth_token_t size");
-
- hidl_vec<uint8_t> result;
- result.resize(sizeof(hw_auth_token_t));
- auto pos = result.begin();
- *pos++ = 0; // Version byte
- pos = copy_bytes_to_iterator(token.challenge, pos);
- pos = copy_bytes_to_iterator(token.userId, pos);
- pos = copy_bytes_to_iterator(token.authenticatorId, pos);
- auto auth_type = htonl(static_cast<uint32_t>(token.authenticatorType));
- pos = copy_bytes_to_iterator(auth_type, pos);
- auto timestamp = htonq(token.timestamp);
- pos = copy_bytes_to_iterator(timestamp, pos);
- if (token.mac.size() != kHmacSize) {
- std::fill(pos, pos + kHmacSize, 0);
- } else {
- std::copy(token.mac.begin(), token.mac.end(), pos);
- }
-
- return result;
-}
-
hidl_vec<V3_0::KeyParameter> convertAndAddAuthToken(const hidl_vec<KeyParameter>& params,
const HardwareAuthToken& authToken) {
hidl_vec<V3_0::KeyParameter> converted(params.size() + 1);
@@ -139,37 +110,32 @@
[&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
const hidl_string& keymasterAuthorName) {
- securityLevel_ =
- isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE;
- supportsEllipticCurve_ = supportsEllipticCurve;
+ version_ = {keymasterName, keymasterAuthorName, 0 /* major version, filled below */,
+ isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
+ supportsEllipticCurve};
supportsSymmetricCryptography_ = supportsSymmetricCryptography;
supportsAttestation_ = supportsAttestation;
supportsAllDigests_ = supportsAllDigests;
- keymasterName_ = keymasterName;
- authorName_ = keymasterAuthorName;
});
CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware features";
- if (securityLevel_ == SecurityLevel::SOFTWARE) {
- majorVersion_ = 3;
+ if (version_.securityLevel == SecurityLevel::SOFTWARE) {
+ version_.majorVersion = 3;
} else if (supportsAttestation_) {
- majorVersion_ = 3; // Could be 2, doesn't matter.
+ version_.majorVersion = 3; // Could be 2, doesn't matter.
} else if (supportsSymmetricCryptography_) {
- majorVersion_ = 1;
+ version_.majorVersion = 1;
} else {
- majorVersion_ = 0;
+ version_.majorVersion = 0;
}
}
-Keymaster::VersionResult Keymaster3::halVersion() {
- getVersionIfNeeded();
- return {ErrorCode::OK, majorVersion_, securityLevel_, supportsEllipticCurve_};
-}
-
Return<void> Keymaster3::getHardwareInfo(Keymaster3::getHardwareInfo_cb _hidl_cb) {
getVersionIfNeeded();
- _hidl_cb(securityLevel_, keymasterName_ + " (wrapped by keystore::Keymaster3)", authorName_);
+ _hidl_cb(version_.securityLevel,
+ std::string(version_.keymasterName) + " (wrapped by keystore::Keymaster3)",
+ version_.authorName);
return Void();
}
diff --git a/keymaster/4.0/support/Keymaster4.cpp b/keymaster/4.0/support/Keymaster4.cpp
index fdf78ae..cc3d656 100644
--- a/keymaster/4.0/support/Keymaster4.cpp
+++ b/keymaster/4.0/support/Keymaster4.cpp
@@ -28,19 +28,17 @@
void Keymaster4::getVersionIfNeeded() {
if (haveVersion_) return;
- auto rc = dev_->getHardwareInfo([&](SecurityLevel securityLevel, auto...) {
- securityLevel_ = securityLevel;
- haveVersion_ = true;
- });
+ auto rc =
+ dev_->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& keymasterName,
+ const hidl_string& authorName) {
+ version_ = {keymasterName, authorName, 4 /* major version */, securityLevel,
+ true /* supportsEc */};
+ haveVersion_ = true;
+ });
CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
}
-Keymaster::VersionResult Keymaster4::halVersion() {
- getVersionIfNeeded();
- return {ErrorCode::OK, halMajorVersion(), securityLevel_, true};
-}
-
} // namespace support
} // namespace V4_0
} // namespace keymaster
diff --git a/keymaster/4.0/support/authorization_set.cpp b/keymaster/4.0/support/authorization_set.cpp
index 81cf365..bf77420 100644
--- a/keymaster/4.0/support/authorization_set.cpp
+++ b/keymaster/4.0/support/authorization_set.cpp
@@ -503,6 +503,18 @@
return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
}
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, macLength);
+}
+
AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
std::initializer_list<V4_0::BlockMode> blockModes) {
for (auto mode : blockModes) {
@@ -519,6 +531,14 @@
return *this;
}
+AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
+ std::initializer_list<V4_0::PaddingMode> paddingModes) {
+ for (auto paddingMode : paddingModes) {
+ push_back(TAG_PADDING, paddingMode);
+ }
+ return *this;
+}
+
} // namespace V4_0
} // namespace keymaster
} // namespace hardware
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
index 2686fcd..f9efd51 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
@@ -37,16 +37,37 @@
*/
class Keymaster : public IKeymasterDevice {
public:
+ Keymaster(const hidl_string& descriptor, const hidl_string& instanceName)
+ : descriptor_(descriptor), instanceName_(instanceName) {}
virtual ~Keymaster() {}
struct VersionResult {
- ErrorCode error;
+ hidl_string keymasterName;
+ hidl_string authorName;
uint8_t majorVersion;
SecurityLevel securityLevel;
bool supportsEc;
+
+ bool operator>(const VersionResult& other) const {
+ auto lhs = std::tie(securityLevel, majorVersion, supportsEc);
+ auto rhs = std::tie(other.securityLevel, other.majorVersion, other.supportsEc);
+ return lhs > rhs;
+ }
};
- virtual VersionResult halVersion() = 0;
+ virtual const VersionResult& halVersion() = 0;
+ const hidl_string& descriptor() { return descriptor_; }
+ const hidl_string& instanceName() { return instanceName_; }
+
+ /**
+ * Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least
+ * secure (as defined by VersionResult::operator<).
+ */
+ static std::vector<std::unique_ptr<Keymaster>> enumerateAvailableDevices();
+
+ private:
+ hidl_string descriptor_;
+ hidl_string instanceName_;
};
} // namespace support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
index 4054620..2bb77ca 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
@@ -40,9 +40,15 @@
class Keymaster3 : public Keymaster {
public:
using WrappedIKeymasterDevice = IKeymaster3Device;
- Keymaster3(sp<IKeymaster3Device> km3_dev) : km3_dev_(km3_dev), haveVersion_(false) {}
+ Keymaster3(sp<IKeymaster3Device> km3_dev, const hidl_string& instanceName)
+ : Keymaster(IKeymaster3Device::descriptor, instanceName),
+ km3_dev_(km3_dev),
+ haveVersion_(false) {}
- VersionResult halVersion() override;
+ const VersionResult& halVersion() override {
+ getVersionIfNeeded();
+ return version_;
+ }
Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb);
@@ -114,14 +120,10 @@
sp<IKeymaster3Device> km3_dev_;
bool haveVersion_;
- uint8_t majorVersion_;
- SecurityLevel securityLevel_;
- bool supportsEllipticCurve_;
+ VersionResult version_;
bool supportsSymmetricCryptography_;
bool supportsAttestation_;
bool supportsAllDigests_;
- std::string keymasterName_;
- std::string authorName_;
};
} // namespace support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
index 86ef4f8..96afb13 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
@@ -32,11 +32,15 @@
class Keymaster4 : public Keymaster {
public:
using WrappedIKeymasterDevice = IKeymaster4Device;
- Keymaster4(sp<IKeymasterDevice> km4_dev) : haveVersion_(false), dev_(km4_dev) {}
+ Keymaster4(sp<IKeymasterDevice> km4_dev, const hidl_string& instanceName)
+ : Keymaster(IKeymaster4Device::descriptor, instanceName),
+ haveVersion_(false),
+ dev_(km4_dev) {}
- uint8_t halMajorVersion() { return 4; }
-
- VersionResult halVersion() override;
+ const VersionResult& halVersion() override {
+ getVersionIfNeeded();
+ return version_;
+ }
Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
return dev_->getHardwareInfo(_hidl_cb);
@@ -143,7 +147,7 @@
void getVersionIfNeeded();
bool haveVersion_;
- SecurityLevel securityLevel_;
+ VersionResult version_;
sp<IKeymaster4Device> dev_;
};
diff --git a/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h b/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
index 09a06fe..6c7fd35 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
@@ -270,11 +270,16 @@
AuthorizationSetBuilder& SigningKey();
AuthorizationSetBuilder& EncryptionKey();
+
AuthorizationSetBuilder& NoDigestOrPadding();
+
AuthorizationSetBuilder& EcbMode();
+ AuthorizationSetBuilder& GcmModeMinMacLen(uint32_t minMacLength);
+ AuthorizationSetBuilder& GcmModeMacLen(uint32_t macLength);
AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> blockModes);
AuthorizationSetBuilder& Digest(std::initializer_list<Digest> digests);
+ AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
template <typename... T>
AuthorizationSetBuilder& BlockMode(T&&... a) {
@@ -288,10 +293,6 @@
AuthorizationSetBuilder& Padding(T&&... a) {
return Padding({std::forward<T>(a)...});
}
-
- AuthorizationSetBuilder& Padding(PaddingMode padding) {
- return Authorization(TAG_PADDING, padding);
- }
};
} // namespace V4_0
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 0dfc735..9d6501b 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -104,46 +104,47 @@
typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
static TAG_##name##_t TAG_##name;
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
DECLARE_TYPED_TAG(INVALID);
DECLARE_TYPED_TAG(KEY_SIZE);
DECLARE_TYPED_TAG(MAC_LENGTH);
-DECLARE_TYPED_TAG(CALLER_NONCE);
-DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
-DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
-DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
-DECLARE_TYPED_TAG(ACTIVE_DATETIME);
-DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
-DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(NONCE);
DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
-DECLARE_TYPED_TAG(AUTH_TIMEOUT);
-DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
-DECLARE_TYPED_TAG(APPLICATION_ID);
-DECLARE_TYPED_TAG(APPLICATION_DATA);
-DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
DECLARE_TYPED_TAG(ROOT_OF_TRUST);
-DECLARE_TYPED_TAG(ASSOCIATED_DATA);
-DECLARE_TYPED_TAG(NONCE);
-DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
-DECLARE_TYPED_TAG(OS_VERSION);
-DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
DECLARE_TYPED_TAG(UNIQUE_ID);
-DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
-DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
-DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
-
-DECLARE_TYPED_TAG(PURPOSE);
-DECLARE_TYPED_TAG(ALGORITHM);
-DECLARE_TYPED_TAG(BLOCK_MODE);
-DECLARE_TYPED_TAG(DIGEST);
-DECLARE_TYPED_TAG(PADDING);
-DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
-DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
DECLARE_TYPED_TAG(USER_AUTH_TYPE);
-DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
template <typename... Elems>
struct MetaList {};
@@ -344,6 +345,7 @@
case Tag::ALLOW_WHILE_ON_BODY:
case Tag::ROLLBACK_RESISTANCE:
case Tag::RESET_SINCE_ID_ROTATION:
+ case Tag::TRUSTED_CONFIRMATION_REQUIRED:
case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
return true;
@@ -388,6 +390,7 @@
case Tag::ATTESTATION_ID_MANUFACTURER:
case Tag::ATTESTATION_ID_MODEL:
case Tag::ASSOCIATED_DATA:
+ case Tag::CONFIRMATION_TOKEN:
case Tag::NONCE:
return a.blob == b.blob;
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
new file mode 100644
index 0000000..1c1b000
--- /dev/null
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
+#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
+
+#include <android/hardware/keymaster/4.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const uint8_t* data, const size_t length,
+ bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(data), length, !inPlace);
+ return result;
+}
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const std::string& value, bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(value.data())),
+ static_cast<size_t>(value.size()), !inPlace);
+ return result;
+}
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const std::vector<uint8_t>& blob,
+ bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<uint8_t*>(blob.data()), static_cast<size_t>(blob.size()),
+ !inPlace);
+ return result;
+}
+
+HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer);
+hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token);
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
new file mode 100644
index 0000000..bc610aa
--- /dev/null
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+template <typename T, typename InIter>
+inline static InIter copy_bytes_from_iterator(T* value, InIter src) {
+ uint8_t* value_ptr = reinterpret_cast<uint8_t*>(value);
+ std::copy(src, src + sizeof(T), value_ptr);
+ return src + sizeof(T);
+}
+
+template <typename T, typename OutIter>
+inline static OutIter copy_bytes_to_iterator(const T& value, OutIter dest) {
+ const uint8_t* value_ptr = reinterpret_cast<const uint8_t*>(&value);
+ return std::copy(value_ptr, value_ptr + sizeof(value), dest);
+}
+
+constexpr size_t kHmacSize = 32;
+
+hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
+ static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
+ "HardwareAuthToken content size does not match hw_auth_token_t size");
+
+ hidl_vec<uint8_t> result;
+ result.resize(sizeof(hw_auth_token_t));
+ auto pos = result.begin();
+ *pos++ = 0; // Version byte
+ pos = copy_bytes_to_iterator(token.challenge, pos);
+ pos = copy_bytes_to_iterator(token.userId, pos);
+ pos = copy_bytes_to_iterator(token.authenticatorId, pos);
+ auto auth_type = htonl(static_cast<uint32_t>(token.authenticatorType));
+ pos = copy_bytes_to_iterator(auth_type, pos);
+ auto timestamp = htonq(token.timestamp);
+ pos = copy_bytes_to_iterator(timestamp, pos);
+ if (token.mac.size() != kHmacSize) {
+ std::fill(pos, pos + kHmacSize, 0);
+ } else {
+ std::copy(token.mac.begin(), token.mac.end(), pos);
+ }
+
+ return result;
+}
+
+HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer) {
+ HardwareAuthToken token;
+ static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
+ "HardwareAuthToken content size does not match hw_auth_token_t size");
+
+ if (buffer.size() != sizeof(hw_auth_token_t)) return {};
+
+ auto pos = buffer.begin();
+ ++pos; // skip first byte
+ pos = copy_bytes_from_iterator(&token.challenge, pos);
+ pos = copy_bytes_from_iterator(&token.userId, pos);
+ pos = copy_bytes_from_iterator(&token.authenticatorId, pos);
+ pos = copy_bytes_from_iterator(&token.authenticatorType, pos);
+ token.authenticatorType = static_cast<HardwareAuthenticatorType>(
+ ntohl(static_cast<uint32_t>(token.authenticatorType)));
+ pos = copy_bytes_from_iterator(&token.timestamp, pos);
+ token.timestamp = ntohq(token.timestamp);
+ token.mac.resize(kHmacSize);
+ std::copy(pos, pos + kHmacSize, token.mac.data());
+
+ return token;
+}
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/4.0/types.hal b/keymaster/4.0/types.hal
index 5714c4d..91ec9bf 100644
--- a/keymaster/4.0/types.hal
+++ b/keymaster/4.0/types.hal
@@ -181,6 +181,16 @@
*/
TRUSTED_USER_PRESENCE_REQUIRED = TagType:BOOL | 507,
+ /** TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and specifies
+ * that this key must not be usable unless the user provides confirmation of the data to be
+ * signed. Confirmation is proven to keymaster via an approval token. See CONFIRMATION_TOKEN,
+ * as well as the ConfirmatinUI HAL.
+ *
+ * If an attempt to use a key with this tag does not have a cryptographically valid
+ * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
+ * match the data described in the token, keymaster must return NO_USER_CONFIRMATION. */
+ TRUSTED_CONFIRMATION_REQUIRED = TagType:BOOL | 508,
+
/* Application access control */
APPLICATION_ID = TagType:BYTES | 601, /* Byte string identifying the authorized application. */
@@ -251,6 +261,13 @@
RESET_SINCE_ID_ROTATION = TagType:BOOL | 1004, /* Whether the device has beeen factory reset
* since the last unique ID rotation. Used for
* key attestation. */
+
+ /**
+ * CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user confirmed a
+ * signing request. The content is a full-length HMAC-SHA256 value. See the ConfirmationUI HAL
+ * for details of token computation.
+ */
+ CONFIRMATION_TOKEN = TagType:BYTES | 1005,
};
/**
@@ -453,6 +470,7 @@
HARDWARE_TYPE_UNAVAILABLE = -68,
PROOF_OF_PRESENCE_REQUIRED = -69,
CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+ NO_USER_CONFIRMATION = -71,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 1d8dfdf..dbf5ece 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -712,6 +712,29 @@
}
/*
+ * SigningOperationsTest.NoUserConfirmation
+ *
+ * Verifies that keymaster rejects signing operations for keys with
+ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token
+ * presented.
+ */
+TEST_F(SigningOperationsTest, NoUserConfirmation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)));
+
+ const string message = "12345678901234567890123456789012";
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+ string signature;
+ EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature));
+}
+
+/*
* SigningOperationsTest.RsaPkcs1Sha256Success
*
* Verifies that digested RSA-PKCS1 signature operations succeed.
diff --git a/media/bufferpool/1.0/Android.bp b/media/bufferpool/1.0/Android.bp
new file mode 100644
index 0000000..986da8a
--- /dev/null
+++ b/media/bufferpool/1.0/Android.bp
@@ -0,0 +1,26 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.media.bufferpool@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IAccessor.hal",
+ "IClientManager.hal",
+ "IConnection.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ types: [
+ "Buffer",
+ "BufferStatus",
+ "BufferStatusMessage",
+ "ResultStatus",
+ ],
+ gen_java: false,
+}
+
diff --git a/media/bufferpool/1.0/IAccessor.hal b/media/bufferpool/1.0/IAccessor.hal
new file mode 100644
index 0000000..5b5aec0
--- /dev/null
+++ b/media/bufferpool/1.0/IAccessor.hal
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.media.bufferpool@1.0;
+
+import IConnection;
+/**
+ * IAccessor creates IConnection which is used from IClientManager in order to
+ * use functionality of the specified buffer pool.
+ */
+interface IAccessor {
+
+ /**
+ * Registers a new client and creates IConnection to the buffer pool for
+ * the client. IConnection and FMQ are used by IClientManager in order to
+ * communicate with the buffer pool. Via FMQ IClientManager sends
+ * BufferStatusMesage(s) to the buffer pool.
+ *
+ * FMQ is used to send buffer ownership status changes to a buffer pool
+ * from a buffer pool client. A buffer pool synchronizes FMQ messages when
+ * there is a hidl request from the clients. Every client has its own
+ * connection and FMQ to communicate with the buffer pool. So sending an
+ * FMQ message on behalf of other clients is not possible.
+ *
+ * FMQ messages are sent when a buffer is acquired or released. Also, FMQ
+ * messages are sent when a buffer is transferred from a client to another
+ * client. FMQ has its own ID from a buffer pool. A client is specified
+ * with the ID.
+ *
+ * To transfer a buffer, a sender must send an FMQ message. The message
+ * must include a receiver's ID and a transaction ID. A receiver must send
+ * the transaction ID to fetch a buffer from a buffer pool. Since the
+ * sender already registered the receiver via an FMQ message, The buffer
+ * pool must verify the receiver with the transaction ID. In order to
+ * prevent faking a receiver, a connection to a buffer pool from client is
+ * made and kept private. Also part of transaction ID is a sender ID in
+ * order to prevent fake transactions from other clients. This must be
+ * verified with an FMQ message from a buffer pool.
+ *
+ * @return status The status of the call.
+ * OK - A connection is made successfully.
+ * NO_MEMORY - Memory allocation failure occurred.
+ * ALREADY_EXISTS - A connection was already made.
+ * CRITICAL_ERROR - Other errors.
+ * @return connection The IConnection have interfaces
+ * to get shared buffers from the buffer pool.
+ * @return connectionId Id of IConnection. The Id identifies
+ * sender and receiver in FMQ messages during buffer transfer.
+ * @return mqDesc FMQ descriptor. The descriptor can be used to
+ * send/receive FMQ messages.
+ */
+ connect()
+ generates (ResultStatus status, IConnection connection,
+ int64_t connectionId, fmq_sync<BufferStatusMessage> mqDesc);
+};
diff --git a/media/bufferpool/1.0/IClientManager.hal b/media/bufferpool/1.0/IClientManager.hal
new file mode 100644
index 0000000..e1e8f95
--- /dev/null
+++ b/media/bufferpool/1.0/IClientManager.hal
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.media.bufferpool@1.0;
+
+import IAccessor;
+/**
+ * IClientManager manages IConnection(s) inside a process. A locally
+ * created IConnection represents a communication node(receiver) with the
+ * specified buffer pool(IAccessor).
+ * IConnection(s) are not exposed to other processes(IClientManager).
+ * IClientManager instance must be unique within a process.
+ */
+interface IClientManager {
+
+ /**
+ * Sets up a buffer receiving communication node for the specified
+ * buffer pool. A manager must create a IConnection to the buffer
+ * pool if it does not already have a connection.
+ *
+ * @param bufferPool a buffer pool which is specified with the IAccessor.
+ * The specified buffer pool is the owner of received buffers.
+ * @return status The status of the call.
+ * OK - A sender was set successfully.
+ * NO_MEMORY - Memory allocation failure occurred.
+ * ALREADY_EXISTS - A sender was registered already.
+ * CRITICAL_ERROR - Other errors.
+ * @return connectionId the Id of the communication node to the buffer pool.
+ * This id is used in FMQ to notify IAccessor that a buffer has been
+ * sent to that connection during transfers.
+ */
+ registerSender(IAccessor bufferPool) generates
+ (ResultStatus status, int64_t connectionId);
+};
diff --git a/media/bufferpool/1.0/IConnection.hal b/media/bufferpool/1.0/IConnection.hal
new file mode 100644
index 0000000..e284db2
--- /dev/null
+++ b/media/bufferpool/1.0/IConnection.hal
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.media.bufferpool@1.0;
+
+/**
+ * A connection to a buffer pool which handles requests from a buffer pool
+ * client. The connection must be made in order to receive buffers from
+ * other buffer pool clients.
+ */
+interface IConnection {
+
+ /**
+ * Retrieves a buffer using bufferId. The method must be called from
+ * receiving side of buffer during transferring only when the specified
+ * buffer is neither cached nor used. This fails if the specified
+ * transaction is not valid.
+ *
+ * @param transactionId Unique transaction id for buffer transferring.
+ * @param bufferId Id of the buffer to be fetched.
+ * @return status The status of the call.
+ * OK - A buffer was fetched successfully.
+ * NO_MEMORY - Memory allocation failure occurred.
+ * NOT_FOUND - A buffer was not found due to invalidation.
+ * CRITICAL_ERROR - Other errors.
+ * @return buffer The actual buffer which is specified with bufferId.
+ */
+ fetch(uint64_t transactionId, uint32_t bufferId) generates
+ (ResultStatus status, Buffer buffer);
+};
diff --git a/media/bufferpool/1.0/README.md b/media/bufferpool/1.0/README.md
new file mode 100644
index 0000000..ed985d8
--- /dev/null
+++ b/media/bufferpool/1.0/README.md
@@ -0,0 +1,54 @@
+1. Overview
+
+A buffer pool enables processes to transfer buffers asynchronously.
+Without a buffer pool, a process calls a synchronous method of the other
+process and waits until the call finishes transferring a buffer. This adds
+unwanted latency due to context switching. With help from a buffer pool, a
+process can pass buffers asynchronously and reduce context switching latency.
+
+Passing an interface and a handle adds extra latency also. To mitigate the
+latency, passing IDs with local cache is used. For security concerns about
+rogue clients, FMQ is used to communicate between a buffer pool and a client
+process. FMQ is used to send buffer ownership change status from a client
+process to a buffer pool. Except FMQ, a buffer pool does not use any shared
+memory.
+
+2. FMQ
+
+FMQ is used to send buffer ownership status changes to a buffer pool from a
+buffer pool client. A buffer pool synchronizes FMQ messages when there is a
+hidl request from the clients. Every client has its own connection and FMQ
+to communicate with the buffer pool. So sending an FMQ message on behalf of
+other clients is not possible.
+
+FMQ messages are sent when a buffer is acquired or released. Also, FMQ messages
+are sent when a buffer is transferred from a client to another client. FMQ has
+its own ID from a buffer pool. A client is specified with the ID.
+
+To transfer a buffer, a sender must send an FMQ message. The message must
+include a receiver's ID and a transaction ID. A receiver must send the
+transaction ID to fetch a buffer from a buffer pool. Since the sender already
+registered the receiver via an FMQ message, The buffer pool must verify the
+receiver with the transaction ID. In order to prevent faking a receiver, a
+connection to a buffer pool from client is made and kept privately. Also part of
+transaction ID is a sender ID in order to prevent fake transactions from other
+clients. This must be verified with an FMQ message from a buffer pool.
+
+FMQ messages are defined in BufferStatus and BufferStatusMessage of 'types.hal'.
+
+3. Interfaces
+
+IConnection
+A connection to a buffer pool from a buffer pool client. The connection
+provides the functionalities to share buffers between buffer pool clients.
+The connection must be unique for each client.
+
+IAccessor
+An accessor to a buffer pool which makes a connection to the buffer pool.
+IAccesssor#connect creates an IConnection.
+
+IClientManager
+A manager of buffer pool clients and clients' connections to buffer pools. It
+sets up a process to be a receiver of buffers from a buffer pool. The manager
+is unique in a process.
+
diff --git a/media/bufferpool/1.0/types.hal b/media/bufferpool/1.0/types.hal
new file mode 100644
index 0000000..d8ab597
--- /dev/null
+++ b/media/bufferpool/1.0/types.hal
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.media.bufferpool@1.0;
+
+enum ResultStatus : int32_t {
+ OK = 0,
+
+ NO_MEMORY = 1,
+ ALREADY_EXISTS = 2,
+ NOT_FOUND = 3,
+ CRITICAL_ERROR = 4,
+};
+
+/**
+ * Generic buffer for fast recycling for media/stagefright.
+ *
+ * During media pipeline buffer references are created, shared and
+ * destroyed frequently. The underlying buffers are allocated on demand
+ * by a buffer pool, and are recycled to the buffer pool when they are
+ * no longer referenced by the clients.
+ *
+ * E.g. ion or gralloc buffer
+ */
+struct Buffer {
+ uint32_t id;
+ handle buffer;
+};
+
+/**
+ * Buffer ownership status for the specified client.
+ * Buffer transfer status for the specified buffer transafer transaction.
+ * BufferStatus is posted along with BufferStatusMessage from a client to
+ * the buffer pool for synchronization after status change.
+ */
+enum BufferStatus : int32_t {
+ /** No longer used by the specified client. */
+ NOT_USED = 0,
+ /** Buffer is acquired by the specified client. */
+ USED = 1,
+ /** Buffer is sent by the specified client. */
+ TRANSFER_TO = 2,
+ /** Buffer transfer is acked by the receiver client. */
+ TRANSFER_FROM = 3,
+ /** Buffer transfer is timed out by receiver client. */
+ TRANSFER_TIMEOUT = 4,
+ /** Buffer transfer is not acked by the receiver. */
+ TRANSFER_LOST = 5,
+ /** Buffer fetch request from the client. */
+ TRANSFER_FETCH = 6,
+ /** Buffer transaction succeeded. */
+ TRANSFER_OK = 7,
+ /** Buffer transaction failure. */
+ TRANSFER_ERROR = 8,
+};
+
+/**
+ * Buffer ownership status change message. This message is
+ * sent via fmq to the buffer pool from client processes.
+ */
+struct BufferStatusMessage {
+ /**
+ * Transaction Id = (SenderId : sender local transaction Id)
+ * Transaction Id is created from sender and posted via fmq within
+ * TRANSFER_TO message.
+ */
+ uint64_t transactionId;
+ uint32_t bufferId;
+ BufferStatus newStatus;
+ /** Used by the buffer pool. not by client. */
+ int64_t connectionId;
+ /** Valid only when TRANSFER_TO is posted. */
+ int64_t targetConnectionId;
+ /**
+ * Used by the buffer pool, not by client.
+ * Monotonic timestamp in Us since fixed point in time as decided
+ * by the sender of the message
+ */
+ int64_t timestampUs;
+};
diff --git a/radio/1.2/IRadio.hal b/radio/1.2/IRadio.hal
index babe86f..67ce56c 100644
--- a/radio/1.2/IRadio.hal
+++ b/radio/1.2/IRadio.hal
@@ -18,6 +18,8 @@
import @1.1::IRadio;
import @1.1::RadioAccessNetworks;
+import @1.0::DataProfileInfo;
+import @1.0::RadioTechnology;
/**
* This interface is used by telephony and telecom to talk to cellular radio.
@@ -106,4 +108,67 @@
oneway setLinkCapacityReportingCriteria(int32_t serial, int32_t hysteresisMs,
int32_t hysteresisDlKbps, int32_t hysteresisUlKbps, vec<int32_t> thresholdsDownlinkKbps,
vec<int32_t> thresholdsUplinkKbps, RadioAccessNetworks ran);
+
+ /**
+ * Setup a packet data connection. If DataCallResponse.status returns DataCallFailCause:NONE,
+ * the data connection must be added to data calls and a unsolDataCallListChanged() must be
+ * sent. The call remains until removed by subsequent unsolDataCallIstChanged(). It may be
+ * lost due to many factors, including deactivateDataCall() being issued, the radio powered
+ * off, reception lost or even transient factors like congestion. This data call list is
+ * returned by getDataCallList() and dataCallListChanged().
+ *
+ * The Radio is expected to:
+ * - Create one data call context.
+ * - Create and configure a dedicated interface for the context.
+ * - The interface must be point to point.
+ * - The interface is configured with one or more addresses and is capable of sending and
+ * receiving packets. The prefix length of the addresses must be /32 for IPv4 and /128
+ * for IPv6.
+ * - Must not modify routing configuration related to this interface; routing management is
+ * exclusively within the purview of the Android OS.
+ * - Support simultaneous data call contexts up to DataRegStateResult.maxDataCalls specified
+ * in the response of getDataRegistrationState.
+ *
+ * @param serial Serial number of request.
+ * @param accessNetwork The access network to setup the data call. If the data connection cannot
+ * be established on the specified access network, the setup request must be failed.
+ * @param dataProfileInfo Data profile info.
+ * @param modemCognitive Indicates that the requested profile has previously been provided via
+ * setDataProfile().
+ * @param roamingAllowed Indicates whether or not data roaming is allowed by the user.
+ * @param isRoaming Indicates whether or not the framework has requested this setupDataCall for
+ * a roaming network. The 'protocol' parameter in the old RIL API must be filled
+ * accordingly based on the roaming condition. Note this is for backward compatibility with
+ * the old radio modem. The modem must not use this param for any other reason.
+ * @param reason The request reason. Must be DataRequestReason.NORMAL or
+ * DataRequestReason.HANDOVER.
+ * @param addresses If the reason is DataRequestReason.HANDOVER, this indicates the list of link
+ * addresses of the existing data connection. The format is IP address with optional "/"
+ * prefix length (The format is defined in RFC-4291 section 2.3). For example, "192.0.1.3",
+ * "192.0.1.11/16", or "2001:db8::1/64". Typically one IPv4 or one IPv6 or one of each. If
+ * the prefix length is absent, then the addresses are assumed to be point to point with
+ * IPv4 with prefix length 32 or IPv6 with prefix length 128. This parameter must be ignored
+ * unless reason is DataRequestReason.HANDOVER.
+ * @param dnses If the reason is DataRequestReason.HANDOVER, this indicates the list of DNS
+ * addresses of the existing data connection. The format is defined in RFC-4291 section
+ * 2.2. For example, "192.0.1.3" or "2001:db8::1". This parameter must be ignored unless
+ * reason is DataRequestReason.HANDOVER.
+ *
+ * Response function is IRadioResponse.setupDataCallResponse()
+ */
+ oneway setupDataCall_1_2(int32_t serial, AccessNetwork accessNetwork,
+ DataProfileInfo dataProfileInfo, bool modemCognitive, bool roamingAllowed,
+ bool isRoaming, DataRequestReason reason, vec<string> addresses, vec<string> dnses);
+
+ /**
+ * Deactivate packet data connection and remove from the data call list. An
+ * unsolDataCallListChanged() must be sent when data connection is deactivated.
+ *
+ * @param serial Serial number of request.
+ * @param cid Data call id.
+ * @param reason The request reason. Must be normal, handover, or shutdown.
+ *
+ * Response function is IRadioResponse.deactivateDataCallResponse()
+ */
+ oneway deactivateDataCall_1_2(int32_t serial, int32_t cid, DataRequestReason reason);
};
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 4515237..f2f0b69 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -335,3 +335,32 @@
*/
int32_t cellBandwidthDownlink;
};
+
+enum AccessNetwork : int32_t {
+ /** GSM EDGE Radio Access Network */
+ GERAN = 1,
+ /** Universal Terrestrial Radio Access Network */
+ UTRAN = 2,
+ /** Evolved Universal Terrestrial Radio Access Network */
+ EUTRAN = 3,
+ /** CDMA 2000 network */
+ CDMA2000 = 4,
+ /** Interworking Wireless LAN */
+ IWLAN = 5
+};
+
+enum DataRequestReason : int32_t {
+ /**
+ * The reason of the data request is normal
+ */
+ NORMAL = 0x01,
+ /**
+ * The reason of the data request is device shutdown
+ */
+ SHUTDOWN = 0x02,
+ /**
+ * The reason of the data request is IWLAN data handover to another transport
+ * (e.g. from cellular to wifi or vise versa)
+ */
+ HANDOVER = 0x03,
+};
diff --git a/renderscript/1.0/default/Android.bp b/renderscript/1.0/default/Android.bp
index b996969..d5d6d8d 100644
--- a/renderscript/1.0/default/Android.bp
+++ b/renderscript/1.0/default/Android.bp
@@ -12,7 +12,7 @@
],
shared_libs: [
"libdl",
- "liblog",
+ "libbase",
"libhidlbase",
"libhidltransport",
"libutils",
diff --git a/renderscript/1.0/default/Context.cpp b/renderscript/1.0/default/Context.cpp
index fbfc652..f5b70c9 100644
--- a/renderscript/1.0/default/Context.cpp
+++ b/renderscript/1.0/default/Context.cpp
@@ -1,5 +1,3 @@
-#define LOG_TAG "android.hardware.renderscript@1.0-impl"
-
#include "Context.h"
#include "Device.h"
diff --git a/renderscript/1.0/default/Device.cpp b/renderscript/1.0/default/Device.cpp
index a2b950d..8fda3ff 100644
--- a/renderscript/1.0/default/Device.cpp
+++ b/renderscript/1.0/default/Device.cpp
@@ -1,6 +1,7 @@
#include "Context.h"
#include "Device.h"
+#include <android-base/logging.h>
#include <android/dlext.h>
#include <dlfcn.h>
@@ -54,12 +55,18 @@
.flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = rsNamespace,
};
handle = android_dlopen_ext(filename, RTLD_LAZY | RTLD_LOCAL, &dlextinfo);
+ if (handle == nullptr) {
+ LOG(WARNING) << "android_dlopen_ext(" << filename << ") failed: " << dlerror();
+ }
}
}
if (handle == nullptr) {
// if there is no "rs" namespace (in case when this HAL impl is loaded
// into a vendor process), then use the plain dlopen.
handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
+ if (handle == nullptr) {
+ LOG(FATAL) << "dlopen(" << filename << ") failed: " << dlerror();
+ }
}
dispatchTable dispatchHal = {
diff --git a/secure_element/1.0/Android.bp b/secure_element/1.0/Android.bp
new file mode 100644
index 0000000..e8081df
--- /dev/null
+++ b/secure_element/1.0/Android.bp
@@ -0,0 +1,23 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.secure_element@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ISecureElement.hal",
+ "ISecureElementHalCallback.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ types: [
+ "LogicalChannelResponse",
+ "SecureElementStatus",
+ ],
+ gen_java: true,
+}
+
diff --git a/secure_element/1.0/ISecureElement.hal b/secure_element/1.0/ISecureElement.hal
new file mode 100644
index 0000000..d1e36f7
--- /dev/null
+++ b/secure_element/1.0/ISecureElement.hal
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.secure_element@1.0;
+
+import ISecureElementHalCallback;
+
+/** According to ISO/IEC 7816 */
+interface ISecureElement {
+ /**
+ * Initializes the Secure Element. This may include updating the applet
+ * and/or vendor-specific initialization.
+ *
+ * HAL service must send onStateChange() with connected equal to true
+ * after all the initialization has been successfully completed.
+ * Clients must wait for a onStateChange(true) before opening channels.
+ *
+ * @param clientCallback callback used to sent status of the SE back to the
+ * client
+ */
+ init(ISecureElementHalCallback clientCallback);
+
+ /**
+ * Returns Answer to Reset as per ISO/IEC 7816
+ *
+ * @return response containing the response. Empty vector if Secure Element
+ * doesn't support ATR.
+ */
+ getAtr() generates (vec<uint8_t> response);
+
+ /**
+ * Returns the current state of the card.
+ *
+ * This is particularly useful for removable
+ * Secure Elements like UICC, Secure Elements on SD cards etc.
+ *
+ * @return present true if present, false otherwise
+ */
+ isCardPresent() generates (bool present);
+
+ /**
+ * Transmits an APDU command (as per ISO/IEC 7816) to the SE.
+ *
+ * @param data APDU command to be sent
+ * @return response to the command. In case of error in communicating with
+ * the secure element, an empty vector is returned.
+ */
+ transmit(vec<uint8_t> data) generates (vec<uint8_t> response);
+
+ /**
+ * Opens a logical channel with the Secure Element, selecting the applet
+ * represented by the Application ID (AID).
+ *
+ * @param aid AID to uniquely identify the applet on the Secure Element
+ * @param p2 P2 paramter of SELECT APDU as per ISO 7816-4
+ * @return status SecureElementStatus::SUCCESS on success,
+ * SecureElementStatus::CHANNEL_NOT_AVAILABLE if secure
+ * element has reached the maximum limit on the number of
+ * channels it can support,
+ * SecureElementStatus::NO_SUCH_ELEMENT_ERROR if AID provided
+ * doesn't match any applet on the secure element and
+ * SecureElementStatus::UNSUPPORTED_OPERATION if operation
+ * provided by the P2 parameter is not permitted by the
+ * applet.
+ * SecureElementStatus::IOERROR if there was an error
+ * communicating with the Secure Element.
+ * @return response On success, response to SELECT command is returned
+ * empty vector on failure.
+ */
+ openLogicalChannel(vec<uint8_t> aid, uint8_t p2)
+ generates (LogicalChannelResponse response, SecureElementStatus status);
+
+
+ /**
+ * Opens a basic channel with the Secure Element, selecting the applet
+ * represented by the Application ID (AID).
+ *
+ * @param aid AID to uniquely identify the applet on the Secure Element
+ * @param p2 P2 paramter of SELECT APDU as per ISO 7816-4
+ * @return status SecureElementStatus::SUCCESS on success,
+ * SecureElementStatus::CHANNEL_NOT_AVAILABLE if secure
+ * element has reached the maximum limit on the number of
+ * channels it can support,
+ * SecureElementStatus::NO_SUCH_ELEMENT_ERROR if AID provided
+ * doesn't match any applet on the secure element and
+ * SecureElementStatus::UNSUPPORTED_OPERATION if operation
+ * provided by the P2 parameter is not permitted by the
+ * applet.
+ * SecureElementStatus::IOERROR if there was an error
+ * communicating with the Secure Element.
+ * @return selectResponse On success, response to SELECT command is returned
+ * empty vector on failure.
+ */
+ openBasicChannel(vec<uint8_t> aid, uint8_t p2)
+ generates (vec<uint8_t> selectResponse, SecureElementStatus status);
+
+ /**
+ * Closes the channel indicated by the channelNumber.
+ *
+ * Closing a basic channel, i.e with channelNumber 0 must return
+ * SecureElementStatus::FAILED.
+ *
+ * @param channelNumber to be closed
+ * @return status SecureElementStatus::SUCCESS on success and
+ * SecureElementStatus::FAILED on error.
+ */
+ closeChannel(uint8_t channelNumber) generates (SecureElementStatus status);
+};
diff --git a/secure_element/1.0/ISecureElementHalCallback.hal b/secure_element/1.0/ISecureElementHalCallback.hal
new file mode 100644
index 0000000..d11600f
--- /dev/null
+++ b/secure_element/1.0/ISecureElementHalCallback.hal
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.secure_element@1.0;
+
+interface ISecureElementHalCallback {
+ onStateChange(bool connected);
+};
diff --git a/secure_element/1.0/types.hal b/secure_element/1.0/types.hal
new file mode 100644
index 0000000..8150be4
--- /dev/null
+++ b/secure_element/1.0/types.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.secure_element@1.0;
+
+enum SecureElementStatus : uint8_t {
+ SUCCESS = 0,
+ FAILED = 1,
+ CHANNEL_NOT_AVAILABLE = 2,
+ NO_SUCH_ELEMENT_ERROR = 3,
+ UNSUPPORTED_OPERATION = 4,
+ IOERROR = 5
+};
+
+struct LogicalChannelResponse {
+ /** Channel number to uniquely identify the channel */
+ uint8_t channelNumber;
+ /** Response to SELECT command as per ISO/IEC 7816 */
+ vec<uint8_t> selectResponse;
+};
diff --git a/wifi/1.2/IWifiChip.hal b/wifi/1.2/IWifiChip.hal
index d336a33..480c5a2 100644
--- a/wifi/1.2/IWifiChip.hal
+++ b/wifi/1.2/IWifiChip.hal
@@ -27,6 +27,50 @@
*/
interface IWifiChip extends @1.1::IWifiChip {
/**
+ * Capabilities exposed by this chip.
+ */
+ enum ChipCapabilityMask : @1.1::IWifiChip.ChipCapabilityMask {
+ /**
+ * Set/Reset Tx Power limits.
+ */
+ USE_BODY_HEAD_SAR = 1 << 11
+ };
+
+ /**
+ * List of preset wifi radio TX power levels for different scenarios.
+ * The actual power values (typically varies based on the channel,
+ * 802.11 connection type, number of MIMO streams, etc) for each scenario
+ * is defined by the OEM as a BDF file since it varies for each wifi chip
+ * vendor and device.
+ */
+ enum TxPowerScenario : @1.1::IWifiChip.TxPowerScenario {
+ ON_HEAD_CELL_OFF = 1,
+ ON_HEAD_CELL_ON = 2,
+ ON_BODY_CELL_OFF = 3,
+ ON_BODY_CELL_ON = 4
+ };
+
+ /**
+ * API to select one of the preset TX power scenarios.
+ *
+ * The framework must invoke this method with the appropriate scenario to let
+ * the wifi chip change it's transmitting power levels.
+ * OEM's should define various power profiles for each of the scenarios
+ * above (defined in |TxPowerScenario|) in a vendor extension.
+ *
+ * @param scenario One of the preselected scenarios defined in
+ * |TxPowerScenario|.
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+ * |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+ * |WifiStatusCode.NOT_AVAILABLE|,
+ * |WifiStatusCode.UNKNOWN|
+ */
+ selectTxPowerScenario_1_2(TxPowerScenario scenario) generates (WifiStatus status);
+
+ /**
* Requests notifications of significant events on this chip. Multiple calls
* to this must register multiple callbacks each of which must receive all
* events.
diff --git a/wifi/1.2/default/hidl_struct_util.cpp b/wifi/1.2/default/hidl_struct_util.cpp
index b1c609e..2e3e0ab 100644
--- a/wifi/1.2/default/hidl_struct_util.cpp
+++ b/wifi/1.2/default/hidl_struct_util.cpp
@@ -66,12 +66,14 @@
return {};
}
-V1_1::IWifiChip::ChipCapabilityMask convertLegacyFeatureToHidlChipCapability(
+IWifiChip::ChipCapabilityMask convertLegacyFeatureToHidlChipCapability(
uint32_t feature) {
- using HidlChipCaps = V1_1::IWifiChip::ChipCapabilityMask;
+ using HidlChipCaps = IWifiChip::ChipCapabilityMask;
switch (feature) {
case WIFI_FEATURE_SET_TX_POWER_LIMIT:
return HidlChipCaps::SET_TX_POWER_LIMIT;
+ case WIFI_FEATURE_USE_BODY_HEAD_SAR:
+ return HidlChipCaps::USE_BODY_HEAD_SAR;
case WIFI_FEATURE_D2D_RTT:
return HidlChipCaps::D2D_RTT;
case WIFI_FEATURE_D2AP_RTT:
@@ -135,6 +137,7 @@
}
}
for (const auto feature : {WIFI_FEATURE_SET_TX_POWER_LIMIT,
+ WIFI_FEATURE_USE_BODY_HEAD_SAR,
WIFI_FEATURE_D2D_RTT, WIFI_FEATURE_D2AP_RTT}) {
if (feature & legacy_feature_set) {
*hidl_caps |= convertLegacyFeatureToHidlChipCapability(feature);
@@ -260,12 +263,32 @@
legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy(
V1_1::IWifiChip::TxPowerScenario hidl_scenario) {
switch (hidl_scenario) {
- case V1_1::IWifiChip::TxPowerScenario::VOICE_CALL:
+ // This is the only supported scenario for V1_1
+ case V1_1::IWifiChip::TxPowerScenario::VOICE_CALL:
return legacy_hal::WIFI_POWER_SCENARIO_VOICE_CALL;
};
CHECK(false);
}
+legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy_1_2(
+ IWifiChip::TxPowerScenario hidl_scenario) {
+ switch (hidl_scenario) {
+ // This is the only supported scenario for V1_1
+ case IWifiChip::TxPowerScenario::VOICE_CALL:
+ return legacy_hal::WIFI_POWER_SCENARIO_VOICE_CALL;
+ // Those are the supported scenarios for V1_2
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+ };
+ CHECK(false);
+}
+
bool convertLegacyWifiMacInfoToHidl(
const legacy_hal::WifiMacInfo& legacy_mac_info,
IWifiChipEventCallback::RadioModeInfo* hidl_radio_mode_info) {
diff --git a/wifi/1.2/default/hidl_struct_util.h b/wifi/1.2/default/hidl_struct_util.h
index ce4bb81..3c789c0 100644
--- a/wifi/1.2/default/hidl_struct_util.h
+++ b/wifi/1.2/default/hidl_struct_util.h
@@ -21,7 +21,7 @@
#include <android/hardware/wifi/1.0/IWifiChip.h>
#include <android/hardware/wifi/1.0/types.h>
-#include <android/hardware/wifi/1.1/IWifiChip.h>
+#include <android/hardware/wifi/1.2/IWifiChip.h>
#include <android/hardware/wifi/1.2/IWifiChipEventCallback.h>
#include <android/hardware/wifi/1.2/types.h>
@@ -56,6 +56,8 @@
WifiDebugHostWakeReasonStats* hidl_stats);
legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy(
V1_1::IWifiChip::TxPowerScenario hidl_scenario);
+legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy_1_2(
+ IWifiChip::TxPowerScenario hidl_scenario);
bool convertLegacyWifiMacInfosToHidl(
const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos);
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 38301e9..05ea638 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -507,7 +507,7 @@
}
Return<void> WifiChip::selectTxPowerScenario(
- TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
+ V1_1::IWifiChip::TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
&WifiChip::selectTxPowerScenarioInternal,
hidl_status_cb, scenario);
@@ -528,6 +528,12 @@
hidl_status_cb, event_callback);
}
+Return<void> WifiChip::selectTxPowerScenario_1_2(
+ TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::selectTxPowerScenarioInternal_1_2, hidl_status_cb, scenario);
+}
+
Return<void> WifiChip::debug(const hidl_handle& handle,
const hidl_vec<hidl_string>&) {
if (handle != nullptr && handle->numFds >= 1) {
@@ -990,7 +996,8 @@
return createWifiStatusFromLegacyError(legacy_status);
}
-WifiStatus WifiChip::selectTxPowerScenarioInternal(TxPowerScenario scenario) {
+WifiStatus WifiChip::selectTxPowerScenarioInternal(
+ V1_1::IWifiChip::TxPowerScenario scenario) {
auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
getWlan0IfaceName(),
hidl_struct_util::convertHidlTxPowerScenarioToLegacy(scenario));
@@ -1011,6 +1018,13 @@
return createWifiStatus(WifiStatusCode::SUCCESS);
}
+WifiStatus WifiChip::selectTxPowerScenarioInternal_1_2(TxPowerScenario scenario) {
+ auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
+ getWlan0IfaceName(),
+ hidl_struct_util::convertHidlTxPowerScenarioToLegacy_1_2(scenario));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
WifiStatus WifiChip::handleChipConfiguration(
/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
ChipModeId mode_id) {
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index 24a5486..ada9458 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -132,16 +132,18 @@
Return<void> enableDebugErrorAlerts(
bool enable, enableDebugErrorAlerts_cb hidl_status_cb) override;
Return<void> selectTxPowerScenario(
- TxPowerScenario scenario,
+ V1_1::IWifiChip::TxPowerScenario scenario,
selectTxPowerScenario_cb hidl_status_cb) override;
Return<void> resetTxPowerScenario(
resetTxPowerScenario_cb hidl_status_cb) override;
- Return<void> debug(const hidl_handle& handle,
- const hidl_vec<hidl_string>& options) override;
Return<void> registerEventCallback_1_2(
const sp<IWifiChipEventCallback>& event_callback,
registerEventCallback_1_2_cb hidl_status_cb) override;
-
+ Return<void> selectTxPowerScenario_1_2(
+ TxPowerScenario scenario,
+ selectTxPowerScenario_cb hidl_status_cb) override;
+ Return<void> debug(const hidl_handle& handle,
+ const hidl_vec<hidl_string>& options) override;
private:
void invalidateAndRemoveAllIfaces();
@@ -193,11 +195,11 @@
std::pair<WifiStatus, WifiDebugHostWakeReasonStats>
getDebugHostWakeReasonStatsInternal();
WifiStatus enableDebugErrorAlertsInternal(bool enable);
- WifiStatus selectTxPowerScenarioInternal(TxPowerScenario scenario);
+ WifiStatus selectTxPowerScenarioInternal(V1_1::IWifiChip::TxPowerScenario scenario);
WifiStatus resetTxPowerScenarioInternal();
WifiStatus registerEventCallbackInternal_1_2(
const sp<IWifiChipEventCallback>& event_callback);
-
+ WifiStatus selectTxPowerScenarioInternal_1_2(TxPowerScenario scenario);
WifiStatus handleChipConfiguration(
std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
WifiStatus registerDebugRingBufferCallback();
diff --git a/wifi/supplicant/1.1/Android.bp b/wifi/supplicant/1.1/Android.bp
index c8c8a32..fafd6ad 100644
--- a/wifi/supplicant/1.1/Android.bp
+++ b/wifi/supplicant/1.1/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"ISupplicant.hal",
+ "ISupplicantStaNetwork.hal",
],
interfaces: [
"android.hardware.wifi.supplicant@1.0",
diff --git a/wifi/supplicant/1.1/ISupplicantStaNetwork.hal b/wifi/supplicant/1.1/ISupplicantStaNetwork.hal
new file mode 100644
index 0000000..186fe75
--- /dev/null
+++ b/wifi/supplicant/1.1/ISupplicantStaNetwork.hal
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant@1.1;
+
+import @1.0::ISupplicantNetwork;
+import @1.0::ISupplicantStaNetworkCallback;
+import @1.0::SupplicantStatus;
+import @1.0::ISupplicantStaNetwork;
+
+/**
+ * Interface exposed by the supplicant for each station mode network
+ * configuration it controls.
+ */
+interface ISupplicantStaNetwork extends @1.0::ISupplicantStaNetwork {
+ /**
+ * EAP IMSI Identity to be used for authentication to EAP SIM networks.
+ * The identity must be derived from the IMSI retrieved from the SIM card.
+ *
+ * See RFC4186 & RFC4187 & RFC5448 for EAP SIM protocols.
+ *
+ * Identity string is built from IMSI. Format is:
+ * eapPrefix | IMSI | '@' | realm
+ * where:
+ * - "|" denotes concatenation
+ * - realm is the 3GPP network domain name derived from the given
+ * MCC/MNC according to the 3GGP spec(TS23.003)
+ *
+ * eapPrefix value:
+ * '0' - EAP-AKA Identity
+ * '1' - EAP-SIM Identity
+ * '6' - EAP-AKA-PRIME Identity
+ */
+ typedef vec<uint8_t> EapSimIdentity;
+
+ /**
+ * Encrypted EAP IMSI Identity to be used for authentication to EAP SIM
+ * networks which supports encrypted IMSI.
+ * The identity must be derived from the IMSI retrieved from the SIM card.
+ * This identity is then encrypted using the public key of the carrier.
+ *
+ * See RFC4186 & RFC4187 & RFC5448 for EAP SIM protocols.
+ * See section 7.1 of RFC 2437 for RSA-OAEP encryption scheme.
+ *
+ * Identity string is built from encrypted IMSI. Format is:
+ * '\0' | Base64{RSA-OAEP-SHA-256(eapPrefix | IMSI)}
+ * | '@' | realm | {',' Key Identifier}
+ * where:
+ * - "|" denotes concatenation
+ * - "{}" denotes an optional value
+ * - realm is the 3GPP network domain name derived from the given
+ * MCC/MNC according to the 3GGP spec(TS23.003)
+ * - Key Identifier is a null-terminated string of the form "<Key>=<Value>"
+ */
+ typedef vec<uint8_t> EapSimEncryptedIdentity;
+
+ /**
+ * Set EAP encrypted IMSI Identity for this network.
+ *
+ * @param identity Identity string built from the encrypted IMSI.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_ARGS_INVALID|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+ */
+ setEapEncryptedImsiIdentity(EapSimEncryptedIdentity identity)
+ generates (SupplicantStatus status);
+
+ /**
+ * Used to send a response to the
+ * |ISupplicantNetworkCallback.onNetworkEapIdentityRequest| request.
+ *
+ * @param identity Identity string containing the IMSI.
+ * @param encryptedIdentity Identity string containing the encrypted IMSI.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+ */
+ sendNetworkEapIdentityResponse_1_1(
+ EapSimIdentity identity,
+ EapSimEncryptedIdentity encryptedIdentity)
+ generates (SupplicantStatus status);
+};