diff options
Diffstat (limited to 'luni')
19 files changed, 1066 insertions, 1490 deletions
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java index 1243262..c236888 100644 --- a/luni/src/main/java/java/io/FileInputStream.java +++ b/luni/src/main/java/java/io/FileInputStream.java @@ -312,25 +312,24 @@ public class FileInputStream extends InputStream implements Closeable { } openCheck(); synchronized (repositioningLock) { - // stdin requires special handling - if (fd == FileDescriptor.in) { - return (int) fileSystem.ttyRead(buffer, offset, count); - } + // BEGIN android-changed + // If you only support Linux, there's nothing special about stdin. return (int) fileSystem.read(fd.descriptor, buffer, offset, count); + // END android-changed } } /** * Skips {@code count} number of bytes in this stream. Subsequent - * {@code read()}'s will not return these bytes unless {@code reset()} is - * used. This method may perform multiple reads to read {@code count} bytes. + * {@code read()}s will not return these bytes unless {@code reset()} is + * used. If the underlying stream is unseekable, an IOException is thrown. * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException - * if {@code count < 0}, this stream is closed or another - * IOException occurs. + * if {@code count < 0}, this stream is closed or unseekable, + * or another IOException occurs. */ @Override public long skip(long count) throws IOException { @@ -344,29 +343,18 @@ public class FileInputStream extends InputStream implements Closeable { throw new IOException(Msg.getString("KA013")); //$NON-NLS-1$ } - // stdin requires special handling - if (fd == FileDescriptor.in) { - // Read and discard count bytes in 8k chunks - long skipped = 0, numRead; - int chunk = count < 8192 ? (int) count : 8192; - byte[] buffer = new byte[chunk]; - for (long i = count / chunk; i >= 0; i--) { - numRead = fileSystem.ttyRead(buffer, 0, chunk); - skipped += numRead; - if (numRead < chunk) { - return skipped; - } - } - return skipped; - } - + // BEGIN android-changed + // The RI doesn't treat stdin as special. It throws IOException for + // all non-seekable streams, so we do too. If you did want to support + // non-seekable streams, the best way to do it would be to recognize + // when lseek(2) fails with ESPIPE and call super.skip(count). synchronized (repositioningLock) { - final long currentPosition = fileSystem.seek(fd.descriptor, 0L, - IFileSystem.SEEK_CUR); - final long newPosition = fileSystem.seek(fd.descriptor, - currentPosition + count, IFileSystem.SEEK_SET); - return newPosition - currentPosition; + // Our seek returns the new offset, but we know it will throw an + // exception if it couldn't perform exactly the seek we asked for. + fileSystem.seek(fd.descriptor, count, IFileSystem.SEEK_CUR); + return count; } + // END android-changed } private synchronized void openCheck() throws IOException { diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java index b8c7056..7c46e89 100644 --- a/luni/src/main/java/java/util/ArrayList.java +++ b/luni/src/main/java/java/util/ArrayList.java @@ -15,12 +15,16 @@ * limitations under the License. */ +// BEGIN android-note +// New implementation: simpler and faster than Harmony implementation. +// BEGIN android-note + package java.util; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.ObjectStreamField; import java.io.Serializable; import java.lang.reflect.Array; @@ -29,37 +33,38 @@ import java.lang.reflect.Array; * optional operations adding, removing, and replacing are supported. The * elements can be any objects. * + * @param <E> The element type of this list. * @since 1.2 */ -public class ArrayList<E> extends AbstractList<E> implements List<E>, - Cloneable, Serializable, RandomAccess { - - private static final long serialVersionUID = 8683452581122892189L; - - // BEGIN android-added - /** zero-element array */ - private static final Object[] emptyArray = new Object[0]; - // END android-added - - private transient int firstIndex; +public class ArrayList<E> extends AbstractList<E> + implements Cloneable, Serializable, RandomAccess { + /** + * An empty array of objects (to be shared among all empty lists). + */ + private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - private transient int lastIndex; + /** + * The minimum amount by which the capacity of an ArrayList will increase. + * This tuning parameter controls a time-space tradeoff. This value (12) + * gives empirically good results and is arguably consistent with the + * RI's specified default initial capacity of 10: instead of 10, we start + * with 0 (sans allocation) and jump to 12. + */ + private static final int MIN_CAPACITY_INCREMENT = 12; - private transient E[] array; + /** + * The number of elements in this list. + */ + int size; /** - * Constructs a new instance of {@code ArrayList} with zero capacity. + * The elements in this list, followed by nulls. */ - public ArrayList() { - // BEGIN android-changed - // default capacity is zero, not ten - this(0); - // END android-changed - } + transient Object[] array; /** * Constructs a new instance of {@code ArrayList} with the specified - * capacity. + * initial capacity. * * @param capacity * the initial capacity of this {@code ArrayList}. @@ -68,37 +73,55 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, if (capacity < 0) { throw new IllegalArgumentException(); } - firstIndex = lastIndex = 0; - array = newElementArray(capacity); + array = (capacity == 0 ? EMPTY_OBJECT_ARRAY : new Object[capacity]); + } + + /** + * Constructs a new {@code ArrayList} instance with zero initial capacity. + */ + public ArrayList() { + array = EMPTY_OBJECT_ARRAY; } /** * Constructs a new instance of {@code ArrayList} containing the elements of - * the specified collection. The initial size of the {@code ArrayList} will - * be 10% higher than the size of the specified collection. + * the specified collection. * * @param collection * the collection of elements to add. */ public ArrayList(Collection<? extends E> collection) { - firstIndex = 0; - Object[] objects = collection.toArray(); - int size = objects.length; - array = newElementArray(size + (size / 10)); - System.arraycopy(objects, 0, array, 0, size); - lastIndex = size; - modCount = 1; + Object[] a = collection.toArray(); + if (a.getClass() != Object[].class) { + Object[] newArray = new Object[a.length]; + System.arraycopy(a, 0, newArray, 0, a.length); + a = newArray; + } + array = a; + size = a.length; } - @SuppressWarnings("unchecked") - private E[] newElementArray(int size) { - // BEGIN android-added - if (size == 0) { - return (E[])emptyArray; + /** + * Adds the specified object at the end of this {@code ArrayList}. + * + * @param object + * the object to add. + * @return always true + */ + @Override public boolean add(E object) { + Object[] a = array; + int s = size; + if (s == a.length) { + Object[] newArray = new Object[s + + (s < (MIN_CAPACITY_INCREMENT / 2) ? + MIN_CAPACITY_INCREMENT : s >> 1)]; + System.arraycopy(a, 0, newArray, 0, s); + array = a = newArray; } - // END android-added - - return (E[]) new Object[size]; + a[s] = object; + size = s + 1; + modCount++; + return true; } /** @@ -107,164 +130,136 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * specified location. If the location is equal to the size of this * {@code ArrayList}, the object is added at the end. * - * @param location + * @param index * the index at which to insert the object. * @param object * the object to add. * @throws IndexOutOfBoundsException * when {@code location < 0 || > size()} */ - @Override - public void add(int location, E object) { - int size = lastIndex - firstIndex; - if (0 < location && location < size) { - if (firstIndex == 0 && lastIndex == array.length) { - growForInsert(location, 1); - } else if ((location < size / 2 && firstIndex > 0) - || lastIndex == array.length) { - System.arraycopy(array, firstIndex, array, --firstIndex, - location); - } else { - int index = location + firstIndex; - System.arraycopy(array, index, array, index + 1, size - - location); - lastIndex++; - } - array[location + firstIndex] = object; - } else if (location == 0) { - if (firstIndex == 0) { - growAtFront(1); - } - array[--firstIndex] = object; - } else if (location == size) { - if (lastIndex == array.length) { - growAtEnd(1); - } - array[lastIndex++] = object; - } else { - throw new IndexOutOfBoundsException(); + @Override public void add(int index, E object) { + Object[] a = array; + int s = size; + if (index > s) { + throwIndexOutOfBoundsException(index, s); } + if (s < a.length) { + System.arraycopy(a, index, a, index + 1, s - index); + } else { + // assert s == a.length; + Object[] newArray = new Object[newCapacity(s)]; + System.arraycopy(a, 0, newArray, 0, index); + System.arraycopy(a, index, newArray, index + 1, s - index); + array = a = newArray; + } + a[index] = object; + size = s + 1; modCount++; } /** - * Adds the specified object at the end of this {@code ArrayList}. + * This method controls the growth of ArrayList capacities. It represents + * a time-space tradeoff: we don't want to grow lists too frequently + * (which wastes time and fragments storage), but we don't want to waste + * too much space in unused excess capacity. * - * @param object - * the object to add. - * @return always true + * NOTE: This method is inlined into {@link #add(Object)} for performance. + * If you change the method, change it there too! */ - @Override - public boolean add(E object) { - if (lastIndex == array.length) { - growAtEnd(1); - } - array[lastIndex++] = object; - modCount++; - return true; + private static int newCapacity(int currentCapacity) { + int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ? + MIN_CAPACITY_INCREMENT : currentCapacity >> 1); + return currentCapacity + increment; } /** - * Inserts the objects in the specified collection at the specified location - * in this List. The objects are added in the order they are returned from - * the collection's iterator. + * Adds the objects in the specified collection to this {@code ArrayList}. * - * @param location - * the index at which to insert. * @param collection * the collection of objects. * @return {@code true} if this {@code ArrayList} is modified, {@code false} * otherwise. - * @throws IndexOutOfBoundsException - * when {@code location < 0 || > size()} */ - @Override - public boolean addAll(int location, Collection<? extends E> collection) { - int size = lastIndex - firstIndex; - if (location < 0 || location > size) { - throw new IndexOutOfBoundsException(); - } - if (this == collection) { - collection = (ArrayList)clone(); - } - Object[] dumparray = collection.toArray(); - int growSize = dumparray.length; - if (growSize == 0) { + @Override public boolean addAll(Collection<? extends E> collection) { + Object[] newPart = collection.toArray(); + int newPartSize = newPart.length; + if (newPartSize == 0) { return false; } - - if (0 < location && location < size) { - if (array.length - size < growSize) { - growForInsert(location, growSize); - } else if ((location < size / 2 && firstIndex > 0) - || lastIndex > array.length - growSize) { - int newFirst = firstIndex - growSize; - if (newFirst < 0) { - int index = location + firstIndex; - System.arraycopy(array, index, array, index - newFirst, - size - location); - lastIndex -= newFirst; - newFirst = 0; - } - System.arraycopy(array, firstIndex, array, newFirst, location); - firstIndex = newFirst; - } else { - int index = location + firstIndex; - System.arraycopy(array, index, array, index + growSize, size - - location); - lastIndex += growSize; - } - } else if (location == 0) { - growAtFront(growSize); - firstIndex -= growSize; - } else if (location == size) { - if (lastIndex > array.length - growSize) { - growAtEnd(growSize); - } - lastIndex += growSize; + Object[] a = array; + int s = size; + int newSize = s + newPartSize; // If add overflows, arraycopy will fail + if (newSize > a.length) { + int newCapacity = newCapacity(newSize - 1); // ~33% growth room + Object[] newArray = new Object[newCapacity]; + System.arraycopy(a, 0, newArray, 0, s); + array = a = newArray; } - - System.arraycopy(dumparray, 0, this.array, location + firstIndex, - growSize); + System.arraycopy(newPart, 0, a, s, newPartSize); + size = newSize; modCount++; return true; } /** - * Adds the objects in the specified collection to this {@code ArrayList}. + * Inserts the objects in the specified collection at the specified location + * in this List. The objects are added in the order they are returned from + * the collection's iterator. * + * @param index + * the index at which to insert. * @param collection * the collection of objects. * @return {@code true} if this {@code ArrayList} is modified, {@code false} * otherwise. + * @throws IndexOutOfBoundsException + * when {@code location < 0 || > size()} */ @Override - public boolean addAll(Collection<? extends E> collection) { - Object[] dumpArray = collection.toArray(); - if (dumpArray.length == 0) { + public boolean addAll(int index, Collection<? extends E> collection) { + Object[] newPart = collection.toArray(); + int newPartSize = newPart.length; + if (newPartSize == 0) { return false; } - if (dumpArray.length > array.length - lastIndex) { - growAtEnd(dumpArray.length); + Object[] a = array; + int s = size; + if (index > s) { + throwIndexOutOfBoundsException(index, s); } - System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); - lastIndex += dumpArray.length; + int newSize = s + newPartSize; // If add overflows, arraycopy will fail + if (newSize <= a.length) { + System.arraycopy(a, index, a, index + newPartSize, s - index); + } else { + int newCapacity = newCapacity(newSize - 1); // ~33% growth room + Object[] newArray = new Object[newCapacity]; + System.arraycopy(a, 0, newArray, 0, index); + System.arraycopy(a, index, newArray, index + newPartSize, s-index); + array = a = newArray; + } + System.arraycopy(newPart, 0, a, index, newPartSize); + size = newSize; modCount++; return true; } + /** This method was extracted to encourage VM to inline callers. */ + private static void throwIndexOutOfBoundsException(int index, int size) { + throw new IndexOutOfBoundsException("Invalid index " + index + + ", size is " + size); + } + /** * Removes all elements from this {@code ArrayList}, leaving it empty. * * @see #isEmpty * @see #size */ - @Override - public void clear() { - if (firstIndex != lastIndex) { - Arrays.fill(array, firstIndex, lastIndex, null); - firstIndex = lastIndex = 0; + @Override public void clear() { + if (size != 0) { + Arrays.fill(array, 0, size, null); + size = 0; modCount++; } } @@ -276,45 +271,17 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @return a shallow copy of this {@code ArrayList} * @see java.lang.Cloneable */ - @Override - @SuppressWarnings("unchecked") - public Object clone() { + @Override public Object clone() { try { - ArrayList<E> newList = (ArrayList<E>) super.clone(); - newList.array = array.clone(); - return newList; + ArrayList<?> result = (ArrayList<?>) super.clone(); + result.array = array.clone(); + return result; } catch (CloneNotSupportedException e) { - return null; + throw new AssertionError(); } } /** - * Searches this {@code ArrayList} for the specified object. - * - * @param object - * the object to search for. - * @return {@code true} if {@code object} is an element of this - * {@code ArrayList}, {@code false} otherwise - */ - @Override - public boolean contains(Object object) { - if (object != null) { - for (int i = firstIndex; i < lastIndex; i++) { - if (object.equals(array[i])) { - return true; - } - } - } else { - for (int i = firstIndex; i < lastIndex; i++) { - if (array[i] == null) { - return true; - } - } - } - return false; - } - - /** * Ensures that after this operation the {@code ArrayList} can hold the * specified number of elements without further growing. * @@ -322,145 +289,93 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * the minimum capacity asked for. */ public void ensureCapacity(int minimumCapacity) { - if (array.length < minimumCapacity) { - if (firstIndex > 0) { - growAtFront(minimumCapacity - array.length); - } else { - growAtEnd(minimumCapacity - array.length); - } + Object[] a = array; + if (a.length < minimumCapacity) { + Object[] newArray = new Object[minimumCapacity]; + System.arraycopy(a, 0, newArray, 0, size); + array = newArray; + modCount++; } } - @Override - public E get(int location) { - // BEGIN android-changed: slight performance improvement - int _firstIndex = firstIndex; - if (0 <= location && location < lastIndex - _firstIndex) { - return array[_firstIndex + location]; - } - throw new IndexOutOfBoundsException("Invalid location " + location - + ", size is " + (lastIndex - _firstIndex)); - // END android-changed + @SuppressWarnings("unchecked") @Override public E get(int index) { + if (index >= size) { + throwIndexOutOfBoundsException(index, size); + } + return (E) array[index]; } - private void growAtEnd(int required) { - int size = lastIndex - firstIndex; - if (firstIndex >= required - (array.length - lastIndex)) { - int newLast = lastIndex - firstIndex; - if (size > 0) { - System.arraycopy(array, firstIndex, array, 0, size); - int start = newLast < firstIndex ? firstIndex : newLast; - Arrays.fill(array, start, array.length, null); - } - firstIndex = 0; - lastIndex = newLast; - } else { - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - if (size > 0) { - System.arraycopy(array, firstIndex, newArray, 0, size); - firstIndex = 0; - lastIndex = size; - } - array = newArray; - } + /** + * Returns the number of elements in this {@code ArrayList}. + * + * @return the number of elements in this {@code ArrayList}. + */ + @Override public int size() { + return size; + } + + @Override public boolean isEmpty() { + return size == 0; } - private void growAtFront(int required) { - int size = lastIndex - firstIndex; - if (array.length - lastIndex + firstIndex >= required) { - int newFirst = array.length - size; - if (size > 0) { - System.arraycopy(array, firstIndex, array, newFirst, size); - int length = firstIndex + size > newFirst ? newFirst - : firstIndex + size; - Arrays.fill(array, firstIndex, length, null); + /** + * Searches this {@code ArrayList} for the specified object. + * + * @param object + * the object to search for. + * @return {@code true} if {@code object} is an element of this + * {@code ArrayList}, {@code false} otherwise + */ + @Override public boolean contains(Object object) { + Object[] a = array; + int s = size; + if (object != null) { + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + return true; + } } - firstIndex = newFirst; - lastIndex = array.length; } else { - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - if (size > 0) { - System.arraycopy(array, firstIndex, newArray, newArray.length - - size, size); + for (int i = 0; i < s; i++) { + if (a[i] == null) { + return true; + } } - firstIndex = newArray.length - size; - lastIndex = newArray.length; - array = newArray; } + return false; } - private void growForInsert(int location, int required) { - int size = lastIndex - firstIndex; - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - int newFirst = increment - required; - // Copy elements after location to the new array skipping inserted - // elements - System.arraycopy(array, location + firstIndex, newArray, newFirst - + location + required, size - location); - // Copy elements before location to the new array from firstIndex - System.arraycopy(array, firstIndex, newArray, newFirst, location); - firstIndex = newFirst; - lastIndex = size + increment; - - array = newArray; - } - - @Override - public int indexOf(Object object) { + @Override public int indexOf(Object object) { + Object[] a = array; + int s = size; if (object != null) { - for (int i = firstIndex; i < lastIndex; i++) { - if (object.equals(array[i])) { - return i - firstIndex; + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + return i; } } } else { - for (int i = firstIndex; i < lastIndex; i++) { - if (array[i] == null) { - return i - firstIndex; + for (int i = 0; i < s; i++) { + if (a[i] == null) { + return i; } } } return -1; } - @Override - public boolean isEmpty() { - return lastIndex == firstIndex; - } - - @Override - public int lastIndexOf(Object object) { + @Override public int lastIndexOf(Object object) { + Object[] a = array; if (object != null) { - for (int i = lastIndex - 1; i >= firstIndex; i--) { - if (object.equals(array[i])) { - return i - firstIndex; + for (int i = size - 1; i >= 0; i--) { + if (object.equals(a[i])) { + return i; } } } else { - for (int i = lastIndex - 1; i >= firstIndex; i--) { - if (array[i] == null) { - return i - firstIndex; + for (int i = size - 1; i >= 0; i--) { + if (a[i] == null) { + return i; } } } @@ -470,99 +385,81 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, /** * Removes the object at the specified location from this list. * - * @param location + * @param index * the index of the object to remove. * @return the removed object. * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} */ - @Override - public E remove(int location) { - E result; - int size = lastIndex - firstIndex; - if (0 <= location && location < size) { - if (location == size - 1) { - result = array[--lastIndex]; - array[lastIndex] = null; - } else if (location == 0) { - result = array[firstIndex]; - array[firstIndex++] = null; - } else { - int elementIndex = firstIndex + location; - result = array[elementIndex]; - if (location < size / 2) { - System.arraycopy(array, firstIndex, array, firstIndex + 1, - location); - array[firstIndex++] = null; - } else { - System.arraycopy(array, elementIndex + 1, array, - elementIndex, size - location - 1); - array[--lastIndex] = null; - } - } - if (firstIndex == lastIndex) { - firstIndex = lastIndex = 0; - } - } else { - throw new IndexOutOfBoundsException(); + @Override public E remove(int index) { + Object[] a = array; + int s = size; + if (index >= s) { + throwIndexOutOfBoundsException(index, s); } - + @SuppressWarnings("unchecked") E result = (E) a[index]; + System.arraycopy(a, index + 1, a, index, --s - index); + a[s] = null; // Prevent memory leak + size = s; modCount++; return result; } - @Override - public boolean remove(Object object) { - int location = indexOf(object); - if (location >= 0) { - remove(location); - return true; + @Override public boolean remove(Object object) { + Object[] a = array; + int s = size; + if (object != null) { + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + System.arraycopy(a, i + 1, a, i, --s - i); + a[s] = null; // Prevent memory leak + size = s; + modCount++; + return true; + } + } + } else { + for (int i = 0; i < s; i++) { + if (a[i] == null) { + System.arraycopy(a, i + 1, a, i, --s - i); + a[s] = null; // Prevent memory leak + size = s; + modCount++; + return true; + } + } } return false; } - /** - * Removes the objects in the specified range from the start to the end, but - * not including the end index. - * - * @param start - * the index at which to start removing. - * @param end - * the index one after the end of the range to remove. - * @throws IndexOutOfBoundsException - * when {@code start < 0, start > end} or {@code end > size()} - */ - @Override - protected void removeRange(int start, int end) { - if (start >= 0 && start <= end && end <= (lastIndex - firstIndex)) { - if (start == end) { - return; - } - int size = lastIndex - firstIndex; - if (end == size) { - Arrays.fill(array, firstIndex + start, lastIndex, null); - lastIndex = firstIndex + start; - } else if (start == 0) { - Arrays.fill(array, firstIndex, firstIndex + end, null); - firstIndex += end; - } else { - System.arraycopy(array, firstIndex + end, array, firstIndex - + start, size - end); - int newLast = lastIndex + start - end; - Arrays.fill(array, newLast, lastIndex, null); - lastIndex = newLast; - } - modCount++; - } else { - throw new IndexOutOfBoundsException(); + @Override protected void removeRange(int fromIndex, int toIndex) { + Object[] a = array; + int s = size; + if (fromIndex >= s) { + throw new IndexOutOfBoundsException("fromIndex " + fromIndex + + " >= size " + size); } + if (toIndex > s) { + throw new IndexOutOfBoundsException("toIndex " + toIndex + + " > size " + size); + } + if (fromIndex > toIndex) { + throw new IndexOutOfBoundsException("fromIndex " + fromIndex + + " > toIndex " + toIndex); + } + + System.arraycopy(a, toIndex, a, fromIndex, s - toIndex); + int rangeSize = toIndex - fromIndex; + Arrays.fill(a, s - rangeSize, s, null); + size = s - rangeSize; + modCount++; } /** * Replaces the element at the specified location in this {@code ArrayList} * with the specified object. * - * @param location + * @param index * the index at which to put the specified object. * @param object * the object to add. @@ -570,24 +467,14 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} */ - @Override - public E set(int location, E object) { - if (0 <= location && location < (lastIndex - firstIndex)) { - E result = array[firstIndex + location]; - array[firstIndex + location] = object; - return result; + @Override public E set(int index, E object) { + Object[] a = array; + if (index >= size) { + throwIndexOutOfBoundsException(index, size); } - throw new IndexOutOfBoundsException(); - } - - /** - * Returns the number of elements in this {@code ArrayList}. - * - * @return the number of elements in this {@code ArrayList}. - */ - @Override - public int size() { - return lastIndex - firstIndex; + @SuppressWarnings("unchecked") E result = (E) a[index]; + a[index] = object; + return result; } /** @@ -596,11 +483,10 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * * @return an array of the elements from this {@code ArrayList} */ - @Override - public Object[] toArray() { - int size = lastIndex - firstIndex; - Object[] result = new Object[size]; - System.arraycopy(array, firstIndex, result, 0, size); + @Override public Object[] toArray() { + int s = size; + Object[] result = new Object[s]; + System.arraycopy(array, 0, result, 0, s); return result; } @@ -619,17 +505,16 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * when the type of an element in this {@code ArrayList} cannot * be stored in the type of the specified array. */ - @Override - @SuppressWarnings("unchecked") - public <T> T[] toArray(T[] contents) { - int size = lastIndex - firstIndex; - if (size > contents.length) { - Class<?> ct = contents.getClass().getComponentType(); - contents = (T[]) Array.newInstance(ct, size); + @Override public <T> T[] toArray(T[] contents) { + int s = size; + if (contents.length < s) { + @SuppressWarnings("unchecked") T[] newArray + = (T[]) Array.newInstance(contents.getClass().getComponentType(), s); + contents = newArray; } - System.arraycopy(array, firstIndex, contents, 0, size); - if (size < contents.length) { - contents[size] = null; + System.arraycopy(this.array, 0, contents, 0, s); + if (contents.length > s) { + contents[s] = null; } return contents; } @@ -641,37 +526,132 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @see #size */ public void trimToSize() { - int size = lastIndex - firstIndex; - E[] newArray = newElementArray(size); - System.arraycopy(array, firstIndex, newArray, 0, size); - array = newArray; - firstIndex = 0; - lastIndex = array.length; - modCount = 0; + int s = size; + if (s == array.length) { + return; + } + if (s == 0) { + array = EMPTY_OBJECT_ARRAY; + } else { + Object[] newArray = new Object[s]; + System.arraycopy(array, 0, newArray, 0, s); + array = newArray; + } + modCount++; + } + + @Override public Iterator<E> iterator() { + return new ArrayListIterator(); + } + + private class ArrayListIterator implements Iterator<E> { + /** Number of elements remaining in this iteration */ + private int remaining = size; + + /** Index of element that remove() would remove, or -1 if no such elt */ + private int removalIndex = -1; + + /** The expected modCount value */ + private int expectedModCount = modCount; + + public boolean hasNext() { + return remaining != 0; + } + + @SuppressWarnings("unchecked") public E next() { + ArrayList<E> ourList = ArrayList.this; + int rem = remaining; + if (ourList.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (rem == 0) { + throw new NoSuchElementException(); + } + remaining = rem - 1; + return (E) ourList.array[removalIndex = ourList.size - rem]; + } + + public void remove() { + Object[] a = array; + int removalIdx = removalIndex; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (removalIdx < 0) { + throw new IllegalStateException(); + } + System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining); + a[--size] = null; // Prevent memory leak + removalIndex = -1; + expectedModCount = ++modCount; + } } - private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField( - "size", Integer.TYPE) }; //$NON-NLS-1$ + @Override public int hashCode() { + Object[] a = array; + int hashCode = 1; + for (int i = 0, s = size; i < s; i++) { + Object e = a[i]; + hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); + } + return hashCode; + } + + @Override public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof List)) { + return false; + } + List<?> that = (List<?>) o; + int s = size; + if (that.size() != s) { + return false; + } + Object[] a = array; + if (that instanceof RandomAccess) { + for (int i = 0; i < s; i++) { + Object eThis = a[i]; + Object ethat = that.get(i); + if (eThis == null ? ethat != null : !eThis.equals(ethat)) { + return false; + } + } + } else { // Argument list is not random access; use its iterator + Iterator<?> it = that.iterator(); + for (int i = 0; i < s; i++) { + Object eThis = a[i]; + Object eThat = it.next(); + if (eThis == null ? eThat != null : !eThis.equals(eThat)) { + return false; + } + } + } + return true; + } + + private static final long serialVersionUID = 8683452581122892189L; private void writeObject(ObjectOutputStream stream) throws IOException { - ObjectOutputStream.PutField fields = stream.putFields(); - fields.put("size", lastIndex - firstIndex); //$NON-NLS-1$ - stream.writeFields(); + stream.defaultWriteObject(); stream.writeInt(array.length); - Iterator<?> it = iterator(); - while (it.hasNext()) { - stream.writeObject(it.next()); + for (int i = 0; i < size; i++) { + stream.writeObject(array[i]); } } - @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = stream.readFields(); - lastIndex = fields.get("size", 0); //$NON-NLS-1$ - array = newElementArray(stream.readInt()); - for (int i = 0; i < lastIndex; i++) { - array[i] = (E) stream.readObject(); + stream.defaultReadObject(); + int cap = stream.readInt(); + if (cap < size) { + throw new InvalidObjectException( + "Capacity: " + cap + " < size: " + size); + } + array = (cap == 0 ? EMPTY_OBJECT_ARRAY : new Object[cap]); + for (int i = 0; i < size; i++) { + array[i] = stream.readObject(); } } -} + } diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java index d1dd417..3d8b94e 100644 --- a/luni/src/main/java/java/util/Formatter.java +++ b/luni/src/main/java/java/util/Formatter.java @@ -885,7 +885,6 @@ public final class Formatter implements Closeable, Flushable { if (transformer == null || ! transformer.locale.equals(l)) { transformer = new Transformer(this, l); } - // END android-changed int currentObjectIndex = 0; Object lastArgument = null; @@ -893,12 +892,13 @@ public final class Formatter implements Closeable, Flushable { while (formatBuffer.hasRemaining()) { parser.reset(); FormatToken token = parser.getNextFormatToken(); - String result; String plainText = token.getPlainText(); if (token.getConversionType() == (char) FormatToken.UNSET) { - result = plainText; + outputCharSequence(plainText); } else { plainText = plainText.substring(0, plainText.indexOf('%')); + outputCharSequence(plainText); + Object argument = null; if (token.requireArgument()) { int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++ @@ -908,21 +908,26 @@ public final class Formatter implements Closeable, Flushable { lastArgument = argument; hasLastArgumentSet = true; } - result = transformer.transform(token, argument); - result = (null == result ? plainText : plainText + result); - } - // if output is made by formattable callback - if (null != result) { - try { - out.append(result); - } catch (IOException e) { - lastIOException = e; - } + outputCharSequence(transformer.transform(token, argument)); } } + // END android-changed return this; } + // BEGIN android-added + // Fixes http://code.google.com/p/android/issues/detail?id=1767. + private void outputCharSequence(CharSequence cs) { + if (cs != null) { + try { + out.append(cs); + } catch (IOException e) { + lastIOException = e; + } + } + } + // END android-added + private Object getArgument(Object[] args, int index, FormatToken token, Object lastArgument, boolean hasLastArgumentSet) { if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) { @@ -1184,13 +1189,13 @@ public final class Formatter implements Closeable, Flushable { * Gets the formatted string according to the format token and the * argument. */ - String transform(FormatToken token, Object argument) { + CharSequence transform(FormatToken token, Object argument) { /* init data member to print */ this.formatToken = token; this.arg = argument; - String result; + CharSequence result; switch (token.getConversionType()) { case 'B': case 'b': { @@ -1254,7 +1259,7 @@ public final class Formatter implements Closeable, Flushable { if (Character.isUpperCase(token.getConversionType())) { if (null != result) { - result = result.toUpperCase(Locale.US); + result = result.toString().toUpperCase(Locale.US); } } return result; @@ -1263,7 +1268,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the Boolean argument to a formatted string. */ - private String transformFromBoolean() { + private CharSequence transformFromBoolean() { StringBuilder result = new StringBuilder(); int startIndex = 0; int flags = formatToken.getFlags(); @@ -1294,7 +1299,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the hashcode of the argument to a formatted string. */ - private String transformFromHashCode() { + private CharSequence transformFromHashCode() { StringBuilder result = new StringBuilder(); int startIndex = 0; @@ -1324,7 +1329,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the String to a formatted string. */ - private String transformFromString() { + private CharSequence transformFromString() { StringBuilder result = new StringBuilder(); int startIndex = 0; int flags = formatToken.getFlags(); @@ -1374,7 +1379,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the Character to a formatted string. */ - private String transformFromCharacter() { + private CharSequence transformFromCharacter() { StringBuilder result = new StringBuilder(); int startIndex = 0; @@ -1434,7 +1439,7 @@ public final class Formatter implements Closeable, Flushable { * Transforms percent to a formatted string. Only '-' is legal flag. * Precision is illegal. */ - private String transformFromPercent() { + private CharSequence transformFromPercent() { StringBuilder result = new StringBuilder("%"); //$NON-NLS-1$ int startIndex = 0; @@ -1462,7 +1467,7 @@ public final class Formatter implements Closeable, Flushable { * Transforms line separator to a formatted string. Any flag, the width * or the precision is illegal. */ - private String transformFromLineSeparator() { + private CharSequence transformFromLineSeparator() { if (formatToken.isPrecisionSet()) { throw new IllegalFormatPrecisionException(formatToken .getPrecision()); @@ -1492,7 +1497,7 @@ public final class Formatter implements Closeable, Flushable { /* * Pads characters to the formatted string. */ - private String padding(StringBuilder source, int startIndex) { + private CharSequence padding(StringBuilder source, int startIndex) { int start = startIndex; boolean paddingRight = formatToken .isFlagSet(FormatToken.FLAG_MINUS); @@ -1520,7 +1525,7 @@ public final class Formatter implements Closeable, Flushable { width = Math.max(source.length(), width); } if (length >= width) { - return source.toString(); + return source; } char[] paddings = new char[width - length]; @@ -1532,13 +1537,13 @@ public final class Formatter implements Closeable, Flushable { } else { source.insert(start, insertString); } - return source.toString(); + return source; } /* * Transforms the Integer to a formatted string. */ - private String transformFromInteger() { + private CharSequence transformFromInteger() { int startIndex = 0; boolean isNegative = false; StringBuilder result = new StringBuilder(); @@ -1651,7 +1656,7 @@ public final class Formatter implements Closeable, Flushable { if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) { @@ -1680,7 +1685,7 @@ public final class Formatter implements Closeable, Flushable { return result; } - private String transformFromSpecialNumber() { + private CharSequence transformFromSpecialNumber() { String source = null; if (!(arg instanceof Number) || arg instanceof BigDecimal) { @@ -1713,12 +1718,12 @@ public final class Formatter implements Closeable, Flushable { formatToken.setPrecision(FormatToken.UNSET); formatToken.setFlags(formatToken.getFlags() & (~FormatToken.FLAG_ZERO)); - source = padding(new StringBuilder(source), 0); + return padding(new StringBuilder(source), 0); } return source; } - private String transformFromNull() { + private CharSequence transformFromNull() { formatToken.setFlags(formatToken.getFlags() & (~FormatToken.FLAG_ZERO)); return padding(new StringBuilder("null"), 0); //$NON-NLS-1$ @@ -1727,7 +1732,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a BigInteger to a formatted string. */ - private String transformFromBigInteger() { + private CharSequence transformFromBigInteger() { int startIndex = 0; boolean isNegative = false; StringBuilder result = new StringBuilder(); @@ -1817,7 +1822,7 @@ public final class Formatter implements Closeable, Flushable { if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) { @@ -1829,7 +1834,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a Float,Double or BigDecimal to a formatted string. */ - private String transformFromFloat() { + private CharSequence transformFromFloat() { StringBuilder result = new StringBuilder(); int startIndex = 0; char currentConversionType = formatToken.getConversionType(); @@ -1883,7 +1888,7 @@ public final class Formatter implements Closeable, Flushable { currentConversionType, arg.getClass()); } - String specialNumberResult = transformFromSpecialNumber(); + CharSequence specialNumberResult = transformFromSpecialNumber(); if (null != specialNumberResult) { return specialNumberResult; } @@ -1896,7 +1901,7 @@ public final class Formatter implements Closeable, Flushable { } // output result FloatUtil floatUtil = new FloatUtil(result, formatToken, - (DecimalFormat) NumberFormat.getInstance(locale), arg); + (DecimalFormat) getNumberFormat(), arg); floatUtil.transform(formatToken, result); formatToken.setPrecision(FormatToken.UNSET); @@ -1904,7 +1909,7 @@ public final class Formatter implements Closeable, Flushable { if (getDecimalFormatSymbols().getMinusSign() == result.charAt(0)) { if (formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } } else { if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) { @@ -1933,7 +1938,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a Date to a formatted string. */ - private String transformFromDateTime() { + private CharSequence transformFromDateTime() { int startIndex = 0; char currentConversionType = formatToken.getConversionType(); diff --git a/luni/src/main/java/java/util/HashMap.java b/luni/src/main/java/java/util/HashMap.java index 28978d4..f79601f 100644 --- a/luni/src/main/java/java/util/HashMap.java +++ b/luni/src/main/java/java/util/HashMap.java @@ -36,7 +36,7 @@ import java.io.Serializable; * @param <V> the type of mapped values */ public class HashMap<K, V> extends AbstractMap<K, V> - implements Cloneable, Serializable, Map<K, V> { + implements Cloneable, Serializable { /** * Min capacity (other than zero) for a HashMap. Must be a power of two * greater than 1 (and less than 1 << 30). diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java index 110a0fd..01319e1 100644 --- a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java +++ b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java @@ -111,12 +111,23 @@ public class ZoneInfo extends TimeZone { // Subtract the raw offset from all offsets so it can be changed // and affect them too. - // Find whether there exist any observances of DST. - for (int i = 0; i < mGmtOffs.length; i++) { mGmtOffs[i] -= mRawOffset; + } - if (mIsDsts[i] != 0) { + // Is this zone still observing DST? + // We don't care if they've historically used it: most places have at least once. + // We want to know whether the last "schedule info" (the unix times in the mTransitions + // array) is in the future. If it is, DST is still relevant. + // See http://code.google.com/p/android/issues/detail?id=877. + // This test means that for somewhere like Morocco, which tried DST in 2009 but has + // no future plans (and thus no future schedule info) will report "true" from + // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate. + long currentUnixTime = System.currentTimeMillis() / 1000; + if (mTransitions.length > 0) { + // (We're really dealing with uint32_t values, so long is most convenient in Java.) + long latestScheduleTime = mTransitions[mTransitions.length - 1] & 0xffffffff; + if (currentUnixTime < latestScheduleTime) { mUseDst = true; } } diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java index 7613f0e..bee1557 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java @@ -108,10 +108,9 @@ public interface IFileSystem { // BEGIN android-deleted // public long ttyAvailable() throws IOException; + // public long ttyRead(byte[] bytes, int offset, int length) throws IOException; // END android-deleted - public long ttyRead(byte[] bytes, int offset, int length) throws IOException; - // BEGIN android-added public int ioctlAvailable(int fileDescriptor) throws IOException; // END android-added diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java index 08bdac6..b7a62e2 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java @@ -69,7 +69,7 @@ class OSFileSystem implements IFileSystem { * Note that this value for Windows differs from the one for the * page size (64K and 4K respectively). */ - public native int getAllocGranularity() throws IOException; + public native int getAllocGranularity(); public boolean lock(int fileDescriptor, long start, long length, int type, boolean waitFlag) throws IOException { @@ -79,160 +79,71 @@ class OSFileSystem implements IFileSystem { return result != -1; } - private native int unlockImpl(int fileDescriptor, long start, long length); + // BEGIN android-changed + private native void unlockImpl(int fileDescriptor, long start, long length) throws IOException; public void unlock(int fileDescriptor, long start, long length) throws IOException { // Validate arguments validateLockArgs(IFileSystem.SHARED_LOCK_TYPE, start, length); - int result = unlockImpl(fileDescriptor, start, length); - if (result == -1) { - throw new IOException(); - } + unlockImpl(fileDescriptor, start, length); } - private native int fflushImpl(int fd, boolean metadata); - - public void fflush(int fileDescriptor, boolean metadata) - throws IOException { - int result = fflushImpl(fileDescriptor, metadata); - if (result == -1) { - throw new IOException(); - } - } + public native void fflush(int fileDescriptor, boolean metadata) throws IOException; /* * File position seeking. */ - - private native long seekImpl(int fd, long offset, int whence); - - public long seek(int fileDescriptor, long offset, int whence) - throws IOException { - long pos = seekImpl(fileDescriptor, offset, whence); - if (pos == -1) { - throw new IOException(); - } - return pos; - } + public native long seek(int fd, long offset, int whence) throws IOException; /* * Direct read/write APIs work on addresses. */ - private native long readDirectImpl(int fileDescriptor, int address, - int offset, int length); - - public long readDirect(int fileDescriptor, int address, int offset, - int length) throws IOException { - long bytesRead = readDirectImpl(fileDescriptor, address, offset, length); - if (bytesRead < -1) { - throw new IOException(); - } - return bytesRead; - } - - private native long writeDirectImpl(int fileDescriptor, int address, - int offset, int length); + public native long readDirect(int fileDescriptor, int address, int offset, int length); - public long writeDirect(int fileDescriptor, int address, int offset, - int length) throws IOException { - long bytesWritten = writeDirectImpl(fileDescriptor, address, offset, - length); - if (bytesWritten < 0) { - throw new IOException(); - } - return bytesWritten; - } + public native long writeDirect(int fileDescriptor, int address, int offset, int length) + throws IOException; /* * Indirect read/writes work on byte[]'s */ private native long readImpl(int fileDescriptor, byte[] bytes, int offset, - int length); + int length) throws IOException; public long read(int fileDescriptor, byte[] bytes, int offset, int length) throws IOException { if (bytes == null) { throw new NullPointerException(); } - long bytesRead = readImpl(fileDescriptor, bytes, offset, length); - if (bytesRead < -1) { - /* - * TODO: bytesRead is never less than -1 so this code - * does nothing? - * The native code throws an exception in only one case - * so perhaps this should be 'bytesRead < 0' to handle - * any other cases. But the other cases have been - * ignored until now so fixing this could break things - */ - throw new IOException(); - } - return bytesRead; + return readImpl(fileDescriptor, bytes, offset, length); } private native long writeImpl(int fileDescriptor, byte[] bytes, - int offset, int length); + int offset, int length) throws IOException; public long write(int fileDescriptor, byte[] bytes, int offset, int length) throws IOException { - long bytesWritten = writeImpl(fileDescriptor, bytes, offset, length); - if (bytesWritten < 0) { - throw new IOException(); + if (bytes == null) { + throw new NullPointerException(); } - return bytesWritten; + return writeImpl(fileDescriptor, bytes, offset, length); } + // END android-changed /* * Scatter/gather calls. */ - public long readv(int fileDescriptor, int[] addresses, int[] offsets, - int[] lengths, int size) throws IOException { - long bytesRead = readvImpl(fileDescriptor, addresses, offsets, lengths, - size); - if (bytesRead < -1) { - throw new IOException(); - } - return bytesRead; - } + public native long readv(int fileDescriptor, int[] addresses, + int[] offsets, int[] lengths, int size) throws IOException; - private native long readvImpl(int fileDescriptor, int[] addresses, - int[] offsets, int[] lengths, int size); + public native long writev(int fileDescriptor, int[] addresses, int[] offsets, + int[] lengths, int size) throws IOException; - public long writev(int fileDescriptor, int[] addresses, int[] offsets, - int[] lengths, int size) throws IOException { - long bytesWritten = writevImpl(fileDescriptor, addresses, offsets, - lengths, size); - if (bytesWritten < 0) { - throw new IOException(); - } - return bytesWritten; - } - - private native long writevImpl(int fileDescriptor, int[] addresses, - int[] offsets, int[] lengths, int size); - - private native int closeImpl(int fileDescriptor); - - /* - * (non-Javadoc) - * - * @see org.apache.harmony.luni.platform.IFileSystem#close(long) - */ - public void close(int fileDescriptor) throws IOException { - int rc = closeImpl(fileDescriptor); - if (rc == -1) { - throw new IOException(); - } - } - - public void truncate(int fileDescriptor, long size) throws IOException { - int rc = truncateImpl(fileDescriptor, size); - if (rc < 0) { - throw new IOException(); - } - } + // BEGIN android-changed + public native void close(int fileDescriptor) throws IOException; - private native int truncateImpl(int fileDescriptor, long size); + public native void truncate(int fileDescriptor, long size) throws IOException; + // END android-changed public int open(byte[] fileName, int mode) throws FileNotFoundException { if (fileName == null) { @@ -254,16 +165,10 @@ class OSFileSystem implements IFileSystem { private native int openImpl(byte[] fileName, int mode); - public long transfer(int fileHandler, FileDescriptor socketDescriptor, - long offset, long count) throws IOException { - long result = transferImpl(fileHandler, socketDescriptor, offset, count); - if (result < 0) - throw new IOException(); - return result; - } - - private native long transferImpl(int fileHandler, - FileDescriptor socketDescriptor, long offset, long count); + // BEGIN android-changed + public native long transfer(int fd, FileDescriptor sd, long offset, long count) + throws IOException; + // END android-changed // BEGIN android-deleted // public long ttyAvailable() throws IOException { @@ -277,17 +182,15 @@ class OSFileSystem implements IFileSystem { // private native long ttyAvailableImpl(); // END android-deleted - public long ttyRead(byte[] bytes, int offset, int length) throws IOException { - long nChar = ttyReadImpl(bytes, offset, length); - // BEGIN android-changed - if (nChar < -1) { - throw new IOException(); - } - // END android-changed - return nChar; - } - - private native long ttyReadImpl(byte[] bytes, int offset, int length); + // BEGIN android-deleted + // public long ttyRead(byte[] bytes, int offset, int length) throws IOException { + // if (bytes == null) { + // throw new NullPointerException(); + // } + // return ttyReadImpl(bytes, offset, length); + // } + // private native long ttyReadImpl(byte[] bytes, int offset, int length) throws IOException; + // END android-deleted // BEGIN android-added public native int ioctlAvailable(int fileDescriptor) throws IOException; diff --git a/luni/src/main/native/java_net_InetAddress.cpp b/luni/src/main/native/java_net_InetAddress.cpp index d7b4931..4e58a1f 100644 --- a/luni/src/main/native/java_net_InetAddress.cpp +++ b/luni/src/main/native/java_net_InetAddress.cpp @@ -24,7 +24,6 @@ #include <stdio.h> #include <string.h> -#include <assert.h> #include <netdb.h> #include <errno.h> @@ -48,19 +47,6 @@ static jstring InetAddress_gethostname(JNIEnv* env, jobject obj) } } -static void throwNullPointerException(JNIEnv* env) -{ - const char* className = "java/lang/NullPointerException"; - - jclass exClass = env->FindClass(className); - - if (exClass == NULL) { - LOGE("Unable to find class %s", className); - } else { - env->ThrowNew(exClass, NULL); - } -} - #if LOG_DNS static void logIpString(struct addrinfo* ai, const char* name) { @@ -208,7 +194,7 @@ jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj, jboolean preferIPv4Stack) { if (javaName == NULL) { - throwNullPointerException(env); + jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } @@ -247,57 +233,44 @@ static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, jbyteArray javaAddress) { if (javaAddress == NULL) { - throwNullPointerException(env); - return NULL; - } - - size_t addrlen = env->GetArrayLength(javaAddress); - jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL); - if (rawAddress == NULL) { - throwNullPointerException(env); + jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } // Convert the raw address bytes into a socket address structure. - int ret = 0; struct sockaddr_storage ss; - struct sockaddr_in *sin = (struct sockaddr_in *) &ss; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; - size_t socklen; memset(&ss, 0, sizeof(ss)); - switch (addrlen) { - case 4: - socklen = sizeof(struct sockaddr_in); - sin->sin_family = AF_INET; - memcpy(&sin->sin_addr.s_addr, rawAddress, addrlen); - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - break; - case 16: - socklen = sizeof(struct sockaddr_in6); - sin6->sin6_family = AF_INET6; - memcpy(&sin6->sin6_addr.s6_addr, rawAddress, addrlen); - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - break; - default: - // The caller already throws an exception in this case. Don't worry - // about it here. - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - return NULL; + + size_t socklen; + const size_t addressLength = env->GetArrayLength(javaAddress); + if (addressLength == 4) { + struct sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); + sin->sin_family = AF_INET; + socklen = sizeof(struct sockaddr_in); + jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); + env->GetByteArrayRegion(javaAddress, 0, 4, dst); + } else if (addressLength == 16) { + struct sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(&ss); + sin6->sin6_family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); + jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); + env->GetByteArrayRegion(javaAddress, 0, 16, dst); + } else { + // The caller already throws an exception in this case. Don't worry + // about it here. + return NULL; } // Look up the host name from the IP address. char name[NI_MAXHOST]; - if (ret == 0) { - ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name), - NULL, 0, NI_NAMEREQD); - } - - if (ret == 0) { - return env->NewStringUTF(name); + int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen, + name, sizeof(name), NULL, 0, NI_NAMEREQD); + if (ret != 0) { + jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); + return NULL; } - jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); - return NULL; + return env->NewStringUTF(name); } /* diff --git a/luni/src/main/native/java_net_NetworkInterface.c b/luni/src/main/native/java_net_NetworkInterface.c index db6d503..e994aea 100644 --- a/luni/src/main/native/java_net_NetworkInterface.c +++ b/luni/src/main/native/java_net_NetworkInterface.c @@ -34,27 +34,7 @@ * Throws an IOException with the given message. */ static void throwSocketException(JNIEnv *env, const char *message) { - jclass exClass = (*env)->FindClass(env, "java/net/SocketException"); - - if(exClass == NULL) { - LOGE("Unable to find class java/net/SocketException"); - } else { - (*env)->ThrowNew(env, exClass, message); - } -} - - -/** - * Throws a NullPointerException. - */ -static void throwNullPointerException(JNIEnv *env) { - jclass exClass = (*env)->FindClass(env, "java/lang/NullPointerException"); - - if(exClass == NULL) { - LOGE("Unable to find class java/lang/NullPointerException"); - } else { - (*env)->ThrowNew(env, exClass, NULL); - } + jniThrowException(env, "java/net/SocketException", message); } /** @@ -241,24 +221,18 @@ static char * netLookupErrorString(int anErrorNum) { static int structInToJavaAddress( JNIEnv *env, struct in_addr *address, jbyteArray java_address) { - if(java_address == NULL) { - throwNullPointerException(env); + if (java_address == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); return -1; } - if((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) { + if ((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) { jniThrowIOException(env, errno); return -1; } - jbyte *java_address_bytes; - - java_address_bytes = (*env)->GetByteArrayElements(env, java_address, NULL); - - memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); - - (*env)->ReleaseByteArrayElements(env, java_address, java_address_bytes, 0); - + jbyte* src = (jbyte*)(&(address->s_addr)); + (*env)->SetByteArrayRegion(env, java_address, 0, sizeof(address->s_addr), src); return 0; } diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp index 38f3d36..3e229d0 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp @@ -14,401 +14,316 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +// BEGIN android-note +// This file corresponds to harmony's OSFileSystem.c and OSFileSystemLinux32.c. +// It has been greatly simplified by the assumption that the underlying +// platform is always Linux. +// END android-note + /* * Common natives supporting the file system interface. */ #define HyMaxPath 1024 -#define HyOpenRead 1 /* Values for HyFileOpen */ + +/* Values for HyFileOpen */ +#define HyOpenRead 1 #define HyOpenWrite 2 #define HyOpenCreate 4 #define HyOpenTruncate 8 #define HyOpenAppend 16 #define HyOpenText 32 - /* Use this flag with HyOpenCreate, if this flag is specified then - * trying to create an existing file will fail + * trying to create an existing file will fail */ #define HyOpenCreateNew 64 -#define HyOpenSync 128 +#define HyOpenSync 128 #define SHARED_LOCK_TYPE 1L #include "JNIHelp.h" #include "AndroidSystemNatives.h" -#include <string.h> -#include <stdio.h> +#include <assert.h> #include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> #include <sys/sendfile.h> #include <sys/uio.h> -#include <fcntl.h> -#include <sys/ioctl.h> -typedef struct socket_struct { - int sock; - unsigned short family; -} socket_struct; +// An equivalent of the glibc macro of the same name. +// We want to hide EINTR from Java by simply retrying directly in +// the native code. We care about all other errors, though. +#define EINTR_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) static void convertToPlatform(char *path) { char *pathIndex; pathIndex = path; while (*pathIndex != '\0') { - if(*pathIndex == '\\') { + if (*pathIndex == '\\') { *pathIndex = '/'; } pathIndex++; } } -static int -EsTranslateOpenFlags(int flags) { +static int EsTranslateOpenFlags(int flags) { int realFlags = 0; - if(flags & HyOpenAppend) { + if (flags & HyOpenAppend) { realFlags |= O_APPEND; } - if(flags & HyOpenTruncate) { + if (flags & HyOpenTruncate) { realFlags |= O_TRUNC; } - if(flags & HyOpenCreate) { + if (flags & HyOpenCreate) { realFlags |= O_CREAT; } - if(flags & HyOpenCreateNew) { + if (flags & HyOpenCreateNew) { realFlags |= O_EXCL | O_CREAT; } #ifdef O_SYNC - if(flags & HyOpenSync) { - realFlags |= O_SYNC; - } -#endif - if(flags & HyOpenRead) { - if(flags & HyOpenWrite) { + if (flags & HyOpenSync) { + realFlags |= O_SYNC; + } +#endif + if (flags & HyOpenRead) { + if (flags & HyOpenWrite) { return (O_RDWR | realFlags); } return (O_RDONLY | realFlags); } - if(flags & HyOpenWrite) { + if (flags & HyOpenWrite) { return (O_WRONLY | realFlags); } return -1; } -/** - * Lock the file identified by the given handle. - * The range and lock type are given. - */ -static jint harmony_io_lockImpl(JNIEnv * env, jobject thiz, jint handle, - jlong start, jlong length, jint typeFlag, jboolean waitFlag) { +// Checks whether we can safely treat the given jlong as an off_t without +// accidental loss of precision. +// TODO: this is bogus; we should use _FILE_OFFSET_BITS=64. +static bool offsetTooLarge(JNIEnv* env, jlong longOffset) { + if (sizeof(off_t) >= sizeof(jlong)) { + // We're only concerned about the possibility that off_t is + // smaller than jlong. off_t is signed, so we don't need to + // worry about signed/unsigned. + return false; + } - int rc; - int waitMode = (waitFlag) ? F_SETLKW : F_SETLK; - struct flock lock; + // TODO: use std::numeric_limits<off_t>::max() and min() when we have them. + assert(sizeof(off_t) == sizeof(int)); + static const off_t off_t_max = INT_MAX; + static const off_t off_t_min = INT_MIN; - memset(&lock, 0, sizeof(lock)); - - // If start or length overflow the max values we can represent, then max them out. - if(start > 0x7fffffffL) { - start = 0x7fffffffL; - } - if(length > 0x7fffffffL) { - length = 0x7fffffffL; + if (longOffset > off_t_max || longOffset < off_t_min) { + // "Value too large for defined data type". + jniThrowIOException(env, EOVERFLOW); + return true; } + return false; +} + +static jlong translateLockLength(jlong length) { + // FileChannel.tryLock uses Long.MAX_VALUE to mean "lock the whole + // file", where POSIX would use 0. We can support that special case, + // even for files whose actual length we can't represent. For other + // out of range lengths, though, we want our range checking to fire. + return (length == 0x7fffffffffffffffLL) ? 0 : length; +} + +static struct flock flockFromStartAndLength(jlong start, jlong length) { + struct flock lock; + memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_start = start; lock.l_len = length; - if((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) { + return lock; +} + +static jint harmony_io_lockImpl(JNIEnv* env, jobject, jint handle, + jlong start, jlong length, jint typeFlag, jboolean waitFlag) { + + length = translateLockLength(length); + if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) { + return -1; + } + + struct flock lock(flockFromStartAndLength(start, length)); + + if ((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) { lock.l_type = F_RDLCK; } else { lock.l_type = F_WRLCK; } - do { - rc = fcntl(handle, waitMode, &lock); - } while ((rc < 0) && (errno == EINTR)); - - return (rc == -1) ? -1 : 0; + int waitMode = (waitFlag) ? F_SETLKW : F_SETLK; + return EINTR_RETRY(fcntl(handle, waitMode, &lock)); } -/** - * Unlocks the specified region of the file. - */ -static jint harmony_io_unlockImpl(JNIEnv * env, jobject thiz, jint handle, +static void harmony_io_unlockImpl(JNIEnv* env, jobject, jint handle, jlong start, jlong length) { - int rc; - struct flock lock; - - memset(&lock, 0, sizeof(lock)); - - // If start or length overflow the max values we can represent, then max them out. - if(start > 0x7fffffffL) { - start = 0x7fffffffL; - } - if(length > 0x7fffffffL) { - length = 0x7fffffffL; + length = translateLockLength(length); + if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) { + return; } - lock.l_whence = SEEK_SET; - lock.l_start = start; - lock.l_len = length; + struct flock lock(flockFromStartAndLength(start, length)); lock.l_type = F_UNLCK; - do { - rc = fcntl(handle, F_SETLKW, &lock); - } while ((rc < 0) && (errno == EINTR)); - - return (rc == -1) ? -1 : 0; + int rc = EINTR_RETRY(fcntl(handle, F_SETLKW, &lock)); + if (rc == -1) { + jniThrowIOException(env, errno); + } } /** * Returns the granularity of the starting address for virtual memory allocation. * (It's the same as the page size.) - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: getAllocGranularity - * Signature: ()I */ -static jint harmony_io_getAllocGranularity(JNIEnv * env, jobject thiz) { - static int allocGranularity = 0; - if(allocGranularity == 0) { - allocGranularity = getpagesize(); - } +static jint harmony_io_getAllocGranularity(JNIEnv* env, jobject) { + static int allocGranularity = getpagesize(); return allocGranularity; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: readvImpl - * Signature: (I[J[I[I)J - */ -static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd, - jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { - - jboolean bufsCopied = JNI_FALSE; - jboolean offsetsCopied = JNI_FALSE; - jboolean lengthsCopied = JNI_FALSE; - jint *bufs; - jint *offsets; - jint *lengths; - int i = 0; - long totalRead = 0; - struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); - if(vectors == NULL) { +static jlong harmony_io_readv(JNIEnv* env, jobject, jint fd, + jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) { + iovec* vectors = new iovec[size]; + if (vectors == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", "native heap"); return -1; } - bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); - offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); - lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); - while(i < size) { - vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + jint *buffers = env->GetIntArrayElements(jBuffers, NULL); + jint *offsets = env->GetIntArrayElements(jOffsets, NULL); + jint *lengths = env->GetIntArrayElements(jLengths, NULL); + for (int i = 0; i < size; ++i) { + vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i])); vectors[i].iov_len = lengths[i]; - i++; - } - totalRead = readv(fd, vectors, size); - if(bufsCopied) { - env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); } - if(offsetsCopied) { - env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); - } - if(lengthsCopied) { - env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + long result = readv(fd, vectors, size); + env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT); + env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT); + env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT); + delete[] vectors; + if (result == -1) { + jniThrowIOException(env, errno); } - free(vectors); - return totalRead; + return result; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: writevImpl - * Signature: (I[J[I[I)J - */ -static jlong harmony_io_writevImpl(JNIEnv *env, jobject thiz, jint fd, - jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { - - jboolean bufsCopied = JNI_FALSE; - jboolean offsetsCopied = JNI_FALSE; - jboolean lengthsCopied = JNI_FALSE; - jint *bufs; - jint *offsets; - jint *lengths; - int i = 0; - long totalRead = 0; - struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); - if(vectors == NULL) { +static jlong harmony_io_writev(JNIEnv* env, jobject, jint fd, + jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) { + iovec* vectors = new iovec[size]; + if (vectors == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", "native heap"); return -1; } - bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); - offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); - lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); - while(i < size) { - vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + jint *buffers = env->GetIntArrayElements(jBuffers, NULL); + jint *offsets = env->GetIntArrayElements(jOffsets, NULL); + jint *lengths = env->GetIntArrayElements(jLengths, NULL); + for (int i = 0; i < size; ++i) { + vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i])); vectors[i].iov_len = lengths[i]; - i++; } - totalRead = writev(fd, vectors, size); - if(bufsCopied) { - env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); - } - if(offsetsCopied) { - env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); - } - if(lengthsCopied) { - env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + long result = writev(fd, vectors, size); + env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT); + env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT); + env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT); + delete[] vectors; + if (result == -1) { + jniThrowIOException(env, errno); } - free(vectors); - return totalRead; + return result; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: transferImpl - * Signature: (IJJ)J - */ -static jlong harmony_io_transferImpl(JNIEnv *env, jobject thiz, jint fd, - jobject sd, jlong offset, jlong count) { - - int socket; - off_t off; +static jlong harmony_io_transfer(JNIEnv* env, jobject, jint fd, jobject sd, + jlong offset, jlong count) { - socket = jniGetFDFromFileDescriptor(env, sd); - if(socket == 0 || socket == -1) { + int socket = jniGetFDFromFileDescriptor(env, sd); + if (socket == -1) { return -1; } /* Value of offset is checked in jint scope (checked in java layer) The conversion here is to guarantee no value lost when converting offset to off_t */ - off = offset; + off_t off = offset; - return sendfile(socket,(int)fd,(off_t *)&off,(size_t)count); + ssize_t rc = sendfile(socket, fd, &off, count); + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: readDirectImpl - * Signature: (IJI)J - */ -static jlong harmony_io_readDirectImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_readDirect(JNIEnv* env, jobject, jint fd, jint buf, jint offset, jint nbytes) { - jint result; - if(nbytes == 0) { - return (jlong) 0; + if (nbytes == 0) { + return 0; } - result = read(fd, (void *) ((jint *)(buf+offset)), (int) nbytes); - if(result == 0) { - return (jlong) -1; - } else { - return (jlong) result; + jbyte* dst = reinterpret_cast<jbyte*>(buf + offset); + jlong rc = EINTR_RETRY(read(fd, dst, nbytes)); + if (rc == 0) { + return -1; } + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: writeDirectImpl - * Signature: (IJI)J - */ -static jlong harmony_io_writeDirectImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_writeDirect(JNIEnv* env, jobject, jint fd, jint buf, jint offset, jint nbytes) { - - - int rc = 0; - - /* write will just do the right thing for HYPORT_TTY_OUT and HYPORT_TTY_ERR */ - rc = write (fd, (const void *) ((jint *)(buf+offset)), (int) nbytes); - - if(rc == -1) { - jniThrowException(env, "java/io/IOException", strerror(errno)); - return -2; + jbyte* src = reinterpret_cast<jbyte*>(buf + offset); + jlong rc = EINTR_RETRY(write(fd, src, nbytes)); + if (rc == -1) { + jniThrowIOException(env, errno); } - return (jlong) rc; - + return rc; } -// BEGIN android-changed -/* - * Class: org_apache_harmony_io - * Method: readImpl - * Signature: (I[BII)J - */ -static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_readImpl(JNIEnv* env, jobject, jint fd, jbyteArray byteArray, jint offset, jint nbytes) { - jboolean isCopy; - jbyte *bytes; - jlong result; - if (nbytes == 0) { return 0; } - bytes = env->GetByteArrayElements(byteArray, &isCopy); - - for (;;) { - result = read(fd, (void *) (bytes + offset), (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - + jbyte* bytes = env->GetByteArrayElements(byteArray, NULL); + jlong rc = EINTR_RETRY(read(fd, bytes + offset, nbytes)); env->ReleaseByteArrayElements(byteArray, bytes, 0); - if (result == 0) { + if (rc == 0) { return -1; } - - if (result == -1) { + if (rc == -1) { if (errno == EAGAIN) { jniThrowException(env, "java/io/InterruptedIOException", "Read timed out"); } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); + jniThrowIOException(env, errno); } } - - return result; + return rc; } -/* - * Class: org_apache_harmony_io - * Method: writeImpl - * Signature: (I[BII)J - */ -static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_writeImpl(JNIEnv* env, jobject, jint fd, jbyteArray byteArray, jint offset, jint nbytes) { - jboolean isCopy; - jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); - jlong result; - - for (;;) { - result = write(fd, (const char *) bytes + offset, (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - + jbyte* bytes = env->GetByteArrayElements(byteArray, NULL); + jlong result = EINTR_RETRY(write(fd, bytes + offset, nbytes)); env->ReleaseByteArrayElements(byteArray, bytes, JNI_ABORT); if (result == -1) { @@ -416,157 +331,77 @@ static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, jniThrowException(env, "java/io/InterruptedIOException", "Write timed out"); } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); + jniThrowIOException(env, errno); } } - return result; } -// END android-changed - -/** - * Seeks a file descriptor to a given file position. - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be seeked - * @param offset distance of movement in bytes relative to whence arg - * @param whence enum value indicating from where the offset is relative - * The valid values are defined in fsconstants.h. - * @return the new file position from the beginning of the file, in bytes; - * or -1 if a problem occurs. - */ -static jlong harmony_io_seekImpl(JNIEnv * env, jobject thiz, jint fd, - jlong offset, jint whence) { - - int mywhence = 0; +static jlong harmony_io_seek(JNIEnv* env, jobject, jint fd, jlong offset, + jint javaWhence) { /* Convert whence argument */ - switch (whence) { - case 1: - mywhence = 0; - break; - case 2: - mywhence = 1; - break; - case 4: - mywhence = 2; - break; - default: - return -1; + int nativeWhence = 0; + switch (javaWhence) { + case 1: + nativeWhence = SEEK_SET; + break; + case 2: + nativeWhence = SEEK_CUR; + break; + case 4: + nativeWhence = SEEK_END; + break; + default: + return -1; } - - off_t localOffset = (int) offset; - - if((mywhence < 0) || (mywhence > 2)) { + // If the offset is relative, lseek(2) will tell us whether it's too large. + // We're just worried about too large an absolute offset, which would cause + // us to lie to lseek(2). + if (offsetTooLarge(env, offset)) { return -1; } - /* If file offsets are 32 bit, truncate the seek to that range */ - if(sizeof (off_t) < sizeof (jlong)) { - if(offset > 0x7FFFFFFF) { - localOffset = 0x7FFFFFFF; - } else if(offset < -0x7FFFFFFF) { - localOffset = -0x7FFFFFFF; - } + jlong result = lseek(fd, offset, nativeWhence); + if (result == -1) { + jniThrowIOException(env, errno); } - - return (jlong) lseek(fd, localOffset, mywhence); + return result; } -/** - * Flushes a file state to disk. - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be flushed - * @param metadata if true also flush metadata, - * otherwise just flush data is possible. - * @return zero on success and -1 on failure - * - * Method: fflushImpl - * Signature: (IZ)I - */ -static jint harmony_io_fflushImpl(JNIEnv * env, jobject thiz, jint fd, +// TODO: are we supposed to support the 'metadata' flag? (false => fdatasync.) +static void harmony_io_fflush(JNIEnv* env, jobject, jint fd, jboolean metadata) { - return (jint) fsync(fd); + int rc = fsync(fd); + if (rc == -1) { + jniThrowIOException(env, errno); + } } -// BEGIN android-changed -/** - * Closes the given file handle - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be closed - * @return zero on success and -1 on failure - * - * Class: org_apache_harmony_io - * Method: closeImpl - * Signature: (I)I - */ -static jint harmony_io_closeImpl(JNIEnv * env, jobject thiz, jint fd) { - jint result; - - for (;;) { - result = (jint) close(fd); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the close() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. - */ +static jint harmony_io_close(JNIEnv* env, jobject, jint fd) { + jint rc = EINTR_RETRY(close(fd)); + if (rc == -1) { + jniThrowIOException(env, errno); } - - return result; + return rc; } -// END android-changed - -/* - * Class: org_apache_harmony_io - * Method: truncateImpl - * Signature: (IJ)I - */ -static jint harmony_io_truncateImpl(JNIEnv * env, jobject thiz, jint fd, - jlong size) { - - int rc; - off_t length = (off_t) size; - - // If file offsets are 32 bit, truncate the newLength to that range - if(sizeof (off_t) < sizeof (jlong)) { - if(length > 0x7FFFFFFF) { - length = 0x7FFFFFFF; - } else if(length < -0x7FFFFFFF) { - length = -0x7FFFFFFF; - } +static jint harmony_io_truncate(JNIEnv* env, jobject, jint fd, jlong length) { + if (offsetTooLarge(env, length)) { + return -1; } - rc = ftruncate((int)fd, length); - - return (jint) rc; - + int rc = ftruncate(fd, length); + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: openImpl - * Signature: ([BI)I - */ -static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, +static jint harmony_io_openImpl(JNIEnv* env, jobject, jbyteArray path, jint jflags) { - int flags = 0; - int mode = 0; - jint * portFD; - jsize length; - char pathCopy[HyMaxPath]; + int mode = 0; // BEGIN android-changed // don't want default permissions to allow global access. @@ -588,7 +423,7 @@ static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, mode = 0600; break; case 256: - flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; + flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; mode = 0600; break; } @@ -596,116 +431,56 @@ static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, flags = EsTranslateOpenFlags(flags); - length = env->GetArrayLength (path); + // TODO: clean this up when we clean up the java.io.File equivalent. + jsize length = env->GetArrayLength (path); length = length < HyMaxPath - 1 ? length : HyMaxPath - 1; + char pathCopy[HyMaxPath]; env->GetByteArrayRegion (path, 0, length, (jbyte *)pathCopy); pathCopy[length] = '\0'; convertToPlatform (pathCopy); - int cc; - - if(pathCopy == NULL) { - jniThrowException(env, "java/lang/NullPointerException", NULL); - return -1; - } - - do { - cc = open(pathCopy, flags, mode); - } while(cc < 0 && errno == EINTR); - - if(cc < 0 && errno > 0) { + jint cc = EINTR_RETRY(open(pathCopy, flags, mode)); + // TODO: chase up the callers of this and check they wouldn't rather + // have us throw a meaningful IOException right here. + if (cc < 0 && errno > 0) { cc = -errno; } - return cc; - - } -// BEGIN android-deleted -#if 0 -/* - * Answers the number of remaining chars in the stdin. - * - * Class: org_apache_harmony_io - * Method: ttyAvailableImpl - * Signature: ()J - */ -static jlong harmony_io_ttyAvailableImpl(JNIEnv *env, jobject thiz) { - - int rc; - off_t curr, end; - - int avail = 0; - - // when redirected from a file - curr = lseek(STDIN_FILENO, 0L, 2); /* don't use tell(), it doesn't exist on all platforms, i.e. linux */ - if(curr != -1) { - end = lseek(STDIN_FILENO, 0L, 4); - lseek(STDIN_FILENO, curr, 1); - if(end >= curr) { - return (jlong) (end - curr); - } - } - - /* ioctl doesn't work for files on all platforms (i.e. SOLARIS) */ - - rc = ioctl (STDIN_FILENO, FIONREAD, &avail); - - /* 64 bit platforms use a 32 bit value, using IDATA fails on big endian */ - /* Pass in IDATA because ioctl() is device dependent, some devices may write 64 bits */ - if(rc != -1) { - return (jlong) *(jint *) & avail; - } - return (jlong) 0; -} -#endif -// END android-deleted - -// BEGIN android-added -/* - * Answers the number of remaining bytes in a file descriptor - * using IOCTL. - * - * Class: org_apache_harmony_io - * Method: ioctlAvailable - * Signature: ()I - */ -static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) { - int avail = 0; - int rc = ioctl(fd, FIONREAD, &avail); - +static jint harmony_io_ioctlAvailable(JNIEnv*env, jobject, jint fd) { /* * On underlying platforms Android cares about (read "Linux"), * ioctl(fd, FIONREAD, &avail) is supposed to do the following: - * + * * If the fd refers to a regular file, avail is set to * the difference between the file size and the current cursor. * This may be negative if the cursor is past the end of the file. - * + * * If the fd refers to an open socket or the read end of a * pipe, then avail will be set to a number of bytes that are * available to be read without blocking. - * + * * If the fd refers to a special file/device that has some concept * of buffering, then avail will be set in a corresponding way. - * + * * If the fd refers to a special device that does not have any * concept of buffering, then the ioctl call will return a negative * number, and errno will be set to ENOTTY. - * + * * If the fd refers to a special file masquerading as a regular file, * then avail may be returned as negative, in that the special file * may appear to have zero size and yet a previous read call may have * actually read some amount of data and caused the cursor to be * advanced. */ - + int avail = 0; + int rc = ioctl(fd, FIONREAD, &avail); if (rc >= 0) { /* * Success, but make sure not to return a negative number (see * above). - */ + */ if (avail < 0) { avail = 0; } @@ -714,61 +489,10 @@ static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) { avail = 0; } else { /* Something strange is happening. */ - jniThrowException(env, "java/io/IOException", strerror(errno)); - avail = 0; - } - - return (jint) avail; -} -// END android-added - -/* - * Reads the number of bytes from stdin. - * - * Class: org_apache_harmony_io - * Method: ttyReadImpl - * Signature: ([BII)J - */ -static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, - jbyteArray byteArray, jint offset, jint nbytes) { - - jboolean isCopy; - jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); - jlong result; - - for(;;) { - - result = (jlong) read(STDIN_FILENO, (char *)(bytes + offset), (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - - env->ReleaseByteArrayElements(byteArray, bytes, 0); - - if (result == 0) { - return -1; - } - - if (result == -1) { - if (errno == EAGAIN) { - jniThrowException(env, "java/io/InterruptedIOException", - "Read timed out"); - } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); - } + jniThrowIOException(env, errno); } - return result; + return (jint) avail; } /* @@ -776,32 +500,26 @@ static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ + { "close", "(I)V", (void*) harmony_io_close }, + { "fflush", "(IZ)V", (void*) harmony_io_fflush }, + { "getAllocGranularity","()I", (void*) harmony_io_getAllocGranularity }, + { "ioctlAvailable", "(I)I", (void*) harmony_io_ioctlAvailable }, { "lockImpl", "(IJJIZ)I", (void*) harmony_io_lockImpl }, - { "getAllocGranularity","()I", (void*) harmony_io_getAllocGranularity }, - { "unlockImpl", "(IJJ)I", (void*) harmony_io_unlockImpl }, - { "fflushImpl", "(IZ)I", (void*) harmony_io_fflushImpl }, - { "seekImpl", "(IJI)J", (void*) harmony_io_seekImpl }, - { "readDirectImpl", "(IIII)J", (void*) harmony_io_readDirectImpl }, - { "writeDirectImpl", "(IIII)J", (void*) harmony_io_writeDirectImpl }, + { "openImpl", "([BI)I", (void*) harmony_io_openImpl }, + { "readDirect", "(IIII)J", (void*) harmony_io_readDirect }, { "readImpl", "(I[BII)J", (void*) harmony_io_readImpl }, + { "readv", "(I[I[I[II)J",(void*) harmony_io_readv }, + { "seek", "(IJI)J", (void*) harmony_io_seek }, + { "transfer", "(ILjava/io/FileDescriptor;JJ)J", + (void*) harmony_io_transfer }, + { "truncate", "(IJ)V", (void*) harmony_io_truncate }, + { "unlockImpl", "(IJJ)V", (void*) harmony_io_unlockImpl }, + { "writeDirect", "(IIII)J", (void*) harmony_io_writeDirect }, { "writeImpl", "(I[BII)J", (void*) harmony_io_writeImpl }, - { "readvImpl", "(I[I[I[II)J",(void*) harmony_io_readvImpl }, - { "writevImpl", "(I[I[I[II)J",(void*) harmony_io_writevImpl }, - { "closeImpl", "(I)I", (void*) harmony_io_closeImpl }, - { "truncateImpl", "(IJ)I", (void*) harmony_io_truncateImpl }, - { "openImpl", "([BI)I", (void*) harmony_io_openImpl }, - { "transferImpl", "(ILjava/io/FileDescriptor;JJ)J", - (void*) harmony_io_transferImpl }, - // BEGIN android-deleted - //{ "ttyAvailableImpl", "()J", (void*) harmony_io_ttyAvailableImpl }, - // END android-deleted - // BEGIN android-added - { "ioctlAvailable", "(I)I", (void*) harmony_io_ioctlAvailable }, - // END android added - { "ttyReadImpl", "([BII)J", (void*) harmony_io_ttyReadImpl } + { "writev", "(I[I[I[II)J",(void*) harmony_io_writev }, }; -int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv *_env) { - return jniRegisterNativeMethods(_env, - "org/apache/harmony/luni/platform/OSFileSystem", gMethods, - NELEM(gMethods)); +int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv* _env) { + return jniRegisterNativeMethods(_env, + "org/apache/harmony/luni/platform/OSFileSystem", gMethods, + NELEM(gMethods)); } diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp index 2e814cc..b1493f8 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp @@ -144,9 +144,8 @@ static jbyte harmony_nio_getByteImpl(JNIEnv *_env, jobject _this, */ static void harmony_nio_getBytesImpl(JNIEnv *_env, jobject _this, jint pointer, jbyteArray dst, jint offset, jint length) { - jbyte *dst_ = (jbyte *)_env->GetPrimitiveArrayCritical(dst, (jboolean *)0); - memcpy(dst_ + offset, (jbyte *)pointer, length); - _env->ReleasePrimitiveArrayCritical(dst, dst_, 0); + jbyte* src = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer)); + _env->SetByteArrayRegion(dst, offset, length, src); } /* @@ -166,9 +165,8 @@ static void harmony_nio_putByteImpl(JNIEnv *_env, jobject _this, jint pointer, */ static void harmony_nio_putBytesImpl(JNIEnv *_env, jobject _this, jint pointer, jbyteArray src, jint offset, jint length) { - jbyte *src_ = (jbyte *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); - memcpy((jbyte *)pointer, src_ + offset, length); - _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); + jbyte* dst = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer)); + _env->GetByteArrayRegion(src, offset, length, dst); } static void diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index 7b6023a..fd3820b 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -178,8 +178,6 @@ struct CachedFields { jclass byte_class; jmethodID byte_class_init; jfieldID byte_class_value; - jclass string_class; - jmethodID string_class_init; jclass socketimpl_class; jfieldID socketimpl_address; jfieldID socketimpl_port; @@ -211,13 +209,6 @@ static void throwSocketException(JNIEnv *env, int errorCode) { } /** - * Throws an IOException with the given message. - */ -static void throwIOExceptionStr(JNIEnv *env, const char *message) { - jniThrowException(env, "java/io/IOException", message); -} - -/** * Throws a NullPointerException. */ static void throwNullPointerException(JNIEnv *env) { @@ -311,8 +302,7 @@ static bool isJavaMappedAddress(jbyte *addressBytes) { /** * Converts a native address structure to an InetAddress object. * Throws a NullPointerException or an IOException in case of - * error. This is signaled by a return value of -1. The normal - * return value is 0. + * error. * * @param sockaddress the sockaddr_storage structure to convert * @@ -377,30 +367,30 @@ static void convertIpv4ToMapped(struct sockaddr_in *sin, * @exception SocketError if the address family is unknown */ static int byteArrayToSocketAddress(JNIEnv *env, - jbyteArray addressByteArray, int port, sockaddr_storage *sockaddress) { - if (addressByteArray == NULL) { - throwNullPointerException(env); - return EFAULT; + jbyteArray addressBytes, int port, sockaddr_storage *sockaddress) { + if (addressBytes == NULL) { + throwNullPointerException(env); + return EFAULT; } - size_t addressLength = env->GetArrayLength(addressByteArray); + size_t addressLength = env->GetArrayLength(addressBytes); // Convert the IP address bytes to the proper IP address type. if (addressLength == 4) { // IPv4 address. - sockaddr_in *sin = (sockaddr_in *) sockaddress; + sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sockaddress); memset(sin, 0, sizeof(sockaddr_in)); sin->sin_family = AF_INET; sin->sin_port = htons(port); - jbyte *rawBytes = (jbyte *) &sin->sin_addr.s_addr; - env->GetByteArrayRegion(addressByteArray, 0, 4, rawBytes); + jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); + env->GetByteArrayRegion(addressBytes, 0, 4, dst); } else if (addressLength == 16) { // IPv6 address. - sockaddr_in6 *sin6 = (sockaddr_in6 *) sockaddress; + sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(sockaddress); memset(sin6, 0, sizeof(sockaddr_in6)); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); - jbyte *rawBytes = (jbyte *) &sin6->sin6_addr.s6_addr; - env->GetByteArrayRegion(addressByteArray, 0, 16, rawBytes); + jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); + env->GetByteArrayRegion(addressBytes, 0, 16, dst); } else { // Unknown address family. throwSocketException(env, SOCKERR_BADAF); @@ -419,20 +409,23 @@ static int byteArrayToSocketAddress(JNIEnv *env, * @param port the port number * @param sockaddress the sockaddr_storage structure to write to * - * @return 0 on success, -1 on failure + * @return 0 on success, a system error code on failure * @throw UnknownHostException if any error occurs * * @exception SocketError if the address family is unknown */ -static int inetAddressToSocketAddress(JNIEnv *env, - jobject inetaddress, int port, sockaddr_storage *sockaddress) { - +static int inetAddressToSocketAddress(JNIEnv *env, jobject inetaddress, + int port, sockaddr_storage *sockaddress) { // Get the byte array that stores the IP address bytes in the InetAddress. - jbyteArray addressByteArray; - addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, - gCachedFields.iaddr_ipaddress); + if (inetaddress == NULL) { + throwNullPointerException(env); + return EFAULT; + } + jbyteArray addressBytes = + reinterpret_cast<jbyteArray>(env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress)); - return byteArrayToSocketAddress(env, addressByteArray, port, sockaddress); + return byteArrayToSocketAddress(env, addressBytes, port, sockaddress); } /** @@ -652,22 +645,17 @@ jobject newJavaLangInteger(JNIEnv * env, jint anInt) { return env->NewObject(tempClass, tempMethod, anInt); } -/** - * Answer a new java.lang.String object. - * - * @param env pointer to the JNI library - * @param anInt the byte[] constructor argument - * - * @return the new String - */ - -jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { - jclass tempClass; - jmethodID tempMethod; +// Converts a number of milliseconds to a timeval. +static timeval toTimeval(long ms) { + timeval tv; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms - tv.tv_sec*1000) * 1000; + return tv; +} - tempClass = gCachedFields.string_class; - tempMethod = gCachedFields.string_class_init; - return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes); +// Converts a timeval to a number of milliseconds. +static long toMs(const timeval& tv) { + return tv.tv_sec * 1000 + tv.tv_usec / 1000; } /** @@ -684,11 +672,10 @@ jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { */ static int time_msec_clock() { - struct timeval tp; + timeval tp; struct timezone tzp; - gettimeofday(&tp, &tzp); - return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); + return toMs(tp); } /** @@ -1102,7 +1089,6 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { return (unsigned short )(~sum); } - /** * Wrapper for connect() that converts IPv4 addresses to IPv4-mapped IPv6 * addresses if necessary. @@ -1177,9 +1163,8 @@ static int doBind(int socket, struct sockaddr_storage *socketAddress) { * @return 0, if no errors occurred, otherwise the (negative) error code. */ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, - unsigned int timeout, unsigned int step, jbyte *ctxt) { + int timeout, unsigned int step, jbyte *ctxt) { int rc = 0; - struct timeval passedTimeout; int errorVal; socklen_t errorValLen = sizeof(int); struct selectFDSet *context = NULL; @@ -1239,13 +1224,13 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, * set the timeout value to be used. Because on some unix platforms we * don't get notified when a socket is closed we only sleep for 100ms * at a time + * + * TODO: is this relevant for Android? */ - passedTimeout.tv_sec = 0; if (timeout > 100) { - passedTimeout.tv_usec = 100 * 1000; - } else if ((int)timeout >= 0) { - passedTimeout.tv_usec = timeout * 1000; + timeout = 100; } + timeval passedTimeout(toTimeval(timeout)); /* initialize the FD sets for the select */ FD_ZERO(&(context->exceptionSet)); @@ -1259,7 +1244,7 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, &(context->readSet), &(context->writeSet), &(context->exceptionSet), - (int)timeout >= 0 ? &passedTimeout : NULL); + timeout >= 0 ? &passedTimeout : NULL); /* if there is at least one descriptor ready to be checked */ if (0 < rc) { @@ -1613,7 +1598,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, {&c->integer_class, "java/lang/Integer"}, {&c->boolean_class, "java/lang/Boolean"}, {&c->byte_class, "java/lang/Byte"}, - {&c->string_class, "java/lang/String"}, {&c->socketimpl_class, "java/net/SocketImpl"}, {&c->dpack_class, "java/net/DatagramPacket"} }; @@ -1635,7 +1619,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, {&c->integer_class_init, c->integer_class, "<init>", "(I)V", false}, {&c->boolean_class_init, c->boolean_class, "<init>", "(Z)V", false}, {&c->byte_class_init, c->byte_class, "<init>", "(B)V", false}, - {&c->string_class_init, c->string_class, "<init>", "([B)V", false}, {&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress", "([B)Ljava/net/InetAddress;", true} }; @@ -2684,13 +2667,10 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, int spaceAvailable = env->GetArrayLength(data) - offset; int localCount = count < spaceAvailable? count : spaceAvailable; - jboolean isCopy; - jbyte *body = env->GetByteArrayElements(data, &isCopy); + jbyte* body = env->GetByteArrayElements(data, NULL); // set timeout - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; + timeval tv(toTimeval(timeout)); setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); @@ -2728,8 +2708,7 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, int handle = 0; int result = 0, sent = 0; - jboolean isCopy; - jbyte *message = env->GetByteArrayElements(data, &isCopy); + jbyte *message = env->GetByteArrayElements(data, NULL); // Cap write length to available buf size int spaceAvailable = env->GetArrayLength(data) - offset; @@ -2875,111 +2854,87 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, return sent; } -static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, - jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, - jint countWriteC, jintArray outFlags, jlong timeout) { - // LOGD("ENTER selectImpl"); - - struct timeval timeP; - int result = 0; - int size = 0; - jobject gotFD; - fd_set *fdset_read,*fdset_write; - int handle; - jboolean isCopy ; - jint *flagArray; - int val; - unsigned int time_sec = (unsigned int)timeout/1000; - unsigned int time_msec = (unsigned int)(timeout%1000); - - fdset_read = (fd_set *)malloc(sizeof(fd_set)); - fdset_write = (fd_set *)malloc(sizeof(fd_set)); - - FD_ZERO(fdset_read); - FD_ZERO(fdset_write); - - for (val = 0; val<countReadC; val++) { - - gotFD = env->GetObjectArrayElement(readFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - FD_SET(handle, fdset_read); - - if (0 > (size - handle)) { - size = handle; +static bool initFdSet(JNIEnv* env, jobjectArray fdArray, jint count, fd_set* fdSet, int* maxFd) { + for (int i = 0; i < count; ++i) { + jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i); + if (fileDescriptor == NULL) { + return false; } - } - - for (val = 0; val<countWriteC; val++) { - - gotFD = env->GetObjectArrayElement(writeFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - FD_SET(handle, fdset_write); - - if (0 > (size - handle)) { - size = handle; + + const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0 || fd > 1024) { + LOGE("selectImpl: invalid fd %i", fd); + continue; } - } - - /* the size is the max_fd + 1 */ - size =size + 1; - - if (0 > size) { - result = SOCKERR_FDSET_SIZEBAD; - } else { - /* only set when timeout >= 0 (non-block)*/ - if (0 <= timeout) { - - timeP.tv_sec = time_sec; - timeP.tv_usec = time_msec*1000; - - result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP); - - } else { - result = sockSelect(size, fdset_read, fdset_write, NULL, NULL); + + FD_SET(fd, fdSet); + + if (fd > *maxFd) { + *maxFd = fd; } } + return true; +} - if (0 < result) { - /*output the result to a int array*/ - flagArray = env->GetIntArrayElements(outFlags, &isCopy); - - for (val=0; val<countReadC; val++) { - gotFD = env->GetObjectArrayElement(readFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - if (FD_ISSET(handle,fdset_read)) { - flagArray[val] = SOCKET_OP_READ; - } else { - flagArray[val] = SOCKET_OP_NONE; - } +static bool translateFdSet(JNIEnv* env, jobjectArray fdArray, jint count, const fd_set& fdSet, jint* flagArray, size_t offset, jint op) { + for (int i = 0; i < count; ++i) { + jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i); + if (fileDescriptor == NULL) { + return false; } - - for (val=0; val<countWriteC; val++) { - - gotFD = env->GetObjectArrayElement(writeFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - if (FD_ISSET(handle,fdset_write)) { - flagArray[val+countReadC] = SOCKET_OP_WRITE; - } else { - flagArray[val+countReadC] = SOCKET_OP_NONE; - } + + const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + const bool valid = fd >= 0 && fd < 1024; + + if (valid && FD_ISSET(fd, &fdSet)) { + flagArray[i + offset] = op; + } else { + flagArray[i + offset] = SOCKET_OP_NONE; } - - env->ReleaseIntArrayElements(outFlags, flagArray, 0); } + return true; +} - free(fdset_write); - free(fdset_read); - - /* return both correct and error result, let java handle the exception*/ - return result; +static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, + jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, + jint countWriteC, jintArray outFlags, jlong timeoutMs) { + // LOGD("ENTER selectImpl"); + + // Initialize the fd_sets. + int maxFd = -1; + fd_set readFds; + fd_set writeFds; + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + bool initialized = initFdSet(env, readFDArray, countReadC, &readFds, &maxFd) && + initFdSet(env, writeFDArray, countWriteC, &writeFds, &maxFd); + if (!initialized) { + return -1; + } + + // Initialize the timeout, if any. + timeval tv; + timeval* tvp = NULL; + if (timeoutMs >= 0) { + tv = toTimeval(timeoutMs); + tvp = &tv; + } + + // Perform the select. + int result = sockSelect(maxFd + 1, &readFds, &writeFds, NULL, tvp); + if (result < 0) { + return result; + } + + // Translate the result into the int[] we're supposed to fill in. + jint* flagArray = env->GetIntArrayElements(outFlags, NULL); + if (flagArray == NULL) { + return -1; + } + bool okay = translateFdSet(env, readFDArray, countReadC, readFds, flagArray, 0, SOCKET_OP_READ) && + translateFdSet(env, writeFDArray, countWriteC, writeFds, flagArray, countReadC, SOCKET_OP_WRITE); + env->ReleaseIntArrayElements(outFlags, flagArray, 0); + return okay ? 0 : -1; } static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, @@ -3212,7 +3167,7 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, throwSocketException(env, convertError(errno)); return NULL; } - return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000); + return newJavaLangInteger(env, toMs(timeout)); } default: { throwSocketException(env, SOCKERR_OPTUNSUPP); @@ -3457,9 +3412,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_SO_RCVTIMEOUT: { - struct timeval timeout; - timeout.tv_sec = intVal / 1000; - timeout.tv_usec = (intVal % 1000) * 1000; + timeval timeout(toTimeval(intVal)); result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)); if (0 != result) { diff --git a/luni/src/test/java/com/google/coretests/CoreTestRunner.java b/luni/src/test/java/com/google/coretests/CoreTestRunner.java index d469c86..d80fa4e 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestRunner.java +++ b/luni/src/test/java/com/google/coretests/CoreTestRunner.java @@ -15,6 +15,8 @@ */ package com.google.coretests; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -134,10 +136,10 @@ public class CoreTestRunner extends TestRunner { * Prints a help screen on the console. */ private void showHelp() { - System.out.println("Usage: run-core-tests {<param>} <test>"); + System.out.println("Usage: run-core-tests [OPTION]... [TEST]..."); System.out.println(); - System.out.println("Where <test> is a class name, optionally followed"); - System.out.println("by \"#\" and a method name, and <param> is one of"); + System.out.println("Where each TEST is a class name, optionally followed"); + System.out.println("by \"#\" and a method name, and each OPTION is one of"); System.out.println("the following:"); System.out.println(); System.out.println(" --include-all"); @@ -185,26 +187,29 @@ public class CoreTestRunner extends TestRunner { } /** - * Tries to create a Test instance from a given string. The string might + * Tries to create a Test instance from the given strings. The strings might * either specify a class only or a class plus a method name, separated by * a "#". */ - private Test createTest(String testCase) throws Exception { - int p = testCase.indexOf("#"); - if (p != -1) { - String testName = testCase.substring(p + 1); - testCase = testCase.substring(0, p); - - return TestSuite.createTest(Class.forName(testCase), testName); - } else { - return getTest(testCase); + private Test createTest(List<String> testCases) throws Exception { + TestSuite result = new TestSuite(); + for (String testCase : testCases) { + int p = testCase.indexOf("#"); + if (p != -1) { + String testName = testCase.substring(p + 1); + testCase = testCase.substring(0, p); + + result.addTest(TestSuite.createTest(Class.forName(testCase), testName)); + } else { + result.addTest(getTest(testCase)); + } } - + return result; } @Override protected TestResult start(String args[]) throws Exception { - String testName = null; + List<String> testNames = new ArrayList<String>(); // String victimName = null; boolean wait = false; @@ -269,12 +274,12 @@ public class CoreTestRunner extends TestRunner { showHelp(); System.exit(1); } else { - System.err.println("Unknown argument " + args[i] + - ", try --help"); - System.exit(1); + unknownArgument(args[i]); } + } else if (args[i].startsWith("-")) { + unknownArgument(args[i]); } else { - testName = args[i]; + testNames.add(args[i]); } } @@ -288,7 +293,7 @@ public class CoreTestRunner extends TestRunner { System.out.println(); try { - return doRun(createTest(testName), wait); + return doRun(createTest(testNames), wait); } catch(Exception e) { e.printStackTrace(); @@ -296,4 +301,8 @@ public class CoreTestRunner extends TestRunner { } } + private static void unknownArgument(String arg) { + System.err.println("Unknown argument " + arg + ", try --help"); + System.exit(1); + } } diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java index cb35324..283c1db 100644 --- a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java +++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java @@ -312,7 +312,7 @@ public class PackageTest extends junit.framework.TestCase { method = "isCompatibleWith", args = {java.lang.String.class} ) - @KnownFailure("isCompatibleWith returns incorrect value.") + @KnownFailure("Dalvik packages are always version '0.0'.") public void test_isCompatibleWithLjava_lang_String() throws Exception { Package p = getTestPackage("hyts_c.jar", "p.C"); diff --git a/luni/src/test/java/tests/api/java/io/FileTest.java b/luni/src/test/java/tests/api/java/io/FileTest.java index 2850826..5c0cb2a 100644 --- a/luni/src/test/java/tests/api/java/io/FileTest.java +++ b/luni/src/test/java/tests/api/java/io/FileTest.java @@ -688,10 +688,7 @@ public class FileTest extends junit.framework.TestCase { method = "delete", args = {} ) - @KnownFailure("Non empty directories are deleted on Android.") public void test_delete() { - // this test passes in the emulator, but it fails on the device - // Test for method boolean java.io.File.delete() try { File dir = new File(System.getProperty("java.io.tmpdir"), platformId diff --git a/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java b/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java index 3b545e3..dc35610 100644 --- a/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java +++ b/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java @@ -1200,16 +1200,25 @@ public class RandomAccessFileTest extends junit.framework.TestCase { } catch (IOException e) { // Expected. } + // BEGIN android-added + try { + // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work. + raf.seek(((long) Integer.MAX_VALUE) + 1); + fail("Test 2: IOException expected."); + } catch (IOException e) { + // Expected. + } + // END android-added raf.write(testString.getBytes(), 0, testLength); raf.seek(12); - assertEquals("Test 2: Seek failed to set file pointer.", 12, + assertEquals("Test 3: Seek failed to set file pointer.", 12, raf.getFilePointer()); raf.close(); try { raf.seek(1); - fail("Test 1: IOException expected."); + fail("Test 4: IOException expected."); } catch (IOException e) { // Expected. } @@ -1296,10 +1305,21 @@ public class RandomAccessFileTest extends junit.framework.TestCase { assertEquals("Test 7: Incorrect file length;", testLength + 2, raf.length()); + // BEGIN android-added + // Exception testing. + try { + // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work. + raf.setLength(((long) Integer.MAX_VALUE) + 1); + fail("Test 8: IOException expected."); + } catch (IOException e) { + // Expected. + } + // END android-added + // Exception testing. try { raf.setLength(-1); - fail("Test 8: IllegalArgumentException expected."); + fail("Test 9: IllegalArgumentException expected."); } catch (IllegalArgumentException e) { // Expected. } @@ -1307,7 +1327,7 @@ public class RandomAccessFileTest extends junit.framework.TestCase { raf.close(); try { raf.setLength(truncLength); - fail("Test 9: IOException expected."); + fail("Test 10: IOException expected."); } catch (IOException e) { // Expected. } @@ -1501,4 +1521,4 @@ public class RandomAccessFileTest extends junit.framework.TestCase { super.tearDown(); } -}
\ No newline at end of file +} diff --git a/luni/src/test/java/tests/api/java/util/ArrayListTest.java b/luni/src/test/java/tests/api/java/util/ArrayListTest.java index 8aa77cc..0356731 100644 --- a/luni/src/test/java/tests/api/java/util/ArrayListTest.java +++ b/luni/src/test/java/tests/api/java/util/ArrayListTest.java @@ -19,7 +19,7 @@ package tests.api.java.util; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetClass; import java.util.ArrayList; import java.util.Arrays; @@ -33,13 +33,13 @@ import java.util.Vector; import tests.support.Support_ListTest; -@TestTargetClass(ArrayList.class) +@TestTargetClass(ArrayList.class) public class ArrayListTest extends junit.framework.TestCase { List alist; Object[] objArray; - + /** * @tests java.util.ArrayList#ArrayList() */ @@ -72,7 +72,7 @@ public class ArrayListTest extends junit.framework.TestCase { // Test for method java.util.ArrayList(int) ArrayList al = new ArrayList(5); assertEquals("Incorrect arrayList created", 0, al.size()); - + try { new ArrayList(-10); fail("IllegalArgumentException expected"); @@ -130,14 +130,14 @@ public class ArrayListTest extends junit.framework.TestCase { assertNull("Should have returned null", alist.get(25)); assertTrue("Should have returned the old item from slot 25", alist .get(26) == oldItem); - + try { alist.add(-1, null); fail("IndexOutOfBoundsException expected"); } catch (IndexOutOfBoundsException e) { //expected } - + try { alist.add(alist.size() + 1, null); fail("IndexOutOfBoundsException expected"); @@ -198,9 +198,9 @@ public class ArrayListTest extends junit.framework.TestCase { assertTrue("Incorrect size: " + alist.size(), alist.size() == 205); assertNull("Item at slot 100 should be null", alist.get(100)); assertNull("Item at slot 101 should be null", alist.get(101)); - assertEquals("Item at slot 102 should be 'yoink'", + assertEquals("Item at slot 102 should be 'yoink'", "yoink", alist.get(102)); - assertEquals("Item at slot 103 should be 'kazoo'", + assertEquals("Item at slot 103 should be 'kazoo'", "kazoo", alist.get(103)); assertNull("Item at slot 104 should be null", alist.get(104)); alist.addAll(205, listWithNulls); @@ -228,24 +228,29 @@ public class ArrayListTest extends junit.framework.TestCase { } } - /** - * @tests java.util.ArrayList#addAll(int, java.util.Collection) - */ - @TestTargetNew( - level = TestLevel.PARTIAL_COMPLETE, - notes = "Verifies IndexOutOfBoundsException.", - method = "addAll", - args = {int.class, java.util.Collection.class} - ) - public void test_addAllILjava_util_Collection_2() { - // Regression for HARMONY-467 - ArrayList obj = new ArrayList(); - try { - obj.addAll((int) -1, (Collection) null); - fail("IndexOutOfBoundsException expected"); - } catch (IndexOutOfBoundsException e) { - } - } +// BEGIN android-removed +// The spec does not mandate that IndexOutOfBoundsException be thrown in +// preference to NullPointerException when the caller desserves both. +// +// /** +// * @tests java.util.ArrayList#addAll(int, java.util.Collection) +// */ +// @TestTargetNew( +// level = TestLevel.PARTIAL_COMPLETE, +// notes = "Verifies IndexOutOfBoundsException.", +// method = "addAll", +// args = {int.class, java.util.Collection.class} +// ) +// public void test_addAllILjava_util_Collection_2() { +// // Regression for HARMONY-467 +// ArrayList obj = new ArrayList(); +// try { +// obj.addAll((int) -1, (Collection) null); +// fail("IndexOutOfBoundsException expected"); +// } catch (IndexOutOfBoundsException e) { +// } +// } +// END android-removed /** * @tests java.util.ArrayList#addAll(java.util.Collection) @@ -287,17 +292,17 @@ public class ArrayListTest extends junit.framework.TestCase { .get(101) == i.next()); assertTrue("Item at slot 103 is wrong: " + alist.get(102), alist .get(102) == i.next()); - - + + // Regression test for Harmony-3481 ArrayList<Integer> originalList = new ArrayList<Integer>(12); for (int j = 0; j < 12; j++) { originalList.add(j); } - + originalList.remove(0); originalList.remove(0); - + ArrayList<Integer> additionalList = new ArrayList<Integer>(11); for (int j = 0; j < 11; j++) { additionalList.add(j); @@ -672,7 +677,7 @@ public class ArrayListTest extends junit.framework.TestCase { assertTrue("Returned incorrect array: " + i, retArray[i] == objArray[i]); } - + String[] strArray = new String[100]; try { alist.toArray(strArray); @@ -746,16 +751,16 @@ public class ArrayListTest extends junit.framework.TestCase { list.remove(0); assertEquals(1, list.size()); - + ArrayList collection = new ArrayList(); collection.add("1"); collection.add("2"); collection.add("3"); assertEquals(3, collection.size()); - + list.addAll(0, collection); assertEquals(4, list.size()); - + list.remove(0); list.remove(0); assertEquals(2, list.size()); @@ -769,13 +774,13 @@ public class ArrayListTest extends junit.framework.TestCase { collection.add("10"); collection.add("11"); collection.add("12"); - + assertEquals(12, collection.size()); - + list.addAll(0, collection); assertEquals(14, list.size()); } - + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -818,7 +823,7 @@ public class ArrayListTest extends junit.framework.TestCase { mal.add("f"); mal.add("g"); mal.add("h"); - + mal.removeRange(2, 4); String[] result = new String[6]; @@ -827,30 +832,30 @@ public class ArrayListTest extends junit.framework.TestCase { new String[] { "a", "b", "e", "f", "g", "h"})); } - + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. */ protected void setUp() throws Exception { super.setUp(); - + objArray = new Object[100]; for (int i = 0; i < objArray.length; i++) { objArray[i] = new Integer(i); } - + alist = new ArrayList(); for (int i = 0; i < objArray.length; i++) { alist.add(objArray[i]); } } - + @Override protected void tearDown() throws Exception { objArray = null; alist = null; - + super.tearDown(); } } diff --git a/luni/src/test/java/tests/api/java/util/FormatterTest.java b/luni/src/test/java/tests/api/java/util/FormatterTest.java index b2030c9..6f86818 100644 --- a/luni/src/test/java/tests/api/java/util/FormatterTest.java +++ b/luni/src/test/java/tests/api/java/util/FormatterTest.java @@ -800,6 +800,36 @@ public class FormatterTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Tests that supplying a Formattable works. See http://code.google.com/p/android/issues/detail?id=1767.", + method = "format", + args = {} + ) + public void test_Formattable() { + Formattable ones = new Formattable() { + public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException { + try { + formatter.out().append("111"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + Formattable twos = new Formattable() { + public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException { + try { + formatter.out().append("222"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + assertEquals("aaa 111?", new Formatter().format("aaa %s?", ones).toString()); + assertEquals("aaa 111 bbb 222?", new Formatter().format("aaa %s bbb %s?", ones, twos).toString()); + } + /** * @tests java.util.Formatter#out() */ diff --git a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java index efdb8a1..75e9ae3 100644 --- a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java +++ b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java @@ -383,6 +383,19 @@ public class TimeZoneTest extends junit.framework.TestCase { @TestTargetNew( level = TestLevel.COMPLETE, notes = "", + method = "useDaylightTime", + args = {} + ) + public void test_useDaylightTime() { + // http://code.google.com/p/android/issues/detail?id=877 + + TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei"); + assertFalse("Taiwan doesn't use DST", asiaTaipei.useDaylightTime()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", method = "setID", args = {java.lang.String.class} ) |