DNG: Update TiffWriter to handle thumbnails and SubIfds.

- Fix SubIfd handling.
- Add StripSources, convenience functions for writing image strips.
- Update Input classes to use with JNI.
- Add skip method.
- Add tag definitions for GPS tags.
- Add name string to tag definitions.

Bug: 15112503

Change-Id: I9535b21261027f6c06a041c1621de8f865a0ad32
diff --git a/media/img_utils/include/img_utils/FileInput.h b/media/img_utils/include/img_utils/FileInput.h
index d3c5ec1..4d4f22b 100644
--- a/media/img_utils/include/img_utils/FileInput.h
+++ b/media/img_utils/include/img_utils/FileInput.h
@@ -52,10 +52,10 @@
          * of bytes given in the count argument will be read.  Bytes will be written
          * into the given buffer starting at the index given in the offset argument.
          *
-         * Returns the number of bytes read.  If an error has occurred, the value pointed
-         * to by the given status_t pointer will be set to a negative error code.
+         * Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file.  If an
+         * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
          */
-        virtual size_t read(uint8_t* buf, size_t offset, size_t count, status_t* err);
+        virtual ssize_t read(uint8_t* buf, size_t offset, size_t count);
 
         /**
          * Close the file descriptor to the path given in the constructor.
diff --git a/media/img_utils/include/img_utils/Input.h b/media/img_utils/include/img_utils/Input.h
index 2166601..6a03647 100644
--- a/media/img_utils/include/img_utils/Input.h
+++ b/media/img_utils/include/img_utils/Input.h
@@ -43,10 +43,18 @@
          * count argument will be read.  Bytes will be written into the given buffer starting
          * at the index given in the offset argument.
          *
-         * Returns the number of bytes read.  If an error has occurred, the value pointed
-         * to by the given status_t pointer will be set to a negative error code.
+         * Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file.  If an
+         * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
          */
