Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd1dc40a authored by Jaesung Chung's avatar Jaesung Chung Committed by Android (Google) Code Review
Browse files

Merge "ExifInterface: add RAW input stream support" into nyc-dev

parents ba27e40d 6e08d2b0
Loading
Loading
Loading
Loading
+142 −95
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package android.media;
package android.media;


import android.annotation.NonNull;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory;
import android.system.ErrnoException;
import android.system.ErrnoException;
@@ -24,6 +26,7 @@ import android.system.OsConstants;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.Pair;


import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.DataOutputStream;
@@ -145,6 +148,7 @@ public class ExifInterface {
    private static final String TAG_HAS_THUMBNAIL = "hasThumbnail";
    private static final String TAG_HAS_THUMBNAIL = "hasThumbnail";
    private static final String TAG_THUMBNAIL_OFFSET = "thumbnailOffset";
    private static final String TAG_THUMBNAIL_OFFSET = "thumbnailOffset";
    private static final String TAG_THUMBNAIL_LENGTH = "thumbnailLength";
    private static final String TAG_THUMBNAIL_LENGTH = "thumbnailLength";
    private static final String TAG_THUMBNAIL_DATA = "thumbnailData";


    // Constants used for the Orientation Exif tag.
    // Constants used for the Orientation Exif tag.
    public static final int ORIENTATION_UNDEFINED = 0;
    public static final int ORIENTATION_UNDEFINED = 0;
@@ -163,6 +167,9 @@ public class ExifInterface {
    public static final int WHITEBALANCE_AUTO = 0;
    public static final int WHITEBALANCE_AUTO = 0;
    public static final int WHITEBALANCE_MANUAL = 1;
    public static final int WHITEBALANCE_MANUAL = 1;


    private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
    private static final int JPEG_SIGNATURE_SIZE = 3;

    private static SimpleDateFormat sFormatter;
    private static SimpleDateFormat sFormatter;


    // See Exchangeable image file format for digital still cameras: Exif version 2.2.
    // See Exchangeable image file format for digital still cameras: Exif version 2.2.
@@ -408,8 +415,8 @@ public class ExifInterface {
    // Mappings from tag number to tag name and each item represents one IFD tag group.
    // Mappings from tag number to tag name and each item represents one IFD tag group.
    private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
    private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
    // Mapping from tag name to tag number and the corresponding tag group.
    // Mapping from tag name to tag number and the corresponding tag group.
    private static final HashMap<String, Pair<Integer, Integer>> sExifTagMapForWriting
    private static final HashMap<String, Pair<Integer, Integer>> sExifTagMapForWriting =
            = new HashMap<>();
            new HashMap<>();


    // See JPEG File Interchange Format Version 1.02.
    // See JPEG File Interchange Format Version 1.02.
    // The following values are defined for handling JPEG streams. In this implementation, we are
    // The following values are defined for handling JPEG streams. In this implementation, we are
@@ -443,7 +450,7 @@ public class ExifInterface {


    static {
    static {
        System.loadLibrary("media_jni");
        System.loadLibrary("media_jni");
        initRawNative();
        nativeInitRaw();
        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));


@@ -468,10 +475,10 @@ public class ExifInterface {
    }
    }


    private final String mFilename;
    private final String mFilename;
    private final FileDescriptor mFileDescriptor;
    private final FileDescriptor mSeekableFileDescriptor;
    private final InputStream mInputStream;
    private final AssetManager.AssetInputStream mAssetInputStream;
    private boolean mIsRaw;
    private final HashMap<String, String> mAttributes = new HashMap<>();
    private final HashMap<String, String> mAttributes = new HashMap<>();
    private boolean mIsRaw;
    private boolean mHasThumbnail;
    private boolean mHasThumbnail;
    // The following values used for indicating a thumbnail position.
    // The following values used for indicating a thumbnail position.
    private int mThumbnailOffset;
    private int mThumbnailOffset;
@@ -488,23 +495,33 @@ public class ExifInterface {
        if (filename == null) {
        if (filename == null) {
            throw new IllegalArgumentException("filename cannot be null");
            throw new IllegalArgumentException("filename cannot be null");
        }
        }
        FileInputStream in = new FileInputStream(filename);
        mAssetInputStream = null;
        mFilename = filename;
        mFilename = filename;
        mFileDescriptor = null;
        if (isSeekableFD(in.getFD())) {
        mInputStream = new FileInputStream(filename);
            mSeekableFileDescriptor = in.getFD();
        loadAttributes();
        } else {
            mSeekableFileDescriptor = null;
        }
        loadAttributes(in);
    }
    }


    /**
    /**
     * Reads Exif tags from the specified image file descriptor.
     * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
     * for seekable file descriptors only.
     */
     */
    public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
    public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
        if (fileDescriptor == null) {
        if (fileDescriptor == null) {
            throw new IllegalArgumentException("parcelFileDescriptor cannot be null");
            throw new IllegalArgumentException("parcelFileDescriptor cannot be null");
        }
        }
        mAssetInputStream = null;
        mFilename = null;
        mFilename = null;
        mFileDescriptor = fileDescriptor;
        if (isSeekableFD(fileDescriptor)) {
        mInputStream = new FileInputStream(fileDescriptor);
            mSeekableFileDescriptor = fileDescriptor;
        loadAttributes();
        } else {
            mSeekableFileDescriptor = null;
        }
        loadAttributes(new FileInputStream(fileDescriptor));
    }
    }


    /**
    /**
@@ -516,9 +533,18 @@ public class ExifInterface {
            throw new IllegalArgumentException("inputStream cannot be null");
            throw new IllegalArgumentException("inputStream cannot be null");
        }
        }
        mFilename = null;
        mFilename = null;
        mFileDescriptor = null;
        if (inputStream instanceof AssetManager.AssetInputStream) {
        mInputStream = inputStream;
            mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
        loadAttributes();
            mSeekableFileDescriptor = null;
        } else if (inputStream instanceof FileInputStream
                && isSeekableFD(((FileInputStream) inputStream).getFD())) {
            mAssetInputStream = null;
            mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
        } else {
            mAssetInputStream = null;
            mSeekableFileDescriptor = null;
        }
        loadAttributes(inputStream);
    }
    }


    /**
    /**
@@ -587,46 +613,76 @@ public class ExifInterface {
    }
    }


    /**
    /**
     * Initialize mAttributes with the attributes from the file mFilename.
     * This function decides which parser to read the image data according to the given input stream
     *
     * type and the content of the input stream. In each case, it reads the first three bytes to
     * mAttributes is a HashMap which stores the Exif attributes of the file.
     * determine whether the image data format is JPEG or not.
     * The key is the standard tag name and the value is the tag's value: e.g.
     * Model -&gt; Nikon. Numeric values are stored as strings.
     *
     * This function also initialize mHasThumbnail to indicate whether the
     * file has a thumbnail inside.
     */
     */
    private void loadAttributes() throws IOException {
    private void loadAttributes(@NonNull InputStream in) throws IOException {
        FileInputStream in = null;
        // Process RAW input stream
        try {
        if (mAssetInputStream != null) {
            if (mFilename != null) {
            long asset = mAssetInputStream.getNativeAsset();
                in = new FileInputStream(mFilename);
            if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
                return;
            }
            }
            if (mFileDescriptor != null) {
        } else if (mSeekableFileDescriptor != null) {
                in = new FileInputStream(mFileDescriptor);
            if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
                    mSeekableFileDescriptor))) {
                return;
            }
            }
            if (in != null) {
        } else {
                // First test whether a given file is a one of RAW format or not.
            in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
                HashMap map = getRawAttributesNative(Os.dup(in.getFD()));
            if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
                mIsRaw = map != null;
                    nativeGetRawAttributesFromInputStream(in))) {
                if (mIsRaw) {
                return;
            }
        }

        // Process JPEG input stream
        getJpegAttributes(in);

        if (DEBUG) {
            printAttributes();
        }
    }

    private static boolean isJpegInputStream(BufferedInputStream in) throws IOException {
        in.mark(JPEG_SIGNATURE_SIZE);
        byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE];
        if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) {
            throw new EOFException();
        }
        boolean isJpeg = Arrays.equals(JPEG_SIGNATURE, signatureBytes);
        in.reset();
        return isJpeg;
    }

    private boolean handleRawResult(HashMap map) {
        if (map == null) {
            return false;
        }

        // Mark for disabling the save feature.
        mIsRaw = true;

        for (Object obj : map.entrySet()) {
        for (Object obj : map.entrySet()) {
            Map.Entry entry = (Map.Entry) obj;
            Map.Entry entry = (Map.Entry) obj;
            String attrName = (String) entry.getKey();
            String attrName = (String) entry.getKey();
                        String attrValue = (String) entry.getValue();


            switch (attrName) {
            switch (attrName) {
                case TAG_HAS_THUMBNAIL:
                case TAG_HAS_THUMBNAIL:
                                mHasThumbnail = attrValue.equalsIgnoreCase("true");
                    mHasThumbnail = ((String) entry.getValue()).equalsIgnoreCase("true");
                    break;
                    break;
                case TAG_THUMBNAIL_OFFSET:
                case TAG_THUMBNAIL_OFFSET:
                                mThumbnailOffset = Integer.parseInt(attrValue);
                    mThumbnailOffset = Integer.parseInt((String) entry.getValue());
                    break;
                    break;
                case TAG_THUMBNAIL_LENGTH:
                case TAG_THUMBNAIL_LENGTH:
                                mThumbnailLength = Integer.parseInt(attrValue);
                    mThumbnailLength = Integer.parseInt((String) entry.getValue());
                    break;
                case TAG_THUMBNAIL_DATA:
                    mThumbnailBytes = (byte[]) entry.getValue();
                    break;
                    break;
                default:
                default:
                                mAttributes.put(attrName, attrValue);
                    mAttributes.put(attrName, (String) entry.getValue());
                    break;
                    break;
            }
            }
        }
        }
@@ -634,29 +690,15 @@ public class ExifInterface {
        if (DEBUG) {
        if (DEBUG) {
            printAttributes();
            printAttributes();
        }
        }
                    return;
        return true;
                }
            }
        } catch (ErrnoException e) {
            e.rethrowAsIOException();
        } finally {
            IoUtils.closeQuietly(in);
    }
    }


    private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
        try {
        try {
            if (mFileDescriptor != null) {
            Os.lseek(fd, 0, OsConstants.SEEK_CUR);
                Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
            return true;
            }

            getJpegAttributes(mInputStream);
        } catch (ErrnoException e) {
        } catch (ErrnoException e) {
            e.rethrowAsIOException();
            return false;
        } finally {
            IoUtils.closeQuietly(mInputStream);
        }

        if (DEBUG) {
            printAttributes();
        }
        }
    }
    }


