summaryrefslogtreecommitdiffstats
path: root/core/jni
diff options
context:
space:
mode:
Diffstat (limited to 'core/jni')
-rw-r--r--core/jni/Android.mk4
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_hardware_camera2_DngCreator.cpp1293
-rw-r--r--core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp335
-rw-r--r--core/jni/android_net_NetUtils.cpp6
-rw-r--r--core/jni/android_util_Process.cpp13
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp9
7 files changed, 1473 insertions, 189 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 869a91b..ef7ef0a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -139,6 +139,7 @@ LOCAL_SRC_FILES:= \
android_hardware_Camera.cpp \
android_hardware_camera2_CameraMetadata.cpp \
android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
+ android_hardware_camera2_legacy_PerfMeasurement.cpp \
android_hardware_camera2_DngCreator.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
@@ -242,7 +243,8 @@ LOCAL_SHARED_LIBRARIES := \
libnetd_client \
libsoundtrigger \
libminikin \
- libstlport
+ libstlport \
+ libprocessgroup \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f7cd8c3..9b66734 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -79,6 +79,7 @@ extern int register_android_opengl_jni_GLES31Ext(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
+extern int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv *env);
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
@@ -1314,6 +1315,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
+ REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 33100bf..3a3328f 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -24,11 +24,14 @@
#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 <utils/Log.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/RefBase.h>
+#include <utils/Vector.h>
#include <cutils/properties.h>
#include <string.h>
@@ -42,17 +45,17 @@
using namespace android;
using namespace img_utils;
-#define BAIL_IF_INVALID(expr, jnienv, tagId) \
+#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
if ((expr) != OK) { \
jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
- "Invalid metadata for tag %x", tagId); \
+ "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
return; \
}
-#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
+#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
if (entry.count == 0) { \
jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
- "Missing metadata fields for tag %x", tagId); \
+ "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
return; \
}
@@ -66,15 +69,98 @@ static struct {
jmethodID mWriteMethod;
} gOutputStreamClassInfo;
+static struct {
+ jmethodID mReadMethod;
+ jmethodID mSkipMethod;
+} gInputStreamClassInfo;
+
+static struct {
+ jmethodID mGetMethod;
+} gInputByteBufferClassInfo;
+
enum {
BITS_PER_SAMPLE = 16,
BYTES_PER_SAMPLE = 2,
- TIFF_IFD_0 = 0
+ 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,
};
// ----------------------------------------------------------------------------
-// This class is not intended to be used across JNI calls.
+/**
+ * Container class for the persistent native context.
+ */
+
+class NativeContext : public LightRefBase<NativeContext> {
+
+public:
+ NativeContext();
+ virtual ~NativeContext();
+
+ TiffWriter* getWriter();
+
+ uint32_t getThumbnailWidth();
+ uint32_t getThumbnailHeight();
+ const uint8_t* getThumbnail();
+
+ bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
+
+private:
+ Vector<uint8_t> mCurrentThumbnail;
+ TiffWriter mWriter;
+ uint32_t mThumbnailWidth;
+ uint32_t mThumbnailHeight;
+};
+
+NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
+
+NativeContext::~NativeContext() {}
+
+TiffWriter* NativeContext::getWriter() {
+ return &mWriter;
+}
+
+uint32_t NativeContext::getThumbnailWidth() {
+ return mThumbnailWidth;
+}
+
+uint32_t NativeContext::getThumbnailHeight() {
+ return mThumbnailHeight;
+}
+
+const uint8_t* NativeContext::getThumbnail() {
+ return mCurrentThumbnail.array();
+}
+
+bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
+ mThumbnailWidth = width;
+ mThumbnailHeight = height;
+
+ size_t size = BYTES_PER_RGB_PIXEL * width * height;
+ if (mCurrentThumbnail.resize(size) < 0) {
+ ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
+ return false;
+ }
+
+ uint8_t* thumb = mCurrentThumbnail.editArray();
+ memcpy(thumb, buffer, size);
+ return true;
+}
+
+// End of NativeContext
+// ----------------------------------------------------------------------------
+
+/**
+ * Wrapper class for a Java OutputStream.
+ *
+ * This class is not intended to be used across JNI calls.
+ */
class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
public:
JniOutputStream(JNIEnv* env, jobject outStream);
@@ -82,11 +168,13 @@ public:
virtual ~JniOutputStream();
status_t open();
+
status_t write(const uint8_t* buf, size_t offset, size_t count);
+
status_t close();
private:
enum {
- BYTE_ARRAY_LENGTH = 1024
+ BYTE_ARRAY_LENGTH = 4096
};
jobject mOutputStream;
JNIEnv* mEnv;
@@ -138,27 +226,465 @@ status_t JniOutputStream::close() {
return OK;
}
+// End of JniOutputStream
// ----------------------------------------------------------------------------
+/**
+ * Wrapper class for a Java InputStream.
+ *
+ * This class is not intended to be used across JNI calls.
+ */
+class JniInputStream : public Input, public LightRefBase<JniInputStream> {
+public:
+ JniInputStream(JNIEnv* env, jobject inStream);
+
+ status_t open();
+
+ status_t close();
+
+ ssize_t read(uint8_t* buf, size_t offset, size_t count);
+
+ ssize_t skip(size_t count);
+
+ virtual ~JniInputStream();
+private:
+ enum {
+ BYTE_ARRAY_LENGTH = 4096
+ };
+ jobject mInStream;
+ JNIEnv* mEnv;
+ jbyteArray mByteArray;
+
+};
+
+JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
+ mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+ if (mByteArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+ }
+}
+
+JniInputStream::~JniInputStream() {
+ mEnv->DeleteLocalRef(mByteArray);
+}
+
+ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
+
+ jint realCount = BYTE_ARRAY_LENGTH;
+ if (count < BYTE_ARRAY_LENGTH) {
+ realCount = count;
+ }
+ jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
+ realCount);
+
+ if (actual < 0) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+ return actual;
+}
+
+ssize_t JniInputStream::skip(size_t count) {
+ jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
+ static_cast<jlong>(count));
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+ if (actual < 0) {
+ return NOT_ENOUGH_DATA;
+ }
+ return actual;
+}
+
+status_t JniInputStream::open() {
+ // Do nothing
+ return OK;
+}
+
+status_t JniInputStream::close() {
+ // Do nothing
+ return OK;
+}
+
+// End of JniInputStream
+// ----------------------------------------------------------------------------
+
+/**
+ * Wrapper class for a non-direct Java ByteBuffer.
+ *
+ * This class is not intended to be used across JNI calls.
+ */
+class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
+public:
+ JniInputByteBuffer(JNIEnv* env, jobject inBuf);
+
+ status_t open();
+
+ status_t close();
+
+ ssize_t read(uint8_t* buf, size_t offset, size_t count);
+
+ virtual ~JniInputByteBuffer();
+private:
+ enum {
+ BYTE_ARRAY_LENGTH = 4096
+ };
+ jobject mInBuf;
+ JNIEnv* mEnv;
+ jbyteArray mByteArray;
+};
+
+JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
+ mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+ if (mByteArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+ }
+}
+
+JniInputByteBuffer::~JniInputByteBuffer() {
+ mEnv->DeleteLocalRef(mByteArray);
+}
+
+ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
+ jint realCount = BYTE_ARRAY_LENGTH;
+ if (count < BYTE_ARRAY_LENGTH) {
+ realCount = count;
+ }
+
+ mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
+ realCount);
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+ return realCount;
+}
+
+status_t JniInputByteBuffer::open() {
+ // Do nothing
+ return OK;
+}
+
+status_t JniInputByteBuffer::close() {
+ // Do nothing
+ return OK;
+}
+
+// End of JniInputByteBuffer
+// ----------------------------------------------------------------------------
+
+/**
+ * StripSource subclass for Input types.
+ *
+ * This class is not intended to be used across JNI calls.
+ */
+
+class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
+public:
+ InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
+ uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
+ uint32_t samplesPerPixel);
+
+ virtual ~InputStripSource();
+
+ virtual status_t writeToStream(Output& stream, uint32_t count);
+
+ virtual uint32_t getIfd() const;
+protected:
+ uint32_t mIfd;
+ Input* mInput;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mPixStride;
+ uint32_t mRowStride;
+ uint64_t mOffset;
+ JNIEnv* mEnv;
+ uint32_t mBytesPerSample;
+ uint32_t mSamplesPerPixel;
+};
+
+InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
+ uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
+ uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
+ mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
+ mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
+ mSamplesPerPixel(samplesPerPixel) {}
+
+InputStripSource::~InputStripSource() {}
+
+status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
+ status_t err = OK;
+ uint32_t fullSize = mRowStride * mHeight;
+ jlong offset = mOffset;
+
+ if (fullSize != count) {
+ ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
+ fullSize);
+ jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
+ return BAD_VALUE;
+ }
+
+ // Skip offset
+ while (offset > 0) {
+ ssize_t skipped = mInput->skip(offset);
+ if (skipped <= 0) {
+ if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
+ jniThrowExceptionFmt(mEnv, "java/io/IOException",
+ "Early EOF encountered in skip, not enough pixel data for image of size %u",
+ fullSize);
+ skipped = NOT_ENOUGH_DATA;
+ } else {
+ if (!mEnv->ExceptionCheck()) {
+ jniThrowException(mEnv, "java/io/IOException",
+ "Error encountered while skip bytes in input stream.");
+ }
+ }
+
+ return skipped;
+ }
+ offset -= skipped;
+ }
+
+ Vector<uint8_t> row;
+ if (row.resize(mRowStride) < 0) {
+ jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
+ return BAD_VALUE;
+ }
+
+ uint8_t* rowBytes = row.editArray();
+
+ for (uint32_t i = 0; i < mHeight; ++i) {
+ size_t rowFillAmt = 0;
+ size_t rowSize = mPixStride;
+
+ while (rowFillAmt < mRowStride) {
+ ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
+ if (bytesRead <= 0) {
+ if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
+ jniThrowExceptionFmt(mEnv, "java/io/IOException",
+ "Early EOF encountered, not enough pixel data for image of size %u",
+ fullSize);
+ bytesRead = NOT_ENOUGH_DATA;
+ } else {
+ if (!mEnv->ExceptionCheck()) {
+ jniThrowException(mEnv, "java/io/IOException",
+ "Error encountered while reading");
+ }
+ }
+ return bytesRead;
+ }
+ rowFillAmt += bytesRead;
+ rowSize -= bytesRead;
+ }
+
+ if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
+ ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
+
+ if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
+ mEnv->ExceptionCheck()) {
+ if (!mEnv->ExceptionCheck()) {
+ jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
+ }
+ return BAD_VALUE;
+ }
+ } else {
+ ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
+ jniThrowException(mEnv, "java/lang/IllegalStateException",
+ "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
+ return BAD_VALUE;
+
+ // TODO: Add support for non-contiguous pixels if needed.
+ }
+ }
+ return OK;
+}
+
+uint32_t InputStripSource::getIfd() const {
+ return mIfd;
+}
+
+// End of InputStripSource
+// ----------------------------------------------------------------------------
+
+/**
+ * StripSource subclass for direct buffer types.
+ *
+ * This class is not intended to be used across JNI calls.
+ */
+
+class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
+public:
+ DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
+ uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
+ uint32_t bytesPerSample, uint32_t samplesPerPixel);
+
+ virtual ~DirectStripSource();
+
+ virtual status_t writeToStream(Output& stream, uint32_t count);
+
+ virtual uint32_t getIfd() const;
+protected:
+ uint32_t mIfd;
+ const uint8_t* mPixelBytes;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mPixStride;
+ uint32_t mRowStride;
+ uint16_t mOffset;
+ JNIEnv* mEnv;
+ uint32_t mBytesPerSample;
+ uint32_t mSamplesPerPixel;
+};
+
+DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
+ uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
+ uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
+ mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
+ mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
+ mSamplesPerPixel(samplesPerPixel) {}
+
+DirectStripSource::~DirectStripSource() {}
+
+status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
+ uint32_t fullSize = mRowStride * mHeight;
+
+ if (fullSize != count) {
+ ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
+ fullSize);
+ jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
+ return BAD_VALUE;
+ }
+
+ if (mPixStride == mBytesPerSample * mSamplesPerPixel
+ && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
+ ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
+
+ if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
+ if (!mEnv->ExceptionCheck()) {
+ jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
+ }
+ return BAD_VALUE;
+ }
+ } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
+ ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
+
+ for (size_t i = 0; i < mHeight; ++i) {
+ if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
+ mEnv->ExceptionCheck()) {
+ if (!mEnv->ExceptionCheck()) {
+ jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
+ }
+ return BAD_VALUE;
+ }
+ }
+ } else {
+ ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
+
+ jniThrowException(mEnv, "java/lang/IllegalStateException",
+ "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
+ return BAD_VALUE;
+
+ // TODO: Add support for non-contiguous pixels if needed.
+ }
+ return OK;
+
+}
+
+uint32_t DirectStripSource::getIfd() const {
+ return mIfd;
+}
+
+// End of DirectStripSource
+// ----------------------------------------------------------------------------
+
+static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
+ bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
+
+ // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
+ uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
+ uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
+
+ if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
+ "Metadata width %d doesn't match image width %d", metadataWidth, width);
+ return false;
+ }
+
+ if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
+ "Metadata height %d doesn't match image height %d", metadataHeight, height);
+ return false;
+ }
+
+ return true;
+}
+
+static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
+ const Vector<uint16_t>& entries) {
+ for (size_t i = 0; i < entries.size(); ++i) {
+ uint16_t tagId = entries[i];
+ sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
+ if (entry == NULL) {
+ ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
+ ifdFrom);
+ return BAD_VALUE;
+ }
+ if (writer->addEntry(entry, ifdTo) != OK) {
+ ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
+ ifdFrom);
+ return BAD_VALUE;
+ }
+ writer->removeEntry(tagId, ifdFrom);
+ }
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
extern "C" {
-static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
ALOGV("%s:", __FUNCTION__);
- return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
+ return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
gDngCreatorClassInfo.mNativeContext));
}
-static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
+static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
ALOGV("%s:", __FUNCTION__);
- TiffWriter* current = DngCreator_getCreator(env, thiz);
- if (writer != NULL) {
- writer->incStrong((void*) DngCreator_setCreator);
+ NativeContext* current = DngCreator_getNativeContext(env, thiz);
+
+ if (context != NULL) {
+ context->incStrong((void*) DngCreator_setNativeContext);
}
+
if (current) {
- current->decStrong((void*) DngCreator_setCreator);
+ current->decStrong((void*) DngCreator_setNativeContext);
}
+
env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
- reinterpret_cast<jlong>(writer.get()));
+ reinterpret_cast<jlong>(context.get()));
+}
+
+static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ NativeContext* current = DngCreator_getNativeContext(env, thiz);
+ if (current) {
+ return current->getWriter();
+ }
+ return NULL;
}
static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
@@ -174,6 +700,19 @@ static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
+
+ jclass inputStreamClazz = env->FindClass("java/io/InputStream");
+ LOG_ALWAYS_FATAL_IF(inputStreamClazz == NULL, "Can't find java/io/InputStream class");
+ gInputStreamClassInfo.mReadMethod = env->GetMethodID(inputStreamClazz, "read", "([BII)I");
+ LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mReadMethod == NULL, "Can't find read method");
+ gInputStreamClassInfo.mSkipMethod = env->GetMethodID(inputStreamClazz, "skip", "(J)J");
+ LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mSkipMethod == NULL, "Can't find skip method");
+
+ jclass inputBufferClazz = env->FindClass("java/nio/ByteBuffer");
+ LOG_ALWAYS_FATAL_IF(inputBufferClazz == NULL, "Can't find java/nio/ByteBuffer class");
+ gInputByteBufferClassInfo.mGetMethod = env->GetMethodID(inputBufferClazz, "get",
+ "([BII)Ljava/nio/ByteBuffer;");
+ LOG_ALWAYS_FATAL_IF(gInputByteBufferClassInfo.mGetMethod == NULL, "Can't find get method");
}
static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
@@ -192,7 +731,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
return;
}
- sp<TiffWriter> writer = new TiffWriter();
+ sp<NativeContext> nativeContext = new NativeContext();
+ TiffWriter* writer = nativeContext->getWriter();
writer->addIfd(TIFF_IFD_0);
@@ -208,96 +748,99 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// TODO: Greensplit.
// TODO: Add remaining non-essential tags
+
+ // Setup main image tags
+
{
// Set orientation
uint16_t orientation = 1; // Normal
BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
- TAG_ORIENTATION);
+ TAG_ORIENTATION, writer);
}
{
// Set subfiletype
uint32_t subfileType = 0; // Main image
BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
- TAG_NEWSUBFILETYPE);
+ TAG_NEWSUBFILETYPE, writer);
}
{
// Set bits per sample
uint16_t bits = static_cast<uint16_t>(bitsPerSample);
BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
- TAG_BITSPERSAMPLE);
+ TAG_BITSPERSAMPLE, writer);
}
{
// Set compression
uint16_t compression = 1; // None
BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
- TAG_COMPRESSION);
+ TAG_COMPRESSION, writer);
}
{
// Set dimensions
camera_metadata_entry entry =
characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
+ BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
- TAG_IMAGEWIDTH);
+ TAG_IMAGEWIDTH, writer);
BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
- TAG_IMAGELENGTH);
+ TAG_IMAGELENGTH, writer);
imageWidth = width;
imageHeight = height;
}
{
// Set photometric interpretation
- uint16_t interpretation = 32803;
+ uint16_t interpretation = 32803; // CFA
BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
- TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
+ TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
}
{
// Set blacklevel tags
camera_metadata_entry entry =
characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
- BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
+ BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
- TAG_BLACKLEVEL);
+ TAG_BLACKLEVEL, writer);
uint16_t repeatDim[2] = {2, 2};
BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
- TAG_BLACKLEVELREPEATDIM);
+ TAG_BLACKLEVELREPEATDIM, writer);
}
{
// Set samples per pixel
uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
- env, TAG_SAMPLESPERPIXEL);
+ env, TAG_SAMPLESPERPIXEL, writer);
}
{
// Set planar configuration
uint16_t config = 1; // Chunky
BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
- env, TAG_PLANARCONFIGURATION);
+ env, TAG_PLANARCONFIGURATION, writer);
}
{
// Set CFA pattern dimensions
uint16_t repeatDim[2] = {2, 2};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
- env, TAG_CFAREPEATPATTERNDIM);
+ env, TAG_CFAREPEATPATTERNDIM, writer);
}
{
// Set CFA pattern
camera_metadata_entry entry =
characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
- BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
+ BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
entry.data.u8[0]);
@@ -305,28 +848,28 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
uint8_t cfa[4] = {0, 1, 1, 2};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
+ env, TAG_CFAPATTERN, writer);
opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
break;
}
case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
uint8_t cfa[4] = {1, 0, 2, 1};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
+ env, TAG_CFAPATTERN, writer);
opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
break;
}
case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
uint8_t cfa[4] = {1, 2, 0, 1};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
+ env, TAG_CFAPATTERN, writer);
opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
break;
}
case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
uint8_t cfa[4] = {2, 1, 1, 0};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
+ env, TAG_CFAPATTERN, writer);
opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
break;
}
@@ -342,21 +885,21 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set CFA plane color
uint8_t cfaPlaneColor[3] = {0, 1, 2};
BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
- env, TAG_CFAPLANECOLOR);
+ env, TAG_CFAPLANECOLOR, writer);
}
{
// Set CFA layout
uint16_t cfaLayout = 1;
BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
- env, TAG_CFALAYOUT);
+ env, TAG_CFALAYOUT, writer);
}
{
// image description
uint8_t imageDescription = '\0'; // empty
BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
- env, TAG_IMAGEDESCRIPTION);
+ env, TAG_IMAGEDESCRIPTION, writer);
}
{
@@ -368,7 +911,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
- TIFF_IFD_0), env, TAG_MAKE);
+ TIFF_IFD_0), env, TAG_MAKE, writer);
}
{
@@ -380,23 +923,23 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
- TIFF_IFD_0), env, TAG_MODEL);
+ TIFF_IFD_0), env, TAG_MODEL, writer);
}
{
// x resolution
uint32_t xres[] = { 72, 1 }; // default 72 ppi
BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
- env, TAG_XRESOLUTION);
+ env, TAG_XRESOLUTION, writer);
// y resolution
uint32_t yres[] = { 72, 1 }; // default 72 ppi
BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
- env, TAG_YRESOLUTION);
+ env, TAG_YRESOLUTION, writer);
uint16_t unit = 2; // inches
BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
- env, TAG_RESOLUTIONUNIT);
+ env, TAG_RESOLUTIONUNIT, writer);
}
{
@@ -405,7 +948,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
property_get("ro.build.fingerprint", software, "");
uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
- TIFF_IFD_0), env, TAG_SOFTWARE);
+ TIFF_IFD_0), env, TAG_SOFTWARE, writer);
}
{
@@ -420,12 +963,22 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
return;
}
- BAIL_IF_INVALID(writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
- reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
+ if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
+ reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
+ env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid metadata for tag %x", TAG_DATETIME);
+ return;
+ }
// datetime original
- BAIL_IF_INVALID(writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
- reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
+ if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
+ reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
+ env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
+ return;
+ }
env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
}
@@ -433,21 +986,21 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// TIFF/EP standard id
uint8_t standardId[] = { 1, 0, 0, 0 };
BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
- TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID);
+ TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
}
{
// copyright
uint8_t copyright = '\0'; // empty
BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
- TIFF_IFD_0), env, TAG_COPYRIGHT);
+ TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
}
{
// exposure time
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_EXPOSURE_TIME);
- BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME);
+ BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
int64_t exposureTime = *(entry.data.i64);
@@ -473,7 +1026,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
- TIFF_IFD_0), env, TAG_EXPOSURETIME);
+ TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
}
@@ -481,7 +1034,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// ISO speed ratings
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_SENSITIVITY);
- BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS);
+ BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
int32_t tempIso = *(entry.data.i32);
if (tempIso < 0) {
@@ -497,57 +1050,57 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint16_t iso = static_cast<uint16_t>(tempIso);
BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
- TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS);
+ TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
}
{
// focal length
camera_metadata_entry entry =
results.find(ANDROID_LENS_FOCAL_LENGTH);
- BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH);
+ BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
- TIFF_IFD_0), env, TAG_FOCALLENGTH);
+ TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
}
{
// f number
camera_metadata_entry entry =
results.find(ANDROID_LENS_APERTURE);
- BAIL_IF_EMPTY(entry, env, TAG_FNUMBER);
+ BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
- TIFF_IFD_0), env, TAG_FNUMBER);
+ TIFF_IFD_0), env, TAG_FNUMBER, writer);
}
{
// Set DNG version information
uint8_t version[4] = {1, 4, 0, 0};
BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
- env, TAG_DNGVERSION);
+ env, TAG_DNGVERSION, writer);
uint8_t backwardVersion[4] = {1, 1, 0, 0};
BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
- env, TAG_DNGBACKWARDVERSION);
+ env, TAG_DNGBACKWARDVERSION, writer);
}
{
// Set whitelevel
camera_metadata_entry entry =
characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
- BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
+ BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
- TAG_WHITELEVEL);
+ TAG_WHITELEVEL, writer);
}
{
// Set default scale
uint32_t defaultScale[4] = {1, 1, 1, 1};
BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
- env, TAG_DEFAULTSCALE);
+ env, TAG_DEFAULTSCALE, writer);
}
bool singleIlluminant = false;
@@ -555,7 +1108,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set calibration illuminants
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
- BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
camera_metadata_entry entry2 =
characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
if (entry2.count == 0) {
@@ -564,12 +1117,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint16_t ref1 = entry1.data.u8[0];
BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
- TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
if (!singleIlluminant) {
uint16_t ref2 = entry2.data.u8[0];
BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
- TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
}
}
@@ -577,7 +1130,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set color transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
- BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
+ BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
int32_t colorTransform1[entry1.count * 2];
@@ -587,12 +1140,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
colorTransform1[ctr++] = entry1.data.r[i].denominator;
}
- BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
- env, TAG_COLORMATRIX1);
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
+ TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
if (!singleIlluminant) {
camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
- BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
+ BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
int32_t colorTransform2[entry2.count * 2];
ctr = 0;
@@ -601,8 +1154,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
colorTransform2[ctr++] = entry2.data.r[i].denominator;
}
- BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
- env, TAG_COLORMATRIX2);
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
+ TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
}
}
@@ -610,7 +1163,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set calibration transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
- BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
int32_t calibrationTransform1[entry1.count * 2];
@@ -621,12 +1174,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
}
BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
- TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
if (!singleIlluminant) {
camera_metadata_entry entry2 =
characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
- BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
+ BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
int32_t calibrationTransform2[entry2.count * 2];
ctr = 0;
@@ -636,7 +1189,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
}
BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
- TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
}
}
@@ -644,7 +1197,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set forward transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
- BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
+ BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
int32_t forwardTransform1[entry1.count * 2];
@@ -655,12 +1208,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
}
BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
- TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
if (!singleIlluminant) {
camera_metadata_entry entry2 =
characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
- BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
+ BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
int32_t forwardTransform2[entry2.count * 2];
ctr = 0;
@@ -670,7 +1223,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
}
BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
- TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
}
}
@@ -678,7 +1231,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
// Set camera neutral
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
- BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
+ BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
uint32_t cameraNeutral[entry.count * 2];
size_t ctr = 0;
@@ -690,23 +1243,18 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
}
BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
- TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
+ TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
}
{
// Setup data strips
// TODO: Switch to tiled implementation.
- uint32_t offset = 0;
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
- TAG_STRIPOFFSETS);
-
- BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
- TAG_ROWSPERSTRIP);
-
- uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
- bitsPerByte;
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
- TAG_STRIPBYTECOUNTS);
+ if (writer->addStrip(TIFF_IFD_0) != OK) {
+ ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to setup strip tags.");
+ return;
+ }
}
{
@@ -717,9 +1265,9 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
uint32_t defaultCropOrigin[] = {margin, margin};
uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
- TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
+ TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
- TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
+ TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
}
}
@@ -742,21 +1290,26 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
- TAG_UNIQUECAMERAMODEL);
+ TAG_UNIQUECAMERAMODEL, writer);
}
{
// Setup opcode List 2
camera_metadata_entry entry1 =
characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
- BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
- uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
- uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+
+ uint32_t lsmWidth = 0;
+ uint32_t lsmHeight = 0;
+
+ if (entry1.count != 0) {
+ lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
+ lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+ }
camera_metadata_entry entry2 =
results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
- BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
- if (entry2.count == lsmWidth * lsmHeight * 4) {
+
+ if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
OpcodeListBuilder builder;
status_t err = builder.addGainMapsForMetadata(lsmWidth,
@@ -773,7 +1326,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
err = builder.buildOpList(opcodeListBuf);
if (err == OK) {
BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
- TIFF_IFD_0), env, TAG_OPCODELIST2);
+ TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
} else {
ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
@@ -783,138 +1336,505 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
jniThrowRuntimeException(env, "failed to add lens shading map.");
}
} else {
- ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
+ ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
+ __FUNCTION__);
}
}
- DngCreator_setCreator(env, thiz, writer);
+ DngCreator_setNativeContext(env, thiz, nativeContext);
}
static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
ALOGV("%s:", __FUNCTION__);
- DngCreator_setCreator(env, thiz, NULL);
+ DngCreator_setNativeContext(env, thiz, NULL);
}
-static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
+static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
-}
-static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "setOrientation called with uninitialized DngCreator");
+ return;
+ }
+
+ uint16_t orientation = static_cast<uint16_t>(orient);
+ BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
+ TAG_ORIENTATION, writer);
+
+ // Set main image orientation also if in a separate IFD
+ if (writer->hasIfd(TIFF_IFD_SUB1)) {
+ BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
+ TAG_ORIENTATION, writer);
+ }
}
-static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
- jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
- jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
+static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
+
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "setDescription called with uninitialized DngCreator");
+ return;
+ }
+
+ const char* desc = env->GetStringUTFChars(description, NULL);
+ size_t len = strlen(desc) + 1;
+
+ if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
+ reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
+ }
+
+ env->ReleaseStringUTFChars(description, desc);
}
-static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
- jint height, jobject inBuffer, jint rowStride, jint pixStride) {
+static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
+ jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
ALOGV("%s:", __FUNCTION__);
- sp<JniOutputStream> out = new JniOutputStream(env, outStream);
- if(env->ExceptionCheck()) {
- ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "setGpsTags called with uninitialized DngCreator");
return;
}
- uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
- if (pixelBytes == NULL) {
- ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
- jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
+ if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
+ if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
+ ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
+ TIFF_IFD_0);
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
+ return;
+ }
+ }
+
+ const jsize GPS_VALUE_LENGTH = 6;
+ jsize latLen = env->GetArrayLength(latTag);
+ jsize longLen = env->GetArrayLength(longTag);
+ jsize timeLen = env->GetArrayLength(timeTag);
+ if (latLen != GPS_VALUE_LENGTH) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "invalid latitude tag length");
+ return;
+ } else if (longLen != GPS_VALUE_LENGTH) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "invalid longitude tag length");
return;
+ } else if (timeLen != GPS_VALUE_LENGTH) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "invalid time tag length");
+ return;
+ }
+
+ uint32_t latitude[GPS_VALUE_LENGTH];
+ uint32_t longitude[GPS_VALUE_LENGTH];
+ uint32_t timestamp[GPS_VALUE_LENGTH];
+
+ env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
+ reinterpret_cast<jint*>(&latitude));
+ env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
+ reinterpret_cast<jint*>(&longitude));
+ env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
+ reinterpret_cast<jint*>(&timestamp));
+
+ const jsize GPS_REF_LENGTH = 2;
+ const jsize GPS_DATE_LENGTH = 11;
+ uint8_t latitudeRef[GPS_REF_LENGTH];
+ uint8_t longitudeRef[GPS_REF_LENGTH];
+ uint8_t date[GPS_DATE_LENGTH];
+
+ env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
+ latitudeRef[GPS_REF_LENGTH - 1] = '\0';
+ env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
+ longitudeRef[GPS_REF_LENGTH - 1] = '\0';
+
+ env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
+ date[GPS_DATE_LENGTH - 1] = '\0';
+
+ {
+ uint8_t version[] = {2, 3, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
+ TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
+ }
+
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
+ TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
+ }
+
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
+ TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
}
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
+ TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
+ }
+
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
+ TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
+ }
+
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
+ TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
+ }
+
+ {
+ BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
+ TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
+ }
+}
+
+static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
+ jint height) {
+ ALOGV("%s:", __FUNCTION__);
+
+ NativeContext* context = DngCreator_getNativeContext(env, thiz);
TiffWriter* writer = DngCreator_getCreator(env, thiz);
- if (writer == NULL) {
+ if (writer == NULL || context == NULL) {
ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
jniThrowException(env, "java/lang/AssertionError",
- "Write called with uninitialized DngCreator");
+ "setThumbnail called with uninitialized DngCreator");
return;
}
- // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
- uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
- uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
- if (metadataWidth != width) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
- "Metadata width %d doesn't match image width %d", metadataWidth, width);
+
+ size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
+ jlong capacity = env->GetDirectBufferCapacity(buffer);
+ if (capacity != fullSize) {
+ jniThrowExceptionFmt(env, "java/lang/AssertionError",
+ "Invalid size %d for thumbnail, expected size was %d",
+ capacity, fullSize);
return;
}
- if (metadataHeight != height) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
- "Metadata height %d doesn't match image height %d", metadataHeight, height);
+ uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+ if (pixelBytes == NULL) {
+ ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
return;
}
- uint32_t stripOffset = writer->getTotalSize();
+ if (!writer->hasIfd(TIFF_IFD_SUB1)) {
+ if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
+ ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
+ TIFF_IFD_0);
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
+ return;
+ }
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
- TAG_STRIPOFFSETS);
+ Vector<uint16_t> tagsToMove;
+ tagsToMove.add(TAG_ORIENTATION);
+ tagsToMove.add(TAG_NEWSUBFILETYPE);
+ tagsToMove.add(TAG_BITSPERSAMPLE);
+ tagsToMove.add(TAG_COMPRESSION);
+ tagsToMove.add(TAG_IMAGEWIDTH);
+ tagsToMove.add(TAG_IMAGELENGTH);
+ tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
+ tagsToMove.add(TAG_BLACKLEVEL);
+ tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
+ tagsToMove.add(TAG_SAMPLESPERPIXEL);
+ tagsToMove.add(TAG_PLANARCONFIGURATION);
+ tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
+ tagsToMove.add(TAG_CFAPATTERN);
+ tagsToMove.add(TAG_CFAPLANECOLOR);
+ tagsToMove.add(TAG_CFALAYOUT);
+ tagsToMove.add(TAG_XRESOLUTION);
+ tagsToMove.add(TAG_YRESOLUTION);
+ tagsToMove.add(TAG_RESOLUTIONUNIT);
+ tagsToMove.add(TAG_WHITELEVEL);
+ tagsToMove.add(TAG_DEFAULTSCALE);
+ tagsToMove.add(TAG_ROWSPERSTRIP);
+ tagsToMove.add(TAG_STRIPBYTECOUNTS);
+ tagsToMove.add(TAG_STRIPOFFSETS);
+ tagsToMove.add(TAG_DEFAULTCROPORIGIN);
+ tagsToMove.add(TAG_DEFAULTCROPSIZE);
+ tagsToMove.add(TAG_OPCODELIST2);
+
+ if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
+ return;
+ }
- if (writer->write(out.get()) != OK) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write metadata");
+ // Make sure both IFDs get the same orientation tag
+ sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
+ if (orientEntry != NULL) {
+ writer->addEntry(orientEntry, TIFF_IFD_0);
}
+ }
+
+ // Setup thumbnail tags
+
+ {
+ // Set photometric interpretation
+ uint16_t interpretation = 2; // RGB
+ BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
+ TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
+ }
+
+ {
+ // Set planar configuration
+ uint16_t config = 1; // Chunky
+ BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
+ env, TAG_PLANARCONFIGURATION, writer);
+ }
+
+ {
+ // Set samples per pixel
+ uint16_t samples = SAMPLES_PER_RGB_PIXEL;
+ BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
+ env, TAG_SAMPLESPERPIXEL, writer);
+ }
+
+ {
+ // Set bits per sample
+ uint16_t bits = BITS_PER_RGB_SAMPLE;
+ BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
+ TAG_BITSPERSAMPLE, writer);
+ }
+
+ {
+ // Set subfiletype
+ uint32_t subfileType = 1; // Thumbnail image
+ BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
+ TAG_NEWSUBFILETYPE, writer);
+ }
+
+ {
+ // Set compression
+ uint16_t compression = 1; // None
+ BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
+ TAG_COMPRESSION, writer);
+ }
+
+ {
+ // Set dimensions
+ uint32_t uWidth = static_cast<uint32_t>(width);
+ uint32_t uHeight = static_cast<uint32_t>(height);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
+ TAG_IMAGEWIDTH, writer);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
+ TAG_IMAGELENGTH, writer);
+ }
+
+ {
+ // x resolution
+ uint32_t xres[] = { 72, 1 }; // default 72 ppi
+ BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
+ env, TAG_XRESOLUTION, writer);
+
+ // y resolution
+ uint32_t yres[] = { 72, 1 }; // default 72 ppi
+ BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
+ env, TAG_YRESOLUTION, writer);
+
+ uint16_t unit = 2; // inches
+ BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
+ env, TAG_RESOLUTIONUNIT, writer);
+ }
+
+ {
+ // Setup data strips
+ if (writer->addStrip(TIFF_IFD_0) != OK) {
+ ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to setup thumbnail strip tags.");
+ return;
+ }
+ if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
+ ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to setup main image strip tags.");
+ return;
+ }
+ }
+
+ if (!context->setThumbnail(pixelBytes, width, height)) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to set thumbnail.");
return;
}
+}
- size_t fullSize = rowStride * height;
- jlong capacity = env->GetDirectBufferCapacity(inBuffer);
- if (capacity < 0 || fullSize > capacity) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Invalid size %d for Image, size given in metadata is %d at current stride",
- capacity, fullSize);
+// TODO: Refactor out common preamble for the two nativeWrite methods.
+static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
+ jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
+ jboolean isDirect) {
+ ALOGV("%s:", __FUNCTION__);
+ ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d,"
+ " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset);
+ uint32_t rStride = static_cast<uint32_t>(rowStride);
+ uint32_t pStride = static_cast<uint32_t>(pixStride);
+ uint32_t uWidth = static_cast<uint32_t>(width);
+ uint32_t uHeight = static_cast<uint32_t>(height);
+ uint64_t uOffset = static_cast<uint64_t>(offset);
+
+ sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+ if(env->ExceptionCheck()) {
+ ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
return;
}
- if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
- if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ NativeContext* context = DngCreator_getNativeContext(env, thiz);
+ if (writer == NULL || context == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "Write called with uninitialized DngCreator");
+ return;
+ }
+
+ // Validate DNG header
+ if (!validateDngHeader(env, writer, width, height)) {
+ return;
+ }
+
+ sp<JniInputByteBuffer> inBuf;
+ Vector<StripSource*> sources;
+ sp<DirectStripSource> thumbnailSource;
+ uint32_t targetIfd = TIFF_IFD_0;
+
+ bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
+
+ if (hasThumbnail) {
+ ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
+ uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
+ uint32_t thumbWidth = context->getThumbnailWidth();
+ thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
+ thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
+ bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
+ SAMPLES_PER_RGB_PIXEL);
+ sources.add(thumbnailSource.get());
+ targetIfd = TIFF_IFD_SUB1;
+ }
+
+ if (isDirect) {
+ size_t fullSize = rStride * uHeight;
+ jlong capacity = env->GetDirectBufferCapacity(inBuffer);
+ if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid size %d for Image, size given in metadata is %d at current stride",
+ capacity, fullSize);
return;
}
- } else if (pixStride == BYTES_PER_SAMPLE) {
- for (size_t i = 0; i < height; ++i) {
- if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
- env->ExceptionCheck()) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
- return;
+
+ uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
+ if (pixelBytes == NULL) {
+ ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
+ return;
+ }
+
+ ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
+ DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
+ rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
+ sources.add(&stripSource);
+
+ status_t ret = OK;
+ if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
+ ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
+ if (!env->ExceptionCheck()) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Encountered error %d while writing file.", ret);
}
+ return;
}
} else {
- for (size_t i = 0; i < height; ++i) {
- for (size_t j = 0; j < width; ++j) {
- if (out->write(pixelBytes, i * rowStride + j * pixStride,
- BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
- if (env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
- return;
- }
+ inBuf = new JniInputByteBuffer(env, inBuffer);
+
+ ALOGV("%s: Using input-type strip source.", __FUNCTION__);
+ InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
+ rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
+ sources.add(&stripSource);
+
+ status_t ret = OK;
+ if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
+ ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
+ if (!env->ExceptionCheck()) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Encountered error %d while writing file.", ret);
}
+ return;
}
}
-
-}
-
-static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
- jobject rawBuffer, jlong offset) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
}
static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
- jobject inStream, jlong offset) {
+ jobject inStream, jint width, jint height, jlong offset) {
ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
+
+ uint32_t rowStride = width * BYTES_PER_SAMPLE;
+ uint32_t pixStride = BYTES_PER_SAMPLE;
+ uint32_t uWidth = static_cast<uint32_t>(width);
+ uint32_t uHeight = static_cast<uint32_t>(height);
+ uint64_t uOffset = static_cast<uint32_t>(offset);
+
+ ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u,"
+ "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride,
+ offset);
+
+ sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+ if(env->ExceptionCheck()) {
+ ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+ return;
+ }
+
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ NativeContext* context = DngCreator_getNativeContext(env, thiz);
+ if (writer == NULL || context == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "Write called with uninitialized DngCreator");
+ return;
+ }
+
+ // Validate DNG header
+ if (!validateDngHeader(env, writer, width, height)) {
+ return;
+ }
+
+ sp<DirectStripSource> thumbnailSource;
+ uint32_t targetIfd = TIFF_IFD_0;
+ bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
+ Vector<StripSource*> sources;
+
+ if (hasThumbnail) {
+ ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
+ uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
+ uint32_t width = context->getThumbnailWidth();
+ thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
+ width, context->getThumbnailHeight(), bytesPerPixel,
+ bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
+ SAMPLES_PER_RGB_PIXEL);
+ sources.add(thumbnailSource.get());
+ targetIfd = TIFF_IFD_SUB1;
+ }
+
+ sp<JniInputStream> in = new JniInputStream(env, inStream);
+
+ ALOGV("%s: Using input-type strip source.", __FUNCTION__);
+ InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
+ rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
+ sources.add(&stripSource);
+
+ status_t ret = OK;
+ if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
+ ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
+ if (!env->ExceptionCheck()) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Encountered error %d while writing file.", ret);
+ }
+ return;
+ }
}
} /*extern "C" */
@@ -926,16 +1846,13 @@ static JNINativeMethod gDngCreatorMethods[] = {
(void*) DngCreator_init},
{"nativeDestroy", "()V", (void*) DngCreator_destroy},
{"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
- {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
- (void*) DngCreator_nativeSetThumbnailBitmap},
- {"nativeSetThumbnailImage",
- "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
- (void*) DngCreator_nativeSetThumbnailImage},
- {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
+ {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
+ {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
+ (void*) DngCreator_nativeSetGpsTags},
+ {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
+ {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
(void*) DngCreator_nativeWriteImage},
- {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
- (void*) DngCreator_nativeWriteByteBuffer},
- {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
+ {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
(void*) DngCreator_nativeWriteInputStream},
};
diff --git a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
new file mode 100644
index 0000000..93473a5
--- /dev/null
+++ b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera2-Legacy-PerfMeasurement-JNI"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <ui/GraphicBuffer.h>
+#include <system/window.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+using namespace android;
+
+// fully-qualified class name
+#define PERF_MEASUREMENT_CLASS_NAME "android/hardware/camera2/legacy/PerfMeasurement"
+
+/** GL utility methods copied from com_google_android_gles_jni_GLImpl.cpp */
+
+// Check if the extension at the head of pExtensions is pExtension. Note that pExtensions is
+// terminated by either 0 or space, while pExtension is terminated by 0.
+
+static bool
+extensionEqual(const GLubyte* pExtensions, const GLubyte* pExtension) {
+ while (true) {
+ char a = *pExtensions++;
+ char b = *pExtension++;
+ bool aEnd = a == '\0' || a == ' ';
+ bool bEnd = b == '\0';
+ if (aEnd || bEnd) {
+ return aEnd == bEnd;
+ }
+ if (a != b) {
+ return false;
+ }
+ }
+}
+
+static const GLubyte*
+nextExtension(const GLubyte* pExtensions) {
+ while (true) {
+ char a = *pExtensions++;
+ if (a == '\0') {
+ return pExtensions-1;
+ } else if ( a == ' ') {
+ return pExtensions;
+ }
+ }
+}
+
+static bool
+checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
+ for (; *pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
+ if (extensionEqual(pExtensions, pExtension)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/** End copied GL utility methods */
+
+bool checkGlError(JNIEnv* env) {
+ int error;
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "GLES20 error: 0x%d", error);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Asynchronous low-overhead GL performance measurement using
+ * http://www.khronos.org/registry/gles/extensions/EXT/EXT_disjoint_timer_query.txt
+ *
+ * Measures the duration of GPU processing for a set of GL commands, delivering
+ * the measurement asynchronously once processing completes.
+ *
+ * All calls must come from a single thread with a valid GL context active.
+ **/
+class PerfMeasurementContext {
+ private:
+ Vector<GLuint> mTimingQueries;
+ size_t mTimingStartIndex;
+ size_t mTimingEndIndex;
+ size_t mTimingQueryIndex;
+ size_t mFreeQueries;
+
+ bool mInitDone;
+ public:
+
+ /**
+ * maxQueryCount should be a conservative estimate of how many query objects
+ * will be active at once, which is a function of the GPU's level of
+ * pipelining and the frequency of queries.
+ */
+ PerfMeasurementContext(size_t maxQueryCount):
+ mTimingStartIndex(0),
+ mTimingEndIndex(0),
+ mTimingQueryIndex(0) {
+ mTimingQueries.resize(maxQueryCount);
+ mFreeQueries = maxQueryCount;
+ mInitDone = false;
+ }
+
+ int getMaxQueryCount() {
+ return mTimingQueries.size();
+ }
+
+ /**
+ * Start a measurement period using the next available query object.
+ * Returns INVALID_OPERATION if called multiple times in a row,
+ * and BAD_VALUE if no more query objects are available.
+ */
+ int startGlTimer() {
+ // Lazy init of queries to avoid needing GL context during construction
+ if (!mInitDone) {
+ glGenQueriesEXT(mTimingQueries.size(), mTimingQueries.editArray());
+ mInitDone = true;
+ }
+
+ if (mTimingEndIndex != mTimingStartIndex) {
+ return INVALID_OPERATION;
+ }
+
+ if (mFreeQueries == 0) {
+ return BAD_VALUE;
+ }
+
+ glBeginQueryEXT(GL_TIME_ELAPSED_EXT, mTimingQueries[mTimingStartIndex]);
+
+ mTimingStartIndex = (mTimingStartIndex + 1) % mTimingQueries.size();
+ mFreeQueries--;
+
+ return OK;
+ }
+
+ /**
+ * Finish the current measurement period
+ * Returns INVALID_OPERATION if called before any startGLTimer calls
+ * or if called multiple times in a row.
+ */
+ int stopGlTimer() {
+ size_t nextEndIndex = (mTimingEndIndex + 1) % mTimingQueries.size();
+ if (nextEndIndex != mTimingStartIndex) {
+ return INVALID_OPERATION;
+ }
+ glEndQueryEXT(GL_TIME_ELAPSED_EXT);
+
+ mTimingEndIndex = nextEndIndex;
+
+ return OK;
+ }
+
+ static const nsecs_t NO_DURATION_YET = -1L;
+ static const nsecs_t FAILED_MEASUREMENT = -2L;
+
+ /**
+ * Get the next available duration measurement.
+ *
+ * Returns NO_DURATION_YET if no new measurement is available,
+ * and FAILED_MEASUREMENT if an error occurred during the next
+ * measurement period.
+ *
+ * Otherwise returns a positive number of nanoseconds measuring the
+ * duration of the oldest completed query.
+ */
+ nsecs_t getNextGlDuration() {
+ if (!mInitDone) {
+ // No start/stop called yet
+ return NO_DURATION_YET;
+ }
+
+ GLint available;
+ glGetQueryObjectivEXT(mTimingQueries[mTimingQueryIndex],
+ GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+ if (!available) {
+ return NO_DURATION_YET;
+ }
+
+ GLint64 duration = FAILED_MEASUREMENT;
+ GLint disjointOccurred;
+ glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjointOccurred);
+
+ if (!disjointOccurred) {
+ glGetQueryObjecti64vEXT(mTimingQueries[mTimingQueryIndex],
+ GL_QUERY_RESULT_EXT,
+ &duration);
+ }
+
+ mTimingQueryIndex = (mTimingQueryIndex + 1) % mTimingQueries.size();
+ mFreeQueries++;
+
+ return static_cast<nsecs_t>(duration);
+ }
+
+ static bool isMeasurementSupported() {
+ const GLubyte* extensions = glGetString(GL_EXTENSIONS);
+ return checkForExtension(extensions,
+ reinterpret_cast<const GLubyte*>("GL_EXT_disjoint_timer_query"));
+ }
+
+};
+
+PerfMeasurementContext* getContext(jlong context) {
+ return reinterpret_cast<PerfMeasurementContext*>(context);
+}
+
+extern "C" {
+
+static jlong PerfMeasurement_nativeCreateContext(JNIEnv* env, jobject thiz,
+ jint maxQueryCount) {
+ PerfMeasurementContext *context = new PerfMeasurementContext(maxQueryCount);
+ return reinterpret_cast<jlong>(context);
+}
+
+static void PerfMeasurement_nativeDeleteContext(JNIEnv* env, jobject thiz,
+ jlong contextHandle) {
+ PerfMeasurementContext *context = getContext(contextHandle);
+ delete(context);
+}
+
+static jboolean PerfMeasurement_nativeQuerySupport(JNIEnv* env, jobject thiz) {
+ bool supported = PerfMeasurementContext::isMeasurementSupported();
+ checkGlError(env);
+ return static_cast<jboolean>(supported);
+}
+
+static void PerfMeasurement_nativeStartGlTimer(JNIEnv* env, jobject thiz,
+ jlong contextHandle) {
+
+ PerfMeasurementContext *context = getContext(contextHandle);
+ status_t err = context->startGlTimer();
+ if (err != OK) {
+ switch (err) {
+ case INVALID_OPERATION:
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Mismatched start/end GL timing calls");
+ return;
+ case BAD_VALUE:
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Too many timing queries in progress, max %d",
+ context->getMaxQueryCount());
+ return;
+ default:
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Unknown error starting GL timing");
+ return;
+ }
+ }
+ checkGlError(env);
+}
+
+static void PerfMeasurement_nativeStopGlTimer(JNIEnv* env, jobject thiz,
+ jlong contextHandle) {
+
+ PerfMeasurementContext *context = getContext(contextHandle);
+ status_t err = context->stopGlTimer();
+ if (err != OK) {
+ switch (err) {
+ case INVALID_OPERATION:
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Mismatched start/end GL timing calls");
+ return;
+ default:
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Unknown error ending GL timing");
+ return;
+ }
+ }
+ checkGlError(env);
+}
+
+static jlong PerfMeasurement_nativeGetNextGlDuration(JNIEnv* env,
+ jobject thiz, jlong contextHandle) {
+ PerfMeasurementContext *context = getContext(contextHandle);
+ nsecs_t duration = context->getNextGlDuration();
+
+ checkGlError(env);
+ return static_cast<jlong>(duration);
+}
+
+} // extern "C"
+
+static JNINativeMethod gPerfMeasurementMethods[] = {
+ { "nativeCreateContext",
+ "(I)J",
+ (jlong *)PerfMeasurement_nativeCreateContext },
+ { "nativeDeleteContext",
+ "(J)V",
+ (void *)PerfMeasurement_nativeDeleteContext },
+ { "nativeQuerySupport",
+ "()Z",
+ (jboolean *)PerfMeasurement_nativeQuerySupport },
+ { "nativeStartGlTimer",
+ "(J)V",
+ (void *)PerfMeasurement_nativeStartGlTimer },
+ { "nativeStopGlTimer",
+ "(J)V",
+ (void *)PerfMeasurement_nativeStopGlTimer },
+ { "nativeGetNextGlDuration",
+ "(J)J",
+ (jlong *)PerfMeasurement_nativeGetNextGlDuration }
+};
+
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv* env)
+{
+ // Register native functions
+ return AndroidRuntime::registerNativeMethods(env,
+ PERF_MEASUREMENT_CLASS_NAME,
+ gPerfMeasurementMethods,
+ NELEM(gPerfMeasurementMethods));
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6f89800..a75d547 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -285,6 +285,11 @@ static jboolean android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz,
return (jboolean) !setNetworkForSocket(netId, socket);
}
+static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
+{
+ return (jboolean) !protectFromVpn(socket);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -308,6 +313,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "unbindProcessToNetworkForHostResolution", "()Z", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(II)Z", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index a6b65cc..aaa680f 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -24,6 +24,7 @@
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <processgroup/processgroup.h>
#include <android_runtime/AndroidRuntime.h>
@@ -1002,6 +1003,16 @@ jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
return pidArray;
}
+jint android_os_Process_killProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid)
+{
+ return killProcessGroup(uid, pid, SIGKILL);
+}
+
+void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz)
+{
+ return removeAllProcessGroups();
+}
+
static const JNINativeMethod methods[] = {
{"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
{"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
@@ -1029,6 +1040,8 @@ static const JNINativeMethod methods[] = {
{"getPss", "(I)J", (void*)android_os_Process_getPss},
{"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands},
//{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
+ {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
+ {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
};
const char* const kProcessPathName = "android/os/Process";
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0cdddba..989b60e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -41,6 +41,7 @@
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <selinux/android.h>
+#include <processgroup/processgroup.h>
#include "android_runtime/AndroidRuntime.h"
#include "JNIHelp.h"
@@ -435,6 +436,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
}
}
+ if (!is_system_server) {
+ int rc = createProcessGroup(uid, getpid());
+ if (rc != 0) {
+ ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
+ RuntimeAbort(env);
+ }
+ }
+
SetGids(env, javaGids);
SetRLimits(env, javaRlimits);