-        virtual size_t read(uint8_t* buf, size_t offset, size_t count, status_t* err) = 0;
+        virtual ssize_t read(uint8_t* buf, size_t offset, size_t count) = 0;
+
+        /**
+         * Skips bytes in the input.
+         *
+         * Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file.  If an
+         * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
+         */
+        virtual ssize_t skip(size_t count);
 
         /**
          * Close the Input.  It is not valid to call open on a previously closed Input.
diff --git a/media/img_utils/include/img_utils/StripSource.h b/media/img_utils/include/img_utils/StripSource.h
new file mode 100644
index 0000000..b5c6b60
--- /dev/null
+++ b/media/img_utils/include/img_utils/StripSource.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IMG_UTILS_STRIP_SOURCE_H
+#define IMG_UTILS_STRIP_SOURCE_H
+
+#include <img_utils/Output.h>
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * This class acts as a data source for strips set in a TiffIfd.
+ */
+class ANDROID_API StripSource {
+    public:
+        virtual ~StripSource();
+
+        /**
+         * Write count bytes to the stream.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t writeToStream(Output& stream, uint32_t count) = 0;
+
+        /**
+         * Return the source IFD.
+         */
+        virtual uint32_t getIfd() const = 0;
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_STRIP_SOURCE_H*/
diff --git a/media/img_utils/include/img_utils/TagDefinitions.h b/media/img_utils/include/img_utils/TagDefinitions.h
index 6cc42b2..e9a7480 100644
--- a/media/img_utils/include/img_utils/TagDefinitions.h
+++ b/media/img_utils/include/img_utils/TagDefinitions.h
@@ -29,16 +29,18 @@
  * Tag definitions contain information about standard TIFF compatible tags.
  */
 typedef struct TagDefinition {
+    // The tag name.
+    const char* tagName;
     // The specified tag ID.
-    uint16_t tagId;
+    const uint16_t tagId;
     // The default type for this tag.  This must be a valid TIFF type.
-    TagType defaultType;
+    const TagType defaultType;
     // The default Image File Directory (IFD) for this tag.
-    uint32_t defaultIfd;
+    const uint32_t defaultIfd;
     // The valid count for this tag, or 0 if the count is not fixed.
-    uint32_t fixedCount;
+    const uint32_t fixedCount;
     // The endianness of the tag value, or UNDEFINED_ENDIAN if there is no fixed endian
-    Endianness fixedEndian;
+    const Endianness fixedEndian;
 } TagDefinition_t;
 
 /**
@@ -180,6 +182,14 @@
     TAG_ISOSPEEDRATINGS = 0x8827u,
     TAG_FOCALLENGTH = 0x920Au,
     TAG_FNUMBER = 0x829Du,
+    TAG_GPSINFO = 0x8825u,
+    TAG_GPSVERSIONID = 0x0u,
+    TAG_GPSLATITUDEREF = 0x1u,
+    TAG_GPSLATITUDE = 0x2u,
+    TAG_GPSLONGITUDEREF = 0x3u,
+    TAG_GPSLONGITUDE = 0x4u,
+    TAG_GPSTIMESTAMP = 0x7u,
+    TAG_GPSDATESTAMP = 0x001Du,
 };
 
 /**
@@ -187,6 +197,7 @@
  */
 const TagDefinition_t TIFF_EP_TAG_DEFINITIONS[] =  {
     { // PhotometricInterpretation
+        "PhotometricInterpretation",
         0x0106u,
         SHORT,
         IFD_0,
@@ -194,6 +205,7 @@
         UNDEFINED_ENDIAN
     },
     { // SubIfds
+        "SubIfds",
         0x014Au,
         LONG,
         IFD_0,
@@ -201,6 +213,7 @@
         UNDEFINED_ENDIAN
     },
     { // CFAPattern
+        "CFAPattern",
         0x828Eu,
         BYTE,
         IFD_0,
@@ -208,6 +221,7 @@
         UNDEFINED_ENDIAN
     },
     { // CFARepeatPatternDim
+        "CFARepeatPatternDim",
         0x828Du,
         SHORT,
         IFD_0,
@@ -215,6 +229,7 @@
         UNDEFINED_ENDIAN
     },
     { // DateTimeOriginal
+        "DateTimeOriginal",
         0x9003u,
         ASCII,
         IFD_0,
@@ -222,6 +237,7 @@
         UNDEFINED_ENDIAN
     },
     { // Tiff/EPStandardID
+        "Tiff",
         0x9216u,
         BYTE,
         IFD_0,
@@ -229,6 +245,7 @@
         UNDEFINED_ENDIAN
     },
     { // ExposureTime
+        "ExposureTime",
         0x829Au,
         RATIONAL,
         IFD_0,
@@ -236,6 +253,7 @@
         UNDEFINED_ENDIAN
     },
     { // ISOSpeedRatings
+        "ISOSpeedRatings",
         0x8827u,
         SHORT,
         IFD_0,
@@ -243,6 +261,7 @@
         UNDEFINED_ENDIAN
     },
     { // FocalLength
+        "FocalLength",
         0x920Au,
         RATIONAL,
         IFD_0,
@@ -250,12 +269,69 @@
         UNDEFINED_ENDIAN
     },
     { // FNumber
+        "FNumber",
         0x829Du,
         RATIONAL,
         IFD_0,
         0,
         UNDEFINED_ENDIAN
     },
+    { // GPSInfo
+        "GPSInfo",
+        0x8825u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSVersionID
+        "GPSVersionID",
+        0x0u,
+        BYTE,
+        IFD_0,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSLatitudeRef
+        "GPSLatitudeRef",
+        0x1u,
+        ASCII,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSLatitude
+        "GPSLatitude",
+        0x2u,
+        RATIONAL,
+        IFD_0,
+        3,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSLongitudeRef
+        "GPSLongitudeRef",
+        0x3u,
+        ASCII,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSLongitude
+        "GPSLongitude",
+        0x4u,
+        RATIONAL,
+        IFD_0,
+        3,
+        UNDEFINED_ENDIAN
+    },
+    { // GPSTimeStamp
+        "GPSTimeStamp",
+        0x7u,
+        RATIONAL,
+        IFD_0,
+        3,
+        UNDEFINED_ENDIAN
+    },
     /*TODO: Remaining TIFF EP tags*/
 };
 
@@ -264,12 +340,21 @@
  */
 const TagDefinition_t EXIF_2_3_TAG_DEFINITIONS[] = {
     { // ExifVersion
+        "ExifVersion",
         0x9000u,
         UNDEFINED,
         IFD_0,
         4,
         UNDEFINED_ENDIAN
     },
+    { // GPSDateStamp
+        "GPSDateStamp",
+        0x001Du,
+        ASCII,
+        IFD_0,
+        11,
+        UNDEFINED_ENDIAN
+    },
     /*TODO: Remaining EXIF 2.3 tags*/
 };
 
@@ -278,6 +363,7 @@
  */
 const TagDefinition_t TIFF_6_TAG_DEFINITIONS[] = {
     { // SubFileType
+        "SubFileType",
         0x00FFu,
         SHORT,
         IFD_0,
@@ -285,6 +371,7 @@
         UNDEFINED_ENDIAN
     },
     { // Artist
+        "Artist",
         0x013Bu,
         ASCII,
         IFD_0,
@@ -292,6 +379,7 @@
         UNDEFINED_ENDIAN
     },
     { // BitsPerSample
+        "BitsPerSample",
         0x0102u,
         SHORT,
         IFD_0,
@@ -299,6 +387,7 @@
         UNDEFINED_ENDIAN
     },
     { // CellLength
+        "CellLength",
         0x0109u,
         SHORT,
         IFD_0,
@@ -306,6 +395,7 @@
         UNDEFINED_ENDIAN
     },
     { // CellWidth
+        "CellWidth",
         0x0108u,
         SHORT,
         IFD_0,
@@ -313,6 +403,7 @@
         UNDEFINED_ENDIAN
     },
     { // ColorMap
+        "ColorMap",
         0x0140u,
         SHORT,
         IFD_0,
@@ -320,6 +411,7 @@
         UNDEFINED_ENDIAN
     },
     { // Compression
+        "Compression",
         0x0103u,
         SHORT,
         IFD_0,
@@ -327,6 +419,7 @@
         UNDEFINED_ENDIAN
     },
     { // Copyright
+        "Copyright",
         0x8298u,
         ASCII,
         IFD_0,
@@ -334,6 +427,7 @@
         UNDEFINED_ENDIAN
     },
     { // DateTime
+        "DateTime",
         0x0132u,
         ASCII,
         IFD_0,
@@ -341,6 +435,7 @@
         UNDEFINED_ENDIAN
     },
     { // ExtraSamples
+        "ExtraSamples",
         0x0152u,
         SHORT,
         IFD_0,
@@ -348,6 +443,7 @@
         UNDEFINED_ENDIAN
     },
     { // FillOrder
+        "FillOrder",
         0x010Au,
         SHORT,
         IFD_0,
@@ -355,6 +451,7 @@
         UNDEFINED_ENDIAN
     },
     { // FreeByteCounts
+        "FreeByteCounts",
         0x0121u,
         LONG,
         IFD_0,
@@ -362,6 +459,7 @@
         UNDEFINED_ENDIAN
     },
     { // FreeOffsets
+        "FreeOffsets",
         0x0120u,
         LONG,
         IFD_0,
@@ -369,6 +467,7 @@
         UNDEFINED_ENDIAN
     },
     { // GrayResponseCurve
+        "GrayResponseCurve",
         0x0123u,
         SHORT,
         IFD_0,
@@ -376,6 +475,7 @@
         UNDEFINED_ENDIAN
     },
     { // GrayResponseUnit
+        "GrayResponseUnit",
         0x0122u,
         SHORT,
         IFD_0,
@@ -383,6 +483,7 @@
         UNDEFINED_ENDIAN
     },
     { // HostComputer
+        "HostComputer",
         0x013Cu,
         ASCII,
         IFD_0,
@@ -390,6 +491,7 @@
         UNDEFINED_ENDIAN
     },
     { // ImageDescription
+        "ImageDescription",
         0x010Eu,
         ASCII,
         IFD_0,
@@ -397,6 +499,7 @@
         UNDEFINED_ENDIAN
     },
     { // ImageLength
+        "ImageLength",
         0x0101u,
         LONG,
         IFD_0,
@@ -404,6 +507,7 @@
         UNDEFINED_ENDIAN
     },
     { // ImageWidth
+        "ImageWidth",
         0x0100u,
         LONG,
         IFD_0,
@@ -411,6 +515,7 @@
         UNDEFINED_ENDIAN
     },
     { // Make
+        "Make",
         0x010Fu,
         ASCII,
         IFD_0,
@@ -418,6 +523,7 @@
         UNDEFINED_ENDIAN
     },
     { // MaxSampleValue
+        "MaxSampleValue",
         0x0119u,
         SHORT,
         IFD_0,
@@ -425,6 +531,7 @@
         UNDEFINED_ENDIAN
     },
     { // MinSampleValue
+        "MinSampleValue",
         0x0118u,
         SHORT,
         IFD_0,
@@ -432,6 +539,7 @@
         UNDEFINED_ENDIAN
     },
     { // Model
+        "Model",
         0x0110u,
         ASCII,
         IFD_0,
@@ -439,6 +547,7 @@
         UNDEFINED_ENDIAN
     },
     { // NewSubfileType
+        "NewSubfileType",
         0x00FEu,
         LONG,
         IFD_0,
@@ -446,6 +555,7 @@
         UNDEFINED_ENDIAN
     },
     { // Orientation
+        "Orientation",
         0x0112u,
         SHORT,
         IFD_0,
@@ -453,6 +563,7 @@
         UNDEFINED_ENDIAN
     },
     { // PhotoMetricInterpretation
+        "PhotoMetricInterpretation",
         0x0106u,
         SHORT,
         IFD_0,
@@ -460,6 +571,7 @@
         UNDEFINED_ENDIAN
     },
     { // PlanarConfiguration
+        "PlanarConfiguration",
         0x011Cu,
         SHORT,
         IFD_0,
@@ -467,6 +579,7 @@
         UNDEFINED_ENDIAN
     },
     { // ResolutionUnit
+        "ResolutionUnit",
         0x0128u,
         SHORT,
         IFD_0,
@@ -474,6 +587,7 @@
         UNDEFINED_ENDIAN
     },
     { // RowsPerStrip
+        "RowsPerStrip",
         0x0116u,
         LONG,
         IFD_0,
@@ -481,6 +595,7 @@
         UNDEFINED_ENDIAN
     },
     { // SamplesPerPixel
+        "SamplesPerPixel",
         0x0115u,
         SHORT,
         IFD_0,
@@ -488,6 +603,7 @@
         UNDEFINED_ENDIAN
     },
     { // Software
+        "Software",
         0x0131u,
         ASCII,
         IFD_0,
@@ -495,6 +611,7 @@
         UNDEFINED_ENDIAN
     },
     { // StripByteCounts
+        "StripByteCounts",
         0x0117u,
         LONG,
         IFD_0,
@@ -502,6 +619,7 @@
         UNDEFINED_ENDIAN
     },
     { // StripOffsets
+        "StripOffsets",
         0x0111u,
         LONG,
         IFD_0,
@@ -509,6 +627,7 @@
         UNDEFINED_ENDIAN
     },
     { // SubfileType
+        "SubfileType",
         0x00FFu,
         SHORT,
         IFD_0,
@@ -516,6 +635,7 @@
         UNDEFINED_ENDIAN
     },
     { // Threshholding
+        "Threshholding",
         0x0107u,
         SHORT,
         IFD_0,
@@ -523,6 +643,7 @@
         UNDEFINED_ENDIAN
     },
     { // XResolution
+        "XResolution",
         0x011Au,
         RATIONAL,
         IFD_0,
@@ -530,19 +651,13 @@
         UNDEFINED_ENDIAN
     },
     { // YResolution
+        "YResolution",
         0x011Bu,
         RATIONAL,
         IFD_0,
         1,
         UNDEFINED_ENDIAN
     },
-    { // YResolution
-        0x011Bu,
-        RATIONAL,
-        IFD_0,
-        1,
-        UNDEFINED_ENDIAN
-    }
 };
 
 /**
@@ -550,6 +665,7 @@
  */
 const TagDefinition_t DNG_TAG_DEFINITIONS[] = {
     { // DNGVersion
+        "DNGVersion",
         0xC612u,
         BYTE,
         IFD_0,
@@ -557,6 +673,7 @@
         UNDEFINED_ENDIAN
     },
     { // DNGBackwardVersion
+        "DNGBackwardVersion",
         0xC613u,
         BYTE,
         IFD_0,
@@ -564,6 +681,7 @@
         UNDEFINED_ENDIAN
     },
     { // UniqueCameraModel
+        "UniqueCameraModel",
         0xC614u,
         ASCII,
         IFD_0,
@@ -571,6 +689,7 @@
         UNDEFINED_ENDIAN
     },
     { // LocalizedCameraModel
+        "LocalizedCameraModel",
         0xC615u,
         ASCII,
         IFD_0,
@@ -578,6 +697,7 @@
         UNDEFINED_ENDIAN
     },
     { // CFAPlaneColor
+        "CFAPlaneColor",
         0xC616u,
         BYTE,
         RAW_IFD,
@@ -585,6 +705,7 @@
         UNDEFINED_ENDIAN
     },
     { // CFALayout
+        "CFALayout",
         0xC617u,
         SHORT,
         RAW_IFD,
@@ -592,6 +713,7 @@
         UNDEFINED_ENDIAN
     },
     { // LinearizationTable
+        "LinearizationTable",
         0xC618u,
         SHORT,
         RAW_IFD,
@@ -599,6 +721,7 @@
         UNDEFINED_ENDIAN
     },
     { // BlackLevelRepeatDim
+        "BlackLevelRepeatDim",
         0xC619u,
         SHORT,
         RAW_IFD,
@@ -606,6 +729,7 @@
         UNDEFINED_ENDIAN
     },
     { // BlackLevel
+        "BlackLevel",
         0xC61Au,
         LONG,
         RAW_IFD,
@@ -613,6 +737,7 @@
         UNDEFINED_ENDIAN
     },
     { // BlackLevelDeltaH
+        "BlackLevelDeltaH",
         0xC61Bu,
         SRATIONAL,
         RAW_IFD,
@@ -620,6 +745,7 @@
         UNDEFINED_ENDIAN
     },
     { // BlackLevelDeltaV
+        "BlackLevelDeltaV",
         0xC61Cu,
         SRATIONAL,
         RAW_IFD,
@@ -627,6 +753,7 @@
         UNDEFINED_ENDIAN
     },
     { // WhiteLevel
+        "WhiteLevel",
         0xC61Du,
         LONG,
         RAW_IFD,
@@ -634,6 +761,7 @@
         UNDEFINED_ENDIAN
     },
     { // DefaultScale
+        "DefaultScale",
         0xC61Eu,
         RATIONAL,
         RAW_IFD,
@@ -641,6 +769,7 @@
         UNDEFINED_ENDIAN
     },
     { // BestQualityScale
+        "BestQualityScale",
         0xC65Cu,
         RATIONAL,
         RAW_IFD,
@@ -648,6 +777,7 @@
         UNDEFINED_ENDIAN
     },
     { // DefaultCropOrigin
+        "DefaultCropOrigin",
         0xC61Fu,
         LONG,
         RAW_IFD,
@@ -655,6 +785,7 @@
         UNDEFINED_ENDIAN
     },
     { // DefaultCropSize
+        "DefaultCropSize",
         0xC620u,
         LONG,
         RAW_IFD,
@@ -662,6 +793,7 @@
         UNDEFINED_ENDIAN
     },
     { // CalibrationIlluminant1
+        "CalibrationIlluminant1",
         0xC65Au,
         SHORT,
         PROFILE_IFD,
@@ -669,6 +801,7 @@
         UNDEFINED_ENDIAN
     },
     { // CalibrationIlluminant2
+        "CalibrationIlluminant2",
         0xC65Bu,
         SHORT,
         PROFILE_IFD,
@@ -676,6 +809,7 @@
         UNDEFINED_ENDIAN
     },
     { // ColorMatrix1
+        "ColorMatrix1",
         0xC621u,
         SRATIONAL,
         PROFILE_IFD,
@@ -683,6 +817,7 @@
         UNDEFINED_ENDIAN
     },
     { // ColorMatrix2
+        "ColorMatrix2",
         0xC622u,
         SRATIONAL,
         PROFILE_IFD,
@@ -690,6 +825,7 @@
         UNDEFINED_ENDIAN
     },
     { // CameraCalibration1
+        "CameraCalibration1",
         0xC623u,
         SRATIONAL,
         IFD_0,
@@ -697,6 +833,7 @@
         UNDEFINED_ENDIAN
     },
     { // CameraCalibration2
+        "CameraCalibration2",
         0xC624u,
         SRATIONAL,
         IFD_0,
@@ -704,6 +841,7 @@
         UNDEFINED_ENDIAN
     },
     { // ReductionMatrix1
+        "ReductionMatrix1",
         0xC625u,
         SRATIONAL,
         PROFILE_IFD,
@@ -711,6 +849,7 @@
         UNDEFINED_ENDIAN
     },
     { // ReductionMatrix2
+        "ReductionMatrix2",
         0xC626u,
         SRATIONAL,
         PROFILE_IFD,
@@ -718,6 +857,7 @@
         UNDEFINED_ENDIAN
     },
     { // AnalogBalance
+        "AnalogBalance",
         0xC627u,
         RATIONAL,
         IFD_0,
@@ -725,6 +865,7 @@
         UNDEFINED_ENDIAN
     },
     { // AsShotNeutral
+        "AsShotNeutral",
         0xC628u,
         RATIONAL,
         IFD_0,
@@ -732,6 +873,7 @@
         UNDEFINED_ENDIAN
     },
     { // AsShotWhiteXY
+        "AsShotWhiteXY",
         0xC629u,
         RATIONAL,
         IFD_0,
@@ -739,6 +881,7 @@
         UNDEFINED_ENDIAN
     },
     { // BaselineExposure
+        "BaselineExposure",
         0xC62Au,
         SRATIONAL,
         IFD_0,
@@ -746,6 +889,7 @@
         UNDEFINED_ENDIAN
     },
     { // BaselineNoise
+        "BaselineNoise",
         0xC62Bu,
         RATIONAL,
         IFD_0,
@@ -753,6 +897,7 @@
         UNDEFINED_ENDIAN
     },
     { // BaselineSharpness
+        "BaselineSharpness",
         0xC62Cu,
         RATIONAL,
         IFD_0,
@@ -760,6 +905,7 @@
         UNDEFINED_ENDIAN
     },
     { // BayerGreenSplit
+        "BayerGreenSplit",
         0xC62Du,
         LONG,
         RAW_IFD,
@@ -767,6 +913,7 @@
         UNDEFINED_ENDIAN
     },
     { // LinearResponseLimit
+        "LinearResponseLimit",
         0xC62Eu,
         RATIONAL,
         IFD_0,
@@ -774,6 +921,7 @@
         UNDEFINED_ENDIAN
     },
     { // CameraSerialNumber
+        "CameraSerialNumber",
         0xC62Fu,
         ASCII,
         IFD_0,
@@ -781,6 +929,7 @@
         UNDEFINED_ENDIAN
     },
     { // LensInfo
+        "LensInfo",
         0xC630u,
         RATIONAL,
         IFD_0,
@@ -788,6 +937,7 @@
         UNDEFINED_ENDIAN
     },
     { // ChromaBlurRadius
+        "ChromaBlurRadius",
         0xC631u,
         RATIONAL,
         RAW_IFD,
@@ -795,6 +945,7 @@
         UNDEFINED_ENDIAN
     },
     { // AntiAliasStrength
+        "AntiAliasStrength",
         0xC632u,
         RATIONAL,
         RAW_IFD,
@@ -802,6 +953,7 @@
         UNDEFINED_ENDIAN
     },
     { // ShadowScale
+        "ShadowScale",
         0xC633u,
         RATIONAL,
         IFD_0,
@@ -809,6 +961,7 @@
         UNDEFINED_ENDIAN
     },
     { // DNGPrivateData
+        "DNGPrivateData",
         0xC634u,
         BYTE,
         IFD_0,
@@ -816,6 +969,7 @@
         UNDEFINED_ENDIAN
     },
     { // MakerNoteSafety
+        "MakerNoteSafety",
         0xC635u,
         SHORT,
         IFD_0,
@@ -823,6 +977,7 @@
         UNDEFINED_ENDIAN
     },
     { // RawDataUniqueID
+        "RawDataUniqueID",
         0xC65Du,
         BYTE,
         IFD_0,
@@ -830,6 +985,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalRawFileName
+        "OriginalRawFileName",
         0xC68Bu,
         ASCII,
         IFD_0,
@@ -837,6 +993,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalRawFileData
+        "OriginalRawFileData",
         0xC68Cu,
         UNDEFINED,
         IFD_0,
@@ -844,6 +1001,7 @@
         BIG
     },
     { // ActiveArea
+        "ActiveArea",
         0xC68Du,
         LONG,
         RAW_IFD,
@@ -851,6 +1009,7 @@
         UNDEFINED_ENDIAN
     },
     { // MaskedAreas
+        "MaskedAreas",
         0xC68Eu,
         LONG,
         RAW_IFD,
@@ -858,6 +1017,7 @@
         UNDEFINED_ENDIAN
     },
     { // AsShotICCProfile
+        "AsShotICCProfile",
         0xC68Fu,
         UNDEFINED,
         IFD_0,
@@ -865,6 +1025,7 @@
         UNDEFINED_ENDIAN
     },
     { // AsShotPreProfileMatrix
+        "AsShotPreProfileMatrix",
         0xC690u,
         SRATIONAL,
         IFD_0,
@@ -872,6 +1033,7 @@
         UNDEFINED_ENDIAN
     },
     { // CurrentICCProfile
+        "CurrentICCProfile",
         0xC691u,
         UNDEFINED,
         IFD_0,
@@ -879,6 +1041,7 @@
         UNDEFINED_ENDIAN
     },
     { // CurrentICCProfile
+        "CurrentICCProfile",
         0xC691u,
         UNDEFINED,
         IFD_0,
@@ -886,6 +1049,7 @@
         UNDEFINED_ENDIAN
     },
     { // CurrentPreProfileMatrix
+        "CurrentPreProfileMatrix",
         0xC692u,
         SRATIONAL,
         IFD_0,
@@ -893,6 +1057,7 @@
         UNDEFINED_ENDIAN
     },
     { // ColorimetricReference
+        "ColorimetricReference",
         0xC6BFu,
         SHORT,
         IFD_0,
@@ -900,6 +1065,7 @@
         UNDEFINED_ENDIAN
     },
     { // CameraCalibrationSignature
+        "CameraCalibrationSignature",
         0xC6F3u,
         ASCII,
         IFD_0,
@@ -907,6 +1073,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileCalibrationSignature
+        "ProfileCalibrationSignature",
         0xC6F4u,
         ASCII,
         PROFILE_IFD,
@@ -914,6 +1081,7 @@
         UNDEFINED_ENDIAN
     },
     { // ExtraCameraProfiles
+        "ExtraCameraProfiles",
         0xC6F5u,
         LONG,
         IFD_0,
@@ -921,6 +1089,7 @@
         UNDEFINED_ENDIAN
     },
     { // AsShotProfileName
+        "AsShotProfileName",
         0xC6F6u,
         ASCII,
         IFD_0,
@@ -928,6 +1097,7 @@
         UNDEFINED_ENDIAN
     },
     { // NoiseReductionApplied
+        "NoiseReductionApplied",
         0xC6F7u,
         RATIONAL,
         RAW_IFD,
@@ -935,6 +1105,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileName
+        "ProfileName",
         0xC6F8u,
         ASCII,
         PROFILE_IFD,
@@ -942,6 +1113,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileHueSatMapDims
+        "ProfileHueSatMapDims",
         0xC6F9u,
         LONG,
         PROFILE_IFD,
@@ -949,6 +1121,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileHueSatMapData1
+        "ProfileHueSatMapData1",
         0xC6FAu,
         FLOAT,
         PROFILE_IFD,
@@ -956,6 +1129,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileHueSatMapData2
+        "ProfileHueSatMapData2",
         0xC6FBu,
         FLOAT,
         PROFILE_IFD,
@@ -963,6 +1137,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileToneCurve
+        "ProfileToneCurve",
         0xC6FCu,
         FLOAT,
         PROFILE_IFD,
@@ -970,6 +1145,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileEmbedPolicy
+        "ProfileEmbedPolicy",
         0xC6FDu,
         LONG,
         PROFILE_IFD,
@@ -977,6 +1153,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileCopyright
+        "ProfileCopyright",
         0xC6FEu,
         ASCII,
         PROFILE_IFD,
@@ -984,6 +1161,7 @@
         UNDEFINED_ENDIAN
     },
     { // ForwardMatrix1
+        "ForwardMatrix1",
         0xC714u,
         SRATIONAL,
         PROFILE_IFD,
@@ -991,6 +1169,7 @@
         UNDEFINED_ENDIAN
     },
     { // ForwardMatrix2
+        "ForwardMatrix2",
         0xC715u,
         SRATIONAL,
         PROFILE_IFD,
@@ -998,6 +1177,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewApplicationName
+        "PreviewApplicationName",
         0xC716u,
         ASCII,
         PREVIEW_IFD,
@@ -1005,6 +1185,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewApplicationVersion
+        "PreviewApplicationVersion",
         0xC717u,
         ASCII,
         PREVIEW_IFD,
@@ -1012,6 +1193,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewSettingsName
+        "PreviewSettingsName",
         0xC718u,
         ASCII,
         PREVIEW_IFD,
@@ -1019,6 +1201,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewSettingsDigest
+        "PreviewSettingsDigest",
         0xC719u,
         BYTE,
         PREVIEW_IFD,
@@ -1026,6 +1209,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewColorSpace
+        "PreviewColorSpace",
         0xC71Au,
         LONG,
         PREVIEW_IFD,
@@ -1033,6 +1217,7 @@
         UNDEFINED_ENDIAN
     },
     { // PreviewDateTime
+        "PreviewDateTime",
         0xC71Bu,
         ASCII,
         PREVIEW_IFD,
@@ -1040,6 +1225,7 @@
         UNDEFINED_ENDIAN
     },
     { // RawImageDigest
+        "RawImageDigest",
         0xC71Cu,
         BYTE,
         IFD_0,
@@ -1047,6 +1233,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalRawFileDigest
+        "OriginalRawFileDigest",
         0xC71Du,
         BYTE,
         IFD_0,
@@ -1054,6 +1241,7 @@
         UNDEFINED_ENDIAN
     },
     { // SubTileBlockSize
+        "SubTileBlockSize",
         0xC71Eu,
         LONG,
         RAW_IFD,
@@ -1061,6 +1249,7 @@
         UNDEFINED_ENDIAN
     },
     { // RowInterleaveFactor
+        "RowInterleaveFactor",
         0xC71Fu,
         LONG,
         RAW_IFD,
@@ -1068,6 +1257,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileLookTableDims
+        "ProfileLookTableDims",
         0xC725u,
         LONG,
         PROFILE_IFD,
@@ -1075,6 +1265,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileLookTableData
+        "ProfileLookTableData",
         0xC726u,
         FLOAT,
         PROFILE_IFD,
@@ -1082,6 +1273,7 @@
         UNDEFINED_ENDIAN
     },
     { // OpcodeList1
+        "OpcodeList1",
         0xC740u,
         UNDEFINED,
         RAW_IFD,
@@ -1089,6 +1281,7 @@
         BIG
     },
     { // OpcodeList2
+        "OpcodeList2",
         0xC741u,
         UNDEFINED,
         RAW_IFD,
@@ -1096,6 +1289,7 @@
         BIG
     },
     { // OpcodeList3
+        "OpcodeList3",
         0xC74Eu,
         UNDEFINED,
         RAW_IFD,
@@ -1103,6 +1297,7 @@
         BIG
     },
     { // NoiseProfile
+        "NoiseProfile",
         0xC761u,
         DOUBLE,
         RAW_IFD,
@@ -1110,6 +1305,7 @@
         UNDEFINED_ENDIAN
     },
     { // DefaultUserCrop
+        "DefaultUserCrop",
         0xC7B5u,
         RATIONAL,
         RAW_IFD,
@@ -1117,6 +1313,7 @@
         UNDEFINED_ENDIAN
     },
     { // DefaultBlackRender
+        "DefaultBlackRender",
         0xC7A6u,
         LONG,
         PROFILE_IFD,
@@ -1124,6 +1321,7 @@
         UNDEFINED_ENDIAN
     },
     { // BaselineExposureOffset
+        "BaselineExposureOffset",
         0xC7A5u,
         RATIONAL,
         PROFILE_IFD,
@@ -1131,6 +1329,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileLookTableEncoding
+        "ProfileLookTableEncoding",
         0xC7A4u,
         LONG,
         PROFILE_IFD,
@@ -1138,6 +1337,7 @@
         UNDEFINED_ENDIAN
     },
     { // ProfileHueSatMapEncoding
+        "ProfileHueSatMapEncoding",
         0xC7A3u,
         LONG,
         PROFILE_IFD,
@@ -1145,6 +1345,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalDefaultFinalSize
+        "OriginalDefaultFinalSize",
         0xC791u,
         LONG,
         IFD_0,
@@ -1152,6 +1353,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalBestQualityFinalSize
+        "OriginalBestQualityFinalSize",
         0xC792u,
         LONG,
         IFD_0,
@@ -1159,6 +1361,7 @@
         UNDEFINED_ENDIAN
     },
     { // OriginalDefaultCropSize
+        "OriginalDefaultCropSize",
         0xC793u,
         LONG,
         IFD_0,
@@ -1166,6 +1369,7 @@
         UNDEFINED_ENDIAN
     },
     { // NewRawImageDigest
+        "NewRawImageDigest",
         0xC7A7u,
         BYTE,
         IFD_0,
@@ -1173,6 +1377,7 @@
         UNDEFINED_ENDIAN
     },
     { // RawToPreviewGain
+        "RawToPreviewGain",
         0xC7A8u,
         DOUBLE,
         PREVIEW_IFD,
diff --git a/media/img_utils/include/img_utils/TiffEntry.h b/media/img_utils/include/img_utils/TiffEntry.h
index cd01640..4d672b2 100644
--- a/media/img_utils/include/img_utils/TiffEntry.h
+++ b/media/img_utils/include/img_utils/TiffEntry.h
@@ -34,10 +34,11 @@
 
 /**
  * This class holds a single TIFF IFD entry.
+ *
+ * Subclasses are expected to support assignment and copying operations.
  */
 class ANDROID_API TiffEntry : public TiffWritable {
     public:
-        // TODO: Copy constructor/equals here.
         virtual ~TiffEntry();
 
         /**
@@ -83,7 +84,7 @@
         template<typename T>
         const T* getData() const;
 
-        String8 toString() const;
+        virtual String8 toString() const;
 
         /**
          * Force the type used here to be a valid TIFF type.
@@ -99,10 +100,10 @@
         COMPARE_DEF(>)
         COMPARE_DEF(<)
 
-        protected:
-            enum {
-                MAX_PRINT_STRING_LENGTH = 256
-            };
+    protected:
+        enum {
+            MAX_PRINT_STRING_LENGTH = 256
+        };
 };
 
 #define COMPARE(op) \
diff --git a/media/img_utils/include/img_utils/TiffEntryImpl.h b/media/img_utils/include/img_utils/TiffEntryImpl.h
index cbe0e9a..f5ccb5e 100644
--- a/media/img_utils/include/img_utils/TiffEntryImpl.h
+++ b/media/img_utils/include/img_utils/TiffEntryImpl.h
@@ -17,6 +17,7 @@
 #ifndef IMG_UTILS_TIFF_ENTRY_IMPL
 #define IMG_UTILS_TIFF_ENTRY_IMPL
 
+#include <img_utils/TiffIfd.h>
 #include <img_utils/TiffEntry.h>
 #include <img_utils/TiffHelpers.h>
 #include <img_utils/Output.h>
@@ -24,6 +25,8 @@
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
+#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
 #include <stdint.h>
 
 namespace android {
@@ -32,7 +35,6 @@
 template<typename T>
 class TiffEntryImpl : public TiffEntry {
     public:
-        // TODO: Copy constructor/equals here.
         TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end, const T* data);
         virtual ~TiffEntryImpl();
 
@@ -54,7 +56,7 @@
         uint16_t mType;
         uint32_t mCount;
         Endianness mEnd;
-        T* mData;
+        Vector<T> mData;
 
 };
 
@@ -63,18 +65,12 @@
         const T* data)
         : mTag(tag), mType(static_cast<uint16_t>(type)), mCount(count), mEnd(end) {
     count = (type == RATIONAL || type == SRATIONAL) ? count * 2 : count;
-    mData = new T[count]();
-    for (uint32_t i = 0; i < count; ++i) {
-        mData[i] = data[i];
-    }
+    ssize_t index = mData.appendArray(data, count);
+    LOG_ALWAYS_FATAL_IF(index < 0, "%s: Could not allocate vector for data.", __FUNCTION__);
 }
 
 template<typename T>
-TiffEntryImpl<T>::~TiffEntryImpl() {
-    if (mData) {
-        delete[] mData;
-    }
-}
+TiffEntryImpl<T>::~TiffEntryImpl() {}
 
 template<typename T>
 uint32_t TiffEntryImpl<T>::getCount() const {
@@ -93,7 +89,7 @@
 
 template<typename T>
 const void* TiffEntryImpl<T>::getDataHelper() const {
-    return reinterpret_cast<const void*>(mData);
+    return reinterpret_cast<const void*>(mData.array());
 }
 
 template<typename T>
@@ -144,7 +140,7 @@
              */
             count <<= 1;
         }
-        BAIL_ON_FAIL(out->write(mData, 0, count), ret);
+        BAIL_ON_FAIL(out->write(mData.array(), 0, count), ret);
         ZERO_TILL_WORD(out, dataSize, ret);
     }
     return ret;
@@ -171,7 +167,7 @@
         count <<= 1;
     }
 
-    BAIL_ON_FAIL(out->write(mData, 0, count), ret);
+    BAIL_ON_FAIL(out->write(mData.array(), 0, count), ret);
 
     if (mEnd != UNDEFINED_ENDIAN) {
         out->setEndianness(tmp);
@@ -182,6 +178,38 @@
     return ret;
 }
 
+template<>
+inline status_t TiffEntryImpl<sp<TiffIfd> >::writeTagInfo(uint32_t offset,
+        /*out*/EndianOutput* out) const {
+    assert((offset % TIFF_WORD_SIZE) == 0);
+    status_t ret = OK;
+    BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
+    BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
+    BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
+
+    BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
+    return ret;
+}
+
+template<>
+inline uint32_t TiffEntryImpl<sp<TiffIfd> >::getActualSize() const {
+    uint32_t total = 0;
+    for (size_t i = 0; i < mData.size(); ++i) {
+        total += mData[i]->getSize();
+    }
+    return total;
+}
+
+template<>
+inline status_t TiffEntryImpl<sp<TiffIfd> >::writeData(uint32_t offset, EndianOutput* out) const {
+    status_t ret = OK;
+    for (uint32_t i = 0; i < mCount; ++i) {
+        BAIL_ON_FAIL(mData[i]->writeData(offset, out), ret);
+        offset += mData[i]->getSize();
+    }
+    return ret;
+}
+
 } /*namespace img_utils*/
 } /*namespace android*/
 