@@ -679,9 +721,9 @@ public class ExifInterface {
            throw new UnsupportedOperationException(
            throw new UnsupportedOperationException(
                    "ExifInterface does not support saving attributes on RAW formats.");
                    "ExifInterface does not support saving attributes on RAW formats.");
        }
        }
        if (mFileDescriptor == null && mFilename == null) {
        if (mSeekableFileDescriptor == null && mFilename == null) {
            throw new UnsupportedOperationException(
            throw new UnsupportedOperationException(
                    "ExifInterface does not support saving attributes for input streams.");
                    "ExifInterface does not support saving attributes for the current input.");
        }
        }


        // Keep the thumbnail in memory
        // Keep the thumbnail in memory
@@ -698,11 +740,10 @@ public class ExifInterface {
                if (!originalFile.renameTo(tempFile)) {
                if (!originalFile.renameTo(tempFile)) {
                    throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
                    throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
                }
                }
            }
            } else if (mSeekableFileDescriptor != null) {
            if (mFileDescriptor != null) {
                tempFile = File.createTempFile("temp", "jpg");
                tempFile = File.createTempFile("temp", "jpg");
                Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                in = new FileInputStream(mFileDescriptor);
                in = new FileInputStream(mSeekableFileDescriptor);
                out = new FileOutputStream(tempFile);
                out = new FileOutputStream(tempFile);
                Streams.copy(in, out);
                Streams.copy(in, out);
            }
            }
