实现DngCreator

TempBranch
Matthew 9 months ago
parent 6d421a6cc1
commit 0779d47b36

@ -72,7 +72,33 @@ endif(OpenCV_FOUND)
set(ncnn_DIR ${NCNN_ROOT}/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)
# include(mars/src/CMakeUtils.txt)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libcutils/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libutils/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/img_utils/include)
SET( IMG_UTILS_SRCS
"img_utils/src/EndianUtils.cpp"
#"img_utils/src/FileInput.cpp"
#"img_utils/src/FileOutput.cpp"
#"img_utils/src/SortedEntryVector.cpp"
"img_utils/src/Input.cpp"
"img_utils/src/Output.cpp"
"img_utils/src/Orderable.cpp"
"img_utils/src/TiffIfd.cpp"
"img_utils/src/TiffWritable.cpp"
"img_utils/src/TiffWriter.cpp"
"img_utils/src/TiffEntry.cpp"
"img_utils/src/TiffEntryImpl.cpp"
"img_utils/src/ByteArrayOutput.cpp"
"img_utils/src/DngUtils.cpp"
"img_utils/src/StripSource.cpp"
libutils/SharedBuffer.cpp
libutils/StrongPointer.cpp
DngCreator.cpp
)
message(WARNING "include_directories ${HDRPLUS_ROOT}/${ANDROID_ABI}/include")
@ -327,6 +353,8 @@ add_library( # Sets the name of the library.
${HDRPLUS_SOURCES}
${CAMERA2_SOURCES}
${IMG_UTILS_SRCS}
${TERM_CORE_ROOT}/Factory.cpp
${TERM_CORE_ROOT}/FilePoster.cpp
${TERM_CORE_ROOT}/LogThread.cpp

File diff suppressed because it is too large Load Diff

@ -0,0 +1,332 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DngCreator_JNI"
#include <inttypes.h>
#include <string.h>
#include <algorithm>
#include <array>
#include <memory>
#include <vector>
#include <cmath>
#include <algorithm>
#include <camera/NdkCameraMetadata.h>
#include <img_utils/DngUtils.h>
#include <img_utils/TagDefinitions.h>
#include <img_utils/TiffIfd.h>
#include <img_utils/TiffWriter.h>
#include <img_utils/Output.h>
#include <img_utils/Input.h>
#include <img_utils/StripSource.h>
#include <sys/system_properties.h>
// #include "core_jni_helpers.h"
// #include "android_runtime/AndroidRuntime.h"
// #include "android_runtime/android_hardware_camera2_CameraMetadata.h"
#include <jni.h>
// #include <nativehelper/JNIHelp.h>
using namespace android;
using namespace img_utils;
// using android::base::GetProperty;
/**
* Max width or height dimension for thumbnails.
*/
// max pixel dimension for TIFF/EP
#define MAX_THUMBNAIL_DIMENSION 256
// bytes per sample
#define DEFAULT_PIXEL_STRIDE 2
// byts per pixel
#define BYTES_PER_RGB_PIX 3
#define GPS_LAT_REF_NORTH "N"
#define GPS_LAT_REF_SOUTH "S"
#define GPS_LONG_REF_EAST "E"
#define GPS_LONG_REF_WEST "W"
#define GPS_DATE_FORMAT_STR "yyyy:MM:dd"
#define TIFF_DATETIME_FORMAT "yyyy:MM:dd kk:mm:ss"
class ByteVectorOutput : public Output {
public:
ByteVectorOutput(std::vector<uint8_t>& buf);
virtual ~ByteVectorOutput();
virtual status_t open();
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
virtual status_t close();
protected:
std::vector<uint8_t>& m_buf;
};
class ByteVectorInput : public Input {
public:
ByteVectorInput(const std::vector<uint8_t>& buf);
virtual ~ByteVectorInput();
/**
* Open this Input.
*
* Returns OK on success, or a negative error code.
*/
status_t open();
/**
* Read bytes into the given buffer. At most, the number of bytes given in the
* count argument will be read. Bytes will be written into the given buffer starting
* at the index given in the offset argument.
*
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
ssize_t read(uint8_t* buf, size_t offset, size_t count);
/**
* Skips bytes in the input.
*
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
ssize_t skip(size_t count);
/**
* Close the Input. It is not valid to call open on a previously closed Input.
*
* Returns OK on success, or a negative error code.
*/
status_t close();
protected:
const std::vector<uint8_t>& m_buf;
size_t m_offset;
};
class ByteBufferInput : public Input {
public:
ByteBufferInput(const uint8_t* buf, size_t len);
virtual ~ByteBufferInput();
/**
* Open this Input.
*
* Returns OK on success, or a negative error code.
*/
status_t open();
/**
* Read bytes into the given buffer. At most, the number of bytes given in the
* count argument will be read. Bytes will be written into the given buffer starting
* at the index given in the offset argument.
*
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
ssize_t read(uint8_t* buf, size_t offset, size_t count);
/**
* Skips bytes in the input.
*
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
ssize_t skip(size_t count);
/**
* Close the Input. It is not valid to call open on a previously closed Input.
*
* Returns OK on success, or a negative error code.
*/
status_t close();
protected:
const uint8_t* m_buf;
size_t m_len;
size_t m_offset;
};
struct SIZE
{
int width;
int height;
};
#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
if ((expr) != OK) { \
return false; \
}
#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
if ((expr) != OK) { \
return nullptr; \
}
#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
if ((expr) != OK) { \
return -1; \
}
#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
if ((entry).count == 0) { \
return nullptr; \
}
#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
if (expr) { \
return nullptr; \
}
#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
enum {
BITS_PER_SAMPLE = 16,
BYTES_PER_SAMPLE = 2,
BYTES_PER_RGB_PIXEL = 3,
BITS_PER_RGB_SAMPLE = 8,
BYTES_PER_RGB_SAMPLE = 1,
SAMPLES_PER_RGB_PIXEL = 3,
SAMPLES_PER_RAW_PIXEL = 1,
TIFF_IFD_0 = 0,
TIFF_IFD_SUB1 = 1,
TIFF_IFD_GPSINFO = 2,
};
/**
* POD container class for GPS tag data.
*/
class GpsData {
public:
enum {
GPS_VALUE_LENGTH = 6,
GPS_REF_LENGTH = 2,
GPS_DATE_LENGTH = 11,
};
uint32_t mLatitude[GPS_VALUE_LENGTH];
uint32_t mLongitude[GPS_VALUE_LENGTH];
uint32_t mTimestamp[GPS_VALUE_LENGTH];
uint8_t mLatitudeRef[GPS_REF_LENGTH];
uint8_t mLongitudeRef[GPS_REF_LENGTH];
uint8_t mDate[GPS_DATE_LENGTH];
};
// ----------------------------------------------------------------------------
/**
* Container class for the persistent native context.
*/
class NativeContext : public LightRefBase<NativeContext> {
public:
enum {
DATETIME_COUNT = 20,
};
NativeContext(ACameraMetadata* characteristics, ACameraMetadata* result);
virtual ~NativeContext();
TiffWriter* getWriter();
ACameraMetadata* getCharacteristics() const;
ACameraMetadata* getResult() const;
uint32_t getThumbnailWidth() const;
uint32_t getThumbnailHeight() const;
const uint8_t* getThumbnail() const;
bool hasThumbnail() const;
bool setThumbnail(const std::vector<uint8_t>& buffer, uint32_t width, uint32_t height);
void setOrientation(uint16_t orientation);
uint16_t getOrientation() const;
void setDescription(const std::string& desc);
std::string getDescription() const;
bool hasDescription() const;
void setGpsData(const GpsData& data);
GpsData getGpsData() const;
bool hasGpsData() const;
void setCaptureTime(const std::string& formattedCaptureTime);
std::string getCaptureTime() const;
bool hasCaptureTime() const;
protected:
std::vector<uint8_t> mCurrentThumbnail;
TiffWriter mWriter;
ACameraMetadata* mCharacteristics;
ACameraMetadata* mResult;
uint32_t mThumbnailWidth;
uint32_t mThumbnailHeight;
uint16_t mOrientation;
bool mThumbnailSet;
bool mGpsSet;
bool mDescriptionSet;
bool mCaptureTimeSet;
std::string mDescription;
GpsData mGpsData;
std::string mFormattedCaptureTime;
};
class DngCreator : public NativeContext
{
public:
DngCreator(ACameraMetadata* characteristics, ACameraMetadata* result);
#if 0
void setLocation(Location location);
#endif
void writeInputStream(std::vector<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& pixels, long offset);
void writeByteBuffer(std::vector<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& pixels, long offset);
#if 0
void writeImage(OutputStream& dngOutput, AImage& pixels);
#endif
void close();
// private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
// private static final DateFormat sDateTimeStampFormat = new SimpleDateFormat(TIFF_DATETIME_FORMAT);
#if 0
static {
sDateTimeStampFormat.setTimeZone(TimeZone.getDefault());
sExifGPSDateStamp.setTimeZone(TimeZone.getTimeZone("UTC"));
}
#endif
/**
* Offset, rowStride, and pixelStride are given in bytes. Height and width are given in pixels.
*/
void writeByteBuffer(int width, int height, const std::vector<uint8_t>& pixels, std::vector<uint8_t>& dngOutput, int pixelStride, int rowStride, long offset);
/**
* Generate a direct RGB {@link ByteBuffer} from a {@link Bitmap}.
*/
/**
* Convert coordinate to EXIF GPS tag format.
*/
void toExifLatLong(double value, int data[6]);
void init(ACameraMetadata* characteristics, ACameraMetadata* result, const std::string& captureTime);
sp<TiffWriter> setup(uint32_t imageWidth, uint32_t imageHeight);
void destroy();
void setGpsTags(const std::vector<int>& latTag, const std::string& latRef, const std::vector<int>& longTag, const std::string& longRef, const std::string& dateTag, const std::vector<int>& timeTag);
void writeImage(std::vector<uint8_t>& out, uint32_t width, uint32_t height, const std::vector<uint8_t>& rawBuffer, int rowStride, int pixStride, uint64_t offset, bool isDirect);
void writeInputStream(std::vector<uint8_t>& out, const std::vector<uint8_t>& rawStream, uint32_t width, uint32_t height, long offset);
void writeInputBuffer(std::vector<uint8_t>& out, const uint8_t* rawBuffer, size_t bufferLen, uint32_t width, uint32_t height, long offset);
};

@ -178,6 +178,11 @@ bool CPhoneDevice::CPhoneCamera::on_image(cv::Mat& rgb)
return false;
}
bool CPhoneDevice::CPhoneCamera::onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames)
{
return true;
}
void CPhoneDevice::CPhoneCamera::on_error(const std::string& msg)
{
if (m_dev != NULL)
@ -199,6 +204,11 @@ CPhoneDevice::CJpegCamera::CJpegCamera(CPhoneDevice* dev, int32_t width, int32_t
{
}
bool CPhoneDevice::CJpegCamera::onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames)
{
return true;
}
void CPhoneDevice::CJpegCamera::onImageAvailable(AImageReader* reader)
{
ALOGD("onImageAvailable %p", reader);

@ -161,6 +161,7 @@ public:
virtual bool on_image(cv::Mat& rgb);
virtual void on_error(const std::string& msg);
virtual void onDisconnected(ACameraDevice* device);
virtual bool onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames);
protected:
CPhoneDevice* m_dev;
@ -173,6 +174,7 @@ public:
virtual void onImageAvailable(AImageReader* reader);
virtual int32_t getOutputFormat() const;
virtual bool onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames);
protected:
std::string m_path;

@ -26,6 +26,7 @@
#include "Camera2Helper.h"
#include <AndroidHelper.h>
#include <LogThread.h>
#include "DngCreator.h"
static void onAvailabilityCallback(void* context, const char* cameraId)
{
@ -279,6 +280,7 @@ int NdkCamera::open(const std::string& cameraId) {
status = ACameraManager_getCameraCharacteristics(camera_manager, cameraId.c_str(), &camera_metadata);
AASSERT(status == ACAMERA_OK, "ACameraManager_getCameraCharacteristics return error, %d", status);
mCharacteristics = std::shared_ptr<ACameraMetadata>(camera_metadata, ACameraMetadata_free);
{
ACameraMetadata_const_entry e = { 0 };
status = ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &e);
@ -327,7 +329,7 @@ int NdkCamera::open(const std::string& cameraId) {
if (!foundIt || foundRes.width() == 0 || foundRes.height() == 0)
{
ACameraMetadata_free(camera_metadata);
// ACameraMetadata_free(camera_metadata);
XYLOG(XYLOG_SEVERITY_ERROR, "Camera RES(%d, %d) Not Found on ID: %s", mWidth, mHeight, cameraId.c_str());
return 1;
}
@ -543,7 +545,7 @@ int NdkCamera::open(const std::string& cameraId) {
}
}
ACameraMetadata_free(camera_metadata);
// ACameraMetadata_free(camera_metadata);
}
// open camera
@ -999,99 +1001,8 @@ void NdkCamera::onImageAvailable(AImageReader* reader)
mFinalResult.duration = GetMicroTimeStamp() - m_startTime;
int32_t format;
AImage_getFormat(image, &format);
mCaptureFrames.push_back(std::shared_ptr<AImage>(image, AImage_delete));
if (format == AIMAGE_FORMAT_YUV_420_888)
{
int32_t width = 0;
int32_t height = 0;
AImage_getWidth(image, &width);
AImage_getHeight(image, &height);
int32_t y_pixelStride = 0;
int32_t u_pixelStride = 0;
int32_t v_pixelStride = 0;
AImage_getPlanePixelStride(image, 0, &y_pixelStride);
AImage_getPlanePixelStride(image, 1, &u_pixelStride);
AImage_getPlanePixelStride(image, 2, &v_pixelStride);
int32_t y_rowStride = 0;
int32_t u_rowStride = 0;
int32_t v_rowStride = 0;
AImage_getPlaneRowStride(image, 0, &y_rowStride);
AImage_getPlaneRowStride(image, 1, &u_rowStride);
AImage_getPlaneRowStride(image, 2, &v_rowStride);
uint8_t* y_data = 0;
uint8_t* u_data = 0;
uint8_t* v_data = 0;
int y_len = 0;
int u_len = 0;
int v_len = 0;
AImage_getPlaneData(image, 0, &y_data, &y_len);
AImage_getPlaneData(image, 1, &u_data, &u_len);
AImage_getPlaneData(image, 2, &v_data, &v_len);
if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width)
{
// already nv21 :)
on_image((unsigned char*)y_data, (int)width, (int)height);
}
else
{
// construct nv21
unsigned char* nv21 = new unsigned char[width * height + width * height / 2];
{
// Y
unsigned char* yptr = nv21;
for (int y = 0; y < height; y++)
{
const unsigned char* y_data_ptr = y_data + y_rowStride * y;
for (int x = 0; x < width; x++)
{
yptr[0] = y_data_ptr[0];
yptr++;
y_data_ptr += y_pixelStride;
}
}
// UV
unsigned char* uvptr = nv21 + width * height;
for (int y = 0; y < height / 2; y++)
{
const unsigned char* v_data_ptr = v_data + v_rowStride * y;
const unsigned char* u_data_ptr = u_data + u_rowStride * y;
for (int x = 0; x < width / 2; x++)
{
uvptr[0] = v_data_ptr[0];
uvptr[1] = u_data_ptr[0];
uvptr += 2;
v_data_ptr += v_pixelStride;
u_data_ptr += u_pixelStride;
}
}
}
on_image((unsigned char*)nv21, (int)width, (int)height);
delete[] nv21;
}
}
else if (format == AIMAGE_FORMAT_JPEG)
{
uint32_t frameNumber = mFrameNumber.fetch_add(1);
std::string path = "/sdcard/com.xypower.mpapp/tmp/" + std::to_string(frameNumber) + ".jpg";
writeJpegFile(image, path.c_str());
}
else if (format == AIMAGE_FORMAT_RAW16)
{
uint32_t frameNumber = mFrameNumber.fetch_add(1);
std::string path = "/sdcard/com.xypower.mpapp/tmp/" + std::to_string(frameNumber) + ".dng";
writeRawFile(image, path.c_str());
}
AImage_delete(image);
}
void NdkCamera::on_error(const std::string& msg)
@ -1108,6 +1019,11 @@ bool NdkCamera::on_image(cv::Mat& rgb)
return false;
}
bool NdkCamera::onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames)
{
return false;
}
void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_height)
{
// ALOGW("nv21 size: %d x %d", nv21_width, nv21_height);
@ -1228,6 +1144,43 @@ void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_hei
void NdkCamera::onSessionReady(ACameraCaptureSession *session)
{
if (m_photoTaken)
{
AASSERT(mCaptureFrames.size() == mCaptureResults.size(), "Frame size %u doesn't equal to result size %u",
(uint32_t)mCaptureFrames.size(), (uint32_t)mCaptureResults.size());
#ifndef NDEBUG
for (int idx = 0; idx < mCaptureFrames.size(); idx++)
{
std::shared_ptr<AImage> spImage = mCaptureFrames[idx];
int32_t format;
AImage_getFormat(spImage.get(), &format);
if (format == AIMAGE_FORMAT_YUV_420_888)
{
}
else
{
ALOGW("Capture Available TID=%lld", (long long)getThreadIdOfULL());
uint32_t frameNumber = mFrameNumber.fetch_add(1);
std::string path = "/sdcard/com.xypower.mpapp/tmp/" + std::to_string(frameNumber);
if (format == AIMAGE_FORMAT_JPEG)
{
path += ".jpg";
writeJpegFile(spImage.get(), path.c_str());
}
else
{
path += ".dng";
writeRawFile(spImage.get(), mCharacteristics.get(), mCaptureResults[idx].get(), path.c_str());
}
}
}
#endif // NDEBUG
}
}
void NdkCamera::onCaptureProgressed(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result)
@ -1433,16 +1386,11 @@ void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureReque
}
else
{
for (int idx = 1; idx < mCaptureRequests.size(); idx++)
{
if (mCaptureRequests[idx]->request == request)
{
ACameraMetadata* pCopy = ACameraMetadata_copy(result);
CaptureResult captureResult = { pCopy, NULL, mCaptureRequests[idx]->sessionSequenceId };
mCaptureResults.push_back(captureResult);
break;
}
}
uint64_t tid = getThreadIdOfULL();
ALOGW("Capture Result sequenceId=%d TID=%lld", pCaptureRequest->sessionSequenceId, (long long)tid);
ACameraMetadata* pCopy = ACameraMetadata_copy(result);
mCaptureResults.push_back(std::shared_ptr<ACameraMetadata>(pCopy, ACameraMetadata_free));
}
}
@ -1632,8 +1580,16 @@ void NdkCamera::writeJpegFile(AImage *image, const char* path)
}
}
void NdkCamera::writeRawFile(AImage *image, const char* path)
void NdkCamera::writeRawFile(AImage *image, ACameraMetadata* characteristics, ACameraMetadata* result, const char* path)
{
// dngCreator.
int32_t width;
int32_t height;
AImage_getWidth(image, &width);
AImage_getHeight(image, &height);
int planeCount;
media_status_t status = AImage_getNumberOfPlanes(image, &planeCount);
// ASSERT(status == AMEDIA_OK && planeCount == 1,
@ -1642,12 +1598,99 @@ void NdkCamera::writeRawFile(AImage *image, const char* path)
int len = 0;
AImage_getPlaneData(image, 0, &data, &len);
DngCreator dngCreator(characteristics, result);
std::vector<uint8_t> dngFile;
// std::vector<uint8_t>& out, const uint8_t* rawBuffer, size_t bufferLen, uint32_t width, uint32_t height, long offset);
dngCreator.writeInputBuffer(dngFile, data, len, width, height, 0);
if (dngFile.empty())
{
return;
}
FILE *file = fopen(path, "wb");
if (file) {
if (data && len)
{
fwrite(data, 1, len, file);
fwrite(&dngFile[0], 1, dngFile.size(), file);
}
fclose(file);
}
}
bool NdkCamera::convertAImageToNv21(AImage* image, uint8_t** nv21, int32_t& width, int32_t& height)
{
media_status_t status;
status = AImage_getWidth(image, &width);
status = AImage_getHeight(image, &height);
int32_t y_pixelStride = 0;
int32_t u_pixelStride = 0;
int32_t v_pixelStride = 0;
AImage_getPlanePixelStride(image, 0, &y_pixelStride);
AImage_getPlanePixelStride(image, 1, &u_pixelStride);
AImage_getPlanePixelStride(image, 2, &v_pixelStride);
int32_t y_rowStride = 0;
int32_t u_rowStride = 0;
int32_t v_rowStride = 0;
AImage_getPlaneRowStride(image, 0, &y_rowStride);
AImage_getPlaneRowStride(image, 1, &u_rowStride);
AImage_getPlaneRowStride(image, 2, &v_rowStride);
uint8_t* y_data = 0;
uint8_t* u_data = 0;
uint8_t* v_data = 0;
int y_len = 0;
int u_len = 0;
int v_len = 0;
AImage_getPlaneData(image, 0, &y_data, &y_len);
AImage_getPlaneData(image, 1, &u_data, &u_len);
AImage_getPlaneData(image, 2, &v_data, &v_len);
if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width)
{
// already nv21 :)
// on_image((unsigned char*)y_data, (int)width, (int)height);
}
else
{
// construct nv21
unsigned char* nv21 = new unsigned char[width * height + width * height / 2];
{
// Y
unsigned char* yptr = nv21;
for (int y = 0; y < height; y++)
{
const unsigned char* y_data_ptr = y_data + y_rowStride * y;
for (int x = 0; x < width; x++)
{
yptr[0] = y_data_ptr[0];
yptr++;
y_data_ptr += y_pixelStride;
}
}
// UV
unsigned char* uvptr = nv21 + width * height;
for (int y = 0; y < height / 2; y++)
{
const unsigned char* v_data_ptr = v_data + v_rowStride * y;
const unsigned char* u_data_ptr = u_data + u_rowStride * y;
for (int x = 0; x < width / 2; x++)
{
uvptr[0] = v_data_ptr[0];
uvptr[1] = u_data_ptr[0];
uvptr += 2;
v_data_ptr += v_pixelStride;
u_data_ptr += u_pixelStride;
}
}
}
// on_image((unsigned char*)nv21, (int)width, (int)height);
delete[] nv21;
}
}

