JDK-4682959 : Spec: SourceDataLine.write() throws unspecified ArrayIndexOutOfBoundsException
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 1.4.1
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2002-05-10
  • Updated: 2017-05-16
  • Resolved: 2003-11-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b28Fixed
Related Reports
Relates :  
Description
                writtenBytes = -1;
                try {
                    writtenBytes =
                        testedSourceLine.write(dataToWrite, offsetToWrite, bufferSizeToWrite);
                    out.println(">  OK - number of written bytes = " + writtenBytes);
                } catch (Throwable thrown) {
                    out.println("## SourceDataLine.write(byte[] b, int off, int len) failed:");
                    out.println("#  Unexpected Exception is thrown");
                    thrown.printStackTrace(out);
                    testResult = STATUS_FAILED;
                }

                testedSourceLine.close();

            }  // for (int j=0; j < supportedSourceLineInfo.length; j++)
            installedMixer.close();

        }  // for (int i=0; i < installedMixersInfo.length; i++)

        if ( testResult == STATUS_FAILED ) {
            out.println("\n==> test FAILED!");
        } else {
            out.println("\n==> test PASSED!");
        }
        return testResult;
    }
    
}    // end of test class 
-------------------------------------------------------------------------
 
======================================================================
###@###.### 2003-11-03
Checked in fix
Name: abR10010			Date: 05/10/2002




The test (see below) shows that the SourceDataLine.write(byte[] b, int off, int len)
method of the javax.sound.sampled.SourceDataLine interface throws an
ArrayIndexOutOfBoundsException when incorrect arguments describing
the data array to write are specified.

Such behavior is shown for one source data line of one of installed on the system
mixers when test runs with JDK 1.4.1-beta-b11.

An ArrayIndexOutOfBoundsException is appropriate Exception for such cases but
the specification says nothing about it.

Also an IllegalArgumentException is appropriate Exception for such cases.

It seems that the spec should be adjusted to describe these situations.


Please, see test log:

% java -version
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b11)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b11, mixed mode)

% java test

==> Test for SourceDataLine.write() method:

>>>  Number of mixers installed on the system = 2

>>>  installedMixer[0] = com.sun.media.sound.HeadspaceMixer@117a8bd

>>  Number of SourceLineInfo supported by installedMixer = 2

>  testSourceLineInfo[0] = interface SourceDataLine supporting 8 audio formats
>  testedSourceLine = com.sun.media.sound.MixerSourceLine@21b6d

>  open tested SourceLine:
>  OK - line is opened

>  check SourceDataLine.write() for data length exceeding real size of data:
>  testedSourceLine.available() = 4
>  frame size = 4
>  real size of data to write = 4
>  offset of data to write = 0
>  number of bytes to write = 8
## SourceDataLine.write(byte[] b, int off, int len) failed:
#  Unexpected Exception is thrown
java.lang.ArrayIndexOutOfBoundsException: 4
	at com.sun.media.sound.Toolkit.getByteSwapped(Toolkit.java:41)
	at com.sun.media.sound.CircularBuffer.write(CircularBuffer.java:67)
	at com.sun.media.sound.MixerSourceLine.write(MixerSourceLine.java:126)
	at test.run(test.java:193)
	at test.main(test.java:13)

>  check SourceDataLine.write() for negative offset of data:
>  testedSourceLine.available() = 4
>  frame size = 4
>  real size of data to write = 4
>  offset of data to write = -1
>  number of bytes to write = 4
## SourceDataLine.write(byte[] b, int off, int len) failed:
#  Unexpected Exception is thrown
java.lang.ArrayIndexOutOfBoundsException: -1
	at com.sun.media.sound.Toolkit.getByteSwapped(Toolkit.java:41)
	at com.sun.media.sound.CircularBuffer.write(CircularBuffer.java:67)
	at com.sun.media.sound.MixerSourceLine.write(MixerSourceLine.java:126)
	at test.run(test.java:214)
	at test.main(test.java:13)