@@ -720,10 +761,9 @@ public class ExifInterface {
            in = new FileInputStream(tempFile);
            in = new FileInputStream(tempFile);
            if (mFilename != null) {
            if (mFilename != null) {
                out = new FileOutputStream(mFilename);
                out = new FileOutputStream(mFilename);
            }
            } else if (mSeekableFileDescriptor != null) {
            if (mFileDescriptor != null) {
                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
                out = new FileOutputStream(mSeekableFileDescriptor);
                out = new FileOutputStream(mFileDescriptor);
            }
            }
            saveJpegAttributes(in, out);
            saveJpegAttributes(in, out);
        } catch (ErrnoException e) {
        } catch (ErrnoException e) {
@@ -761,12 +801,14 @@ public class ExifInterface {
        // Read the thumbnail.
        // Read the thumbnail.
        FileInputStream in = null;
        FileInputStream in = null;
        try {
        try {
            if (mFileDescriptor != null) {
            if (mAssetInputStream != null) {
                Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
                return nativeGetThumbnailFromAsset(
                in = new FileInputStream(mFileDescriptor);
                        mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength);
            }
            } else if (mFilename != null) {
            if (mFilename != null) {
                in = new FileInputStream(mFilename);
                in = new FileInputStream(mFilename);
            } else if (mSeekableFileDescriptor != null) {
                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                in = new FileInputStream(mSeekableFileDescriptor);
            }
            }
            if (in == null) {
            if (in == null) {
                // Should not be reached this.
                // Should not be reached this.
@@ -1180,8 +1222,9 @@ public class ExifInterface {
                mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
                mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
                mThumbnailLength = jpegInterchangeFormatLength;
                mThumbnailLength = jpegInterchangeFormatLength;


                // Do not store a thumbnail in memory if the given input can be re-read.
                if (mFilename == null && mAssetInputStream == null
                if (mFileDescriptor == null && mFilename == null) {
                        && mSeekableFileDescriptor == null) {
                    // Save the thumbnail in memory if the input doesn't support reading again.
                    byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
                    byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
                    dataInputStream.seek(jpegInterchangeFormat);
                    dataInputStream.seek(jpegInterchangeFormat);
                    dataInputStream.readFully(thumbnailBytes);
                    dataInputStream.readFully(thumbnailBytes);
@@ -1988,6 +2031,10 @@ public class ExifInterface {
    }
    }


    // JNI methods for RAW formats.
    // JNI methods for RAW formats.
    private static native void initRawNative();
    private static native void nativeInitRaw();
    private static native HashMap getRawAttributesNative(FileDescriptor fileDescriptor);
    private static native byte[] nativeGetThumbnailFromAsset(
            long asset, int thumbnailOffset, int thumbnailLength);
    private static native HashMap nativeGetRawAttributesFromAsset(long asset);
    private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd);
    private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in);
}
}
+162 −11
Original line number Original line Diff line number Diff line
@@ -19,12 +19,15 @@


#include "android_media_Utils.h"
#include "android_media_Utils.h"


#include "android/graphics/CreateJavaOutputStreamAdaptor.h"
#include "src/piex_types.h"
#include "src/piex_types.h"
#include "src/piex.h"
#include "src/piex.h"


#include <jni.h>
#include <jni.h>
#include <JNIHelp.h>
#include <JNIHelp.h>
#include <androidfw/Asset.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/AndroidRuntime.h>
#include <android/graphics/Utils.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedLocalRef.h>


#include <utils/Log.h>
#include <utils/Log.h>
@@ -35,6 +38,9 @@


using namespace android;
using namespace android;


static const char kJpegSignatureChars[] = {(char)0xff, (char)0xd8, (char)0xff};
static const int kJpegSignatureSize = 3;

#define FIND_CLASS(var, className) \
#define FIND_CLASS(var, className) \
    var = env->FindClass(className); \
    var = env->FindClass(className); \
    LOG_FATAL_IF(! var, "Unable to find class " className);
    LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -82,18 +88,48 @@ static void ExifInterface_initRaw(JNIEnv *env) {
                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
}
}


static jobject ExifInterface_getRawMetadata(
static bool is_asset_stream(const SkStream& stream) {
        JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
    return stream.hasLength() && stream.hasPosition();
    int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
}
    if (fd < 0) {

        ALOGI("Invalid file descriptor");
static jobject ExifInterface_getThumbnailFromAsset(
        JNIEnv* env, jclass /* clazz */, jlong jasset, jint jthumbnailOffset,
        jint jthumbnailLength) {
    Asset* asset = reinterpret_cast<Asset*>(jasset);
    std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));

    std::unique_ptr<jbyte[]> thumbnailData(new jbyte[(int)jthumbnailLength]);
    if (thumbnailData.get() == NULL) {
        ALOGI("No memory to get thumbnail");
        return NULL;
        return NULL;
    }
    }


    // Do not know the current offset. So rewind it.
    stream->rewind();

    // Read thumbnail.
    stream->skip((int)jthumbnailOffset);
    stream->read((void*)thumbnailData.get(), (int)jthumbnailLength);

    // Copy to the byte array.
    jbyteArray byteArray = env->NewByteArray(jthumbnailLength);
    env->SetByteArrayRegion(byteArray, 0, jthumbnailLength, thumbnailData.get());
    return byteArray;
}

static jobject getRawAttributes(JNIEnv* env, SkStream* stream, bool returnThumbnail) {
    std::unique_ptr<SkStream> streamDeleter(stream);

    std::unique_ptr<::piex::StreamInterface> piexStream;
    if (is_asset_stream(*stream)) {
        piexStream.reset(new AssetStream(streamDeleter.release()));
    } else {
        piexStream.reset(new BufferedStream(streamDeleter.release()));
    }

    piex::PreviewImageData image_data;
    piex::PreviewImageData image_data;
    std::unique_ptr<FileStream> stream(new FileStream(fd));


    if (!GetExifFromRawImage(stream.get(), String8("[file descriptor]"), image_data)) {
    if (!GetExifFromRawImage(piexStream.get(), String8("[piex stream]"), image_data)) {
        ALOGI("Raw image not detected");
        ALOGI("Raw image not detected");
        return NULL;
        return NULL;
    }
    }
@@ -253,7 +289,117 @@ static jobject ExifInterface_getRawMetadata(
        }
        }
    }
    }


    return KeyedVectorToHashMap(env, map);
    jobject hashMap = KeyedVectorToHashMap(env, map);

    if (returnThumbnail) {
        std::unique_ptr<jbyte[]> thumbnailData(new jbyte[image_data.thumbnail.length]);
        if (thumbnailData.get() == NULL) {
            ALOGE("No memory to parse a thumbnail");
            return NULL;
        }
        jbyteArray jthumbnailByteArray = env->NewByteArray(image_data.thumbnail.length);
        if (jthumbnailByteArray == NULL) {
            ALOGE("No memory to parse a thumbnail");
            return NULL;
        }
        piexStream.get()->GetData(image_data.thumbnail.offset, image_data.thumbnail.length,
                (uint8_t*)thumbnailData.get());
        env->SetByteArrayRegion(
                jthumbnailByteArray, 0, image_data.thumbnail.length, thumbnailData.get());
        jstring jkey = env->NewStringUTF(String8("thumbnailData"));
        env->CallObjectMethod(hashMap, gFields.hashMap.put, jkey, jthumbnailByteArray);
        env->DeleteLocalRef(jkey);
        env->DeleteLocalRef(jthumbnailByteArray);
    }
    return hashMap;
}