@ -144,7 +144,7 @@ public:
int selfTest(const std::string& cameraId, int32_t& maxResolutionX, int32_t& maxResolutionY);
void writeJpegFile(AImage *image, const char* path);
void writeRawFile(AImage *image, const char* path);
void writeRawFile(AImage *image, ACameraMetadata* characteristics, ACameraMetadata* result, const char* path);
void onAvailabilityCallback(const char* cameraId);
void onUnavailabilityCallback(const char* cameraId);
@ -160,6 +160,7 @@ public:
virtual void on_error(const std::string& msg);
virtual void on_image(const unsigned char* nv21, int nv21_width, int nv21_height);
virtual void onDisconnected(ACameraDevice* device);
virtual bool onBurstCapture(std::shared_ptr<ACameraMetadata> characteristics, const std::vector<std::shared_ptr<ACameraMetadata> >& results, const std::vector<std::shared_ptr<AImage> >& frames);
void onCaptureProgressed(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result);
void onCaptureCompleted(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result);
@ -174,6 +175,8 @@ public:
bool IsCameraAvailable(const std::string& cameraId);
static bool convertAImageToNv21(AImage* image, uint8_t** nv21, int32_t& width, int32_t& height);
protected:
std::mutex m_locker;
std::set<std::string> m_availableCameras;
@ -235,9 +238,11 @@ protected:
ANativeWindow* mImageWindow;
ACameraOutputTarget* mOutputTarget;
std::shared_ptr<ACameraMetadata> mCharacteristics;
std::vector<CaptureRequest*> mCaptureRequests;
std::vector<CaptureResult> mCaptureResults;
std::vector<std::shared_ptr<ACameraMetadata> > mCaptureResults;
std::vector<std::shared_ptr<AImage> > mCaptureFrames;
ACameraCaptureSession* capture_session;

@ -0,0 +1,60 @@
// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_library_shared {
name: "libimg_utils",
srcs: [
"src/EndianUtils.cpp",
"src/FileInput.cpp",
"src/FileOutput.cpp",
"src/SortedEntryVector.cpp",
"src/Input.cpp",
"src/Output.cpp",
"src/Orderable.cpp",
"src/TiffIfd.cpp",
"src/TiffWritable.cpp",
"src/TiffWriter.cpp",
"src/TiffEntry.cpp",
"src/TiffEntryImpl.cpp",
"src/ByteArrayOutput.cpp",
"src/DngUtils.cpp",
"src/StripSource.cpp",
],
shared_libs: [
"liblog",
"libutils",
"libcutils",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-fvisibility=hidden",
],
product_variables: {
debuggable: {
// Enable assert() in eng builds
cflags: [
"-UNDEBUG",
"-DLOG_NDEBUG=1",
],
},
},
export_include_dirs: ["include"],
}