>  check SourceDataLine.write() when "offset + number of bytes to write" exceeds the bounds of data
>  testedSourceLine.available() = 4
>  frame size = 4
>  real size of data to write = 4
>  offset of data to write = 1
>  number of bytes to write = 4
## SourceDataLine.write(byte[] b, int off, int len) failed:
#  Unexpected Exception is thrown
java.lang.ArrayIndexOutOfBoundsException: 4
	at com.sun.media.sound.Toolkit.getByteSwapped(Toolkit.java:42)
	at com.sun.media.sound.CircularBuffer.write(CircularBuffer.java:67)
	at com.sun.media.sound.MixerSourceLine.write(MixerSourceLine.java:126)
	at test.run(test.java:235)
	at test.main(test.java:13)

>  testSourceLineInfo[1] = interface Clip supporting 8 audio formats, and buffers of 0 to 4194304 bytes
>  testedSourceLine = com.sun.media.sound.MixerClip@124bbbf
>  testSourceLine is not SourceDataLine

>>>  installedMixer[1] = com.sun.media.sound.SimpleInputDevice@a20892

>>  Number of SourceLineInfo supported by installedMixer = 0

==> test FAILED!
 
 
The test source:
------------------------------- test.java --------------------------------
// File: %Z%%M% %I% %E% 
// Copyright %G% Sun Microsystems, Inc. All Rights Reserved

import javax.sound.sampled.*;

public class test {

    static final int STATUS_PASSED = 0;
    static final int STATUS_FAILED = 2;
    static final int STATUS_TEMP = 95;
    
    public static void main(String argv[]) {
        int testExitStatus = run(argv, System.out) + STATUS_TEMP;
        System.exit(testExitStatus);
    }