static jobject ExifInterface_getRawAttributesFromAsset(
        JNIEnv* env, jclass /* clazz */, jlong jasset) {
    std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
    if (jpegSignature.get() == NULL) {
        ALOGE("No enough memory to parse");
        return NULL;
    }

    Asset* asset = reinterpret_cast<Asset*>(jasset);
    std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));

    if (stream.get()->read(jpegSignature.get(), kJpegSignatureSize) != kJpegSignatureSize) {
        // Rewind the stream.
        stream.get()->rewind();

        ALOGI("Corrupted image.");
        return NULL;
    }

    // Rewind the stream.
    stream.get()->rewind();

    if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
        ALOGI("Should be a JPEG stream.");
        return NULL;
    }

    // Try to parse from the given stream.
    jobject result = getRawAttributes(env, stream.get(), false);

    // Rewind the stream for the chance to read JPEG.
    if (result == NULL) {
        stream.get()->rewind();
    }
    return result;
}

static jobject ExifInterface_getRawAttributesFromFileDescriptor(
        JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
    std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
    if (jpegSignature.get() == NULL) {
        ALOGE("No enough memory to parse");
        return NULL;
    }

    int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
    if (fd < 0) {
        ALOGI("Invalid file descriptor");
        return NULL;
    }

    // Restore the file descriptor's offset on exiting this function.
    AutoFDSeek autoRestore(fd);

    int dupFd = dup(fd);

    FILE* file = fdopen(dupFd, "r");
    if (file == NULL) {
        ALOGI("Failed to open the file descriptor");
        return NULL;
    }

    if (fgets(jpegSignature.get(), kJpegSignatureSize, file) == NULL) {
        ALOGI("Corrupted image.");
        return NULL;
    }

    if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
        ALOGI("Should be a JPEG stream.");
        return NULL;
    }

    // Rewind the file descriptor.
    fseek(file, 0L, SEEK_SET);

    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file,
                SkFILEStream::kCallerPasses_Ownership));
    return getRawAttributes(env, fileStream.get(), false);
}