@ -0,0 +1,83 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_BYTE_ARRAY_OUTPUT_H
#define IMG_UTILS_BYTE_ARRAY_OUTPUT_H
#include <img_utils/Output.h>
#include <utils/Errors.h>
// #include <utils/Vector.h>
#include <cutils/compiler.h>
#include <stdint.h>
#include <vector>
namespace android {
namespace img_utils {
/**
* Utility class that accumulates written bytes into a buffer.
*/
class ANDROID_API ByteArrayOutput : public Output {
public:
ByteArrayOutput();
virtual ~ByteArrayOutput();
/**
* Open this ByteArrayOutput.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t open();
/**
* Write bytes from the given buffer. The number of bytes given in the count
* argument will be written. Bytes will be written from the given buffer starting
* at the index given in the offset argument.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
/**
* Close this ByteArrayOutput.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t close();
/**
* Get current size of the array of bytes written.
*/
virtual size_t getSize() const;
/**
* Get pointer to array of bytes written. It is not valid to use this pointer if
* open, write, or close is called after this method.
*/
virtual const uint8_t* getArray() const;
protected:
std::vector<uint8_t> mByteArray;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_BYTE_ARRAY_OUTPUT_H*/

@ -0,0 +1,232 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_DNG_UTILS_H
#define IMG_UTILS_DNG_UTILS_H
#include <img_utils/ByteArrayOutput.h>
#include <img_utils/EndianUtils.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <cutils/compiler.h>
#include <stdint.h>
namespace android {
namespace img_utils {
#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
/**
* Utility class for building values for the OpcodeList tags specified
* in the Adobe DNG 1.4 spec.
*/
class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> {
public:
// Note that the Adobe DNG 1.4 spec for Bayer phase (defined for the
// FixBadPixelsConstant and FixBadPixelsList opcodes) is incorrect. It's
// inconsistent with the DNG SDK (cf. dng_negative::SetBayerMosaic and
// dng_opcode_FixBadPixelsList::IsGreen), and Adobe confirms that the
// spec should be updated to match the SDK.
enum CfaLayout {
CFA_GRBG = 0,
CFA_RGGB,
CFA_BGGR,
CFA_GBRG,
CFA_NONE,
};
OpcodeListBuilder();
virtual ~OpcodeListBuilder();
/**
* Get the total size of this opcode list in bytes.
*/
virtual size_t getSize() const;
/**
* Get the number of opcodes defined in this list.
*/
virtual uint32_t getCount() const;
/**
* Write the opcode list into the given buffer. This buffer
* must be able to hold at least as many elements as returned
* by calling the getSize() method.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t buildOpList(/*out*/ uint8_t* buf) const;
/**
* Add GainMap opcode(s) for the given metadata parameters. The given
* CFA layout must match the layout of the shading map passed into the
* lensShadingMap parameter.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaTop,
uint32_t activeAreaLeft,
uint32_t activeAreaBottom,
uint32_t activeAreaRight,
CfaLayout cfa,
const float* lensShadingMap);
/**
* Add a GainMap opcode with the given fields. The mapGains array
* must have mapPointsV * mapPointsH * mapPlanes elements.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addGainMap(uint32_t top,
uint32_t left,
uint32_t bottom,
uint32_t right,
uint32_t plane,
uint32_t planes,
uint32_t rowPitch,
uint32_t colPitch,
uint32_t mapPointsV,
uint32_t mapPointsH,
double mapSpacingV,
double mapSpacingH,
double mapOriginV,
double mapOriginH,
uint32_t mapPlanes,
const float* mapGains);
/**
* Add WarpRectilinear opcode for the given metadata parameters.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addWarpRectilinearForMetadata(const float* kCoeffs,
uint32_t activeArrayWidth,
uint32_t activeArrayHeight,
float opticalCenterX,
float opticalCenterY);
/**
* Add a WarpRectilinear opcode.
*
* numPlanes - Number of planes included in this opcode.
* opticalCenterX, opticalCenterY - Normalized x,y coordinates of the sensor optical
* center relative to the top,left pixel of the produced images (e.g. [0.5, 0.5]
* gives a sensor optical center in the image center.
* kCoeffs - A list of coefficients for the polynomial equation representing the distortion
* correction. For each plane, 6 coefficients must be included:
* {k_r0, k_r1, k_r2, k_r3, k_t0, k_t1}. See the DNG 1.4 specification for an
* outline of the polynomial used here.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addWarpRectilinear(uint32_t numPlanes,
double opticalCenterX,
double opticalCenterY,
const double* kCoeffs);
/**
* Add FixBadPixelsList opcode for the given metadata parameters.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addBadPixelListForMetadata(const uint32_t* hotPixels,
uint32_t xyPairCount,
uint32_t colorFilterArrangement);
/**
* Add FixBadPixelsList opcode.
*
* bayerPhase - 0=top-left of image is red, 1=top-left of image is green pixel in red row,
* 2=top-left of image is green pixel in blue row, 3=top-left of image is
* blue.
* badPointCount - number of (x,y) pairs of bad pixels are given in badPointRowColPairs.
* badRectCount - number of (top, left, bottom, right) tuples are given in
* badRectTopLeftBottomRightTuples
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addBadPixelList(uint32_t bayerPhase,
uint32_t badPointCount,
uint32_t badRectCount,
const uint32_t* badPointRowColPairs,
const uint32_t* badRectTopLeftBottomRightTuples);
// TODO: Add other Opcode methods
protected:
static const uint32_t FLAG_OPTIONAL = 0x1u;
static const uint32_t FLAG_OPTIONAL_FOR_PREVIEW = 0x2u;
// Opcode IDs
enum {
WARP_RECTILINEAR_ID = 1,
FIX_BAD_PIXELS_LIST = 5,
GAIN_MAP_ID = 9,
};
// LSM mosaic indices
enum {
LSM_R_IND = 0,
LSM_GE_IND = 1,
LSM_GO_IND = 2,
LSM_B_IND = 3,
};
uint32_t mCount;
ByteArrayOutput mOpList;
EndianOutput mEndianOut;
status_t addOpcodePreamble(uint32_t opcodeId);
private:
/**
* Add Bayer GainMap opcode(s) for the given metadata parameters.
* CFA layout must match the layout of the shading map passed into the
* lensShadingMap parameter.
*
* Returns OK on success, or a negative error code.
*/
status_t addBayerGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaWidth,
uint32_t activeAreaHeight,
CfaLayout cfa,
const float* lensShadingMap);
/**
* Add Bayer GainMap opcode(s) for the given metadata parameters.
* CFA layout must match the layout of the shading map passed into the
* lensShadingMap parameter.
*
* Returns OK on success, or a negative error code.
*/
status_t addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaWidth,
uint32_t activeAreaHeight,
const float* lensShadingMap);
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_DNG_UTILS_H*/

@ -0,0 +1,250 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_ENDIAN_UTILS
#define IMG_UTILS_ENDIAN_UTILS
#include <img_utils/Output.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <stdint.h>
#include <endian.h>
#include <assert.h>
namespace android {
namespace img_utils {
/**
* Endianness types supported.
*/
enum ANDROID_API Endianness {
UNDEFINED_ENDIAN, // Default endianness will be used.
BIG,
LITTLE
};
/**
* Convert from the native device endianness to big endian.
*/
template<typename T>
T convertToBigEndian(T in);
/**
* Convert from the native device endianness to little endian.
*/
template<typename T>
T convertToLittleEndian(T in);
/**
* A utility class for writing to an Output with the given endianness.
*/
class ANDROID_API EndianOutput : public Output {
public:
/**
* Wrap the given Output. Calling write methods will result in
* writes to this output.
*/
explicit EndianOutput(Output* out, Endianness end=LITTLE);
virtual ~EndianOutput();
/**
* Call open on the wrapped output.
*/
virtual status_t open();
/**
* Call close on the wrapped output.
*/
virtual status_t close();
/**
* Set the endianness to use when writing.
*/
virtual void setEndianness(Endianness end);
/**
* Get the currently configured endianness.
*/
virtual Endianness getEndianness() const;
/**
* Get the current number of bytes written by this EndianOutput.
*/
virtual uint32_t getCurrentOffset() const;
// TODO: switch write methods to uint32_t instead of size_t,
// the max size of a TIFF files is bounded
/**
* The following methods will write elements from given input buffer to the output.
* Count elements in the buffer will be written with the endianness set for this
* EndianOutput. If the given offset is greater than zero, that many elements will
* be skipped in the buffer before writing.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
virtual status_t write(const int8_t* buf, size_t offset, size_t count);
virtual status_t write(const uint16_t* buf, size_t offset, size_t count);
virtual status_t write(const int16_t* buf, size_t offset, size_t count);
virtual status_t write(const uint32_t* buf, size_t offset, size_t count);
virtual status_t write(const int32_t* buf, size_t offset, size_t count);
virtual status_t write(const uint64_t* buf, size_t offset, size_t count);
virtual status_t write(const int64_t* buf, size_t offset, size_t count);
virtual status_t write(const float* buf, size_t offset, size_t count);
virtual status_t write(const double* buf, size_t offset, size_t count);
protected:
template<typename T>
inline status_t writeHelper(const T* buf, size_t offset, size_t count);
uint32_t mOffset;
Output* mOutput;
Endianness mEndian;
};
template<typename T>
inline status_t EndianOutput::writeHelper(const T* buf, size_t offset, size_t count) {
assert(offset <= count);
status_t res = OK;
size_t size = sizeof(T);
switch(mEndian) {
case BIG: {
for (size_t i = offset; i < count; ++i) {
T tmp = convertToBigEndian<T>(buf[offset + i]);
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
!= OK) {
return res;
}
mOffset += size;
}
break;
}
case LITTLE: {
for (size_t i = offset; i < count; ++i) {
T tmp = convertToLittleEndian<T>(buf[offset + i]);
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
!= OK) {
return res;
}
mOffset += size;
}
break;
}
default: {
return BAD_VALUE;
}
}
return res;
}
template<>
inline uint8_t convertToBigEndian(uint8_t in) {
return in;
}
template<>
inline int8_t convertToBigEndian(int8_t in) {
return in;
}
template<>
inline uint16_t convertToBigEndian(uint16_t in) {
return htobe16(in);
}
template<>
inline int16_t convertToBigEndian(int16_t in) {
return htobe16(in);
}
template<>
inline uint32_t convertToBigEndian(uint32_t in) {
return htobe32(in);
}
template<>
inline int32_t convertToBigEndian(int32_t in) {
return htobe32(in);
}
template<>
inline uint64_t convertToBigEndian(uint64_t in) {
return htobe64(in);
}
template<>
inline int64_t convertToBigEndian(int64_t in) {
return htobe64(in);
}
template<>
inline uint8_t convertToLittleEndian(uint8_t in) {
return in;
}
template<>
inline int8_t convertToLittleEndian(int8_t in) {
return in;
}
template<>
inline uint16_t convertToLittleEndian(uint16_t in) {
return htole16(in);
}
template<>
inline int16_t convertToLittleEndian(int16_t in) {
return htole16(in);
}
template<>
inline uint32_t convertToLittleEndian(uint32_t in) {
return htole32(in);
}
template<>
inline int32_t convertToLittleEndian(int32_t in) {
return htole32(in);
}
template<>
inline uint64_t convertToLittleEndian(uint64_t in) {
return htole64(in);
}
template<>
inline int64_t convertToLittleEndian(int64_t in) {
return htole64(in);
}
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_ENDIAN_UTILS*/

@ -0,0 +1,76 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_FILE_INPUT_H
#define IMG_UTILS_FILE_INPUT_H
#include <img_utils/Input.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <stdio.h>
#include <stdint.h>
namespace android {
namespace img_utils {
/**
* Utility class for reading from a file.
*/
class ANDROID_API FileInput : public Input {
public:
/**
* Create a file input for the given path.
*/
explicit FileInput(String8 path);
virtual ~FileInput();
/**
* Open a file descriptor to the path given in the constructor.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t open();
/**
* Read bytes from the file into the given buffer. At most, the number
* of bytes given in the count argument will be read. Bytes will be written
* into the given buffer starting at the index given in the offset argument.
*
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count);
/**
* Close the file descriptor to the path given in the constructor.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t close();
private:
FILE *mFp;
String8 mPath;
bool mOpen;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_INPUT_H*/

@ -0,0 +1,46 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_FILE_OUTPUT_H
#define IMG_UTILS_FILE_OUTPUT_H
#include <img_utils/Output.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <stdio.h>
#include <stdint.h>
namespace android {
namespace img_utils {
class ANDROID_API FileOutput : public Output {
public:
explicit FileOutput(String8 path);
virtual ~FileOutput();
virtual status_t open();
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
virtual status_t close();
private:
FILE *mFp;
String8 mPath;
bool mOpen;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_FILE_OUTPUT_H*/

@ -0,0 +1,71 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_INPUT_H
#define IMG_UTILS_INPUT_H
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <stdint.h>
namespace android {
namespace img_utils {
/**
* Utility class used as a source of bytes.
*/
class ANDROID_API Input {
public:
virtual ~Input();
/**
* Open this Input.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t open();
/**
* Read bytes into the given buffer. At most, the number of bytes given in the
* count argument will be read. Bytes will be written into the given buffer starting
* at the index given in the offset argument.
*
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count) = 0;
/**
* Skips bytes in the input.
*
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
*/
virtual ssize_t skip(size_t count);
/**
* Close the Input. It is not valid to call open on a previously closed Input.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t close();
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_INPUT_H*/

@ -0,0 +1,57 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_ORDERABLE
#define IMG_UTILS_ORDERABLE
#include <cutils/compiler.h>
#include <stdint.h>
namespace android {
namespace img_utils {
#define COMPARE_DEF(op) \
inline bool operator op (const Orderable& orderable) const;
/**
* Subclasses of Orderable can be compared and sorted. This is
* intended to be used to create sorted arrays of TIFF entries
* and IFDs.
*/
class ANDROID_API Orderable {
public:
virtual ~Orderable();
/**
* Comparison operatotors are based on the value returned
* from this method.
*/
virtual uint32_t getComparableValue() const = 0;
COMPARE_DEF(>)
COMPARE_DEF(<)
COMPARE_DEF(>=)
COMPARE_DEF(<=)
COMPARE_DEF(==)
COMPARE_DEF(!=)
};
#undef COMPARE_DEF
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_ORDERABLE*/

@ -0,0 +1,61 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_OUTPUT_H
#define IMG_UTILS_OUTPUT_H
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <stdint.h>
namespace android {
namespace img_utils {
/**
* Utility class used to output bytes.
*/
class ANDROID_API Output {
public:
virtual ~Output();
/**
* Open this Output.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t open();
/**
* Write bytes from the given buffer. The number of bytes given in the count
* argument will be written. Bytes will be written from the given buffer starting
* at the index given in the offset argument.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t write(const uint8_t* buf, size_t offset, size_t count) = 0;
/**
* Close this Output. It is not valid to call open on a previously closed Output.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t close();
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_OUTPUT_H*/

@ -0,0 +1,44 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_PAIR_H
#define IMG_UTILS_PAIR_H
#include <cutils/compiler.h>
namespace android {
namespace img_utils {
/**
* Generic pair utility class. Nothing special here.
*/
template<typename F, typename S>
class ANDROID_API Pair {
public:
F first;
S second;
Pair() {}
Pair(const Pair& o) : first(o.first), second(o.second) {}
Pair(const F& f, const S& s) : first(f), second(s) {}
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_PAIR_H*/

@ -0,0 +1,53 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_SORTED_ENTRY_VECTOR_H
#define IMG_UTILS_SORTED_ENTRY_VECTOR_H
#include <img_utils/TiffEntry.h>
#include <utils/StrongPointer.h>
#include <utils/SortedVector.h>
namespace android {
namespace img_utils {
/**
* Subclass of SortedVector that has been extended to
* do comparisons/lookups based on the tag ID of the entries.
*/
class SortedEntryVector : public SortedVector<sp<TiffEntry> > {
public:
virtual ~SortedEntryVector();
/**
* Returns the index of the entry with the given tag ID, or
* -1 if none exists.
*/
ssize_t indexOfTag(uint16_t tag) const;
protected:
/**
* Compare tag ID.
*/
virtual int do_compare(const void* lhs, const void* rhs) const;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_SORTED_ENTRY_VECTOR_H*/

@ -0,0 +1,53 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_STRIP_SOURCE_H
#define IMG_UTILS_STRIP_SOURCE_H
#include <img_utils/Output.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <stdint.h>
namespace android {
namespace img_utils {
/**
* This class acts as a data source for strips set in a TiffIfd.
*/
class ANDROID_API StripSource {
public:
virtual ~StripSource();
/**
* Write count bytes to the stream.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t writeToStream(Output& stream, uint32_t count) = 0;
/**
* Return the source IFD.
*/
virtual uint32_t getIfd() const = 0;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_STRIP_SOURCE_H*/

@ -0,0 +1,130 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_ENTRY
#define IMG_UTILS_TIFF_ENTRY
#include <img_utils/TiffWritable.h>
#include <img_utils/TiffHelpers.h>
#include <img_utils/EndianUtils.h>
#include <cutils/compiler.h>
// #include <utils/String8.h>
#include <utils/Errors.h>
#include <stdint.h>
namespace android {
namespace img_utils {
#define COMPARE_DEF(op) \
inline bool operator op (const TiffEntry& entry) const;
/**
* This class holds a single TIFF IFD entry.
*
* Subclasses are expected to support assignment and copying operations.
*/
class ANDROID_API TiffEntry : public TiffWritable {
public:
virtual ~TiffEntry();
/**
* Write the 12-byte IFD entry to the output. The given offset will be
* set as the tag value if the size of the tag value exceeds the max
* size for the TIFF Value field (4 bytes), and should be word aligned.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const = 0;
/**
* Get the count set for this entry. This corresponds to the TIFF Count
* field.
*/
virtual uint32_t getCount() const = 0;
/**
* Get the tag id set for this entry. This corresponds to the TIFF Tag
* field.
*/
virtual uint16_t getTag() const = 0;
/**
* Get the type set for this entry. This corresponds to the TIFF Type
* field.
*/
virtual TagType getType() const = 0;
/**
* Get the defined endianness for this entry. If this is defined,
* the tag value will be written with the given byte order.
*/
virtual Endianness getEndianness() const = 0;
/**
* Get the value for this entry. This corresponds to the TIFF Value
* field.
*
* Returns NULL if the value is NULL, or if the type used does not
* match the type of this tag.
*/
template<typename T>
const T* getData() const;
virtual std::string toString() const;
/**
* Force the type used here to be a valid TIFF type.
*
* Returns NULL if the given value is NULL, or if the type given does
* not match the type of the value given.
*/
template<typename T>
static const T* forceValidType(TagType type, const T* value);
virtual const void* getDataHelper() const = 0;
COMPARE_DEF(>)
COMPARE_DEF(<)
protected:
enum {
MAX_PRINT_STRING_LENGTH = 256
};
};
#define COMPARE(op) \
bool TiffEntry::operator op (const TiffEntry& entry) const { \
return getComparableValue() op entry.getComparableValue(); \
}
COMPARE(>)
COMPARE(<)
template<typename T>
const T* TiffEntry::getData() const {
const T* value = reinterpret_cast<const T*>(getDataHelper());
return forceValidType<T>(getType(), value);
}
#undef COMPARE
#undef COMPARE_DEF
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_ENTRY*/

@ -0,0 +1,219 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_ENTRY_IMPL
#define IMG_UTILS_TIFF_ENTRY_IMPL
#include <img_utils/TiffIfd.h>
#include <img_utils/TiffEntry.h>
#include <img_utils/TiffHelpers.h>
#include <img_utils/Output.h>
#include <img_utils/EndianUtils.h>
#include <utils/Log.h>
#include <utils/Errors.h>
// #include <utils/Vector.h>
#include <utils/StrongPointer.h>
#include <stdint.h>
#include <vector>
namespace android {
namespace img_utils {
template<typename T>
class TiffEntryImpl : public TiffEntry {
public:
TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end, const T* data);
virtual ~TiffEntryImpl();
status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const;
uint32_t getCount() const;
uint16_t getTag() const;
TagType getType() const;
Endianness getEndianness() const;
size_t getSize() const;
uint32_t getComparableValue() const;
protected:
const void* getDataHelper() const;
uint32_t getActualSize() const;
uint16_t mTag;
uint16_t mType;
uint32_t mCount;
Endianness mEnd;
std::vector<T> mData;
};
template<typename T>
TiffEntryImpl<T>::TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end,
const T* data)
: mTag(tag), mType(static_cast<uint16_t>(type)), mCount(count), mEnd(end) {
count = (type == RATIONAL || type == SRATIONAL) ? count * 2 : count;
auto it = mData.insert(mData.end(), data, data + count);
// LOG_ALWAYS_FATAL_IF(index < 0, "%s: Could not allocate vector for data.", __FUNCTION__);
}
template<typename T>
TiffEntryImpl<T>::~TiffEntryImpl() {}
template<typename T>
uint32_t TiffEntryImpl<T>::getCount() const {
return mCount;
}
template<typename T>
uint16_t TiffEntryImpl<T>::getTag() const {
return mTag;
}
template<typename T>
TagType TiffEntryImpl<T>::getType() const {
return static_cast<TagType>(mType);
}
template<typename T>
const void* TiffEntryImpl<T>::getDataHelper() const {
return reinterpret_cast<const void*>(&mData[0]);
}
template<typename T>
size_t TiffEntryImpl<T>::getSize() const {
uint32_t total = getActualSize();
WORD_ALIGN(total)
return (total <= OFFSET_SIZE) ? 0 : total;
}
template<typename T>
uint32_t TiffEntryImpl<T>::getActualSize() const {
uint32_t total = sizeof(T) * mCount;
if (getType() == RATIONAL || getType() == SRATIONAL) {
// 2 ints stored for each rational, multiply by 2
total <<= 1;
}
return total;
}
template<typename T>
Endianness TiffEntryImpl<T>::getEndianness() const {
return mEnd;
}
template<typename T>
uint32_t TiffEntryImpl<T>::getComparableValue() const {
return mTag;
}
template<typename T>
status_t TiffEntryImpl<T>::writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const {
assert((offset % TIFF_WORD_SIZE) == 0);
status_t ret = OK;
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
uint32_t dataSize = getActualSize();
if (dataSize > OFFSET_SIZE) {
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
} else {
uint32_t count = mCount;
if (getType() == RATIONAL || getType() == SRATIONAL) {
/**
* Rationals are stored as an array of ints. Each
* rational is represented by 2 ints. To recover the
* size of the array here, multiply the count by 2.
*/
count <<= 1;
}
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
ZERO_TILL_WORD(out, dataSize, ret);
}
return ret;
}
template<typename T>
status_t TiffEntryImpl<T>::writeData(uint32_t /*offset*/, EndianOutput* out) const {
status_t ret = OK;
// Some tags have fixed-endian value output
Endianness tmp = UNDEFINED_ENDIAN;
if (mEnd != UNDEFINED_ENDIAN) {
tmp = out->getEndianness();
out->setEndianness(mEnd);
}
uint32_t count = mCount;
if (getType() == RATIONAL || getType() == SRATIONAL) {
/**
* Rationals are stored as an array of ints. Each
* rational is represented by 2 ints. To recover the
* size of the array here, multiply the count by 2.
*/
count <<= 1;
}
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
if (mEnd != UNDEFINED_ENDIAN) {
out->setEndianness(tmp);
}
// Write to next word alignment
ZERO_TILL_WORD(out, sizeof(T) * count, ret);
return ret;
}
template<>
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeTagInfo(uint32_t offset,
/*out*/EndianOutput* out) const {
assert((offset % TIFF_WORD_SIZE) == 0);
status_t ret = OK;
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
return ret;
}
template<>
inline uint32_t TiffEntryImpl<sp<TiffIfd> >::getActualSize() const {
uint32_t total = 0;
for (size_t i = 0; i < mData.size(); ++i) {
total += mData[i]->getSize();
}
return total;
}
template<>
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeData(uint32_t offset, EndianOutput* out) const {
status_t ret = OK;
for (uint32_t i = 0; i < mCount; ++i) {
BAIL_ON_FAIL(mData[i]->writeData(offset, out), ret);
offset += mData[i]->getSize();
}
return ret;
}
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_ENTRY_IMPL*/

@ -0,0 +1,132 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_HELPERS_H
#define IMG_UTILS_TIFF_HELPERS_H
#include <stdint.h>
namespace android {
namespace img_utils {
const uint8_t ZERO_WORD[] = {0, 0, 0, 0};
#define BAIL_ON_FAIL(x, flag) \
if (((flag) = (x)) != OK) return flag;
#define BYTES_TILL_WORD(index) \
((TIFF_WORD_SIZE - ((index) % TIFF_WORD_SIZE)) % TIFF_WORD_SIZE)
#define WORD_ALIGN(count) \
count += BYTES_TILL_WORD(count);
#define ZERO_TILL_WORD(output, index, ret) \
{ \
size_t remaining = BYTES_TILL_WORD(index); \
if (remaining > 0) { \
BAIL_ON_FAIL((output)->write(ZERO_WORD, 0, remaining), ret); \
} \
}
/**
* Basic TIFF header constants.
*/
enum {
BAD_OFFSET = 0,
TIFF_WORD_SIZE = 4, // Size in bytes
IFD_HEADER_SIZE = 2, // Size in bytes
IFD_FOOTER_SIZE = 4, // Size in bytes
TIFF_ENTRY_SIZE = 12, // Size in bytes
MAX_IFD_ENTRIES = UINT16_MAX,
FILE_HEADER_SIZE = 8, // Size in bytes
ENDIAN_MARKER_SIZE = 2, // Size in bytes
TIFF_MARKER_SIZE = 2, // Size in bytes
OFFSET_MARKER_SIZE = 4, // Size in bytes
TIFF_FILE_MARKER = 42,
BIG_ENDIAN_MARKER = 0x4D4Du,
LITTLE_ENDIAN_MARKER = 0x4949u
};
/**
* Constants for the TIFF tag types.
*/
enum TagType {
UNKNOWN_TAGTYPE = 0,
BYTE=1,
ASCII,
SHORT,
LONG,
RATIONAL,
SBYTE,
UNDEFINED,
SSHORT,
SLONG,
SRATIONAL,
FLOAT,
DOUBLE
};
/**
* Sizes of the TIFF entry fields (in bytes).
*/
enum {
TAG_SIZE = 2,
TYPE_SIZE = 2,
COUNT_SIZE = 4,
OFFSET_SIZE = 4
};
/**
* Convenience IFD id constants.
*/
enum {
IFD_0 = 0,
RAW_IFD,
PROFILE_IFD,
PREVIEW_IFD
};
inline size_t getTypeSize(TagType type) {
switch(type) {
case UNDEFINED:
case ASCII:
case BYTE:
case SBYTE:
return 1;
case SHORT:
case SSHORT:
return 2;
case LONG:
case SLONG:
case FLOAT:
return 4;
case RATIONAL:
case SRATIONAL:
case DOUBLE:
return 8;
default:
return 0;
}
}
inline uint32_t calculateIfdSize(size_t numberOfEntries) {
return IFD_HEADER_SIZE + IFD_FOOTER_SIZE + TIFF_ENTRY_SIZE * numberOfEntries;
}
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_HELPERS_H*/

@ -0,0 +1,164 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_IFD_H
#define IMG_UTILS_TIFF_IFD_H
#include <img_utils/TiffWritable.h>
#include <img_utils/TiffEntry.h>
#include <img_utils/Output.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <stdint.h>
#include <map>
namespace android {
namespace img_utils {
/**
* This class holds a single TIFF Image File Directory (IFD) structure.
*
* This maps to the TIFF IFD structure that is logically composed of:
* - A 2-byte field listing the number of entries.
* - A list of 12-byte TIFF entries.
* - A 4-byte offset to the next IFD.
*/
class ANDROID_API TiffIfd : public TiffWritable {
public:
explicit TiffIfd(uint32_t ifdId);
virtual ~TiffIfd();
/**
* Add a TiffEntry to this IFD or replace an existing entry with the
* same tag ID. No validation is done.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t addEntry(const sp<TiffEntry>& entry);
/**
* Set the pointer to the next IFD. This is used to create a linked
* list of IFDs as defined by the TIFF 6.0 spec., and is not included
* when calculating the size of IFD and entries for the getSize()
* method (unlike SubIFDs).
*/
virtual void setNextIfd(const sp<TiffIfd>& ifd);
/**
* Get the pointer to the next IFD, or NULL if none exists.
*/
virtual sp<TiffIfd> getNextIfd() const;
/**
* Write the IFD data. This includes the IFD header, entries, footer,
* and the corresponding values for each entry (recursively including
* sub-IFDs). The written amount should end on a word boundary, and
* the given offset should be word aligned.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
/**
* Get the size of the IFD. This includes the IFD header, entries, footer,
* and the corresponding values for each entry (recursively including
* any sub-IFDs).
*/
virtual size_t getSize() const;
/**
* Get the id of this IFD.
*/
virtual uint32_t getId() const;
/**
* Get an entry with the given tag ID.
*
* Returns a strong pointer to the entry if it exists, or an empty strong
* pointer.
*/
virtual sp<TiffEntry> getEntry(uint16_t tag) const;
/**
* Remove the entry with the given tag ID if it exists.
*/
virtual void removeEntry(uint16_t tag);
/**
* Convenience method to validate and set strip-related image tags.
*
* This sets all strip related tags, but leaves offset values unitialized.
* setStripOffsets must be called with the desired offset before writing.
* The strip tag values are calculated from the existing tags for image
* dimensions and pixel type set in the IFD.
*
* Does not handle planar image configurations (PlanarConfiguration != 1).
*
* Returns OK on success, or a negative error code.
*/
virtual status_t validateAndSetStripTags();
/**
* Returns true if validateAndSetStripTags has been called, but not setStripOffsets.
*/
virtual bool uninitializedOffsets() const;
/**
* Convenience method to set beginning offset for strips.
*
* Call this to update the strip offsets before calling writeData.
*
* Returns OK on success, or a negative error code.
*/
virtual status_t setStripOffset(uint32_t offset);
/**
* Get the total size of the strips in bytes.
*
* This sums the byte count at each strip offset, and returns
* the total count of bytes stored in strips for this IFD.
*/
virtual uint32_t getStripSize() const;
/**
* Get a formatted string representing this IFD.
*/
virtual std::string toString() const;
/**
* Print a formatted string representing this IFD to logcat.
*/
void log() const;
/**
* Get value used to determine sort order.
*/
virtual uint32_t getComparableValue() const;
protected:
virtual uint32_t checkAndGetOffset(uint32_t offset) const;
std::map<uint16_t, sp<TiffEntry> > mEntries;
sp<TiffIfd> mNextIfd;
uint32_t mIfdId;
bool mStripOffsetsInitialized;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_IFD_H*/

@ -0,0 +1,60 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_WRITABLE
#define IMG_UTILS_TIFF_WRITABLE
#include <img_utils/Orderable.h>
#include <img_utils/EndianUtils.h>
#include <img_utils/Output.h>
#include <cutils/compiler.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <stdint.h>
namespace android {
namespace img_utils {
/**
* TiffWritable subclasses represent TIFF metadata objects that can be written
* to an EndianOutput object. This is used for TIFF entries and IFDs.
*/
class ANDROID_API TiffWritable : public Orderable, public LightRefBase<TiffWritable> {
public:
TiffWritable();
virtual ~TiffWritable();
/**
* Write the data to the output. The given offset is used to calculate
* the header offset for values written. The offset is defined
* relative to the beginning of the TIFF header, and is word aligned.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const = 0;
/**
* Get the size of the data to write.
*/
virtual size_t getSize() const = 0;
};
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_WRITABLE*/

@ -0,0 +1,328 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMG_UTILS_TIFF_WRITER_H
#define IMG_UTILS_TIFF_WRITER_H
#include <img_utils/EndianUtils.h>
#include <img_utils/StripSource.h>
#include <img_utils/TiffEntryImpl.h>
#include <img_utils/TagDefinitions.h>
#include <img_utils/TiffIfd.h>
#include <utils/Log.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <cutils/compiler.h>
#include <stdint.h>
#include <vector>
#include <map>
namespace android {
namespace img_utils {
class TiffEntry;
class TiffIfd;
class Output;
/**
* This class holds a collection of TIFF IFDs that can be written as a
* complete DNG file header.
*
* This maps to the TIFF header structure that is logically composed of:
* - An 8-byte file header containing an endianness indicator, the TIFF
* file marker, and the offset to the first IFD.
* - A list of TIFF IFD structures.
*/
class ANDROID_API TiffWriter : public LightRefBase<TiffWriter> {
public:
enum SubIfdType {
SUBIFD = 0,
GPSINFO
};
/**
* Constructs a TiffWriter with the default tag mappings. This enables
* all of the tags defined in TagDefinitions.h, and uses the following
* mapping precedence to resolve collisions:
* (highest precedence) TIFF/EP > DNG > EXIF 2.3 > TIFF 6.0
*/
TiffWriter();
/**
* Constructs a TiffWriter with the given tag mappings. The mapping
* precedence will be in the order that the definition maps are given,
* where the lower index map gets precedence.
*
* This can be used with user-defined definitions, or definitions form
* TagDefinitions.h
*
* The enabledDefinitions mapping object is owned by the caller, and must
* stay alive for the lifespan of the constructed TiffWriter object.
*/
TiffWriter(std::map<uint16_t, const TagDefinition_t*>* enabledDefinitions,
size_t length);
virtual ~TiffWriter();
/**
* Write a TIFF header containing each IFD set. This will recursively
* write all SubIFDs and tags.
*
* Any StripSources passed in will be written to the output as image strips
* at the appropriate offests. The StripByteCounts, RowsPerStrip, and
* StripOffsets tags must be set to use this. To set these tags in a
* given IFD, use the addStrip method.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t write(Output* out, StripSource** sources, size_t sourcesCount,
Endianness end = LITTLE);
/**
* Write a TIFF header containing each IFD set. This will recursively
* write all SubIFDs and tags.
*
* Image data for strips or tiles must be written separately at the
* appropriate offsets. These offsets must not fall within the file
* header written this way. The size of the header written is given
* by the getTotalSize() method.
*
* Returns OK on success, or a negative error code on failure.
*/
virtual status_t write(Output* out, Endianness end = LITTLE);
/**
* Get the total size in bytes of the TIFF header. This includes all
* IFDs, tags, and values set for this TiffWriter.
*/
virtual uint32_t getTotalSize() const;
/**
* Add an entry to the IFD with the given ID.
*
* Returns OK on success, or a negative error code on failure. Valid
* error codes for this method are:
* - BAD_INDEX - The given tag doesn't exist.
* - BAD_VALUE - The given count doesn't match the required count for
* this tag.
* - BAD_TYPE - The type of the given data isn't compatible with the
* type required for this tag.
* - NAME_NOT_FOUND - No ifd exists with the given ID.
*/
virtual status_t addEntry(const sp<TiffEntry>& entry, uint32_t ifd);
/**
* Build an entry for a known tag and add it to the IFD with the given ID.
* This tag must be defined in one of the definition vectors this TIFF writer
* was constructed with. The count and type are validated.
*
* Returns OK on success, or a negative error code on failure. Valid
* error codes for this method are:
* - BAD_INDEX - The given tag doesn't exist.
* - BAD_VALUE - The given count doesn't match the required count for
* this tag.
* - BAD_TYPE - The type of the given data isn't compatible with the
* type required for this tag.
* - NAME_NOT_FOUND - No ifd exists with the given ID.
*/
template<typename T>
status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
/**
* Build an entry for a known tag. This tag must be one of the tags
* defined in one of the definition vectors this TIFF writer was constructed
* with. The count and type are validated. If this succeeds, the resulting
* entry will be placed in the outEntry pointer.
*
* Returns OK on success, or a negative error code on failure. Valid
* error codes for this method are:
* - BAD_INDEX - The given tag doesn't exist.
* - BAD_VALUE - The given count doesn't match the required count for
* this tag.
* - BAD_TYPE - The type of the given data isn't compatible with the
* type required for this tag.
*/
template<typename T>
status_t buildEntry(uint16_t tag, uint32_t count, const T* data,
/*out*/sp<TiffEntry>* outEntry) const;
/**
* Convenience function to set the strip related tags for a given IFD.
*
* Call this before using a StripSource as an input to write.
* The following tags must be set before calling this method:
* - ImageWidth
* - ImageLength
* - SamplesPerPixel
* - BitsPerSample
*
* Returns OK on success, or a negative error code.
*/
virtual status_t addStrip(uint32_t ifd);
/**
* Return the TIFF entry with the given tag ID in the IFD with the given ID,
* or an empty pointer if none exists.
*/
virtual sp<TiffEntry> getEntry(uint16_t tag, uint32_t ifd) const;
/**
* Remove the TIFF entry with the given tag ID in the given IFD if it exists.
*/
virtual void removeEntry(uint16_t tag, uint32_t ifd);
/**
* Create an empty IFD with the given ID and add it to the end of the
* list of IFDs.
*/
virtual status_t addIfd(uint32_t ifd);
/**
* Create an empty IFD with the given ID and add it as a SubIfd of the
* parent IFD.
*/
virtual status_t addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type = SUBIFD);
/**
* Returns the default type for the given tag ID.
*/
virtual TagType getDefaultType(uint16_t tag) const;
/**
* Returns the default count for a given tag ID, or 0 if this
* tag normally has a variable count.
*/
virtual uint32_t getDefaultCount(uint16_t tag) const;
/**
* Returns true if an IFD with the given ID exists.
*/
virtual bool hasIfd(uint32_t ifd) const;
/**
* Returns true if a definition exist for the given tag ID.
*/
virtual bool checkIfDefined(uint16_t tag) const;
/**
* Returns the name of the tag if a definition exists for the given tag
* ID, or null if no definition exists.
*/
virtual const char* getTagName(uint16_t tag) const;
/**
* Print the currently configured IFDs and entries to logcat.
*/
virtual void log() const;
/**
* Build an entry. No validation is done.
*
* WARNING: Using this method can result in creating poorly formatted
* TIFF files.
*
* Returns a TiffEntry with the given tag, type, count, endianness,
* and data.
*/
template<typename T>
static sp<TiffEntry> uncheckedBuildEntry(uint16_t tag, TagType type,
uint32_t count, Endianness end, const T* data);
/**
* Utility function to build atag-to-definition mapping from a given
* array of tag definitions.
*/
#if 0
static KeyedVector<uint16_t, const TagDefinition_t*> buildTagMap(
const TagDefinition_t* definitions, size_t length);
#endif
protected:
enum {
DEFAULT_NUM_TAG_MAPS = 4,
};
sp<TiffIfd> findLastIfd();
status_t writeFileHeader(EndianOutput& out);
const TagDefinition_t* lookupDefinition(uint16_t tag) const;
status_t calculateOffsets();
sp<TiffIfd> mIfd;
std::map<uint32_t, sp<TiffIfd> > mNamedIfds;
std::vector<std::map<uint16_t, const TagDefinition_t*> > mTagMaps;
size_t mNumTagMaps;
#if 0
static KeyedVector<uint16_t, const TagDefinition_t*> sTagMaps[];
#endif
};
template<typename T>
status_t TiffWriter::buildEntry(uint16_t tag, uint32_t count, const T* data,
/*out*/sp<TiffEntry>* outEntry) const {
const TagDefinition_t* definition = lookupDefinition(tag);
if (definition == NULL) {
ALOGE("%s: No such tag exists for id %x.", __FUNCTION__, tag);
return BAD_INDEX;
}
uint32_t fixedCount = definition->fixedCount;
if (fixedCount > 0 && fixedCount != count) {
ALOGE("%s: Invalid count %d for tag %x (expects %d).", __FUNCTION__, count, tag,
fixedCount);
return BAD_VALUE;
}
TagType fixedType = definition->defaultType;
if (TiffEntry::forceValidType(fixedType, data) == NULL) {
ALOGE("%s: Invalid type used for tag value for tag %x.", __FUNCTION__, tag);
return BAD_TYPE;
}
*outEntry = new TiffEntryImpl<T>(tag, fixedType, count,
definition->fixedEndian, data);
return OK;
}
template<typename T>
status_t TiffWriter::addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd) {
sp<TiffEntry> outEntry;
status_t ret = buildEntry<T>(tag, count, data, &outEntry);
if (ret != OK) {
ALOGE("%s: Could not build entry for tag %x.", __FUNCTION__, tag);
return ret;
}
return addEntry(outEntry, ifd);
}
template<typename T>
sp<TiffEntry> TiffWriter::uncheckedBuildEntry(uint16_t tag, TagType type, uint32_t count,
Endianness end, const T* data) {
TiffEntryImpl<T>* entry = new TiffEntryImpl<T>(tag, type, count, end, data);
return sp<TiffEntry>(entry);
}
} /*namespace img_utils*/
} /*namespace android*/
#endif /*IMG_UTILS_TIFF_WRITER_H*/

@ -0,0 +1,54 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/ByteArrayOutput.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
ByteArrayOutput::ByteArrayOutput() {}
ByteArrayOutput::~ByteArrayOutput() {}
status_t ByteArrayOutput::open() {
return OK;
}
status_t ByteArrayOutput::write(const uint8_t* buf, size_t offset, size_t count) {
if (mByteArray.insert(mByteArray.end(), buf + offset, buf + offset + count) == mByteArray.end()) {
ALOGE("%s: Failed to write to ByteArrayOutput.", __FUNCTION__);
return BAD_VALUE;
}
return OK;
}
status_t ByteArrayOutput::close() {
mByteArray.clear();
return OK;
}
size_t ByteArrayOutput::getSize() const {
return mByteArray.size();
}
const uint8_t* ByteArrayOutput::getArray() const {
return &mByteArray[0];
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,496 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/DngUtils.h>
#include <inttypes.h>
#include <algorithm>
#include <vector>
#include <math.h>
namespace android {
namespace img_utils {
OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
if(mEndianOut.open() != OK) {
ALOGE("%s: Open failed.", __FUNCTION__);
}
}
OpcodeListBuilder::~OpcodeListBuilder() {
if(mEndianOut.close() != OK) {
ALOGE("%s: Close failed.", __FUNCTION__);
}
}
size_t OpcodeListBuilder::getSize() const {
return mOpList.getSize() + sizeof(mCount);
}
uint32_t OpcodeListBuilder::getCount() const {
return mCount;
}
status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
uint32_t count = convertToBigEndian(mCount);
memcpy(buf, &count, sizeof(count));
memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
return OK;
}
status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaTop,
uint32_t activeAreaLeft,
uint32_t activeAreaBottom,
uint32_t activeAreaRight,
CfaLayout cfa,
const float* lensShadingMap) {
status_t err = OK;
uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
switch (cfa) {
case CFA_RGGB:
case CFA_GRBG:
case CFA_GBRG:
case CFA_BGGR:
err = addBayerGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
activeAreaHeight, cfa, lensShadingMap);
break;
case CFA_NONE:
err = addMonochromeGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
activeAreaHeight, lensShadingMap);
break;
default:
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
err = BAD_VALUE;
break;
}
return err;
}
status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaWidth,
uint32_t activeAreaHeight,
CfaLayout cfa,
const float* lensShadingMap) {
uint32_t redTop = 0;
uint32_t redLeft = 0;
uint32_t greenEvenTop = 0;
uint32_t greenEvenLeft = 1;
uint32_t greenOddTop = 1;
uint32_t greenOddLeft = 0;
uint32_t blueTop = 1;
uint32_t blueLeft = 1;
switch(cfa) {
case CFA_RGGB:
redTop = 0;
redLeft = 0;
greenEvenTop = 0;
greenEvenLeft = 1;
greenOddTop = 1;
greenOddLeft = 0;
blueTop = 1;
blueLeft = 1;
break;
case CFA_GRBG:
redTop = 0;
redLeft = 1;
greenEvenTop = 0;
greenEvenLeft = 0;
greenOddTop = 1;
greenOddLeft = 1;
blueTop = 1;
blueLeft = 0;
break;
case CFA_GBRG:
redTop = 1;
redLeft = 0;
greenEvenTop = 0;
greenEvenLeft = 0;
greenOddTop = 1;
greenOddLeft = 1;
blueTop = 0;
blueLeft = 1;
break;
case CFA_BGGR:
redTop = 1;
redLeft = 1;
greenEvenTop = 0;
greenEvenLeft = 1;
greenOddTop = 1;
greenOddLeft = 0;
blueTop = 0;
blueLeft = 0;
break;
default:
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
return BAD_VALUE;
}
std::vector<float> redMapVector(lsmWidth * lsmHeight);
float *redMap = redMapVector.data();
std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
float *greenEvenMap = greenEvenMapVector.data();
std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
float *greenOddMap = greenOddMapVector.data();
std::vector<float> blueMapVector(lsmWidth * lsmHeight);
float *blueMap = blueMapVector.data();
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
// Split lens shading map channels into separate arrays
size_t j = 0;
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
redMap[j] = lensShadingMap[i + LSM_R_IND];
greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
blueMap[j] = lensShadingMap[i + LSM_B_IND];
}
status_t err = addGainMap(/*top*/redTop,
/*left*/redLeft,
/*bottom*/activeAreaHeight,
/*right*/activeAreaWidth,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/redMap);
if (err != OK) return err;
err = addGainMap(/*top*/greenEvenTop,
/*left*/greenEvenLeft,
/*bottom*/activeAreaHeight,
/*right*/activeAreaWidth,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/greenEvenMap);
if (err != OK) return err;
err = addGainMap(/*top*/greenOddTop,
/*left*/greenOddLeft,
/*bottom*/activeAreaHeight,
/*right*/activeAreaWidth,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/greenOddMap);
if (err != OK) return err;
err = addGainMap(/*top*/blueTop,
/*left*/blueLeft,
/*bottom*/activeAreaHeight,
/*right*/activeAreaWidth,
/*plane*/0,
/*planes*/1,
/*rowPitch*/2,
/*colPitch*/2,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/blueMap);
return err;
}
status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
uint32_t lsmHeight,
uint32_t activeAreaWidth,
uint32_t activeAreaHeight,
const float* lensShadingMap) {
std::vector<float> mapVector(lsmWidth * lsmHeight);
float *map = mapVector.data();
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
// Split lens shading map channels into separate arrays
size_t j = 0;
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
map[j] = lensShadingMap[i];
}
status_t err = addGainMap(/*top*/0,
/*left*/0,
/*bottom*/activeAreaHeight,
/*right*/activeAreaWidth,
/*plane*/0,
/*planes*/1,
/*rowPitch*/1,
/*colPitch*/1,
/*mapPointsV*/lsmHeight,
/*mapPointsH*/lsmWidth,
/*mapSpacingV*/spacingV,
/*mapSpacingH*/spacingH,
/*mapOriginV*/0,
/*mapOriginH*/0,
/*mapPlanes*/1,
/*mapGains*/map);
if (err != OK) return err;
return err;
}
status_t OpcodeListBuilder::addGainMap(uint32_t top,
uint32_t left,
uint32_t bottom,
uint32_t right,
uint32_t plane,
uint32_t planes,
uint32_t rowPitch,
uint32_t colPitch,
uint32_t mapPointsV,
uint32_t mapPointsH,
double mapSpacingV,
double mapSpacingH,
double mapOriginV,
double mapOriginH,
uint32_t mapPlanes,
const float* mapGains) {
status_t err = addOpcodePreamble(GAIN_MAP_ID);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUMBER_INT_ARGS = 11;
const uint32_t NUMBER_DOUBLE_ARGS = 4;
uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
mapPointsV * mapPointsH * mapPlanes * sizeof(float);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
// Batch writes as much as possible
uint32_t settings1[] = { top,
left,
bottom,
right,
plane,
planes,
rowPitch,
colPitch,
mapPointsV,
mapPointsH };
err = mEndianOut.write(settings1, 0, NELEMS(settings1));
if (err != OK) return err;
double settings2[] = { mapSpacingV,
mapSpacingH,
mapOriginV,
mapOriginH };
err = mEndianOut.write(settings2, 0, NELEMS(settings2));
if (err != OK) return err;
err = mEndianOut.write(&mapPlanes, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
if (err != OK) return err;
mCount++;
return OK;
}
status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
uint32_t activeArrayWidth,
uint32_t activeArrayHeight,
float opticalCenterX,
float opticalCenterY) {
if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
__FUNCTION__, activeArrayWidth, activeArrayHeight);
return BAD_VALUE;
}
double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth);
double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight);
normalizedOCX = CLAMP(normalizedOCX, 0, 1);
normalizedOCY = CLAMP(normalizedOCY, 0, 1);
double coeffs[6] = {
kCoeffs[0],
kCoeffs[1],
kCoeffs[2],
kCoeffs[3],
kCoeffs[4],
kCoeffs[5]
};
return addWarpRectilinear(/*numPlanes*/1,
/*opticalCenterX*/normalizedOCX,
/*opticalCenterY*/normalizedOCY,
coeffs);
}
status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
double opticalCenterX,
double opticalCenterY,
const double* kCoeffs) {
status_t err = addOpcodePreamble(WARP_RECTILINEAR_ID);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUMBER_CENTER_ARGS = 2;
const uint32_t NUMBER_COEFFS = numPlanes * 6;
uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&numPlanes, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
if (err != OK) return err;
err = mEndianOut.write(&opticalCenterX, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&opticalCenterY, 0, 1);
if (err != OK) return err;
mCount++;
return OK;
}
status_t OpcodeListBuilder::addBadPixelListForMetadata(const uint32_t* hotPixels,
uint32_t xyPairCount,
uint32_t colorFilterArrangement) {
if (colorFilterArrangement > 3) {
ALOGE("%s: Unknown color filter arrangement %" PRIu32, __FUNCTION__,
colorFilterArrangement);
return BAD_VALUE;
}
return addBadPixelList(colorFilterArrangement, xyPairCount, 0, hotPixels, nullptr);
}
status_t OpcodeListBuilder::addBadPixelList(uint32_t bayerPhase,
uint32_t badPointCount,
uint32_t badRectCount,
const uint32_t* badPointRowColPairs,
const uint32_t* badRectTopLeftBottomRightTuples) {
status_t err = addOpcodePreamble(FIX_BAD_PIXELS_LIST);
if (err != OK) return err;
// Allow this opcode to be skipped if not supported
uint32_t flags = FLAG_OPTIONAL;
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
const uint32_t NUM_NON_VARLEN_FIELDS = 3;
const uint32_t SIZE_OF_POINT = 2;
const uint32_t SIZE_OF_RECT = 4;
uint32_t totalSize = (NUM_NON_VARLEN_FIELDS + badPointCount * SIZE_OF_POINT +
badRectCount * SIZE_OF_RECT) * sizeof(uint32_t);
err = mEndianOut.write(&totalSize, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&bayerPhase, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&badPointCount, 0, 1);
if (err != OK) return err;
err = mEndianOut.write(&badRectCount, 0, 1);
if (err != OK) return err;
if (badPointCount > 0) {
err = mEndianOut.write(badPointRowColPairs, 0, SIZE_OF_POINT * badPointCount);
if (err != OK) return err;
}
if (badRectCount > 0) {
err = mEndianOut.write(badRectTopLeftBottomRightTuples, 0, SIZE_OF_RECT * badRectCount);
if (err != OK) return err;
}
mCount++;
return OK;
}
status_t OpcodeListBuilder::addOpcodePreamble(uint32_t opcodeId) {
status_t err = mEndianOut.write(&opcodeId, 0, 1);
if (err != OK) return err;
uint8_t version[] = {1, 3, 0, 0};
err = mEndianOut.write(version, 0, NELEMS(version));
if (err != OK) return err;
return OK;
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,83 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/EndianUtils.h>
namespace android {
namespace img_utils {
EndianOutput::EndianOutput(Output* out, Endianness end)
: mOffset(0), mOutput(out), mEndian(end) {}
EndianOutput::~EndianOutput() {}
status_t EndianOutput::open() {
mOffset = 0;
return mOutput->open();
}
status_t EndianOutput::close() {
return mOutput->close();
}
void EndianOutput::setEndianness(Endianness end) {
mEndian = end;
}
uint32_t EndianOutput::getCurrentOffset() const {
return mOffset;
}
Endianness EndianOutput::getEndianness() const {
return mEndian;
}
status_t EndianOutput::write(const uint8_t* buf, size_t offset, size_t count) {
status_t res = OK;
if((res = mOutput->write(buf, offset, count)) == OK) {
mOffset += count;
}
return res;
}
status_t EndianOutput::write(const int8_t* buf, size_t offset, size_t count) {
return write(reinterpret_cast<const uint8_t*>(buf), offset, count);
}
#define DEFINE_WRITE(_type_) \
status_t EndianOutput::write(const _type_* buf, size_t offset, size_t count) { \
return writeHelper<_type_>(buf, offset, count); \
}
DEFINE_WRITE(uint16_t)
DEFINE_WRITE(int16_t)
DEFINE_WRITE(uint32_t)
DEFINE_WRITE(int32_t)
DEFINE_WRITE(uint64_t)
DEFINE_WRITE(int64_t)
status_t EndianOutput::write(const float* buf, size_t offset, size_t count) {
assert(sizeof(float) == sizeof(uint32_t));
return writeHelper<uint32_t>(reinterpret_cast<const uint32_t*>(buf), offset, count);
}
status_t EndianOutput::write(const double* buf, size_t offset, size_t count) {
assert(sizeof(double) == sizeof(uint64_t));
return writeHelper<uint64_t>(reinterpret_cast<const uint64_t*>(buf), offset, count);
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,85 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/FileInput.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
FileInput::FileInput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
FileInput::~FileInput() {
if (mOpen) {
ALOGE("%s: FileInput destroyed without calling close!", __FUNCTION__);
close();
}
}
status_t FileInput::open() {
if (mOpen) {
ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
return OK;
}
mFp = ::fopen(mPath, "rb");
if (!mFp) {
ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
return BAD_VALUE;
}
mOpen = true;
return OK;
}
ssize_t FileInput::read(uint8_t* buf, size_t offset, size_t count) {
if (!mOpen) {
ALOGE("%s: Could not read file %s, file not open.", __FUNCTION__, mPath.string());
return BAD_VALUE;
}
size_t bytesRead = ::fread(buf + offset, sizeof(uint8_t), count, mFp);
int error = ::ferror(mFp);
if (error != 0) {
ALOGE("%s: Error %d occurred while reading file %s.", __FUNCTION__, error, mPath.string());
return BAD_VALUE;
}
// End of file reached
if (::feof(mFp) != 0 && bytesRead == 0) {
return NOT_ENOUGH_DATA;
}
return bytesRead;
}
status_t FileInput::close() {
if(!mOpen) {
ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
return OK;
}
status_t ret = OK;
if(::fclose(mFp) != 0) {
ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
ret = BAD_VALUE;
}
mOpen = false;
return ret;
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,79 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/FileOutput.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
FileOutput::FileOutput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
FileOutput::~FileOutput() {
if (mOpen) {
ALOGW("%s: Destructor called with %s still open.", __FUNCTION__, mPath.string());
close();
}
}
status_t FileOutput::open() {
if (mOpen) {
ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
return OK;
}
mFp = ::fopen(mPath, "wb");
if (!mFp) {
ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
return BAD_VALUE;
}
mOpen = true;
return OK;
}
status_t FileOutput::write(const uint8_t* buf, size_t offset, size_t count) {
if (!mOpen) {
ALOGE("%s: Could not write file %s, file not open.", __FUNCTION__, mPath.string());
return BAD_VALUE;
}
::fwrite(buf + offset, sizeof(uint8_t), count, mFp);
int error = ::ferror(mFp);
if (error != 0) {
ALOGE("%s: Error %d occurred while writing file %s.", __FUNCTION__, error, mPath.string());
return BAD_VALUE;
}
return OK;
}
status_t FileOutput::close() {
if(!mOpen) {
ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
return OK;
}
status_t ret = OK;
if(::fclose(mFp) != 0) {
ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
ret = BAD_VALUE;
}
mOpen = false;
return ret;
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,57 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/Input.h>
namespace android {
namespace img_utils {
Input::~Input() {}
status_t Input::open() { return OK; }
status_t Input::close() { return OK; }
ssize_t Input::skip(size_t count) {
const size_t SKIP_BUF_SIZE = 1024;
uint8_t skipBuf[SKIP_BUF_SIZE];
size_t remaining = count;
while (remaining > 0) {
size_t amt = (SKIP_BUF_SIZE > remaining) ? remaining : SKIP_BUF_SIZE;
ssize_t ret = read(skipBuf, 0, amt);
if (ret < 0) {
if(ret == NOT_ENOUGH_DATA) {
// End of file encountered
if (remaining == count) {
// Read no bytes, return EOF
return NOT_ENOUGH_DATA;
} else {
// Return num bytes read
return count - remaining;
}
}
// Return error code.
return ret;
}
remaining -= ret;
}
return count;
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,39 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/Orderable.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
#define COMPARE(op) \
bool Orderable::operator op (const Orderable& orderable) const { \
return getComparableValue() op orderable.getComparableValue(); \
}
COMPARE(>)
COMPARE(<)
COMPARE(>=)
COMPARE(<=)
COMPARE(==)
COMPARE(!=)
Orderable::~Orderable() {}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,28 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/Output.h>
namespace android {
namespace img_utils {
Output::~Output() {}
status_t Output::open() { return OK; }
status_t Output::close() { return OK; }
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,44 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/SortedEntryVector.h>
#include <utils/TypeHelpers.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
SortedEntryVector::~SortedEntryVector() {}
ssize_t SortedEntryVector::indexOfTag(uint16_t tag) const {
// TODO: Use binary search here.
for (size_t i = 0; i < size(); ++i) {
if (itemAt(i)->getTag() == tag) {
return i;
}
}
return -1;
}
int SortedEntryVector::do_compare(const void* lhs, const void* rhs) const {
const sp<TiffEntry>* lEntry = reinterpret_cast<const sp<TiffEntry>*>(lhs);
const sp<TiffEntry>* rEntry = reinterpret_cast<const sp<TiffEntry>*>(rhs);
return compare_type(**lEntry, **rEntry);
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,25 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/StripSource.h>
namespace android {
namespace img_utils {
StripSource::~StripSource() {}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,251 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/TiffIfd.h>
#include <img_utils/TiffHelpers.h>
#include <img_utils/TiffEntry.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
TiffEntry::~TiffEntry() {}
/**
* Specialize for each valid type, including sub-IFDs.
*
* Values with types other than the ones given here should not compile.
*/
template<>
const sp<TiffIfd>* TiffEntry::forceValidType<sp<TiffIfd> >(TagType type, const sp<TiffIfd>* value) {
if (type == LONG) {
return value;
}
ALOGE("%s: Value of type 'ifd' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const uint8_t* TiffEntry::forceValidType<uint8_t>(TagType type, const uint8_t* value) {
if (type == BYTE || type == ASCII || type == UNDEFINED) {
return value;
}
ALOGE("%s: Value of type 'uint8_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const int8_t* TiffEntry::forceValidType<int8_t>(TagType type, const int8_t* value) {
if (type == SBYTE || type == ASCII || type == UNDEFINED) {
return value;
}
ALOGE("%s: Value of type 'int8_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const uint16_t* TiffEntry::forceValidType<uint16_t>(TagType type, const uint16_t* value) {
if (type == SHORT) {
return value;
}
ALOGE("%s: Value of type 'uint16_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const int16_t* TiffEntry::forceValidType<int16_t>(TagType type, const int16_t* value) {
if (type == SSHORT) {
return value;
}
ALOGE("%s: Value of type 'int16_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const uint32_t* TiffEntry::forceValidType<uint32_t>(TagType type, const uint32_t* value) {
if (type == LONG || type == RATIONAL) {
return value;
}
ALOGE("%s: Value of type 'uint32_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const int32_t* TiffEntry::forceValidType<int32_t>(TagType type, const int32_t* value) {
if (type == SLONG || type == SRATIONAL) {
return value;
}
ALOGE("%s: Value of type 'int32_t' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const double* TiffEntry::forceValidType<double>(TagType type, const double* value) {
if (type == DOUBLE) {
return value;
}
ALOGE("%s: Value of type 'double' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
template<>
const float* TiffEntry::forceValidType<float>(TagType type, const float* value) {
if (type == FLOAT) {
return value;
}
ALOGE("%s: Value of type 'float' is not valid for tag with TIFF type %d.",
__FUNCTION__, type);
return NULL;
}
std::string TiffEntry::toString() const {
std::string output;
uint32_t count = getCount();
char buf[256] = { 0 };
snprintf(buf, sizeof(buf), "[id: %x, type: %d, count: %u, value: '", getTag(), getType(), count);
output.append(buf);
size_t cappedCount = count;
if (count > MAX_PRINT_STRING_LENGTH) {
cappedCount = MAX_PRINT_STRING_LENGTH;
}
TagType type = getType();
switch (type) {
case UNDEFINED:
case BYTE: {
const uint8_t* typed_data = getData<uint8_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case ASCII: {
const char* typed_data = reinterpret_cast<const char*>(getData<uint8_t>());
size_t len = count;
if (count > MAX_PRINT_STRING_LENGTH) {
len = MAX_PRINT_STRING_LENGTH;
}
output.append(typed_data, len);
break;
}
case SHORT: {
const uint16_t* typed_data = getData<uint16_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case LONG: {
const uint32_t* typed_data = getData<uint32_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case RATIONAL: {
const uint32_t* typed_data = getData<uint32_t>();
cappedCount <<= 1;
for (size_t i = 0; i < cappedCount; i+=2) {
output.append(std::to_string(typed_data[i]));
output.append("/");
output.append(std::to_string(typed_data[i + 1]));
output.append(" ");
}
break;
}
case SBYTE: {
const int8_t* typed_data = getData<int8_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case SSHORT: {
const int16_t* typed_data = getData<int16_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case SLONG: {
const int32_t* typed_data = getData<int32_t>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case SRATIONAL: {
const int32_t* typed_data = getData<int32_t>();
cappedCount <<= 1;
for (size_t i = 0; i < cappedCount; i+=2) {
output.append(std::to_string(typed_data[i]));
output.append("/");
output.append(std::to_string(typed_data[i + 1]));
output.append(" ");
}
break;
}
case FLOAT: {
const float* typed_data = getData<float>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
case DOUBLE: {
const double* typed_data = getData<double>();
for (size_t i = 0; i < cappedCount; ++i) {
output.append(std::to_string(typed_data[i]));
output.append(" ");
}
break;
}
default: {
output.append("unknown type ");
break;
}
}
if (count > MAX_PRINT_STRING_LENGTH) {
output.append("...");
}
output.append("']");
return output;
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,25 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/TiffEntryImpl.h>
// #include <utils/Vector.h>
namespace android {
namespace img_utils {
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,386 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "TiffIfd"
#include <img_utils/TagDefinitions.h>
#include <img_utils/TiffHelpers.h>
#include <img_utils/TiffIfd.h>
#include <img_utils/TiffWriter.h>
#include <utils/Log.h>
namespace android {
namespace img_utils {
TiffIfd::TiffIfd(uint32_t ifdId)
: mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
TiffIfd::~TiffIfd() {}
status_t TiffIfd::addEntry(const sp<TiffEntry>& entry) {
size_t size = mEntries.size();
if (size >= MAX_IFD_ENTRIES) {
ALOGW("%s: Failed to add entry for tag 0x%x to IFD %u, too many entries in IFD!",
__FUNCTION__, entry->getTag(), mIfdId);
return BAD_INDEX;
}
mEntries[entry->getTag()] = entry;
return OK;
}
sp<TiffEntry> TiffIfd::getEntry(uint16_t tag) const {
auto it = mEntries.find(tag);
if (it == mEntries.cend()) {
ALOGW("%s: No entry for tag 0x%x in ifd %u.", __FUNCTION__, tag, mIfdId);
return NULL;
}
return it->second;
}
void TiffIfd::removeEntry(uint16_t tag) {
std::map<uint16_t, sp<TiffEntry> >::iterator it = mEntries.find(tag);
if (it != mEntries.end()) {
mEntries.erase(it);
}
}
void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
mNextIfd = ifd;
}
sp<TiffIfd> TiffIfd::getNextIfd() const {
return mNextIfd;
}
uint32_t TiffIfd::checkAndGetOffset(uint32_t offset) const {
size_t size = mEntries.size();
if (size > MAX_IFD_ENTRIES) {
ALOGW("%s: Could not calculate IFD offsets, IFD %u contains too many entries.",
__FUNCTION__, mIfdId);
return BAD_OFFSET;
}
if (size <= 0) {
ALOGW("%s: Could not calculate IFD offsets, IFD %u contains no entries.", __FUNCTION__,
mIfdId);
return BAD_OFFSET;
}
if (offset == BAD_OFFSET) {
ALOGW("%s: Could not calculate IFD offsets, IFD %u had a bad initial offset.",
__FUNCTION__, mIfdId);
return BAD_OFFSET;
}
uint32_t ifdSize = calculateIfdSize(size);
WORD_ALIGN(ifdSize);
return offset + ifdSize;
}
status_t TiffIfd::writeData(uint32_t offset, /*out*/EndianOutput* out) const {
assert((offset % TIFF_WORD_SIZE) == 0);
status_t ret = OK;
ALOGV("%s: IFD %u written to offset %u", __FUNCTION__, mIfdId, offset );
uint32_t valueOffset = checkAndGetOffset(offset);
if (valueOffset == 0) {
return BAD_VALUE;
}
size_t size = mEntries.size();
// Writer IFD header (2 bytes, number of entries).
uint16_t header = static_cast<uint16_t>(size);
BAIL_ON_FAIL(out->write(&header, 0, 1), ret);
// Write tag entries
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
BAIL_ON_FAIL(it->second->writeTagInfo(valueOffset, out), ret);
valueOffset += it->second->getSize();
}
// Writer IFD footer (4 bytes, offset to next IFD).
uint32_t footer = (mNextIfd != NULL) ? offset + getSize() : 0;
BAIL_ON_FAIL(out->write(&footer, 0, 1), ret);
assert(out->getCurrentOffset() == offset + calculateIfdSize(size));
// Write zeroes till word aligned
ZERO_TILL_WORD(out, calculateIfdSize(size), ret);
// Write values for each tag entry
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
size_t last = out->getCurrentOffset();
// Only write values that are too large to fit in the 12-byte TIFF entry
if (it->second->getSize() > OFFSET_SIZE) {
BAIL_ON_FAIL(it->second->writeData(out->getCurrentOffset(), out), ret);
}
size_t next = out->getCurrentOffset();
size_t diff = (next - last);
size_t actual = it->second->getSize();
if (diff != actual) {
ALOGW("Sizes do not match for tag %x. Expected %zu, received %zu",
it->first, actual, diff);
}
}
assert(out->getCurrentOffset() == offset + getSize());
return ret;
}
size_t TiffIfd::getSize() const {
size_t size = mEntries.size();
uint32_t total = calculateIfdSize(size);
WORD_ALIGN(total);
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
total += it->second->getSize();
}
return total;
}
uint32_t TiffIfd::getId() const {
return mIfdId;
}
uint32_t TiffIfd::getComparableValue() const {
return mIfdId;
}
status_t TiffIfd::validateAndSetStripTags() {
sp<TiffEntry> widthEntry = getEntry(TAG_IMAGEWIDTH);
if (widthEntry == NULL) {
ALOGE("%s: IFD %u doesn't have a ImageWidth tag set", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
sp<TiffEntry> heightEntry = getEntry(TAG_IMAGELENGTH);
if (heightEntry == NULL) {
ALOGE("%s: IFD %u doesn't have a ImageLength tag set", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
sp<TiffEntry> samplesEntry = getEntry(TAG_SAMPLESPERPIXEL);
if (samplesEntry == NULL) {
ALOGE("%s: IFD %u doesn't have a SamplesPerPixel tag set", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
sp<TiffEntry> bitsEntry = getEntry(TAG_BITSPERSAMPLE);
if (bitsEntry == NULL) {
ALOGE("%s: IFD %u doesn't have a BitsPerSample tag set", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
uint32_t width = *(widthEntry->getData<uint32_t>());
uint32_t height = *(heightEntry->getData<uint32_t>());
uint16_t bitsPerSample = *(bitsEntry->getData<uint16_t>());
uint16_t samplesPerPixel = *(samplesEntry->getData<uint16_t>());
if ((bitsPerSample % 8) != 0) {
ALOGE("%s: BitsPerSample %d in IFD %u is not byte-aligned.", __FUNCTION__,
bitsPerSample, mIfdId);
return BAD_VALUE;
}
uint32_t bytesPerSample = bitsPerSample / 8;
// Choose strip size as close to 8kb as possible without splitting rows.
// If the row length is >8kb, each strip will only contain a single row.
const uint32_t rowLengthBytes = bytesPerSample * samplesPerPixel * width;
const uint32_t idealChunkSize = (1 << 13); // 8kb
uint32_t rowsPerChunk = idealChunkSize / rowLengthBytes;
rowsPerChunk = (rowsPerChunk == 0) ? 1 : rowsPerChunk;
const uint32_t actualChunkSize = rowLengthBytes * rowsPerChunk;
const uint32_t lastChunkRows = height % rowsPerChunk;
const uint32_t lastChunkSize = lastChunkRows * rowLengthBytes;
if (actualChunkSize > /*max strip size for TIFF/EP*/65536) {
ALOGE("%s: Strip length too long.", __FUNCTION__);
return BAD_VALUE;
}
size_t numStrips = height / rowsPerChunk;
// Add another strip for the incomplete chunk.
if (lastChunkRows > 0) {
numStrips += 1;
}
// Put each row in it's own strip
uint32_t rowsPerStripVal = rowsPerChunk;
sp<TiffEntry> rowsPerStrip = TiffWriter::uncheckedBuildEntry(TAG_ROWSPERSTRIP, LONG, 1,
UNDEFINED_ENDIAN, &rowsPerStripVal);
if (rowsPerStrip == NULL) {
ALOGE("%s: Could not build entry for RowsPerStrip tag.", __FUNCTION__);
return BAD_VALUE;
}
std::vector<uint32_t> byteCounts;
byteCounts.reserve(numStrips);
for (size_t i = 0; i < numStrips; ++i) {
if (lastChunkRows > 0 && i == (numStrips - 1)) {
byteCounts.push_back(lastChunkSize);
} else {
byteCounts.push_back(actualChunkSize);
}
}
// Set byte counts for each strip
sp<TiffEntry> stripByteCounts = TiffWriter::uncheckedBuildEntry(TAG_STRIPBYTECOUNTS, LONG,
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &byteCounts[0]);
if (stripByteCounts == NULL) {
ALOGE("%s: Could not build entry for StripByteCounts tag.", __FUNCTION__);
return BAD_VALUE;
}
std::vector<uint32_t> stripOffsetsVector;
stripOffsetsVector.resize(numStrips);
// Set uninitialized offsets
sp<TiffEntry> stripOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &stripOffsetsVector[0]);
if (stripOffsets == NULL) {
ALOGE("%s: Could not build entry for StripOffsets tag.", __FUNCTION__);
return BAD_VALUE;
}
if(addEntry(stripByteCounts) != OK) {
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
if(addEntry(rowsPerStrip) != OK) {
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
if(addEntry(stripOffsets) != OK) {
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
mStripOffsetsInitialized = true;
return OK;
}
bool TiffIfd::uninitializedOffsets() const {
return mStripOffsetsInitialized;
}
status_t TiffIfd::setStripOffset(uint32_t offset) {
// Get old offsets and bytecounts
sp<TiffEntry> oldOffsets = getEntry(TAG_STRIPOFFSETS);
if (oldOffsets == NULL) {
ALOGE("%s: IFD %u does not contain StripOffsets entry.", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
if (stripByteCounts == NULL) {
ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
uint32_t offsetsCount = oldOffsets->getCount();
uint32_t byteCount = stripByteCounts->getCount();
if (offsetsCount != byteCount) {
ALOGE("%s: StripOffsets count (%u) doesn't match StripByteCounts count (%u) in IFD %u",
__FUNCTION__, offsetsCount, byteCount, mIfdId);
return BAD_VALUE;
}
const uint32_t* stripByteCountsArray = stripByteCounts->getData<uint32_t>();
size_t numStrips = offsetsCount;
std::vector<uint32_t> stripOffsets;
stripOffsets.reserve(numStrips);
// Calculate updated byte offsets
for (size_t i = 0; i < numStrips; ++i) {
stripOffsets.push_back(offset);
offset += stripByteCountsArray[i];
}
sp<TiffEntry> newOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &stripOffsets[0]);
if (newOffsets == NULL) {
ALOGE("%s: Coult not build updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
if (addEntry(newOffsets) != OK) {
ALOGE("%s: Failed to add updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
return OK;
}
uint32_t TiffIfd::getStripSize() const {
sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
if (stripByteCounts == NULL) {
ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
return BAD_VALUE;
}
uint32_t count = stripByteCounts->getCount();
const uint32_t* byteCounts = stripByteCounts->getData<uint32_t>();
uint32_t total = 0;
for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
total += byteCounts[i];
}
return total;
}
std::string TiffIfd::toString() const {
size_t s = mEntries.size();
std::string output;
char buf[1024] = { 0 };
snprintf(buf, sizeof(buf), "[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
output.append(buf);
for(auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
output.append("\t");
output.append(it->second->toString());
output.append("\n");
}
output.append(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
return output;
}
void TiffIfd::log() const {
size_t s = mEntries.size();
ALOGI("[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
for(auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
ALOGI("\t%s", it->second->toString().c_str());
}
ALOGI(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,31 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <img_utils/TiffWritable.h>
#include <img_utils/TiffHelpers.h>
#include <assert.h>
namespace android {
namespace img_utils {
TiffWritable::TiffWritable() {}
TiffWritable::~TiffWritable() {}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,425 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "TiffWriter"
#include <img_utils/TiffHelpers.h>
#include <img_utils/TiffWriter.h>
#include <img_utils/TagDefinitions.h>
#include <assert.h>
namespace android {
namespace img_utils {
#if 0
KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::buildTagMap(
const TagDefinition_t* definitions, size_t length) {
KeyedVector<uint16_t, const TagDefinition_t*> map;
for(size_t i = 0; i < length; ++i) {
map.add(definitions[i].tagId, definitions + i);
}
return map;
}
#endif
#define COMPARE(op) \
bool Orderable::operator op (const Orderable& orderable) const { \
return getComparableValue() op orderable.getComparableValue(); \
}
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof((array)[0]))
#if 0
KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::sTagMaps[] = {
buildTagMap(TIFF_EP_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_EP_TAG_DEFINITIONS)),
buildTagMap(DNG_TAG_DEFINITIONS, ARRAY_SIZE(DNG_TAG_DEFINITIONS)),
buildTagMap(EXIF_2_3_TAG_DEFINITIONS, ARRAY_SIZE(EXIF_2_3_TAG_DEFINITIONS)),
buildTagMap(TIFF_6_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_6_TAG_DEFINITIONS))
};
#endif
TiffWriter::TiffWriter() : mNumTagMaps(DEFAULT_NUM_TAG_MAPS)
{
mTagMaps.reserve(DEFAULT_NUM_TAG_MAPS);
// = new KeyedVector<uint16_t, const TagDefinition_t*>[DEFAULT_NUM_TAG_MAPS];
std::vector<std::map<uint16_t, const TagDefinition_t*> >::iterator it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
for(size_t i = 0; i < ARRAY_SIZE(TIFF_EP_TAG_DEFINITIONS); ++i) {
(*it)[TIFF_EP_TAG_DEFINITIONS[i].tagId] = TIFF_EP_TAG_DEFINITIONS + i;
}
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
for(size_t i = 0; i < ARRAY_SIZE(DNG_TAG_DEFINITIONS); ++i) {
(*it)[DNG_TAG_DEFINITIONS[i].tagId] = DNG_TAG_DEFINITIONS + i;
}
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
for(size_t i = 0; i < ARRAY_SIZE(EXIF_2_3_TAG_DEFINITIONS); ++i) {
(*it)[EXIF_2_3_TAG_DEFINITIONS[i].tagId] = EXIF_2_3_TAG_DEFINITIONS + i;
}
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
for(size_t i = 0; i < ARRAY_SIZE(TIFF_6_TAG_DEFINITIONS); ++i) {
(*it)[TIFF_6_TAG_DEFINITIONS[i].tagId] = TIFF_6_TAG_DEFINITIONS + i;
}
}
TiffWriter::TiffWriter(std::map<uint16_t, const TagDefinition_t*>* enabledDefinitions,
size_t length) : mNumTagMaps(length)
{
mTagMaps.reserve(length);
for (int i = 0; i < length; ++i)
{
auto it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
for(auto it2 = enabledDefinitions[i].cbegin(); it2 != enabledDefinitions[i].cend(); ++it2) {
(*it)[it2->first] = it2->second;
}
}
}
TiffWriter::~TiffWriter() {}
status_t TiffWriter::write(Output* out, StripSource** sources, size_t sourcesCount,
Endianness end) {
status_t ret = OK;
EndianOutput endOut(out, end);
if (mIfd == NULL) {
ALOGE("%s: Tiff header is empty.", __FUNCTION__);
return BAD_VALUE;
}
uint32_t totalSize = getTotalSize();
std::map<uint32_t, uint32_t> offsetVector;
for (std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.begin(); it != mNamedIfds.end(); ++it) {
if (it->second->uninitializedOffsets()) {
uint32_t stripSize = it->second->getStripSize();
if (it->second->setStripOffset(totalSize) != OK) {
ALOGE("%s: Could not set strip offsets.", __FUNCTION__);
return BAD_VALUE;
}
totalSize += stripSize;
WORD_ALIGN(totalSize);
offsetVector[it->first] = totalSize;
}
}
size_t offVecSize = offsetVector.size();
if (offVecSize != sourcesCount) {
ALOGE("%s: Mismatch between number of IFDs with uninitialized strips (%zu) and"
" sources (%zu).", __FUNCTION__, offVecSize, sourcesCount);
return BAD_VALUE;
}
BAIL_ON_FAIL(writeFileHeader(endOut), ret);
uint32_t offset = FILE_HEADER_SIZE;
sp<TiffIfd> ifd = mIfd;
while(ifd != NULL) {
BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
offset += ifd->getSize();
ifd = ifd->getNextIfd();
}
#ifndef NDEBUG
log();
#endif
for (auto it = offsetVector.begin(); it != offsetVector.end(); ++it) {
uint32_t ifdKey = it->first;
uint32_t sizeToWrite = mNamedIfds[ifdKey]->getStripSize();
bool found = false;
for (size_t j = 0; j < sourcesCount; ++j) {
if (sources[j]->getIfd() == ifdKey) {
int i = std::distance(offsetVector.begin(), it);
if ((ret = sources[i]->writeToStream(endOut, sizeToWrite)) != OK) {
ALOGE("%s: Could not write to stream, received %d.", __FUNCTION__, ret);
return ret;
}
ZERO_TILL_WORD(&endOut, sizeToWrite, ret);
found = true;
break;
}
}
if (!found) {
ALOGE("%s: No stream for byte strips for IFD %u", __FUNCTION__, ifdKey);
return BAD_VALUE;
}
assert(it->second == endOut.getCurrentOffset());
}
return ret;
}
status_t TiffWriter::write(Output* out, Endianness end) {
status_t ret = OK;
EndianOutput endOut(out, end);
if (mIfd == NULL) {
ALOGE("%s: Tiff header is empty.", __FUNCTION__);
return BAD_VALUE;
}
BAIL_ON_FAIL(writeFileHeader(endOut), ret);
uint32_t offset = FILE_HEADER_SIZE;
sp<TiffIfd> ifd = mIfd;
while(ifd != NULL) {
BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
offset += ifd->getSize();
ifd = ifd->getNextIfd();
}
return ret;
}
const TagDefinition_t* TiffWriter::lookupDefinition(uint16_t tag) const {
const TagDefinition_t* definition = NULL;
for (size_t i = 0; i < mNumTagMaps; ++i) {
auto it = mTagMaps[i].find(tag);
if (it != mTagMaps[i].cend()) {
definition = it->second;
break;
}
}
if (definition == NULL) {
ALOGE("%s: No definition exists for tag with id %x.", __FUNCTION__, tag);
}
return definition;
}
sp<TiffEntry> TiffWriter::getEntry(uint16_t tag, uint32_t ifd) const {
auto it = mNamedIfds.find(ifd);
if (it == mNamedIfds.cend()) {
ALOGE("%s: No IFD %d set for this writer.", __FUNCTION__, ifd);
return NULL;
}
return it->second->getEntry(tag);
}
void TiffWriter::removeEntry(uint16_t tag, uint32_t ifd) {
auto it = mNamedIfds.find(ifd);
if (it != mNamedIfds.end()) {
it->second->removeEntry(tag);
}
}
status_t TiffWriter::addEntry(const sp<TiffEntry>& entry, uint32_t ifd) {
uint16_t tag = entry->getTag();
const TagDefinition_t* definition = lookupDefinition(tag);
if (definition == NULL) {
ALOGE("%s: No definition exists for tag 0x%x.", __FUNCTION__, tag);
return BAD_INDEX;
}
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
// Add a new IFD if necessary
if (it == mNamedIfds.end()) {
ALOGE("%s: No IFD %u exists.", __FUNCTION__, ifd);
return NAME_NOT_FOUND;
}
sp<TiffIfd> selectedIfd = it->second;
return selectedIfd->addEntry(entry);
}
status_t TiffWriter::addStrip(uint32_t ifd) {
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
if (it == mNamedIfds.end()) {
ALOGE("%s: Ifd %u doesn't exist, cannot add strip entries.", __FUNCTION__, ifd);
return BAD_VALUE;
}
sp<TiffIfd> selected = it->second;
return selected->validateAndSetStripTags();
}
status_t TiffWriter::addIfd(uint32_t ifd) {
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
if (it != mNamedIfds.end()) {
ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
return BAD_VALUE;
}
sp<TiffIfd> newIfd = new TiffIfd(ifd);
if (mIfd == NULL) {
mIfd = newIfd;
} else {
sp<TiffIfd> last = findLastIfd();
last->setNextIfd(newIfd);
}
mNamedIfds[ifd] = newIfd;
return OK;
}
status_t TiffWriter::addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type) {
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
if (it != mNamedIfds.end()) {
ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
return BAD_VALUE;
}
std::map<uint32_t, sp<TiffIfd> >::iterator parentIt = mNamedIfds.find(parentIfd);
if (parentIt == mNamedIfds.end()) {
ALOGE("%s: Parent IFD with ID 0x%x does not exist.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
sp<TiffIfd> parent = parentIt->second;
sp<TiffIfd> newIfd = new TiffIfd(ifd);
uint16_t subIfdTag;
if (type == SUBIFD) {
subIfdTag = TAG_SUBIFDS;
} else if (type == GPSINFO) {
subIfdTag = TAG_GPSINFO;
} else {
ALOGE("%s: Unknown SubIFD type %d.", __FUNCTION__, type);
return BAD_VALUE;
}
sp<TiffEntry> subIfds = parent->getEntry(subIfdTag);
if (subIfds == NULL) {
if (buildEntry(subIfdTag, 1, &newIfd, &subIfds) < 0) {
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
} else {
if (type == GPSINFO) {
ALOGE("%s: Cannot add GPSInfo SubIFD to IFD %u, one already exists.", __FUNCTION__,
ifd);
return BAD_VALUE;
}
std::vector<sp<TiffIfd> > subIfdList;
const sp<TiffIfd>* oldIfdArray = subIfds->getData<sp<TiffIfd> >();
subIfdList.insert(subIfdList.end(), oldIfdArray, oldIfdArray + subIfds->getCount());
#if 0
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
#endif
subIfdList.push_back(newIfd); // < 0) {
#if 0
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
#endif
uint32_t count = subIfdList.size();
if (buildEntry(subIfdTag, count, &subIfdList[0], &subIfds) < 0) {
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
}
if (parent->addEntry(subIfds) < 0) {
ALOGE("%s: Failed to add SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
return BAD_VALUE;
}
mNamedIfds[ifd] = newIfd;
return OK;
}
TagType TiffWriter::getDefaultType(uint16_t tag) const {
const TagDefinition_t* definition = lookupDefinition(tag);
if (definition == NULL) {
ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
return UNKNOWN_TAGTYPE;
}
return definition->defaultType;
}
uint32_t TiffWriter::getDefaultCount(uint16_t tag) const {
const TagDefinition_t* definition = lookupDefinition(tag);
if (definition == NULL) {
ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
return 0;
}
return definition->fixedCount;
}
bool TiffWriter::hasIfd(uint32_t ifd) const {
auto it = mNamedIfds.find(ifd);
return it != mNamedIfds.cend();
}
bool TiffWriter::checkIfDefined(uint16_t tag) const {
return lookupDefinition(tag) != NULL;
}
const char* TiffWriter::getTagName(uint16_t tag) const {
const TagDefinition_t* definition = lookupDefinition(tag);
if (definition == NULL) {
return NULL;
}
return definition->tagName;
}
sp<TiffIfd> TiffWriter::findLastIfd() {
sp<TiffIfd> ifd = mIfd;
while(ifd != NULL) {
sp<TiffIfd> nextIfd = ifd->getNextIfd();
if (nextIfd == NULL) {
break;
}
ifd = std::move(nextIfd);
}
return ifd;
}
status_t TiffWriter::writeFileHeader(EndianOutput& out) {
status_t ret = OK;
uint16_t endMarker = (out.getEndianness() == BIG) ? BIG_ENDIAN_MARKER : LITTLE_ENDIAN_MARKER;
BAIL_ON_FAIL(out.write(&endMarker, 0, 1), ret);
uint16_t tiffMarker = TIFF_FILE_MARKER;
BAIL_ON_FAIL(out.write(&tiffMarker, 0, 1), ret);
uint32_t offsetMarker = FILE_HEADER_SIZE;
BAIL_ON_FAIL(out.write(&offsetMarker, 0, 1), ret);
return ret;
}
uint32_t TiffWriter::getTotalSize() const {
uint32_t totalSize = FILE_HEADER_SIZE;
sp<TiffIfd> ifd = mIfd;
while(ifd != NULL) {
totalSize += ifd->getSize();
ifd = ifd->getNextIfd();
}
return totalSize;
}
void TiffWriter::log() const {
ALOGI("%s: TiffWriter:", __FUNCTION__);
size_t length = mNamedIfds.size();
for (auto it = mNamedIfds.begin(); it != mNamedIfds.end(); ++it) {
it->second->log();
}
}
} /*namespace img_utils*/
} /*namespace android*/

@ -0,0 +1,53 @@
/*
* Copyright 2011, 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 <cutils/android_reboot.h>
#include <stdio.h>
#include <stdlib.h>
#include <cutils/properties.h>
#define TAG "android_reboot"
int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
int ret;
const char* restart_cmd = NULL;
char* prop_value;
switch (cmd) {
case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
restart_cmd = "reboot";
break;
case ANDROID_RB_POWEROFF:
restart_cmd = "shutdown";
break;
case ANDROID_RB_THERMOFF:
restart_cmd = "shutdown,thermal";
break;
}
if (!restart_cmd) return -1;
if (arg && arg[0]) {
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
} else {
ret = asprintf(&prop_value, "%s", restart_cmd);
}
if (ret < 0) return -1;
ret = property_set(ANDROID_RB_PROPERTY, prop_value);
free(prop_value);
return ret;
}

@ -0,0 +1,28 @@
/*
* Copyright (C) 2010 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.
*/
#if defined(__slm__)
/* Values are optimized for Silvermont */
#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
#else
/* Values are optimized for Atom */
#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
#endif
#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)

@ -0,0 +1,22 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Values are optimized for Silvermont */
#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)

@ -0,0 +1,468 @@
/*
* Copyright (C) 2008 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 <cutils/ashmem.h>
/*
* Implementation of the user-space ashmem API for devices, which have our
* ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
* used by the simulator.
*/
#define LOG_TAG "ashmem"
#include <errno.h>
#include <fcntl.h>
#include <linux/ashmem.h>
#include <linux/memfd.h>
#include <log/log.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
/* Will be added to UAPI once upstream change is merged */
#define F_SEAL_FUTURE_WRITE 0x0010
/*
* The minimum vendor API level at and after which it is safe to use memfd.
* This is to facilitate deprecation of ashmem.
*/
#define MIN_MEMFD_VENDOR_API_LEVEL 29
#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
/* ashmem identity */
static dev_t __ashmem_rdev;
/*
* If we trigger a signal handler in the middle of locked activity and the
* signal handler calls ashmem, we could get into a deadlock state.
*/
static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* has_memfd_support() determines if the device can use memfd. memfd support
* has been there for long time, but certain things in it may be missing. We
* check for needed support in it. Also we check if the VNDK version of
* libcutils being used is new enough, if its not, then we cannot use memfd
* since the older copies may be using ashmem so we just use ashmem. Once all
* Android devices that are getting updates are new enough (ex, they were
* originally shipped with Android release > P), then we can just use memfd and
* delete all ashmem code from libcutils (while preserving the interface).
*
* NOTE:
* The sys.use_memfd property is set by default to false in Android
* to temporarily disable memfd, till vendor and apps are ready for it.
* The main issue: either apps or vendor processes can directly make ashmem
* IOCTLs on FDs they receive by assuming they are ashmem, without going
* through libcutils. Such fds could have very well be originally created with
* libcutils hence they could be memfd. Thus the IOCTLs will break.
*
* Set default value of sys.use_memfd property to true once the issue is
* resolved, so that the code can then self-detect if kernel support is present
* on the device. The property can also set to true from adb shell, for
* debugging.
*/
static bool debug_log = false; /* set to true for verbose logging and other debug */
static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
/* Determine if vendor processes would be ok with memfd in the system:
*
* If VNDK is using older libcutils, don't use memfd. This is so that the
* same shared memory mechanism is used across binder transactions between
* vendor partition processes and system partition processes.
*/
static bool check_vendor_memfd_allowed() {
std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
if (vndk_version == "") {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
return false;
}
/* No issues if vendor is targetting current Dessert */
if (vndk_version == "current") {
return false;
}
/* Check if VNDK version is a number and act on it */
char* p;
long int vers = strtol(vndk_version.c_str(), &p, 10);
if (*p == 0) {
if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
vndk_version.c_str());
return false;
}
return true;
}
/* If its not a number, assume string, but check if its a sane string */
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
return false;
}
if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
vndk_version.c_str());
return false;
}
return true;
}
/* Determine if memfd can be supported. This is just one-time hardwork
* which will be cached by the caller.
*/
static bool __has_memfd_support() {
if (check_vendor_memfd_allowed() == false) {
return false;
}
/* Used to turn on/off the detection at runtime, in the future this
* property will be removed once we switch everything over to ashmem.
* Currently it is used only for debugging to switch the system over.
*/
if (!android::base::GetBoolProperty("sys.use_memfd", false)) {
if (debug_log) {
ALOGD("sys.use_memfd=false so memfd disabled\n");
}
return false;
}
/* Check if kernel support exists, otherwise fall back to ashmem */
android::base::unique_fd fd(
syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
return false;
}
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
ALOGE("fcntl(F_ADD_SEALS) failed: %s, no memfd support.\n", strerror(errno));
return false;
}
if (debug_log) {
ALOGD("memfd: device has memfd support, using it\n");
}
return true;
}
static bool has_memfd_support() {
/* memfd_supported is the initial global per-process state of what is known
* about memfd.
*/
static bool memfd_supported = __has_memfd_support();
return memfd_supported;
}
static std::string get_ashmem_device_path() {
static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
std::string boot_id;
if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {
ALOGE("Failed to read %s: %s.\n", boot_id_path.c_str(), strerror(errno));
return "";
};
boot_id = android::base::Trim(boot_id);
return "/dev/ashmem" + boot_id;
}
/* logistics of getting file descriptor for ashmem */
static int __ashmem_open_locked()
{
static const std::string ashmem_device_path = get_ashmem_device_path();
if (ashmem_device_path.empty()) {
return -1;
}
int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
// fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
if (fd < 0) {
fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
}
if (fd < 0) {
return fd;
}
struct stat st;
int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
if (ret < 0) {
int save_errno = errno;
close(fd);
errno = save_errno;
return ret;
}
if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
close(fd);
errno = ENOTTY;
return -1;
}
__ashmem_rdev = st.st_rdev;
return fd;
}
static int __ashmem_open()
{
int fd;
pthread_mutex_lock(&__ashmem_lock);
fd = __ashmem_open_locked();
pthread_mutex_unlock(&__ashmem_lock);
return fd;
}
/* Make sure file descriptor references ashmem, negative number means false */
static int __ashmem_is_ashmem(int fd, int fatal)
{
dev_t rdev;
struct stat st;
if (fstat(fd, &st) < 0) {
return -1;
}
rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
if (S_ISCHR(st.st_mode) && st.st_rdev) {
pthread_mutex_lock(&__ashmem_lock);
rdev = __ashmem_rdev;
if (rdev) {
pthread_mutex_unlock(&__ashmem_lock);
} else {
int fd = __ashmem_open_locked();
if (fd < 0) {
pthread_mutex_unlock(&__ashmem_lock);
return -1;
}
rdev = __ashmem_rdev;
pthread_mutex_unlock(&__ashmem_lock);
close(fd);
}
if (st.st_rdev == rdev) {
return 0;
}
}
if (fatal) {
if (rdev) {
LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
major(rdev), minor(rdev));
} else {
LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
}
/* NOTREACHED */
}
errno = ENOTTY;
return -1;
}
static int __ashmem_check_failure(int fd, int result)
{
if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
return result;
}
static bool memfd_is_ashmem(int fd) {
static bool fd_check_error_once = false;
if (__ashmem_is_ashmem(fd, 0) == 0) {
if (!fd_check_error_once) {
ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils.\n");
fd_check_error_once = true;
}
return true;
}
return false;
}
int ashmem_valid(int fd)
{
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
return 1;
}
return __ashmem_is_ashmem(fd, 0) >= 0;
}
static int memfd_create_region(const char* name, size_t size) {
android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
return -1;
}
if (ftruncate(fd, size) == -1) {
ALOGE("ftruncate(%s, %zd) failed for memfd creation: %s\n", name, size, strerror(errno));
return -1;
}
if (debug_log) {
ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
}
return fd.release();
}
/*
* ashmem_create_region - creates a new ashmem region and returns the file
* descriptor, or <0 on error
*
* `name' is an optional label to give the region (visible in /proc/pid/maps)
* `size' is the size of the region, in page-aligned bytes
*/
int ashmem_create_region(const char *name, size_t size)
{
int ret, save_errno;
if (has_memfd_support()) {
return memfd_create_region(name ? name : "none", size);
}
int fd = __ashmem_open();
if (fd < 0) {
return fd;
}
if (name) {
char buf[ASHMEM_NAME_LEN] = {0};
strlcpy(buf, name, sizeof(buf));
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
if (ret < 0) {
goto error;
}
}
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
if (ret < 0) {
goto error;
}
return fd;
error:
save_errno = errno;
close(fd);
errno = save_errno;
return ret;
}
static int memfd_set_prot_region(int fd, int prot) {
/* Only proceed if an fd needs to be write-protected */
if (prot & PROT_WRITE) {
return 0;
}
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
strerror(errno));
return -1;
}
return 0;
}
int ashmem_set_prot_region(int fd, int prot)
{
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
return memfd_set_prot_region(fd, prot);
}
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
}
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
if (!pin_deprecation_warn || debug_log) {
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
pin_deprecation_warn = true;
}
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
return 0;
}
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
if (!pin_deprecation_warn || debug_log) {
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
pin_deprecation_warn = true;
}
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
return 0;
}
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
}
int ashmem_get_size_region(int fd)
{
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
struct stat sb;
if (fstat(fd, &sb) == -1) {
ALOGE("ashmem_get_size_region(%d): fstat failed: %s\n", fd, strerror(errno));
return -1;
}
if (debug_log) {
ALOGD("ashmem_get_size_region(%d): %d\n", fd, static_cast<int>(sb.st_size));
}
return sb.st_size;
}
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
}

@ -0,0 +1,96 @@
/*
* Copyright (C) 2008 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 <cutils/ashmem.h>
/*
* Implementation of the user-space ashmem API for the simulator, which lacks
* an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
*/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utils/Compat.h>
static bool ashmem_validate_stat(int fd, struct stat* buf) {
int result = fstat(fd, buf);
if (result == -1) {
return false;
}
/*
* Check if this is an "ashmem" region.
* TODO: This is very hacky, and can easily break.
* We need some reliable indicator.
*/
if (!(buf->st_nlink == 0 && S_ISREG(buf->st_mode))) {
errno = ENOTTY;
return false;
}
return true;
}
int ashmem_valid(int fd) {
struct stat buf;
return ashmem_validate_stat(fd, &buf);
}
int ashmem_create_region(const char* /*ignored*/, size_t size) {
char pattern[PATH_MAX];
snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
int fd = mkstemp(pattern);
if (fd == -1) return -1;
unlink(pattern);
if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
close(fd);
return -1;
}
return fd;
}
int ashmem_set_prot_region(int /*fd*/, int /*prot*/) {
return 0;
}
int ashmem_pin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
return 0 /*ASHMEM_NOT_PURGED*/;
}
int ashmem_unpin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
return 0 /*ASHMEM_IS_UNPINNED*/;
}
int ashmem_get_size_region(int fd)
{
struct stat buf;
if (!ashmem_validate_stat(fd, &buf)) {
return -1;
}
return buf.st_size;
}

@ -0,0 +1,128 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <private/android_filesystem_config.h>
#include <private/canned_fs_config.h>
#include <private/fs_config.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char* path;
unsigned uid;
unsigned gid;
unsigned mode;
uint64_t capabilities;
} Path;
static Path* canned_data = NULL;
static int canned_alloc = 0;
static int canned_used = 0;
static int path_compare(const void* a, const void* b) {
return strcmp(((Path*)a)->path, ((Path*)b)->path);
}
int load_canned_fs_config(const char* fn) {
char buf[PATH_MAX + 200];
FILE* f;
f = fopen(fn, "r");
if (f == NULL) {
fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
Path* p;
char* token;
char* line = buf;
bool rootdir;
while (canned_used >= canned_alloc) {
canned_alloc = (canned_alloc+1) * 2;
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
}
p = canned_data + canned_used;
if (line[0] == '/') line++;
rootdir = line[0] == ' ';
p->path = strdup(rootdir ? "" : strtok(line, " "));
p->uid = atoi(strtok(rootdir ? line : NULL, " "));
p->gid = atoi(strtok(NULL, " "));
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
p->capabilities = 0;
do {
token = strtok(NULL, " ");
if (token && strncmp(token, "capabilities=", 13) == 0) {
p->capabilities = strtoll(token+13, NULL, 0);
break;
}
} while (token);
canned_used++;
}
fclose(f);
qsort(canned_data, canned_used, sizeof(Path), path_compare);
printf("loaded %d fs_config entries\n", canned_used);
return 0;
}
static const int kDebugCannedFsConfig = 0;
void canned_fs_config(const char* path, int dir, const char* target_out_path,
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
Path key, *p;
key.path = path;
if (path[0] == '/') key.path++; // canned paths lack the leading '/'
p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
if (p == NULL) {
fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
exit(1);
}
*uid = p->uid;
*gid = p->gid;
*mode = p->mode;
*capabilities = p->capabilities;
if (kDebugCannedFsConfig) {
// for debugging, run the built-in fs_config and compare the results.
unsigned c_uid, c_gid, c_mode;
uint64_t c_capabilities;
fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
if (c_capabilities != *capabilities) {
printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
path,
*capabilities,
c_capabilities);
}
}
}

@ -0,0 +1,328 @@
/*
* Copyright (C) 2007 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 <cutils/config_utils.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <cutils/misc.h>
cnode* config_node(const char *name, const char *value)
{
cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
if(node) {
node->name = name ? name : "";
node->value = value ? value : "";
}
return node;
}
cnode* config_find(cnode *root, const char *name)
{
cnode *node, *match = NULL;
/* we walk the whole list, as we need to return the last (newest) entry */
for(node = root->first_child; node; node = node->next)
if(!strcmp(node->name, name))
match = node;
return match;
}
static cnode* _config_create(cnode *root, const char *name)
{
cnode *node;
node = config_node(name, NULL);
if(root->last_child)
root->last_child->next = node;
else
root->first_child = node;
root->last_child = node;
return node;
}
int config_bool(cnode *root, const char *name, int _default)
{
cnode *node;
node = config_find(root, name);
if(!node)
return _default;
switch(node->value[0]) {
case 'y':
case 'Y':
case '1':
return 1;
default:
return 0;
}
}
const char* config_str(cnode *root, const char *name, const char *_default)
{
cnode *node;
node = config_find(root, name);
if(!node)
return _default;
return node->value;
}
void config_set(cnode *root, const char *name, const char *value)
{
cnode *node;
node = config_find(root, name);
if(node)
node->value = value;
else {
node = _config_create(root, name);
node->value = value;
}
}
#define T_EOF 0
#define T_TEXT 1
#define T_DOT 2
#define T_OBRACE 3
#define T_CBRACE 4
typedef struct
{
char *data;
char *text;
int len;
char next;
} cstate;
static int _lex(cstate *cs, int value)
{
char c;
char *s;
char *data;
data = cs->data;
if(cs->next != 0) {
c = cs->next;
cs->next = 0;
goto got_c;
}
restart:
for(;;) {
c = *data++;
got_c:
if(isspace(c))
continue;
switch(c) {
case 0:
return T_EOF;
case '#':
for(;;) {
switch(*data) {
case 0:
cs->data = data;
return T_EOF;
case '\n':
cs->data = data + 1;
goto restart;
default:
data++;
}
}
break;
case '.':
cs->data = data;
return T_DOT;
case '{':
cs->data = data;
return T_OBRACE;
case '}':
cs->data = data;
return T_CBRACE;
default:
s = data - 1;
if(value) {
for(;;) {
if(*data == 0) {
cs->data = data;
break;
}
if(*data == '\n') {
cs->data = data + 1;
*data-- = 0;
break;
}
data++;
}
/* strip trailing whitespace */
while(data > s){
if(!isspace(*data)) break;
*data-- = 0;
}
goto got_text;
} else {
for(;;) {
if(isspace(*data)) {
*data = 0;
cs->data = data + 1;
goto got_text;
}
switch(*data) {
case 0:
cs->data = data;
goto got_text;
case '.':
case '{':
case '}':
cs->next = *data;
*data = 0;
cs->data = data + 1;
goto got_text;
default:
data++;
}
}
}
}
}
got_text:
cs->text = s;
return T_TEXT;
}
#if 0
char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
static int lex(cstate *cs, int value)
{
int tok = _lex(cs, value);
printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
tok == T_TEXT ? cs->text : "");
return tok;
}
#else
#define lex(cs,v) _lex(cs,v)
#endif
static int parse_expr(cstate *cs, cnode *node);
static int parse_block(cstate *cs, cnode *node)
{
for(;;){
switch(lex(cs, 0)){
case T_TEXT:
if(parse_expr(cs, node)) return -1;
continue;
case T_CBRACE:
return 0;
default:
return -1;
}
}
}
static int parse_expr(cstate *cs, cnode *root)
{
cnode *node;
/* last token was T_TEXT */
node = config_find(root, cs->text);
if(!node || *node->value)
node = _config_create(root, cs->text);
for(;;) {
switch(lex(cs, 1)) {
case T_DOT:
if(lex(cs, 0) != T_TEXT)
return -1;
node = _config_create(node, cs->text);
continue;
case T_TEXT:
node->value = cs->text;
return 0;
case T_OBRACE:
return parse_block(cs, node);
default:
return -1;
}
}
}
void config_load(cnode *root, char *data)
{
if(data != 0) {
cstate cs;
cs.data = data;
cs.next = 0;
for(;;) {
switch(lex(&cs, 0)) {
case T_TEXT:
if(parse_expr(&cs, root))
return;
break;
default:
return;
}
}
}
}
void config_load_file(cnode *root, const char *fn)
{
char* data = static_cast<char*>(load_file(fn, nullptr));
config_load(root, data);
// TODO: deliberate leak :-/
}
void config_free(cnode *root)
{
cnode *cur = root->first_child;
while (cur) {
cnode *prev = cur;
config_free(cur);
cur = cur->next;
free(prev);
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 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_CUTILS_COMPILER_H
#define ANDROID_CUTILS_COMPILER_H
/*
* helps the compiler's optimizer predicting branches
*/
#ifdef __cplusplus
# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
#else
# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
#endif
/**
* exports marked symbols
*
* if used on a C++ class declaration, this macro must be inserted
* after the "class" keyword. For instance:
*
* template <typename TYPE>
* class ANDROID_API Singleton { }
*/
#define ANDROID_API __attribute__((visibility("default")))
#endif // ANDROID_CUTILS_COMPILER_H

@ -0,0 +1,65 @@
// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
// <log/log.h> has replaced this file and all changes should go there instead.
// This path remains strictly to include that header as there are thousands of
// references to <utils/Log.h> in the tree.
// #include <log/log.h>
#include <jni.h>
#include <android/log.h>
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#define LOG_TAG "MPLOG"
#define ALOGV(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,__VA_ARGS__)
#define android_printAssert(cond, tag, ...) \
__android_log_assert(cond, tag, \
__android_second(0, ##__VA_ARGS__, NULL) \
__android_rest(__VA_ARGS__))
#define __FAKE_USE_VA_ARGS(...) ((void)(0))
#ifndef LOG_ALWAYS_FATAL_IF
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \
((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
: ((void)0))
#endif
#ifndef LOG_ALWAYS_FATAL
#define LOG_ALWAYS_FATAL(...) \
(((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
#endif
#if NDEBUG
#ifndef LOG_FATAL_IF
#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
#endif
#ifndef LOG_FATAL
#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
#endif
#else
#ifndef LOG_FATAL_IF
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
#endif
#ifndef LOG_FATAL
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
#endif
#endif

@ -0,0 +1,144 @@
/*
* Copyright (C) 2005 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 "sharedbuffer"
#include "SharedBuffer.h"
#include <stdlib.h>
#include <string.h>
#include <log/log.h>
// ---------------------------------------------------------------------------
namespace android {
SharedBuffer* SharedBuffer::alloc(size_t size)
{
// Don't overflow if the combined size of the buffer / header is larger than
// size_max.
#if 0
LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))),
"Invalid buffer size %zu", size);
#endif
SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
if (sb) {
// Should be std::atomic_init(&sb->mRefs, 1);
// But that generates a warning with some compilers.
// The following is OK on Android-supported platforms.
sb->mRefs.store(1, std::memory_order_relaxed);
sb->mSize = size;
sb->mClientMetadata = 0;
}
return sb;
}
void SharedBuffer::dealloc(const SharedBuffer* released)
{
free(const_cast<SharedBuffer*>(released));
}
SharedBuffer* SharedBuffer::edit() const
{
if (onlyOwner()) {
return const_cast<SharedBuffer*>(this);
}
SharedBuffer* sb = alloc(mSize);
if (sb) {
memcpy(sb->data(), data(), size());
release();
}
return sb;
}
SharedBuffer* SharedBuffer::editResize(size_t newSize) const
{
if (onlyOwner()) {
SharedBuffer* buf = const_cast<SharedBuffer*>(this);
if (buf->mSize == newSize) return buf;
// Don't overflow if the combined size of the new buffer / header is larger than
// size_max.
#if 0
LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))),
"Invalid buffer size %zu", newSize);
#endif
buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
if (buf != nullptr) {
buf->mSize = newSize;
return buf;
}
}
SharedBuffer* sb = alloc(newSize);
if (sb) {
const size_t mySize = mSize;
memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
release();
}
return sb;
}
SharedBuffer* SharedBuffer::attemptEdit() const
{
if (onlyOwner()) {
return const_cast<SharedBuffer*>(this);
}
return nullptr;
}
SharedBuffer* SharedBuffer::reset(size_t new_size) const
{
// cheap-o-reset.
SharedBuffer* sb = alloc(new_size);
if (sb) {
release();
}
return sb;
}
void SharedBuffer::acquire() const {
mRefs.fetch_add(1, std::memory_order_relaxed);
}
int32_t SharedBuffer::release(uint32_t flags) const
{
const bool useDealloc = ((flags & eKeepStorage) == 0);
if (onlyOwner()) {
// Since we're the only owner, our reference count goes to zero.
mRefs.store(0, std::memory_order_relaxed);
if (useDealloc) {
dealloc(this);
}
// As the only owner, our previous reference count was 1.
return 1;
}
// There's multiple owners, we need to use an atomic decrement.
int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release);
if (prevRefCount == 1) {
// We're the last reference, we need the acquire fence.
atomic_thread_fence(std::memory_order_acquire);
if (useDealloc) {
dealloc(this);
}
}
return prevRefCount;
}
}; // namespace android

@ -0,0 +1,151 @@
/*
* Copyright (C) 2005 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.
*/
/*
* DEPRECATED. DO NOT USE FOR NEW CODE.
*/
#ifndef ANDROID_SHARED_BUFFER_H
#define ANDROID_SHARED_BUFFER_H
#include <atomic>
#include <stdint.h>
#include <sys/types.h>
// ---------------------------------------------------------------------------
namespace android {
class SharedBuffer
{
public:
/* flags to use with release() */
enum {
eKeepStorage = 0x00000001
};
/*! allocate a buffer of size 'size' and acquire() it.
* call release() to free it.
*/
static SharedBuffer* alloc(size_t size);
/*! free the memory associated with the SharedBuffer.
* Fails if there are any users associated with this SharedBuffer.
* In other words, the buffer must have been release by all its
* users.
*/
static void dealloc(const SharedBuffer* released);
//! access the data for read
inline const void* data() const;
//! access the data for read/write
inline void* data();
//! get size of the buffer
inline size_t size() const;
//! get back a SharedBuffer object from its data
static inline SharedBuffer* bufferFromData(void* data);
//! get back a SharedBuffer object from its data
static inline const SharedBuffer* bufferFromData(const void* data);
//! get the size of a SharedBuffer object from its data
static inline size_t sizeFromData(const void* data);
//! edit the buffer (get a writtable, or non-const, version of it)
SharedBuffer* edit() const;
//! edit the buffer, resizing if needed
SharedBuffer* editResize(size_t size) const;
//! like edit() but fails if a copy is required
SharedBuffer* attemptEdit() const;
//! resize and edit the buffer, loose it's content.
SharedBuffer* reset(size_t size) const;
//! acquire/release a reference on this buffer
void acquire() const;
/*! release a reference on this buffer, with the option of not
* freeing the memory associated with it if it was the last reference
* returns the previous reference count
*/
int32_t release(uint32_t flags = 0) const;
//! returns wether or not we're the only owner
inline bool onlyOwner() const;
private:
inline SharedBuffer() { }
inline ~SharedBuffer() { }
SharedBuffer(const SharedBuffer&);
SharedBuffer& operator = (const SharedBuffer&);
// Must be sized to preserve correct alignment.
mutable std::atomic<int32_t> mRefs;
size_t mSize;
uint32_t mReserved;
public:
// mClientMetadata is reserved for client use. It is initialized to 0
// and the clients can do whatever they want with it. Note that this is
// placed last so that it is adjcent to the buffer allocated.
uint32_t mClientMetadata;
};
static_assert(sizeof(SharedBuffer) % 8 == 0
&& (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16),
"SharedBuffer has unexpected size");
// ---------------------------------------------------------------------------
const void* SharedBuffer::data() const {
return this + 1;
}
void* SharedBuffer::data() {
return this + 1;
}
size_t SharedBuffer::size() const {
return mSize;
}
SharedBuffer* SharedBuffer::bufferFromData(void* data) {
return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;
}
const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;
}
size_t SharedBuffer::sizeFromData(const void* data) {
return data ? bufferFromData(data)->mSize : 0;
}
bool SharedBuffer::onlyOwner() const {
return (mRefs.load(std::memory_order_acquire) == 1);
}
} // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_VECTOR_H

@ -0,0 +1,37 @@
/*
* 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.
*/
#define LOG_TAG "sp"
#include <log/log.h>
namespace android {
void sp_report_race()
{
#if 0
LOG_ALWAYS_FATAL("sp<> assignment detected data race");
#endif
}
void sp_report_stack_pointer()
{
#if 0
LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument");
#endif
}
}

@ -0,0 +1,85 @@
/*
* Copyright (C) 2007 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.
*/
#pragma once
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <string>
namespace android {
/**
* The type used to return success/failure from frameworks APIs.
* See the anonymous enum below for valid values.
*/
typedef int32_t status_t;
/*
* Error codes.
* All error codes are negative values.
*/
// Win32 #defines NO_ERROR as well. It has the same value, so there's no
// real conflict, though it's a bit awkward.
#ifdef _WIN32
# undef NO_ERROR
#endif
enum {
OK = 0, // Preferred constant for checking success.
NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
NO_MEMORY = -ENOMEM,
INVALID_OPERATION = -ENOSYS,
BAD_VALUE = -EINVAL,
BAD_TYPE = (UNKNOWN_ERROR + 1),
NAME_NOT_FOUND = -ENOENT,
PERMISSION_DENIED = -EPERM,
NO_INIT = -ENODEV,
ALREADY_EXISTS = -EEXIST,
DEAD_OBJECT = -EPIPE,
FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
#if !defined(_WIN32)
BAD_INDEX = -EOVERFLOW,
NOT_ENOUGH_DATA = -ENODATA,
WOULD_BLOCK = -EWOULDBLOCK,
TIMED_OUT = -ETIMEDOUT,
UNKNOWN_TRANSACTION = -EBADMSG,
#else
BAD_INDEX = -E2BIG,
NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
WOULD_BLOCK = (UNKNOWN_ERROR + 4),
TIMED_OUT = (UNKNOWN_ERROR + 5),
UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
#endif
FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
};
// Human readable name of error
std::string statusToString(status_t status);
// Restore define; enumeration is in "android" namespace, so the value defined
// there won't work for Win32 code in a different namespace.
#ifdef _WIN32
# define NO_ERROR 0L
#endif
} // namespace android

@ -0,0 +1,70 @@
/*
* 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.
*/
#pragma once
/*
* See documentation in RefBase.h
*/
#include <atomic>
#include <sys/types.h>
namespace android {
class ReferenceRenamer;
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
mCount.fetch_add(1, std::memory_order_relaxed);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
delete static_cast<const T*>(this);
}
}
//! DEBUGGING ONLY: Get current strong ref count.
inline int32_t getStrongCount() const {
return mCount.load(std::memory_order_relaxed);
}
protected:
inline ~LightRefBase() { }
private:
friend class ReferenceMover;
inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
private:
mutable std::atomic<int32_t> mCount;
};
// This is a wrapper around LightRefBase that simply enforces a virtual
// destructor to eliminate the template requirement of LightRefBase
class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
public:
virtual ~VirtualLightRefBase() = default;
};
} // namespace android

@ -0,0 +1,9 @@
// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
// <log/log.h> has replaced this file and all changes should go there instead.
// This path remains strictly to include that header as there are thousands of
// references to <utils/Log.h> in the tree.
// #include <log/log.h>
#include <android/log.h>
#include <AndroidHelper.h>

@ -0,0 +1,713 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// SOME COMMENTS ABOUT USAGE:
// This provides primarily wp<> weak pointer types and RefBase, which work
// together with sp<> from <StrongPointer.h>.
// sp<> (and wp<>) are a type of smart pointer that use a well defined protocol
// to operate. As long as the object they are templated with implements that
// protocol, these smart pointers work. In several places the platform
// instantiates sp<> with non-RefBase objects; the two are not tied to each
// other.
// RefBase is such an implementation and it supports strong pointers, weak
// pointers and some magic features for the binder.
// So, when using RefBase objects, you have the ability to use strong and weak
// pointers through sp<> and wp<>.
// Normally, when the last strong pointer goes away, the object is destroyed,
// i.e. it's destructor is called. HOWEVER, parts of its associated memory is not
// freed until the last weak pointer is released.
// Weak pointers are essentially "safe" pointers. They are always safe to
// access through promote(). They may return nullptr if the object was
// destroyed because it ran out of strong pointers. This makes them good candidates
// for keys in a cache for instance.
// Weak pointers remain valid for comparison purposes even after the underlying
// object has been destroyed. Even if object A is destroyed and its memory reused
// for B, A remaining weak pointer to A will not compare equal to one to B.
// This again makes them attractive for use as keys.
// How is this supposed / intended to be used?
// Our recommendation is to use strong references (sp<>) when there is an
// ownership relation. e.g. when an object "owns" another one, use a strong
// ref. And of course use strong refs as arguments of functions (it's extremely
// rare that a function will take a wp<>).
// Typically a newly allocated object will immediately be used to initialize
// a strong pointer, which may then be used to construct or assign to other
// strong and weak pointers.
// Use weak references when there are no ownership relation. e.g. the keys in a
// cache (you cannot use plain pointers because there is no safe way to acquire
// a strong reference from a vanilla pointer).
// This implies that two objects should never (or very rarely) have sp<> on
// each other, because they can't both own each other.
// Caveats with reference counting
// Obviously, circular strong references are a big problem; this creates leaks
// and it's hard to debug -- except it's in fact really easy because RefBase has
// tons of debugging code for that. It can basically tell you exactly where the
// leak is.
// Another problem has to do with destructors with side effects. You must
// assume that the destructor of reference counted objects can be called AT ANY
// TIME. For instance code as simple as this:
// void setStuff(const sp<Stuff>& stuff) {
// std::lock_guard<std::mutex> lock(mMutex);
// mStuff = stuff;
// }
// is very dangerous. This code WILL deadlock one day or another.
// What isn't obvious is that ~Stuff() can be called as a result of the
// assignment. And it gets called with the lock held. First of all, the lock is
// protecting mStuff, not ~Stuff(). Secondly, if ~Stuff() uses its own internal
// mutex, now you have mutex ordering issues. Even worse, if ~Stuff() is
// virtual, now you're calling into "user" code (potentially), by that, I mean,
// code you didn't even write.
// A correct way to write this code is something like:
// void setStuff(const sp<Stuff>& stuff) {
// std::unique_lock<std::mutex> lock(mMutex);
// sp<Stuff> hold = mStuff;
// mStuff = stuff;
// lock.unlock();
// }
// More importantly, reference counted objects should do as little work as
// possible in their destructor, or at least be mindful that their destructor
// could be called from very weird and unintended places.
// Other more specific restrictions for wp<> and sp<>:
// Do not construct a strong pointer to "this" in an object's constructor.
// The onFirstRef() callback would be made on an incompletely constructed
// object.
// Construction of a weak pointer to "this" in an object's constructor is also
// discouraged. But the implementation was recently changed so that, in the
// absence of extendObjectLifetime() calls, weak pointers no longer impact
// object lifetime, and hence this no longer risks premature deallocation,
// and hence usually works correctly.
// Such strong or weak pointers can be safely created in the RefBase onFirstRef()
// callback.
// Use of wp::unsafe_get() for any purpose other than debugging is almost
// always wrong. Unless you somehow know that there is a longer-lived sp<> to
// the same object, it may well return a pointer to a deallocated object that
// has since been reallocated for a different purpose. (And if you know there
// is a longer-lived sp<>, why not use an sp<> directly?) A wp<> should only be
// dereferenced by using promote().
// Any object inheriting from RefBase should always be destroyed as the result
// of a reference count decrement, not via any other means. Such objects
// should never be stack allocated, or appear directly as data members in other
// objects. Objects inheriting from RefBase should have their strong reference
// count incremented as soon as possible after construction. Usually this
// will be done via construction of an sp<> to the object, but may instead
// involve other means of calling RefBase::incStrong().
// Explicitly deleting or otherwise destroying a RefBase object with outstanding
// wp<> or sp<> pointers to it will result in an abort or heap corruption.
// It is particularly important not to mix sp<> and direct storage management
// since the sp from raw pointer constructor is implicit. Thus if a RefBase-
// -derived object of type T is managed without ever incrementing its strong
// count, and accidentally passed to f(sp<T>), a strong pointer to the object
// will be temporarily constructed and destroyed, prematurely deallocating the
// object, and resulting in heap corruption. None of this would be easily
// visible in the source.
// Extra Features:
// RefBase::extendObjectLifetime() can be used to prevent destruction of the
// object while there are still weak references. This is really special purpose
// functionality to support Binder.
// Wp::promote(), implemented via the attemptIncStrong() member function, is
// used to try to convert a weak pointer back to a strong pointer. It's the
// normal way to try to access the fields of an object referenced only through
// a wp<>. Binder code also sometimes uses attemptIncStrong() directly.
// RefBase provides a number of additional callbacks for certain reference count
// events, as well as some debugging facilities.
// Debugging support can be enabled by turning on DEBUG_REFS in RefBase.cpp.
// Otherwise little checking is provided.
// Thread safety:
// Like std::shared_ptr, sp<> and wp<> allow concurrent accesses to DIFFERENT
// sp<> and wp<> instances that happen to refer to the same underlying object.
// They do NOT support concurrent access (where at least one access is a write)
// to THE SAME sp<> or wp<>. In effect, their thread-safety properties are
// exactly like those of T*, NOT atomic<T*>.
#ifndef ANDROID_REF_BASE_H
#define ANDROID_REF_BASE_H
#include <atomic>
#include <functional>
#include <type_traits> // for common_type.
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
// LightRefBase used to be declared in this header, so we have to include it
#include <utils/LightRefBase.h>
#include <utils/StrongPointer.h>
#include <utils/TypeHelpers.h>
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
template<typename U> \
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
} \
/* Needed to handle type inference for nullptr: */ \
inline bool operator _op_ (const T* o) const { \
return m_ptr _op_ o; \
}
template<template<typename C> class comparator, typename T, typename U>
static inline bool _wp_compare_(T* a, U* b) {
return comparator<typename std::common_type<T*, U*>::type>()(a, b);
}
// Use std::less and friends to avoid undefined behavior when ordering pointers
// to different objects.
#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_) \
template<typename U> \
inline bool operator _op_ (const U* o) const { \
return _wp_compare_<_compare_>(m_ptr, o); \
}
// ---------------------------------------------------------------------------
// RefererenceRenamer is pure abstract, there is no virtual method
// implementation to put in a translation unit in order to silence the
// weak vtables warning.
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables"
#endif
class ReferenceRenamer {
protected:
// destructor is purposely not virtual so we avoid code overhead from
// subclasses; we have to make it protected to guarantee that it
// cannot be called from this base class (and to make strict compilers
// happy).
~ReferenceRenamer() { }
public:
virtual void operator()(size_t i) const = 0;
};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
// ---------------------------------------------------------------------------
class RefBase
{
public:
void incStrong(const void* id) const;
void decStrong(const void* id) const;
void forceIncStrong(const void* id) const;
//! DEBUGGING ONLY: Get current strong ref count.
int32_t getStrongCount() const;
class weakref_type
{
public:
RefBase* refBase() const;
void incWeak(const void* id);
void decWeak(const void* id);
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);
// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
bool attemptIncWeak(const void* id);
//! DEBUGGING ONLY: Get current weak ref count.
int32_t getWeakCount() const;
//! DEBUGGING ONLY: Print references held on object.
void printRefs() const;
//! DEBUGGING ONLY: Enable tracking for this object.
// enable -- enable/disable tracking
// retain -- when tracking is enable, if true, then we save a stack trace
// for each reference and dereference; when retain == false, we
// match up references and dereferences and keep only the
// outstanding ones.
void trackMe(bool enable, bool retain);
};
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
//! DEBUGGING ONLY: Print references held on object.
inline void printRefs() const { getWeakRefs()->printRefs(); }
//! DEBUGGING ONLY: Enable tracking of object.
inline void trackMe(bool enable, bool retain)
{
getWeakRefs()->trackMe(enable, retain);
}
protected:
RefBase();
virtual ~RefBase();
//! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
void extendObjectLifetime(int32_t mode);
//! Flags for onIncStrongAttempted()
enum {
FIRST_INC_STRONG = 0x0001
};
// Invoked after creation of initial strong pointer/reference.
virtual void onFirstRef();
// Invoked when either the last strong reference goes away, or we need to undo
// the effect of an unnecessary onIncStrongAttempted.
virtual void onLastStrongRef(const void* id);
// Only called in OBJECT_LIFETIME_WEAK case. Returns true if OK to promote to
// strong reference. May have side effects if it returns true.
// The first flags argument is always FIRST_INC_STRONG.
// TODO: Remove initial flag argument.
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
// Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either
// kind goes away. Unused.
// TODO: Remove.
virtual void onLastWeakRef(const void* id);
private:
friend class weakref_type;
class weakref_impl;
RefBase(const RefBase& o);
RefBase& operator=(const RefBase& o);
private:
friend class ReferenceMover;
static void renameRefs(size_t n, const ReferenceRenamer& renamer);
static void renameRefId(weakref_type* ref,
const void* old_id, const void* new_id);
static void renameRefId(RefBase* ref,
const void* old_id, const void* new_id);
weakref_impl* const mRefs;
};
// ---------------------------------------------------------------------------
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
wp(T* other); // NOLINT(implicit)
wp(const wp<T>& other);
explicit wp(const sp<T>& other);
template<typename U> wp(U* other); // NOLINT(implicit)
template<typename U> wp(const sp<U>& other); // NOLINT(implicit)
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
~wp();
// Assignment
wp& operator = (T* other);
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
sp<T> promote() const;
// Reset
void clear();
// Accessors
inline weakref_type* get_refs() const { return m_refs; }
inline T* unsafe_get() const { return m_ptr; }
// Operators
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
COMPARE_WEAK_FUNCTIONAL(>, std::greater)
COMPARE_WEAK_FUNCTIONAL(<, std::less)
COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
template<typename U>
inline bool operator == (const wp<U>& o) const {
return m_refs == o.m_refs; // Implies m_ptr == o.mptr; see invariants below.
}
template<typename U>
inline bool operator == (const sp<U>& o) const {
// Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
// object at the same address.
if (o == nullptr) {
return m_ptr == nullptr;
} else {
return m_refs == o->getWeakRefs(); // Implies m_ptr == o.mptr.
}
}
template<typename U>
inline bool operator != (const sp<U>& o) const {
return !(*this == o);
}
template<typename U>
inline bool operator > (const wp<U>& o) const {
if (m_ptr == o.m_ptr) {
return _wp_compare_<std::greater>(m_refs, o.m_refs);
} else {
return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
}
}
template<typename U>
inline bool operator < (const wp<U>& o) const {
if (m_ptr == o.m_ptr) {
return _wp_compare_<std::less>(m_refs, o.m_refs);
} else {
return _wp_compare_<std::less>(m_ptr, o.m_ptr);
}
}
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
T* m_ptr;
weakref_type* m_refs;
};
#undef COMPARE_WEAK
#undef COMPARE_WEAK_FUNCTIONAL
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
// Implementation invariants:
// Either
// 1) m_ptr and m_refs are both null, or
// 2) m_refs == m_ptr->mRefs, or
// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
// to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
//
// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
// null or point to the same object. If two wp's have identical m_ptr fields, they either both
// point to the same live object and thus have the same m_ref fields, or at least one of the
// objects is no longer live.
//
// Note that the above comparison operations go out of their way to provide an ordering consistent
// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
template<typename T>
wp<T>::wp(const wp<T>& other)
: m_ptr(other.m_ptr), m_refs(other.m_refs)
{
if (m_ptr) m_refs->incWeak(this);
}
template<typename T>
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
wp<T>::wp(U* other)
: m_ptr(other)
{
m_refs = other ? other->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
wp<T>::wp(const wp<U>& other)
: m_ptr(other.m_ptr)
{
if (m_ptr) {
m_refs = other.m_refs;
m_refs->incWeak(this);
} else {
m_refs = nullptr;
}
}
template<typename T> template<typename U>
wp<T>::wp(const sp<U>& other)
: m_ptr(other.m_ptr)
{
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
template<typename T>
wp<T>& wp<T>::operator = (T* other)
{
weakref_type* newRefs =
other ? other->createWeak(this) : nullptr;
if (m_ptr) m_refs->decWeak(this);
m_ptr = other;
m_refs = newRefs;
return *this;
}
template<typename T>
wp<T>& wp<T>::operator = (const wp<T>& other)
{
weakref_type* otherRefs(other.m_refs);
T* otherPtr(other.m_ptr);
if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
m_ptr = otherPtr;
m_refs = otherRefs;
return *this;
}
template<typename T>
wp<T>& wp<T>::operator = (const sp<T>& other)
{
weakref_type* newRefs =
other != nullptr ? other->createWeak(this) : nullptr;
T* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
template<typename T> template<typename U>
wp<T>& wp<T>::operator = (U* other)
{
weakref_type* newRefs =
other ? other->createWeak(this) : 0;
if (m_ptr) m_refs->decWeak(this);
m_ptr = other;
m_refs = newRefs;
return *this;
}
template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const wp<U>& other)
{
weakref_type* otherRefs(other.m_refs);
U* otherPtr(other.m_ptr);
if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
m_ptr = otherPtr;
m_refs = otherRefs;
return *this;
}
template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const sp<U>& other)
{
weakref_type* newRefs =
other != nullptr ? other->createWeak(this) : 0;
U* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
template<typename T>
void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
{
if (other) refs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
m_ptr = other;
m_refs = refs;
}
template<typename T>
sp<T> wp<T>::promote() const
{
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
return result;
}
template<typename T>
void wp<T>::clear()
{
if (m_ptr) {
m_refs->decWeak(this);
m_refs = 0;
m_ptr = 0;
}
}
// ---------------------------------------------------------------------------
// this class just serves as a namespace so TYPE::moveReferences can stay
// private.
class ReferenceMover {
public:
// it would be nice if we could make sure no extra code is generated
// for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
// Using a sp<RefBase> override doesn't work; it's a bit like we wanted
// a template<typename TYPE inherits RefBase> template...
template<typename TYPE> static inline
void move_references(sp<TYPE>* dest, sp<TYPE> const* src, size_t n) {
class Renamer : public ReferenceRenamer {
sp<TYPE>* d_;
sp<TYPE> const* s_;
virtual void operator()(size_t i) const {
// The id are known to be the sp<>'s this pointer
TYPE::renameRefId(d_[i].get(), &s_[i], &d_[i]);
}
public:
Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d_(d), s_(s) { }
virtual ~Renamer() { }
};
memmove(dest, src, n*sizeof(sp<TYPE>));
TYPE::renameRefs(n, Renamer(dest, src));
}
template<typename TYPE> static inline
void move_references(wp<TYPE>* dest, wp<TYPE> const* src, size_t n) {
class Renamer : public ReferenceRenamer {
wp<TYPE>* d_;
wp<TYPE> const* s_;
virtual void operator()(size_t i) const {
// The id are known to be the wp<>'s this pointer
TYPE::renameRefId(d_[i].get_refs(), &s_[i], &d_[i]);
}
public:
Renamer(wp<TYPE>* rd, wp<TYPE> const* rs) : d_(rd), s_(rs) { }
virtual ~Renamer() { }
};
memmove(dest, src, n*sizeof(wp<TYPE>));
TYPE::renameRefs(n, Renamer(dest, src));
}
};
// specialization for moving sp<> and wp<> types.
// these are used by the [Sorted|Keyed]Vector<> implementations
// sp<> and wp<> need to be handled specially, because they do not
// have trivial copy operation in the general case (see RefBase.cpp
// when DEBUG ops are enabled), but can be implemented very
// efficiently in most cases.
template<typename TYPE> inline
void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
ReferenceMover::move_references(d, s, n);
}
template<typename TYPE> inline
void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
ReferenceMover::move_references(d, s, n);
}
template<typename TYPE> inline
void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
ReferenceMover::move_references(d, s, n);
}
template<typename TYPE> inline
void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
ReferenceMover::move_references(d, s, n);
}
} // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_REF_BASE_H

@ -0,0 +1,317 @@
/*
* Copyright (C) 2005 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_STRONG_POINTER_H
#define ANDROID_STRONG_POINTER_H
#include <functional>
#include <type_traits> // for common_type.
// ---------------------------------------------------------------------------
namespace android {
template<typename T> class wp;
// ---------------------------------------------------------------------------
template<typename T>
class sp {
public:
inline sp() : m_ptr(nullptr) { }
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
~sp();
// Assignment
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear();
// Accessors
inline T& operator* () const { return *m_ptr; }
inline T* operator-> () const { return m_ptr; }
inline T* get() const { return m_ptr; }
inline explicit operator bool () const { return m_ptr != nullptr; }
// Punt these to the wp<> implementation.
template<typename U>
inline bool operator == (const wp<U>& o) const {
return o == *this;
}
template<typename U>
inline bool operator != (const wp<U>& o) const {
return o != *this;
}
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
static inline void check_not_on_stack(const void* ptr);
T* m_ptr;
};
#define COMPARE_STRONG(_op_) \
template <typename T, typename U> \
static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
return t.get() _op_ u.get(); \
} \
template <typename T, typename U> \
static inline bool operator _op_(const T* t, const sp<U>& u) { \
return t _op_ u.get(); \
} \
template <typename T, typename U> \
static inline bool operator _op_(const sp<T>& t, const U* u) { \
return t.get() _op_ u; \
} \
template <typename T> \
static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
return t.get() _op_ nullptr; \
} \
template <typename T> \
static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
return nullptr _op_ t.get(); \
}
template <template <typename C> class comparator, typename T, typename U>
static inline bool _sp_compare_(T* a, U* b) {
return comparator<typename std::common_type<T*, U*>::type>()(a, b);
}
#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
template <typename T, typename U> \
static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
return _sp_compare_<_compare_>(t.get(), u.get()); \
} \
template <typename T, typename U> \
static inline bool operator _op_(const T* t, const sp<U>& u) { \
return _sp_compare_<_compare_>(t, u.get()); \
} \
template <typename T, typename U> \
static inline bool operator _op_(const sp<T>& t, const U* u) { \
return _sp_compare_<_compare_>(t.get(), u); \
} \
template <typename T> \
static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
return _sp_compare_<_compare_>(t.get(), nullptr); \
} \
template <typename T> \
static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
return _sp_compare_<_compare_>(nullptr, t.get()); \
}
COMPARE_STRONG(==)
COMPARE_STRONG(!=)
COMPARE_STRONG_FUNCTIONAL(>, std::greater)
COMPARE_STRONG_FUNCTIONAL(<, std::less)
COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
#undef COMPARE_STRONG
#undef COMPARE_STRONG_FUNCTIONAL
// For code size reasons, we do not want these inlined or templated.
void sp_report_race();
void sp_report_stack_pointer();
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
// Check whether address is definitely on the calling stack. We actually check whether it is on
// the same 4K page as the frame pointer.
//
// Assumptions:
// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
// - Malloced memory never shares a page with a stack.
//
// It does not appear safe to broaden this check to include adjacent pages; apparently this code
// is used in environments where there may not be a guard page below (at higher addresses than)
// the bottom of the stack.
//
// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
// without checking overhead.
template <typename T>
void sp<T>::check_not_on_stack(const void* ptr) {
static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
uintptr_t my_frame_address =
reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
sp_report_stack_pointer();
}
}
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other) {
check_not_on_stack(other);
other->incStrong(this);
}
}
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
m_ptr->incStrong(this);
}
template <typename T>
sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
other.m_ptr = nullptr;
}
template<typename T> template<typename U>
sp<T>::sp(U* other)
: m_ptr(other) {
if (other) {
check_not_on_stack(other);
(static_cast<T*>(other))->incStrong(this);
}
}
template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
m_ptr->incStrong(this);
}
template<typename T> template<typename U>
sp<T>::sp(sp<U>&& other)
: m_ptr(other.m_ptr) {
other.m_ptr = nullptr;
}
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
template<typename T>
sp<T>& sp<T>::operator =(const sp<T>& other) {
// Force m_ptr to be read twice, to heuristically check for data races.
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
T* otherPtr(other.m_ptr);
if (otherPtr) otherPtr->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = otherPtr;
return *this;
}
template <typename T>
sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
return *this;
}
template<typename T>
sp<T>& sp<T>::operator =(T* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (other) {
check_not_on_stack(other);
other->incStrong(this);
}
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = other;
return *this;
}
template<typename T> template<typename U>
sp<T>& sp<T>::operator =(const sp<U>& other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
T* otherPtr(other.m_ptr);
if (otherPtr) otherPtr->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = otherPtr;
return *this;
}
template<typename T> template<typename U>
sp<T>& sp<T>::operator =(sp<U>&& other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (m_ptr) m_ptr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
return *this;
}
template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (other) (static_cast<T*>(other))->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = other;
return *this;
}
template<typename T>
void sp<T>::force_set(T* other) {
other->forceIncStrong(this);
m_ptr = other;
}
template<typename T>
void sp<T>::clear() {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (oldPtr) {
oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = nullptr;
}
}
template<typename T>
void sp<T>::set_pointer(T* ptr) {
m_ptr = ptr;
}
} // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_STRONG_POINTER_H

@ -0,0 +1,336 @@
/*
* Copyright (C) 2005 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_TYPE_HELPERS_H
#define ANDROID_TYPE_HELPERS_H
#include <new>
#include <type_traits>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
// ---------------------------------------------------------------------------
namespace android {
/*
* Types traits
*/
template <typename T> struct trait_trivial_ctor { enum { value = false }; };
template <typename T> struct trait_trivial_dtor { enum { value = false }; };
template <typename T> struct trait_trivial_copy { enum { value = false }; };
template <typename T> struct trait_trivial_move { enum { value = false }; };
template <typename T> struct trait_pointer { enum { value = false }; };
template <typename T> struct trait_pointer<T*> { enum { value = true }; };
template <typename TYPE>
struct traits {
enum {
// whether this type is a pointer
is_pointer = trait_pointer<TYPE>::value,
// whether this type's constructor is a no-op
has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
// whether this type's destructor is a no-op
has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
// whether this type type can be copy-constructed with memcpy
has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
// whether this type can be moved with memmove
has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
};
};
template <typename T, typename U>
struct aggregate_traits {
enum {
is_pointer = false,
has_trivial_ctor =
traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
has_trivial_dtor =
traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
has_trivial_copy =
traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
has_trivial_move =
traits<T>::has_trivial_move && traits<U>::has_trivial_move
};
};
#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
template<> struct trait_trivial_ctor< T > { enum { value = true }; };
#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
template<> struct trait_trivial_dtor< T > { enum { value = true }; };
#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
template<> struct trait_trivial_copy< T > { enum { value = true }; };
#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
template<> struct trait_trivial_move< T > { enum { value = true }; };
#define ANDROID_BASIC_TYPES_TRAITS( T ) \
ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
ANDROID_TRIVIAL_COPY_TRAIT( T ) \
ANDROID_TRIVIAL_MOVE_TRAIT( T )
// ---------------------------------------------------------------------------
/*
* basic types traits
*/
ANDROID_BASIC_TYPES_TRAITS( void )
ANDROID_BASIC_TYPES_TRAITS( bool )
ANDROID_BASIC_TYPES_TRAITS( char )
ANDROID_BASIC_TYPES_TRAITS( unsigned char )
ANDROID_BASIC_TYPES_TRAITS( short )
ANDROID_BASIC_TYPES_TRAITS( unsigned short )
ANDROID_BASIC_TYPES_TRAITS( int )
ANDROID_BASIC_TYPES_TRAITS( unsigned int )
ANDROID_BASIC_TYPES_TRAITS( long )
ANDROID_BASIC_TYPES_TRAITS( unsigned long )
ANDROID_BASIC_TYPES_TRAITS( long long )
ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
ANDROID_BASIC_TYPES_TRAITS( float )
ANDROID_BASIC_TYPES_TRAITS( double )
// ---------------------------------------------------------------------------
/*
* compare and order types
*/
template<typename TYPE> inline
int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
return (lhs < rhs) ? 1 : 0;
}
template<typename TYPE> inline
int compare_type(const TYPE& lhs, const TYPE& rhs) {
return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
}
/*
* create, destroy, copy and move types...
*/
template<typename TYPE> inline
void construct_type(TYPE* p, size_t n) {
if (!traits<TYPE>::has_trivial_ctor) {
while (n > 0) {
n--;
new(p++) TYPE;
}
}
}
template<typename TYPE> inline
void destroy_type(TYPE* p, size_t n) {
if (!traits<TYPE>::has_trivial_dtor) {
while (n > 0) {
n--;
p->~TYPE();
p++;
}
}
}
template<typename TYPE>
typename std::enable_if<traits<TYPE>::has_trivial_copy>::type
inline
copy_type(TYPE* d, const TYPE* s, size_t n) {
memcpy(d,s,n*sizeof(TYPE));
}
template<typename TYPE>
typename std::enable_if<!traits<TYPE>::has_trivial_copy>::type
inline
copy_type(TYPE* d, const TYPE* s, size_t n) {
while (n > 0) {
n--;
new(d) TYPE(*s);
d++, s++;
}
}
template<typename TYPE> inline
void splat_type(TYPE* where, const TYPE* what, size_t n) {
if (!traits<TYPE>::has_trivial_copy) {
while (n > 0) {
n--;
new(where) TYPE(*what);
where++;
}
} else {
while (n > 0) {
n--;
*where++ = *what;
}
}
}
template<typename TYPE>
struct use_trivial_move : public std::integral_constant<bool,
(traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
|| traits<TYPE>::has_trivial_move
> {};
template<typename TYPE>
typename std::enable_if<use_trivial_move<TYPE>::value>::type
inline
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
memmove(d, s, n*sizeof(TYPE));
}
template<typename TYPE>
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
inline
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
d += n;
s += n;
while (n > 0) {
n--;
--d, --s;
if (!traits<TYPE>::has_trivial_copy) {
new(d) TYPE(*s);
} else {
*d = *s;
}
if (!traits<TYPE>::has_trivial_dtor) {
s->~TYPE();
}
}
}
template<typename TYPE>
typename std::enable_if<use_trivial_move<TYPE>::value>::type
inline
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
memmove(d, s, n*sizeof(TYPE));
}
template<typename TYPE>
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
inline
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
while (n > 0) {
n--;
if (!traits<TYPE>::has_trivial_copy) {
new(d) TYPE(*s);
} else {
*d = *s;
}
if (!traits<TYPE>::has_trivial_dtor) {
s->~TYPE();
}
d++, s++;
}
}
// ---------------------------------------------------------------------------
/*
* a key/value pair
*/
template <typename KEY, typename VALUE>
struct key_value_pair_t {
typedef KEY key_t;
typedef VALUE value_t;
KEY key;
VALUE value;
key_value_pair_t() { }
key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
key_value_pair_t& operator=(const key_value_pair_t& o) {
key = o.key;
value = o.value;
return *this;
}
key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
explicit key_value_pair_t(const KEY& k) : key(k) { }
inline bool operator < (const key_value_pair_t& o) const {
return strictly_order_type(key, o.key);
}
inline const KEY& getKey() const {
return key;
}
inline const VALUE& getValue() const {
return value;
}
};
template <typename K, typename V>
struct trait_trivial_ctor< key_value_pair_t<K, V> >
{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
template <typename K, typename V>
struct trait_trivial_dtor< key_value_pair_t<K, V> >
{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
template <typename K, typename V>
struct trait_trivial_copy< key_value_pair_t<K, V> >
{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
template <typename K, typename V>
struct trait_trivial_move< key_value_pair_t<K, V> >
{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
// ---------------------------------------------------------------------------
/*
* Hash codes.
*/
typedef uint32_t hash_t;
template <typename TKey>
hash_t hash_type(const TKey& key);
/* Built-in hash code specializations */
#define ANDROID_INT32_HASH(T) \
template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
#define ANDROID_INT64_HASH(T) \
template <> inline hash_t hash_type(const T& value) { \
return hash_t((value >> 32) ^ value); }
#define ANDROID_REINTERPRET_HASH(T, R) \
template <> inline hash_t hash_type(const T& value) { \
R newValue; \
static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \
memcpy(&newValue, &value, sizeof(newValue)); \
return hash_type(newValue); \
}
ANDROID_INT32_HASH(bool)
ANDROID_INT32_HASH(int8_t)
ANDROID_INT32_HASH(uint8_t)
ANDROID_INT32_HASH(int16_t)
ANDROID_INT32_HASH(uint16_t)
ANDROID_INT32_HASH(int32_t)
ANDROID_INT32_HASH(uint32_t)
ANDROID_INT64_HASH(int64_t)
ANDROID_INT64_HASH(uint64_t)
ANDROID_REINTERPRET_HASH(float, uint32_t)
ANDROID_REINTERPRET_HASH(double, uint64_t)
template <typename T> inline hash_t hash_type(T* const & value) {
return hash_type(uintptr_t(value));
}
} // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_TYPE_HELPERS_H
Loading…
Cancel
Save