diff --git a/media/img_utils/include/img_utils/TiffHelpers.h b/media/img_utils/include/img_utils/TiffHelpers.h
index fd0ea7a..0969e4d 100644
--- a/media/img_utils/include/img_utils/TiffHelpers.h
+++ b/media/img_utils/include/img_utils/TiffHelpers.h
@@ -37,7 +37,7 @@
     { \
         size_t remaining = BYTES_TILL_WORD(index); \
         if (remaining > 0) { \
-            BAIL_ON_FAIL(output->write(ZERO_WORD, 0, remaining), ret); \
+            BAIL_ON_FAIL((output)->write(ZERO_WORD, 0, remaining), ret); \
         } \
     }
 
diff --git a/media/img_utils/include/img_utils/TiffIfd.h b/media/img_utils/include/img_utils/TiffIfd.h
index 9400456..51b5c9a 100644
--- a/media/img_utils/include/img_utils/TiffIfd.h
+++ b/media/img_utils/include/img_utils/TiffIfd.h
@@ -42,7 +42,6 @@
  */
 class ANDROID_API TiffIfd : public TiffWritable {
     public:
-        // TODO: Copy constructor/equals here - needed for SubIfds.
         TiffIfd(uint32_t ifdId);
         virtual ~TiffIfd();
 
@@ -98,9 +97,50 @@
         virtual sp<TiffEntry> getEntry(uint16_t tag) const;
 
         /**
+         * Remove the entry with the given tag ID if it exists.
+         */
+        virtual void removeEntry(uint16_t tag);
+
+        /**
+         * Convenience method to validate and set strip-related image tags.
+         *
+         * This sets all strip related tags, but leaves offset values unitialized.
+         * setStripOffsets must be called with the desired offset before writing.
+         * The strip tag values are calculated from the existing tags for image
+         * dimensions and pixel type set in the IFD.
+         *
+         * Does not handle planar image configurations (PlanarConfiguration != 1).
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t validateAndSetStripTags();
+
+        /**
+         * Returns true if validateAndSetStripTags has been called, but not setStripOffsets.
+         */
+        virtual bool uninitializedOffsets() const;
+
+        /**
+         * Convenience method to set beginning offset for strips.
+         *
+         * Call this to update the strip offsets before calling writeData.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t setStripOffset(uint32_t offset);
+
+        /**
+         * Get the total size of the strips in bytes.
+         *
+         * This sums the byte count at each strip offset, and returns
+         * the total count of bytes stored in strips for this IFD.
+         */
+        virtual uint32_t getStripSize() const;
+
+        /**
          * Get a formatted string representing this IFD.
          */
-        String8 toString() const;
+        virtual String8 toString() const;
 
         /**
          * Print a formatted string representing this IFD to logcat.
@@ -111,11 +151,13 @@
          * Get value used to determine sort order.
          */
         virtual uint32_t getComparableValue() const;
+
     protected:
         virtual uint32_t checkAndGetOffset(uint32_t offset) const;
         SortedEntryVector mEntries;
         sp<TiffIfd> mNextIfd;
         uint32_t mIfdId;
+        bool mStripOffsetsInitialized;
 };
 
 } /*namespace img_utils*/