    public static int run(String argv[], java.io.PrintStream out) {
        int testResult = STATUS_PASSED;
    
        int framesNumberToExceed = 2;
        if ( argv.length > 0 ) {
            try {
                framesNumberToExceed = Integer.parseInt(argv[0]);
            }
            catch (NumberFormatException e) { 
            }
        }

        out.println
            ("\n==> Test for SourceDataLine.write() method:");

        Mixer.Info[] installedMixersInfo = AudioSystem.getMixerInfo();
        
        if ( installedMixersInfo == null ) {
            out.println("## AudioSystem.getMixerInfo() returned unexpected result:");
            out.println("#  expected: an array of Mixer.Info objects (may be array of length 0);");
            out.println("#  produced: null;");
            return STATUS_FAILED;
        }

        if ( installedMixersInfo.length == 0 ) {
            // there are no mixers installed on the system -
            // so this testcase can not be tested
            out.println("\n>>>  There are no mixers installed on the system!");
            return STATUS_PASSED;
        }

        out.println("\n>>>  Number of mixers installed on the system = "
            + installedMixersInfo.length);
        Mixer installedMixer = null;
        for (int i=0; i < installedMixersInfo.length; i++) {
            try {
                installedMixer = AudioSystem.getMixer(installedMixersInfo[i]);
            } catch (SecurityException securityException) {
                // installed Mixer is unavailable because of security restrictions
                out.println("\n>>>  installedMixer[" + i
                    + "] is unavailable because of security restrictions");
                continue;
            } catch (Throwable thrown) {
                out.println("\n##  installedMixer[" + i + "] is unavailable because of");
                out.println("#  AudioSystem.getMixer() threw unexpected exception:");
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                continue;
            }

            out.println("\n>>>  installedMixer["+i+"] = " + installedMixer);
            try {
                installedMixer.open();
            } catch (LineUnavailableException lineUnavailableException) {
                // installedMixer is not available due to resource restrictions
                out.println(">>   installedMixer[" + i
                    + "] is not opened because of resource restrictions");
                continue;
            } catch (SecurityException securityException) {
                // installedMixer is not available due to security restrictions
                out.println(">>   installedMixer[" + i
                    + "] is not opened because of security restrictions");
                continue;
            } catch (Throwable thrown) {
                out.println("## installedMixer.open() throws unexpected exception:");
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                continue;
            }
            Line.Info supportedSourceLineInfo[] = null;
            try {
                supportedSourceLineInfo = installedMixer.getSourceLineInfo();
            } catch (Throwable thrown) {
                out.println("## installedMixer.getSourceLineInfo() throws "
                    + "unexpected exception:");
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                installedMixer.close();
                continue;
            }
            if ( supportedSourceLineInfo == null ) {
                out.println("## installedMixer.getSourceLineInfo() returned null array");
                out.println("#  Mixer = " + installedMixer);
                testResult = STATUS_FAILED;
                installedMixer.close();
                continue;
            }
            out.println("\n>>  Number of SourceLineInfo supported by installedMixer = "
                + supportedSourceLineInfo.length);

            for (int j=0; j < supportedSourceLineInfo.length; j++) {
                Line.Info testSourceLineInfo = supportedSourceLineInfo[j];
                
                out.println("\n>  testSourceLineInfo["+j+"] = " + testSourceLineInfo);
                Line testSourceLine = null;
                try {
                    testSourceLine = installedMixer.getLine(testSourceLineInfo);
                } catch (LineUnavailableException lineUnavailableException) {
                    // line is not available due to resource restrictions
                    out.println(">  Line for this SourceLine Info is not available "
                        + "due to resource restrictions");
                    continue;
                } catch (SecurityException securityException) {
                    // line is not available due to security restrictions
                    out.println(">  Line for this SourceLine Info is not available "
                        + "due to security restrictions");
                    continue;
                } catch (Throwable thrown) {
                    out.println("## installedMixer.getLine(testSourceLineInfo) throws "
                        + "unexpected Exception:");
                    thrown.printStackTrace(out);
                    testResult = STATUS_FAILED;
                    continue;
                }

                out.println(">  testedSourceLine = " + testSourceLine);
                if ( ! (testSourceLine instanceof SourceDataLine) ) {
                    out.println(">  testSourceLine is not SourceDataLine");
                    continue;
                }

                SourceDataLine testedSourceLine = (SourceDataLine)testSourceLine;
                AudioFormat lineAudioFormat = testedSourceLine.getFormat();

                out.println("\n>  open tested SourceLine:");
                try {
                    testedSourceLine.open(lineAudioFormat);
                    out.println(">  OK - line is opened");
                } catch (LineUnavailableException lineUnavailableException) {
                    out.println(">  Line is not available due to resource restrictions:");
                    lineUnavailableException.printStackTrace(out);
                    continue;
                } catch (SecurityException securityException) {
                    out.println("> Line is not available due to security restrictions:");
                    securityException.printStackTrace(out);
                    continue;
                } catch (Throwable thrown) {
                    out.println("## SourceDataLine.open(AudioFormat format) failed:");
                    out.println("#  Unexpected Exception is thrown");
                    out.println("#  Mixer = " + installedMixer);
                    out.println("#  SourceDataLine = " + testedSourceLine);
                    thrown.printStackTrace(out);
                    testResult = STATUS_FAILED;
                    continue;
                }
                
                testedSourceLine.start();

                int frameSize = 1;
                if ( lineAudioFormat.getFrameSize() != AudioSystem.NOT_SPECIFIED ) {
                    frameSize = lineAudioFormat.getFrameSize();
                } else {
                    if ( lineAudioFormat.getSampleSizeInBits() != AudioSystem.NOT_SPECIFIED ) {
                        frameSize = lineAudioFormat.getSampleSizeInBits()/8;
                        if ( lineAudioFormat.getSampleSizeInBits()%8 != 0 ) {
                            frameSize++;
                        }
                    }
                }
                int bufferSizeToWrite = testedSourceLine.available();
                bufferSizeToWrite = testedSourceLine.available();
                byte[] dataToWrite = new byte[bufferSizeToWrite];
                for (int k=0; k < bufferSizeToWrite; k++) {
                    dataToWrite[k] = (byte)1;
                }
                int offsetToWrite = 0;

                out.println("\n>  check SourceDataLine.write() for data length exceeding real size of 
data:");

                out.println(">  testedSourceLine.available() = " + testedSourceLine.available());
                out.println(">  frame size = " + frameSize);
                out.println(">  real size of data to write = " + bufferSizeToWrite);
                out.println(">  offset of data to write = " + offsetToWrite);
                out.println(">  number of bytes to write = " + (bufferSizeToWrite + frameSize));
                int writtenBytes = -1;
                try {
                    writtenBytes =
                        testedSourceLine.write(dataToWrite, offsetToWrite, 
bufferSizeToWrite+frameSize);
                    out.println(">  OK - number of written bytes = " + writtenBytes);
                } catch (Throwable thrown) {
                    out.println("## SourceDataLine.write(byte[] b, int off, int len) failed:");
                    out.println("#  Unexpected Exception is thrown");
                    thrown.printStackTrace(out);
                    testResult = STATUS_FAILED;
                }


                out.println("\n>  check SourceDataLine.write() for negative offset of data:");

                offsetToWrite = -1;
                out.println(">  testedSourceLine.available() = " + testedSourceLine.available());
                out.println(">  frame size = " + frameSize);
                out.println(">  real size of data to write = " + bufferSizeToWrite);
                out.println(">  offset of data to write = " + offsetToWrite);
                out.println(">  number of bytes to write = " + bufferSizeToWrite);
                writtenBytes = -1;
                try {
                    writtenBytes =
                        testedSourceLine.write(dataToWrite, offsetToWrite, bufferSizeToWrite);
                    out.println(">  OK - number of written bytes = " + writtenBytes);
                } catch (Throwable thrown) {
                    out.println("## SourceDataLine.write(byte[] b, int off, int len) failed:");
                    out.println("#  Unexpected Exception is thrown");
                    thrown.printStackTrace(out);
                    testResult = STATUS_FAILED;
                }

                out.println("\n>  check SourceDataLine.write() when \"offset + number of bytes to 
write\""
                    + " exceeds the bounds of data");

                offsetToWrite = 1;
                out.println(">  testedSourceLine.available() = " + testedSourceLine.available());
                out.println(">  frame size = " + frameSize);
                out.println(">  real size of data to write = " + bufferSizeToWrite);
                out.println(">  offset of data to write = " + offsetToWrite);
                out.println(">  number of bytes to write = " + bufferSizeToWrite);

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger tiger-beta FIXED IN: tiger-beta INTEGRATED IN: tiger-b28 tiger-beta
14-06-2004

PUBLIC COMMENTS Spec: SourceDataLine.write() throws unspecified ArrayIndexOutOfBoundsException
10-06-2004

EVALUATION ###@###.### 2002-05-15 Easy to fix, add to the doc that this exception may be thrown. Commit to mantis. ###@###.### 2003-01-09 Proposed addition to SourceDataLine.write(byte[], int, int), this is analogous to OutputStream.write(): If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. <p> If <code>off</code> is negative, or <code>len</code> is negative, or <code>off+len</code> is greater than the length of the array <code>b</code>, then an <code>ArrayIndexOutOfBoundsException</code> is thrown. TargetDataLine.read(byte[], int, int) would add this text: If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. <p> If <code>off</code> is negative, or <code>len</code> is negative, or <code>off+len</code> is greater than the length of the array <code>b</code>, then an <code>ArrayIndexOutOfBoundsException</code> is thrown. Discussion with the CCC showed that this may not be the correct way to go although it is the same as in java.io. It should rather be declared in the @throws clause in the javadoc. Commit to tiger. ###@###.### 2003-10-14 New proposed addition to SourceDataLine.write(byte[], int, int) and TargetDataLine.read(byte[], int, int): @throws ArrayIndexOutOfBoundsException if <code>off</code> is negative, or <code>off+len</code> is greater than the length of the array <code>b</code> @throws IllegalArgumentException if the requested number of bytes does not represent an integral number of sample frames, or <code>len</code> is negative Regarding the NullPointerException, see RFE 4912693: Behavior of null arguments not specified in Java Sound. ###@###.### 2003-10-14
14-10-2003