1 /** 2 * @test 3 * @bug 7088367 4 * @summary SourceDataLine.write and TargetDataLine.read don't throw ArrayIndexOutOfBoundsException 5 * @author Alex Menkov 6 */ 7 8 import javax.sound.sampled.AudioSystem; 9 import javax.sound.sampled.DataLine; 10 import javax.sound.sampled.Line; 11 import javax.sound.sampled.LineUnavailableException; 12 import javax.sound.sampled.Mixer; 13 import javax.sound.sampled.SourceDataLine; 14 import javax.sound.sampled.TargetDataLine; 15 16 public class DataLine_ArrayIndexOutOfBounds { 17 18 static int total = 0; 19 static int failed = 0; 20 21 // shared buffer for all tests 22 static final byte[] buffer = new byte[5000000]; 23 24 // the class describes different test scenarios (buffer properties) 25 static abstract class Scenario { 26 abstract int getBufferOffset(DataLine line); 27 abstract int getBufferLength(DataLine line); 28 } 29 30 // scenarios to tests 31 static Scenario[] scenarios = new Scenario[]{ 32 new Scenario() { 33 public String toString() { 34 return "offset is near Integer.MAX_VALUE"; 35 } 36 public int getBufferOffset(DataLine line) { 37 return Integer.MAX_VALUE - 4096; 38 } 39 public int getBufferLength(DataLine line) { 40 return 65536; 41 } 42 }, 43 new Scenario() { 44 public String toString() { 45 return "offset is less than buffer.length, length is large"; 46 } 47 int getBufferOffset(DataLine line) { 48 return buffer.length / 10; 49 } 50 int getBufferLength(DataLine line) { 51 return Integer.MAX_VALUE - getBufferOffset(line) + 4096; 52 } 53 } 54 }; 55 56 public static void main(String[] args) throws Exception { 57 Mixer.Info[] infos = AudioSystem.getMixerInfo(); 58 log("" + infos.length + " mixers detected"); 59 for (int i=0; i<infos.length; i++) { 60 Mixer mixer = AudioSystem.getMixer(infos[i]); 61 log("Mixer " + (i+1) + ": " + infos[i]); 62 try { 63 mixer.open(); 64 for (Scenario scenario: scenarios) { 65 testSDL(mixer, scenario); 66 testTDL(mixer, scenario); 67 } 68 mixer.close(); 69 } catch (LineUnavailableException ex) { 70 log("LineUnavailableException: " + ex); 71 } 72 } 73 if (failed == 0) { 74 log("PASSED (" + total + " tests)"); 75 } else { 76 log("FAILED (" + failed + " of " + total + " tests)"); 77 throw new Exception("Test FAILED"); 78 } 79 } 80 81 final static int STOPPER_DELAY = 5000; // 1 sec 82 83 static class AsyncLineStopper implements Runnable { 84 private final DataLine line; 85 private final long delayMS; // delay before stop the line 86 private final Thread thread; 87 private final Object readyEvent = new Object(); 88 private final Object startEvent = new Object(); 89 90 public AsyncLineStopper(DataLine line, long delayMS) { 91 this.line = line; 92 this.delayMS = delayMS; 93 thread = new Thread(this); 94 thread.setDaemon(true); 95 // starts the thread and waits until it becomes ready 96 synchronized (readyEvent) { 97 thread.start(); 98 try { 99 readyEvent.wait(); 100 } catch (InterruptedException ex) { } 101 } 102 } 103 104 // makes the delay and then stops the line 105 public void schedule() { 106 synchronized(startEvent) { 107 startEvent.notifyAll(); 108 } 109 } 110 111 // force stop/close the line 112 public void force() { 113 thread.interrupt(); 114 try { 115 thread.join(); 116 } catch (InterruptedException ex) { 117 log("join exception: " + ex); 118 } 119 } 120 121 // Runnable implementation 122 public void run() { 123 try { 124 synchronized(readyEvent) { 125 readyEvent.notifyAll(); 126 } 127 synchronized(startEvent) { 128 startEvent.wait(); 129 } 130 // delay 131 Thread.sleep(delayMS); 132 } catch (InterruptedException ex) { 133 log(" AsyncLineStopper has been interrupted: " + ex); 134 } 135 // and flush 136 log(" stop..."); 137 line.stop(); 138 log(" close..."); 139 line.close(); 140 } 141 } 142 143 static void testSDL(Mixer mixer, Scenario scenario) { 144 log(" Testing SDL (scenario: " + scenario + ")..."); 145 Line.Info linfo = new Line.Info(SourceDataLine.class); 146 SourceDataLine line = null; 147 try { 148 line = (SourceDataLine)mixer.getLine(linfo); 149 log(" got line: " + line); 150 log(" open..."); 151 line.open(); 152 } catch (IllegalArgumentException ex) { 153 log(" unsupported (IllegalArgumentException)"); 154 return; 155 } catch (LineUnavailableException ex) { 156 log(" unavailable: " + ex); 157 return; 158 } 159 160 total++; 161 162 log(" start..."); 163 line.start(); 164 165 AsyncLineStopper lineStopper = new AsyncLineStopper(line, STOPPER_DELAY); 166 int offset = scenario.getBufferOffset(line); 167 int len = scenario.getBufferLength(line); 168 // ensure len represents integral number of frames 169 len -= len % line.getFormat().getFrameSize(); 170 171 log(" write..."); 172 lineStopper.schedule(); 173 try { 174 line.write(buffer, offset, len); 175 log(" ERROR: didn't get ArrayIndexOutOfBoundsException"); 176 failed++; 177 } catch (ArrayIndexOutOfBoundsException ex) { 178 log(" OK: got ArrayIndexOutOfBoundsException: " + ex); 179 } 180 lineStopper.force(); 181 } 182 183 static void testTDL(Mixer mixer, Scenario scenario) { 184 log(" Testing TDL (scenario: " + scenario + ")..."); 185 Line.Info linfo = new Line.Info(TargetDataLine.class); 186 TargetDataLine line = null; 187 try { 188 line = (TargetDataLine)mixer.getLine(linfo); 189 log(" got line: " + line); 190 log(" open..."); 191 line.open(); 192 } catch (IllegalArgumentException ex) { 193 log(" unsupported (IllegalArgumentException)"); 194 return; 195 } catch (LineUnavailableException ex) { 196 log(" unavailable: " + ex); 197 return; 198 } 199 200 total++; 201 202 log(" start..."); 203 line.start(); 204 205 AsyncLineStopper lineStopper = new AsyncLineStopper(line, STOPPER_DELAY); 206 int offset = scenario.getBufferOffset(line); 207 int len = scenario.getBufferLength(line); 208 // ensure len represents integral number of frames 209 len -= len % line.getFormat().getFrameSize(); 210 211 log(" read..."); 212 try { 213 line.read(buffer, offset, len); 214 log(" ERROR: didn't get ArrayIndexOutOfBoundsException"); 215 failed++; 216 } catch (ArrayIndexOutOfBoundsException ex) { 217 log(" OK: got ArrayIndexOutOfBoundsException: " + ex); 218 } 219 lineStopper.force(); 220 } 221 222 static void log(String s) { 223 System.out.println(s); 224 System.out.flush(); 225 } 226 }