diff --git a/media/img_utils/include/img_utils/TiffWriter.h b/media/img_utils/include/img_utils/TiffWriter.h
index ec27fc3..b7af239 100644
--- a/media/img_utils/include/img_utils/TiffWriter.h
+++ b/media/img_utils/include/img_utils/TiffWriter.h
@@ -18,8 +18,10 @@
 #define IMG_UTILS_TIFF_WRITER_H
 
 #include <img_utils/EndianUtils.h>
+#include <img_utils/StripSource.h>
 #include <img_utils/TiffEntryImpl.h>
 #include <img_utils/TagDefinitions.h>
+#include <img_utils/TiffIfd.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -48,6 +50,10 @@
  */
 class ANDROID_API TiffWriter : public LightRefBase<TiffWriter> {
     public:
+        enum SubIfdType {
+            SUBIFD = 0,
+            GPSINFO
+        };
 
         /**
          * Constructs a TiffWriter with the default tag mappings. This enables
@@ -77,6 +83,25 @@
          * Write a TIFF header containing each IFD set.  This will recursively
          * write all SubIFDs and tags.
          *
+         * Any StripSources passed in will be written to the output as image strips
+         * at the appropriate offests.  The StripByteCounts, RowsPerStrip, and
+         * StripOffsets tags must be set to use this.  To set these tags in a
+         * given IFD, use the addStrip method.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t write(Output* out, StripSource** sources, size_t sourcesCount,
+                Endianness end = LITTLE);
+
+        /**
+         * Write a TIFF header containing each IFD set.  This will recursively
+         * write all SubIFDs and tags.
+         *
+         * Image data for strips or tiles must be written separately at the
+         * appropriate offsets.  These offsets must not fall within the file
+         * header written this way.  The size of the header written is given
+         * by the getTotalSize() method.
+         *
          * Returns OK on success, or a negative error code on failure.
          */
         virtual status_t write(Output* out, Endianness end = LITTLE);
@@ -88,10 +113,35 @@
         virtual uint32_t getTotalSize() const;
 
         /**
-         * Add the given entry to its default IFD.  If that IFD does not
-         * exist, it will be created.
+         * Add an entry to the IFD with the given ID.
+         *
+         * Returns OK on success, or a negative error code on failure. Valid
+         * error codes for this method are:
+         * - BAD_INDEX - The given tag doesn't exist.
+         * - BAD_VALUE - The given count doesn't match the required count for
+         *               this tag.
+         * - BAD_TYPE  - The type of the given data isn't compatible with the
+         *               type required for this tag.
+         * - NAME_NOT_FOUND - No ifd exists with the given ID.
          */
-        virtual status_t addEntry(const sp<TiffEntry>& entry);
+        virtual status_t addEntry(const sp<TiffEntry>& entry, uint32_t ifd);
+
+        /**
+         * Build an entry for a known tag and add it to the IFD with the given ID.
+         * This tag must be defined in one of the definition vectors this TIFF writer
+         * was constructed with. The count and type are validated.
+         *
+         * Returns OK on success, or a negative error code on failure. Valid
+         * error codes for this method are:
+         * - BAD_INDEX - The given tag doesn't exist.
+         * - BAD_VALUE - The given count doesn't match the required count for
+         *               this tag.
+         * - BAD_TYPE  - The type of the given data isn't compatible with the
+         *               type required for this tag.
+         * - NAME_NOT_FOUND - No ifd exists with the given ID.
+         */
+        template<typename T>
+        status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
 
         /**
          * Build an entry for a known tag.  This tag must be one of the tags
@@ -111,23 +161,19 @@
         status_t buildEntry(uint16_t tag, uint32_t count, const T* data,
                   /*out*/sp<TiffEntry>* outEntry) const;
 
