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

Commit c67de112 authored by Sal Savage's avatar Sal Savage
Browse files

Null check data before stringifying/serializing and check validity

The BIP and AVRCP-BIP specifications claim you must (1) have a fixed
version, (2) have an image handle and (3) have an image format that
captures the imaging thumbnail format defined in 5.14.2.2.1 of AVRCP
1.6.

This change makes it so
(1) we no longer assume the data we get will be completely in spec.
We null check before serializing what we've built or printing what
we received for debug purposes
(2) We can check the BipImageProperties object for validity against the
specification requirements.

Significant tests have been added to show the new code works and is
robust.

Tag: #stability
Bug: 181811272
Test: atest BluetoothInstrumentationTests
Test: Interop with Pixel, iPhone, and Samsung devices
Change-Id: I4340de3311551372c847b79b6ee5f49c7adc2a9a
parent 8496b3a4
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -359,6 +359,9 @@ public class AvrcpCoverArtManager {
     * @return A descriptor containing the desirable download format
     */
    private BipImageDescriptor determineImageDescriptor(BipImageProperties properties) {
        if (properties == null || !properties.isValid()) {
            warn("Provided properties don't meet the spec. Requesting thumbnail format anyway.");
        }
        BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
        switch (mDownloadScheme) {
            // BIP Specification says a blank/null descriptor signals to pull the native format
+64 −2
Original line number Diff line number Diff line
@@ -43,6 +43,13 @@ import java.util.Objects;
 * various transformations. Attachments describes other items that can be downloaded that are
 * associated with the image (text, sounds, etc.)
 *
 * The specification requires that
 *     1. The fixed version string of "1.0" is used
 *     2. There is an image handle
 *     3. The "imaging thumbnail format" is included. This is defined for BIP in section 4.4.3
 *        (160x120 JPEG) and redefined for AVRCP in section 5.14.2.2.1 I (200x200 JPEG). It can be
 *        either a native or variant format.
 *
 * Example:
 *     <image-properties version="1.0" handle="123456789">
 *     <native encoding="JPEG" pixel="1280*1024" size="1048576"/>
@@ -142,6 +149,11 @@ public class BipImageProperties {
     */
    private String mFriendlyName = null;

    /**
     * Whether we have the required imaging thumbnail format
     */
    private boolean mHasThumbnailFormat = false;

    /**
     * The various sets of available formats.
     */
@@ -220,6 +232,10 @@ public class BipImageProperties {
        return mImageHandle;
    }

    public String getVersion() {
        return mVersion;
    }

    public String getFriendlyName() {
        return mFriendlyName;
    }
@@ -243,6 +259,10 @@ public class BipImageProperties {
                    + "' but expected '" + BipImageFormat.FORMAT_NATIVE + "'");
        }
        mNativeFormats.add(format);

        if (!mHasThumbnailFormat && isThumbnailFormat(format)) {
            mHasThumbnailFormat = true;
        }
    }

    private void addVariantFormat(BipImageFormat format) {
@@ -252,6 +272,29 @@ public class BipImageProperties {
                    + "' but expected '" + BipImageFormat.FORMAT_VARIANT + "'");
        }
        mVariantFormats.add(format);

        if (!mHasThumbnailFormat && isThumbnailFormat(format)) {
            mHasThumbnailFormat = true;
        }
    }

    private boolean isThumbnailFormat(BipImageFormat format) {
        if (format == null) return false;

        BipEncoding encoding = format.getEncoding();
        if (encoding == null || encoding.getType() != BipEncoding.JPEG) return false;

        BipPixel pixel = format.getPixel();
        if (pixel == null) return false;
        switch (pixel.getType()) {
            case BipPixel.TYPE_FIXED:
                return pixel.getMaxWidth() == 200 && pixel.getMaxHeight() == 200;
            case BipPixel.TYPE_RESIZE_MODIFIED_ASPECT_RATIO:
                return pixel.getMaxWidth() >= 200 && pixel.getMaxHeight() >= 200;
            case BipPixel.TYPE_RESIZE_FIXED_ASPECT_RATIO:
                return pixel.getMaxWidth() == pixel.getMaxHeight() && pixel.getMaxWidth() >= 200;
        }
        return false;
    }

    private void addAttachment(BipAttachmentFormat format) {
@@ -268,8 +311,11 @@ public class BipImageProperties {
            xmlMsgElement.startDocument("UTF-8", true);
            xmlMsgElement.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
            xmlMsgElement.startTag(null, "image-properties");
            xmlMsgElement.attribute(null, "version", mVersion);
            xmlMsgElement.attribute(null, "handle", mImageHandle);
            if (mVersion != null) xmlMsgElement.attribute(null, "version", mVersion);
            if (mImageHandle != null) xmlMsgElement.attribute(null, "handle", mImageHandle);
            if (mFriendlyName != null) {
                xmlMsgElement.attribute(null, "friendly-name", mFriendlyName);
            }

            for (BipImageFormat format : mNativeFormats) {
                BipEncoding encoding = format.getEncoding();
@@ -354,9 +400,12 @@ public class BipImageProperties {
    /**
     * Serialize this object into a byte array
     *
     * Objects that are not valid will fail to serialize and return null.
     *
     * @return Byte array representing this object, ready to send over OBEX, or null on error.
     */
    public byte[] serialize() {
        if (!isValid()) return null;
        String s = toString();
        try {
            return s != null ? s.getBytes("UTF-8") : null;
@@ -365,6 +414,19 @@ public class BipImageProperties {
        }
    }

    /**
     * Determine if the contents of this BipImageProperties object are valid and meet the
     * specification requirements:
     *     1. Include the fixed 1.0 version
     *     2. Include an image handle
     *     3. Have the thumbnail format as either the native or variant
     *
     * @return True if our contents are valid, false otherwise
     */
    public boolean isValid() {
        return sVersion.equals(mVersion) && mImageHandle != null && mHasThumbnailFormat;
    }

    private static void warn(String msg) {
        Log.w(TAG, msg);
    }
+351 −22
Original line number Diff line number Diff line
@@ -31,20 +31,72 @@ import java.io.UnsupportedEncodingException;
 */
@RunWith(AndroidJUnit4.class)
public class BipImagePropertiesTest {
    private static String sImageHandle = "123456789";
    private static final String sXmlDocDecl =
    private static final String IMAGE_HANDLE = "123456789";
    private static final String FRIENDLY_NAME = "current-track.jpeg";
    private static final String VERSION = "1.0";
    private static final String XML_DOC_DECL =
            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n";
    private static String sXmlString = sXmlDocDecl
            + "<image-properties version=\"1.0\" handle=\"123456789\">\n"
            + "    <native encoding=\"JPEG\" pixel=\"1280*1024\" size=\"1048576\" />\n"
            + "    <variant encoding=\"JPEG\" pixel=\"640*480\" />\n"
            + "    <variant encoding=\"GIF\" pixel=\"80*60-640*480\" "
            + "transformation=\"stretch fill crop\" />\n"
            + "    <variant encoding=\"JPEG\" pixel=\"150**-600*120\" />\n"
            + "    <attachment content-type=\"text/plain\" name=\"ABCD1234.txt\" size=\"5120\" />\n"
            + "    <attachment content-type=\"audio/basic\" name=\"ABCD1234.wav\" size=\"102400\" "
            + "/>\n"
            + "</image-properties>\n";

    // An image-properties tag with all available attributes
    private static final String IMAGE_PROPERTIES =
            "<image-properties version=\"" + VERSION + "\" handle=\"" + IMAGE_HANDLE
            + "\" friendly-name=\"" + FRIENDLY_NAME + "\">\n";

    // An image-properties tag without an xml version - OUT OF SPEC / INVALID
    private static final String IMAGE_PROPERTIES_NO_VERSION =
            "<image-properties handle=\"" + IMAGE_HANDLE + "\" friendly-name=\""
            + FRIENDLY_NAME + "\">\n";

    // An image-properties tag without an image handle - OUT OF SPEC / INVALID
    private static final String IMAGE_PROPERTIES_NO_HANDLE =
            "<image-properties version=\"" + VERSION + "\" friendly-name=\"" + FRIENDLY_NAME
            + "\">\n";

    // An image-properties tag without an xml version - IN SPEC / VALID
    private static final String IMAGE_PROPERTIES_NO_FRIENDLY_NAME =
            "<image-properties version=\"" + VERSION + "\" handle=\"" + IMAGE_HANDLE + "\">\n";

    // A native format representing the unaltered image available. Has a basic pixel and size
    private static final String NATIVE_FORMAT =
            "    <native encoding=\"JPEG\" pixel=\"1280*1024\" size=\"1048576\" />\n";

    // A native format representation of the imaging thumbnail format
    private static final String NATIVE_THUMBNAIL_FORMAT =
            "    <native encoding=\"JPEG\" pixel=\"200*200\" />\n";

    // A variant format representing a static altered image type. Has a basic pixel and no size
    private static final String VARIANT_FIXED_FORMAT =
            "    <variant encoding=\"JPEG\" pixel=\"640*480\" />\n";

    // A variant format representing an range of sizes available. Has transformations and no size
    private static final String VARIANT_RANGE_FORMAT =
            "    <variant encoding=\"GIF\" pixel=\"80*60-640*175\" "
            + "transformation=\"stretch fill crop\" />\n";

    // A variant format representing a range of sizes within a fixed aspect ratio.
    private static final String VARIANT_FIXED_RANGE_FORMAT =
            "    <variant encoding=\"JPEG\" pixel=\"150**-600*120\" />\n";

    // A fixed variant format representation of the imaging thumbnail format
    private static final String VARIANT_FIXED_THUMBNAIL_FORMAT =
            "    <variant encoding=\"JPEG\" pixel=\"200*200\" />\n";

    // A resizable modifiable aspect ratio variant format containing the imaging thumbnail format
    private static final String VARIANT_RANGE_THUMBNAIL_FORMAT =
            "    <variant encoding=\"JPEG\" pixel=\"80*60-640*480\" />\n";

    // A resizable fixed variant format containing the imaging thumbnail format
    private static final String VARIANT_FIXED_RANGE_THUMBNAIL_FORMAT =
            "    <variant encoding=\"JPEG\" pixel=\"150**-600*600\" />\n";

    // Though not in the specification, we should be robust to attachments of various formats
    private static final String ATTACHMENT_1 =
            "    <attachment content-type=\"text/plain\" name=\"ABCD1234.txt\" size=\"5120\" />\n";
    private static final String ATTACHMENT_2 =
            "    <attachment content-type=\"audio/basic\" name=\"ABCD1234.wav\" size=\"102400\" "
            + "/>\n";

    private static final String IMAGE_PROPERTIES_END = "</image-properties>\n";

    private InputStream toUtf8Stream(String s) {
        try {
@@ -54,25 +106,299 @@ public class BipImagePropertiesTest {
        }
    }

    /**
     * Test parsing image-properties with very simple information available.
     *
     * This is the most common type of object we will receive.
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" >
     *         <native encoding="JPEG" pixel="200*200" />
     *     </image-properties>";
     */
    @Test
    public void testParsePropertiesSimple() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES_NO_FRIENDLY_NAME
                + NATIVE_THUMBNAIL_FORMAT + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(null, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties with very rich information available.
     *
     * This information includes attachments, which are not allowed in AVRCP-BIP but completely
     * allowed in standard BIP.
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="200*200" />
     *         <variant encoding="JPEG" pixel="640*480" />
     *         <variant encoding="GIF" pixel="80*60-640*175" transformation="stretch fill crop" />
     *         <variant encoding="JPEG" pixel="150**-600*120" />
     *         <attachment content-type="text/plain" name="ABCD1234.txt" size="5120" />
     *         <attachment content-type="audio/basic" name="ABCD1234.wav" size="102400" />
     *     </image-properties>";
     */
    @Test
    public void testParsePropertiesRich() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + NATIVE_THUMBNAIL_FORMAT
                + VARIANT_FIXED_FORMAT + VARIANT_RANGE_FORMAT + VARIANT_FIXED_RANGE_FORMAT
                + ATTACHMENT_1 + ATTACHMENT_2 + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties without an image handle.
     *
     * This is out of spec, but should not crash. Instead, the individual attributes should be
     * available and serializing should return null.
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="200*200" />
     *     </image-properties>";
     */
    @Test
    public void testParseNoHandle() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES_NO_HANDLE + NATIVE_THUMBNAIL_FORMAT
                + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(null, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertFalse(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
        Assert.assertEquals(null, properties.serialize());
    }

    /**
     * Test parsing image-properties without a version.
     *
     * This is out of spec, but should not crash. Instead, the individual attributes should be
     * available and serializing should return null.
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties handle="123456789" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="200*200" />
     *     </image-properties>";
     */
    @Test
    public void testParseProperties() {
        InputStream stream = toUtf8Stream(sXmlString);
    public void testParseNoVersion() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES_NO_VERSION + NATIVE_THUMBNAIL_FORMAT
                + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(sImageHandle, properties.getImageHandle());
        Assert.assertEquals(sXmlString, properties.toString());
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(null, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertFalse(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
        Assert.assertEquals(null, properties.serialize());
    }

    /**
     * Test parsing image-properties without a friendly name.
     *
     * This is in spec, as friendly name isn't required.
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="200*200"/>
     *     </image-properties>";
     */
    @Test
    public void testParseNoFriendlyName() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES_NO_FRIENDLY_NAME
                + NATIVE_THUMBNAIL_FORMAT + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(null, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties with a fixed variant thumbnail format
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <variant encoding="JPEG" pixel="200*200" />
     *     </image-properties>";
     */
    @Test
    public void testParseFixedVariantThumbnailFormat() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + VARIANT_FIXED_THUMBNAIL_FORMAT
                + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties with a range variant thumbnail format
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <variant encoding="JPEG" pixel="80*60-640*480" />
     *     </image-properties>";
     */
    @Test
    public void testParseRangeVariantThumbnailFormat() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + VARIANT_RANGE_THUMBNAIL_FORMAT
                + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties with a fixed aspect ratio range variant thumbnail format
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <variant encoding="JPEG" pixel="80*60-640*480" />
     *     </image-properties>";
     */
    @Test
    public void testParseFixedRangeVariantThumbnailFormat() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + VARIANT_FIXED_RANGE_THUMBNAIL_FORMAT
                + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }

    /**
     * Test parsing image-properties without any thumbnail formats
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="1280*1024" size="1048576" />
     *         <variant encoding="JPEG" pixel="640*480" />
     *         <variant encoding="GIF" pixel="80*60-640*480" transformation="stretch fill crop" />
     *         <variant encoding="JPEG" pixel="150**-600*120" />
     *     </image-properties>";
     */
    @Test
    public void testParseNoThumbnailFormats() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + NATIVE_FORMAT + VARIANT_FIXED_FORMAT
                + VARIANT_RANGE_FORMAT + VARIANT_FIXED_RANGE_FORMAT + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertFalse(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
        Assert.assertEquals(null, properties.serialize());
    }

    /**
     * Test parsing image-properties without any formats
     *
     * Payload:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *     </image-properties>";
     */
    @Test
    public void testParseNoFormats() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertFalse(properties.isValid());
        Assert.assertEquals(null, properties.serialize());
    }

    /**
     * Test parsing an image-properties with no open tag
     */
    @Test (expected = ParseException.class)
    public void testParseMalformedNoOpen() {
        String xmlString = XML_DOC_DECL + NATIVE_FORMAT + IMAGE_PROPERTIES_END;
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
    }

    /**
     * Test parsing a malformed image-properties that just cuts out
     */
    @Test (expected = ParseException.class)
    public void testParseSimulateStreamEndedUnexpectedly() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + "<native encoding=\"JPE";
        InputStream stream = toUtf8Stream(xmlString);
        BipImageProperties properties = new BipImageProperties(stream);
    }

    /**
     * Test creating image-properties with very rich information available:
     *
     * Expected Payload created:
     *     <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
     *     <image-properties version="1.0" handle="123456789" friendly-name="current-track.jpeg">
     *         <native encoding="JPEG" pixel="200*200" />
     *         <variant encoding="JPEG" pixel="640*480" />
     *         <variant encoding="GIF" pixel="80*60-640*175" transformation="stretch fill crop" />
     *         <variant encoding="JPEG" pixel="150**-600*120" />
     *         <attachment content-type="text/plain" name="ABCD1234.txt" size="5120" />
     *         <attachment content-type="audio/basic" name="ABCD1234.wav" size="102400" />
     *     </image-properties>";
     */
    @Test
    public void testCreateProperties() {
        String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + NATIVE_THUMBNAIL_FORMAT
                + VARIANT_FIXED_FORMAT + VARIANT_RANGE_FORMAT + VARIANT_FIXED_RANGE_FORMAT
                + ATTACHMENT_1 + ATTACHMENT_2 + IMAGE_PROPERTIES_END;

        BipTransformation trans = new BipTransformation();
        trans.addTransformation(BipTransformation.STRETCH);
        trans.addTransformation(BipTransformation.CROP);
        trans.addTransformation(BipTransformation.FILL);

        BipImageProperties.Builder builder = new BipImageProperties.Builder();
        builder.setImageHandle(sImageHandle);
        builder.setImageHandle(IMAGE_HANDLE);
        builder.setFriendlyName(FRIENDLY_NAME);
        builder.addNativeFormat(BipImageFormat.createNative(new BipEncoding(BipEncoding.JPEG, null),
                BipPixel.createFixed(1280, 1024), 1048576));
                BipPixel.createFixed(200, 200), -1));

        builder.addVariantFormat(
                BipImageFormat.createVariant(
@@ -81,7 +407,7 @@ public class BipImagePropertiesTest {
        builder.addVariantFormat(
                BipImageFormat.createVariant(
                    new BipEncoding(BipEncoding.GIF, null),
                    BipPixel.createResizableModified(80, 60, 640, 480), -1, trans));
                    BipPixel.createResizableModified(80, 60, 640, 175), -1, trans));
        builder.addVariantFormat(
                BipImageFormat.createVariant(
                    new BipEncoding(BipEncoding.JPEG, null),
@@ -93,7 +419,10 @@ public class BipImagePropertiesTest {
                new BipAttachmentFormat("audio/basic", null, "ABCD1234.wav", 102400, null, null));

        BipImageProperties properties = builder.build();
        Assert.assertEquals(sImageHandle, properties.getImageHandle());
        Assert.assertEquals(sXmlString, properties.toString());
        Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
        Assert.assertEquals(VERSION, properties.getVersion());
        Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
        Assert.assertTrue(properties.isValid());
        Assert.assertEquals(xmlString, properties.toString());
    }
}