Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JDK-8347377 : Add validation checks for ICC_Profile header fields #23044

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,11 @@ public ICC_ColorSpace(ICC_Profile profile) {
if (profileClass != ICC_Profile.CLASS_INPUT
&& profileClass != ICC_Profile.CLASS_DISPLAY
&& profileClass != ICC_Profile.CLASS_OUTPUT
&& profileClass != ICC_Profile.CLASS_DEVICELINK
honkar-jdk marked this conversation as resolved.
Show resolved Hide resolved
&& profileClass != ICC_Profile.CLASS_COLORSPACECONVERSION
&& profileClass != ICC_Profile.CLASS_NAMEDCOLOR
&& profileClass != ICC_Profile.CLASS_ABSTRACT) {
throw new IllegalArgumentException("Invalid profile type");
throw new IllegalArgumentException("Invalid device class");
}

thisProfile = profile;
Expand Down
70 changes: 66 additions & 4 deletions src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -756,6 +756,7 @@ private interface BuiltInProfile {
*/
public static final int icXYZNumberX = 8;

private static final int HEADER_SIZE = 128;

/**
* Constructs an {@code ICC_Profile} object with a given ID.
Expand Down Expand Up @@ -790,6 +791,12 @@ public static ICC_Profile getInstance(byte[] data) {
} catch (CMMException c) {
throw new IllegalArgumentException("Invalid ICC Profile Data");
}

if (p != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it possible to get null here we should thrown an exception, but I think we thrown that exception already in the native.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is probably better to add this validation into ProfileDataVerifier.verify(data), and check it even before .getModule().loadProfile(data)

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires making the new method verifyHeader()public so that it can be used in ProfileDataVerifier.verify(data) as follows.

  byte[] theHeader = new byte[HEADER_SIZE];
  System.arraycopy(data,0, theHeader, 0, HEADER_SIZE);
  ICC_Profile.verifyHeader(theHeader);

or it can be added before .getModule().loadProfile(data) within ICC_Profile.getInstance() and this keeps verifyHeader() private.

 public static ICC_Profile getInstance(byte[] data) {
        ProfileDataVerifier.verify(data);
        Profile p;
        try {
            byte[] theHeader = new byte[HEADER_SIZE]; 
            System.arraycopy(data, 0, theHeader, 0, HEADER_SIZE);
            verifyHeader(theHeader);

            p = CMSManager.getModule().loadProfile(data);
        } catch (CMMException c) {
            throw new IllegalArgumentException("Invalid ICC Profile Data");
        }

@prrace Your suggestion on whether to have verifyHeader() as private or public method? If we decide to make it public then a CSR is required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrserb I have changed it so that verifyHeader is called before CMSManager.getModule().loadProfile(data);
Validation checks cannot be moved to ProfileDataVerifier without making verifyHeader() public.

NOTE: ProfileDataVerifier.verify() verifies entire profile data and not just the header and at this point we have not created a profile yet.

byte[] headerData = getData(p, icSigHead);
verifyHeader(headerData);
}

try {
if (getColorSpaceType(p) == ColorSpace.TYPE_GRAY
&& getData(p, icSigMediaWhitePointTag) != null
Expand Down Expand Up @@ -972,6 +979,10 @@ public int getProfileClass() {
return info.profileClass;
}
byte[] theHeader = getData(icSigHead);
return getProfileClass(theHeader);
}

private static int getProfileClass(byte[] theHeader) {
int theClassSig = intFromBigEndian(theHeader, icHdrDeviceClass);
return switch (theClassSig) {
case icSigInputClass -> CLASS_INPUT;
Expand All @@ -981,9 +992,7 @@ public int getProfileClass() {
case icSigColorSpaceClass -> CLASS_COLORSPACECONVERSION;
case icSigAbstractClass -> CLASS_ABSTRACT;
case icSigNamedColorClass -> CLASS_NAMEDCOLOR;
default -> {
throw new IllegalArgumentException("Unknown profile class");
}
default -> throw new IllegalArgumentException("Unknown device class");
honkar-jdk marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down Expand Up @@ -1013,6 +1022,11 @@ private static int getColorSpaceType(Profile p) {
return iccCStoJCS(theColorSpaceSig);
}

private static int getColorSpaceType(byte[] theHeader) {
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
return iccCStoJCS(theColorSpaceSig);
}

/**
* Returns the color space type of the Profile Connection Space (PCS).
* Returns one of the color space type constants defined by the ColorSpace
Expand All @@ -1032,6 +1046,21 @@ public int getPCSType() {
return iccCStoJCS(thePCSSig);
}

private static int getPCSType(byte[] theHeader) {
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
int theDeviceClass = intFromBigEndian(theHeader, icHdrDeviceClass);

if (theDeviceClass == icSigLinkClass) {
return iccCStoJCS(thePCSSig);
} else {
return switch (thePCSSig) {
case icSigXYZData -> ColorSpace.TYPE_XYZ;
case icSigLabData -> ColorSpace.TYPE_Lab;
default -> throw new IllegalArgumentException("Unexpected PCS type");
};
}
}

/**
* Write this {@code ICC_Profile} to a file.
*
Expand Down Expand Up @@ -1112,9 +1141,42 @@ private static byte[] getData(Profile p, int tagSignature) {
* @see #getData
*/
public void setData(int tagSignature, byte[] tagData) {
if (tagSignature == ICC_Profile.icSigHead) {
verifyHeader(tagData);
}
CMSManager.getModule().setTagData(cmmProfile(), tagSignature, tagData);
}

private static void verifyHeader(byte[] data) {
if (data == null || data.length < HEADER_SIZE) {
throw new IllegalArgumentException("Invalid header data");
}
getProfileClass(data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can any of these fields have custom values (not covered by java constants inside iccCStoJCS) ​​that can still be used for color transformation?

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using custom values of color space to create a new color transform (For instance: cmsSigMCH1Data: 0x4D434831 which is specified in LCMS API but not in ICC Spec) results in ProfileDataException as below. (this is expected since cmsSigMCH1Data constant is NOT present in JDK).

java.awt.color.ProfileDataException: invalid ICC color space
	at java.desktop/java.awt.color.ICC_Profile.getNumComponents(ICC_Profile.java:1217)
	at java.desktop/sun.java2d.cmm.lcms.LCMSTransform.<init>(LCMSTransform.java:85)
	at java.desktop/sun.java2d.cmm.lcms.LCMS.createTransform(LCMS.java:132)
	at java.desktop/java.awt.color.ICC_ColorSpace.toRGB(ICC_ColorSpace.java:212)

To summarize:

  • Profile/Device class - constants match (in ICC Spec doc and LCMS API doc)
  • Color Space - constants differ
  • Rendering Intent - constants differ
  • PCS - constants differ. This is dependent on type of device class and takes color space values.

getColorSpaceType(data);
getPCSType(data);
checkRenderingIntent(data);
}

private static boolean checkRenderingIntent(byte[] header) {
int index = ICC_Profile.icHdrRenderingIntent;

/* According to ICC spec, only the least-significant 16 bits shall be
* used to encode the rendering intent. The most significant 16 bits
* shall be set to zero. Thus, we are ignoring two most significant
* bytes here. Please refer ICC Spec Document for more details.
*/
int renderingIntent = ((header[index+2] & 0xff) << 8) |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int renderingIntent = ((header[index+2] & 0xff) << 8) |
int renderingIntent = ((header[index+2] & 0xff) << 8) |

(header[index+3] & 0xff);

switch (renderingIntent) {
case icPerceptual, icMediaRelativeColorimetric,
icSaturation, icAbsoluteColorimetric -> {
return true;
}
default -> throw new IllegalArgumentException("Unknown Rendering Intent");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how it is handled by the lcms library? don't we need to ignore unknown intents(and other parameters) and lets lcms decide what to do?

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrserb
Non-header data are updated using cooked approach (and validated by LCMS) whereas header data are updated using raw LCMS APIs hence require additional validation before setData() is called (On native side it is handled here: setTagDataNative() in LCMS.c).

Without the fix, if invalid rendering intent, PCS, ColorSpace or Device class is updated using setData() it does not throw IAE.

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we need to ignore unknown intents(and other parameters) and lets lcms decide what to do?

Yes, LCMS ignores invalid header data and updates the profile, this can cause exceptions later on for instance when the modified profile is used to create BufferedImage which can be prevented by adding checks in setData() and restricting updates to only allowed values as specified in ICC Spec Doc.

Copy link
Contributor

@prrace prrace Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how it is handled by the lcms library? don't we need to ignore unknown intents(and other parameters) and lets lcms decide what to do?

The ICC spec. defines only these 4 intents, so I don't see a problem here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrserb Non-header data are updated using cooked approach (and validated by LCMS) whereas header data are updated using raw LCMS APIs hence require additional validation before setData() is called (On native side it is handled here: setTagDataNative() in LCMS.c).

Then probably we can use approach similar to 8282577: f66070b and try to rely on lcms for validation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW

Typically, the user or application will set the rendering intent dynamically at runtime or embedding time. Therefore, this flag may not have any meaning until the profile is used in some context, e.g. in a DeviceLink or an embedded source profile."

Why this usecase should not be covered by the java? As of the current version of patch it will not be possible to load the profile and then set the intent, right?

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right. It's not a javase spec, and there may be a reason why the most common library for icc profiles accepts thad data. We shouldn't be more strict than that, it will limit java applications compared to the alternatives.

The path that you mentioned in LCMSTransformfor RenderingIntent involves creating a new color space transform using 2 or more profiles and does not involve creating or updating a profile.
Moreover when a random value is added for rendering intent in LCMSTransform, LCMS throws CMMException. If LCMS validates the Rendering Intent while creating a new color space transform then wouldn't it be better to validate it while creating/updating a ICC_Profile?

And as for the different values specified in ICC_Spec vs LCMS API doc, @prrace confirmed that jdk is required to follow ICC_Spec. In other words the color mgnt engine can have different implementations (LCMS or KCMS) but we need to follow the ICC Specification.

Did the upstream provide any feedback/comments about this validation?

Discussed with @prrace, in this case it was decided that contacting upstream is not necessary since we are choosing to do validations in any case (whether or not LCMS validates).

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrserb

there may be a reason why the most common library for icc profiles accepts thad data. We shouldn't be more strict than that, it will limit java applications compared to the alternatives.

I see your point but color management engines may have different implementations (LCMS or KCMS) and ICC Specification is a standard.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point but color management engines may have different implementations (LCMS or KCMS) and ICC Specification is a standard.

I am pretty sure the lcms/kcms follow the standard, because of the next statement in the "ICC.1-2022-05.pdf"

Any colour management system, application, utility or device driver that claims conformance with this ICC
specification shall have the ability to read the profiles as they are defined in this ICC specification. Any
profile-generating software and/or hardware that claims conformance with this ICC specification shall have the
ability to create profiles as they are defined in this ICC specification.

Since you already mentioned "non-ICC intent", please specify the part of the specification in ICC.1-2022-05 where their use is prohibited.

Copy link
Contributor Author

@honkar-jdk honkar-jdk Jan 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you already mentioned "non-ICC intent", please specify the part of the specification in ICC.1-2022-05 where their use is prohibited.

@mrserb
The ICC Spec doesn't explicitly say that certain values are prohibited. Although it does not list Non-ICC Intent values under Rendering Intent (pg#23, Table 23) either.

What do you suggest is the best solution here to address the difference in ICC Spec Doc vs LCMS API doc?

  1. Add the missing constants present in LCMS API doc to Java?
  2. Or skip validating Rendering Intent?
  3. Since Color Space has few extra constants in LCMS API do we need to address it too?

@prrace Can you please suggest how to address Sergey's concern, since the last time we discussed we agreed to follow ICC Spec Doc.

there may be a reason why the most common library for icc profiles accepts that data. We shouldn't be more strict than that, it will limit java applications compared to the alternatives.

}
}

/**
* Returns the number of color components in the "input" color space of this
* profile. For example if the color space type of this profile is
Expand Down
212 changes: 212 additions & 0 deletions test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @bug 8337703
* @summary To verify if ICC_Profile's setData() validates header data
* and throws IAE for invalid values.
* @run main ValidateICCHeaderData
*/

import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.math.BigInteger;
import java.nio.ByteBuffer;

public class ValidateICCHeaderData {
private static ICC_Profile profile;
honkar-jdk marked this conversation as resolved.
Show resolved Hide resolved

private static final boolean DEBUG = false;
private static final int VALID_HEADER_SIZE = 128;
private static final int HEADER_TAG = ICC_Profile.icSigHead;
private static final int PROFILE_CLASS_START_INDEX = ICC_Profile.icHdrDeviceClass;
private static final int COLOR_SPACE_START_INDEX = ICC_Profile.icHdrColorSpace;
private static final int RENDER_INTENT_START_INDEX = ICC_Profile.icHdrRenderingIntent;
private static final int PCS_START_INDEX = ICC_Profile.icHdrPcs;

private static final int[] VALID_PROFILE_CLASS = new int[] {
ICC_Profile.icSigInputClass, ICC_Profile.icSigDisplayClass,
ICC_Profile.icSigOutputClass, ICC_Profile.icSigLinkClass,
ICC_Profile.icSigAbstractClass, ICC_Profile.icSigColorSpaceClass,
ICC_Profile.icSigNamedColorClass
};

private static final int[] VALID_COLOR_SPACE = new int[] {
ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData,
ICC_Profile.icSigLuvData, ICC_Profile.icSigYCbCrData,
ICC_Profile.icSigYxyData, ICC_Profile.icSigRgbData,
ICC_Profile.icSigGrayData, ICC_Profile.icSigHsvData,
ICC_Profile.icSigHlsData, ICC_Profile.icSigCmykData,
ICC_Profile.icSigSpace2CLR, ICC_Profile.icSigSpace3CLR,
ICC_Profile.icSigSpace4CLR, ICC_Profile.icSigSpace5CLR,
ICC_Profile.icSigSpace6CLR, ICC_Profile.icSigSpace7CLR,
ICC_Profile.icSigSpace8CLR, ICC_Profile.icSigSpace9CLR,
ICC_Profile.icSigSpaceACLR, ICC_Profile.icSigSpaceBCLR,
ICC_Profile.icSigSpaceCCLR, ICC_Profile.icSigSpaceDCLR,
ICC_Profile.icSigSpaceECLR, ICC_Profile.icSigSpaceFCLR,
ICC_Profile.icSigCmyData
};

private static final int[] VALID_RENDER_INTENT = new int[] {
ICC_Profile.icPerceptual, ICC_Profile.icMediaRelativeColorimetric,
ICC_Profile.icSaturation, ICC_Profile.icAbsoluteColorimetric
};

private static void createCopyOfBuiltInProfile() {
ICC_Profile builtInProfile = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
//copy of SRGB BuiltIn Profile that can be modified
//using ICC_Profile.setData()
profile = ICC_Profile.getInstance(builtInProfile.getData());
}

public static void main(String[] args) {
createCopyOfBuiltInProfile();

System.out.println("CASE 1: Testing VALID Profile Classes ...");
testValidHeaderData(VALID_PROFILE_CLASS, PROFILE_CLASS_START_INDEX, 4);
System.out.println("CASE 1: Passed \n");

// PCS field validation for Profile class != DEVICE_LINK
System.out.println("CASE 2: Testing VALID PCS Type"
+ " for Profile class != DEVICE_LINK ...");
testValidHeaderData(new int[] {ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData},
PCS_START_INDEX, 4);
System.out.println("CASE 2: Passed \n");

System.out.println("CASE 3: Testing INVALID PCS Type"
+ " for Profile class != DEVICE_LINK ...");
testInvalidHeaderData(ICC_Profile.icSigCmykData, PCS_START_INDEX, 4);
System.out.println("CASE 3: Passed \n");

System.out.println("CASE 4: Testing DEVICE LINK PROFILE CLASS ...");
testValidHeaderData(new int[] {ICC_Profile.icSigLinkClass},
PROFILE_CLASS_START_INDEX, 4);
//to check if instantiating BufferedImage with
//ICC_Profile device class = CLASS_DEVICELINK does not throw IAE.
BufferedImage img = new BufferedImage(100, 100,
BufferedImage.TYPE_3BYTE_BGR);
System.out.println("CASE 4: Passed \n");

// PCS field validation for Profile class == DEVICE_LINK
System.out.println("CASE 5: Testing VALID PCS Type"
+ " for Profile class == DEVICE_LINK ...");
testValidHeaderData(VALID_COLOR_SPACE, PCS_START_INDEX, 4);
System.out.println("CASE 5: Passed \n");

System.out.println("CASE 6: Testing INVALID PCS Type"
+ " for Profile class == DEVICE_LINK ...");
//original icSigLabData = 0x4C616220
int invalidSigLabData = 0x4C616221;
testInvalidHeaderData(invalidSigLabData, PCS_START_INDEX, 4);
System.out.println("CASE 6: Passed \n");

System.out.println("CASE 7: Testing VALID Color Spaces ...");
testValidHeaderData(VALID_COLOR_SPACE, COLOR_SPACE_START_INDEX, 4);
System.out.println("CASE 7: Passed \n");

System.out.println("CASE 8: Testing VALID Rendering Intent ...");
testValidHeaderData(VALID_RENDER_INTENT, RENDER_INTENT_START_INDEX, 4);
System.out.println("CASE 8: Passed \n");

System.out.println("CASE 9: Testing INVALID Profile Class ...");
//original icSigInputClass = 0x73636E72
int invalidSigInputClass = 0x73636E70;
testInvalidHeaderData(invalidSigInputClass, PROFILE_CLASS_START_INDEX, 4);
System.out.println("CASE 9: Passed \n");

System.out.println("CASE 10: Testing INVALID Color Space ...");
//original icSigXYZData = 0x58595A20
int invalidSigXYZData = 0x58595A21;
testInvalidHeaderData(invalidSigXYZData, COLOR_SPACE_START_INDEX, 4);
System.out.println("CASE 10: Passed \n");

System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
//valid rendering intent values are 1-4
int invalidRenderIntent = 5;
testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
System.out.println("CASE 11: Passed \n");

System.out.println("CASE 12: Testing INVALID Header Size ...");
testInvalidHeaderSize();
System.out.println("CASE 12: Passed \n");

System.out.println("Successfully completed testing all 12 cases. Test Passed !!");
}

private static void testValidHeaderData(int[] validData, int startIndex,
int fieldLength) {
for (int value : validData) {
setTag(value, startIndex, fieldLength);
}
}

private static void testInvalidHeaderData(int invalidData, int startIndex,
int fieldLength) {
try {
setTag(invalidData, startIndex, fieldLength);
throw new RuntimeException("Test Failed ! Expected IllegalArgumentException:"
+ " NOT thrown");
} catch (IllegalArgumentException iae) {
System.out.println("Expected IllegalArgumentException thrown: " + iae.getMessage());
}
}

private static void setTag(int value, int startIndex, int fieldLength) {
byte[] byteArray;
if (startIndex == RENDER_INTENT_START_INDEX) {
byteArray = ByteBuffer.allocate(4).putInt(value).array();
} else {
BigInteger big = BigInteger.valueOf(value);
byteArray = (big.toByteArray());
}

if (DEBUG) {
System.out.print("Byte Array : ");
for (int i = 0; i < byteArray.length; i++) {
System.out.print(byteArray[i] + " ");
}
System.out.println("\n");
}

byte[] iccProfileHeaderData = profile.getData(HEADER_TAG);
System.arraycopy(byteArray, 0, iccProfileHeaderData, startIndex, fieldLength);
profile.setData(HEADER_TAG, iccProfileHeaderData);
}

private static void testInvalidHeaderSize() {
byte[] iccProfileHeaderData = profile.getData(HEADER_TAG);
byte[] invalidHeaderSize = new byte[VALID_HEADER_SIZE - 1];
System.arraycopy(iccProfileHeaderData, 0,
invalidHeaderSize, 0, invalidHeaderSize.length);
try {
profile.setData(HEADER_TAG, invalidHeaderSize);
throw new RuntimeException("Test Failed ! Expected IllegalArgumentException:"
+ " NOT thrown");
} catch (IllegalArgumentException iae) {
System.out.println("Expected IllegalArgumentException thrown: "
+ iae.getMessage());
}
}
}