-         /**
-         * Build an entry for a known tag and add it to the IFD with the given ID.
-         * This tag must be defined in one of the definition vectors this TIFF writer
-         * was constructed with. The count and type are validated. If this succeeds,
-         * the resulting entry will be placed in the outEntry pointer.
+        /**
+         * Convenience function to set the strip related tags for a given IFD.
          *
-         * Returns OK on success, or a negative error code on failure. Valid
-         * error codes for this method are:
-         * - BAD_INDEX - The given tag doesn't exist.
-         * - BAD_VALUE - The given count doesn't match the required count for
-         *               this tag.
-         * - BAD_TYPE  - The type of the given data isn't compatible with the
-         *               type required for this tag.
-         * - NAME_NOT_FOUND - No ifd exists with the given ID.
+         * Call this before using a StripSource as an input to write.
+         * The following tags must be set before calling this method:
+         * - ImageWidth
+         * - ImageLength
+         * - SamplesPerPixel
+         * - BitsPerSample
+         *
+         * Returns OK on success, or a negative error code.
          */
-        template<typename T>
-        status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
+        virtual status_t addStrip(uint32_t ifd);
 
         /**
          * Return the TIFF entry with the given tag ID in the IFD with the given ID,
@@ -136,12 +182,9 @@
         virtual sp<TiffEntry> getEntry(uint16_t tag, uint32_t ifd) const;
 
         /**
-         * Add the given IFD to the end of the top-level IFD chain. No
-         * validation is done.
-         *
-         * Returns OK on success, or a negative error code on failure.
+         * Remove the TIFF entry with the given tag ID in the given IFD if it exists.
          */