static jobject ExifInterface_getRawAttributesFromInputStream(
        JNIEnv* env, jclass /* clazz */, jobject jinputStream) {
    jbyteArray byteArray = env->NewByteArray(8*1024);
    ScopedLocalRef<jbyteArray> scoper(env, byteArray);
    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, jinputStream, scoper.get()));
    return getRawAttributes(env, stream.get(), true);
}
}


} // extern "C"
} // extern "C"
@@ -261,9 +407,14 @@ static jobject ExifInterface_getRawMetadata(
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------


static JNINativeMethod gMethods[] = {
static JNINativeMethod gMethods[] = {
    { "initRawNative", "()V", (void *)ExifInterface_initRaw },
    { "nativeInitRaw", "()V", (void *)ExifInterface_initRaw },
    { "getRawAttributesNative", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
    { "nativeGetThumbnailFromAsset", "(JII)[B", (void *)ExifInterface_getThumbnailFromAsset },
      (void*)ExifInterface_getRawMetadata },
    { "nativeGetRawAttributesFromAsset", "(J)Ljava/util/HashMap;",
      (void*)ExifInterface_getRawAttributesFromAsset },
    { "nativeGetRawAttributesFromFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
      (void*)ExifInterface_getRawAttributesFromFileDescriptor },
    { "nativeGetRawAttributesFromInputStream", "(Ljava/io/InputStream;)Ljava/util/HashMap;",
      (void*)ExifInterface_getRawAttributesFromInputStream },
};
};


int register_android_media_ExifInterface(JNIEnv *env) {
int register_android_media_ExifInterface(JNIEnv *env) {
+63 −30

File changed.

Preview size limit exceeded, changes collapsed.

+46 −5
Original line number Original line Diff line number Diff line
@@ -21,20 +21,62 @@
#include "src/piex.h"
#include "src/piex.h"


#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/AndroidRuntime.h>
#include <camera3.h>
#include <gui/CpuConsumer.h>
#include <jni.h>
#include <jni.h>
#include <JNIHelp.h>
#include <JNIHelp.h>
#include <utils/KeyedVector.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/String8.h>
#include <gui/CpuConsumer.h>
#include <SkStream.h>
#include <camera3.h>


namespace android {
namespace android {


class AssetStream : public piex::StreamInterface {
private:
    SkStream *mStream;
    size_t mPosition;

public:
    AssetStream(SkStream* stream);
    ~AssetStream();

    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
    // provided by the caller, guaranteed to be at least "length" bytes long.
    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
    // 'offset' bytes from the start of the stream.
    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
    // change the contents of 'data'.
    piex::Error GetData(
            const size_t offset, const size_t length, std::uint8_t* data) override;
};

class BufferedStream : public piex::StreamInterface {
private:
    SkStream *mStream;
    // Growable memory stream
    SkDynamicMemoryWStream mStreamBuffer;

    // Minimum size to read on filling the buffer.
    const size_t kMinSizeToRead = 8192;

public:
    BufferedStream(SkStream* stream);
    ~BufferedStream();

    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
    // provided by the caller, guaranteed to be at least "length" bytes long.
    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
    // 'offset' bytes from the start of the stream.
    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
    // change the contents of 'data'.
    piex::Error GetData(
            const size_t offset, const size_t length, std::uint8_t* data) override;
};

class FileStream : public piex::StreamInterface {
class FileStream : public piex::StreamInterface {
private:
private:
    FILE *mFile;
    FILE *mFile;
    size_t mPosition;
    size_t mPosition;
    size_t mSize;


public:
public:
    FileStream(const int fd);
    FileStream(const int fd);
@@ -50,13 +92,12 @@ public:
    piex::Error GetData(
    piex::Error GetData(
            const size_t offset, const size_t length, std::uint8_t* data) override;
            const size_t offset, const size_t length, std::uint8_t* data) override;
    bool exists() const;
    bool exists() const;
    size_t size() const;
};
};


// Reads EXIF metadata from a given raw image via piex.
// Reads EXIF metadata from a given raw image via piex.
// And returns true if the operation is successful; otherwise, false.
// And returns true if the operation is successful; otherwise, false.
bool GetExifFromRawImage(
bool GetExifFromRawImage(
        FileStream* stream, const String8& filename, piex::PreviewImageData& image_data);
        piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);


// Returns true if the conversion is successful; otherwise, false.
// Returns true if the conversion is successful; otherwise, false.
bool ConvertKeyValueArraysToKeyedVector(
bool ConvertKeyValueArraysToKeyedVector(
+103 KiB
Loading image diff...
Loading