-        virtual status_t uncheckedAddIfd(const sp<TiffIfd>& ifd);
+        virtual void removeEntry(uint16_t tag, uint32_t ifd);
 
         /**
          * Create an empty IFD with the given ID and add it to the end of the
@@ -150,6 +193,44 @@
         virtual status_t addIfd(uint32_t ifd);
 
         /**
+         * Create an empty IFD with the given ID and add it as a SubIfd of the
+         * parent IFD.
+         */
+        virtual status_t addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type = SUBIFD);
+
+        /**
+         * Returns the default type for the given tag ID.
+         */
+        virtual TagType getDefaultType(uint16_t tag) const;
+
+        /**
+         * Returns the default count for a given tag ID, or 0 if this
+         * tag normally has a variable count.
+         */
+        virtual uint32_t getDefaultCount(uint16_t tag) const;
+
+        /**
+         * Returns true if an IFD with the given ID exists.
+         */
+        virtual bool hasIfd(uint32_t ifd) const;
+
+        /**
+         * Returns true if a definition exist for the given tag ID.
+         */
+        virtual bool checkIfDefined(uint16_t tag) const;
+
+        /**
+         * Returns the name of the tag if a definition exists for the given tag
+         * ID, or null if no definition exists.
+         */
+        virtual const char* getTagName(uint16_t tag) const;
+
+        /**
+         * Print the currently configured IFDs and entries to logcat.
+         */
+        virtual void log() const;
+
+        /**
          * Build an entry.  No validation is done.
          *
          * WARNING: Using this method can result in creating poorly formatted
@@ -169,27 +250,6 @@
         static KeyedVector<uint16_t, const TagDefinition_t*> buildTagMap(
                   const TagDefinition_t* definitions, size_t length);
 
-        /**
-         * Returns the default type for the given tag ID.
-         */
-        virtual TagType getDefaultType(uint16_t tag) const;
-
-        /**
-         * Returns the default count for a given tag ID, or 0 if this
-         * tag normally has a variable count.
-         */
-        virtual uint32_t getDefaultCount(uint16_t tag) const;
-
-        /**
-         * Returns true if a definition exist for the given tag ID.
-         */
-        virtual bool checkIfDefined(uint16_t tag) const;
-
-        /**
-         * Print the currently configured IFDs and entries to logcat.
-         */
-        virtual void log() const;
-
     protected:
         enum {
             DEFAULT_NUM_TAG_MAPS = 4,
@@ -240,17 +300,14 @@
 template<typename T>
 status_t TiffWriter::addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd) {
     sp<TiffEntry> outEntry;
+
     status_t ret = buildEntry<T>(tag, count, data, &outEntry);
     if (ret != OK) {
         ALOGE("%s: Could not build entry for tag %x.", __FUNCTION__, tag);
         return ret;
     }
-    ssize_t index = mNamedIfds.indexOfKey(ifd);
-    if (index < 0) {
-        ALOGE("%s: No IFD %d set for this writer.", __FUNCTION__, ifd);
-        return NAME_NOT_FOUND;
-    }
-    return mNamedIfds[index]->addEntry(outEntry);
+
+    return addEntry(outEntry, ifd);
 }
 
 template<typename T>
diff --git a/media/img_utils/src/Android.mk b/media/img_utils/src/Android.mk
index 80893be..4074849 100644
--- a/media/img_utils/src/Android.mk
+++ b/media/img_utils/src/Android.mk
@@ -31,6 +31,7 @@
   TiffEntryImpl.cpp \
   ByteArrayOutput.cpp \
   DngUtils.cpp \
+  StripSource.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
   libexpat \
diff --git a/media/img_utils/src/FileInput.cpp b/media/img_utils/src/FileInput.cpp
index e43fd53..498e715 100644
--- a/media/img_utils/src/FileInput.cpp
+++ b/media/img_utils/src/FileInput.cpp
@@ -45,19 +45,24 @@
     return OK;
 }
 
-size_t FileInput::read(uint8_t* buf, size_t offset, size_t count, status_t* err) {
+ssize_t FileInput::read(uint8_t* buf, size_t offset, size_t count) {
     if (!mOpen) {
         ALOGE("%s: Could not read file %s, file not open.", __FUNCTION__, mPath.string());
-        if (err != NULL) *err = BAD_VALUE;
-        return 0;
+        return BAD_VALUE;
     }
 
     size_t bytesRead = ::fread(buf + offset, sizeof(uint8_t), count, mFp);
     int error = ::ferror(mFp);
     if (error != 0) {
         ALOGE("%s: Error %d occurred while reading file %s.", __FUNCTION__, error, mPath.string());
-        if (err != NULL) *err = BAD_VALUE;
+        return BAD_VALUE;
     }
+
+    // End of file reached
+    if (::feof(mFp) != 0 && bytesRead == 0) {
+        return NOT_ENOUGH_DATA;
+    }
+
     return bytesRead;
 }
 
diff --git a/media/img_utils/src/Input.cpp b/media/img_utils/src/Input.cpp
index 1e51e10..3782014 100644
--- a/media/img_utils/src/Input.cpp
+++ b/media/img_utils/src/Input.cpp
@@ -20,9 +20,37 @@
 namespace img_utils {
 
 Input::~Input() {}
+
 status_t Input::open() { return OK; }
+
 status_t Input::close() { return OK; }
 
+ssize_t Input::skip(size_t count) {
+    const size_t SKIP_BUF_SIZE = 1024;
+    uint8_t skipBuf[SKIP_BUF_SIZE];
+
+    size_t remaining = count;
+    while (remaining > 0) {
+        size_t amt = (SKIP_BUF_SIZE > remaining) ? remaining : SKIP_BUF_SIZE;
+        ssize_t ret = read(skipBuf, 0, amt);
+        if (ret < 0) {
+            if(ret == NOT_ENOUGH_DATA) {
+                // End of file encountered
+                if (remaining == count) {
+                    // Read no bytes, return EOF
+                    return NOT_ENOUGH_DATA;
+                } else {
+                    // Return num bytes read
+                    return count - remaining;
+                }
+            }
+            // Return error code.
+            return ret;
+        }
+        remaining -= ret;
+    }
+    return count;
+}
 
 } /*namespace img_utils*/
 } /*namespace android*/
diff --git a/media/img_utils/src/StripSource.cpp b/media/img_utils/src/StripSource.cpp
new file mode 100644
index 0000000..57b6082
--- /dev/null
+++ b/media/img_utils/src/StripSource.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <img_utils/StripSource.h>
+
+namespace android {
+namespace img_utils {
+
+StripSource::~StripSource() {}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffEntry.cpp b/media/img_utils/src/TiffEntry.cpp
index e028827..9cea721 100644
--- a/media/img_utils/src/TiffEntry.cpp
+++ b/media/img_utils/src/TiffEntry.cpp
@@ -32,16 +32,6 @@
  *
  * Values with types other than the ones given here should not compile.
  */
-template<>
-const Vector<sp<TiffIfd> >* TiffEntry::forceValidType<Vector<sp<TiffIfd> > >(TagType type,
-          const Vector<sp<TiffIfd> >* value) {
-    if (type == LONG) {
-        return value;
-    }
-    ALOGE("%s: Value of type 'ifd vector' is not valid for tag with TIFF type %d.",
-            __FUNCTION__, type);
-    return NULL;
-}
 
 template<>
 const sp<TiffIfd>* TiffEntry::forceValidType<sp<TiffIfd> >(TagType type, const sp<TiffIfd>* value) {
diff --git a/media/img_utils/src/TiffEntryImpl.cpp b/media/img_utils/src/TiffEntryImpl.cpp
index 6efa458..257c266 100644
--- a/media/img_utils/src/TiffEntryImpl.cpp
+++ b/media/img_utils/src/TiffEntryImpl.cpp
@@ -15,30 +15,11 @@
  */
 
 #include <img_utils/TiffEntryImpl.h>
-#include <img_utils/TiffIfd.h>
 
 #include <utils/Vector.h>
 
 namespace android {
 namespace img_utils {
 
-template<>
-size_t TiffEntryImpl<TiffIfd>::getSize() const {
-    uint32_t total = 0;
-    for (uint32_t i = 0; i < mCount; ++i) {
-        total += mData[i].getSize();
-    }
-    return total;
-}
-
-template<>
-status_t TiffEntryImpl<TiffIfd>::writeData(uint32_t offset, EndianOutput* out) const {
-    status_t ret = OK;
-    for (uint32_t i = 0; i < mCount; ++i) {
-        BAIL_ON_FAIL(mData[i].writeData(offset, out), ret);
-    }
-    return ret;
-}
-
 } /*namespace img_utils*/
 } /*namespace android*/
diff --git a/media/img_utils/src/TiffIfd.cpp b/media/img_utils/src/TiffIfd.cpp
index 1b3b40d..3fb00cc 100644
--- a/media/img_utils/src/TiffIfd.cpp
+++ b/media/img_utils/src/TiffIfd.cpp
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
-#include <img_utils/TiffIfd.h>
+#define LOG_TAG "TiffIfd"
+
+#include <img_utils/TagDefinitions.h>
 #include <img_utils/TiffHelpers.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
 
 #include <utils/Log.h>
 
@@ -23,7 +27,7 @@
 namespace img_utils {
 
 TiffIfd::TiffIfd(uint32_t ifdId)
-        : mNextIfd(), mIfdId(ifdId) {}
+        : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
 
 TiffIfd::~TiffIfd() {}
 
@@ -52,6 +56,14 @@
     return mEntries[index];
 }
 
+void TiffIfd::removeEntry(uint16_t tag) {
+    ssize_t index = mEntries.indexOfTag(tag);
+    if (index >= 0) {
+        mEntries.removeAt(index);
+    }
+}
+
+
 void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
     mNextIfd = ifd;
 }
@@ -156,6 +168,198 @@
     return mIfdId;
 }
 
+status_t TiffIfd::validateAndSetStripTags() {
+    sp<TiffEntry> widthEntry = getEntry(TAG_IMAGEWIDTH);
+    if (widthEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a ImageWidth tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> heightEntry = getEntry(TAG_IMAGELENGTH);
+    if (heightEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a ImageLength tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> samplesEntry = getEntry(TAG_SAMPLESPERPIXEL);
+    if (samplesEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a SamplesPerPixel tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> bitsEntry = getEntry(TAG_BITSPERSAMPLE);
+    if (bitsEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a BitsPerSample tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t width = *(widthEntry->getData<uint32_t>());
+    uint32_t height = *(heightEntry->getData<uint32_t>());
+    uint16_t bitsPerSample = *(bitsEntry->getData<uint16_t>());
+    uint16_t samplesPerPixel = *(samplesEntry->getData<uint16_t>());
+
+    if ((bitsPerSample % 8) != 0) {
+        ALOGE("%s: BitsPerSample %d in IFD %u is not byte-aligned.", __FUNCTION__,
+                bitsPerSample, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t bytesPerSample = bitsPerSample / 8;
+
+    // Choose strip size as close to 8kb as possible without splitting rows.
+    // If the row length is >8kb, each strip will only contain a single row.
+    const uint32_t rowLengthBytes = bytesPerSample * samplesPerPixel * width;
+    const uint32_t idealChunkSize = (1 << 13); // 8kb
+    uint32_t rowsPerChunk = idealChunkSize / rowLengthBytes;
+    rowsPerChunk = (rowsPerChunk == 0) ? 1 : rowsPerChunk;
+    const uint32_t actualChunkSize = rowLengthBytes * rowsPerChunk;
+
+    const uint32_t lastChunkRows = height % rowsPerChunk;
+    const uint32_t lastChunkSize = lastChunkRows * rowLengthBytes;
+
+    if (actualChunkSize > /*max strip size for TIFF/EP*/65536) {
+        ALOGE("%s: Strip length too long.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    size_t numStrips = height / rowsPerChunk;
+
+    // Add another strip for the incomplete chunk.
+    if (lastChunkRows > 0) {
+        numStrips += 1;
+    }
+
+    // Put each row in it's own strip
+    uint32_t rowsPerStripVal = rowsPerChunk;
+    sp<TiffEntry> rowsPerStrip = TiffWriter::uncheckedBuildEntry(TAG_ROWSPERSTRIP, LONG, 1,
+            UNDEFINED_ENDIAN, &rowsPerStripVal);
+
+    if (rowsPerStrip == NULL) {
+        ALOGE("%s: Could not build entry for RowsPerStrip tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Vector<uint32_t> byteCounts;
+
+    for (size_t i = 0; i < numStrips; ++i) {
+        if (lastChunkRows > 0 && i == (numStrips - 1)) {
+            byteCounts.add(lastChunkSize);
+        } else {
+            byteCounts.add(actualChunkSize);
+        }
+    }
+
+    // Set byte counts for each strip
+    sp<TiffEntry> stripByteCounts = TiffWriter::uncheckedBuildEntry(TAG_STRIPBYTECOUNTS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, byteCounts.array());
+
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: Could not build entry for StripByteCounts tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Vector<uint32_t> stripOffsetsVector;
+    stripOffsetsVector.resize(numStrips);
+
+    // Set uninitialized offsets
+    sp<TiffEntry> stripOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsetsVector.array());
+
+    if (stripOffsets == NULL) {
+        ALOGE("%s: Could not build entry for StripOffsets tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(stripByteCounts) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(rowsPerStrip) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(stripOffsets) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    mStripOffsetsInitialized = true;
+    return OK;
+}
+
+bool TiffIfd::uninitializedOffsets() const {
+    return mStripOffsetsInitialized;
+}
+
+status_t TiffIfd::setStripOffset(uint32_t offset) {
+
+    // Get old offsets and bytecounts
+    sp<TiffEntry> oldOffsets = getEntry(TAG_STRIPOFFSETS);
+    if (oldOffsets == NULL) {
+        ALOGE("%s: IFD %u does not contain StripOffsets entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t offsetsCount = oldOffsets->getCount();
+    uint32_t byteCount = stripByteCounts->getCount();
+    if (offsetsCount != byteCount) {
+        ALOGE("%s: StripOffsets count (%u) doesn't match StripByteCounts count (%u) in IFD %u",
+            __FUNCTION__, offsetsCount, byteCount, mIfdId);
+        return BAD_VALUE;
+    }
+
+    const uint32_t* stripByteCountsArray = stripByteCounts->getData<uint32_t>();
+
+    size_t numStrips = offsetsCount;
+
+    Vector<uint32_t> stripOffsets;
+
+    // Calculate updated byte offsets
+    for (size_t i = 0; i < numStrips; ++i) {
+        stripOffsets.add(offset);
+        offset += stripByteCountsArray[i];
+    }
+
+    sp<TiffEntry> newOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsets.array());
+
+    if (newOffsets == NULL) {
+        ALOGE("%s: Coult not build updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if (addEntry(newOffsets) != OK) {
+        ALOGE("%s: Failed to add updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+uint32_t TiffIfd::getStripSize() const {
+    sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t count = stripByteCounts->getCount();
+    const uint32_t* byteCounts = stripByteCounts->getData<uint32_t>();
+
+    uint32_t total = 0;
+    for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
+        total += byteCounts[i];
+    }
+    return total;
+}
+
 String8 TiffIfd::toString() const {
     size_t s = mEntries.size();
     String8 output;
diff --git a/media/img_utils/src/TiffWriter.cpp b/media/img_utils/src/TiffWriter.cpp
index 2439033..d85289e 100644
--- a/media/img_utils/src/TiffWriter.cpp
+++ b/media/img_utils/src/TiffWriter.cpp
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <img_utils/TiffIfd.h>
+#define LOG_TAG "TiffWriter"
+
 #include <img_utils/TiffHelpers.h>
 #include <img_utils/TiffWriter.h>
 #include <img_utils/TagDefinitions.h>
@@ -55,6 +56,82 @@
 
 TiffWriter::~TiffWriter() {}
 
+status_t TiffWriter::write(Output* out, StripSource** sources, size_t sourcesCount,
+        Endianness end) {
+    status_t ret = OK;
+    EndianOutput endOut(out, end);
+
+    if (mIfd == NULL) {
+        ALOGE("%s: Tiff header is empty.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (LOG_NDEBUG == 0) {
+        log();
+    }
+
+    uint32_t totalSize = getTotalSize();
+
+    KeyedVector<uint32_t, uint32_t> offsetVector;
+
+    for (size_t i = 0; i < mNamedIfds.size(); ++i) {
+        if (mNamedIfds[i]->uninitializedOffsets()) {
+            uint32_t stripSize = mNamedIfds[i]->getStripSize();
+            if (mNamedIfds[i]->setStripOffset(totalSize) != OK) {
+                ALOGE("%s: Could not set strip offsets.", __FUNCTION__);
+                return BAD_VALUE;
+            }
+            totalSize += stripSize;
+            WORD_ALIGN(totalSize);
+            offsetVector.add(mNamedIfds.keyAt(i), totalSize);
+        }
+    }
+
+    size_t offVecSize = offsetVector.size();
+    if (offVecSize != sourcesCount) {
+        ALOGE("%s: Mismatch between number of IFDs with uninitialized strips (%zu) and"
+                " sources (%zu).", __FUNCTION__, offVecSize, sourcesCount);
+        return BAD_VALUE;
+    }
+
+    BAIL_ON_FAIL(writeFileHeader(endOut), ret);
+
+    uint32_t offset = FILE_HEADER_SIZE;
+    sp<TiffIfd> ifd = mIfd;
+    while(ifd != NULL) {
+        BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
+        offset += ifd->getSize();
+        ifd = ifd->getNextIfd();
+    }
+
+    log();
+
+    for (size_t i = 0; i < offVecSize; ++i) {
+        uint32_t ifdKey = offsetVector.keyAt(i);
+        uint32_t nextOffset = offsetVector[i];
+        uint32_t sizeToWrite = mNamedIfds[ifdKey]->getStripSize();
+        bool found = false;
+        for (size_t j = 0; j < sourcesCount; ++j) {
+            if (sources[j]->getIfd() == ifdKey) {
+                if ((ret = sources[i]->writeToStream(endOut, sizeToWrite)) != OK) {
+                    ALOGE("%s: Could not write to stream, received %d.", __FUNCTION__, ret);
+                    return ret;
+                }
+                ZERO_TILL_WORD(&endOut, sizeToWrite, ret);
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            ALOGE("%s: No stream for byte strips for IFD %u", __FUNCTION__, ifdKey);
+            return BAD_VALUE;
+        }
+        assert(nextOffset == endOut.getCurrentOffset());
+    }
+
+    return ret;
+}
+
 status_t TiffWriter::write(Output* out, Endianness end) {
     status_t ret = OK;
     EndianOutput endOut(out, end);
@@ -101,48 +178,43 @@
     return mNamedIfds[index]->getEntry(tag);
 }
 
+void TiffWriter::removeEntry(uint16_t tag, uint32_t ifd) {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index >= 0) {
+        mNamedIfds[index]->removeEntry(tag);
+    }
+}
 
-// TODO: Fix this to handle IFD position in chain/sub-IFD tree
-status_t TiffWriter::addEntry(const sp<TiffEntry>& entry) {
+status_t TiffWriter::addEntry(const sp<TiffEntry>& entry, uint32_t ifd) {
     uint16_t tag = entry->getTag();
 
     const TagDefinition_t* definition = lookupDefinition(tag);
 
     if (definition == NULL) {
+        ALOGE("%s: No definition exists for tag 0x%x.", __FUNCTION__, tag);
         return BAD_INDEX;
     }
-    uint32_t ifdId = 0;  // TODO: all in IFD0 for now.
 
-    ssize_t index = mNamedIfds.indexOfKey(ifdId);
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
 
     // Add a new IFD if necessary
     if (index < 0) {
-        sp<TiffIfd> ifdEntry = new TiffIfd(ifdId);
-        if (mIfd == NULL) {
-            mIfd = ifdEntry;
-        }
-        index = mNamedIfds.add(ifdId, ifdEntry);
-        assert(index >= 0);
+        ALOGE("%s: No IFD %u exists.", __FUNCTION__, ifd);
+        return NAME_NOT_FOUND;
     }
 
     sp<TiffIfd> selectedIfd  = mNamedIfds[index];
     return selectedIfd->addEntry(entry);
 }
 
-status_t TiffWriter::uncheckedAddIfd(const sp<TiffIfd>& ifd) {
-    mNamedIfds.add(ifd->getId(), ifd);
-    sp<TiffIfd> last = findLastIfd();
-    if (last == NULL) {
-        mIfd = ifd;
-    } else {
-        last->setNextIfd(ifd);
+status_t TiffWriter::addStrip(uint32_t ifd) {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index < 0) {
+        ALOGE("%s: Ifd %u doesn't exist, cannot add strip entries.", __FUNCTION__, ifd);
+        return BAD_VALUE;
     }
-    last = ifd->getNextIfd();
-    while (last != NULL) {
-        mNamedIfds.add(last->getId(), last);
-        last = last->getNextIfd();
-    }
-    return OK;
+    sp<TiffIfd> selected = mNamedIfds[index];
+    return selected->validateAndSetStripTags();
 }
 
 status_t TiffWriter::addIfd(uint32_t ifd) {
@@ -151,6 +223,7 @@
         ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
         return BAD_VALUE;
     }
+
     sp<TiffIfd> newIfd = new TiffIfd(ifd);
     if (mIfd == NULL) {
         mIfd = newIfd;
@@ -158,7 +231,83 @@
         sp<TiffIfd> last = findLastIfd();
         last->setNextIfd(newIfd);
     }
-    mNamedIfds.add(ifd, newIfd);
+
+    if(mNamedIfds.add(ifd, newIfd) < 0) {
+        ALOGE("%s: Failed to add new IFD 0x%x.", __FUNCTION__, ifd);
+        return BAD_VALUE;
+    }
+
+    return OK;
+}
+
+status_t TiffWriter::addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type) {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index >= 0) {
+        ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
+        return BAD_VALUE;
+    }
+
+    ssize_t parentIndex = mNamedIfds.indexOfKey(parentIfd);
+    if (parentIndex < 0) {
+        ALOGE("%s: Parent IFD with ID 0x%x does not exist.", __FUNCTION__, parentIfd);
+        return BAD_VALUE;
+    }
+
+    sp<TiffIfd> parent = mNamedIfds[parentIndex];
+    sp<TiffIfd> newIfd = new TiffIfd(ifd);
+
+    uint16_t subIfdTag;
+    if (type == SUBIFD) {
+        subIfdTag = TAG_SUBIFDS;
+    } else if (type == GPSINFO) {
+        subIfdTag = TAG_GPSINFO;
+    } else {
+        ALOGE("%s: Unknown SubIFD type %d.", __FUNCTION__, type);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> subIfds = parent->getEntry(subIfdTag);
+    if (subIfds == NULL) {
+        if (buildEntry(subIfdTag, 1, &newIfd, &subIfds) < 0) {
+            ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
+            return BAD_VALUE;
+        }
+    } else {
+        if (type == GPSINFO) {
+            ALOGE("%s: Cannot add GPSInfo SubIFD to IFD %u, one already exists.", __FUNCTION__,
+                    ifd);
+            return BAD_VALUE;
+        }
+
+        Vector<sp<TiffIfd> > subIfdList;
+        const sp<TiffIfd>* oldIfdArray = subIfds->getData<sp<TiffIfd> >();
+        if (subIfdList.appendArray(oldIfdArray, subIfds->getCount()) < 0) {
+            ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
+            return BAD_VALUE;
+        }
+
+        if (subIfdList.add(newIfd) < 0) {
+            ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
+            return BAD_VALUE;
+        }
+
+        uint32_t count = subIfdList.size();
+        if (buildEntry(subIfdTag, count, subIfdList.array(), &subIfds) < 0) {
+            ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
+            return BAD_VALUE;
+        }
+    }
+
+    if (parent->addEntry(subIfds) < 0) {
+        ALOGE("%s: Failed to add SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
+        return BAD_VALUE;
+    }
+
+    if(mNamedIfds.add(ifd, newIfd) < 0) {
+        ALOGE("%s: Failed to add new IFD 0x%x.", __FUNCTION__, ifd);
+        return BAD_VALUE;
+    }
+
     return OK;
 }
 
@@ -180,10 +329,23 @@
     return definition->fixedCount;
 }
 
+bool TiffWriter::hasIfd(uint32_t ifd) const {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    return index >= 0;
+}
+
 bool TiffWriter::checkIfDefined(uint16_t tag) const {
     return lookupDefinition(tag) != NULL;
 }
 
+const char* TiffWriter::getTagName(uint16_t tag) const {
+    const TagDefinition_t* definition = lookupDefinition(tag);
+    if (definition == NULL) {
+        return NULL;
+    }
+    return definition->tagName;
+}
+
 sp<TiffIfd> TiffWriter::findLastIfd() {
     sp<TiffIfd> ifd = mIfd;
     while(ifd != NULL) {
@@ -221,10 +383,9 @@
 
 void TiffWriter::log() const {
     ALOGI("%s: TiffWriter:", __FUNCTION__);
-    sp<TiffIfd> ifd = mIfd;
-    while(ifd != NULL) {
-        ifd->log();
-        ifd = ifd->getNextIfd();
+    size_t length = mNamedIfds.size();
+    for (size_t i = 0; i < length; ++i) {
+        mNamedIfds[i]->log();
     }
 }