diff options
118 files changed, 4114 insertions, 3460 deletions
@@ -51,7 +51,7 @@ include $(BUILD_JAVA_LIBRARY) # resource with a "#" in its name, but Perforce doesn't # allow us to submit such a file. So we create it here # on-the-fly. -TMP_RESOURCE_DIR := /tmp/ +TMP_RESOURCE_DIR := $(OUT_DIR)/tmp/ TMP_RESOURCE_FILE := org/apache/harmony/luni/tests/java/lang/test\#.properties $(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE): diff --git a/annotation/src/main/java/java/lang/annotation/Annotation.java b/annotation/src/main/java/java/lang/annotation/Annotation.java index 43ad1bb..00707b1 100644 --- a/annotation/src/main/java/java/lang/annotation/Annotation.java +++ b/annotation/src/main/java/java/lang/annotation/Annotation.java @@ -22,17 +22,15 @@ package java.lang.annotation; * itself is <i>not</i> an annotation, and neither is an interface that simply * extends this one. Only the compiler is able to create proper annotation * types. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface Annotation { /** * Returns the type of this annotation. - * + * * @return A {@code Class} instance representing the annotation type. - * - * @since Android 1.0 */ Class<? extends Annotation> annotationType(); @@ -70,14 +68,12 @@ public interface Annotation { * calling their {@code equals()} method. * </li> * </ul> - * + * * @param obj * The object to compare to. - * - * @return {@code true} if {@code obj} is equal to this annotation, - * {@code false} otherwise. - * - * @since Android 1.0 + * + * @return {@code true} if {@code obj} is equal to this annotation, + * {@code false} otherwise. */ boolean equals(Object obj); @@ -111,10 +107,8 @@ public interface Annotation { * calling their {@code hashCode} method. * </li> * </ul> - * + * * @return the hash code. - * - * @since Android 1.0 */ int hashCode(); @@ -123,11 +117,9 @@ public interface Annotation { * strictly defined what the representation has to look like, but it usually * consists of the name of the annotation, preceded by a "@". If the * annotation contains field members, their names and values are also - * included in the result. + * included in the result. * * @return the {@code String} that represents this annotation. - * - * @since Android 1.0 */ String toString(); } diff --git a/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java b/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java index ce5c3a0..67775c7 100644 --- a/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java +++ b/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java @@ -22,8 +22,8 @@ package java.lang.annotation; * syntactically incorrect and the annotation parser is unable to process it. * This exception is unlikely to ever occur, given that the code has been * compiled by an ordinary Java compiler. - * - * @since Android 1.0 + * + * @since 1.5 */ public class AnnotationFormatError extends Error { @@ -31,11 +31,9 @@ public class AnnotationFormatError extends Error { /** * Constructs an instance with the message provided. - * + * * @param message * the details of the error. - * - * @since Android 1.0 */ public AnnotationFormatError(String message) { super(message); @@ -43,14 +41,11 @@ public class AnnotationFormatError extends Error { /** * Constructs an instance with a message and a cause. - * + * * @param message * the details of the error. - * * @param cause * the cause of the error or {@code null} if none. - * - * @since Android 1.0 */ public AnnotationFormatError(String message, Throwable cause) { super(message, cause); @@ -60,11 +55,9 @@ public class AnnotationFormatError extends Error { * Constructs an instance with a cause. If the cause is not * {@code null}, then {@code cause.toString()} is used as the * error's message. - * + * * @param cause * the cause of the error or {@code null} if none. - * - * @since Android 1.0 */ public AnnotationFormatError(Throwable cause) { super(cause == null ? null : cause.toString(), cause); diff --git a/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java b/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java index 5bb3cbf..0ff79ec 100644 --- a/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java +++ b/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java @@ -24,8 +24,8 @@ import org.apache.harmony.annotation.internal.nls.Messages; /** * Indicates that an annotation type has changed since it was compiled or * serialized. - * - * @since Android 1.0 + * + * @since 1.5 */ public class AnnotationTypeMismatchException extends RuntimeException { @@ -37,16 +37,13 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Constructs an instance for the given type element and the type found. - * + * * @param element * the annotation type element. - * * @param foundType * the invalid type that was found. This is actually the textual * type description found in the binary class representation, * so it may not be human-readable. - * - * @since Android 1.0 */ public AnnotationTypeMismatchException(Method element, String foundType) { super(Messages.getString("annotation.1", element, foundType)); //$NON-NLS-1$ @@ -56,10 +53,8 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Returns the method object for the invalid type. - * + * * @return a {@link Method} instance. - * - * @since Android 1.0 */ public Method element() { return element; @@ -67,10 +62,8 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Returns the invalid type. - * + * * @return a string describing the invalid data. - * - * @since Android 1.0 */ public String foundType() { return foundType; diff --git a/annotation/src/main/java/java/lang/annotation/Documented.java b/annotation/src/main/java/java/lang/annotation/Documented.java index 2849fd2..7e7f72f 100644 --- a/annotation/src/main/java/java/lang/annotation/Documented.java +++ b/annotation/src/main/java/java/lang/annotation/Documented.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for indicating that an annotation is documented and * considered part of the public API. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/annotation/src/main/java/java/lang/annotation/ElementType.java b/annotation/src/main/java/java/lang/annotation/ElementType.java index 92f5109..f0f52aa 100644 --- a/annotation/src/main/java/java/lang/annotation/ElementType.java +++ b/annotation/src/main/java/java/lang/annotation/ElementType.java @@ -21,8 +21,8 @@ package java.lang.annotation; * Defines an enumeration for Java program elements. It is used in conjunction * with the {@link Target} meta-annotation to restrict the use of an annotation * to certain program elements. - * - * @since Android 1.0 + * + * @since 1.5 */ public enum ElementType { /** diff --git a/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java b/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java index 3a31551..a5d2068 100644 --- a/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java +++ b/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java @@ -23,8 +23,8 @@ import org.apache.harmony.annotation.internal.nls.Messages; * Indicates that an element of an annotation type was accessed that was added * after the type was compiled or serialized. This does not apply to new * elements that have default values. - * - * @since Android 1.0 + * + * @since 1.5 */ public class IncompleteAnnotationException extends RuntimeException { @@ -37,13 +37,11 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Constructs an instance with the incomplete annotation type and the name * of the element that's missing. - * + * * @param annotationType * the annotation type. * @param elementName * the name of the incomplete element. - * - * @since Android 1.0 */ public IncompleteAnnotationException( Class<? extends Annotation> annotationType, String elementName) { @@ -54,10 +52,8 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Returns the annotation type. - * + * * @return a Class instance. - * - * @since Android 1.0 */ public Class<? extends Annotation> annotationType() { return annotationType; @@ -65,10 +61,8 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Returns the incomplete element's name. - * + * * @return the name of the element. - * - * @since Android 1.0 */ public String elementName() { return elementName; diff --git a/annotation/src/main/java/java/lang/annotation/Inherited.java b/annotation/src/main/java/java/lang/annotation/Inherited.java index cf16928..730d30a 100644 --- a/annotation/src/main/java/java/lang/annotation/Inherited.java +++ b/annotation/src/main/java/java/lang/annotation/Inherited.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for indicating that an annotation is automatically * inherited. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/annotation/src/main/java/java/lang/annotation/Retention.java b/annotation/src/main/java/java/lang/annotation/Retention.java index 198fccc..275739e 100644 --- a/annotation/src/main/java/java/lang/annotation/Retention.java +++ b/annotation/src/main/java/java/lang/annotation/Retention.java @@ -21,15 +21,12 @@ package java.lang.annotation; * Defines a meta-annotation for determining the scope of retention for an * annotation. If the retention annotation is not set {@code * RetentionPolicy.CLASS} is used as default retention. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { - // BEGIN android-changed - // copied from newer version of harmony RetentionPolicy value(); - // END android-changed } diff --git a/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java b/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java index 014b910..70de3b0 100644 --- a/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java +++ b/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java @@ -21,8 +21,8 @@ package java.lang.annotation; * Defines an enumeration for annotation retention policies. Used in conjunction * with the {@link Retention} annotation to specify an annotation's time-to-live * in the overall development life cycle. - * - * @since Android 1.0 + * + * @since 1.5 */ public enum RetentionPolicy { /** diff --git a/annotation/src/main/java/java/lang/annotation/Target.java b/annotation/src/main/java/java/lang/annotation/Target.java index 1f53fa0..4ba0938 100644 --- a/annotation/src/main/java/java/lang/annotation/Target.java +++ b/annotation/src/main/java/java/lang/annotation/Target.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for determining what {@link ElementType}s an * annotation can be applied to. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java b/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java index 1134887..5430286 100644 --- a/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java +++ b/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java @@ -65,7 +65,7 @@ public class AnnotationTypeMismatchExceptionTest extends TestCase { Method m = methods[0]; AnnotationTypeMismatchException e = new AnnotationTypeMismatchException( m, "some type"); - assertNotNull("can not instanciate AnnotationTypeMismatchException", e); + assertNotNull("can not instantiate AnnotationTypeMismatchException", e); assertSame("wrong method name", m, e.element()); assertEquals("wrong found type", "some type", e.foundType()); } diff --git a/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java b/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java index de56330..5c718ed 100644 --- a/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java +++ b/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java @@ -80,7 +80,7 @@ public class IncompleteAnnotationExceptionTest extends TestCase { String elementName = "some element"; IncompleteAnnotationException e = new IncompleteAnnotationException( clazz, elementName); - assertNotNull("can not instanciate IncompleteAnnotationException", e); + assertNotNull("can not instantiate IncompleteAnnotationException", e); assertSame("wrong annotation type", clazz, e.annotationType()); assertSame("wrong element name", elementName, e.elementName()); } diff --git a/archive/src/main/java/java/util/jar/Attributes.java b/archive/src/main/java/java/util/jar/Attributes.java index 5a4d923..4ee94df 100644 --- a/archive/src/main/java/java/util/jar/Attributes.java +++ b/archive/src/main/java/java/util/jar/Attributes.java @@ -17,6 +17,7 @@ package java.util.jar; +import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,7 +29,6 @@ import org.apache.harmony.archive.util.Util; * The {@code Attributes} class is used to store values for manifest entries. * Attribute keys are generally instances of {@code Attributes.Name}. Values * associated with attribute keys are of type {@code String}. - * @since Android 1.0 */ public class Attributes implements Cloneable, Map<Object, Object> { @@ -37,8 +37,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The * attribute names thus are obtained from the {@link Manifest} for * convenience. - * - * @since Android 1.0 */ protected Map<Object, Object> map; @@ -46,46 +44,35 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The name part of the name/value pairs constituting an attribute as * defined by the specification of the JAR manifest. May be composed of the * following ASCII signs as defined in the EBNF below: - * + * * <pre> * name = alphanum *headerchar * headerchar = alphanum | - | _ - * alphanum = {A-Z} | {a-z} | {0-9} + * alphanum = {A-Z} | {a-z} | {0-9} * </pre> - * - * @since Android 1.0 */ public static class Name { - private final String name; + private final byte[] name; private int hashCode; /** * The class path (a main attribute). - * - * @since Android 1.0 */ public static final Name CLASS_PATH = new Name("Class-Path"); //$NON-NLS-1$ /** * The version of the manifest file (a main attribute). - * - * @since Android 1.0 */ - public static final Name MANIFEST_VERSION = new Name( - "Manifest-Version"); //$NON-NLS-1$ + public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); //$NON-NLS-1$ /** * The main class's name (for stand-alone applications). - * - * @since Android 1.0 */ public static final Name MAIN_CLASS = new Name("Main-Class"); //$NON-NLS-1$ /** * Defines the signature version of the JAR file. - * - * @since Android 1.0 */ public static final Name SIGNATURE_VERSION = new Name( "Signature-Version"); //$NON-NLS-1$ @@ -98,16 +85,12 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Sealed} manifest attribute which may have the value * {@code true} for sealed archives. - * - * @since Android 1.0 */ public static final Name SEALED = new Name("Sealed"); //$NON-NLS-1$ /** * The {@code Implementation-Title} attribute whose value is a string * that defines the title of the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_TITLE = new Name( "Implementation-Title"); //$NON-NLS-1$ @@ -115,8 +98,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Implementation-Version} attribute defining the version of * the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VERSION = new Name( "Implementation-Version"); //$NON-NLS-1$ @@ -124,8 +105,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Implementation-Vendor} attribute defining the organization * that maintains the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR = new Name( "Implementation-Vendor"); //$NON-NLS-1$ @@ -133,8 +112,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Title} attribute defining the title of the * extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_TITLE = new Name( "Specification-Title"); //$NON-NLS-1$ @@ -142,8 +119,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Version} attribute defining the version of * the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VERSION = new Name( "Specification-Version"); //$NON-NLS-1$ @@ -151,8 +126,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Vendor} attribute defining the organization * that maintains the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VENDOR = new Name( "Specification-Vendor"); //$NON-NLS-1$ @@ -160,23 +133,17 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Extension-List} attribute defining the extensions that are * needed by the applet. - * - * @since Android 1.0 */ public static final Name EXTENSION_LIST = new Name("Extension-List"); //$NON-NLS-1$ /** * The {@code Extension-Name} attribute which defines the unique name of * the extension. - * - * @since Android 1.0 */ public static final Name EXTENSION_NAME = new Name("Extension-Name"); //$NON-NLS-1$ /** * The {@code Extension-Installation} attribute. - * - * @since Android 1.0 */ public static final Name EXTENSION_INSTALLATION = new Name( "Extension-Installation"); //$NON-NLS-1$ @@ -185,8 +152,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The {@code Implementation-Vendor-Id} attribute specifies the vendor * of an extension implementation if the applet requires an * implementation from a specific vendor. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR_ID = new Name( "Implementation-Vendor-Id"); //$NON-NLS-1$ @@ -195,91 +160,112 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The {@code Implementation-URL} attribute specifying a URL that can be * used to obtain the most recent version of the extension if the * required version is not already installed. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_URL = new Name( "Implementation-URL"); //$NON-NLS-1$ + static final Name NAME = new Name("Name"); + /** * A String which must satisfy the following EBNF grammar to specify an * additional attribute: - * + * * <pre> * name = alphanum *headerchar * headerchar = alphanum | - | _ * alphanum = {A-Z} | {a-z} | {0-9} * </pre> - * + * * @param s * The Attribute string. * @exception IllegalArgumentException * if the string does not satisfy the EBNF grammar. - * @since Android 1.0 */ public Name(String s) { int i = s.length(); - if (i == 0 || i > 70) { + if (i == 0 || i > Manifest.LINE_LENGTH_LIMIT - 2) { throw new IllegalArgumentException(); } + + name = new byte[i]; + for (; --i >= 0;) { char ch = s.charAt(i); if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) { throw new IllegalArgumentException(s); } + name[i] = (byte) ch; } - name = s; + } + + /** + * A private constructor for a trusted attribute name. + */ + Name(byte[] buf) { + name = buf; + } + + byte[] getBytes() { + return name; } /** * Returns this attribute name. - * + * * @return the attribute name. - * @since Android 1.0 */ @Override public String toString() { - return name; + try { + return new String(name, "ISO-8859-1"); + } catch (UnsupportedEncodingException iee) { + throw new InternalError(iee.getLocalizedMessage()); + } } /** * returns whether the argument provided is the same as the attribute * name. - * + * * @return if the attribute names correspond. - * @param an + * @param object * An attribute name to be compared with this name. - * @since Android 1.0 */ @Override - public boolean equals(Object an) { - if (an == null) { + public boolean equals(Object object) { + if (object == null || object.getClass() != getClass() + || object.hashCode() != hashCode()) { return false; } - return an.getClass() == this.getClass() - && name.equalsIgnoreCase(((Name) an).name); + + return Util.equalsIgnoreCase(name, ((Name) object).name); } /** * Computes a hash code of the name. - * + * * @return the hash value computed from the name. - * @since Android 1.0 */ @Override public int hashCode() { if (hashCode == 0) { - hashCode = Util.toASCIILowerCase("name").hashCode(); + int hash = 0, multiplier = 1; + for (int i = name.length - 1; i >= 0; i--) { + // 'A' & 0xDF == 'a' & 0xDF, ..., 'Z' & 0xDF == 'z' & 0xDF + hash += (name[i] & 0xDF) * multiplier; + int shifted = multiplier << 5; + multiplier = shifted - multiplier; + } + hashCode = hash; } return hashCode; } + } /** * Constructs an {@code Attributes} instance. - * - * @since Android 1.0 */ public Attributes() { map = new HashMap<Object, Object>(); @@ -288,23 +274,21 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Constructs an {@code Attributes} instance obtaining keys and values from * the parameter {@code attrib}. - * + * * @param attrib * The attributes to obtain entries from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Attributes(Attributes attrib) { - map = (Map<Object, Object>)((HashMap) attrib.map).clone(); + map = (Map<Object, Object>) ((HashMap) attrib.map).clone(); } /** * Constructs an {@code Attributes} instance with initial capacity of size * {@code size}. - * + * * @param size * Initial size of this {@code Attributes} instance. - * @since Android 1.0 */ public Attributes(int size) { map = new HashMap<Object, Object>(size); @@ -312,8 +296,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Removes all key/value pairs from this {@code Attributes}. - * - * @since Android 1.0 */ public void clear() { map.clear(); @@ -321,11 +303,10 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains the specified key. - * + * * @param key * The key to search for. * @return {@code true} if the key is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsKey(Object key) { return map.containsKey(key); @@ -333,11 +314,10 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if the value is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsValue(Object value) { return map.containsValue(value); @@ -346,9 +326,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns a set containing map entries for each of the key/value pair * contained in this {@code Attributes}. - * + * * @return a set of Map.Entry's - * @since Android 1.0 */ public Set<Map.Entry<Object, Object>> entrySet() { return map.entrySet(); @@ -356,12 +335,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the value associated with the parameter key. - * + * * @param key * the key to search for. * @return Object associated with key, or {@code null} if key does not * exist. - * @since Android 1.0 */ public Object get(Object key) { return map.get(key); @@ -369,9 +347,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains any keys. - * + * * @return {@code true} if one or more keys exist, {@code false} otherwise. - * @since Android 1.0 */ public boolean isEmpty() { return map.isEmpty(); @@ -380,9 +357,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns a {@code Set} containing all the keys found in this {@code * Attributes}. - * + * * @return a {@code Set} of all keys. - * @since Android 1.0 */ public Set<Object> keySet() { return map.keySet(); @@ -390,7 +366,7 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Stores key/value pairs in this {@code Attributes}. - * + * * @param key * the key to associate with value. * @param value @@ -399,21 +375,20 @@ public class Attributes implements Cloneable, Map<Object, Object> { * @exception ClassCastException * when key is not an {@code Attributes.Name} or value is not * a {@code String}. - *@since Android 1.0 */ - @SuppressWarnings("cast") // Require cast to force ClassCastException + @SuppressWarnings("cast") + // Require cast to force ClassCastException public Object put(Object key, Object value) { - return map.put((Name)key, (String)value); + return map.put((Name) key, (String) value); } /** - * Stores all the key/value pairs in the argument in this {@code Attributes} - * . - * + * Stores all the key/value pairs in the argument in this {@code + * Attributes}. + * * @param attrib - * the associations to store (must be of type {@code Attributes} - * ). - * @since Android 1.0 + * the associations to store (must be of type {@code + * Attributes}). */ public void putAll(Map<?, ?> attrib) { if (attrib == null || !(attrib instanceof Attributes)) { @@ -425,12 +400,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Deletes the key/value pair with key {@code key} from this {@code * Attributes}. - * + * * @param key * the key to remove. * @return the values associated with the removed key, {@code null} if not * present. - * @since Android 1.0 */ public Object remove(Object key) { return map.remove(key); @@ -439,20 +413,18 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the number of key/value pairs associated with this {@code * Attributes}. - * + * * @return the size of this {@code Attributes}. - * @since Android 1.0 */ public int size() { return map.size(); } /** - * Returns a collection of all the values present in this {@code Attributes} - * . - * + * Returns a collection of all the values present in this {@code + * Attributes}. + * * @return a collection of all values present. - * @since Android 1.0 */ public Collection<Object> values() { return map.values(); @@ -473,9 +445,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the hash code of this {@code Attributes}. - * + * * @return the hash code of this object. - * @since Android 1.0 */ @Override public int hashCode() { @@ -486,12 +457,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { * Determines if this {@code Attributes} and the parameter {@code * Attributes} are equal. Two {@code Attributes} instances are equal if they * contain the same keys and values. - * + * * @param obj * the object with which this {@code Attributes} is compared. * @return {@code true} if the {@code Attributes} are equal, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -507,12 +477,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the value associated with the parameter {@code Attributes.Name} * key. - * + * * @param name * the key to obtain the value for. * @return the {@code String} associated with name, or {@code null} if name * is not a valid key. - * @since Android 1.0 */ public String getValue(Attributes.Name name) { return (String) map.get(name); @@ -520,12 +489,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the string associated with the parameter name. - * + * * @param name * the key to obtain the value for. * @return the string associated with name, or {@code null} if name is not a * valid key. - * @since Android 1.0 */ public String getValue(String name) { return (String) map.get(new Attributes.Name(name)); @@ -534,13 +502,12 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Stores the value {@code val} associated with the key {@code name} in this * {@code Attributes}. - * + * * @param name * the key to store. * @param val * the value to store in this {@code Attributes}. * @return the value being stored. - * @since Android 1.0 */ public String putValue(String name, String val) { return (String) map.put(new Attributes.Name(name), val); diff --git a/archive/src/main/java/java/util/jar/InitManifest.java b/archive/src/main/java/java/util/jar/InitManifest.java index 47fdf1c..bf9c397 100644 --- a/archive/src/main/java/java/util/jar/InitManifest.java +++ b/archive/src/main/java/java/util/jar/InitManifest.java @@ -17,279 +17,206 @@ package java.util.jar; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.UTFDataFormatException; -import java.security.AccessController; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; import java.util.Map; import org.apache.harmony.archive.internal.nls.Messages; -import org.apache.harmony.luni.util.PriviAction; -import org.apache.harmony.luni.util.Util; +import org.apache.harmony.luni.util.ThreadLocalCache; class InitManifest { - private final byte[] inbuf = new byte[1024]; - private int inbufCount = 0, inbufPos = 0; + private byte[] buf; - private byte[] buffer = new byte[5]; + private int pos; - private char[] charbuf = new char[0]; + Attributes.Name name; - private final ByteArrayOutputStream out = new ByteArrayOutputStream(256); + String value; - private String encoding; + CharsetDecoder decoder = ThreadLocalCache.utf8Decoder.get(); + CharBuffer cBuf = ThreadLocalCache.charBuffer.get(); - private boolean usingUTF8 = true; - - private final Map<String, Attributes.Name> attributeNames = new HashMap<String, Attributes.Name>(); + InitManifest(byte[] buf, Attributes main, Attributes.Name ver) + throws IOException { - private final byte[] mainAttributesChunk; + this.buf = buf; - InitManifest(InputStream is, Attributes main, Map<String, Attributes> entries, Map<String, byte[]> chunks, - String verString) throws IOException { - encoding = AccessController.doPrivileged(new PriviAction<String>( - "manifest.read.encoding")); //$NON-NLS-1$ - if ("".equals(encoding)) { //$NON-NLS-1$ - encoding = null; + // check a version attribute + if (!readHeader() || (ver != null && !name.equals(ver))) { + throw new IOException(Messages.getString( + "archive.2D", ver)); //$NON-NLS-1$ } - Attributes current = main; - ArrayList<String> list = new ArrayList<String>(); - - // Return the chunk of main attributes in the manifest. - mainAttributesChunk = nextChunk(is, list); - - Iterator<String> it = list.iterator(); - while (it.hasNext()) { - addAttribute(it.next(), current); + main.put(name, value); + while (readHeader()) { + main.put(name, value); } + } - // Check for version attribute - if (verString != null && main.getValue(verString) == null) { - throw new IOException(Messages.getString("archive.2D", verString)); //$NON-NLS-1$ - } + void initEntries(Map<String, Attributes> entries, + Map<String, Manifest.Chunk> chunks) throws IOException { - list.clear(); - byte[] chunk = null; - while (chunks == null ? readLines(is, list) : (chunk = nextChunk(is, - list)) != null) { - it = list.iterator(); - String line = it.next(); - if (line.length() < 7 - || !Util.toASCIILowerCase(line.substring(0, 5)).equals("name:")) { //$NON-NLS-1$ + int mark = pos; + while (readHeader()) { + if (!Attributes.Name.NAME.equals(name)) { throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$ } - // Name: length required space char - String name = line.substring(6, line.length()); - current = new Attributes(12); - if (chunks != null) { - chunks.put(name, chunk); + String entryNameValue = value; + + Attributes entry = entries.get(entryNameValue); + if (entry == null) { + entry = new Attributes(12); } - entries.put(name, current); - while (it.hasNext()) { - addAttribute(it.next(), current); + + while (readHeader()) { + entry.put(name, value); + } + + if (chunks != null) { + if (chunks.get(entryNameValue) != null) { + // TODO A bug: there might be several verification chunks for + // the same name. I believe they should be used to update + // signature in order of appearance; there are two ways to fix + // this: either use a list of chunks, or decide on used + // signature algorithm in advance and reread the chunks while + // updating the signature; for now a defensive error is thrown + throw new IOException(Messages.getString("archive.34")); //$NON-NLS-1$ + } + chunks.put(entryNameValue, new Manifest.Chunk(mark, pos)); + mark = pos; } - list.clear(); + + entries.put(entryNameValue, entry); } + } + + int getPos() { + return pos; + } + /** + * Number of subsequent line breaks. + */ + int linebreak = 0; + + /** + * Read a single line from the manifest buffer. + */ + private boolean readHeader() throws IOException { + if (linebreak > 1) { + // break a section on an empty line + linebreak = 0; + return false; + } + readName(); + linebreak = 0; + readValue(); + // if the last line break is missed, the line + // is ignored by the reference implementation + return linebreak > 0; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + private byte[] wrap(int mark, int pos) { + byte[] buffer = new byte[pos - mark]; + System.arraycopy(buf, mark, buffer, 0, pos - mark); + return buffer; } - private void addLine(int length, List<String> lines) throws IOException { - if (encoding != null) { - lines.add(new String(buffer, 0, length, encoding)); - } else { - if (usingUTF8) { - try { - if (charbuf.length < length) { - charbuf = new char[length]; - } - lines.add(Util.convertUTF8WithBuf(buffer, charbuf, 0, - length)); - } catch (UTFDataFormatException e) { - usingUTF8 = false; + private void readName() throws IOException { + int i = 0; + int mark = pos; + + while (pos < buf.length) { + byte b = buf[pos++]; + + if (b == ':') { + byte[] nameBuffer = wrap(mark, pos - 1); + + if (buf[pos++] != ' ') { + throw new IOException(Messages.getString( + "archive.30", nameBuffer)); //$NON-NLS-1$ } + + name = new Attributes.Name(nameBuffer); + return; } - if (!usingUTF8) { - if (charbuf.length < length) { - charbuf = new char[length]; - } - // If invalid UTF8, convert bytes to chars setting the upper - // bytes to zeros - int charOffset = 0; - int offset = 0; - for (int i = length; --i >= 0;) { - charbuf[charOffset++] = (char) (buffer[offset++] & 0xff); - } - lines.add(new String(charbuf, 0, length)); + + if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' + || b == '-' || (b >= '0' && b <= '9'))) { + throw new IOException(Messages.getString("archive.30", b)); //$NON-NLS-1$ } } + if (i > 0) { + throw new IOException(Messages.getString( + "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$ + } } - private byte[] nextChunk(InputStream in, List<String> lines) - throws IOException { - if (inbufCount == -1) { - return null; - } + private void readValue() throws IOException { byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - out.reset(); - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (out.size() == 0) { - return null; - } - if (blankline) { - addLine(pos, lines); - } - return out.toByteArray(); - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; + boolean lastCr = false; + int mark = pos; + int last = pos; + + decoder.reset(); + cBuf.clear(); + + while (pos < buf.length) { + next = buf[pos++]; + + switch (next) { + case 0: + throw new IOException(Messages.getString("archive.2F")); //$NON-NLS-1$ + case '\n': + if (lastCr) { + lastCr = false; } else { - if (out.size() == 0) { - continue; - } - out.write('\r'); + linebreak++; } - lastCr = false; - } else if (next == '\r') { + continue; + case '\r': lastCr = true; + linebreak++; continue; - } - if (blankline) { - if (next == ' ') { - out.write(next); - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - out.write(next); - return out.toByteArray(); - } - pos = 0; - } else if (next == '\n') { - if (out.size() == 0) { + case ' ': + if (linebreak == 1) { + decode(mark, last, false); + mark = pos; + linebreak = 0; continue; } - out.write(next); - blankline = true; - continue; } - blankline = false; - out.write(next); - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; + + if (linebreak >= 1) { + pos--; + break; } - buffer[pos++] = next; + last = pos; } - } - private boolean readLines(InputStream in, List<String> lines) - throws IOException { - if (inbufCount == -1) { - return false; - } - byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (blankline) { - addLine(pos, lines); - } - return lines.size() != 0; - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; - } - lastCr = false; - } else if (next == '\r') { - lastCr = true; - continue; - } - if (blankline) { - if (next == ' ') { - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - return true; - } - pos = 0; - } else if (next == '\n') { - if (pos == 0 && lines.size() == 0) { - continue; - } - blankline = true; - continue; - } - blankline = false; - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; - } - buffer[pos++] = next; + decode(mark, last, true); + while (CoderResult.OVERFLOW == decoder.flush(cBuf)) { + enlargeBuffer(); } + value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); } - /* Get the next attribute and add it */ - private void addAttribute(String line, Attributes current) + private void decode(int mark, int pos, boolean endOfInput) throws IOException { - String header; - int hdrIdx = line.indexOf(':'); - if (hdrIdx < 1) { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ - } - header = line.substring(0, hdrIdx); - Attributes.Name name = attributeNames.get(header); - if (name == null) { - try { - name = new Attributes.Name(header); - } catch (IllegalArgumentException e) { - throw new IOException(e.toString()); - } - attributeNames.put(header, name); - } - if (hdrIdx + 1 >= line.length() || line.charAt(hdrIdx + 1) != ' ') { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ + ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark); + while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) { + enlargeBuffer(); } - // +2 due to required SPACE char - current.put(name, line.substring(hdrIdx + 2, line.length())); + } + + private void enlargeBuffer() { + CharBuffer newBuf = CharBuffer.allocate(cBuf.capacity() * 2); + newBuf.put(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); + cBuf = newBuf; + ThreadLocalCache.charBuffer.set(cBuf); } } diff --git a/archive/src/main/java/java/util/jar/JarEntry.java b/archive/src/main/java/java/util/jar/JarEntry.java index b8dabee..869e4b4 100644 --- a/archive/src/main/java/java/util/jar/JarEntry.java +++ b/archive/src/main/java/java/util/jar/JarEntry.java @@ -33,27 +33,27 @@ import javax.security.auth.x500.X500Principal; /** * Represents a single file in a JAR archive together with the manifest * attributes and digital signatures associated with it. - * - * @since Android 1.0 + * + * @see JarFile + * @see JarInputStream */ public class JarEntry extends ZipEntry { private Attributes attributes; JarFile parentJar; - + CodeSigner signers[]; // Cached factory used to build CertPath-s in <code>getCodeSigners()</code>. private CertificateFactory factory; - private boolean isFactoryChecked = false; + private boolean isFactoryChecked = false; /** * Creates a new {@code JarEntry} named name. * * @param name * The name of the new {@code JarEntry}. - * @since Android 1.0 */ public JarEntry(String name) { super(name); @@ -64,7 +64,6 @@ public class JarEntry extends ZipEntry { * * @param entry * The ZipEntry to obtain values from. - * @since Android 1.0 */ public JarEntry(ZipEntry entry) { super(entry); @@ -78,7 +77,6 @@ public class JarEntry extends ZipEntry { * @exception IOException * If an error occurs obtaining the {@code Attributes}. * @see Attributes - * @since Android 1.0 */ public Attributes getAttributes() throws IOException { if (attributes != null || parentJar == null) { @@ -99,7 +97,6 @@ public class JarEntry extends ZipEntry { * * @return the certificate for this entry. * @see java.security.cert.Certificate - * @since Android 1.0 */ public Certificate[] getCertificates() { if (null == parentJar) { @@ -122,12 +119,11 @@ public class JarEntry extends ZipEntry { * * @param je * The {@code JarEntry} to obtain values from. - * @since Android 1.0 */ public JarEntry(JarEntry je) { super(je); parentJar = je.parentJar; - attributes = je.attributes; + attributes = je.attributes; signers = je.signers; } @@ -139,7 +135,6 @@ public class JarEntry extends ZipEntry { * * @return the code signers for the JAR entry. * @see CodeSigner - * @since Android 1.0 */ public CodeSigner[] getCodeSigners() { if (null == signers) { @@ -155,7 +150,7 @@ public class JarEntry extends ZipEntry { } private CodeSigner[] getCodeSigners(Certificate[] certs) { - if(null == certs) { + if (null == certs) { return null; } diff --git a/archive/src/main/java/java/util/jar/JarException.java b/archive/src/main/java/java/util/jar/JarException.java index f18d639..d6943c4 100644 --- a/archive/src/main/java/java/util/jar/JarException.java +++ b/archive/src/main/java/java/util/jar/JarException.java @@ -22,8 +22,6 @@ import java.util.zip.ZipException; /** * This runtime exception is thrown when a problem occurs while reading a JAR * file. - * - * @since Android 1.0 */ public class JarException extends ZipException { @@ -31,8 +29,6 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance. - * - * @since Android 1.0 */ public JarException() { super(); @@ -41,10 +37,9 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public JarException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/jar/JarFile.java b/archive/src/main/java/java/util/jar/JarFile.java index 9af9056..d6e8339 100644 --- a/archive/src/main/java/java/util/jar/JarFile.java +++ b/archive/src/main/java/java/util/jar/JarFile.java @@ -29,7 +29,6 @@ import java.io.File; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.MessageDigest; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -39,17 +38,14 @@ import org.apache.harmony.archive.util.Util; /** * {@code JarFile} is used to read jar entries and their associated data from * jar files. - * + * * @see JarInputStream * @see JarEntry - * @since Android 1.0 */ public class JarFile extends ZipFile { /** * The MANIFEST file name. - * - * @since Android 1.0 */ public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$ @@ -70,62 +66,68 @@ public class JarFile extends ZipFile { private ZipEntry zipEntry; - private JarVerifier verifier; - private JarVerifier.VerifierEntry entry; - private MessageDigest digest; - - JarFileInputStream(InputStream is, ZipEntry ze, JarVerifier ver) { + JarFileInputStream(InputStream is, ZipEntry ze, + JarVerifier.VerifierEntry e) { super(is); - if (ver != null) { - zipEntry = ze; - verifier = ver; - count = zipEntry.getSize(); - entry = verifier.initEntry(ze.getName()); - if (entry != null) { - digest = entry.digest; - } - } + zipEntry = ze; + count = zipEntry.getSize(); + entry = e; } @Override public int read() throws IOException { - int r = super.read(); - if (entry != null) { + if (count > 0) { + int r = super.read(); if (r != -1) { - digest.update((byte) r); + entry.write(r); count--; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + entry.verify(); } + return r; + } else { + return -1; } - return r; } @Override public int read(byte[] buf, int off, int nbytes) throws IOException { - int r = super.read(buf, off, nbytes); - if (entry != null) { + if (count > 0) { + int r = super.read(buf, off, nbytes); if (r != -1) { int size = r; if (count < size) { size = (int) count; } - digest.update(buf, off, size); - count -= r; + entry.write(buf, off, size); + count -= size; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + entry.verify(); } + return r; + } else { + return -1; + } + } + + // BEGIN android-added + @Override + public int available() throws IOException { + if (count > 0) { + return super.available(); + } else { + return 0; } - return r; } + // END android-added @Override public long skip(long nbytes) throws IOException { @@ -146,12 +148,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file) throws IOException { this(file, true); @@ -159,14 +160,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @param verify * if this JAR file is signed whether it must be verified. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify) throws IOException { super(file); @@ -178,7 +178,7 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of file. - * + * * @param file * the JAR file as {@link File}. * @param verify @@ -188,7 +188,6 @@ public class JarFile extends ZipFile { * {@link ZipFile#OPEN_DELETE OPEN_DELETE}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify, int mode) throws IOException { super(file, mode); @@ -201,12 +200,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * filename. - * + * * @param filename * the file name referring to the JAR file. * @throws IOException * if file name cannot be opened for reading. - * @since Android 1.0 */ public JarFile(String filename) throws IOException { this(filename, true); @@ -216,14 +214,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * {@code filename}. - * + * * @param filename * the file name referring to the JAR file. * @param verify * if this JAR filed is signed whether it must be verified. * @throws IOException * If file cannot be opened or read. - * @since Android 1.0 */ public JarFile(String filename, boolean verify) throws IOException { super(filename); @@ -236,11 +233,10 @@ public class JarFile extends ZipFile { /** * Return an enumeration containing the {@code JarEntrys} contained in this * {@code JarFile}. - * + * * @return the {@code Enumeration} containing the JAR entries. * @throws IllegalStateException * if this {@code JarFile} is closed. - * @since Android 1.0 */ @Override public Enumeration<JarEntry> entries() { @@ -260,7 +256,7 @@ public class JarFile extends ZipFile { public JarEntry nextElement() { JarEntry je = new JarEntry(ze.nextElement()); - je.parentJar = jf; + je.parentJar = jf; return je; } } @@ -270,11 +266,10 @@ public class JarFile extends ZipFile { /** * Return the {@code JarEntry} specified by its name or {@code null} if no * such entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the JAR entry defined by the name. - * @since Android 1.0 */ public JarEntry getJarEntry(String name) { return (JarEntry) getEntry(name); @@ -302,14 +297,13 @@ public class JarFile extends ZipFile { /** * Returns the {@code Manifest} object associated with this {@code JarFile} * or {@code null} if no MANIFEST entry exists. - * + * * @return the MANIFEST. * @throws IOException * if an error occurs reading the MANIFEST file. * @throws IllegalStateException * if the jar file is closed. * @see Manifest - * @since Android 1.0 */ public Manifest getManifest() throws IOException { // BEGIN android-added @@ -358,10 +352,14 @@ public class JarFile extends ZipFile { if (verifier == null) { break; } - } else if (verifier != null && entryName.length() > dirLength - && (Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 3, ".SF", 0 ,3) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".DSA", 0 ,4) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".RSA", 0 ,4))){ //$NON-NLS-1$ + } else if (verifier != null + && entryName.length() > dirLength + && (Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 3, ".SF", 0, 3) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".DSA", 0, 4) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".RSA", 0, 4))) { //$NON-NLS-1$ signed = true; InputStream is = super.getInputStream(entry); // BEGIN android-modified @@ -379,13 +377,12 @@ public class JarFile extends ZipFile { /** * Return an {@code InputStream} for reading the decompressed contents of * ZIP entry. - * + * * @param ze * the ZIP entry to be read. * @return the input stream to read from. * @throws IOException * if an error occurred while creating the input stream. - * @since Android 1.0 */ @Override public InputStream getInputStream(ZipEntry ze) throws IOException { @@ -395,8 +392,7 @@ public class JarFile extends ZipFile { if (verifier != null) { verifier.setManifest(getManifest()); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest.getMainAttributesEnd(); } if (verifier.readCertificates()) { verifier.removeMetaEntries(); @@ -412,19 +408,24 @@ public class JarFile extends ZipFile { if (in == null) { return null; } - return new JarFileInputStream(in, ze, ze.getSize() >= 0 ? verifier - : null); + if (verifier == null || ze.getSize() == -1) { + return in; + } + JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName()); + if (entry == null) { + return in; + } + return new JarFileInputStream(in, ze, entry); } /** * Return the {@code JarEntry} specified by name or {@code null} if no such * entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the ZIP entry extracted. - * @since Android 1.0 - */ + */ @Override public ZipEntry getEntry(String name) { ZipEntry ze = super.getEntry(name); @@ -432,16 +433,16 @@ public class JarFile extends ZipFile { return ze; } JarEntry je = new JarEntry(ze); - je.parentJar = this; + je.parentJar = this; return je; } // BEGIN android-modified private ZipEntry[] getMetaEntriesImpl(byte[] buf) { int n = 0; - + List<ZipEntry> list = new ArrayList<ZipEntry>(); - + Enumeration<? extends ZipEntry> allEntries = entries(); while (allEntries.hasMoreElements()) { ZipEntry ze = allEntries.nextElement(); @@ -463,10 +464,9 @@ public class JarFile extends ZipFile { // BEGIN android-added /** * Closes this {@code JarFile}. - * + * * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarInputStream.java b/archive/src/main/java/java/util/jar/JarInputStream.java index ef353ab..c803183 100644 --- a/archive/src/main/java/java/util/jar/JarInputStream.java +++ b/archive/src/main/java/java/util/jar/JarInputStream.java @@ -24,14 +24,13 @@ import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; /** * The input stream from which the JAR file to be read may be fetched. It is * used like the {@code ZipInputStream}. - * + * * @see ZipInputStream - * @since Android 1.0 */ public class JarInputStream extends ZipInputStream { @@ -51,7 +50,7 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @param verify @@ -59,7 +58,6 @@ public class JarInputStream extends ZipInputStream { * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream, boolean verify) throws IOException { @@ -84,8 +82,8 @@ public class JarInputStream extends ZipInputStream { if (verify) { verifier.setManifest(manifest); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest + .getMainAttributesEnd(); } } @@ -103,13 +101,12 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream) throws IOException { this(stream, true); @@ -120,7 +117,6 @@ public class JarInputStream extends ZipInputStream { * JarInputStream} or {@code null} if no manifest entry exists. * * @return the MANIFEST specifying the contents of the JAR file. - * @since Android 1.0 */ public Manifest getManifest() { return manifest; @@ -133,7 +129,6 @@ public class JarInputStream extends ZipInputStream { * @return the next JAR entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ public JarEntry getNextJarEntry() throws IOException { return (JarEntry) getNextEntry(); @@ -142,7 +137,7 @@ public class JarInputStream extends ZipInputStream { /** * Reads up to {@code length} of decompressed data and stores it in * {@code buffer} starting at {@code offset}. - * + * * @param buffer * Buffer to store into * @param offset @@ -152,7 +147,6 @@ public class JarInputStream extends ZipInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -175,9 +169,7 @@ public class JarInputStream extends ZipInputStream { throw e; } } else { - verifier.verifySignatures( - (JarVerifier.VerifierEntry) verStream, - jarEntry); + ((JarVerifier.VerifierEntry) verStream).verify(); } } } else { @@ -194,7 +186,6 @@ public class JarInputStream extends ZipInputStream { * @return the next extracted ZIP entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ @Override public ZipEntry getNextEntry() throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarOutputStream.java b/archive/src/main/java/java/util/jar/JarOutputStream.java index 640f4ef..e901d87 100644 --- a/archive/src/main/java/java/util/jar/JarOutputStream.java +++ b/archive/src/main/java/java/util/jar/JarOutputStream.java @@ -25,8 +25,6 @@ import java.util.zip.ZipOutputStream; /** * The {@code JarOutputStream} is used to write data in the {@code JarFile} * format to an arbitrary output stream - * - * @since Android 1.0 */ public class JarOutputStream extends ZipOutputStream { @@ -79,7 +77,6 @@ public class JarOutputStream extends ZipOutputStream { * @throws IOException * if an error occurs writing to the entry. * @see ZipEntry - * @since Android 1.0 */ @Override public void putNextEntry(ZipEntry ze) throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarVerifier.java b/archive/src/main/java/java/util/jar/JarVerifier.java index b9173f2..6c1ee93 100644 --- a/archive/src/main/java/java/util/jar/JarVerifier.java +++ b/archive/src/main/java/java/util/jar/JarVerifier.java @@ -31,13 +31,12 @@ import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; -import java.util.zip.ZipEntry; import org.apache.harmony.archive.internal.nls.Messages; import org.apache.harmony.luni.util.Base64; import org.apache.harmony.security.utils.JarUtils; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; // BEGIN android-added import org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK; @@ -65,65 +64,81 @@ class JarVerifier { private HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(5); - private final Hashtable<String, HashMap<String, Attributes>> signatures = - new Hashtable<String, HashMap<String, Attributes>>(5); + private final Hashtable<String, HashMap<String, Attributes>> signatures = new Hashtable<String, HashMap<String, Attributes>>( + 5); - private final Hashtable<String, Certificate[]> certificates = - new Hashtable<String, Certificate[]>(5); + private final Hashtable<String, Certificate[]> certificates = new Hashtable<String, Certificate[]>( + 5); - private final Hashtable<String, Certificate[]> verifiedEntries = - new Hashtable<String, Certificate[]>(); + private final Hashtable<String, Certificate[]> verifiedEntries = new Hashtable<String, Certificate[]>(); - byte[] mainAttributesChunk; + int mainAttributesEnd; - // BEGIN android-added - private static long measureCount = 0; - - private static long averageTime = 0; - // END android-added - /** - * TODO Type description + * Stores and a hash and a message digest and verifies that massage digest + * matches the hash. */ - static class VerifierEntry extends OutputStream { + class VerifierEntry extends OutputStream { - MessageDigest digest; + private String name; - byte[] hash; + private MessageDigest digest; - Certificate[] certificates; + private byte[] hash; - VerifierEntry(MessageDigest digest, byte[] hash, + private Certificate[] certificates; + + VerifierEntry(String name, MessageDigest digest, byte[] hash, Certificate[] certificates) { + this.name = name; this.digest = digest; this.hash = hash; this.certificates = certificates; } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(int) + /** + * Updates a digest with one byte. */ @Override public void write(int value) { digest.update((byte) value); } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(byte[], int, int) + /** + * Updates a digest with byte array. */ @Override public void write(byte[] buf, int off, int nbytes) { digest.update(buf, off, nbytes); } + + /** + * Verifies that the digests stored in the manifest match the decrypted + * digests from the .SF file. This indicates the validity of the + * signing, not the integrity of the file, as it's digest must be + * calculated and verified when its contents are read. + * + * @throws SecurityException + * if the digest value stored in the manifest does <i>not</i> + * agree with the decrypted digest as recovered from the + * <code>.SF</code> file. + * @see #initEntry(String) + */ + void verify() { + byte[] d = digest.digest(); + if (!MessageDigest.isEqual(d, Base64.decode(hash))) { + throw new SecurityException(Messages.getString( + "archive.32", new Object[] { //$NON-NLS-1$ + JarFile.MANIFEST_NAME, name, jarName })); + } + verifiedEntries.put(name, certificates); + } + } /** * Constructs and returns a new instance of {@code JarVerifier}. - * + * * @param name * the name of the JAR file being verified. */ @@ -136,13 +151,12 @@ class JarVerifier { * stream. This method constructs and returns a new {@link VerifierEntry} * which contains the certificates used to sign the entry and its hash value * as specified in the JAR MANIFEST format. - * + * * @param name * the name of an entry in a JAR file which is <b>not</b> in the * {@code META-INF} directory. * @return a new instance of {@link VerifierEntry} which can be used by * callers as an {@link OutputStream}. - * @since Android 1.0 */ VerifierEntry initEntry(String name) { // If no manifest is present by the time an entry is found, @@ -159,8 +173,8 @@ class JarVerifier { } Vector<Certificate> certs = new Vector<Certificate>(); - Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = - signatures.entrySet().iterator(); + Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures + .entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); HashMap<String, Attributes> hm = entry.getValue(); @@ -197,18 +211,18 @@ class JarVerifier { } byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } try { // BEGIN android-changed - return new VerifierEntry(OpenSSLMessageDigestJDK.getInstance(algorithm), + return new VerifierEntry(name, OpenSSLMessageDigestJDK.getInstance(algorithm), hashBytes, certificatesArray); // END android-changed } catch (NoSuchAlgorithmException e) { - // Ignored + // ignored } } return null; @@ -219,7 +233,7 @@ class JarVerifier { * entry in the {@code META-INF} directory including the manifest * file itself. Files associated with the signing of a JAR would also be * added to this collection. - * + * * @param name * the name of the file located in the {@code META-INF} * directory. @@ -234,7 +248,7 @@ class JarVerifier { /** * If the associated JAR file is signed, check on the validity of all of the * known signatures. - * + * * @return {@code true} if the associated JAR is signed and an internal * check verifies the validity of the signature(s). {@code false} if * the associated JAR file has no entries at all in its {@code @@ -243,12 +257,10 @@ class JarVerifier { * <p> * Will also return {@code true} if the JAR file is <i>not</i> * signed. - * </p> * @throws SecurityException * if the JAR file is signed and it is determined that a * signature block file contains an invalid signature for the * corresponding signature file. - * @since Android 1.0 */ synchronized boolean readCertificates() { if (metaEntries == null) { @@ -281,6 +293,12 @@ class JarVerifier { return; } + byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); + // Manifest entry is required for any verifications. + if (manifest == null) { + return; + } + byte[] sBlockBytes = metaEntries.get(certFile); try { Certificate[] signerCertChain = JarUtils.verifySignature( @@ -288,7 +306,7 @@ class JarVerifier { new ByteArrayInputStream(sBlockBytes)); /* * Recursive call in loading security provider related class which - * is in a signed JAR. + * is in a signed JAR. */ if (null == metaEntries) { return; @@ -299,74 +317,70 @@ class JarVerifier { } catch (IOException e) { return; } catch (GeneralSecurityException e) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } // Verify manifest hash in .sf file Attributes attributes = new Attributes(); - HashMap<String, Attributes> hm = new HashMap<String, Attributes>(); + HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); try { - new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm, - null, "Signature-Version"); //$NON-NLS-1$ + InitManifest im = new InitManifest(sfBytes, attributes, Attributes.Name.SIGNATURE_VERSION); + im.initEntries(entries, null); } catch (IOException e) { return; } boolean createdBySigntool = false; - String createdByValue = attributes.getValue("Created-By"); //$NON-NLS-1$ - if (createdByValue != null) { - createdBySigntool = createdByValue.indexOf("signtool") != -1; //$NON-NLS-1$ + String createdBy = attributes.getValue("Created-By"); //$NON-NLS-1$ + if (createdBy != null) { + createdBySigntool = createdBy.indexOf("signtool") != -1; //$NON-NLS-1$ } // Use .SF to verify the mainAttributes of the manifest // If there is no -Digest-Manifest-Main-Attributes entry in .SF // file, such as those created before java 1.5, then we ignore // such verification. - // FIXME: The meaning of createdBySigntool - if (mainAttributesChunk != null && !createdBySigntool) { + if (mainAttributesEnd > 0 && !createdBySigntool) { String digestAttribute = "-Digest-Manifest-Main-Attributes"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, mainAttributesChunk, - false, true)) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + if (!verify(attributes, digestAttribute, manifest, 0, + mainAttributesEnd, false, true)) { + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } } - byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); - if (manifest == null) { - return; - } - // Use .SF to verify the whole manifest + // Use .SF to verify the whole manifest. String digestAttribute = createdBySigntool ? "-Digest" //$NON-NLS-1$ : "-Digest-Manifest"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, manifest, false, false)) { - Iterator<Map.Entry<String, Attributes>> it = hm.entrySet() + if (!verify(attributes, digestAttribute, manifest, 0, manifest.length, + false, false)) { + Iterator<Map.Entry<String, Attributes>> it = entries.entrySet() .iterator(); while (it.hasNext()) { Map.Entry<String, Attributes> entry = it.next(); - byte[] chunk = man.getChunk(entry.getKey()); + Manifest.Chunk chunk = man.getChunk(entry.getKey()); if (chunk == null) { return; } - if (!verify(entry.getValue(), "-Digest", chunk, //$NON-NLS-1$ - createdBySigntool, false)) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException( - Messages.getString("archive.31", //$NON-NLS-1$ - new Object[] { signatureFile, entry.getKey(), jarName })); + if (!verify(entry.getValue(), "-Digest", manifest, //$NON-NLS-1$ + chunk.start, chunk.end, createdBySigntool, false)) { + throw new SecurityException(Messages.getString( + "archive.32", //$NON-NLS-1$ + new Object[] { signatureFile, entry.getKey(), + jarName })); } } } metaEntries.put(signatureFile, null); - signatures.put(signatureFile, hm); + signatures.put(signatureFile, entries); } /** * Associate this verifier with the specified {@link Manifest} object. - * + * * @param mf * a {@code java.util.jar.Manifest} object. */ @@ -375,36 +389,9 @@ class JarVerifier { } /** - * Verifies that the digests stored in the manifest match the decrypted - * digests from the .SF file. This indicates the validity of the signing, - * not the integrity of the file, as it's digest must be calculated and - * verified when its contents are read. - * - * @param entry - * the {@link VerifierEntry} associated with the specified - * {@code zipEntry}. - * @param zipEntry - * an entry in the JAR file - * @throws SecurityException - * if the digest value stored in the manifest does <i>not</i> - * agree with the decrypted digest as recovered from the - * {@code .SF} file. - * @see #initEntry(String) - */ - void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) { - byte[] digest = entry.digest.digest(); - if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException(Messages.getString("archive.31", new Object[] { //$NON-NLS-1$ - JarFile.MANIFEST_NAME, zipEntry.getName(), jarName })); - } - verifiedEntries.put(zipEntry.getName(), entry.certificates); - } - - /** - * Returns a {@code boolean} indication of whether or not the - * associated JAR file is signed. - * + * Returns a <code>boolean</code> indication of whether or not the + * associated jar file is signed. + * * @return {@code true} if the JAR is signed, {@code false} * otherwise. */ @@ -413,7 +400,7 @@ class JarVerifier { } private boolean verify(Attributes attributes, String entry, byte[] data, - boolean ignoreSecondEndline, boolean ignorable) { + int start, int end, boolean ignoreSecondEndline, boolean ignorable) { String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$ if (algorithms == null) { algorithms = "SHA SHA1"; //$NON-NLS-1$ @@ -434,16 +421,16 @@ class JarVerifier { } catch (NoSuchAlgorithmException e) { continue; } - if (ignoreSecondEndline && data[data.length - 1] == '\n' - && data[data.length - 2] == '\n') { - md.update(data, 0, data.length - 1); + if (ignoreSecondEndline && data[end - 1] == '\n' + && data[end - 2] == '\n') { + md.update(data, start, end - 1 - start); } else { - md.update(data, 0, data.length); + md.update(data, start, end - start); } byte[] b = md.digest(); byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } @@ -456,7 +443,7 @@ class JarVerifier { * Returns all of the {@link java.security.cert.Certificate} instances that * were used to verify the signature on the JAR entry called * {@code name}. - * + * * @param name * the name of a JAR entry. * @return an array of {@link java.security.cert.Certificate}. @@ -472,7 +459,7 @@ class JarVerifier { /** * Remove all entries from the internal collection of data held about each * JAR entry in the {@code META-INF} directory. - * + * * @see #addMetaEntry(String, byte[]) */ void removeMetaEntries() { @@ -483,7 +470,7 @@ class JarVerifier { * Returns a {@code Vector} of all of the * {@link java.security.cert.Certificate}s that are associated with the * signing of the named signature file. - * + * * @param signatureFileName * the name of a signature file. * @param certificates diff --git a/archive/src/main/java/java/util/jar/Manifest.java b/archive/src/main/java/java/util/jar/Manifest.java index 3b0d89a..b28f3fb 100644 --- a/archive/src/main/java/java/util/jar/Manifest.java +++ b/archive/src/main/java/java/util/jar/Manifest.java @@ -17,47 +17,65 @@ package java.util.jar; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.security.AccessController; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import org.apache.harmony.luni.util.PriviAction; +import org.apache.harmony.archive.internal.nls.Messages; +import org.apache.harmony.luni.util.InputStreamExposer; +import org.apache.harmony.luni.util.ThreadLocalCache; /** * The {@code Manifest} class is used to obtain attribute information for a * {@code JarFile} and its entries. - * - * @since Android 1.0 */ public class Manifest implements Cloneable { - private static final int LINE_LENGTH_LIMIT = 70; + static final int LINE_LENGTH_LIMIT = 72; private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' }; - private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name"); //$NON-NLS-1$ + private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' }; + + private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name( + "Name"); //$NON-NLS-1$ private Attributes mainAttributes = new Attributes(); - private HashMap<String, Attributes> entryAttributes = new HashMap<String, Attributes>(); + private HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); + + static class Chunk { + int start; + int end; + + Chunk(int start, int end) { + this.start = start; + this.end = end; + } + } + + private HashMap<String, Chunk> chunks; - private HashMap<String, byte[]> chunks; + /** + * Manifest bytes are used for delayed entry parsing. + */ + private InitManifest im; /** - * The data chunk of main attributes in the manifest is needed in + * The end of the main attributes section in the manifest is needed in * verification. */ - private byte[] mainAttributesChunk; + private int mainEnd; /** * Creates a new {@code Manifest} instance. - * - * @since Android 1.0 */ public Manifest() { super(); @@ -66,12 +84,11 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance using the attributes obtained * from the input stream. - * + * * @param is * {@code InputStream} to parse for attributes. * @throws IOException * if an IO error occurs while creating this {@code Manifest} - * @since Android 1.0 */ public Manifest(InputStream is) throws IOException { super(); @@ -81,20 +98,20 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance. The new instance will have the * same attributes as those found in the parameter {@code Manifest}. - * + * * @param man * {@code Manifest} instance to obtain attributes from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Manifest(Manifest man) { mainAttributes = (Attributes) man.mainAttributes.clone(); - entryAttributes = (HashMap<String, Attributes>) man.entryAttributes.clone(); + entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man + .getEntries()).clone(); } Manifest(InputStream is, boolean readChunks) throws IOException { if (readChunks) { - chunks = new HashMap<String, byte[]>(); + chunks = new HashMap<String, Chunk>(); } read(is); } @@ -102,23 +119,21 @@ public class Manifest implements Cloneable { /** * Resets the both the main attributes as well as the entry attributes * associated with this {@code Manifest}. - * - * @since Android 1.0 */ public void clear() { - entryAttributes.clear(); + im = null; + entries.clear(); mainAttributes.clear(); } /** * Returns the {@code Attributes} associated with the parameter entry * {@code name}. - * + * * @param name * the name of the entry to obtain {@code Attributes} from. * @return the Attributes for the entry or {@code null} if the entry does * not exist. - * @since Android 1.0 */ public Attributes getAttributes(String name) { return getEntries().get(name); @@ -127,20 +142,31 @@ public class Manifest implements Cloneable { /** * Returns a map containing the {@code Attributes} for each entry in the * {@code Manifest}. - * + * * @return the map of entry attributes. - * @since Android 1.0 */ public Map<String, Attributes> getEntries() { - return entryAttributes; + initEntries(); + return entries; + } + + private void initEntries() { + if (im == null) { + return; + } + // try { + // im.initEntries(entries, chunks); + // } catch (IOException ioe) { + // throw new RuntimeException(ioe); + // } + // im = null; } /** * Returns the main {@code Attributes} of the {@code JarFile}. - * + * * @return main {@code Attributes} associated with the source {@code * JarFile}. - * @since Android 1.0 */ public Attributes getMainAttributes() { return mainAttributes; @@ -149,9 +175,8 @@ public class Manifest implements Cloneable { /** * Creates a copy of this {@code Manifest}. The returned {@code Manifest} * will equal the {@code Manifest} from which it was cloned. - * + * * @return a copy of this instance. - * @since Android 1.0 */ @Override public Object clone() { @@ -161,12 +186,11 @@ public class Manifest implements Cloneable { /** * Writes out the attribute information of the receiver to the specified * {@code OutputStream}. - * + * * @param os * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ public void write(OutputStream os) throws IOException { write(this, os); @@ -175,39 +199,98 @@ public class Manifest implements Cloneable { /** * Constructs a new {@code Manifest} instance obtaining attribute * information from the specified input stream. - * + * * @param is * The {@code InputStream} to read from. * @throws IOException * If an error occurs reading the {@code Manifest}. - * @since Android 1.0 */ public void read(InputStream is) throws IOException { - InitManifest initManifest = new InitManifest(is, mainAttributes, entryAttributes, - chunks, null); - mainAttributesChunk = initManifest.getMainAttributesChunk(); + byte[] buf; + // Try to read get a reference to the bytes directly + try { + buf = InputStreamExposer.expose(is); + } catch (UnsupportedOperationException uoe) { + buf = readFully(is); + } + + if (buf.length == 0) { + return; + } + + // a workaround for HARMONY-5662 + // replace EOF and NUL with another new line + // which does not trigger an error + byte b = buf[buf.length - 1]; + if (0 == b || 26 == b) { + buf[buf.length - 1] = '\n'; + } + + // Attributes.Name.MANIFEST_VERSION is not used for + // the second parameter for RI compatibility + im = new InitManifest(buf, mainAttributes, null); + mainEnd = im.getPos(); + // FIXME + im.initEntries(entries, chunks); + im = null; + } + + /* + * Helper to read the entire contents of the manifest from the + * given input stream. Usually we can do this in a single read + * but we need to account for 'infinite' streams, by ensuring we + * have a line feed within a reasonable number of characters. + */ + private byte[] readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + + while (true) { + int count = is.read(buffer); + if (count == -1) { + // TODO: Do we need to copy this, or can we live with junk at the end? + return baos.toByteArray(); + } + baos.write(buffer, 0, count); + + if (!containsLine(buffer, count)) { + throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ + } + } + } + + /* + * Check to see if the buffer contains a newline or carriage + * return character within the first 'length' bytes. Used to + * check the validity of the manifest input stream. + */ + private boolean containsLine(byte[] buffer, int length) { + for (int i = 0; i < length; i++) { + if (buffer[i] == 0x0A || buffer[i] == 0x0D) { + return true; + } + } + return false; } /** * Returns the hash code for this instance. - * + * * @return this {@code Manifest}'s hashCode. - * @since Android 1.0 */ @Override public int hashCode() { - return mainAttributes.hashCode() ^ entryAttributes.hashCode(); + return mainAttributes.hashCode() ^ getEntries().hashCode(); } /** * Determines if the receiver is equal to the parameter object. Two {@code * Manifest}s are equal if they have identical main attributes as well as * identical entry attributes. - * + * * @param o * the object to compare against. * @return {@code true} if the manifests are equal, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -220,10 +303,10 @@ public class Manifest implements Cloneable { if (!mainAttributes.equals(((Manifest) o).mainAttributes)) { return false; } - return entryAttributes.equals(((Manifest) o).entryAttributes); + return getEntries().equals(((Manifest) o).getEntries()); } - byte[] getChunk(String name) { + Chunk getChunk(String name) { return chunks.get(name); } @@ -231,114 +314,84 @@ public class Manifest implements Cloneable { chunks = null; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + int getMainAttributesEnd() { + return mainEnd; } /** * Writes out the attribute information of the specified manifest to the * specified {@code OutputStream} - * + * * @param manifest * the manifest to write out. * @param out * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ static void write(Manifest manifest, OutputStream out) throws IOException { - Charset charset = null; - String encoding = AccessController.doPrivileged(new PriviAction<String>( - "manifest.write.encoding")); //$NON-NLS-1$ - if (encoding != null) { - if (encoding.length() == 0) { - encoding = "UTF8"; //$NON-NLS-1$ - } - charset = Charset.forName(encoding); - } - String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION); + CharsetEncoder encoder = ThreadLocalCache.utf8Encoder.get(); + ByteBuffer buffer = ThreadLocalCache.byteBuffer.get(); + + String version = manifest.mainAttributes + .getValue(Attributes.Name.MANIFEST_VERSION); if (version != null) { - writeEntry(out, charset, Attributes.Name.MANIFEST_VERSION, version); + writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder, + buffer); Iterator<?> entries = manifest.mainAttributes.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); if (!name.equals(Attributes.Name.MANIFEST_VERSION)) { - writeEntry(out, charset, name, manifest.mainAttributes.getValue(name)); + writeEntry(out, name, manifest.mainAttributes + .getValue(name), encoder, buffer); } } } out.write(LINE_SEPARATOR); - Iterator<String> i = manifest.entryAttributes.keySet().iterator(); + Iterator<String> i = manifest.getEntries().keySet().iterator(); while (i.hasNext()) { String key = i.next(); - writeEntry(out, charset, NAME_ATTRIBUTE, key); - Attributes attrib = manifest.entryAttributes.get(key); + writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer); + Attributes attrib = manifest.entries.get(key); Iterator<?> entries = attrib.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); - writeEntry(out, charset, name, attrib.getValue(name)); + writeEntry(out, name, attrib.getValue(name), encoder, buffer); } out.write(LINE_SEPARATOR); } } - private static void writeEntry(OutputStream os, Charset charset, Attributes.Name name, - String value) throws IOException { - int offset = 0; - int limit = LINE_LENGTH_LIMIT; - byte[] out = (name.toString() + ": ").getBytes("ISO8859_1"); //$NON-NLS-1$ //$NON-NLS-2$ - if (out.length > limit) { - while (out.length - offset >= limit) { - int len = out.length - offset; - if (len > limit) { - len = limit; - } - if (offset > 0) { - os.write(' '); - } - os.write(out, offset, len); - os.write(LINE_SEPARATOR); - offset += len; - limit = LINE_LENGTH_LIMIT - 1; - } - } - int size = out.length - offset; - final byte[] outBuf = new byte[LINE_LENGTH_LIMIT]; - System.arraycopy(out, offset, outBuf, 0, size); - for (int i = 0; i < value.length(); i++) { - char[] oneChar = new char[1]; - oneChar[0] = value.charAt(i); - byte[] buf; - if (oneChar[0] < 128 || charset == null) { - byte[] oneByte = new byte[1]; - oneByte[0] = (byte) oneChar[0]; - buf = oneByte; - } else { - buf = charset.encode(CharBuffer.wrap(oneChar, 0, 1)).array(); - } - if (size + buf.length > limit) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); - } - os.write(outBuf, 0, size); - os.write(LINE_SEPARATOR); - limit = LINE_LENGTH_LIMIT - 1; - size = 0; - } - if (buf.length == 1) { - outBuf[size] = buf[0]; - } else { - System.arraycopy(buf, 0, outBuf, size, buf.length); - } - size += buf.length; + private static void writeEntry(OutputStream os, Attributes.Name name, + String value, CharsetEncoder encoder, ByteBuffer bBuf) + throws IOException { + byte[] out = name.getBytes(); + if (out.length > LINE_LENGTH_LIMIT) { + throw new IOException(Messages.getString( + "archive.33", name, Integer.valueOf(LINE_LENGTH_LIMIT))); //$NON-NLS-1$ } - if (size > 0) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); + + os.write(out); + os.write(VALUE_SEPARATOR); + + encoder.reset(); + bBuf.clear().limit(LINE_LENGTH_LIMIT - out.length - 2); + + CharBuffer cBuf = CharBuffer.wrap(value); + CoderResult r; + + while (true) { + r = encoder.encode(cBuf, bBuf, true); + if (CoderResult.UNDERFLOW == r) { + r = encoder.flush(bBuf); } - os.write(outBuf, 0, size); + os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position()); os.write(LINE_SEPARATOR); + if (CoderResult.UNDERFLOW == r) { + break; + } + os.write(' '); + bBuf.clear().limit(LINE_LENGTH_LIMIT - 1); } } } diff --git a/archive/src/main/java/java/util/jar/Pack200.java b/archive/src/main/java/java/util/jar/Pack200.java index e0689e0..8145fa1 100644 --- a/archive/src/main/java/java/util/jar/Pack200.java +++ b/archive/src/main/java/java/util/jar/Pack200.java @@ -27,8 +27,6 @@ import java.util.SortedMap; /** * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}. - * - * @since Android 1.0 */ public abstract class Pack200 { @@ -39,8 +37,8 @@ public abstract class Pack200 { /** * Prevent this class from being instantiated. */ - private Pack200(){ - //do nothing + private Pack200() { + // do nothing } /** @@ -50,10 +48,8 @@ public abstract class Pack200 { * {@code 'java.util.jar.Pack200.Packer'}. If this system property is * defined an instance of the specified class is returned, otherwise the * system's default implementation is returned. - * </p> * * @return an instance of {@code Packer} - * @since Android 1.0 */ public static Pack200.Packer newPacker() { return (Packer) AccessController @@ -82,10 +78,8 @@ public abstract class Pack200 { * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system * property is defined an instance of the specified class is returned, * otherwise the system's default implementation is returned. - * </p> * * @return a instance of {@code Unpacker}. - * @since Android 1.0 */ public static Pack200.Unpacker newUnpacker() { return (Unpacker) AccessController @@ -107,150 +101,109 @@ public abstract class Pack200 { /** * The interface defining the API for converting a JAR file to an output * stream in the Pack200 format. - * - * @since Android 1.0 */ public static interface Packer { /** * the format of a class attribute name. - * - * @since Android 1.0 */ static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$ /** * the format of a code attribute name. - * - * @since Android 1.0 */ static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$ /** * the deflation hint to set in the output archive. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$ /** * the indicated amount of effort to use in compressing the archive. - * - * @since Android 1.0 */ static final String EFFORT = "pack.effort";//$NON-NLS-1$ /** * a String representation for {@code error}. - * - * @since Android 1.0 */ static final String ERROR = "error";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * the format of a field attribute name. - * - * @since Android 1.0 */ static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$ /** * a String representation for {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * decide if all elements shall transmit in their original order. - * - * @since Android 1.0 */ static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$ /** * a String representation for {@code latest}. - * - * @since Android 1.0 */ static final String LATEST = "latest";//$NON-NLS-1$ /** * the format of a method attribute name. - * - * @since Android 1.0 */ static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$ /** * if it shall attempt to determine the latest modification time if this * is set to {@code LATEST}. - * - * @since Android 1.0 */ static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$ /** * a String representation of {@code pass}. - * - * @since Android 1.0 */ static final String PASS = "pass";//$NON-NLS-1$ /** * the file that will not be compressed. - * - * @since Android 1.0 */ static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$ /** * packer progress as a percentage. - * - * @since Android 1.0 */ static final String PROGRESS = "pack.progress";//$NON-NLS-1$ /** * The number of bytes of each archive segment. - * - * @since Android 1.0 */ static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$ /** * a String representation of {@code strip}. - * - * @since Android 1.0 */ static final String STRIP = "strip";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * the action to take if an unknown attribute is encountered. - * - * @since Android 1.0 */ static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this packer. - * + * * @return the properties of the packer. - * @since Android 1.0 */ SortedMap<String, String> properties(); @@ -263,7 +216,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarFile in, OutputStream out) throws IOException; @@ -277,7 +229,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarInputStream in, OutputStream out) throws IOException; @@ -301,52 +252,39 @@ public abstract class Pack200 { /** * The interface defining the API for converting a packed stream in the * Pack200 format to a JAR file. - * - * @since Android 1.0 */ public static interface Unpacker { /** * The String indicating if the unpacker should ignore all transmitted * values,can be replaced by either {@code true} or {@code false}. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * a String representation of {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * the progress as a {@code percentage}. - * - * @since Android 1.0 */ static final String PROGRESS = "unpack.progress";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this unpacker. - * + * * @return the properties of unpacker. - * @since Android 1.0 */ SortedMap<String, String> properties(); @@ -359,7 +297,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(InputStream in, JarOutputStream out) throws IOException; @@ -373,7 +310,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(File in, JarOutputStream out) throws IOException; diff --git a/archive/src/main/java/java/util/zip/Adler32.java b/archive/src/main/java/java/util/zip/Adler32.java index 8eaa18e..a5f77d7 100644 --- a/archive/src/main/java/java/util/zip/Adler32.java +++ b/archive/src/main/java/java/util/zip/Adler32.java @@ -17,14 +17,12 @@ package java.util.zip; - /** * The Adler-32 class is used to compute the {@code Adler32} checksum from a set * of data. Compared to the CRC-32 algorithm it trades reliabilty for speed. * Refer to RFC 1950 for the specification. - * + * * @see CRC32 - * @since Android 1.0 */ public class Adler32 implements java.util.zip.Checksum { @@ -34,7 +32,6 @@ public class Adler32 implements java.util.zip.Checksum { * Returns the {@code Adler32} checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return adler; @@ -42,8 +39,6 @@ public class Adler32 implements java.util.zip.Checksum { /** * Reset this instance to its initial checksum. - * - * @since Android 1.0 */ public void reset() { adler = 1; @@ -55,7 +50,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param i * the byte to update checksum with. - * @since Android 1.0 */ public void update(int i) { adler = updateByteImpl(i, adler); @@ -66,7 +60,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param buf * bytes to update checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -85,7 +78,6 @@ public class Adler32 implements java.util.zip.Checksum { * @throws ArrayIndexOutOfBoundsException * if {@code offset > buf.length} or {@code nbytes} is negative * or {@code offset + nbytes > buf.length}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/archive/src/main/java/java/util/zip/CRC32.java b/archive/src/main/java/java/util/zip/CRC32.java index 36dc376..59e8f81 100644 --- a/archive/src/main/java/java/util/zip/CRC32.java +++ b/archive/src/main/java/java/util/zip/CRC32.java @@ -17,12 +17,9 @@ package java.util.zip; - /** * The CRC32 class is used to compute a CRC32 checksum from data provided as * input value. - * - * @since Android 1.0 */ public class CRC32 implements java.util.zip.Checksum { @@ -34,7 +31,6 @@ public class CRC32 implements java.util.zip.Checksum { * Returns the CRC32 checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return crc; @@ -42,8 +38,6 @@ public class CRC32 implements java.util.zip.Checksum { /** * Resets the CRC32 checksum to it initial state. - * - * @since Android 1.0 */ public void reset() { tbytes = crc = 0; @@ -52,10 +46,9 @@ public class CRC32 implements java.util.zip.Checksum { /** * Updates this checksum with the byte value provided as integer. - * + * * @param val * represents the byte to update the checksum. - * @since Android 1.0 */ public void update(int val) { crc = updateByteImpl((byte) val, crc); @@ -66,7 +59,6 @@ public class CRC32 implements java.util.zip.Checksum { * * @param buf * the buffer holding the data to update the checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -82,7 +74,6 @@ public class CRC32 implements java.util.zip.Checksum { * the offset in {@code buf} to obtain data from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/archive/src/main/java/java/util/zip/CheckedInputStream.java b/archive/src/main/java/java/util/zip/CheckedInputStream.java index 659125a..6513e66 100644 --- a/archive/src/main/java/java/util/zip/CheckedInputStream.java +++ b/archive/src/main/java/java/util/zip/CheckedInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.InputStream; @@ -26,8 +25,6 @@ import java.io.InputStream; * same time as the data, on which the checksum is computed, is read from a * stream. The purpose of this checksum is to establish data integrity, * comparing the computed checksum against a published checksum value. - * - * @since Android 1.0 */ public class CheckedInputStream extends java.io.FilterInputStream { @@ -42,7 +39,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * the input stream to calculate checksum from. * @param csum * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedInputStream(InputStream is, Checksum csum) { super(is); @@ -57,7 +53,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * otherwise. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -84,7 +79,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * end of the filtered stream while reading the data. * @throws IOException * if this stream is closed or some I/O error occurs. - * @since Android 1.0 */ @Override public int read(byte[] buf, int off, int nbytes) throws IOException { @@ -99,7 +93,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -114,7 +107,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * @throws IOException * if this stream is closed or another I/O error occurs. * @return the number of bytes skipped. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/CheckedOutputStream.java b/archive/src/main/java/java/util/zip/CheckedOutputStream.java index 9699492..08fe799 100644 --- a/archive/src/main/java/java/util/zip/CheckedOutputStream.java +++ b/archive/src/main/java/java/util/zip/CheckedOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; @@ -26,8 +25,6 @@ import java.io.OutputStream; * of all data written to a stream. The purpose of this checksum is to establish * data integrity, by publishing the checksum to other parties wanting to read * the non corrupted data. - * - * @since Android 1.0 */ public class CheckedOutputStream extends java.io.FilterOutputStream { @@ -42,7 +39,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the output stream to calculate checksum for. * @param cs * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedOutputStream(OutputStream os, Checksum cs) { super(os); @@ -53,7 +49,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -67,7 +62,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the data value to written to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(int val) throws IOException { @@ -88,7 +82,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * number of bytes to write to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(byte[] buf, int off, int nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/Checksum.java b/archive/src/main/java/java/util/zip/Checksum.java index 0405c08..901ff7a 100644 --- a/archive/src/main/java/java/util/zip/Checksum.java +++ b/archive/src/main/java/java/util/zip/Checksum.java @@ -20,8 +20,6 @@ package java.util.zip; /** * Holds information about a checksum which was computed with the methods * implementing a checksum algorithm. - * - * @since Android 1.0 */ public interface Checksum { @@ -29,37 +27,32 @@ public interface Checksum { * Returns the current calculated checksum value. * * @return the checksum. - * @since Android 1.0 */ public long getValue(); /** * Resets the checksum value applied before beginning calculations on a new * stream of data. - * - * @since Android 1.0 */ public void reset(); /** - * Updates the checksum value with the given byte. - * - * @param val - * the byte to update the checksum with. - * @since Android 1.0 - */ - public void update(int val); - - /** * Updates the checksum with the given bytes. - * + * * @param buf * the byte array from which to read the bytes. * @param off * the initial position in {@code buf} to read the bytes from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes); + + /** + * Updates the checksum value with the given byte. + * + * @param val + * the byte to update the checksum with. + */ + public void update(int val); } diff --git a/archive/src/main/java/java/util/zip/DataFormatException.java b/archive/src/main/java/java/util/zip/DataFormatException.java index 9ffe2ab..1e9c5a2 100644 --- a/archive/src/main/java/java/util/zip/DataFormatException.java +++ b/archive/src/main/java/java/util/zip/DataFormatException.java @@ -20,8 +20,6 @@ package java.util.zip; /** * {@code DataFormatException} is used to indicate an error in the format of a * particular data stream which is to be uncompressed. - * - * @since Android 1.0 */ public class DataFormatException extends Exception { @@ -29,8 +27,6 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance. - * - * @since Android 1.0 */ public DataFormatException() { super(); @@ -39,10 +35,9 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public DataFormatException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/zip/Deflater.java b/archive/src/main/java/java/util/zip/Deflater.java index f91f1ca..38771a8 100644 --- a/archive/src/main/java/java/util/zip/Deflater.java +++ b/archive/src/main/java/java/util/zip/Deflater.java @@ -17,6 +17,10 @@ package java.util.zip; +// BEGIN android-changed +// import org.apache.harmony.luni.platform.OSResourcesMonitor; +// END android-changed + /** * This class compresses data using the <i>DEFLATE</i> algorithm (see <a * href="http://www.gzip.org/algorithm.txt">specification</a>). @@ -24,83 +28,65 @@ package java.util.zip; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code DeflaterOutputStream} and its * descendants. - * </p> * <p> * The typical usage of a {@code Deflater} instance outside this package * consists of a specific call to one of its constructors before being passed to * an instance of {@code DeflaterOutputStream}. - * </p> - * + * * @see DeflaterOutputStream * @see Inflater - * @since Android 1.0 */ public class Deflater { /** * Upper bound for the compression level range. - * - * @since Android 1.0 */ public static final int BEST_COMPRESSION = 9; /** * Lower bound for compression level range. - * - * @since Android 1.0 */ public static final int BEST_SPEED = 1; - + /** * Usage of the default compression level. - * - * @since Android 1.0 */ public static final int DEFAULT_COMPRESSION = -1; - + /** * Default value for compression strategy. - * - * @since Android 1.0 */ public static final int DEFAULT_STRATEGY = 0; - + /** * Default value for compression method. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int FILTERED = 1; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int HUFFMAN_ONLY = 2; - + /** * Possible value for compression level. - * - * @since Android 1.0 */ public static final int NO_COMPRESSION = 0; private static final int Z_NO_FLUSH = 0; private static final int Z_FINISH = 4; - + // Fill in the JNI id caches private static native void oneTimeInitialization(); - - // A stub buffer used when deflate() called while inputBuffer has not been set. + + // A stub buffer used when deflate() called while inputBuffer has not been + // set. private static final byte[] STUB_INPUT_BUFFER = new byte[0]; static { @@ -120,21 +106,19 @@ public class Deflater { private byte[] inputBuffer; private int inRead; - + private int inLength; - + /** * Constructs a new {@code Deflater} instance with default compression * level. The strategy can be specified with {@link #setStrategy}, only. A * header is added to the output by default; use constructor {@code * Deflater(level, boolean)} if you need to omit the header. - * - * @since Android 1.0 */ public Deflater() { this(DEFAULT_COMPRESSION, false); } - + /** * Constructs a new {@code Deflater} instance with a specific compression * level. The strategy can be specified with {@code setStrategy}, only. A @@ -143,7 +127,6 @@ public class Deflater { * * @param level * the compression level in the range between 0 and 9. - * @since Android 1.0 */ public Deflater(int level) { this(level, false); @@ -159,7 +142,6 @@ public class Deflater { * the compression level in the range between 0 and 9. * @param noHeader * {@code true} indicates that no ZLIB header should be written. - * @since Android 1.0 */ public Deflater(int level, boolean noHeader) { super(); @@ -167,7 +149,8 @@ public class Deflater { throw new IllegalArgumentException(); } compressLevel = level; - streamHandle = createStream(compressLevel, strategy, noHeader); + streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy, + noHeader); } /** @@ -178,7 +161,6 @@ public class Deflater { * buffer to write compressed data to. * @return number of bytes of compressed data written to {@code buf}. * @see #deflate(byte[], int, int) - * @since Android 1.0 */ public int deflate(byte[] buf) { return deflate(buf, 0, buf.length); @@ -195,7 +177,6 @@ public class Deflater { * @param nbytes * maximum number of bytes of compressed data to be written. * @return the number of bytes of compressed data written to {@code buf}. - * @since Android 1.0 */ public synchronized int deflate(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -224,8 +205,6 @@ public class Deflater { * finalize()}, it can be called explicitly in order to free native * resources before the next GC cycle. After {@code end()} was called other * methods will typically throw an {@code IllegalStateException}. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -245,7 +224,6 @@ public class Deflater { * to it. * * @see #finished - * @since Android 1.0 */ public synchronized void finish() { flushParm = Z_FINISH; @@ -256,7 +234,6 @@ public class Deflater { * compressed. * * @return true if all data has been compressed, false otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -271,7 +248,6 @@ public class Deflater { * used. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -287,14 +263,13 @@ public class Deflater { * Returns the total number of bytes of input consumed by the {@code Deflater}. * * @return number of bytes of input read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalInImpl(streamHandle); + return (int) getTotalInImpl(streamHandle); } private synchronized native long getTotalInImpl(long handle); @@ -303,14 +278,13 @@ public class Deflater { * Returns the total number of compressed bytes output by this {@code Deflater}. * * @return number of compressed bytes output. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalOutImpl(streamHandle); + return (int) getTotalOutImpl(streamHandle); } private synchronized native long getTotalOutImpl(long handle); @@ -327,7 +301,6 @@ public class Deflater { * @see #finished() * @see #setInput(byte[]) * @see #setInput(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsInput() { if (inputBuffer == null) { @@ -343,7 +316,6 @@ public class Deflater { * {@code true} if the {@code Deflater} is to be reused. * * @see #finished - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -367,7 +339,6 @@ public class Deflater { * @param buf * the buffer containing the dictionary data bytes. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -378,7 +349,7 @@ public class Deflater { * setDictionary() can only be called if this {@code Deflater} supports the writing * of ZLIB headers. This is the default behaviour but can be overridden * using {@code Deflater(int, boolean)}. - * + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -386,7 +357,6 @@ public class Deflater { * @param nbytes * the length of the data. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -410,7 +380,6 @@ public class Deflater { * * @param buf * the buffer. - * @since Android 1.0 */ public void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -427,7 +396,6 @@ public class Deflater { * the offset of the data. * @param nbytes * the length of the data. - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -463,7 +431,6 @@ public class Deflater { * compression level to use * @exception IllegalArgumentException * If the compression level is invalid. - * @since Android 1.0 */ public synchronized void setLevel(int level) { if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { @@ -485,7 +452,6 @@ public class Deflater { * @exception IllegalArgumentException * If the strategy specified is not one of FILTERED, * HUFFMAN_ONLY or DEFAULT_STRATEGY. - * @since Android 1.0 */ public synchronized void setStrategy(int strategy) { if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) { @@ -496,14 +462,14 @@ public class Deflater { } this.strategy = strategy; } - + /** * Returns a long int of total number of bytes read by the {@code Deflater}. This * method performs the same as {@code getTotalIn} except it returns a long value * instead of an integer * + * @see #getTotalIn() * @return total number of bytes read by {@code Deflater}. - * @since Android 1.0 */ public synchronized long getBytesRead() { // Throw NPE here @@ -518,8 +484,8 @@ public class Deflater { * method performs the same as {@code getTotalOut} except it returns a long * value instead of an integer * + * @see #getTotalOut() * @return bytes exactly write by {@code Deflater} - * @since Android 1.0 */ public synchronized long getBytesWritten() { // Throw NPE here @@ -529,5 +495,13 @@ public class Deflater { return getTotalOutImpl(streamHandle); } + private long createStreamWithMemoryEnsurance(int level, int strategy1, + boolean noHeader1) { + // BEGIN android-changed + // OSResourcesMonitor.ensurePhysicalMemoryCapacity(); + // END android-changed + return createStream(level, strategy1, noHeader1); + } + private native long createStream(int level, int strategy1, boolean noHeader1); } diff --git a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java index 773e4c4..03769fb 100644 --- a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java +++ b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -28,24 +27,19 @@ import org.apache.harmony.archive.internal.nls.Messages; * This class provides an implementation of {@code FilterOutputStream} that * compresses data using the <i>DEFLATE</i> algorithm. Basically it wraps the * {@code Deflater} class and takes care of the buffering. - * + * * @see Deflater - * @since Android 1.0 */ public class DeflaterOutputStream extends FilterOutputStream { static final int BUF_SIZE = 512; /** * The buffer for the data to be written to. - * - * @since Android 1.0 */ protected byte[] buf; /** * The deflater used. - * - * @since Android 1.0 */ protected Deflater def; @@ -61,7 +55,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @param def * is the specific {@code Deflater} that is used to compress * data. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def) { this(os, def, BUF_SIZE); @@ -76,7 +69,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @param os * is the OutputStream where to write the compressed data to. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os) { this(os, new Deflater()); @@ -94,7 +86,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * data. * @param bsize * is the size to be used for the internal buffer. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def, int bsize) { super(os); @@ -114,7 +105,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs during deflation. - * @since Android 1.0 */ protected void deflate() throws IOException { int x = 0; @@ -132,7 +122,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @throws IOException * If an error occurs while closing the data compression * process. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -149,7 +138,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs. - * @since Android 1.0 */ public void finish() throws IOException { if (done) { @@ -186,7 +174,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * the number of bytes of data to read from the buffer. * @throws IOException * If an error occurs during writing. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/GZIPInputStream.java b/archive/src/main/java/java/util/zip/GZIPInputStream.java index fc70d62..bb84f5b 100644 --- a/archive/src/main/java/java/util/zip/GZIPInputStream.java +++ b/archive/src/main/java/java/util/zip/GZIPInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -28,49 +27,40 @@ import org.apache.harmony.archive.internal.nls.Messages; * The {@code GZIPInputStream} class is used to read data stored in the GZIP * format, reading and decompressing GZIP data from the underlying stream into * its buffer. - * - * @since Android 1.0 */ -public class GZIPInputStream extends java.util.zip.InflaterInputStream { +public class GZIPInputStream extends InflaterInputStream { + + private static final int FCOMMENT = 16; + + private static final int FEXTRA = 4; + + private static final int FHCRC = 2; + + private static final int FNAME = 8; + + /** + * The magic header for the GZIP format. + */ + public final static int GZIP_MAGIC = 0x8b1f; /** * The checksum algorithm used when handling uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); /** * Indicates the end of the input stream. - * - * @since Android 1.0 */ protected boolean eos = false; /** - * The magic header for the GZIP format. - * - * @since Android 1.0 - */ - public final static int GZIP_MAGIC = 0x8b1f; - - private static final int FHCRC = 2; - - private static final int FEXTRA = 4; - - private static final int FNAME = 8; - - private static final int FCOMMENT = 16; - - /** * Construct a {@code GZIPInputStream} to read from GZIP data from the * underlying stream. - * + * * @param is * the {@code InputStream} to read data from. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is) throws IOException { this(is, BUF_SIZE); @@ -86,7 +76,6 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { * the internal read buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is, int size) throws IOException { super(is, new Inflater(true), size); @@ -134,6 +123,15 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } } + /** + * Closes this stream and any underlying streams. + */ + @Override + public void close() throws IOException { + eos = true; + super.close(); + } + private long getLong(byte[] buffer, int off) { long l = 0; l |= (buffer[off] & 0xFF); @@ -147,12 +145,23 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8); } + /** + * Reads and decompresses GZIP data from the underlying stream into the + * given buffer. + * + * @param buffer + * Buffer to receive data + * @param off + * Offset in buffer to store data + * @param nbytes + * Number of bytes to read + */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { if (closed) { throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$ } - if(eof){ + if (eof) { return -1; } // avoid int overflow, check null buffer @@ -164,17 +173,15 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } else if (!eos) { eos = true; // Get non-compressed bytes read by fill - // BEGIN android-changed - // copied from newer version of harmony int size = inf.getRemaining(); final int trailerSize = 8; // crc (4 bytes) + total out (4 - // bytes) + // bytes) byte[] b = new byte[trailerSize]; int copySize = (size > trailerSize) ? trailerSize : size; System.arraycopy(buf, len - size, b, 0, copySize); - readFully(b, copySize, trailerSize - copySize); - // END android-changed + readFully(b, copySize, trailerSize - copySize); + if (getLong(b, 0) != crc.getValue()) { throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$ } @@ -186,12 +193,6 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } throw new ArrayIndexOutOfBoundsException(); } - - @Override - public void close() throws IOException { - eos = true; - super.close(); - } private void readFully(byte[] buffer, int offset, int length) throws IOException { diff --git a/archive/src/main/java/java/util/zip/GZIPOutputStream.java b/archive/src/main/java/java/util/zip/GZIPOutputStream.java index fa41e19..f146da1 100644 --- a/archive/src/main/java/java/util/zip/GZIPOutputStream.java +++ b/archive/src/main/java/java/util/zip/GZIPOutputStream.java @@ -17,22 +17,17 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; /** * The {@code GZIPOutputStream} class is used to write data to a stream in the * GZIP storage format. - * - * @since Android 1.0 */ public class GZIPOutputStream extends DeflaterOutputStream { /** * The checksum algorithm used when treating uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); @@ -44,7 +39,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the {@code OutputStream} to write data to. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os) throws IOException { this(os, BUF_SIZE); @@ -61,7 +55,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the internal buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os, int size) throws IOException { super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); @@ -76,10 +69,9 @@ public class GZIPOutputStream extends DeflaterOutputStream { /** * Indicates to the stream that all data has been written out, and any GZIP * terminal data can now be written. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -88,24 +80,29 @@ public class GZIPOutputStream extends DeflaterOutputStream { writeLong(crc.tbytes); } + /** + * Write up to nbytes of data from the given buffer, starting at offset off, + * to the underlying stream in GZIP format. + */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { super.write(buffer, off, nbytes); crc.update(buffer, off, nbytes); } - private int writeShort(int i) throws IOException { - out.write(i & 0xFF); - out.write((i >> 8) & 0xFF); + private long writeLong(long i) throws IOException { + // Write out the long value as an unsigned int + int unsigned = (int) i; + out.write(unsigned & 0xFF); + out.write((unsigned >> 8) & 0xFF); + out.write((unsigned >> 16) & 0xFF); + out.write((unsigned >> 24) & 0xFF); return i; } - private long writeLong(long i) throws IOException { - // Write out the long value as an unsigned int - out.write((int) (i & 0xFF)); - out.write((int) (i >> 8) & 0xFF); - out.write((int) (i >> 16) & 0xFF); - out.write((int) (i >> 24) & 0xFF); + private int writeShort(int i) throws IOException { + out.write(i & 0xFF); + out.write((i >> 8) & 0xFF); return i; } } diff --git a/archive/src/main/java/java/util/zip/Inflater.java b/archive/src/main/java/java/util/zip/Inflater.java index 9b93e54..cb1ce68 100644 --- a/archive/src/main/java/java/util/zip/Inflater.java +++ b/archive/src/main/java/java/util/zip/Inflater.java @@ -30,45 +30,65 @@ import java.io.FileDescriptor; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code InflaterInputStream} and its * descendants. - * </p> * <p> * The typical usage of a {@code Inflater} outside this package consists of a * specific call to one of its constructors before being passed to an instance * of {@code InflaterInputStream}. - * </p> - * + * * @see InflaterInputStream * @see Deflater - * @since Android 1.0 */ public class Inflater { + private static final byte MAGIC_NUMBER = 120; + + static { + oneTimeInitialization(); + } + + // Fill in the JNI id caches + private static native void oneTimeInitialization(); + private boolean finished; // Set by the inflateImpl native - private boolean needsDictionary; // Set by the inflateImpl native + private boolean gotFirstByte = false; - private long streamHandle = -1; + int inLength; int inRead; - - int inLength; - // Fill in the JNI id caches - private static native void oneTimeInitialization(); + private boolean needsDictionary; // Set by the inflateImpl native - static { - oneTimeInitialization(); - } - - private static final byte MAGIC_NUMBER = 120; - private boolean gotFirstByte = false; private boolean pass_magic_number_check = true; - + + private long streamHandle = -1; + + /** + * This constructor creates an inflater that expects a header from the input + * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB + * header. + */ + public Inflater() { + this(false); + } + + /** + * This constructor allows to create an inflater that expects no header from + * the input stream. + * + * @param noHeader + * {@code true} indicates that no ZLIB header comes with the + * input. + */ + public Inflater(boolean noHeader) { + streamHandle = createStream(noHeader); + } + + private native long createStream(boolean noHeader1); + /** * Release any resources associated with this {@code Inflater}. Any unused * input/output is discarded. This is also called by the finalize method. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -91,10 +111,9 @@ public class Inflater { * stream. If deflated bytes remain and {@code needsInput()} returns {@code * true} this method will return {@code false}. This method should be * called after all deflated input is supplied to the {@code Inflater}. - * + * * @return {@code true} if all input has been inflated, {@code false} * otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -103,10 +122,9 @@ public class Inflater { /** * Returns the <i>Adler32</i> checksum of either all bytes inflated, or the * checksum of the preset dictionary if one has been supplied. - * + * * @return The <i>Adler32</i> checksum associated with this * {@code Inflater}. - * @since Android 1.0 . */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -118,11 +136,40 @@ public class Inflater { private native synchronized int getAdlerImpl(long handle); /** + * Returns the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalIn()} except that it returns a + * {@code long} value instead of an integer. + * + * @return the total number of bytes read. + */ + public synchronized long getBytesRead() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalInImpl(streamHandle); + } + + /** + * Returns a the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalOut} except it returns a + * {@code long} value instead of an integer. + * + * @return the total bytes written to the output buffer. + */ + public synchronized long getBytesWritten() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalOutImpl(streamHandle); + } + + /** * Returns the number of bytes of current input remaining to be read by the * inflater. - * + * * @return the number of bytes of unread input. - * @since Android 1.0 */ public synchronized int getRemaining() { return inLength - inRead; @@ -131,9 +178,8 @@ public class Inflater { /** * Returns total number of bytes of input read by the {@code Inflater}. The * result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total number of bytes read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { @@ -149,9 +195,8 @@ public class Inflater { /** * Returns total number of bytes written to the output buffer by the {@code * Inflater}. The result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total bytes of output data written. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { @@ -166,14 +211,13 @@ public class Inflater { /** * Inflates bytes from current input and stores them in {@code buf}. - * + * * @param buf * the buffer where decompressed data bytes are written. * @return the number of bytes inflated. * @throws DataFormatException * if the underlying stream is corrupted or was not compressed * using a {@code Deflater}. - * @since Android 1.0 */ public int inflate(byte[] buf) throws DataFormatException { return inflate(buf, 0, buf.length); @@ -182,7 +226,7 @@ public class Inflater { /** * Inflates up to n bytes from the current input and stores them in {@code * buf} starting at {@code off}. - * + * * @param buf * the buffer to write inflated bytes to. * @param off @@ -205,7 +249,7 @@ public class Inflater { if (streamHandle == -1) { throw new IllegalStateException(); } - + if (!pass_magic_number_check) { throw new DataFormatException(); } @@ -213,7 +257,7 @@ public class Inflater { if (needsInput()) { return 0; } - + boolean neededDict = needsDictionary; needsDictionary = false; int result = inflateImpl(buf, off, nbytes, streamHandle); @@ -229,41 +273,15 @@ public class Inflater { int nbytes, long handle); /** - * This constructor creates an inflater that expects a header from the input - * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB - * header. - * - * @since Android 1.0 - * @since Android 1.0 - */ - public Inflater() { - this(false); - } - - /** - * This constructor allows to create an inflater that expects no header from - * the input stream. - * - * @param noHeader - * {@code true} indicates that no ZLIB header comes with the - * input. - * @since Android 1.0 - */ - public Inflater(boolean noHeader) { - streamHandle = createStream(noHeader); - } - - /** * Indicates whether the input bytes were compressed with a preset * dictionary. This method should be called prior to {@code inflate()} to * determine whether a dictionary is required. If so {@code setDictionary()} * should be called with the appropriate dictionary prior to calling {@code * inflate()}. - * + * * @return {@code true} if a preset dictionary is required for inflation. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsDictionary() { return needsDictionary; @@ -271,11 +289,10 @@ public class Inflater { /** * Indicates that input has to be passed to the inflater. - * + * * @return {@code true} if {@code setInput} has to be called before * inflation can proceed. * @see #setInput(byte[]) - * @since Android 1.0 */ public synchronized boolean needsInput() { return inRead == inLength; @@ -284,8 +301,6 @@ public class Inflater { /** * Resets the {@code Inflater}. Should be called prior to inflating a new * set of data. - * - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -303,11 +318,10 @@ public class Inflater { * Sets the preset dictionary to be used for inflation to {@code buf}. * {@code needsDictionary()} can be called to determine whether the current * input was deflated using a preset dictionary. - * + * * @param buf * The buffer containing the dictionary bytes. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -316,7 +330,11 @@ public class Inflater { /** * Like {@code setDictionary(byte[])}, allowing to define a specific region * inside {@code buf} to be used as a dictionary. - * + * <p> + * The dictionary should be set if the {@link #inflate(byte[])} returned + * zero bytes inflated and {@link #needsDictionary()} returns + * <code>true</code>. + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -324,7 +342,6 @@ public class Inflater { * @param nbytes * the length of the data. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -345,11 +362,10 @@ public class Inflater { /** * Sets the current input to to be decrompressed. This method should only be * called if {@code needsInput()} returns {@code true}. - * + * * @param buf * the input buffer. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -360,7 +376,7 @@ public class Inflater { * {@code off} and ending at {@code nbytes - 1} where data is written after * decompression. This method should only be called if {@code needsInput()} * returns {@code true}. - * + * * @param buf * the input buffer. * @param off @@ -368,7 +384,6 @@ public class Inflater { * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -394,10 +409,9 @@ public class Inflater { // And at a first glance it doesn't look like the first byte has // to be 120. // END android-note - if(!gotFirstByte && nbytes>0) - { - pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1); - gotFirstByte = true; + if (!gotFirstByte && nbytes > 0) { + pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1); + gotFirstByte = true; } } @@ -407,14 +421,13 @@ public class Inflater { * off} and ending at {@code nbytes - 1}. This method should only be called * if {@code needsInput()} returns {@code true}. * - * @param file + * @param fd * the input file. * @param off * the offset to read from in buffer. * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ synchronized int setFileInput(FileDescriptor fd, long off, int nbytes) { if (streamHandle == -1) { @@ -426,38 +439,6 @@ public class Inflater { } // END android-added - /** - * Returns the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalIn()} except that it returns a - * {@code long} value instead of an integer. - * - * @return the total number of bytes read. - * @since Android 1.0 - */ - public synchronized long getBytesRead() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalInImpl(streamHandle); - } - - /** - * Returns a the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalOut} except it returns a - * {@code long} value instead of an integer. - * - * @return the total bytes written to the output buffer. - * @since Android 1.0 - */ - public synchronized long getBytesWritten() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalOutImpl(streamHandle); - } - private native synchronized void setInputImpl(byte[] buf, int off, int nbytes, long handle); @@ -465,6 +446,4 @@ public class Inflater { private native synchronized int setFileInputImpl(FileDescriptor fd, long off, int nbytes, long handle); // END android-added - - private native long createStream(boolean noHeader1); } diff --git a/archive/src/main/java/java/util/zip/InflaterInputStream.java b/archive/src/main/java/java/util/zip/InflaterInputStream.java index 5d3bda0..1fd3602 100644 --- a/archive/src/main/java/java/util/zip/InflaterInputStream.java +++ b/archive/src/main/java/java/util/zip/InflaterInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; @@ -31,31 +30,24 @@ import org.apache.harmony.archive.internal.nls.Messages; * (see <a href="http://www.gzip.org/algorithm.txt">specification</a>). * Basically it wraps the {@code Inflater} class and takes care of the * buffering. - * + * * @see Inflater * @see DeflaterOutputStream - * @since Android 1.0 */ public class InflaterInputStream extends FilterInputStream { /** * The inflater used for this stream. - * - * @since Android 1.0 */ protected Inflater inf; /** * The input buffer used for decompression. - * - * @since Android 1.0 */ protected byte[] buf; /** * The length of the buffer. - * - * @since Android 1.0 */ protected int len; @@ -74,10 +66,9 @@ public class InflaterInputStream extends FilterInputStream { * InputStream} from which the compressed data is to be read from. Default * settings for the {@code Inflater} and internal buffer are be used. In * particular the Inflater expects a ZLIB header from the input stream. - * + * * @param is * the {@code InputStream} to read data from. - * @since Android 1.0 */ public InflaterInputStream(InputStream is) { this(is, new Inflater(), BUF_SIZE); @@ -86,12 +77,11 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you pass a specifically initialized Inflater, * for example one that expects no ZLIB header. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf) { this(is, inf, BUF_SIZE); @@ -100,14 +90,13 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you specify both the {@code Inflater} as well as * the internal buffer size to be used. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. * @param bsize * the size to be used for the internal buffer. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf, int bsize) { super(is); @@ -129,11 +118,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads a single byte of decompressed data. - * + * * @return the byte read. * @throws IOException * if an error occurs reading the byte. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -147,7 +135,7 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads up to {@code nbytes} of decompressed data and stores it in * {@code buffer} starting at {@code off}. - * + * * @param buffer * the buffer to write data to. * @param off @@ -157,7 +145,6 @@ public class InflaterInputStream extends FilterInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { @@ -197,7 +184,7 @@ public class InflaterInputStream extends FilterInputStream { if (len == -1) { throw new EOFException(); } - throw (IOException)(new IOException().initCause(e)); + throw (IOException) (new IOException().initCause(e)); } if (result > 0) { return result; @@ -208,20 +195,18 @@ public class InflaterInputStream extends FilterInputStream { return -1; } else if (len == -1) { throw new EOFException(); - // If result == 0, fill() and try again + // If result == 0, fill() and try again } } while (true); } throw new ArrayIndexOutOfBoundsException(); } - /** * Fills the input buffer with data to be decompressed. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ protected void fill() throws IOException { if (closed) { @@ -246,17 +231,21 @@ public class InflaterInputStream extends FilterInputStream { /** * Skips up to n bytes of uncompressed data. - * + * * @param nbytes * the number of bytes to skip. * @return the number of uncompressed bytes skipped. * @throws IOException * if an error occurs skipping. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { if (nbytes >= 0) { + // BEGIN android-changed + if (buf == null) { + buf = new byte[BUF_SIZE]; + } + // END android-changed long count = 0, rem = 0; while (count < nbytes) { int x = read(buf, 0, @@ -275,11 +264,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Returns whether data can be read from this stream. - * + * * @return 0 if this stream has been closed, 1 otherwise. * @throws IOException * If an error occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -295,10 +283,9 @@ public class InflaterInputStream extends FilterInputStream { /** * Closes the input stream. - * + * * @throws IOException * If an error occurs closing the input stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -309,17 +296,15 @@ public class InflaterInputStream extends FilterInputStream { super.close(); } } - + /** - * This implementation overrides the super type implementation to do nothing - * at all. - * + * Marks the current position in the stream. This implementation overrides + * the super type implementation to do nothing at all. + * * @param readlimit * of no use. - * @since Android 1.0 */ @Override - @SuppressWarnings("unused") public void mark(int readlimit) { // do nothing } @@ -328,22 +313,20 @@ public class InflaterInputStream extends FilterInputStream { * Reset the position of the stream to the last marked position. This * implementation overrides the supertype implementation and always throws * an {@link IOException IOException} when called. - * + * * @throws IOException * if the method is called - * @since Android 1.0 */ @Override - public void reset() throws IOException{ + public void reset() throws IOException { throw new IOException(); } - + /** * Returns whether the receiver implements {@code mark} semantics. This type * does not support {@code mark()}, so always responds {@code false}. - * + * * @return false, always - * @since Android 1.0 */ @Override public boolean markSupported() { diff --git a/archive/src/main/java/java/util/zip/ZipConstants.java b/archive/src/main/java/java/util/zip/ZipConstants.java index d804b0e..d00adc9 100644 --- a/archive/src/main/java/java/util/zip/ZipConstants.java +++ b/archive/src/main/java/java/util/zip/ZipConstants.java @@ -17,7 +17,6 @@ package java.util.zip; - interface ZipConstants { public static final long LOCSIG = 0x4034b50, EXTSIG = 0x8074b50, diff --git a/archive/src/main/java/java/util/zip/ZipEntry.java b/archive/src/main/java/java/util/zip/ZipEntry.java index 2cc7a9c..9774b6a 100644 --- a/archive/src/main/java/java/util/zip/ZipEntry.java +++ b/archive/src/main/java/java/util/zip/ZipEntry.java @@ -36,10 +36,9 @@ import java.util.GregorianCalendar; * itself. For example when reading a <i>ZIP-file</i> you will first retrieve * all its entries in a collection and then read the data for a specific entry * through an input stream. - * + * * @see ZipFile * @see ZipOutputStream - * @since Android 1.0 */ public class ZipEntry implements ZipConstants, Cloneable { String name, comment; @@ -94,26 +93,21 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Zip entry state: Deflated. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Zip entry state: Stored. - * - * @since Android 1.0 */ public static final int STORED = 0; /** * Constructs a new {@code ZipEntry} with the specified name. - * + * * @param name * the name of the ZIP entry. * @throws IllegalArgumentException * if the name length is outside the range (> 0xFFFF). - * @since Android 1.0 */ public ZipEntry(String name) { if (name == null) { @@ -147,11 +141,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the comment for this {@code ZipEntry}. - * + * * @return the comment for this {@code ZipEntry}, or {@code null} if there * is no comment. If we're reading an archive with * {@code ZipInputStream} the comment is not available. - * @since Android 1.0 */ public String getComment() { return comment; @@ -159,10 +152,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compressed size of this {@code ZipEntry}. - * + * * @return the compressed size, or -1 if the compressed size has not been * set. - * @since Android 1.0 */ public long getCompressedSize() { return compressedSize; @@ -170,9 +162,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the checksum for this {@code ZipEntry}. - * + * * @return the checksum, or -1 if the checksum has not been set. - * @since Android 1.0 */ public long getCrc() { return crc; @@ -180,10 +171,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the extra information for this {@code ZipEntry}. - * + * * @return a byte array containing the extra information, or {@code null} if * there is none. - * @since Android 1.0 */ public byte[] getExtra() { return extra; @@ -191,10 +181,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compression method for this {@code ZipEntry}. - * + * * @return the compression method, either {@code DEFLATED}, {@code STORED} * or -1 if the compression method has not been set. - * @since Android 1.0 */ public int getMethod() { return compressionMethod; @@ -202,9 +191,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the name of this {@code ZipEntry}. - * + * * @return the entry name. - * @since Android 1.0 */ public String getName() { return name; @@ -212,10 +200,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the uncompressed size of this {@code ZipEntry}. - * + * * @return the uncompressed size, or {@code -1} if the size has not been * set. - * @since Android 1.0 */ public long getSize() { return size; @@ -223,10 +210,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the last modification time of this {@code ZipEntry}. - * + * * @return the last modification time as the number of milliseconds since * Jan. 1, 1970. - * @since Android 1.0 */ public long getTime() { if (time != -1) { @@ -242,10 +228,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Determine whether or not this {@code ZipEntry} is a directory. - * + * * @return {@code true} when this {@code ZipEntry} is a directory, {@code * false} otherwise. - * @since Android 1.0 */ public boolean isDirectory() { return name.charAt(name.length() - 1) == '/'; @@ -253,10 +238,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the comment for this {@code ZipEntry}. - * + * * @param string * the comment for this entry. - * @since Android 1.0 */ public void setComment(String string) { if (string == null || string.length() <= 0xFFFF) { @@ -268,10 +252,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compressed size for this {@code ZipEntry}. - * + * * @param value * the compressed size (in bytes). - * @since Android 1.0 */ public void setCompressedSize(long value) { compressedSize = value; @@ -279,12 +262,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the checksum for this {@code ZipEntry}. - * + * * @param value * the checksum for this entry. * @throws IllegalArgumentException * if {@code value} is < 0 or > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setCrc(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -296,12 +278,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the extra information for this {@code ZipEntry}. - * + * * @param data * a byte array containing the extra information. * @throws IllegalArgumentException * when the length of data is greater than 0xFFFF bytes. - * @since Android 1.0 */ public void setExtra(byte[] data) { if (data == null || data.length <= 0xFFFF) { @@ -313,13 +294,12 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compression method for this {@code ZipEntry}. - * + * * @param value * the compression method, either {@code DEFLATED} or {@code * STORED}. * @throws IllegalArgumentException * when value is not {@code DEFLATED} or {@code STORED}. - * @since Android 1.0 */ public void setMethod(int value) { if (value != STORED && value != DEFLATED) { @@ -330,12 +310,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the uncompressed size of this {@code ZipEntry}. - * + * * @param value * the uncompressed size for this entry. * @throws IllegalArgumentException * if {@code value} < 0 or {@code value} > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setSize(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -347,11 +326,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the modification time of this {@code ZipEntry}. - * + * * @param value * the modification time as the number of milliseconds since Jan. * 1, 1970. - * @since Android 1.0 */ public void setTime(long value) { GregorianCalendar cal = new GregorianCalendar(); @@ -372,9 +350,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the string representation of this {@code ZipEntry}. - * + * * @return the string representation of this {@code ZipEntry}. - * @since Android 1.0 */ @Override public String toString() { @@ -401,10 +378,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Constructs a new {@code ZipEntry} using the values obtained from {@code * ze}. - * + * * @param ze * the {@code ZipEntry} from which to obtain values. - * @since Android 1.0 */ public ZipEntry(ZipEntry ze) { name = ze.name; @@ -434,9 +410,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns a shallow copy of this entry. - * + * * @return a copy of this entry. - * @since Android 1.0 */ @Override public Object clone() { @@ -445,9 +420,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the hash code for this {@code ZipEntry}. - * + * * @return the hash code of the entry. - * @since Android 1.0 */ @Override public int hashCode() { @@ -471,7 +445,7 @@ public class ZipEntry implements ZipConstants, Cloneable { * readIntLE, so we're going to read the entire header at once * and then parse the results out without using any function calls. * Uglier, but should be much faster. - * + * * Note that some lines look a bit different, because the corresponding * fields or locals are long and so we need to do & 0xffffffffl to avoid * problems induced by sign extension. @@ -549,7 +523,7 @@ public class ZipEntry implements ZipConstants, Cloneable { int count; int len = b.length; int off = 0; - + while (len > 0) { count = in.read(b, off, len); if (count <= 0) diff --git a/archive/src/main/java/java/util/zip/ZipException.java b/archive/src/main/java/java/util/zip/ZipException.java index 590117b..6dab26f 100644 --- a/archive/src/main/java/java/util/zip/ZipException.java +++ b/archive/src/main/java/java/util/zip/ZipException.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; /** @@ -26,7 +25,6 @@ import java.io.IOException; * * @see ZipFile * @see ZipInputStream - * @since Android 1.0 */ public class ZipException extends IOException { @@ -34,8 +32,6 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance. - * - * @since Android 1.0 */ public ZipException() { super(); @@ -44,10 +40,9 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public ZipException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/zip/ZipFile.java b/archive/src/main/java/java/util/zip/ZipFile.java index f1415d9..653b2c9 100644 --- a/archive/src/main/java/java/util/zip/ZipFile.java +++ b/archive/src/main/java/java/util/zip/ZipFile.java @@ -37,17 +37,13 @@ import java.util.NoSuchElementException; * While {@code ZipInputStream} provides stream based read access to a * <i>ZIP-archive</i>, this class implements more efficient (file based) access * and makes use of the <i>central directory</i> within a <i>ZIP-archive</i>. - * </p> * <p> * Use {@code ZipOutputStream} if you want to create an archive. - * </p> * <p> * A temporary ZIP file can be marked for automatic deletion upon closing it. - * </p> - * + * * @see ZipEntry * @see ZipOutputStream - * @since Android 1.0 */ public class ZipFile implements ZipConstants { @@ -57,28 +53,23 @@ public class ZipFile implements ZipConstants { /** * Open zip file for read. - * - * @since Android 1.0 */ public static final int OPEN_READ = 1; /** * Delete zip file when closed. - * - * @since Android 1.0 */ public static final int OPEN_DELETE = 4; /** * Constructs a new {@code ZipFile} with the specified file. - * + * * @param file * the file to read from. * @throws ZipException * if a ZIP error occurs. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file) throws ZipException, IOException { this(file, OPEN_READ); @@ -88,22 +79,31 @@ public class ZipFile implements ZipConstants { * Opens a file as <i>ZIP-archive</i>. "mode" must be {@code OPEN_READ} or * {@code OPEN_DELETE} . The latter sets the "delete on exit" flag through a * file. - * + * * @param file * the ZIP file to read. * @param mode * the mode of the file open operation. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file, int mode) throws IOException { - if (mode == (OPEN_READ | OPEN_DELETE)) - fileToDeleteOnClose = file; // file.deleteOnExit(); - else if (mode != OPEN_READ) - throw new IllegalArgumentException("invalid mode"); - fileName = file.getPath(); + if (mode == OPEN_READ || mode == (OPEN_READ | OPEN_DELETE)) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(fileName); + } + if ((mode & OPEN_DELETE) != 0) { + if (security != null) { + security.checkDelete(fileName); + } + fileToDeleteOnClose = file; // file.deleteOnExit(); + } + } else { + throw new IllegalArgumentException(); + } + mRaf = new RandomAccessFile(fileName, "r"); mEntryList = new ArrayList<ZipEntry>(); @@ -124,12 +124,11 @@ public class ZipFile implements ZipConstants { /** * Opens a ZIP archived file. - * + * * @param name * the name of the ZIP file. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public ZipFile(String name) throws IOException { this(new File(name), OPEN_READ); @@ -142,10 +141,9 @@ public class ZipFile implements ZipConstants { /** * Closes this ZIP file. - * + * * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public void close() throws IOException { RandomAccessFile raf = mRaf; @@ -171,9 +169,8 @@ public class ZipFile implements ZipConstants { /** * Returns an enumeration of the entries. The entries are listed in the * order in which they appear in the ZIP archive. - * + * * @return the enumeration of the entries. - * @since Android 1.0 */ public Enumeration<? extends ZipEntry> entries() { return new Enumeration<ZipEntry>() { @@ -195,12 +192,11 @@ public class ZipFile implements ZipConstants { /** * Gets the ZIP entry with the specified name from this {@code ZipFile}. - * + * * @param entryName * the name of the entry in the ZIP file. * @return a {@code ZipEntry} or {@code null} if the entry name does not * exist in the ZIP file. - * @since Android 1.0 */ public ZipEntry getEntry(String entryName) { if (entryName != null) { @@ -213,13 +209,12 @@ public class ZipFile implements ZipConstants { /** * Returns an input stream on the data of the specified {@code ZipEntry}. - * + * * @param entry * the ZipEntry. * @return an input stream of the data contained in the {@code ZipEntry}. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public InputStream getInputStream(ZipEntry entry) throws IOException { /* @@ -259,9 +254,8 @@ public class ZipFile implements ZipConstants { /** * Gets the file name of this {@code ZipFile}. - * + * * @return the file name of this {@code ZipFile}. - * @since Android 1.0 */ public String getName() { return fileName; @@ -269,9 +263,8 @@ public class ZipFile implements ZipConstants { /** * Returns the number of {@code ZipEntries} in this {@code ZipFile}. - * + * * @return the number of entries in this file. - * @since Android 1.0 */ public int size() { return mEntryList.size(); diff --git a/archive/src/main/java/java/util/zip/ZipInputStream.java b/archive/src/main/java/java/util/zip/ZipInputStream.java index 262fa3f..1a35b1c 100644 --- a/archive/src/main/java/java/util/zip/ZipInputStream.java +++ b/archive/src/main/java/java/util/zip/ZipInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -36,18 +35,14 @@ import org.apache.harmony.luni.util.Util; * the so called ZIP entries. Therefore when reading from a {@code * ZipInputStream} first the entry's attributes will be retrieved with {@code * getNextEntry} before its data is read. - * </p> * <p> * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i> * entry, this extension can read uncompressed entries as well. - * </p> * <p> * Use {@code ZipFile} if you can access the archive as a file directly. - * </p> - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipInputStream extends InflaterInputStream implements ZipConstants { static final int DEFLATED = 8; @@ -58,7 +53,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants static final int ZIPLocalHeaderVersionNeeded = 20; - // BEGI android-removed + // BEGIN android-removed // private boolean zipClosed = false; // END android-removed @@ -82,10 +77,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Constructs a new {@code ZipInputStream} from the specified input stream. - * + * * @param stream * the input stream to representing a ZIP archive. - * @since Android 1.0 */ public ZipInputStream(InputStream stream) { super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true)); @@ -96,10 +90,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes this {@code ZipInputStream}. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -113,10 +106,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes the current ZIP entry and positions to read the next entry. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public void closeEntry() throws IOException { // BEGIN android-changed @@ -173,13 +165,12 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Reads the next entry from this {@code ZipInputStream}. - * + * * @return the next {@code ZipEntry} contained in the input stream. * @throws IOException * if the stream is not positioned at the beginning of an entry * or if an other {@code IOException} occurs. * @see ZipEntry - * @since Android 1.0 */ public ZipEntry getNextEntry() throws IOException { if (currentEntry != null) { @@ -274,6 +265,18 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /* Read 4 bytes from the buffer and store it as an int */ + /** + * Reads up to the specified number of uncompressed bytes into the buffer + * starting at the offset. + * + * @param buffer + * a byte array + * @param start + * the starting offset into the buffer + * @param length + * the number of bytes to read + * @return the number of bytes read + */ @Override public int read(byte[] buffer, int start, int length) throws IOException { // BEGIN android-changed @@ -340,13 +343,12 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Skips up to the specified number of bytes in the current ZIP entry. - * + * * @param value * the number of bytes to skip. * @return the number of bytes skipped. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public long skip(long value) throws IOException { @@ -364,15 +366,14 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants return skipped; } throw new IllegalArgumentException(); -} + } /** * Returns 0 if the {@code EOF} has been reached, otherwise returns 1. - * + * * @return 0 after {@code EOF} of current entry, 1 otherwise. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -389,11 +390,10 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * creates a {@link ZipEntry } with the given name. - * + * * @param name * the name of the entry. * @return the created {@code ZipEntry}. - * @since Android 1.0 */ protected ZipEntry createZipEntry(String name) { return new ZipEntry(name); diff --git a/archive/src/main/java/java/util/zip/ZipOutputStream.java b/archive/src/main/java/java/util/zip/ZipOutputStream.java index 4ddf643..58e781f 100644 --- a/archive/src/main/java/java/util/zip/ZipOutputStream.java +++ b/archive/src/main/java/java/util/zip/ZipOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -32,33 +31,26 @@ import org.apache.harmony.archive.internal.nls.Messages; * {@code ZipOutputStream} is used to write {@code ZipEntries} to the underlying * stream. Output from {@code ZipOutputStream} conforms to the {@code ZipFile} * file format. - * </p> * <p> * While {@code DeflaterOutputStream} can write a compressed <i>ZIP-archive</i> * entry, this extension can write uncompressed entries as well. In this case * special rules apply, for this purpose refer to the <a * href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">file format * specification</a>. - * </p> - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { /** * Indicates deflated entries. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Indicates uncompressed entries. - * - * @since Android 1.0 */ public static final int STORED = 0; @@ -90,7 +82,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param p1 * the {@code OutputStream} to write the data to. - * @since Android 1.0 */ public ZipOutputStream(OutputStream p1) { super(p1, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); @@ -102,7 +93,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -119,7 +109,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the entry. - * @since Android 1.0 */ public void closeEntry() throws IOException { if (cDir == null) { @@ -205,7 +194,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * if an error occurs while terminating the stream. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -253,7 +241,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @throws IOException * If an error occurs storing the entry. * @see #write - * @since Android 1.0 */ public void putNextEntry(ZipEntry ze) throws java.io.IOException { if (currentEntry != null) { @@ -286,7 +273,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements nameLength = utf8Count(ze.name); if (nameLength > 0xffff) { /* [MSG "archive.2A", "Name too long: {0}"] */ - throw new IllegalArgumentException(Messages.getString("archive.2A", ze.name)); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString( + "archive.2A", ze.name)); //$NON-NLS-1$ } def.setLevel(compressLevel); @@ -338,7 +326,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param comment * the comment associated with the file. - * @since Android 1.0 */ public void setComment(String comment) { if (comment.length() > 0xFFFF) { @@ -355,7 +342,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @param level * the compression level (ranging from -1 to 8). * @see Deflater - * @since Android 1.0 */ public void setLevel(int level) { if (level < Deflater.DEFAULT_COMPRESSION @@ -372,7 +358,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param method * the compression method to use. - * @since Android 1.0 */ public void setMethod(int method) { if (method != STORED && method != DEFLATED) { @@ -398,11 +383,17 @@ public class ZipOutputStream extends DeflaterOutputStream implements } + /** + * Writes data for the current entry to the underlying stream. + * + * @exception IOException + * If an error occurs writing to the stream + */ @Override public void write(byte[] buffer, int off, int nbytes) throws java.io.IOException { // avoid int overflow, check null buf - if ((off > buffer.length) || (nbytes < 0) || (off < 0) + if ((off < 0 || (nbytes < 0) || off > buffer.length) || (buffer.length - off < nbytes)) { throw new IndexOutOfBoundsException(); } diff --git a/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java index 764f34d..3ba50fa 100644 --- a/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java +++ b/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java @@ -27,7 +27,6 @@ package org.apache.harmony.archive.internal.nls; - import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale; @@ -50,7 +49,7 @@ import org.apache.harmony.luni.util.MsgHelp; * is looked up, or resource bundle support is not available, the key itself * will be returned as the associated message. This means that the <em>KEY</em> * should a reasonable human-readable (english) string. - * + * */ public class Messages { @@ -61,7 +60,7 @@ public class Messages { /** * Retrieves a message which has no arguments. - * + * * @param msg * String the key to look up. * @return String the message for that key in the system message bundle. @@ -74,7 +73,7 @@ public class Messages { /** * Retrieves a message which takes 1 argument. - * + * * @param msg * String the key to look up. * @param arg @@ -87,7 +86,7 @@ public class Messages { /** * Retrieves a message which takes 1 integer argument. - * + * * @param msg * String the key to look up. * @param arg @@ -100,7 +99,7 @@ public class Messages { /** * Retrieves a message which takes 1 character argument. - * + * * @param msg * String the key to look up. * @param arg @@ -113,7 +112,7 @@ public class Messages { /** * Retrieves a message which takes 2 arguments. - * + * * @param msg * String the key to look up. * @param arg1 @@ -128,7 +127,7 @@ public class Messages { /** * Retrieves a message which takes several arguments. - * + * * @param msg * String the key to look up. * @param args diff --git a/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties b/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties index 1ae5c5e..e909af0 100644 --- a/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties +++ b/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties @@ -60,8 +60,11 @@ archive.29=Entry already exists: {0} archive.2A=Name too long: {0} archive.2B=String is too long archive.2C=No active entry -archive.2D=Missing version string\: {0} -archive.2E=Line too long -archive.2F=Invalid attribute {0} -archive.30={0} failed verification of {1} -archive.31={0} has invalid digest for {1} in {2} +archive.2D=Missing version attribute\: {0} +archive.2E=Manifest is too long +archive.2F=NUL character in a manifest +archive.30=Invalid attribute {0} +archive.31={0} failed verification of {1} +archive.32={0} has invalid digest for {1} in {2} +archive.33=A length of the encoded header name "{1}" exceeded maximum length {2} +archive.34=A jar verifier does not support more than one entry with the same name diff --git a/archive/src/main/java/org/apache/harmony/archive/util/Util.java b/archive/src/main/java/org/apache/harmony/archive/util/Util.java index bed3e91..b15108a 100644 --- a/archive/src/main/java/org/apache/harmony/archive/util/Util.java +++ b/archive/src/main/java/org/apache/harmony/archive/util/Util.java @@ -30,39 +30,49 @@ public class Util { return false; } - s1 = s1.substring(start1, start1 + length); - s2 = s2.substring(start2, start2 + length); - - return toASCIILowerCase(s1).equals(toASCIILowerCase(s2)); + char c1, c2; + for (int i = 0; i < length; i++) { + if ((c1 = s1.charAt(start1++)) != (c2 = s2.charAt(start2++)) + && toASCIIUpperCase(c1) != toASCIIUpperCase(c2)) { + return false; + } + } + return true; } throw new NullPointerException(); } - public static String toASCIILowerCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('A' <= c && c <= 'Z') { - buffer.append((char) (c + ('a' - 'A'))); - } else { - buffer.append(c); + public static final boolean equalsIgnoreCase(byte[] buf1, byte[] buf2) { + if (buf1 == buf2) { + return true; + } + + if (buf1 == null || buf2 == null || buf1.length != buf2.length) { + return false; + } + + byte b1, b2; + + for (int i = 0; i < buf1.length; i++) { + if ((b1 = buf1[i]) != (b2 = buf2[i]) + && toASCIIUpperCase(b1) != toASCIIUpperCase(b2)) { + return false; } } - return buffer.toString(); + return true; } - public static String toASCIIUpperCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('a' <= c && c <= 'z') { - buffer.append((char) (c - ('a' - 'A'))); - } else { - buffer.append(c); - } + static final char toASCIIUpperCase(char c) { + if ('a' <= c && c <= 'z') { + return (char) (c - ('a' - 'A')); + } + return c; + } + + static final byte toASCIIUpperCase(byte b) { + if ('a' <= b && b <= 'z') { + return (byte) (b - ('a' - 'A')); } - return buffer.toString(); + return b; } } diff --git a/archive/src/main/native/java_util_zip_Adler32.c b/archive/src/main/native/java_util_zip_Adler32.c index a7a182a..1b02a11 100644 --- a/archive/src/main/native/java_util_zip_Adler32.c +++ b/archive/src/main/native/java_util_zip_Adler32.c @@ -15,23 +15,25 @@ * limitations under the License. */ +#include "jni.h" #include "hy2sie.h" - #include "zlib.h" - +#include "sieb.h" JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - jbyte *b; jboolean isCopy; jlong result; b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy); + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); + return 0; + } result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); (*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT); @@ -42,9 +44,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateByteImpl (JNIEnv * env, jobject recv, jint val, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - - return adler32 ((uLong) crc, (Bytef *) (&val), 1); + Bytef bytefVal = val; + return adler32 ((uLong) crc, (Bytef *) (&bytefVal), 1); } diff --git a/archive/src/main/native/java_util_zip_CRC32.c b/archive/src/main/native/java_util_zip_CRC32.c index 0688868..cee25e5 100644 --- a/archive/src/main/native/java_util_zip_CRC32.c +++ b/archive/src/main/native/java_util_zip_CRC32.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "sieb.h" #include "zlib.h" @@ -28,8 +29,10 @@ Java_java_util_zip_CRC32_updateImpl (JNIEnv * env, jobject recv, jlong result; b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (b == NULL) + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT)); return result; diff --git a/archive/src/main/native/java_util_zip_Deflater.c b/archive/src/main/native/java_util_zip_Deflater.c index c8bd199..2e0e268 100644 --- a/archive/src/main/native/java_util_zip_Deflater.c +++ b/archive/src/main/native/java_util_zip_Deflater.c @@ -18,11 +18,13 @@ #include "hy2sie.h" #include "zlib.h" -#include "zipsup.h" - +#include "zip.h" +#include "jni.h" +#ifndef HY_ZIP_API void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); +#endif static struct { @@ -52,10 +54,10 @@ Java_java_util_zip_Deflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } - stream->dict = dBytes; + stream->dict = (U_8*) dBytes; } JNIEXPORT jlong JNICALL @@ -94,9 +96,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, jint level, jint strategy, jboolean noHeader) -{ +{ PORT_ACCESS_FROM_ENV (env); - JCLZipStream *jstream; z_stream *stream; int err = 0; @@ -109,7 +110,12 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, // results in 2 x 128K being allocated per Deflater, which is // not acceptable. // END android-changed - +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif + /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); if (jstream == NULL) @@ -141,11 +147,10 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, mlevel, /*Memory allocation for internal compression state. 9 uses the most. */ // END android-changed strategy); - if (err != Z_OK) - { - throwNewIllegalArgumentException (env, ""); - return -1; - } + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); + return -1; + } return (jlong) ((IDATA) jstream); } @@ -170,8 +175,10 @@ Java_java_util_zip_Deflater_setInputImpl (JNIEnv * env, jobject recv, return; } in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (stream->inaddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); stream->stream->next_in = (Bytef *) stream->inaddr; @@ -185,8 +192,6 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushParm) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream; jint err = 0; @@ -203,29 +208,34 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = deflate (stream->stream, flushParm); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); - if (err != Z_OK) - { - if (err == Z_STREAM_END) - { - ((*env)-> - SetBooleanField (env, recv, - gCachedFields.finished, - JNI_TRUE)); - return stream->stream->total_out - sout; - } + if (err != Z_OK) { + if (err == Z_MEM_ERROR) { + throwNewOutOfMemoryError(env, ""); + return 0; } + if (err == Z_STREAM_END) + { + ((*env)-> + SetBooleanField (env, recv, + gCachedFields.finished, + JNI_TRUE)); + return stream->stream->total_out - sout; + } + } if (flushParm != Z_FINISH) { /* Need to update the number of input bytes read. */ ((*env)-> SetIntField (env, recv, - gCachedFields.inRead, - (jint) stream->stream->total_in - sin + inBytes)); + gCachedFields.inRead, + (jint) stream->stream->total_in - sin + inBytes)); } return stream->stream->total_out - sout; } @@ -262,8 +272,6 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, int level, int strategy, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - JCLZipStream *stream; jbyte b = 0; int err = 0; @@ -276,8 +284,9 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); stream->stream->next_out = (Bytef *) & b; err = deflateParams (stream->stream, level, strategy); - if (err != Z_OK) - throwNewIllegalStateException (env, ""); + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalStateException); + } } JNIEXPORT void JNICALL diff --git a/archive/src/main/native/java_util_zip_Inflater.c b/archive/src/main/native/java_util_zip_Inflater.c index d3a7d7c..4b30d4e 100644 --- a/archive/src/main/native/java_util_zip_Inflater.c +++ b/archive/src/main/native/java_util_zip_Inflater.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "zip.h" #include "zlib.h" #include <memory.h> @@ -24,6 +25,7 @@ #include <fcntl.h> +void throwNewDataFormatException (JNIEnv * env, const char *message); void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); @@ -36,27 +38,6 @@ static struct { } gCachedFields; -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - int inCap; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - - -/** - * Throw java.util.zip.DataFormatException - */ -void -throwNewDataFormatException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/util/zip/DataFormatException", message); -} - /* Create a new stream . This stream cannot be used until it has been properly initialized. */ JNIEXPORT jlong JNICALL @@ -69,6 +50,11 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, z_stream *stream; int err = 0; int wbits = 15; /*Use MAX for fastest */ +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); @@ -104,7 +90,7 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, { jclmem_free_memory (env, stream); jclmem_free_memory (env, jstream); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return -1; } @@ -134,8 +120,10 @@ Java_java_util_zip_Inflater_setInputImpl (JNIEnv * env, jobject recv, stream->stream->next_in = (Bytef *) baseAddr; stream->stream->avail_in = len; in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (baseAddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); return; @@ -176,8 +164,6 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle); jint err = 0; @@ -192,9 +178,10 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = inflate (stream->stream, Z_SYNC_FLUSH); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); @@ -217,7 +204,7 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, } else { - throwNewDataFormatException (env, ""); + THROW_ZIP_EXCEPTION(env, err, DataFormatException); return -1; } } @@ -280,7 +267,7 @@ Java_java_util_zip_Inflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } stream->dict = dBytes; @@ -297,11 +284,19 @@ Java_java_util_zip_Inflater_resetImpl (JNIEnv * env, jobject recv, err = inflateReset (stream->stream); if (err != Z_OK) { - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } } +/** + * Throw java.util.zip.DataFormatException + */ +void +throwNewDataFormatException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/util/zip/DataFormatException", message); +} JNIEXPORT jlong JNICALL Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, @@ -311,6 +306,7 @@ Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); return stream->stream->total_out; + } JNIEXPORT jlong JNICALL diff --git a/archive/src/main/native/sieb.c b/archive/src/main/native/sieb.c index ab9430b..6881cf6 100644 --- a/archive/src/main/native/sieb.c +++ b/archive/src/main/native/sieb.c @@ -10,20 +10,6 @@ void throwNewOutOfMemoryError (JNIEnv * env, const char *message) jniThrowException(env, "java/lang/OutOfMemoryError", message); } -// Throw java.lang.IllegalStateException -void throwNewIllegalStateException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalStateException", message); -} - -// Throw java.lang.IllegalArgumentException -void throwNewIllegalArgumentException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalArgumentException", message); -} - - - void * sieb_malloc (JNIEnv * env, size_t byteCnt) { void * adr = malloc(byteCnt); if (adr == 0) { diff --git a/archive/src/main/native/sieb.h b/archive/src/main/native/sieb.h index 536c806..541ad90 100644 --- a/archive/src/main/native/sieb.h +++ b/archive/src/main/native/sieb.h @@ -7,9 +7,8 @@ -void throwNewOutOfMemoryError (JNIEnv * env, const char *message); -void throwNewIllegalArgumentException (JNIEnv * env, const char *message); -void throwNewIllegalStateException (JNIEnv * env, const char *message); +void throwNewOutOfMemoryError (JNIEnv * env, + const char *message); void * sieb_malloc (JNIEnv * env, size_t byteCnt); diff --git a/archive/src/main/native/sub.mk b/archive/src/main/native/sub.mk index 047c319..694c185 100644 --- a/archive/src/main/native/sub.mk +++ b/archive/src/main/native/sub.mk @@ -7,7 +7,8 @@ LOCAL_SRC_FILES := \ java_util_zip_CRC32.c \ java_util_zip_Deflater.c \ java_util_zip_Inflater.c \ - zipalloc.c \ + zip.c \ + zipalloc.c \ sieb.c LOCAL_C_INCLUDES += \ diff --git a/archive/src/main/native/zip.c b/archive/src/main/native/zip.c new file mode 100644 index 0000000..3d15d2a --- /dev/null +++ b/archive/src/main/native/zip.c @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "zip.h" +#include "jni.h" + +/** + * Throw java.lang.IllegalStateException + */ +void +throwNewIllegalStateException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalStateException", message); +} + +/** + * Throw java.lang.IllegalArgumentException + */ +void +throwNewIllegalArgumentException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalArgumentException", message); +} diff --git a/archive/src/main/native/zip.h b/archive/src/main/native/zip.h new file mode 100644 index 0000000..1452073 --- /dev/null +++ b/archive/src/main/native/zip.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(zip_h) +#define zip_h + +#ifndef HY_ZIP_API +#include "zipsup.h" +#else /* HY_ZIP_API */ +#include "vmizip.h" +#endif /* HY_ZIP_API */ + +#include "hymutex.h" + +typedef struct JCLZipFile +{ + struct JCLZipFile *last; + struct JCLZipFile *next; +#ifndef HY_ZIP_API + HyZipFile hyZipFile; +#else + VMIZipFile hyZipFile; +#endif +} JCLZipFile; + +/* Fake JCLZipFile entry. last, next must be in the same position as JCLZipFile */ +typedef struct JCLZipFileLink +{ + JCLZipFile *last; + JCLZipFile *next; + MUTEX mutex; +} JCLZipFileLink; + +// Contents from Harmony's inflater.h was put here: +// +typedef struct JCLZipStream +{ + U_8 *inaddr; + int inCap; + U_8 *dict; + z_stream *stream; +} JCLZipStream; + +#define THROW_ZIP_EXCEPTION(env, err, type) \ + if (err == Z_MEM_ERROR) { \ + throwNewOutOfMemoryError(env, ""); \ + } else { \ + throwNew##type(env, (const char*) zError(err)); \ + } + +void throwNewIllegalStateException PROTOTYPE((JNIEnv* env, + const char* message)); +void throwNewIllegalArgumentException PROTOTYPE((JNIEnv* env, + const char* message)); + +#endif /* zip_h */ diff --git a/archive/src/main/native/zipsup.c b/archive/src/main/native/zipsup.c index 1bbe51f..22ea7e9 100644 --- a/archive/src/main/native/zipsup.c +++ b/archive/src/main/native/zipsup.c @@ -1438,7 +1438,7 @@ retry: return ZIP_ERR_FILE_CORRUPT; /* should never happen! */ } result = zip_establishCache (portLib, zipFile); - if (result) + if (!result) { /* (silently start operating without a cache if we couldn't make a new one) */ } diff --git a/archive/src/main/native/zipsup.h b/archive/src/main/native/zipsup.h index adc086a..67a2eda 100644 --- a/archive/src/main/native/zipsup.h +++ b/archive/src/main/native/zipsup.h @@ -34,23 +34,17 @@ extern "C" #include "zlib.h" -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - typedef struct HyZipCachePool HyZipCachePool; HyZipCachePool * zipsup_GetZipCachePool(HyPortLibrary * portLib); +#if defined(HY_LOCAL_ZLIB) +#define HY_ZIP_DLL_NAME "z" +#else #define HY_ZIP_DLL_NAME "hyzlib" +#endif #define ZIP_INTERNAL_MAX 80 #define ZIP_CM_Reduced1 2 @@ -156,18 +150,6 @@ zipsup_GetZipCachePool(HyPortLibrary * portLib); -// Contents from Harmony's zip.h were put in java_util_zip_ZipFile.c -// and here: -typedef struct JCLZipFile -{ - struct JCLZipFile *last; - struct JCLZipFile *next; - HyZipFile hyZipFile; -} JCLZipFile; - - - - #include "hymutex.h" extern MUTEX zip_GlobalMutex; diff --git a/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java b/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java new file mode 100644 index 0000000..9c28dc2 --- /dev/null +++ b/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.archive.util; + +import junit.framework.TestCase; + +public class UtilTest extends TestCase { + private static final String ASCII_ALPHABET_LC = "abcdefghijklmnopqrstuvwxyz"; + private static final String ASCII_ALPHABET_UC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final byte[] ASCII_ALPHABET_LC_BYTES; + private static final byte[] ASCII_ALPHABET_UC_BYTES; + + static { + ASCII_ALPHABET_LC_BYTES = new byte[ASCII_ALPHABET_LC.length()]; + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_LC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_LC_BYTES[i] = b; + } + + ASCII_ALPHABET_UC_BYTES = new byte[ASCII_ALPHABET_UC.length()]; + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_UC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_UC_BYTES[i] = b; + } + } + + public void testASCIIIgnoreCaseRegionMatches() { + final String s1 = ASCII_ALPHABET_LC; + final String s2 = ASCII_ALPHABET_UC; + for (int i = 0; i < s1.length(); i++) { + assertTrue(Util.ASCIIIgnoreCaseRegionMatches(s1, i, s2, i, s1 + .length() + - i)); + } + } + + public void testToASCIIUpperCaseByte() { + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_LC_BYTES[i])); + } + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_UC_BYTES[i])); + } + } + + public void testToASCIIUpperCaseChar() { + for (int i = 0; i < ASCII_ALPHABET_LC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_LC.charAt(i))); + } + for (int i = 0; i < ASCII_ALPHABET_UC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_UC.charAt(i))); + } + } + + public void testEqualsIgnoreCaseByteArrayByteArray() { + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_LC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_UC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + } + +} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java index 0a8b037..0b3d2cf 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java @@ -421,6 +421,27 @@ public class AttributesTest extends TestCase { assertNull(attribute.get(name)); } + /** + * @tests java.util.jar.Attributes.hashCode() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "hashCode", + args = {} + ) + public void test_hashCode_consistent_with_map() { + MockAttributes mockAttr = new MockAttributes(); + mockAttr.putValue("1", "one"); + assertEquals(mockAttr.getMap().hashCode(), mockAttr.hashCode()); + } + + private static class MockAttributes extends Attributes { + public Map<Object, Object> getMap() { + return map; + } + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -470,7 +491,7 @@ public class AttributesTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "hashCode", args = {} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java index 40eff3b..90144be 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java @@ -72,6 +72,29 @@ public class JarEntryTest extends TestCase { } /** + * @throws IOException + * @tests java.util.jar.JarEntry#JarEntry(java.util.jar.JarEntry) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarEntry", + args = {java.util.jar.JarEntry.class} + ) + public void test_ConstructorLjava_util_jar_JarEntry_on_null() throws IOException { + JarEntry newJarEntry = new JarEntry(jarFile.getJarEntry(entryName)); + assertNotNull(newJarEntry); + + jarEntry = null; + try { + newJarEntry = new JarEntry(jarEntry); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + /** * @tests java.util.jar.JarEntry#JarEntry(java.util.zip.ZipEntry) */ @TestTargetNew( @@ -163,10 +186,21 @@ public class JarEntryTest extends TestCase { JarEntry jarEntry2 = jarFile.getJarEntry("Test.class"); InputStream in = jarFile.getInputStream(jarEntry1); byte[] buffer = new byte[1024]; + // BEGIN android-changed + // the certificates are non-null too early and in.available() fails + // while (in.available() > 0) { + // assertNull("getCertificates() should be null until the entry is read", + // jarEntry1.getCertificates()); + // assertNull(jarEntry2.getCertificates()); + // in.read(buffer); + // } while (in.read(buffer) >= 0); in.close(); + // END android-changed + assertEquals("the file is fully read", -1, in.read()); assertNotNull(jarEntry1.getCertificates()); assertNotNull(jarEntry2.getCertificates()); + in.close(); } /** @@ -187,8 +221,14 @@ public class JarEntryTest extends TestCase { InputStream in = jarFile.getInputStream(jarEntry); byte[] buffer = new byte[1024]; while (in.available() > 0) { + // BEGIN android-changed + // the code signers are non-null too early + // assertNull("getCodeSigners() should be null until the entry is read", + // jarEntry.getCodeSigners()); + // END android-changed in.read(buffer); } + assertEquals("the file is fully read", -1, in.read()); CodeSigner[] codeSigners = jarEntry.getCodeSigners(); assertEquals(2, codeSigners.length); List<?> certs_bob = codeSigners[0].getSignerCertPath() @@ -240,7 +280,7 @@ public class JarEntryTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarEntry", args = {java.util.jar.JarEntry.class} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java index 720f78d..96321a4 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java @@ -14,13 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; import dalvik.annotation.AndroidOnly; import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; @@ -73,13 +71,17 @@ public class JarFileTest extends TestCase { private final String jarName3 = "hyts_manifest1.jar"; private final String jarName4 = "hyts_signed.jar"; - + private final String jarName5 = "hyts_signed_inc.jar"; + private final String integrateJar = "Integrate.jar"; + private final String entryName = "foo/bar/A.class"; private final String entryName3 = "coucou/FileAccess.class"; + private final String integrateJarEntry = "Test.class"; + private File resources; // custom security manager @@ -102,7 +104,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#JarFile(java.io.File) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarFile", args = {java.io.File.class} @@ -300,6 +302,27 @@ public class JarFileTest extends TestCase { } /** + * Constructs JarFile object. + * + * @tests java.util.jar.JarFile#JarFile(java.io.File) + * @tests java.util.jar.JarFile#JarFile(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarFile", + args = {java.io.File.class} + ) + public void testConstructor_file() throws IOException { + File f = new File(resources, jarName); + Support_Resources.copyFile(resources, null, jarName); + assertTrue(new JarFile(f).getEntry(entryName).getName().equals( + entryName)); + assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName() + .equals(entryName)); + } + + /** * @tests java.util.jar.JarFile#entries() */ @TestTargetNew( @@ -316,11 +339,11 @@ public class JarFileTest extends TestCase { Support_Resources.copyFile(resources, null, jarName); JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration<JarEntry> e = jarFile.entries(); - int i = 0; - while (e.hasMoreElements()) { - i++; + int i; + for (i = 0; e.hasMoreElements(); i++) { e.nextElement(); } + assertEquals(jarFile.size(), i); jarFile.close(); assertEquals(6, i); } @@ -336,24 +359,20 @@ public class JarFileTest extends TestCase { JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration<JarEntry> enumeration = jarFile.entries(); jarFile.close(); - boolean pass = false; try { enumeration.hasMoreElements(); + fail("hasMoreElements() did not detect a closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("hasMoreElements did not detect closed jar file", pass); Support_Resources.copyFile(resources, null, jarName); jarFile = new JarFile(new File(resources, jarName)); enumeration = jarFile.entries(); jarFile.close(); - pass = false; try { enumeration.nextElement(); + fail("nextElement() did not detect closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("nextElement did not detect closed jar file", pass); } /** @@ -361,7 +380,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "getEntry", args = {java.lang.String.class} @@ -442,6 +461,92 @@ public class JarFileTest extends TestCase { } } + + /** + * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getEntry", + args = {java.lang.String.class} + ) + public void testGetJarEntry() throws Exception { + Support_Resources.copyFile(resources, null, jarName); + JarFile jarFile = new JarFile(new File(resources, jarName)); + assertEquals("Error in returned entry", 311, jarFile.getEntry( + entryName).getSize()); + jarFile.close(); + + // tests for signed jars + // test all signed jars in the /Testres/Internal/SignedJars directory + String jarDirUrl = Support_Resources + .getResourceURL("/../internalres/signedjars"); + Vector<String> signedJars = new Vector<String>(); + try { + InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream(); + while (is.available() > 0) { + StringBuilder linebuff = new StringBuilder(80); // Typical line + // length + done: while (true) { + int nextByte = is.read(); + switch (nextByte) { + case -1: + break done; + case (byte) '\r': + if (linebuff.length() == 0) { + // ignore + } + break done; + case (byte) '\n': + if (linebuff.length() == 0) { + // ignore + } + break done; + default: + linebuff.append((char) nextByte); + } + } + if (linebuff.length() == 0) { + break; + } + String line = linebuff.toString(); + signedJars.add(line); + } + is.close(); + } catch (IOException e) { + // no list of jars found + } + + for (int i = 0; i < signedJars.size(); i++) { + String jarName = signedJars.get(i); + try { + File file = Support_Resources.getExternalLocalFile(jarDirUrl + + "/" + jarName); + jarFile = new JarFile(file, true); + boolean foundCerts = false; + Enumeration<JarEntry> e = jarFile.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + InputStream is = jarFile.getInputStream(entry); + is.skip(100000); + is.close(); + Certificate[] certs = entry.getCertificates(); + if (certs != null && certs.length > 0) { + foundCerts = true; + break; + } + } + assertTrue( + "No certificates found during signed jar test for jar \"" + + jarName + "\"", foundCerts); + } catch (IOException e) { + fail("Exception during signed jar test for jar \"" + jarName + + "\": " + e.toString()); + } + } + } + /** * @tests java.util.jar.JarFile#getManifest() */ @@ -540,85 +645,6 @@ public class JarFileTest extends TestCase { } /** - * @throws IOException - * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getInputStream", - args = {java.util.zip.ZipEntry.class} - ) - public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { - File localFile = null; - try { - Support_Resources.copyFile(resources, null, jarName); - localFile = new File(resources, jarName); - } catch (Exception e) { - fail("Failed to create local file: " + e); - } - - byte[] b = new byte[1024]; - try { - JarFile jf = new JarFile(localFile); - java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); - // BEGIN android-removed - // jf.close(); - // END android-removed - assertTrue("Returned invalid stream", is.available() > 0); - int r = is.read(b, 0, 1024); - is.close(); - StringBuffer sb = new StringBuffer(r); - for (int i = 0; i < r; i++) { - sb.append((char) (b[i] & 0xff)); - } - String contents = sb.toString(); - assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); - // BEGIN android-added - jf.close(); - // END android-added - } catch (Exception e) { - fail("Exception during test: " + e.toString()); - } - - try { - JarFile jf = new JarFile(localFile); - InputStream in = jf.getInputStream(new JarEntry("invalid")); - assertNull("Got stream for non-existent entry", in); - } catch (Exception e) { - fail("Exception during test 2: " + e); - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // fail("Should throw ZipException"); - } catch (ZipException ee) { - // expected - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.close(); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // The same for IOException - fail("Should throw IllegalStateException"); - } catch (IllegalStateException ee) { - // expected - } - } - - /** * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) */ @TestTargetNew( @@ -660,7 +686,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Exception during test 4: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -682,7 +708,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Failed to create local file 5: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -732,7 +758,37 @@ public class JarFileTest extends TestCase { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); - jarFile.getInputStream(zipEntry); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + } + } + + /** + * The jar is intact, but the entry object is modified. + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getInputStream", + args = {ZipEntry.class} + ) + public void testJarVerificationModifiedEntry() throws IOException { + Support_Resources.copyFile(resources, null, integrateJar); + File f = new File(resources, integrateJar); + + JarFile jarFile = new JarFile(f); + ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() + 1); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + + jarFile = new JarFile(f); + zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() - 1); + try { + //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000); + fail("SecurityException expected"); + } catch (SecurityException e) { + // desired } } @@ -781,7 +837,6 @@ public class JarFileTest extends TestCase { Enumeration<JarEntry> entries = jarFile.entries(); int count = 0; while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); jarFile.getInputStream(zipEntry); count++; @@ -818,7 +873,7 @@ public class JarFileTest extends TestCase { while (in.available() > 0) { in.read(buffer); } - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -827,7 +882,7 @@ public class JarFileTest extends TestCase { /* * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any - * JarEntry will throw security exception, but the apache harmony will not. + * JarEntry will throw security exception. */ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, @@ -846,7 +901,7 @@ public class JarFileTest extends TestCase { ZipEntry zipEntry = entries.nextElement(); try { jarFile.getInputStream(zipEntry); - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -927,4 +982,83 @@ public class JarFileTest extends TestCase { // Can not check IOException } + + /** + * @throws IOException + * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { + File localFile = null; + try { + Support_Resources.copyFile(resources, null, jarName); + localFile = new File(resources, jarName); + } catch (Exception e) { + fail("Failed to create local file: " + e); + } + + byte[] b = new byte[1024]; + try { + JarFile jf = new JarFile(localFile); + java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); + // BEGIN android-removed + // jf.close(); + // END android-removed + assertTrue("Returned invalid stream", is.available() > 0); + int r = is.read(b, 0, 1024); + is.close(); + StringBuffer sb = new StringBuffer(r); + for (int i = 0; i < r; i++) { + sb.append((char) (b[i] & 0xff)); + } + String contents = sb.toString(); + assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); + // BEGIN android-added + jf.close(); + // END android-added + } catch (Exception e) { + fail("Exception during test: " + e.toString()); + } + + try { + JarFile jf = new JarFile(localFile); + InputStream in = jf.getInputStream(new JarEntry("invalid")); + assertNull("Got stream for non-existent entry", in); + } catch (Exception e) { + fail("Exception during test 2: " + e); + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // fail("Should throw ZipException"); + } catch (ZipException ee) { + // expected + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.close(); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // The same for IOException + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ee) { + // expected + } + } } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java index e652137..acdad71 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java @@ -48,7 +48,7 @@ public class JarOutputStreamTest extends junit.framework.TestCase { method = "putNextEntry", args = {java.util.zip.ZipEntry.class} ) - public void test_putNextEntryLjava_util_zip_ZipEntry() { + public void test_putNextEntryLjava_util_zip_ZipEntry() throws Exception { // testClass file`s actual extension is .class, since having .class // extension files in source dir causes // problems on eclipse, the extension is changed into .ser or it can be @@ -76,35 +76,30 @@ public class JarOutputStreamTest extends junit.framework.TestCase { File outputJar = null; JarOutputStream jout = null; - try { - // open the output jarfile - outputJar = File.createTempFile("hyts_", ".jar"); - jout = new JarOutputStream(new FileOutputStream(outputJar), - newman); - jout.putNextEntry(new JarEntry(entryName)); - } catch (Exception e) { - fail("Error creating JarOutputStream: " + e); - } + // open the output jarfile + outputJar = File.createTempFile("hyts_", ".jar"); + jout = new JarOutputStream(new FileOutputStream(outputJar), + newman); + jout.putNextEntry(new JarEntry(entryName)); + File resources = Support_Resources.createTempFolder(); - try { - // read in the class file, and output it to the jar - Support_Resources.copyFile(resources, null, testClass); - URL jarURL = new URL((new File(resources, testClass)).toURL() - .toString()); - InputStream jis = jarURL.openStream(); - - byte[] bytes = new byte[1024]; - int len; - while ((len = jis.read(bytes)) != -1) { - jout.write(bytes, 0, len); - } - - jout.flush(); - jout.close(); - jis.close(); - } catch (Exception e) { - fail("Error writing JAR file for testing: " + e); + + // read in the class file, and output it to the jar + Support_Resources.copyFile(resources, null, testClass); + URL jarURL = new URL((new File(resources, testClass)).toURL() + .toString()); + InputStream jis = jarURL.openStream(); + + byte[] bytes = new byte[1024]; + int len; + while ((len = jis.read(bytes)) != -1) { + jout.write(bytes, 0, len); } + + jout.flush(); + jout.close(); + jis.close(); + String res = null; // set up the VM parameters String[] args = new String[2]; diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java index 57e4744..42b2543 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java @@ -14,12 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; -import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; @@ -28,14 +27,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; +import java.net.MalformedURLException; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import junit.framework.TestCase; + import tests.support.resource.Support_Resources; @TestTargetClass(Manifest.class) @@ -49,6 +49,10 @@ public class ManifestTest extends TestCase { private Manifest m2; + private final String ATT_ENTRY_NAME = "HasAttributes.txt"; + + private final String MANIFEST_NAME = "manifest/hyts_MANIFEST.MF"; + private File resources; @Override @@ -68,6 +72,19 @@ public class ManifestTest extends TestCase { } } + private Manifest getManifest(String fileName) { + try { + Support_Resources.copyFile(resources, null, fileName); + JarFile jarFile = new JarFile(new File(resources, fileName)); + Manifest m = jarFile.getManifest(); + jarFile.close(); + return m; + } catch (Exception e) { + fail("Exception during setup: " + e.toString()); + return null; + } + } + /** * @tests java.util.jar.Manifest#Manifest() */ @@ -87,264 +104,136 @@ public class ManifestTest extends TestCase { } /** - * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "IOException checking missed.", - method = "Manifest", - args = {java.io.InputStream.class} - ) - public void test_ConstructorLjava_io_InputStream() { - // Test for method java.util.jar.Manifest(java.io.InputStream) - /* - * ByteArrayOutputStream baos = new ByteArrayOutputStream(); - * m2.write(baos); InputSteam is = new ByteArrayInputStream - * (baos.toByteArray()); Manifest myManifest = new Manifest (is); - * assertTrue("Manifests should be equal", myManifest.equals(m2)); - */ - - Manifest manifest = null; - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - try { - is.close(); - } catch (IOException e1) { - fail("Failed to close InputStream object"); - } - try { - manifest = new Manifest(is); - fail("IOException expected"); - } catch (MalformedURLException e) { - fail("IOException expected"); - } catch (IOException e) { - // expected - } - } - - /** - * @tests java.util.jar.Manifest#clear() + * @tests java.util.jar.Manifest#Manifest(java.util.jar.Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clear", - args = {} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_clear() { - // Test for method void java.util.jar.Manifest.clear() - m2.clear(); - assertTrue("Should have no entries", m2.getEntries().isEmpty()); - assertTrue("Should have no main attributes", m2.getMainAttributes() - .isEmpty()); + public void testCopyingConstructor() throws IOException { + Manifest firstManifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + Manifest secondManifest = new Manifest(firstManifest); + assertEquals(firstManifest, secondManifest); } /** - * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + * @tests java.util.jar.Manifest#Manifest(Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getAttributes", - args = {java.lang.String.class} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_getAttributesLjava_lang_String() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getAttributes(java.lang.String) - assertNull("Should not exist", m2.getAttributes("Doesn't Exist")); - assertEquals("Should exist", "OK", m2 - .getAttributes("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); + public void test_ConstructorLjava_util_jar_Manifest() { + // Test for method java.util.jar.Manifest() + Manifest emptyManifest = new Manifest(); + Manifest emptyClone = new Manifest(emptyManifest); + assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); + assertTrue("Should have no main attributes", emptyClone + .getMainAttributes().isEmpty()); + assertEquals(emptyClone, emptyManifest); + assertEquals(emptyClone, emptyManifest.clone()); } - /** - * @tests java.util.jar.Manifest#getEntries() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getEntries", - args = {} - ) - public void test_getEntries() { - // Test for method java.util.Map java.util.jar.Manifest.getEntries() - Map<String, Attributes> myMap = m2.getEntries(); - assertNull("Shouldn't exist", myMap.get("Doesn't exist")); - assertEquals("Should exist", "OK", myMap.get("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); - + private void assertAttribute(Attributes attr, String name, String value) { + assertEquals("Incorrect " + name, value, attr.getValue(name)); } - /** - * @tests java.util.jar.Manifest#getMainAttributes() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getMainAttributes", - args = {} - ) - public void test_getMainAttributes() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getMainAttributes() - Attributes a = m.getMainAttributes(); - assertEquals("Manifest_Version should return 1.0", "1.0", a - .get(Attributes.Name.MANIFEST_VERSION)); + private void checkManifest(Manifest manifest) { + Attributes main = manifest.getMainAttributes(); + assertAttribute(main, "Bundle-Name", "ClientSupport"); + assertAttribute(main, "Bundle-Description", + "Provides SessionService, AuthenticationService. Extends RegistryService."); + assertAttribute(main, "Bundle-Activator", + "com.ibm.ive.eccomm.client.support.ClientSupportActivator"); + assertAttribute( + main, + "Import-Package", + "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client"); + assertAttribute( + main, + "Import-Service", + "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService"); + assertAttribute( + main, + "Export-Package", + "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0"); + assertAttribute( + main, + "Export-Service", + "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService"); + assertAttribute(main, "Bundle-Vendor", "IBM"); + assertAttribute(main, "Bundle-Version", "1.2.0"); } /** - * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) */ @TestTargetNew( level = TestLevel.COMPLETE, - notes = "", - method = "read", + notes = "IOException checking missed.", + method = "Manifest", args = {java.io.InputStream.class} ) - public void test_readLjava_io_InputStream() { - // Regression for HARMONY-89 - InputStream is = new InputStreamImpl(); - try { - new Manifest().read(is); - fail("Assert 0: Should have thrown IOException"); - } catch (IOException e) { - // expected - } + public void test_ConstructorLjava_io_InputStream() throws IOException { + Manifest m = getManifest(attJarName); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + InputStream is = new ByteArrayInputStream(baos.toByteArray()); + Manifest mCopy = new Manifest(is); + assertEquals(m, mCopy); - Manifest manifest = new Manifest(); - try { - manifest.read(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Can nor read manifest"); - } catch (IOException e) { - fail("Can nor read manifest"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + checkManifest(manifest); - // helper class - class InputStreamImpl extends InputStream { - public InputStreamImpl() { - super(); - } + // regression test for HARMONY-5424 + String manifestContent = "Manifest-Version: 1.0\nCreated-By: Apache\nPackage: \nBuild-Jdk: 1.4.1_01\n\n" + + "Name: \nSpecification-Title: foo\nSpecification-Version: 1.0\nSpecification-Vendor: \n" + + "Implementation-Title: \nImplementation-Version: 1.0\nImplementation-Vendor: \n\n"; + ByteArrayInputStream bis = new ByteArrayInputStream(manifestContent + .getBytes("ISO-8859-1")); - @Override - public int read() { - return 0; - } + + Manifest mf = new Manifest(bis); + assertEquals("Should be 4 main attributes", 4, mf.getMainAttributes() + .size()); + + Map<String, Attributes> entries = mf.getEntries(); + assertEquals("Should be one named entry", 1, entries.size()); + + Attributes namedEntryAttributes = (Attributes) (entries.get("")); + assertEquals("Should be 6 named entry attributes", 6, + namedEntryAttributes.size()); } /** - * @tests java.util.jar.Manifest#Manifest(Manifest) + * @tests java.util.jar.Manifest#clear() */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Manifest", - args = {java.util.jar.Manifest.class} + level = TestLevel.COMPLETE, + notes = "", + method = "clear", + args = {} ) - public void test_ConstructorLjava_util_jar_Manifest() { - // Test for method java.util.jar.Manifest() - Manifest emptyManifest = new Manifest(); - Manifest emptyClone = new Manifest(emptyManifest); - assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); - assertTrue("Should have no main attributes", emptyClone - .getMainAttributes().isEmpty()); - assertEquals(emptyClone, emptyManifest); - assertEquals(emptyClone, emptyManifest.clone()); + public void test_clear() { + m2.clear(); + assertTrue("Should have no entries", m2.getEntries().isEmpty()); + assertTrue("Should have no main attributes", m2.getMainAttributes() + .isEmpty()); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clone", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "clone", + args = {} ) - public void test_clone() { + public void test_clone() throws IOException { Manifest emptyManifest = new Manifest(); Manifest emptyClone = (Manifest) emptyManifest.clone(); assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); @@ -354,88 +243,25 @@ public class ManifestTest extends TestCase { assertEquals(emptyManifest.clone().getClass().getName(), "java.util.jar.Manifest"); - Manifest manifest = null; - try { - manifest = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifestClone = (Manifest) manifest.clone(); - Attributes main = manifestClone.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); + manifestClone.getMainAttributes(); + checkManifest(manifestClone); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "equals", - args = {java.lang.Object.class} + level = TestLevel.COMPLETE, + notes = "", + method = "equals", + args = {java.lang.Object.class} ) - public void test_equals() { - Manifest manifest1 = null; - Manifest manifest2 = null; + public void test_equals() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); + Manifest manifest2 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest3 = new Manifest(); - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest1 = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - - try { - manifest2 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertTrue(manifest1.equals(manifest1)); assertTrue(manifest1.equals(manifest2)); @@ -444,27 +270,69 @@ public class ManifestTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "hashCode", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "hashCode", + args = {} ) - public void test_hashCode() { - Manifest manifest1 = null; + public void test_hashCode() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest2 = new Manifest(); - InputStream is = null; - try { - manifest1 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertEquals(manifest1.hashCode(), manifest1.hashCode()); assertNotSame(manifest1.hashCode(), manifest2.hashCode()); } + /** + * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getAttributes", + args = {String.class} + ) + public void test_getAttributesLjava_lang_String() { + assertNull("Should not exist", + m2.getAttributes("Doesn't Exist")); + assertEquals("Should exist", "OK", m2.getAttributes("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getEntries() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getEntries", + args = {} + ) + public void test_getEntries() { + Map<String, Attributes> myMap = m2.getEntries(); + assertNull("Shouldn't exist", myMap.get("Doesn't exist")); + assertEquals("Should exist", + "OK", myMap.get("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getMainAttributes() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getMainAttributes", + args = {} + ) + public void test_getMainAttributes() { + // Test for method java.util.jar.Attributes + // java.util.jar.Manifest.getMainAttributes() + Attributes a = m.getMainAttributes(); + assertEquals("Manifest_Version should return 1.0", "1.0", a.get( + Attributes.Name.MANIFEST_VERSION)); + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -510,4 +378,219 @@ public class ManifestTest extends TestCase { assertTrue(manifest1.equals(manifest2)); } + + /** + * Ensures compatibility with manifests produced by gcc. + * + * @see <a + * href="http://issues.apache.org/jira/browse/HARMONY-5662">HARMONY-5662</a> + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {InputStream.class} + ) + public void testNul() throws IOException { + String manifestContent = + "Manifest-Version: 1.0\nCreated-By: nasty gcc tool\n\n\0"; + + byte[] bytes = manifestContent.getBytes("ISO-8859-1"); + new Manifest(new ByteArrayInputStream(bytes)); // the last NUL is ok + + bytes[bytes.length - 1] = 26; + new Manifest(new ByteArrayInputStream(bytes)); // the last EOF is ok + + bytes[bytes.length - 1] = 'A'; // the last line ignored + new Manifest(new ByteArrayInputStream(bytes)); + + bytes[2] = 0; // NUL char in Manifest + try { + new Manifest(new ByteArrayInputStream(bytes)); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "Manifest", + args = {InputStream.class} + ) + @KnownFailure("CharsetDecoder fails with an IllegalStateException") + public void testDecoding() throws IOException { + Manifest m = getManifest(attJarName); + final byte[] bVendor = new byte[] { (byte) 0xd0, (byte) 0x9C, + (byte) 0xd0, (byte) 0xb8, (byte) 0xd0, (byte) 0xbb, + (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, (byte) 0x8f, ' ', + (byte) 0xd0, (byte) 0xb4, (byte) 0xd0, (byte) 0xbe, + (byte) 0xd1, (byte) 0x87, (byte) 0xd1, (byte) 0x83, + (byte) 0xd0, (byte) 0xbd, (byte) 0xd1, (byte) 0x8C, + (byte) 0xd0, (byte) 0xba, (byte) 0xd0, (byte) 0xb0, ' ', + (byte) 0xd0, (byte) 0x9C, (byte) 0xd0, (byte) 0xb0, + (byte) 0xd1, (byte) 0x88, (byte) 0xd0, (byte) 0xb0 }; + + final byte[] bSpec = new byte[] { (byte) 0xe1, (byte) 0x88, + (byte) 0xb0, (byte) 0xe1, (byte) 0x88, (byte) 0x8b, + (byte) 0xe1, (byte) 0x88, (byte) 0x9d, ' ', (byte) 0xe1, + (byte) 0x9a, (byte) 0xa0, (byte) 0xe1, (byte) 0x9a, + (byte) 0xb1, (byte) 0xe1, (byte) 0x9b, (byte) 0x81, + (byte) 0xe1, (byte) 0x9a, (byte) 0xa6, ' ', (byte) 0xd8, + (byte) 0xb3, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xa7, (byte) 0xd9, (byte) 0x85, ' ', (byte) 0xd8, + (byte) 0xb9, (byte) 0xd8, (byte) 0xb3, (byte) 0xd9, + (byte) 0x84, (byte) 0xd8, (byte) 0xa7, (byte) 0xd9, + (byte) 0x85, (byte) 0xd8, (byte) 0xa9, ' ', (byte) 0xdc, + (byte) 0xab, (byte) 0xdc, (byte) 0xa0, (byte) 0xdc, + (byte) 0xa1, (byte) 0xdc, (byte) 0x90, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xd0, (byte) 0xa0, (byte) 0xd0, + (byte) 0xb5, (byte) 0xd0, (byte) 0xba, (byte) 0xd1, + (byte) 0x8a, (byte) 0xd0, (byte) 0xb5, (byte) 0xd0, + (byte) 0xbb, ' ', (byte) 0xd0, (byte) 0x9c, (byte) 0xd0, + (byte) 0xb8, (byte) 0xd1, (byte) 0x80, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xd0, + (byte) 0x9c, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x88, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x80, ' ', (byte) 0xe1, (byte) 0x8f, (byte) 0x99, + (byte) 0xe1, (byte) 0x8e, (byte) 0xaf, (byte) 0xe1, + (byte) 0x8f, (byte) 0xb1, ' ', (byte) 0xcf, (byte) 0xa8, + (byte) 0xce, (byte) 0xb9, (byte) 0xcf, (byte) 0x81, + (byte) 0xce, (byte) 0xb7, (byte) 0xce, (byte) 0xbd, + (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xde, (byte) 0x90, + (byte) 0xde, (byte) 0xaa, (byte) 0xde, (byte) 0x85, + (byte) 0xde, (byte) 0xa6, ' ', (byte) 0xe0, (byte) 0xbd, + (byte) 0x82, (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xce, + (byte) 0x95, (byte) 0xce, (byte) 0xb9, (byte) 0xcf, + (byte) 0x81, (byte) 0xce, (byte) 0xae, (byte) 0xce, + (byte) 0xbd, (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xd8, + (byte) 0xb5, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xad, ' ', (byte) 0xe0, (byte) 0xaa, (byte) 0xb6, + (byte) 0xe0, (byte) 0xaa, (byte) 0xbe, (byte) 0xe0, + (byte) 0xaa, (byte) 0x82, (byte) 0xe0, (byte) 0xaa, + (byte) 0xa4, (byte) 0xe0, (byte) 0xaa, (byte) 0xbf, ' ', + (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, (byte) 0xe5, + (byte) 0x92, (byte) 0x8c, ' ', (byte) 0xd7, (byte) 0xa9, + (byte) 0xd7, (byte) 0x9c, (byte) 0xd7, (byte) 0x95, + (byte) 0xd7, (byte) 0x9d, ' ', (byte) 0xd7, (byte) 0xa4, + (byte) 0xd7, (byte) 0xa8, (byte) 0xd7, (byte) 0x99, + (byte) 0xd7, (byte) 0x93, (byte) 0xd7, (byte) 0x9f, ' ', + (byte) 0xe5, (byte) 0x92, (byte) 0x8c, (byte) 0xe5, + (byte) 0xb9, (byte) 0xb3, ' ', (byte) 0xe5, (byte) 0x92, + (byte) 0x8c, (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, ' ', + (byte) 0xd8, (byte) 0xaa, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x86, (byte) 0xda, (byte) 0x86, + (byte) 0xd9, (byte) 0x84, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x82, ' ', (byte) 0xe0, (byte) 0xae, + (byte) 0x85, (byte) 0xe0, (byte) 0xae, (byte) 0xae, + (byte) 0xe0, (byte) 0xaf, (byte) 0x88, (byte) 0xe0, + (byte) 0xae, (byte) 0xa4, (byte) 0xe0, (byte) 0xae, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xb0, (byte) 0xb6, + (byte) 0xe0, (byte) 0xb0, (byte) 0xbe, (byte) 0xe0, + (byte) 0xb0, (byte) 0x82, (byte) 0xe0, (byte) 0xb0, + (byte) 0xa4, (byte) 0xe0, (byte) 0xb0, (byte) 0xbf, ' ', + (byte) 0xe0, (byte) 0xb8, (byte) 0xaa, (byte) 0xe0, + (byte) 0xb8, (byte) 0xb1, (byte) 0xe0, (byte) 0xb8, + (byte) 0x99, (byte) 0xe0, (byte) 0xb8, (byte) 0x95, + (byte) 0xe0, (byte) 0xb8, (byte) 0xb4, (byte) 0xe0, + (byte) 0xb8, (byte) 0xa0, (byte) 0xe0, (byte) 0xb8, + (byte) 0xb2, (byte) 0xe0, (byte) 0xb8, (byte) 0x9e, ' ', + (byte) 0xe1, (byte) 0x88, (byte) 0xb0, (byte) 0xe1, + (byte) 0x88, (byte) 0x8b, (byte) 0xe1, (byte) 0x88, + (byte) 0x9d, ' ', (byte) 0xe0, (byte) 0xb7, (byte) 0x83, + (byte) 0xe0, (byte) 0xb7, (byte) 0x8f, (byte) 0xe0, + (byte) 0xb6, (byte) 0xb8, (byte) 0xe0, (byte) 0xb6, + (byte) 0xba, ' ', (byte) 0xe0, (byte) 0xa4, (byte) 0xb6, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbe, (byte) 0xe0, + (byte) 0xa4, (byte) 0xa8, (byte) 0xe0, (byte) 0xa5, + (byte) 0x8d, (byte) 0xe0, (byte) 0xa4, (byte) 0xa4, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbf, (byte) 0xe0, + (byte) 0xa4, (byte) 0x83, ' ', (byte) 0xe1, (byte) 0x83, + (byte) 0x9b, (byte) 0xe1, (byte) 0x83, (byte) 0xa8, + (byte) 0xe1, (byte) 0x83, (byte) 0x95, (byte) 0xe1, + (byte) 0x83, (byte) 0x98, (byte) 0xe1, (byte) 0x83, + (byte) 0x93, (byte) 0xe1, (byte) 0x83, (byte) 0x9d, + (byte) 0xe1, (byte) 0x83, (byte) 0x91, (byte) 0xe1, + (byte) 0x83, (byte) 0x90 }; + // TODO Cannot make the following word work, encoder changes needed + // (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb2, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbd, (byte) 0x85, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb8, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, ' ' + + final String vendor = new String(bVendor, "UTF-8"); + final String spec = new String(bSpec, "UTF-8"); + m.getMainAttributes() + .put(Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getAttributes(ATT_ENTRY_NAME).put( + Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getEntries().get(ATT_ENTRY_NAME).put( + Attributes.Name.SPECIFICATION_TITLE, spec); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + m = new Manifest(new ByteArrayInputStream(baos.toByteArray())); + + assertEquals(vendor, m.getMainAttributes().get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(vendor, m.getEntries().get(ATT_ENTRY_NAME).get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(spec, m.getAttributes(ATT_ENTRY_NAME).get( + Attributes.Name.SPECIFICATION_TITLE)); + } + + /** + * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "read", + args = {InputStream.class} + ) + public void testRead() { + // Regression for HARMONY-89 + InputStream is = new InputStreamImpl(); + try { + new Manifest().read(is); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + // helper class + private class InputStreamImpl extends InputStream { + public InputStreamImpl() { + super(); + } + + @Override + public int read() { + return 0; + } + } } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java index 75060bd..1e8ddb4 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java @@ -25,6 +25,8 @@ import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -310,7 +312,43 @@ public class GZIPInputStreamTest extends junit.framework.TestCase { } } - @Override + /** + * Regression test for HARMONY-3703. + * @tests java.util.zip.GZIPInputStream#read() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "read", + args = {byte[].class} + ) + public void test_read() throws IOException { + GZIPInputStream gis = null; + int result = 0; + byte[] buffer = new byte[] {1,2,3,4,5,6,7,8,9,10}; + File f = new File(resources.getAbsolutePath() + "test.gz"); + FileOutputStream out = new FileOutputStream(f); + GZIPOutputStream gout = new GZIPOutputStream(out); + + // write 100 bytes to the stream + for(int i = 0; i < 10; i++) { + gout.write(buffer); + } + gout.finish(); + out.write(1); + out.close(); + + gis = new GZIPInputStream(new FileInputStream(f)); + buffer = new byte[100]; + gis.read(buffer); + result = gis.read(); + gis.close(); + f.delete(); + + assertEquals("Incorrect value returned at the end of the file", -1, result); + } + + @Override protected void setUp() { resources = Support_Resources.createTempFolder(); } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java index 9b23b56..b71ce63 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java @@ -201,8 +201,6 @@ public class GZIPOutputStreamTest extends junit.framework.TestCase { int r = 0; try { outGZIP.write(byteArray, 0, 11); - } catch (ArrayIndexOutOfBoundsException e) { - r = 1; } catch (IndexOutOfBoundsException ee) { r = 1; } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java index 8b89180..6039c5b 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java @@ -409,6 +409,22 @@ public class InflaterTest extends junit.framework.TestCase { } /** + * @tests java.util.zip.Inflater#Inflater() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "Inflater", + args = {} + ) + public void test_Constructor() { + // test method of java.util.zip.inflater.Inflater() + Inflater inflate = new Inflater(); + assertNotNull("failed to create the instance of inflater", + inflate); + } + + /** * @tests java.util.zip.Inflater#inflate(byte[], int, int) */ @TestTargetNew( @@ -504,27 +520,6 @@ public class InflaterTest extends junit.framework.TestCase { } /** - * @tests java.util.zip.Inflater#Inflater() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Inflater", - args = {} - ) - public void test_Constructor() { - // test method of java.util.zip.inflater.Inflater() - try { - Inflater inflate = new Inflater(); - assertNotNull("failed to create the instance of inflater", inflate); - - } catch (Exception e) { - - assertTrue("Inflate () constructor threw an exception", true); - } - } - - /** * @tests java.util.zip.Inflater#Inflater(boolean) */ @TestTargetNew( diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java index 5530a2e..c9e7bb8 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java @@ -67,7 +67,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void checkPermission(Permission perm) { // only check if it's a FilePermission because Locale checks // for a PropertyPermission with action"read" to get system props. - if (perm instanceof FilePermission + if (perm instanceof FilePermission && perm.getActions().equals(forbidenPermissionAction)) { throw new SecurityException(); } @@ -145,7 +145,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void test_ConstructorLjava_lang_String() throws IOException { String oldUserDir = System.getProperty("user.dir"); System.setProperty("user.dir", System.getProperty("java.io.tmpdir")); - + zfile.close(); // about to reopen the same temp file ZipFile zip = new ZipFile(tempFileName); zip.close(); @@ -260,7 +260,7 @@ public class ZipFileTest extends junit.framework.TestCase { method = "entries", args = {} ) - public void test_entries() { + public void test_entries() throws Exception { // Test for method java.util.Enumeration java.util.zip.ZipFile.entries() Enumeration<? extends ZipEntry> enumer = zfile.entries(); int c = 0; @@ -270,20 +270,16 @@ public class ZipFileTest extends junit.framework.TestCase { } assertTrue("Incorrect number of entries returned: " + c, c == 6); + Enumeration<? extends ZipEntry> enumeration = zfile.entries(); + zfile.close(); + zfile = null; + boolean pass = false; try { - Enumeration<? extends ZipEntry> enumeration = zfile.entries(); - zfile.close(); - zfile = null; - boolean pass = false; - try { - enumeration.hasMoreElements(); - } catch (IllegalStateException e) { - pass = true; - } - assertTrue("did not detect closed jar file", pass); - } catch (Exception e) { - fail("Exception during entries test: " + e.toString()); + enumeration.hasMoreElements(); + } catch (IllegalStateException e) { + pass = true; } + assertTrue("did not detect closed jar file", pass); } /** @@ -454,6 +450,99 @@ public class ZipFileTest extends junit.framework.TestCase { } /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[6]; + byte[] rbuf2 = new byte[6]; + int r1, r2; + r1 = is.read(rbuf1); + assertEquals(rbuf1.length, r1); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + + is.reset(); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.reset(); + + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.close(); + + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.reset(); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.close(); + } + + /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset_subtest0() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[12]; + byte[] rbuf2 = new byte[12]; + int r = is.read(rbuf1, 0, 4); + assertEquals(4, r); + is.mark(0); + r = is.read(rbuf1); + assertEquals(8, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf2); + assertEquals(8, r); + assertEquals(-1, is.read()); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r = is.read(rbuf3, 0, 3000); + assertEquals(3000, r); + is.mark(0); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + is.close(); + } + + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. */ diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java index 9a5f63a..8ca551d 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java @@ -170,11 +170,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { public void test_setCommentLjava_lang_String() { // There is no way to get the comment back, so no way to determine if // the comment is set correct - try { - zos.setComment("test setComment"); - } catch (Exception e) { - fail("Trying to set comment failed"); - } + zos.setComment("test setComment"); + try { zos.setComment(new String(new byte[0xFFFF + 1])); fail("Comment over 0xFFFF in length should throw exception"); @@ -301,6 +298,17 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IndexOutOfBoundsException e) { // expected } + + // Regression for HARMONY-4405 + try { + zip.write(null, 0, -2); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + // Close stream because ZIP is invalid + stream.close(); } /** @@ -337,6 +345,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IOException e2) { // expected } + + zip1.close(); } @Override diff --git a/auth/src/main/java/javax/security/auth/AuthPermission.java b/auth/src/main/java/javax/security/auth/AuthPermission.java index 06ea3fb..697d5c7 100644 --- a/auth/src/main/java/javax/security/auth/AuthPermission.java +++ b/auth/src/main/java/javax/security/auth/AuthPermission.java @@ -26,33 +26,31 @@ import org.apache.harmony.auth.internal.nls.Messages; * <i>target name</i> of the permission specifies which methods are allowed * without specifying the concrete action lists. Possible target names and * associated authentication permissions are: - * + * * <pre> * doAs invoke Subject.doAs methods. * doAsPrivileged invoke the Subject.doAsPrivileged methods. * getSubject invoke Subject.getSubject(). * getSubjectFromDomainCombiner invoke SubjectDomainCombiner.getSubject(). * setReadOnly invoke Subject.setReadonly(). - * modifyPrincipals modify the set of principals + * modifyPrincipals modify the set of principals * associated with a Subject. * modifyPublicCredentials modify the set of public credentials * associated with a Subject. * modifyPrivateCredentials modify the set of private credentials * associated with a Subject. - * refreshCredential invoke the refresh method on a credential of a + * refreshCredential invoke the refresh method on a credential of a * refreshable credential class. * destroyCredential invoke the destroy method on a credential of a * destroyable credential class. * createLoginContext.<i>name</i> instantiate a LoginContext with the * specified name. The wildcard name ('*') * allows to a LoginContext of any name. - * getLoginConfiguration invoke the getConfiguration method of + * getLoginConfiguration invoke the getConfiguration method of * javax.security.auth.login.Configuration. - * refreshLoginConfiguration Invoke the refresh method of + * refreshLoginConfiguration Invoke the refresh method of * javax.security.auth.login.Configuration. * </pre> - * - * @since Android 1.0 */ public final class AuthPermission extends BasicPermission { @@ -77,7 +75,7 @@ public final class AuthPermission extends BasicPermission { /** * Creates an authentication permission with the specified target name. - * + * * @param name * the target name of this authentication permission. */ @@ -87,7 +85,7 @@ public final class AuthPermission extends BasicPermission { /** * Creates an authentication permission with the specified target name. - * + * * @param name * the target name of this authentication permission. * @param actions diff --git a/auth/src/main/java/javax/security/auth/DestroyFailedException.java b/auth/src/main/java/javax/security/auth/DestroyFailedException.java index a5438a6..27d4dfd 100644 --- a/auth/src/main/java/javax/security/auth/DestroyFailedException.java +++ b/auth/src/main/java/javax/security/auth/DestroyFailedException.java @@ -19,8 +19,6 @@ package javax.security.auth; /** * Signals that the {@link Destroyable#destroy()} method failed. - * - * @since Android 1.0 */ public class DestroyFailedException extends Exception { @@ -35,7 +33,7 @@ public class DestroyFailedException extends Exception { /** * Creates an exception of type {@code DestroyFailedException}. - * + * * @param message * A detail message that describes the reason for this exception. */ diff --git a/auth/src/main/java/javax/security/auth/Destroyable.java b/auth/src/main/java/javax/security/auth/Destroyable.java index 6194db6..b4d0fa2 100644 --- a/auth/src/main/java/javax/security/auth/Destroyable.java +++ b/auth/src/main/java/javax/security/auth/Destroyable.java @@ -20,8 +20,6 @@ package javax.security.auth; /** * Allows for special treatment of sensitive information, when it comes to * destroying or clearing of the data. - * - * @since Android 1.0 */ public interface Destroyable { @@ -29,7 +27,7 @@ public interface Destroyable { * Erases the sensitive information. Once an object is destroyed any calls * to its methods will throw an {@code IllegalStateException}. If it does * not succeed a DestroyFailedException is thrown. - * + * * @throws DestroyFailedException * if the information cannot be erased. */ @@ -37,7 +35,7 @@ public interface Destroyable { /** * Returns {@code true} once an object has been safely destroyed. - * + * * @return whether the object has been safely destroyed. */ boolean isDestroyed(); diff --git a/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java b/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java index d92ede5..35072b8 100644 --- a/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java +++ b/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java @@ -31,11 +31,11 @@ import org.apache.harmony.auth.internal.nls.Messages; * Protects private credential objects belonging to a {@code Subject}. It has * only one action which is "read". The target name of this permission has a * special syntax: - * + * * <pre> * targetName = CredentialClass {PrincipalClass "PrincipalName"}* * </pre> - * + * * First it states a credential class and is followed then by a list of one or * more principals identifying the subject. * <p> @@ -43,12 +43,11 @@ import org.apache.harmony.auth.internal.nls.Messages; * Principal} class followed by the principal name in quotes. For example, the * following file may define permission to read the private credentials of a * principal named "Bob": "com.sun.PrivateCredential com.sun.Principal \"Bob\"" - * </p> + * <p> * The syntax also allows the use of the wildcard "*" in place of {@code * CredentialClass} or {@code PrincipalClass} and/or {@code PrincipalName}. - * + * * @see Principal - * @since Android 1.0 */ public final class PrivateCredentialPermission extends Permission { @@ -69,7 +68,7 @@ public final class PrivateCredentialPermission extends Permission { * Creates a new permission for private credentials specified by the target * name {@code name} and an {@code action}. The action is always * {@code "read"}. - * + * * @param name * the target name of the permission. * @param action @@ -197,13 +196,13 @@ public final class PrivateCredentialPermission extends Permission { * dimension of the array corresponds to the number of principals. The * second dimension defines either the name of the {@code PrincipalClass} * [x][0] or the value of {@code PrincipalName} [x][1]. - * + * <p> * This corresponds to the the target name's syntax: - * + * * <pre> * targetName = CredentialClass {PrincipalClass "PrincipalName"}* * </pre> - * + * * @return the principal classes and names associated with this {@code * PrivateCredentialPermission}. */ @@ -225,7 +224,7 @@ public final class PrivateCredentialPermission extends Permission { /** * Returns the class name of the credential associated with this permission. - * + * * @return the class name of the credential associated with this permission. */ public String getCredentialClass() { diff --git a/auth/src/main/java/javax/security/auth/Subject.java b/auth/src/main/java/javax/security/auth/Subject.java index 5a4cceb..5bf6bba 100644 --- a/auth/src/main/java/javax/security/auth/Subject.java +++ b/auth/src/main/java/javax/security/auth/Subject.java @@ -51,8 +51,6 @@ import org.apache.harmony.auth.internal.nls.Messages; * <li>Credentials (public and private) such as certificates, keys, or * authentication proofs such as tickets</li> * </ul> - * </p> - * @since Android 1.0 */ public final class Subject implements Serializable { @@ -104,7 +102,7 @@ public final class Subject implements Serializable { /** * The constructor for the subject, setting its public and private * credentials and principals according to the arguments. - * + * * @param readOnly * {@code true} if this {@code Subject} is read-only, thus * preventing any modifications to be done. @@ -135,7 +133,7 @@ public final class Subject implements Serializable { /** * Runs the code defined by {@code action} using the permissions granted to * the {@code Subject} itself and to the code as well. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -154,7 +152,7 @@ public final class Subject implements Serializable { * Run the code defined by {@code action} using the permissions granted to * the {@code Subject} and to the code itself, additionally providing a more * specific context. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -209,7 +207,7 @@ public final class Subject implements Serializable { /** * Runs the code defined by {@code action} using the permissions granted to * the subject and to the code itself. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -231,7 +229,7 @@ public final class Subject implements Serializable { * Runs the code defined by {@code action} using the permissions granted to * the subject and to the code itself, additionally providing a more * specific context. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -290,7 +288,7 @@ public final class Subject implements Serializable { * Checks two Subjects for equality. More specifically if the principals, * public and private credentials are equal, equality for two {@code * Subjects} is implied. - * + * * @param obj * the {@code Object} checked for equality with this {@code * Subject}. @@ -320,18 +318,18 @@ public final class Subject implements Serializable { /** * Returns this {@code Subject}'s {@link Principal}. - * + * * @return this {@code Subject}'s {@link Principal}. */ public Set<Principal> getPrincipals() { return principals; } - + /** * Returns this {@code Subject}'s {@link Principal} which is a subclass of * the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the {@code Principal} * returned must satisfy. @@ -345,7 +343,7 @@ public final class Subject implements Serializable { /** * Returns the private credentials associated with this {@code Subject}. - * + * * @return the private credentials associated with this {@code Subject}. */ public Set<Object> getPrivateCredentials() { @@ -355,7 +353,7 @@ public final class Subject implements Serializable { /** * Returns this {@code Subject}'s private credentials which are a subclass * of the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the private credentials * returned must satisfy. @@ -369,18 +367,18 @@ public final class Subject implements Serializable { /** * Returns the public credentials associated with this {@code Subject}. - * + * * @return the public credentials associated with this {@code Subject}. */ public Set<Object> getPublicCredentials() { return publicCredentials; } - + /** * Returns this {@code Subject}'s public credentials which are a subclass of * the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the public credentials * returned must satisfy. @@ -394,7 +392,7 @@ public final class Subject implements Serializable { /** * Returns a hash code of this {@code Subject}. - * + * * @return a hash code of this {@code Subject}. */ @Override @@ -417,7 +415,7 @@ public final class Subject implements Serializable { /** * Returns whether this {@code Subject} is read-only or not. - * + * * @return whether this {@code Subject} is read-only or not. */ public boolean isReadOnly() { @@ -426,7 +424,7 @@ public final class Subject implements Serializable { /** * Returns a {@code String} representation of this {@code Subject}. - * + * * @return a {@code String} representation of this {@code Subject}. */ @Override @@ -479,7 +477,7 @@ public final class Subject implements Serializable { /** * Returns the {@code Subject} that was last associated with the {@code * context} provided as argument. - * + * * @param context * the {@code context} that was associated with the * {@code Subject}. @@ -781,4 +779,4 @@ public final class Subject implements Serializable { } } } -} +}
\ No newline at end of file diff --git a/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java b/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java index 6a8f00b..4b91084 100644 --- a/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java +++ b/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java @@ -25,8 +25,6 @@ import java.util.Set; /** * Merges permissions based on code source and code signers with permissions * granted to the specified {@link Subject}. - * - * @since Android 1.0 */ public class SubjectDomainCombiner implements DomainCombiner { @@ -39,7 +37,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Creates a domain combiner for the entity provided in {@code subject}. - * + * * @param subject * the entity to which this domain combiner is associated. */ @@ -53,7 +51,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Returns the entity to which this domain combiner is associated. - * + * * @return the entity to which this domain combiner is associated. */ public Subject getSubject() { @@ -68,7 +66,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Merges the {@code ProtectionDomain} with the {@code Principal}s * associated with the subject of this {@code SubjectDomainCombiner}. - * + * * @param currentDomains * the {@code ProtectionDomain}s associated with the context of * the current thread. The domains must be sorted according to diff --git a/auth/src/main/java/javax/security/auth/callback/Callback.java b/auth/src/main/java/javax/security/auth/callback/Callback.java index 6cf46b8..4854d3f 100644 --- a/auth/src/main/java/javax/security/auth/callback/Callback.java +++ b/auth/src/main/java/javax/security/auth/callback/Callback.java @@ -20,8 +20,6 @@ package javax.security.auth.callback; /** * Defines an empty base interface for all {@code Callback}s used during * authentication. - * - * @since Android 1.0 */ public interface Callback { }
\ No newline at end of file diff --git a/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java b/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java index 952b81a..21bf30b 100644 --- a/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java +++ b/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java @@ -27,8 +27,6 @@ import java.io.IOException; * also possible to configure a system-default {@code CallbackHandler} by * setting the {@code auth.login.defaultCallbackHandler} property in the * standard {@code security.properties} file. - * - * @since Android 1.0 */ public interface CallbackHandler { @@ -42,7 +40,7 @@ public interface CallbackHandler { * values. If a {@code CallbackHandler} is not able to handle a specific * {@code Callback}, it needs to throw an * {@link UnsupportedCallbackException}. - * + * * @param callbacks * the array of {@code Callback}s that need handling * @throws IOException diff --git a/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java b/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java index 00020fe..3617b75 100644 --- a/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java +++ b/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java @@ -25,8 +25,6 @@ import org.apache.harmony.auth.internal.nls.Messages; /** * Is used in conjunction with a {@link CallbackHandler} to retrieve a password * when needed. - * - * @since Android 1.0 */ public class PasswordCallback implements Callback, Serializable { @@ -47,7 +45,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Creates a new {@code PasswordCallback} instance. - * + * * @param prompt * the message that should be displayed to the user * @param echoOn @@ -62,7 +60,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Returns the prompt that was specified when creating this {@code * PasswordCallback} - * + * * @return the prompt */ public String getPrompt() { @@ -72,7 +70,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Queries whether this {@code PasswordCallback} expects user input to be * echoed, which is specified during the creation of the object. - * + * * @return {@code true} if (and only if) user input should be echoed */ public boolean isEchoOn() { @@ -83,7 +81,7 @@ public class PasswordCallback implements Callback, Serializable { * Sets the password. The {@link CallbackHandler} that performs the actual * provisioning or input of the password needs to call this method to hand * back the password to the security service that requested it. - * + * * @param password * the password. A copy of this is stored, so subsequent changes * to the input array do not affect the {@code PasswordCallback}. @@ -101,7 +99,7 @@ public class PasswordCallback implements Callback, Serializable { * Returns the password. The security service that needs the password * usually calls this method once the {@link CallbackHandler} has finished * its work. - * + * * @return the password. A copy of the internal password is created and * returned, so subsequent changes to the internal password do not * affect the result. diff --git a/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java b/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java index d40ff45..bee7bd3 100644 --- a/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java +++ b/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java @@ -20,8 +20,6 @@ package javax.security.auth.callback; /** * Thrown when a {@link CallbackHandler} does not support a particular {@link * Callback}. - * - * @since Android 1.0 */ public class UnsupportedCallbackException extends Exception { @@ -32,7 +30,7 @@ public class UnsupportedCallbackException extends Exception { /** * Creates a new exception instance and initializes it with just the * unsupported {@code Callback}, but no error message. - * + * * @param callback * the {@code Callback} */ @@ -44,7 +42,7 @@ public class UnsupportedCallbackException extends Exception { /** * Creates a new exception instance and initializes it with both the * unsupported {@code Callback} and an error message. - * + * * @param callback * the {@code Callback} * @param message @@ -57,7 +55,7 @@ public class UnsupportedCallbackException extends Exception { /** * Returns the unsupported {@code Callback} that triggered this exception. - * + * * @return the {@code Callback} */ public Callback getCallback() { diff --git a/auth/src/main/java/javax/security/auth/login/LoginException.java b/auth/src/main/java/javax/security/auth/login/LoginException.java index a1d6ec0..9433c43 100644 --- a/auth/src/main/java/javax/security/auth/login/LoginException.java +++ b/auth/src/main/java/javax/security/auth/login/LoginException.java @@ -21,8 +21,6 @@ import java.security.GeneralSecurityException; /** * Base class for exceptions that are thrown when a login error occurs. - * - * @since Android 1.0 */ public class LoginException extends GeneralSecurityException { @@ -37,7 +35,7 @@ public class LoginException extends GeneralSecurityException { /** * Creates a new exception instance and initializes it with a given message. - * + * * @param message the error message */ public LoginException(String message) { diff --git a/auth/src/main/java/javax/security/auth/x500/X500Principal.java b/auth/src/main/java/javax/security/auth/x500/X500Principal.java index fa9dfe8..41f3a6d 100644 --- a/auth/src/main/java/javax/security/auth/x500/X500Principal.java +++ b/auth/src/main/java/javax/security/auth/x500/X500Principal.java @@ -29,13 +29,11 @@ import org.apache.harmony.security.x501.Name; /** * Represents an X.500 principal, which holds the distinguished name of some - * network entity. An example of a distinguished name is {@code "O=Google, - * OU=Android, C=US"}. The class can be instantiated from a byte representation + * network entity. An example of a distinguished name is {@code "O=SomeOrg, + * OU=SomeOrgUnit, C=US"}. The class can be instantiated from a byte representation * of an object identifier (OID), an ASN.1 DER-encoded version, or a simple * string holding the distinguished name. The representations must follow either * RFC 2253, RFC 1779, or RFC2459. - * - * @since Android 1.0 */ public final class X500Principal implements Serializable, Principal { @@ -65,10 +63,10 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. - * + * * @param name * the ASN.1 DER-encoded distinguished name - * + * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ @@ -91,11 +89,11 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. - * + * * @param in * an {@code InputStream} holding the ASN.1 DER-encoded * distinguished name - * + * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ @@ -118,10 +116,10 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a string representation of a * distinguished name. - * + * * @param name * the string representation of the distinguished name - * + * * @throws IllegalArgumentException * if the string representation of the distinguished name is * incorrect @@ -155,8 +153,8 @@ public final class X500Principal implements Serializable, Principal { /** * Returns an ASN.1 DER-encoded representation of the distinguished name - * contained in this X.500 principal. - * + * contained in this X.500 principal. + * * @return the ASN.1 DER-encoded representation */ public byte[] getEncoded() { @@ -167,9 +165,9 @@ public final class X500Principal implements Serializable, Principal { } /** - * Returns a human-readable string representation of the distinguished name + * Returns a human-readable string representation of the distinguished name * contained in this X.500 principal. - * + * * @return the string representation */ public String getName() { @@ -185,12 +183,12 @@ public final class X500Principal implements Serializable, Principal { * some canonicalizing operations like removing leading and trailing * whitespace, lower-casing the whole name, and bringing it into a * normalized Unicode representation. - * + * * @param format * the name of the format to use for the representation - * + * * @return the string representation - * + * * @throws IllegalArgumentException * if the {@code format} argument is not one of the three * mentioned above diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java index da51e45..00de314 100644 --- a/dalvik/src/main/java/dalvik/system/DexFile.java +++ b/dalvik/src/main/java/dalvik/system/DexFile.java @@ -38,11 +38,13 @@ public final class DexFile { /** * Opens a DEX file from a given File object. This will usually be a ZIP/JAR - * file with a "classes.dex" inside. The method should not be used for files - * inside the Dalvik cache. - * - * @cts What will happen if we refer to the Dalvik cache? Should be either - * specified or throw an exception... + * file with a "classes.dex" inside. + * + * The VM will generate the name of the coresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. * * @param file * the File object referencing the actual DEX file @@ -57,11 +59,13 @@ public final class DexFile { /** * Opens a DEX file from a given filename. This will usually be a ZIP/JAR - * file with a "classes.dex" inside. The method should not be used for files - * inside the Dalvik cache. - * - * @cts What will happen if we refer to the Dalvik cache? Should be either - * specified or throw an exception... + * file with a "classes.dex" inside. + * + * The VM will generate the name of the coresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. * * @param fileName * the filename of the DEX file @@ -190,11 +194,23 @@ public final class DexFile { * @cts Exception comment is a bit cryptic. What exception will be thrown? */ public Class loadClass(String name, ClassLoader loader) { + String slashName = name.replace('.', '/'); + return loadClassBinaryName(slashName, loader); + } + + /** + * See {@link #loadClass(String, ClassLoader)}. + * + * This takes a "binary" class name to better match ClassLoader semantics. + * + * {@hide} + */ + public Class loadClassBinaryName(String name, ClassLoader loader) { return defineClass(name, loader, mCookie, null); //new ProtectionDomain(name) /*DEBUG ONLY*/); } - + native private static Class defineClass(String name, ClassLoader loader, int cookie, ProtectionDomain pd); diff --git a/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/dalvik/src/main/java/dalvik/system/PathClassLoader.java index c80aef8..597eb5b 100644 --- a/dalvik/src/main/java/dalvik/system/PathClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/PathClassLoader.java @@ -56,8 +56,8 @@ public class PathClassLoader extends ClassLoader { /** * Creates a {@code PathClassLoader} that operates on a given list of files * and directories. This method is equivalent to calling - * {@link #PathClassLoader(String, String, ClassLoader) with a {@code null} - * value for the second argument (see description there). + * {@link #PathClassLoader(String, String, ClassLoader)} with a + * {@code null} value for the second argument (see description there). * * @param path * the list of files and directories @@ -179,8 +179,9 @@ public class PathClassLoader extends ClassLoader { * parent ClassLoader has failed to find a loaded class of the same name. * * @param name - * The name of the class to search for, in a human-readable form - * like "java.lang.String" or "java.net.URLClassLoader$3$1". + * The "binary name" of the class to search for, in a + * human-readable form like "java.lang.String" or + * "java.net.URLClassLoader$3$1". * @return the {@link Class} object representing the class * @throws ClassNotFoundException * if the class cannot be found @@ -199,8 +200,7 @@ public class PathClassLoader extends ClassLoader { //System.out.println("My path is: " + mPaths[i]); if (mDexs[i] != null) { - String slashName = name.replace('.', '/'); - Class clazz = mDexs[i].loadClass(slashName, this); + Class clazz = mDexs[i].loadClassBinaryName(name, this); if (clazz != null) return clazz; } else if (mZips[i] != null) { diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java index efc25d6..06a67b6 100644 --- a/dalvik/src/main/java/dalvik/system/VMDebug.java +++ b/dalvik/src/main/java/dalvik/system/VMDebug.java @@ -275,6 +275,13 @@ public final class VMDebug { */ public static native void dumpHprofData(String fileName) throws IOException; + /** + * Primes the register map cache. + * + * @hide + */ + public static native boolean cacheRegisterMap(String classAndMethodDesc); + /* don't ask */ static native void printThis(Object thisThing, int count, int thing); diff --git a/icu/src/main/native/DecimalFormatInterface.cpp b/icu/src/main/native/DecimalFormatInterface.cpp index 6221826..7e37d6c 100644 --- a/icu/src/main/native/DecimalFormatInterface.cpp +++ b/icu/src/main/native/DecimalFormatInterface.cpp @@ -15,6 +15,7 @@ * the VM-specific behavior isolated in VMThread. */ +#define LOG_TAG "DecimalFormatInterface" #include "JNIHelp.h" #include "AndroidSystemNatives.h" #include "unicode/unum.h" @@ -28,7 +29,6 @@ #include <string.h> #include "cutils/log.h" -#define LOG_TAG "DecimalFormatInterface" static UBool icuError(JNIEnv *env, UErrorCode errorcode) { diff --git a/icu/src/main/native/ResourceInterface.cpp b/icu/src/main/native/ResourceInterface.cpp index 5f9d442..a88e15c 100644 --- a/icu/src/main/native/ResourceInterface.cpp +++ b/icu/src/main/native/ResourceInterface.cpp @@ -943,7 +943,7 @@ static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, - jclass obj_class = env->FindClass("java/lang/Object"); + jclass obj_class = env->FindClass("[Ljava/lang/Object;"); jclass integer_class = env->FindClass("java/lang/Integer"); jmethodID integerInit = env->GetMethodID(integer_class, "<init>", "(I)V"); jobjectArray result; @@ -1207,7 +1207,8 @@ endOfCalendar: intCurrencySymbol = env->NewStringUTF("XXX"); } if(currencySymbol == NULL) { - currencySymbol = env->NewStringUTF("\u00a4"); + // creating a new string explicitly with the UTF-8 encoding of "\u00a4" + currencySymbol = env->NewStringUTF("\xc2\xa4"); } counter += 2; diff --git a/logging/src/main/java/java/util/logging/ConsoleHandler.java b/logging/src/main/java/java/util/logging/ConsoleHandler.java index a88cf0c..ef365ca 100644 --- a/logging/src/main/java/java/util/logging/ConsoleHandler.java +++ b/logging/src/main/java/java/util/logging/ConsoleHandler.java @@ -38,19 +38,13 @@ package java.util.logging; * handler will use to encode log messages, defaults to {@code null} if this * property is not found or has an invalid value. * </ul> - * </p> * <p> * This class is not thread-safe. - * </p> - * - * @since Android 1.0 */ public class ConsoleHandler extends StreamHandler { /** * Constructs a {@code ConsoleHandler} object. - * - * @since Android 1.0 */ public ConsoleHandler() { super(System.err); @@ -58,8 +52,6 @@ public class ConsoleHandler extends StreamHandler { /** * Closes this handler. The {@code System.err} is flushed but not closed. - * - * @since Android 1.0 */ @Override public void close() { @@ -68,16 +60,13 @@ public class ConsoleHandler extends StreamHandler { /** * Logs a record if necessary. A flush operation will be done. - * + * * @param record * the log record to be logged. - * - * @since Android 1.0 */ @Override public void publish(LogRecord record) { super.publish(record); super.flush(); - } } diff --git a/logging/src/main/java/java/util/logging/ErrorManager.java b/logging/src/main/java/java/util/logging/ErrorManager.java index 6f5084c..708ddfa 100644 --- a/logging/src/main/java/java/util/logging/ErrorManager.java +++ b/logging/src/main/java/java/util/logging/ErrorManager.java @@ -24,51 +24,37 @@ import org.apache.harmony.logging.internal.nls.Messages; * error that may happen during logging. {@code Handlers} should report errors * to an {@code ErrorManager}, instead of throwing exceptions, which would * interfere with the log issuer's execution. - * - * @since Android 1.0 */ public class ErrorManager { /** * The error code indicating a failure that does not fit in any of the * specific types of failures that follow. - * - * @since Android 1.0 */ public static final int GENERIC_FAILURE = 0; /** * The error code indicating a failure when writing to an output stream. - * - * @since Android 1.0 */ public static final int WRITE_FAILURE = 1; /** * The error code indicating a failure when flushing an output stream. - * - * @since Android 1.0 */ public static final int FLUSH_FAILURE = 2; /** * The error code indicating a failure when closing an output stream. - * - * @since Android 1.0 */ public static final int CLOSE_FAILURE = 3; /** * The error code indicating a failure when opening an output stream. - * - * @since Android 1.0 */ public static final int OPEN_FAILURE = 4; /** * The error code indicating a failure when formatting the error messages. - * - * @since Android 1.0 */ public static final int FORMAT_FAILURE = 5; @@ -85,20 +71,16 @@ public class ErrorManager { /** * Constructs an instance of {@code ErrorManager}. - * - * @since Android 1.0 */ public ErrorManager() { super(); } /** - * <p> * Reports an error using the given message, exception and error code. This * implementation will write out the message to {@link System#err} on the * first call and all subsequent calls are ignored. A subclass of this class * should override this method. - * </p> * * @param message * the error message, which may be {@code null}. @@ -108,8 +90,6 @@ public class ErrorManager { * @param errorCode * the error code that identifies the type of error; see the * constant fields of this class for possible values. - * - * @since Android 1.0 */ public void error(String message, Exception exception, int errorCode) { synchronized (this) { @@ -119,7 +99,7 @@ public class ErrorManager { called = true; } System.err.println(this.getClass().getName() - + ": " + FAILURES[errorCode]); //$NON-NLS-1$ + + ": " + FAILURES[errorCode]); //$NON-NLS-1$ if (message != null) { // logging.1E=Error message - {0} System.err.println(Messages.getString("logging.1E", message)); //$NON-NLS-1$ diff --git a/logging/src/main/java/java/util/logging/FileHandler.java b/logging/src/main/java/java/util/logging/FileHandler.java index af71a6d..e1eba9e 100644 --- a/logging/src/main/java/java/util/logging/FileHandler.java +++ b/logging/src/main/java/java/util/logging/FileHandler.java @@ -38,48 +38,45 @@ import org.apache.harmony.logging.internal.nls.Messages; * When a set of files is used and a given amount of data has been written to * one file, then this file is closed and another file is opened. The name of * these files are generated by given name pattern, see below for details. - * </p> + * When the files have all been filled the Handler returns to the first and goes + * through the set again. * <p> * By default, the I/O buffering mechanism is enabled, but when each log record * is complete, it is flushed out. - * </p> * <p> * {@code XMLFormatter} is the default formatter for {@code FileHandler}. - * </p> * <p> * {@code FileHandler} reads the following {@code LogManager} properties for * initialization; if a property is not defined or has an invalid value, a * default value is used. * <ul> - * <li>java.util.logging.FileHandler.level specifies the level for this - * {@code Handler}, defaults to {@code Level.ALL}.</li> + * <li>java.util.logging.FileHandler.append specifies whether this + * {@code FileHandler} should append onto existing files, defaults to + * {@code false}.</li> + * <li>java.util.logging.FileHandler.count specifies how many output files to + * rotate, defaults to 1.</li> * <li>java.util.logging.FileHandler.filter specifies the {@code Filter} class * name, defaults to no {@code Filter}.</li> * <li>java.util.logging.FileHandler.formatter specifies the {@code Formatter} * class, defaults to {@code java.util.logging.XMLFormatter}.</li> * <li>java.util.logging.FileHandler.encoding specifies the character set * encoding name, defaults to the default platform encoding.</li> + * <li>java.util.logging.FileHandler.level specifies the level for this + * {@code Handler}, defaults to {@code Level.ALL}.</li> * <li>java.util.logging.FileHandler.limit specifies the maximum number of * bytes to write to any one file, defaults to zero, which means no limit.</li> - * <li>java.util.logging.FileHandler.count specifies how many output files to - * rotate, defaults to 1.</li> * <li>java.util.logging.FileHandler.pattern specifies name pattern for the * output files. See below for details. Defaults to "%h/java%u.log".</li> - * <li>java.util.logging.FileHandler.append specifies whether this - * {@code FileHandler} should append onto existing files, defaults to - * {@code false}.</li> * </ul> - * </p> * <p> * Name pattern is a string that may include some special substrings, which will * be replaced to generate output files: - * </p> * <ul> * <li>"/" represents the local pathname separator</li> - * <li>"%t" represents the system's temporary directory</li> + * <li>"%g" represents the generation number to distinguish rotated logs</li> * <li>"%h" represents the home directory of the current user, which is * specified by "user.home" system property</li> - * <li>"%g" represents the generation number to distinguish rotated logs</li> + * <li>"%t" represents the system's temporary directory</li> * <li>"%u" represents a unique number to resolve conflicts</li> * <li>"%%" represents the percent sign character '%'</li> * </ul> @@ -88,7 +85,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * follow the sequence 0, 1, 2.... If the file count is larger than one, but the * generation field("%g") has not been specified in the pattern, then the * generation number after a dot will be added to the end of the file name. - * </p> * <p> * The "%u" unique field is used to avoid conflicts and is set to 0 at first. If * one {@code FileHandler} tries to open the filename which is currently in use @@ -98,8 +94,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * value will be added to the end of the filename in question immediately to the * right of a dot. The generation of unique IDs for avoiding conflicts is only * guaranteed to work reliably when using a local disk file system. - * </p> - * @since Android 1.0 */ public class FileHandler extends StreamHandler { @@ -150,7 +144,7 @@ public class FileHandler extends StreamHandler { /** * Construct a {@code FileHandler} using {@code LogManager} properties or * their default value. - * + * * @throws IOException * if any I/O error occurs. * @throws SecurityException @@ -159,7 +153,6 @@ public class FileHandler extends StreamHandler { * handler; required permissions include * {@code LogPermission("control")}, * {@code FilePermission("write")} etc. - * @since Android 1.0 */ public FileHandler() throws IOException { init(null, null, null, null); @@ -232,21 +225,22 @@ public class FileHandler extends StreamHandler { setOutputStream(output); } + @SuppressWarnings("nls") private void initProperties(String p, Boolean a, Integer l, Integer c) { - super.initProperties("ALL", null, "java.util.logging.XMLFormatter", //$NON-NLS-1$//$NON-NLS-2$ + super.initProperties("ALL", null, "java.util.logging.XMLFormatter", null); String className = this.getClass().getName(); - pattern = (null == p) ? getStringProperty(className + ".pattern", //$NON-NLS-1$ + pattern = (null == p) ? getStringProperty(className + ".pattern", DEFAULT_PATTERN) : p; - if (null == pattern || "".equals(pattern)) { //$NON-NLS-1$ + if (null == pattern || "".equals(pattern)) { // logging.19=Pattern cannot be empty - throw new NullPointerException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new NullPointerException(Messages.getString("logging.19")); } - append = (null == a) ? getBooleanProperty(className + ".append", //$NON-NLS-1$ + append = (null == a) ? getBooleanProperty(className + ".append", DEFAULT_APPEND) : a.booleanValue(); - count = (null == c) ? getIntProperty(className + ".count", //$NON-NLS-1$ + count = (null == c) ? getIntProperty(className + ".count", DEFAULT_COUNT) : c.intValue(); - limit = (null == l) ? getIntProperty(className + ".limit", //$NON-NLS-1$ + limit = (null == l) ? getIntProperty(className + ".limit", DEFAULT_LIMIT) : l.intValue(); count = count < 1 ? DEFAULT_COUNT : count; limit = limit < 0 ? DEFAULT_LIMIT : limit; @@ -279,7 +273,7 @@ public class FileHandler extends StreamHandler { /** * Transform the pattern to the valid file name, replacing any patterns, and * applying generation and uniqueID if present. - * + * * @param gen * generation of this file * @return transformed filename ready for use. @@ -396,9 +390,9 @@ public class FileHandler extends StreamHandler { * Constructs a new {@code FileHandler}. The given name pattern is used as * output filename, the file limit is set to zero (no limit), the file count * is set to one; the remaining configuration is done using - * {@code LogManager} properties or their default values. This handler write - * to only one file without size limit. - * + * {@code LogManager} properties or their default values. This handler + * writes to only one file with no size limit. + * * @param pattern * the name pattern for the output file. * @throws IOException @@ -413,7 +407,6 @@ public class FileHandler extends StreamHandler { * if the pattern is empty. * @throws NullPointerException * if the pattern is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ @@ -429,9 +422,9 @@ public class FileHandler extends StreamHandler { * output filename, the file limit is set to zero (no limit), the file count * is initialized to one and the value of {@code append} becomes the new * instance's append mode. The remaining configuration is done using - * {@code LogManager} properties. This handler write to only one file - * without size limit. - * + * {@code LogManager} properties. This handler writes to only one file + * with no size limit. + * * @param pattern * the name pattern for the output file. * @param append @@ -448,11 +441,10 @@ public class FileHandler extends StreamHandler { * if {@code pattern} is empty. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, boolean append) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } init(pattern, Boolean.valueOf(append), Integer.valueOf(DEFAULT_LIMIT), @@ -466,7 +458,7 @@ public class FileHandler extends StreamHandler { * is done using {@code LogManager} properties. This handler is configured * to write to a rotating set of count files, when the limit of bytes has * been written to one output file, another file will be opened instead. - * + * * @param pattern * the name pattern for the output file. * @param limit @@ -487,11 +479,10 @@ public class FileHandler extends StreamHandler { * {@code count < 1}. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, int limit, int count) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } if (limit < 0 || count < 1) { // logging.1B=The limit and count property must be larger than 0 and @@ -509,7 +500,7 @@ public class FileHandler extends StreamHandler { * {@code LogManager} properties. This handler is configured to write to a * rotating set of count files, when the limit of bytes has been written to * one output file, another file will be opened instead. - * + * * @param pattern * the name pattern for the output file. * @param limit @@ -532,12 +523,11 @@ public class FileHandler extends StreamHandler { * {@code count < 1}. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, int limit, int count, boolean append) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } if (limit < 0 || count < 1) { // logging.1B=The limit and count property must be larger than 0 and @@ -550,14 +540,13 @@ public class FileHandler extends StreamHandler { /** * Flushes and closes all opened files. - * + * * @throws SecurityException * if a security manager exists and it determines that the * caller does not have the required permissions to control this * handler; required permissions include * {@code LogPermission("control")}, * {@code FilePermission("write")} etc. - * @since Android 1.0 */ @Override public void close() { @@ -577,10 +566,9 @@ public class FileHandler extends StreamHandler { /** * Publish a {@code LogRecord}. - * + * * @param record * the log record to publish. - * @since Android 1.0 */ @Override public void publish(LogRecord record) { diff --git a/logging/src/main/java/java/util/logging/Filter.java b/logging/src/main/java/java/util/logging/Filter.java index e81f216..f5dbd9f 100644 --- a/logging/src/main/java/java/util/logging/Filter.java +++ b/logging/src/main/java/java/util/logging/Filter.java @@ -20,8 +20,6 @@ package java.util.logging; /** * A {@code Filter} provides a mechanism for exercising fine-grained control * over which records get logged. - * - * @since Android 1.0 */ public interface Filter { @@ -32,7 +30,6 @@ public interface Filter { * the {@link LogRecord} to be checked. * @return {@code true} if the supplied log record needs to be logged, * {@code false} otherwise. - * @since Android 1.0 */ boolean isLoggable(LogRecord record); } diff --git a/logging/src/main/java/java/util/logging/Formatter.java b/logging/src/main/java/java/util/logging/Formatter.java index 2941c24..f9b0d25 100644 --- a/logging/src/main/java/java/util/logging/Formatter.java +++ b/logging/src/main/java/java/util/logging/Formatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.text.MessageFormat; @@ -26,41 +25,24 @@ import java.util.ResourceBundle; * string representation. Head and tail strings are sometimes used to wrap a set * of records. The {@code getHead} and {@code getTail} methods are used for this * purpose. - * - * @since Android 1.0 */ public abstract class Formatter { - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Formatter} object. - * - * @since Android 1.0 */ protected Formatter() { super(); } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - /** * Converts a {@link LogRecord} object into a string representation. The * resulted string is usually localized and includes the message field of * the record. - * + * * @param r * the log record to be formatted into a string. * @return the formatted string. - * @since Android 1.0 */ public abstract String format(LogRecord r); @@ -71,16 +53,13 @@ public abstract class Formatter { * <p> * The message string is firstly localized using the {@code ResourceBundle} * object associated with the supplied {@code LogRecord}. - * </p> * <p> * Notice : if message contains "{0", then java.text.MessageFormat is used. * Otherwise no formatting is performed. - * </p> - * + * * @param r * the log record to be formatted. * @return the string resulted from the formatting. - * @since Android 1.0 */ public String formatMessage(LogRecord r) { String pattern = r.getMessage(); @@ -114,14 +93,12 @@ public abstract class Formatter { /** * Gets the head string used to wrap a set of log records. This base class * always returns an empty string. - * + * * @param h * the target handler. * @return the head string used to wrap a set of log records, empty in this * implementation. - * @since Android 1.0 */ - @SuppressWarnings("unused") public String getHead(Handler h) { return ""; //$NON-NLS-1$ } @@ -129,17 +106,13 @@ public abstract class Formatter { /** * Gets the tail string used to wrap a set of log records. This base class * always returns the empty string. - * + * * @param h * the target handler. * @return the tail string used to wrap a set of log records, empty in this * implementation. - * @since Android 1.0 */ - @SuppressWarnings("unused") public String getTail(Handler h) { return ""; //$NON-NLS-1$ } - } - diff --git a/logging/src/main/java/java/util/logging/Handler.java b/logging/src/main/java/java/util/logging/Handler.java index d28bce0..a5b92a0 100644 --- a/logging/src/main/java/java/util/logging/Handler.java +++ b/logging/src/main/java/java/util/logging/Handler.java @@ -15,13 +15,12 @@ * limitations under the License. */ - package java.util.logging; +import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedExceptionAction; -import java.io.UnsupportedEncodingException; import org.apache.harmony.logging.internal.nls.Messages; @@ -29,24 +28,11 @@ import org.apache.harmony.logging.internal.nls.Messages; * A {@code Handler} object accepts a logging request and exports the desired * messages to a target, for example, a file, the console, etc. It can be * disabled by setting its logging level to {@code Level.OFF}. - * - * @since Android 1.0 */ public abstract class Handler { - /* - * ------------------------------------------------------------------- - * Constants - * ------------------------------------------------------------------- - */ private static final Level DEFAULT_LEVEL = Level.ALL; - /* - * ------------------------------------------------------------------- - * Instance variables - * ------------------------------------------------------------------- - */ - // the error manager to report errors during logging private ErrorManager errorMan; @@ -65,18 +51,10 @@ public abstract class Handler { // class name, used for property reading private String prefix; - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Handler} object with a default error manager instance * {@code ErrorManager}, the default encoding, and the default logging * level {@code Level.ALL}. It has no filter and no formatter. - * - * @since Android 1.0 */ protected Handler() { this.errorMan = new ErrorManager(); @@ -87,12 +65,6 @@ public abstract class Handler { this.prefix = this.getClass().getName(); } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - // get a instance from given class name, using Class.forName() private Object getDefaultInstance(String className) { Object result = null; @@ -102,7 +74,7 @@ public abstract class Handler { try { result = Class.forName(className).newInstance(); } catch (Exception e) { - //ignore + // ignore } return result; } @@ -110,7 +82,8 @@ public abstract class Handler { // get a instance from given class name, using context classloader private Object getCustomizeInstance(final String className) throws Exception { - Class<?> c = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() { + Class<?> c = AccessController + .doPrivileged(new PrivilegedExceptionAction<Class<?>>() { public Class<?> run() throws Exception { ClassLoader loader = Thread.currentThread() .getContextClassLoader(); @@ -126,22 +99,22 @@ public abstract class Handler { // print error message in some format void printInvalidPropMessage(String key, String value, Exception e) { // logging.12=Invalid property value for - String msg = new StringBuilder().append(Messages.getString("logging.12")) //$NON-NLS-1$ + String msg = new StringBuilder().append( + Messages.getString("logging.12")) //$NON-NLS-1$ .append(prefix).append(":").append(key).append("/").append( //$NON-NLS-1$//$NON-NLS-2$ value).toString(); errorMan.error(msg, e, ErrorManager.GENERIC_FAILURE); } - /* + /** * init the common properties, including filter, level, formatter, and * encoding */ - @SuppressWarnings("unused") void initProperties(String defaultLevel, String defaultFilter, String defaultFormatter, String defaultEncoding) { LogManager manager = LogManager.getLogManager(); - //set filter + // set filter final String filterName = manager.getProperty(prefix + ".filter"); //$NON-NLS-1$ if (null != filterName) { try { @@ -154,7 +127,7 @@ public abstract class Handler { filter = (Filter) getDefaultInstance(defaultFilter); } - //set level + // set level String levelName = manager.getProperty(prefix + ".level"); //$NON-NLS-1$ if (null != levelName) { try { @@ -167,7 +140,7 @@ public abstract class Handler { level = Level.parse(defaultLevel); } - //set formatter + // set formatter final String formatterName = manager.getProperty(prefix + ".formatter"); //$NON-NLS-1$ if (null != formatterName) { try { @@ -180,7 +153,7 @@ public abstract class Handler { formatter = (Formatter) getDefaultInstance(defaultFormatter); } - //set encoding + // set encoding final String encodingName = manager.getProperty(prefix + ".encoding"); //$NON-NLS-1$ try { internalSetEncoding(encodingName); @@ -193,37 +166,31 @@ public abstract class Handler { * Closes this handler. A flush operation will be performed and all the * associated resources will be freed. Client applications should not use * this handler after closing it. - * + * * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * - * @since Android 1.0 */ public abstract void close(); /** * Flushes any buffered output. - * - * @since Android 1.0 */ public abstract void flush(); /** * Accepts a logging request and sends it to the the target. - * + * * @param record * the log record to be logged; {@code null} records are ignored. - * @since Android 1.0 */ public abstract void publish(LogRecord record); /** * Gets the character encoding used by this handler, {@code null} for * default encoding. - * + * * @return the character encoding used by this handler. - * @since Android 1.0 */ public String getEncoding() { return this.encoding; @@ -232,12 +199,11 @@ public abstract class Handler { /** * Gets the error manager used by this handler to report errors during * logging. - * + * * @return the error manager used by this handler. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public ErrorManager getErrorManager() { LogManager.getLogManager().checkAccess(); @@ -246,9 +212,8 @@ public abstract class Handler { /** * Gets the filter used by this handler. - * + * * @return the filter used by this handler (possibly {@code null}). - * @since Android 1.0 */ public Filter getFilter() { return this.filter; @@ -256,9 +221,8 @@ public abstract class Handler { /** * Gets the formatter used by this handler to format the logging messages. - * + * * @return the formatter used by this handler (possibly {@code null}). - * @since Android 1.0 */ public Formatter getFormatter() { return this.formatter; @@ -267,9 +231,8 @@ public abstract class Handler { /** * Gets the logging level of this handler, records with levels lower than * this value will be dropped. - * + * * @return the logging level of this handler. - * @since Android 1.0 */ public Level getLevel() { return this.level; @@ -278,12 +241,11 @@ public abstract class Handler { /** * Determines whether the supplied log record needs to be logged. The * logging levels will be checked as well as the filter. - * + * * @param record * the log record to be checked. * @return {@code true} if the supplied log record needs to be logged, * otherwise {@code false}. - * @since Android 1.0 */ public boolean isLoggable(LogRecord record) { if (null == record) { @@ -302,14 +264,13 @@ public abstract class Handler { * {@code ErrorManager} is used for that purpose. No security checks are * done, therefore this is compatible with environments where the caller * is non-privileged. - * + * * @param msg * the error message, may be {@code null}. * @param ex * the associated exception, may be {@code null}. * @param code * an {@code ErrorManager} error code. - * @since Android 1.0 */ protected void reportError(String msg, Exception ex, int code) { this.errorMan.error(msg, ex, code); @@ -319,12 +280,11 @@ public abstract class Handler { * Sets the character encoding used by this handler. A {@code null} value * indicates the use of the default encoding. This internal method does * not check security. - * + * * @param newEncoding * the character encoding to set. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ void internalSetEncoding(String newEncoding) throws UnsupportedEncodingException { @@ -347,7 +307,7 @@ public abstract class Handler { /** * Sets the character encoding used by this handler, {@code null} indicates * a default encoding. - * + * * @param encoding * the character encoding to set. * @throws SecurityException @@ -355,7 +315,6 @@ public abstract class Handler { * have the required permission. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ public void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException { @@ -365,7 +324,7 @@ public abstract class Handler { /** * Sets the error manager for this handler. - * + * * @param em * the error manager to set. * @throws NullPointerException @@ -373,7 +332,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setErrorManager(ErrorManager em) { LogManager.getLogManager().checkAccess(); @@ -385,13 +343,12 @@ public abstract class Handler { /** * Sets the filter to be used by this handler. - * + * * @param newFilter * the filter to set, may be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFilter(Filter newFilter) { LogManager.getLogManager().checkAccess(); @@ -401,7 +358,7 @@ public abstract class Handler { /** * Sets the formatter to be used by this handler. This internal method does * not check security. - * + * * @param newFormatter * the formatter to set. */ @@ -414,7 +371,7 @@ public abstract class Handler { /** * Sets the formatter to be used by this handler. - * + * * @param newFormatter * the formatter to set. * @throws NullPointerException @@ -422,7 +379,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFormatter(Formatter newFormatter) { LogManager.getLogManager().checkAccess(); @@ -432,7 +388,7 @@ public abstract class Handler { /** * Sets the logging level of the messages logged by this handler, levels * lower than this value will be dropped. - * + * * @param newLevel * the logging level to set. * @throws NullPointerException @@ -440,7 +396,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setLevel(Level newLevel) { if (null == newLevel) { @@ -450,4 +405,3 @@ public abstract class Handler { this.level = newLevel; } } - diff --git a/logging/src/main/java/java/util/logging/Level.java b/logging/src/main/java/java/util/logging/Level.java index 32ba017..f988127 100644 --- a/logging/src/main/java/java/util/logging/Level.java +++ b/logging/src/main/java/java/util/logging/Level.java @@ -41,8 +41,6 @@ import dalvik.system.VMStack; * INFO, WARNING, SEVERE. There are two additional predefined levels, which are * ALL and OFF. ALL indicates logging all messages, and OFF indicates logging no * messages. - * </p> - * @since Android 1.0 */ public class Level implements Serializable { @@ -52,70 +50,52 @@ public class Level implements Serializable { /** * The OFF level provides no logging messages. - * - * @since Android 1.0 */ public static final Level OFF = new Level("OFF", Integer.MAX_VALUE); //$NON-NLS-1$ /** * The SEVERE level provides severe failure messages. - * - * @since Android 1.0 */ public static final Level SEVERE = new Level("SEVERE", 1000); //$NON-NLS-1$ /** * The WARNING level provides warnings. - * - * @since Android 1.0 */ public static final Level WARNING = new Level("WARNING", 900); //$NON-NLS-1$ /** * The INFO level provides informative messages. - * - * @since Android 1.0 */ public static final Level INFO = new Level("INFO", 800); //$NON-NLS-1$ /** * The CONFIG level provides static configuration messages. - * - * @since Android 1.0 */ public static final Level CONFIG = new Level("CONFIG", 700); //$NON-NLS-1$ /** * The FINE level provides tracing messages. - * - * @since Android 1.0 */ public static final Level FINE = new Level("FINE", 500); //$NON-NLS-1$ /** * The FINER level provides more detailed tracing messages. - * - * @since Android 1.0 */ public static final Level FINER = new Level("FINER", 400); //$NON-NLS-1$ /** * The FINEST level provides highly detailed tracing messages. - * - * @since Android 1.0 */ public static final Level FINEST = new Level("FINEST", 300); //$NON-NLS-1$ /** * The ALL level provides all logging messages. - * - * @since Android 1.0 */ public static final Level ALL = new Level("ALL", Integer.MIN_VALUE); //$NON-NLS-1$ /** * Parses a level name into a {@code Level} object. - * + * * @param name * the name of the desired {@code level}, which cannot be * {@code null}. @@ -124,13 +104,8 @@ public class Level implements Serializable { * if {@code name} is {@code null}. * @throws IllegalArgumentException * if {@code name} is not valid. - * @since Android 1.0 */ public static Level parse(String name) throws IllegalArgumentException { - // BEGIN android-note - // final modifier removed and IAE added to get closer to the RI - // copied from newer version of harmony - // END android-note if (name == null) { // logging.1C=The 'name' parameter is null. throw new NullPointerException(Messages.getString("logging.1C")); //$NON-NLS-1$ @@ -155,8 +130,8 @@ public class Level implements Serializable { if (isNameAnInt) { /* - * Loop through levels a second time, so that the - * returned instance will be passed on the order of construction. + * Loop through levels a second time, so that the returned + * instance will be passed on the order of construction. */ for (Level level : levels) { if (nameAsInt == level.intValue()) { @@ -168,7 +143,8 @@ public class Level implements Serializable { if (!isNameAnInt) { // logging.1D=Cannot parse this name: {0} - throw new IllegalArgumentException(Messages.getString("logging.1D", name)); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString( + "logging.1D", name)); //$NON-NLS-1$ } return new Level(name, nameAsInt); @@ -176,21 +152,21 @@ public class Level implements Serializable { /** * The name of this Level. - * + * * @serial */ private final String name; /** * The integer value indicating the level. - * + * * @serial */ private final int value; /** * The name of the resource bundle used to localize the level name. - * + * * @serial */ private final String resourceBundleName; @@ -204,14 +180,13 @@ public class Level implements Serializable { /** * Constructs an instance of {@code Level} taking the supplied name and * level value. - * + * * @param name * the name of the level. * @param level * an integer value indicating the level. * @throws NullPointerException * if {@code name} is {@code null}. - * @since Android 1.0 */ protected Level(String name, int level) { this(name, level, null); @@ -220,7 +195,7 @@ public class Level implements Serializable { /** * Constructs an instance of {@code Level} taking the supplied name, level * value and resource bundle name. - * + * * @param name * the name of the level. * @param level @@ -229,7 +204,6 @@ public class Level implements Serializable { * the name of the resource bundle to use. * @throws NullPointerException * if {@code name} is {@code null}. - * @since Android 1.0 */ protected Level(String name, int level, String resourceBundleName) { if (name == null) { @@ -256,9 +230,8 @@ public class Level implements Serializable { /** * Gets the name of this level. - * + * * @return this level's name. - * @since Android 1.0 */ public String getName() { return this.name; @@ -266,9 +239,8 @@ public class Level implements Serializable { /** * Gets the name of the resource bundle associated with this level. - * + * * @return the name of this level's resource bundle. - * @since Android 1.0 */ public String getResourceBundleName() { return this.resourceBundleName; @@ -276,20 +248,17 @@ public class Level implements Serializable { /** * Gets the integer value indicating this level. - * + * * @return this level's integer value. - * @since Android 1.0 */ public final int intValue() { return this.value; } /** - * <p> * Serialization helper method to maintain singletons and add any new * levels. - * </p> - * + * * @return the resolved instance. */ private Object readResolve() { @@ -298,7 +267,7 @@ public class Level implements Serializable { if (value != level.value) { continue; } - if (!name.equals(name)) { + if (!name.equals(level.name)) { continue; } if (resourceBundleName == level.resourceBundleName) { @@ -316,7 +285,7 @@ public class Level implements Serializable { /** * Serialization helper to setup transient resource bundle instance. - * + * * @param in * the input stream to read the instance data from. * @throws IOException @@ -324,7 +293,8 @@ public class Level implements Serializable { * @throws ClassNotFoundException * if a class is not found. */ - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException { in.defaultReadObject(); if (resourceBundleName != null) { try { @@ -339,9 +309,8 @@ public class Level implements Serializable { * Gets the localized name of this level. The default locale is used. If no * resource bundle is associated with this level then the original level * name is returned. - * + * * @return the localized name of this level. - * @since Android 1.0 */ public String getLocalizedName() { if (rb == null) { @@ -358,12 +327,11 @@ public class Level implements Serializable { /** * Compares two {@code Level} objects for equality. They are considered to * be equal if they have the same level value. - * + * * @param o * the other object to compare this level to. * @return {@code true} if this object equals to the supplied object, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -380,9 +348,8 @@ public class Level implements Serializable { /** * Returns the hash code of this {@code Level} object. - * + * * @return this level's hash code. - * @since Android 1.0 */ @Override public int hashCode() { @@ -392,9 +359,8 @@ public class Level implements Serializable { /** * Returns the string representation of this {@code Level} object. In * this case, it is the level's name. - * + * * @return the string representation of this level. - * @since Android 1.0 */ @Override public final String toString() { diff --git a/logging/src/main/java/java/util/logging/LogManager.java b/logging/src/main/java/java/util/logging/LogManager.java index 8409b81..308daaf 100644 --- a/logging/src/main/java/java/util/logging/LogManager.java +++ b/logging/src/main/java/java/util/logging/LogManager.java @@ -24,6 +24,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +// BEGIN android-removed +//import java.lang.management.ManagementFactory; +//import java.lang.reflect.Method; +// END android-removed import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; @@ -33,13 +37,11 @@ import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; + // BEGIN android-removed -// import java.lang.management.ManagementFactory; -// import java.lang.reflect.Method; -// import javax.management.MBeanServer; -// import javax.management.ObjectName; -// import javax.management.ObjectInstance; -// import javax.management.MalformedObjectNameException; +//import javax.management.MBeanServer; +//import javax.management.ObjectInstance; +//import javax.management.ObjectName; // END android-removed import org.apache.harmony.logging.internal.nls.Messages; @@ -49,23 +51,19 @@ import org.apache.harmony.logging.internal.nls.Messages; * logging framework, and to manage a hierarchical namespace of all named * {@code Logger} objects. * <p> - * * There is only one global {@code LogManager} instance in the * application, which can be get by calling static method * {@link #getLogManager()}. This instance is created and * initialized during class initialization and cannot be changed. - * </p> * <p> * The {@code LogManager} class can be specified by * java.util.logging.manager system property, if the property is unavailable or * invalid, the default class {@link java.util.logging.LogManager} will * be used. - * </p> * <p> - * When initialization, {@code LogManager} read its configuration from a + * On initialization, {@code LogManager} reads its configuration from a * properties file, which by default is the "lib/logging.properties" in the JRE * directory. - * </p> * <p> * However, two optional system properties can be used to customize the initial * configuration process of {@code LogManager}. @@ -73,31 +71,26 @@ import org.apache.harmony.logging.internal.nls.Messages; * <li>"java.util.logging.config.class"</li> * <li>"java.util.logging.config.file"</li> * </ul> - * </p> * <p> * These two properties can be set in three ways, by the Preferences API, by the * "java" command line property definitions, or by system property definitions * passed to JNI_CreateJavaVM. - * </p> * <p> * The "java.util.logging.config.class" should specifies a class name. If it is * set, this given class will be loaded and instantiated during * {@code LogManager} initialization, so that this object's default * constructor can read the initial configuration and define properties for * {@code LogManager}. - * </p> * <p> * If "java.util.logging.config.class" property is not set, or it is invalid, or * some exception is thrown during the instantiation, then the * "java.util.logging.config.file" system property can be used to specify a * properties file. The {@code LogManager} will read initial * configuration from this file. - * </p> * <p> * If neither of these properties is defined, or some exception is thrown * during these two properties using, the {@code LogManager} will read * its initial configuration from default properties file, as described above. - * </p> * <p> * The global logging properties may include: * <ul> @@ -113,22 +106,18 @@ import org.apache.harmony.logging.internal.nls.Messages; * some logger, etc. These classes will be loaded and instantiated during * {@code LogManager} configuration</li> * </ul> - * </p> * <p> * This class, together with any handler and configuration classes associated * with it, <b>must</b> be loaded from the system classpath when * {@code LogManager} configuration occurs. - * </p> * <p> * Besides global properties, the properties for loggers and Handlers can be * specified in the property files. The names of these properties will start * with the complete dot separated names for the handlers or loggers. - * </p> * <p> * In the {@code LogManager}'s hierarchical namespace, * {@code Loggers} are organized based on their dot separated names. For * example, "x.y.z" is child of "x.y". - * </p> * <p> * Levels for {@code Loggers} can be defined by properties whose name end * with ".level". Thus "alogger.level" defines a level for the logger named as @@ -136,23 +125,15 @@ import org.apache.harmony.logging.internal.nls.Messages; * properties are read and applied in the same order as they are specified in * the property file. The root logger's level can be defined by the property * named as ".level". - * </p> * <p> * All methods on this type can be taken as being thread safe. - * </p> - * + * */ public class LogManager { - /* - * ------------------------------------------------------------------- - * Class variables - * ------------------------------------------------------------------- - */ // The line separator of the underlying OS // Use privileged code to read the line.separator system property - private static final String lineSeparator = - getPrivilegedSystemProperty("line.separator"); //$NON-NLS-1$ + private static final String lineSeparator = getPrivilegedSystemProperty("line.separator"); //$NON-NLS-1$ // The shared logging permission private static final LoggingPermission perm = new LoggingPermission( @@ -160,63 +141,55 @@ public class LogManager { // the singleton instance static LogManager manager; - + /** * The {@code String} value of the {@link LoggingMXBean}'s ObjectName. - * - * @since Android 1.0 */ - public static final String LOGGING_MXBEAN_NAME = - "java.util.logging:type=Logging"; //$NON-NLS-1$ + public static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging"; //$NON-NLS-1$ /** * Get the {@code LoggingMXBean} instance. this implementation always throws * an UnsupportedOperationException. - * + * * @return the {@code LoggingMXBean} instance */ public static LoggingMXBean getLoggingMXBean() { - // BEGIN android-added - throw new UnsupportedOperationException(); - // END android-added - // BEGIN android-removed - // try { - // ObjectName loggingMXBeanName = new ObjectName(LOGGING_MXBEAN_NAME); - // MBeanServer platformBeanServer = - // ManagementFactory.getPlatformMBeanServer(); - // Set loggingMXBeanSet = platformBeanServer.queryMBeans( - // loggingMXBeanName, null); - // - // if (loggingMXBeanSet.size() != 1) { - // // logging.21=There Can Be Only One logging MX bean. - // throw new AssertionError(Messages.getString("logging.21")); - // } - // - // Iterator i = loggingMXBeanSet.iterator(); - // ObjectInstance loggingMXBeanOI = (ObjectInstance) i.next(); - // String lmxbcn = loggingMXBeanOI.getClassName(); - // Class lmxbc = Class.forName(lmxbcn); - // Method giMethod = lmxbc.getDeclaredMethod("getInstance"); - // giMethod.setAccessible(true); - // LoggingMXBean lmxb = (LoggingMXBean) - // giMethod.invoke(null, new Object[] {}); - // - // return lmxb; - // } catch (Exception e) { - // //TODO - // //e.printStackTrace(); - // } - // // logging.22=Exception occurred while getting the logging MX bean. - // throw new AssertionError(Messages.getString("logging.22")); //$NON-NLS-1$ - // END android-removed - } + // BEGIN android-added + throw new UnsupportedOperationException(); + // END android-added + // BEGIN android-removed + // try { + // ObjectName loggingMXBeanName = new ObjectName(LOGGING_MXBEAN_NAME); + // MBeanServer platformBeanServer = ManagementFactory + // .getPlatformMBeanServer(); + // Set<?> loggingMXBeanSet = platformBeanServer.queryMBeans( + // loggingMXBeanName, null); + // + // if (loggingMXBeanSet.size() != 1) { + // // logging.21=There Can Be Only One logging MX bean. + // throw new AssertionError(Messages.getString("logging.21")); //$NON-NLS-1$ + // } + // + // Iterator<?> i = loggingMXBeanSet.iterator(); + // ObjectInstance loggingMXBeanOI = (ObjectInstance) i.next(); + // String lmxbcn = loggingMXBeanOI.getClassName(); + // Class<?> lmxbc = Class.forName(lmxbcn); + // Method giMethod = lmxbc.getDeclaredMethod("getInstance"); //$NON-NLS-1$ + // giMethod.setAccessible(true); + // LoggingMXBean lmxb = (LoggingMXBean) giMethod.invoke(null, + // new Object[] {}); + // + // return lmxb; + // } catch (Exception e) { + // // TODO + // // e.printStackTrace(); + // } + // // logging.22=Exception occurred while getting the logging MX bean. + // throw new AssertionError(Messages.getString("logging.22")); //$NON-NLS-1$ + // END android-removed + } - /* - * ------------------------------------------------------------------- - * Instance variables - * ------------------------------------------------------------------- - */ - //FIXME: use weak reference to avoid heap memory leak + // FIXME: use weak reference to avoid heap memory leak private Hashtable<String, Logger> loggers; // the configuration properties @@ -225,19 +198,13 @@ public class LogManager { // the property change listener private PropertyChangeSupport listeners; - /* - * ------------------------------------------------------------------- - * Global initialization - * ------------------------------------------------------------------- - */ - static { // init LogManager singleton instance AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { - String className = System.getProperty( - "java.util.logging.manager"); //$NON-NLS-1$ - + String className = System + .getProperty("java.util.logging.manager"); //$NON-NLS-1$ + if (null != className) { manager = (LogManager) getInstanceByClass(className); } @@ -256,7 +223,7 @@ public class LogManager { Logger root = new Logger("", null); //$NON-NLS-1$ root.setLevel(Level.INFO); Logger.global.setParent(root); - + manager.addLogger(root); manager.addLogger(Logger.global); return null; @@ -290,11 +257,6 @@ public class LogManager { } /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - /* * Package private utilities Returns the line separator of the underlying * OS. */ @@ -307,7 +269,7 @@ public class LogManager { * that it is trusted to modify the configuration for logging framework. If * the check passes, just return, otherwise {@code SecurityException} * will be thrown. - * + * * @throws SecurityException * if there is a security manager in operation and the invoker * of this method does not have the required security permission @@ -330,7 +292,7 @@ public class LogManager { * unexpectedly garbage collected it is necessary for <i>applications</i> * to maintain references to them. * </p> - * + * * @param logger * the logger to be added. * @return true if the given logger is added into the namespace @@ -347,7 +309,6 @@ public class LogManager { return true; } - private void addToFamilyTree(Logger logger, String name) { Logger parent = null; // find parent @@ -359,8 +320,8 @@ public class LogManager { if (parent != null) { logger.internalSetParent(parent); break; - } else if (getProperty(parentName+".level") != null || //$NON-NLS-1$ - getProperty(parentName+".handlers") != null) { //$NON-NLS-1$ + } else if (getProperty(parentName + ".level") != null || //$NON-NLS-1$ + getProperty(parentName + ".handlers") != null) { //$NON-NLS-1$ parent = Logger.getLogger(parentName); logger.internalSetParent(parent); break; @@ -371,16 +332,22 @@ public class LogManager { } // find children - //TODO: performance can be improved here? + // TODO: performance can be improved here? Collection<Logger> allLoggers = loggers.values(); - for (Logger child : allLoggers) { + for (final Logger child : allLoggers) { Logger oldParent = child.getParent(); if (parent == oldParent && (name.length() == 0 || child.getName().startsWith( name + '.'))) { - child.setParent(logger); + final Logger thisLogger = logger; + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + child.setParent(thisLogger); + return null; + } + }); if (null != oldParent) { - //-- remove from old parent as the parent has been changed + // -- remove from old parent as the parent has been changed oldParent.removeChild(child); } } @@ -389,7 +356,7 @@ public class LogManager { /** * Get the logger with the given name. - * + * * @param name * name of logger * @return logger with given name, or {@code null} if nothing is found. @@ -400,7 +367,7 @@ public class LogManager { /** * Get a {@code Enumeration} of all registered logger names. - * + * * @return enumeration of registered logger names */ public synchronized Enumeration<String> getLoggerNames() { @@ -409,7 +376,7 @@ public class LogManager { /** * Get the global {@code LogManager} instance. - * + * * @return the global {@code LogManager} instance */ public static LogManager getLogManager() { @@ -418,7 +385,7 @@ public class LogManager { /** * Get the value of property with given name. - * + * * @param name * the name of property * @return the value of property @@ -433,7 +400,7 @@ public class LogManager { * <p> * Notice : No {@code PropertyChangeEvent} are fired. * </p> - * + * * @throws IOException * if any IO related problems happened. * @throws SecurityException @@ -443,19 +410,20 @@ public class LogManager { public void readConfiguration() throws IOException { checkAccess(); // check config class - String configClassName = System.getProperty( - "java.util.logging.config.class"); //$NON-NLS-1$ - if (null == configClassName || null == getInstanceByClass(configClassName)) { - // if config class failed, check config file - String configFile = System.getProperty( - "java.util.logging.config.file"); //$NON-NLS-1$ + String configClassName = System + .getProperty("java.util.logging.config.class"); //$NON-NLS-1$ + if (null == configClassName + || null == getInstanceByClass(configClassName)) { + // if config class failed, check config file + String configFile = System + .getProperty("java.util.logging.config.file"); //$NON-NLS-1$ if (null == configFile) { // if cannot find configFile, use default logging.properties configFile = new StringBuilder().append( System.getProperty("java.home")).append(File.separator) //$NON-NLS-1$ .append("lib").append(File.separator).append( //$NON-NLS-1$ - "logging.properties").toString(); //$NON-NLS-1$ + "logging.properties").toString(); //$NON-NLS-1$ } InputStream input = null; @@ -463,7 +431,7 @@ public class LogManager { // BEGIN android-removed // input = new BufferedInputStream(new FileInputStream(configFile)); // END android-removed - + // BEGIN android-added try { input = new BufferedInputStream( @@ -504,13 +472,12 @@ public class LogManager { return clazz.newInstance(); } catch (Exception e) { try { - Class<?> clazz = Thread.currentThread() - .getContextClassLoader().loadClass(className); + Class<?> clazz = Thread.currentThread().getContextClassLoader() + .loadClass(className); return clazz.newInstance(); } catch (Exception innerE) { - //logging.20=Loading class "{0}" failed - System.err.println(Messages.getString( - "logging.20", className)); //$NON-NLS-1$ + // logging.20=Loading class "{0}" failed + System.err.println(Messages.getString("logging.20", className)); //$NON-NLS-1$ System.err.println(innerE); return null; } @@ -523,7 +490,7 @@ public class LogManager { throws IOException { reset(); props.load(ins); - + // parse property "config" and apply setting String configs = props.getProperty("config"); //$NON-NLS-1$ if (null != configs) { @@ -533,27 +500,25 @@ public class LogManager { getInstanceByClass(configerName); } } - + // set levels for logger Collection<Logger> allLoggers = loggers.values(); - for(Logger logger : allLoggers){ - String property = props.getProperty( - logger.getName()+".level"); //$NON-NLS-1$ - if(null != property){ + for (Logger logger : allLoggers) { + String property = props.getProperty(logger.getName() + ".level"); //$NON-NLS-1$ + if (null != property) { logger.setLevel(Level.parse(property)); } } listeners.firePropertyChange(null, null, null); } - /** * Re-initialize the properties and configuration from the given * {@code InputStream} * <p> * Notice : No {@code PropertyChangeEvent} are fired. * </p> - * + * * @param ins * the input stream * @throws IOException @@ -574,7 +539,7 @@ public class LogManager { * level is set to null, except the root logger's level is set to * {@code Level.INFO}. * </p> - * + * * @throws SecurityException * if security manager exists and it determines that caller does * not have the required permissions to perform this action. @@ -583,10 +548,10 @@ public class LogManager { checkAccess(); props = new Properties(); Enumeration<String> names = getLoggerNames(); - while(names.hasMoreElements()){ + while (names.hasMoreElements()) { String name = names.nextElement(); Logger logger = getLogger(name); - if(logger != null){ + if (logger != null) { logger.reset(); } } @@ -599,7 +564,7 @@ public class LogManager { /** * Add a {@code PropertyChangeListener}, which will be invoked when * the properties are reread. - * + * * @param l * the {@code PropertyChangeListener} to be added. * @throws SecurityException @@ -607,7 +572,7 @@ public class LogManager { * not have the required permissions to perform this action. */ public void addPropertyChangeListener(PropertyChangeListener l) { - if(l == null){ + if (l == null) { throw new NullPointerException(); } checkAccess(); @@ -617,7 +582,7 @@ public class LogManager { /** * Remove a {@code PropertyChangeListener}, do nothing if the given * listener is not found. - * + * * @param l * the {@code PropertyChangeListener} to be removed. * @throws SecurityException diff --git a/logging/src/main/java/java/util/logging/LogRecord.java b/logging/src/main/java/java/util/logging/LogRecord.java index b8a98ef..0a8e257 100644 --- a/logging/src/main/java/java/util/logging/LogRecord.java +++ b/logging/src/main/java/java/util/logging/LogRecord.java @@ -40,9 +40,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * {@code getSourceClassName} or {@code getSourceMethodName} if they expect to * use them after passing the {@code LogRecord} object to another thread or * transmitting it over RMI. - * </p> - * - * @since Android 1.0 */ public class LogRecord implements Serializable { @@ -65,70 +62,70 @@ public class LogRecord implements Serializable { /** * The logging level. - * + * * @serial */ private Level level; /** * The sequence number. - * + * * @serial */ private long sequenceNumber; /** * The name of the class that issued the logging call. - * + * * @serial */ private String sourceClassName; /** * The name of the method that issued the logging call. - * + * * @serial */ private String sourceMethodName; /** * The original message text. - * + * * @serial */ private String message; /** * The ID of the thread that issued the logging call. - * + * * @serial */ private int threadID; /** * The time that the event occurred, in milliseconds since 1970. - * + * * @serial */ private long millis; /** * The associated {@code Throwable} object if any. - * + * * @serial */ private Throwable thrown; /** * The name of the source logger. - * + * * @serial */ private String loggerName; /** * The name of the resource bundle used to localize the log message. - * + * * @serial */ private String resourceBundleName; @@ -148,14 +145,13 @@ public class LogRecord implements Serializable { * sequence property is set to a new unique value, allocated in increasing * order within the virtual machine. The thread ID is set to a unique value * for the current thread. All other properties are set to {@code null}. - * + * * @param level * the logging level, may not be {@code null}. * @param msg * the raw message. * @throws NullPointerException * if {@code level} is {@code null}. - * @since Android 1.0 */ public LogRecord(Level level, String msg) { if (null == level) { @@ -188,9 +184,8 @@ public class LogRecord implements Serializable { /** * Gets the logging level. - * + * * @return the logging level. - * @since Android 1.0 */ public Level getLevel() { return level; @@ -198,12 +193,11 @@ public class LogRecord implements Serializable { /** * Sets the logging level. - * + * * @param level * the level to set. * @throws NullPointerException * if {@code level} is {@code null}. - * @since Android 1.0 */ public void setLevel(Level level) { if (null == level) { @@ -215,9 +209,8 @@ public class LogRecord implements Serializable { /** * Gets the name of the logger. - * + * * @return the logger name. - * @since Android 1.0 */ public String getLoggerName() { return loggerName; @@ -225,10 +218,9 @@ public class LogRecord implements Serializable { /** * Sets the name of the logger. - * + * * @param loggerName * the logger name to set. - * @since Android 1.0 */ public void setLoggerName(String loggerName) { this.loggerName = loggerName; @@ -236,9 +228,8 @@ public class LogRecord implements Serializable { /** * Gets the raw message. - * + * * @return the raw message, may be {@code null}. - * @since Android 1.0 */ public String getMessage() { return message; @@ -248,10 +239,9 @@ public class LogRecord implements Serializable { * Sets the raw message. When this record is formatted by a logger that has * a localization resource bundle that contains an entry for {@code message}, * then the raw message is replaced with its localized version. - * + * * @param message * the raw message to set, may be {@code null}. - * @since Android 1.0 */ public void setMessage(String message) { this.message = message; @@ -259,9 +249,8 @@ public class LogRecord implements Serializable { /** * Gets the time when this event occurred, in milliseconds since 1970. - * + * * @return the time when this event occurred, in milliseconds since 1970. - * @since Android 1.0 */ public long getMillis() { return millis; @@ -269,10 +258,9 @@ public class LogRecord implements Serializable { /** * Sets the time when this event occurred, in milliseconds since 1970. - * + * * @param millis * the time when this event occurred, in milliseconds since 1970. - * @since Android 1.0 */ public void setMillis(long millis) { this.millis = millis; @@ -280,10 +268,9 @@ public class LogRecord implements Serializable { /** * Gets the parameters. - * + * * @return the array of parameters or {@code null} if there are no * parameters. - * @since Android 1.0 */ public Object[] getParameters() { return parameters; @@ -291,10 +278,9 @@ public class LogRecord implements Serializable { /** * Sets the parameters. - * + * * @param parameters * the array of parameters to set, may be {@code null}. - * @since Android 1.0 */ public void setParameters(Object[] parameters) { this.parameters = parameters; @@ -303,21 +289,20 @@ public class LogRecord implements Serializable { /** * Gets the resource bundle used to localize the raw message during * formatting. - * + * * @return the associated resource bundle, {@code null} if none is * available or the message is not localizable. - * @since Android 1.0 */ public ResourceBundle getResourceBundle() { return resourceBundle; } /** - * Sets the resource bundle. - * + * Sets the resource bundle used to localize the raw message during + * formatting. + * * @param resourceBundle * the resource bundle to set, may be {@code null}. - * @since Android 1.0 */ public void setResourceBundle(ResourceBundle resourceBundle) { this.resourceBundle = resourceBundle; @@ -325,10 +310,9 @@ public class LogRecord implements Serializable { /** * Gets the name of the resource bundle. - * + * * @return the name of the resource bundle, {@code null} if none is * available or the message is not localizable. - * @since Android 1.0 */ public String getResourceBundleName() { return resourceBundleName; @@ -336,10 +320,9 @@ public class LogRecord implements Serializable { /** * Sets the name of the resource bundle. - * + * * @param resourceBundleName * the name of the resource bundle to set. - * @since Android 1.0 */ public void setResourceBundleName(String resourceBundleName) { this.resourceBundleName = resourceBundleName; @@ -347,9 +330,8 @@ public class LogRecord implements Serializable { /** * Gets the sequence number. - * + * * @return the sequence number. - * @since Android 1.0 */ public long getSequenceNumber() { return sequenceNumber; @@ -359,10 +341,9 @@ public class LogRecord implements Serializable { * Sets the sequence number. It is usually not necessary to call this method * to change the sequence number because the number is allocated when this * instance is constructed. - * + * * @param sequenceNumber * the sequence number to set. - * @since Android 1.0 */ public void setSequenceNumber(long sequenceNumber) { this.sequenceNumber = sequenceNumber; @@ -371,9 +352,8 @@ public class LogRecord implements Serializable { /** * Gets the name of the class that is the source of this log record. This * information can be changed, may be {@code null} and is untrusted. - * + * * @return the name of the source class of this log record (possiblity {@code null}) - * @since Android 1.0 */ public String getSourceClassName() { initSource(); @@ -394,7 +374,8 @@ public class LogRecord implements Serializable { break FINDLOG; } } - while(++i<elements.length && elements[i].getClassName().equals(current)) { + while (++i < elements.length + && elements[i].getClassName().equals(current)) { // do nothing } if (i < elements.length) { @@ -407,11 +388,10 @@ public class LogRecord implements Serializable { /** * Sets the name of the class that is the source of this log record. - * + * * @param sourceClassName * the name of the source class of this log record, may be * {@code null}. - * @since Android 1.0 */ public void setSourceClassName(String sourceClassName) { sourceInited = true; @@ -420,9 +400,8 @@ public class LogRecord implements Serializable { /** * Gets the name of the method that is the source of this log record. - * + * * @return the name of the source method of this log record. - * @since Android 1.0 */ public String getSourceMethodName() { initSource(); @@ -431,11 +410,10 @@ public class LogRecord implements Serializable { /** * Sets the name of the method that is the source of this log record. - * + * * @param sourceMethodName * the name of the source method of this log record, may be * {@code null}. - * @since Android 1.0 */ public void setSourceMethodName(String sourceMethodName) { sourceInited = true; @@ -448,9 +426,8 @@ public class LogRecord implements Serializable { * <p> * Notice : the ID doesn't necessary map the OS thread ID * </p> - * + * * @return the ID of the thread originating this log record. - * @since Android 1.0 */ public int getThreadID() { return threadID; @@ -458,10 +435,9 @@ public class LogRecord implements Serializable { /** * Sets the ID of the thread originating this log record. - * + * * @param threadID * the new ID of the thread originating this log record. - * @since Android 1.0 */ public void setThreadID(int threadID) { this.threadID = threadID; @@ -469,9 +445,8 @@ public class LogRecord implements Serializable { /** * Gets the {@code Throwable} object associated with this log record. - * + * * @return the {@code Throwable} object associated with this log record. - * @since Android 1.0 */ public Throwable getThrown() { return thrown; @@ -479,11 +454,10 @@ public class LogRecord implements Serializable { /** * Sets the {@code Throwable} object associated with this log record. - * + * * @param thrown * the new {@code Throwable} object to associate with this log * record. - * @since Android 1.0 */ public void setThrown(Throwable thrown) { this.thrown = thrown; @@ -514,12 +488,13 @@ public class LogRecord implements Serializable { in.defaultReadObject(); byte major = in.readByte(); byte minor = in.readByte(); - //only check MAJOR version + // only check MAJOR version if (major != MAJOR) { // logging.5=Different version - {0}.{1} - throw new IOException(Messages.getString("logging.5", major, minor)); //$NON-NLS-1$ + throw new IOException(Messages.getString("logging.5", //$NON-NLS-1$ + Byte.valueOf(major), Byte.valueOf(minor))); } - + int length = in.readInt(); if (length >= 0) { parameters = new Object[length]; diff --git a/logging/src/main/java/java/util/logging/Logger.java b/logging/src/main/java/java/util/logging/Logger.java index cd88ca0..fe124d3 100644 --- a/logging/src/main/java/java/util/logging/Logger.java +++ b/logging/src/main/java/java/util/logging/Logger.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; -import java.util.StringTokenizer; import org.apache.harmony.logging.internal.nls.Messages; @@ -39,7 +38,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * namespace hierarchy managed by a log manager. The naming convention is * usually the same as java package's naming convention, that is using * dot-separated strings. Anonymous loggers do not belong to any namespace. - * </p> * <p> * Loggers "inherit" log level setting from their parent if their own level is * set to {@code null}. This is also true for the resource bundle. The logger's @@ -49,36 +47,28 @@ import org.apache.harmony.logging.internal.nls.Messages; * context, "inherit" only means that "behavior" is inherited. The internal * field values will not change, for example, {@code getLevel()} still returns * {@code null}. - * </p> * <p> * When loading a given resource bundle, the logger first tries to use the * context classloader. If that fails, it tries the system classloader. And if * that still fails, it searches up the class stack and uses each class's * classloader to try to locate the resource bundle. - * </p> * <p> * Some log methods accept log requests that do not specify the source class and * source method. In these cases, the logging framework will automatically infer * the calling class and method, but this is not guaranteed to be accurate. - * </p> * <p> * Once a {@code LogRecord} object has been passed into the logging framework, * it is owned by the logging framework and the client applications should not * use it any longer. - * </p> * <p> * All methods of this class are thread-safe. - * </p> - * + * * @see LogManager - * @since Android 1.0 */ public class Logger { /** * The global logger is provided as convenience for casual use. - * - * @since Android 1.0 */ public final static Logger global = new Logger("global", null); //$NON-NLS-1$ @@ -116,27 +106,19 @@ public class Logger { private boolean isNamed; private List<Logger> childs; - + private LogManager manager; // BEGIN android-changed private volatile boolean handlerInited; // END android-changed - - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Logger} object with the supplied name and resource * bundle name; {@code notifiyParentHandlers} is set to {@code true}. * <p> - * Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x". - * </p> - * + * Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x". + * * @param name * the name of this logger, may be {@code null} for anonymous * loggers. @@ -145,7 +127,6 @@ public class Logger { * messages, may be {@code null}. * @throws MissingResourceException * if the specified resource bundle can not be loaded. - * @since Android 1.0 */ protected Logger(String name, String resourceBundleName) { // try to load the specified resource bundle first @@ -164,24 +145,24 @@ public class Logger { // any logger is not anonymous by default this.isNamed = true; - //-- 'null' means that level will be inherited from parent (see getLevel) - //-- Level.INFO is default level if we don't set it. It will be - //-- changed to parent level or to configLevel after adding to the - //-- family tree. As of this, actually, setting to Level.INFO is - //-- not needed here. + // -- 'null' means that level will be inherited from parent (see + // getLevel) + // -- Level.INFO is default level if we don't set it. It will be + // -- changed to parent level or to configLevel after adding to the + // -- family tree. As of this, actually, setting to Level.INFO is + // -- not needed here. this.levelObjVal = null; this.levelIntVal = Level.INFO.intValue(); } - //-- should be called under the lm lock + // -- should be called under the lm lock private void setLevelImpl(Level newLevel) { // update levels for the whole hierarchy int oldVal = levelIntVal; levelObjVal = newLevel; if (null == newLevel) { - levelIntVal = null != parent - ? parent.levelIntVal - : Level.INFO.intValue(); + levelIntVal = null != parent ? parent.levelIntVal : Level.INFO + .intValue(); } else { levelIntVal = newLevel.intValue(); } @@ -190,7 +171,7 @@ public class Logger { } } - //-- should be called under the lm lock + // -- should be called under the lm lock private void forceChildsToInherit() { for (Logger child : childs) { if (null == child.levelObjVal) { // should inherit @@ -199,15 +180,9 @@ public class Logger { } } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - /** * Load the specified resource bundle, use privileged code. - * + * * @param resourceBundleName * the name of the resource bundle to load, cannot be {@code null}. * @return the loaded resource bundle. @@ -216,8 +191,8 @@ public class Logger { */ static ResourceBundle loadResourceBundle(String resourceBundleName) { // try context class loader to load the resource - ClassLoader cl = AccessController.doPrivileged( - new PrivilegedAction<ClassLoader>() { + ClassLoader cl = AccessController + .doPrivileged(new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } @@ -231,12 +206,11 @@ public class Logger { } } // try system class loader to load the resource - cl = AccessController.doPrivileged( - new PrivilegedAction<ClassLoader>() { - public ClassLoader run() { - return ClassLoader.getSystemClassLoader(); - } - }); + cl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { + public ClassLoader run() { + return ClassLoader.getSystemClassLoader(); + } + }); if (null != cl) { try { return ResourceBundle.getBundle(resourceBundleName, Locale @@ -257,8 +231,8 @@ public class Logger { for (int i = 1; i < classes.length; i++) { final int index = i; try { - cl = AccessController.doPrivileged( - new PrivilegedAction<ClassLoader>() { + cl = AccessController + .doPrivileged(new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return classes[index].getClassLoader(); } @@ -274,7 +248,7 @@ public class Logger { } // logging.8=Failed to load the specified resource bundle "{0}". throw new MissingResourceException(Messages.getString("logging.8", //$NON-NLS-1$ - resourceBundleName), resourceBundleName, null); + resourceBundleName), resourceBundleName, null); } /** @@ -284,10 +258,8 @@ public class Logger { * <p> * The anonymous loggers' parent is set to be the root logger. This way it * inherits the default logging level and handlers from the root logger. - * </p> - * + * * @return a new instance of anonymous logger. - * @since Android 1.0 */ public static Logger getAnonymousLogger() { return getAnonymousLogger(null); @@ -300,14 +272,12 @@ public class Logger { * <p> * The anonymous loggers' parent is set to be the root logger. This way it * inherits default logging level and handlers from the root logger. - * </p> - * + * * @param resourceBundleName * the name of the resource bundle used to localize log messages. * @return a new instance of anonymous logger. * @throws MissingResourceException * if the specified resource bundle can not be loaded. - * @since Android 1.0 */ public static Logger getAnonymousLogger(String resourceBundleName) { final Logger l = new Logger(null, resourceBundleName); @@ -324,26 +294,24 @@ public class Logger { private static void updateResourceBundle(Logger l, String resourceBundleName) { synchronized (l) { if (null == l.getResourceBundleName()) { - if(null == resourceBundleName){ + if (null == resourceBundleName) { return; } /* - * load the resource bundle if none is specified - * before + * load the resource bundle if none is specified before */ l.resBundle = loadResourceBundle(resourceBundleName); l.resBundleName = resourceBundleName; } else if (!l.getResourceBundleName().equals(resourceBundleName)) { /* - * throw exception if the specified resource bundles - * are inconsistent with each other, i.e., different - * names + * throw exception if the specified resource bundles are + * inconsistent with each other, i.e., different names */ - // logging.9=The specified resource bundle name "{0}" is + // logging.9=The specified resource bundle name "{0}" is // inconsistent with the existing one "{1}". throw new IllegalArgumentException(Messages.getString( "logging.9", //$NON-NLS-1$ - resourceBundleName, l.getResourceBundleName())); + resourceBundleName, l.getResourceBundleName())); } } } @@ -378,11 +346,12 @@ public class Logger { * Gets a named logger. The returned logger may already exist or may be * newly created. In the latter case, its level will be set to the * configured level according to the {@code LogManager}'s properties. - * + * * @param name * the name of the logger to get, cannot be {@code null}. * @return a named logger. - * @since Android 1.0 + * @throws MissingResourceException + * If the specified resource bundle can not be loaded. */ public static Logger getLogger(String name) { return getLoggerWithRes(name, null, false); @@ -391,7 +360,7 @@ public class Logger { /** * Gets a named logger associated with the supplied resource bundle. The * resource bundle will be used to localize logging messages. - * + * * @param name * the name of the logger to get, cannot be {@code null}. * @param resourceBundleName @@ -403,7 +372,6 @@ public class Logger { * @throws MissingResourceException * if the name of the resource bundle cannot be found. * @return a named logger. - * @since Android 1.0 */ public static Logger getLogger(String name, String resourceBundleName) { return getLoggerWithRes(name, resourceBundleName, true); @@ -412,13 +380,12 @@ public class Logger { /** * Adds a handler to this logger. The {@code name} will be fed with log * records received by this logger. - * + * * @param handler * the handler object to add, cannot be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void addHandler(Handler handler) { if (null == handler) { @@ -430,25 +397,25 @@ public class Logger { LogManager.getLogManager().checkAccess(); } initHandler(); - synchronized(this){ + synchronized (this) { this.handlers.add(handler); } } - + /* - * Be cautious to avoid deadlock when using this method, it gets lock on manager - * at first, and then gets lock on this Logger, so any methods should not hold - * lock on this Logger when invoking this method. + * Be cautious to avoid deadlock when using this method, it gets lock on manager + * at first, and then gets lock on this Logger, so any methods should not hold + * lock on this Logger when invoking this method. */ private void initHandler() { - if(!handlerInited){ + if (!handlerInited) { synchronized (this) { if (!handlerInited) { // BEGIN android-added /* * Force LogManager to be initialized, since its * class init code performs necessary one-time setup. - */ + */ LogManager.getLogManager(); // END android-added if (handlers == null) { @@ -463,26 +430,27 @@ public class Logger { if (null == handlerStr) { return; } - StringTokenizer st = new StringTokenizer(handlerStr, " "); //$NON-NLS-1$ - while (st.hasMoreTokens()) { - String handlerName = st.nextToken(); - // BEGIN android-changed - // deal with non-existing handler - try { - Handler handler = (Handler) LogManager - .getInstanceByClass(handlerName); - handlers.add(handler); - String level = manager.getProperty(handlerName - + ".level"); //$NON-NLS-1$ - if (null != level) { - handler.setLevel(Level.parse(level)); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - // END android-changed + String[] strs = handlerStr.split(",|\\s"); //$NON-NLS-1$ + for (int i = 0; i < strs.length; i++) { + String handlerName = strs[i]; + if (handlerName.equals("")) { //$NON-NLS-1$ + continue; + } + // BEGIN android-changed + // deal with non-existing handler + try { + Handler handler = (Handler) LogManager.getInstanceByClass(handlerName); + handlers.add(handler); + String level = manager.getProperty(handlerName + ".level"); //$NON-NLS-1$ + if (null != level) { + handler.setLevel(Level.parse(level)); + } + } catch (Exception ex) { + ex.printStackTrace(); } - handlerInited = true; + // END android-changed + } + handlerInited = true; } } } @@ -490,13 +458,12 @@ public class Logger { /** * Gets all the handlers associated with this logger. - * + * * @return an array of all the handlers associated with this logger. - * @since Android 1.0 */ public Handler[] getHandlers() { initHandler(); - synchronized(this){ + synchronized (this) { return handlers.toArray(new Handler[handlers.size()]); } } @@ -504,13 +471,12 @@ public class Logger { /** * Removes a handler from this logger. If the specified handler does not * exist then this method has no effect. - * + * * @param handler * the handler to be removed. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void removeHandler(Handler handler) { // Anonymous loggers can always remove handlers @@ -521,16 +487,15 @@ public class Logger { return; } initHandler(); - synchronized(this){ + synchronized (this) { this.handlers.remove(handler); } } /** * Gets the filter used by this logger. - * + * * @return the filter used by this logger, may be {@code null}. - * @since Android 1.0 */ public Filter getFilter() { return this.filter; @@ -538,13 +503,12 @@ public class Logger { /** * Sets the filter used by this logger. - * + * * @param newFilter * the filter to set, may be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFilter(Filter newFilter) { // Anonymous loggers can always set the filter @@ -557,9 +521,8 @@ public class Logger { /** * Gets the logging level of this logger. A {@code null} level indicates * that this logger inherits its parent's level. - * + * * @return the logging level of this logger. - * @since Android 1.0 */ public Level getLevel() { return levelObjVal; @@ -568,13 +531,12 @@ public class Logger { /** * Sets the logging level for this logger. A {@code null} level indicates * that this logger will inherit its parent's level. - * + * * @param newLevel * the logging level to set. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setLevel(Level newLevel) { // Anonymous loggers can always set the level @@ -590,10 +552,9 @@ public class Logger { * Gets the flag which indicates whether to use the handlers of this * logger's parent to publish incoming log records, potentially recursively * up the namespace. - * + * * @return {@code true} if set to use parent's handlers, {@code false} * otherwise. - * @since Android 1.0 */ public boolean getUseParentHandlers() { return this.notifyParentHandlers; @@ -602,13 +563,12 @@ public class Logger { /** * Sets the flag which indicates whether to use the handlers of this * logger's parent, potentially recursively up the namespace. - * + * * @param notifyParentHandlers * the new flag indicating whether to use the parent's handlers. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setUseParentHandlers(boolean notifyParentHandlers) { // Anonymous loggers can always set the useParentHandlers flag @@ -621,9 +581,8 @@ public class Logger { /** * Gets the nearest parent of this logger in the namespace, a {@code null} * value will be returned if called on the root logger. - * + * * @return the parent of this logger in the namespace. - * @since Android 1.0 */ public Logger getParent() { return parent; @@ -633,14 +592,14 @@ public class Logger { * Sets the parent of this logger in the namespace. This method should * usually be used by the {@code LogManager} object only. This method does * not check security. - * + * * @param newParent * the parent logger to set. - * @since Android 1.0 */ void internalSetParent(Logger newParent) { - //All hierarchy related modifications should get LogManager lock at first - synchronized(LogManager.getLogManager()){ + // All hierarchy related modifications should get LogManager lock at + // first + synchronized (LogManager.getLogManager()) { parent = newParent; // -- update level after setting a parent. // -- if level == null we should inherit the parent's level @@ -654,13 +613,12 @@ public class Logger { /** * Sets the parent of this logger in the namespace. This method should be * used by the {@code LogManager} object only. - * + * * @param parent * the parent logger to set. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setParent(Logger parent) { if (null == parent) { @@ -680,12 +638,10 @@ public class Logger { childs.remove(child); } - /** * Gets the name of this logger, {@code null} for anonymous loggers. - * + * * @return the name of this logger. - * @since Android 1.0 */ public String getName() { return this.name; @@ -695,9 +651,8 @@ public class Logger { * Gets the loaded resource bundle used by this logger to localize logging * messages. If the value is {@code null}, the parent's resource bundle will be * inherited. - * + * * @return the loaded resource bundle used by this logger. - * @since Android 1.0 */ public ResourceBundle getResourceBundle() { return this.resBundle; @@ -707,9 +662,8 @@ public class Logger { * Gets the name of the loaded resource bundle used by this logger to * localize logging messages. If the value is {@code null}, the parent's resource * bundle name will be inherited. - * + * * @return the name of the loaded resource bundle used by this logger. - * @since Android 1.0 */ public String getResourceBundleName() { return this.resBundleName; @@ -734,12 +688,11 @@ public class Logger { * Determines whether this logger will actually log messages of the * specified level. The effective level used to do the determination may be * inherited from its parent. The default level is {@code Level.INFO}. - * + * * @param l * the level to check. * @return {@code true} if this logger will actually log this level, * otherwise {@code false}. - * @since Android 1.0 */ public boolean isLoggable(Level l) { return internalIsLoggable(l); @@ -775,12 +728,11 @@ public class Logger { * Logs a message indicating that a method has been entered. A log record * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name and source method name is submitted for logging. - * + * * @param sourceClass * the calling class name. * @param sourceMethod * the method name. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod) { if (internalIsLoggable(Level.FINER)) { @@ -798,14 +750,13 @@ public class Logger { * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name, source method name and one parameter is submitted for * logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param param * the parameter for the method call. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod, Object param) { if (internalIsLoggable(Level.FINER)) { @@ -824,14 +775,13 @@ public class Logger { * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name, source method name and array of parameters is * submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param params * an array of parameters for the method call. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod, Object[] params) { @@ -858,12 +808,11 @@ public class Logger { * Logs a message indicating that a method is exited. A log record with log * level {@code Level.FINER}, log message "RETURN", the specified source * class name and source method name is submitted for logging. - * + * * @param sourceClass * the calling class name. * @param sourceMethod * the method name. - * @since Android 1.0 */ public void exiting(String sourceClass, String sourceMethod) { if (internalIsLoggable(Level.FINER)) { @@ -880,14 +829,13 @@ public class Logger { * Logs a message indicating that a method is exited. A log record with log * level {@code Level.FINER}, log message "RETURN", the specified source * class name, source method name and return value is submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param result * the return value of the method call. - * @since Android 1.0 */ public void exiting(String sourceClass, String sourceMethod, Object result) { if (internalIsLoggable(Level.FINER)) { @@ -906,14 +854,13 @@ public class Logger { * log level {@code Level.FINER}, log message "THROW", the specified source * class name, source method name and the {@code Throwable} object is * submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void throwing(String sourceClass, String sourceMethod, Throwable thrown) { @@ -931,10 +878,9 @@ public class Logger { /** * Logs a message of level {@code Level.SEVERE}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void severe(String msg) { if (internalIsLoggable(Level.SEVERE)) { @@ -948,10 +894,9 @@ public class Logger { /** * Logs a message of level {@code Level.WARNING}; the message is * transmitted to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void warning(String msg) { if (internalIsLoggable(Level.WARNING)) { @@ -965,10 +910,9 @@ public class Logger { /** * Logs a message of level {@code Level.INFO}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void info(String msg) { if (internalIsLoggable(Level.INFO)) { @@ -982,10 +926,9 @@ public class Logger { /** * Logs a message of level {@code Level.CONFIG}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void config(String msg) { if (internalIsLoggable(Level.CONFIG)) { @@ -999,10 +942,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINE}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void fine(String msg) { if (internalIsLoggable(Level.FINE)) { @@ -1016,10 +958,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINER}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void finer(String msg) { if (internalIsLoggable(Level.FINER)) { @@ -1033,10 +974,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINEST}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void finest(String msg) { if (internalIsLoggable(Level.FINEST)) { @@ -1050,12 +990,11 @@ public class Logger { /** * Logs a message of the specified level. The message is transmitted to all * subscribed handlers. - * + * * @param logLevel * the level of the specified message. * @param msg * the message to log. - * @since Android 1.0 */ public void log(Level logLevel, String msg) { if (internalIsLoggable(logLevel)) { @@ -1069,14 +1008,13 @@ public class Logger { /** * Logs a message of the specified level with the supplied parameter. The * message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message. * @param msg * the message to log. * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Object param) { if (internalIsLoggable(logLevel)) { @@ -1091,14 +1029,13 @@ public class Logger { /** * Logs a message of the specified level with the supplied parameter array. * The message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message * @param msg * the message to log. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Object[] params) { if (internalIsLoggable(logLevel)) { @@ -1113,7 +1050,7 @@ public class Logger { /** * Logs a message of the specified level with the supplied {@code Throwable} * object. The message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message. * @param msg @@ -1121,7 +1058,6 @@ public class Logger { * @param thrown * the {@code Throwable} object associated with the event that is * logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Throwable thrown) { if (internalIsLoggable(logLevel)) { @@ -1144,10 +1080,9 @@ public class Logger { * logging action, subclasses of this class can override this method to * catch all logging activities. * </p> - * + * * @param record * the log record to be logged. - * @since Android 1.0 */ public void log(LogRecord record) { if (internalIsLoggable(record.getLevel())) { @@ -1158,8 +1093,7 @@ public class Logger { } initHandler(); /* - * call the handlers of this logger, throw any exception that - * occurs + * call the handlers of this logger, throw any exception that occurs */ Handler[] allHandlers = getHandlers(); for (Handler element : allHandlers) { @@ -1182,7 +1116,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name * and source method name. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1191,7 +1125,6 @@ public class Logger { * the source method name. * @param msg * the message to be logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg) { @@ -1208,7 +1141,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and parameter. - * + * * @param logLevel * the level of the given message * @param sourceClass @@ -1219,7 +1152,6 @@ public class Logger { * the message to be logged * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Object param) { @@ -1237,7 +1169,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and parameter array. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1248,7 +1180,6 @@ public class Logger { * the message to be logged. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Object[] params) { @@ -1266,7 +1197,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and {@code Throwable} object. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1277,7 +1208,6 @@ public class Logger { * the message to be logged. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Throwable thrown) { @@ -1297,7 +1227,7 @@ public class Logger { * and source method name, using the given resource bundle to localize the * message. If {@code bundleName} is null, the empty string or not valid then * the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1308,7 +1238,6 @@ public class Logger { * the name of the resource bundle used to localize the message. * @param msg * the message to be logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg) { @@ -1334,7 +1263,7 @@ public class Logger { * source method name and parameter, using the given resource bundle to * localize the message. If {@code bundleName} is null, the empty string * or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1347,7 +1276,6 @@ public class Logger { * the message to be logged. * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Object param) { @@ -1374,7 +1302,7 @@ public class Logger { * source method name and parameter array, using the given resource bundle * to localize the message. If {@code bundleName} is null, the empty string * or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1387,7 +1315,6 @@ public class Logger { * the message to be logged. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Object[] params) { @@ -1414,7 +1341,7 @@ public class Logger { * source method name and {@code Throwable} object, using the given resource * bundle to localize the message. If {@code bundleName} is null, the empty * string or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message * @param sourceClass @@ -1427,7 +1354,6 @@ public class Logger { * the message to be logged. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Throwable thrown) { @@ -1459,25 +1385,30 @@ public class Logger { } void setManager(LogManager manager) { - if(this.manager != manager){ + if (this.manager != manager) { this.manager = manager; - handlerInited = false; + handlerInited = false; } - //init level here, but let handlers be for lazy loading - String configedLevel = manager.getProperty(name+ ".level"); //$NON-NLS-1$ + // init level here, but let handlers be for lazy loading + final String configedLevel = manager.getProperty(name + ".level"); //$NON-NLS-1$ if (null != configedLevel) { try { - setLevel(Level.parse(configedLevel)); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + setLevel(Level.parse(configedLevel)); + return null; + } + }); } catch (IllegalArgumentException e) { - //ignore + // ignore } - } + } } synchronized void reset() { levelObjVal = null; levelIntVal = Level.INFO.intValue(); - if(handlers != null){ + if (handlers != null) { for (Handler element : handlers) { // close all handlers, when unknown exceptions happen, // ignore them and go on @@ -1492,4 +1423,3 @@ public class Logger { handlerInited = false; } } - diff --git a/logging/src/main/java/java/util/logging/LoggingMXBean.java b/logging/src/main/java/java/util/logging/LoggingMXBean.java index f6b49a6..18cc4cc 100644 --- a/logging/src/main/java/java/util/logging/LoggingMXBean.java +++ b/logging/src/main/java/java/util/logging/LoggingMXBean.java @@ -25,29 +25,28 @@ import java.util.List; * The ObjectName for identifying the {@code LoggingMXBean} in a bean server is * {@link LogManager#LOGGING_MXBEAN_NAME}. * </p> - * - * @since Android 1.0 + * + * @since 1.5 */ public interface LoggingMXBean { + /** * Gets the string value of the logging level of a logger. An empty string * is returned when the logger's level is defined by its parent. A * {@code null} is returned if the specified logger does not exist. - * + * * @param loggerName * the name of the logger lookup. * @return a {@code String} if the logger is found, otherwise {@code null}. * @see Level#getName() - * @since Android 1.0 */ String getLoggerLevel(String loggerName); /** * Gets a list of all currently registered logger names. This is performed * using the {@link LogManager#getLoggerNames()}. - * + * * @return a list of logger names. - * @since Android 1.0 */ List<String> getLoggerNames(); @@ -55,18 +54,17 @@ public interface LoggingMXBean { * Gets the name of the parent logger of a logger. If the logger doesn't * exist then {@code null} is returned. If the logger is the root logger, * then an empty {@code String} is returned. - * + * * @param loggerName * the name of the logger to lookup. * @return a {@code String} if the logger was found, otherwise {@code null}. - * @since Android 1.0 */ String getParentLoggerName(String loggerName); /** * Sets the log level of a logger. LevelName set to {@code null} means the * level is inherited from the nearest non-null ancestor. - * + * * @param loggerName * the name of the logger to set the level on, which must not be * {@code null}. @@ -79,7 +77,6 @@ public interface LoggingMXBean { * if a security manager exists and the caller doesn't have * LoggingPermission("control"). * @see Level#parse(String) - * @since Android 1.0 */ void setLoggerLevel(String loggerName, String levelName); } diff --git a/logging/src/main/java/java/util/logging/LoggingPermission.java b/logging/src/main/java/java/util/logging/LoggingPermission.java index fb6d4f8..aa41a2c 100644 --- a/logging/src/main/java/java/util/logging/LoggingPermission.java +++ b/logging/src/main/java/java/util/logging/LoggingPermission.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.Serializable; @@ -27,20 +26,12 @@ import org.apache.harmony.logging.internal.nls.Messages; /** * The permission required to control the logging when run with a * {@code SecurityManager}. - * */ public final class LoggingPermission extends BasicPermission implements Guard, Serializable { - //for serialization compatibility with J2SE 1.4.2 - private static final long serialVersionUID =63564341580231582L; - - - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ + // for serialization compatibility with J2SE 1.4.2 + private static final long serialVersionUID = 63564341580231582L; /** * Constructs a {@code LoggingPermission} object required to control the @@ -50,7 +41,7 @@ public final class LoggingPermission extends BasicPermission implements Guard, * and depends on the security policy file, therefore programmers shouldn't * normally use them directly. * </p> - * + * * @param name * currently must be "control". * @param actions @@ -71,4 +62,3 @@ public final class LoggingPermission extends BasicPermission implements Guard, } } - diff --git a/logging/src/main/java/java/util/logging/MemoryHandler.java b/logging/src/main/java/java/util/logging/MemoryHandler.java index c1e8670..3312083 100644 --- a/logging/src/main/java/java/util/logging/MemoryHandler.java +++ b/logging/src/main/java/java/util/logging/MemoryHandler.java @@ -22,7 +22,6 @@ import java.security.PrivilegedExceptionAction; import org.apache.harmony.logging.internal.nls.Messages; - /** * A {@code Handler} put the description of log events into a cycled memory * buffer. @@ -30,68 +29,61 @@ import org.apache.harmony.logging.internal.nls.Messages; * Mostly this {@code MemoryHandler} just puts the given {@code LogRecord} into * the internal buffer and doesn't perform any formatting or any other process. * When the buffer is full, the earliest buffered records will be discarded. - * </p> * <p> * Every {@code MemoryHandler} has a target handler, and push action can be * triggered so that all buffered records will be output to the target handler * and normally the latter will publish the records. After the push action, the * buffer will be cleared. - * </p> * <p> - * The push action can be triggered in three ways: - * <ul> - * <li>The push method is called explicitly</li> - * <li>When a new {@code LogRecord} is put into the internal buffer, and it has - * a level which is not less than the specified push level.</li> - * <li>A subclass extends this {@code MemoryHandler} and call push method - * implicitly according to some criteria.</li> - * </ul> - * </p> + * The push method can be called directly, but will also be called automatically + * if a new <code>LogRecord</code> is added that has a level greater than or + * equal to than the value defined for the property + * java.util.logging.MemoryHandler.push. * <p> * {@code MemoryHandler} will read following {@code LogManager} properties for * initialization, if given properties are not defined or has invalid values, * default value will be used. * <ul> - * <li>java.util.logging.MemoryHandler.level specifies the level for this - * {@code Handler}, defaults to {@code Level.ALL}.</li> * <li>java.util.logging.MemoryHandler.filter specifies the {@code Filter} * class name, defaults to no {@code Filter}.</li> - * <li>java.util.logging.MemoryHandler.size specifies the buffer size in number - * of {@code LogRecord}, defaults to 1000.</li> + * <li>java.util.logging.MemoryHandler.level specifies the level for this + * {@code Handler}, defaults to {@code Level.ALL}.</li> * <li>java.util.logging.MemoryHandler.push specifies the push level, defaults * to level.SEVERE.</li> + * <li>java.util.logging.MemoryHandler.size specifies the buffer size in number + * of {@code LogRecord}, defaults to 1000.</li> * <li>java.util.logging.MemoryHandler.target specifies the class of the target * {@code Handler}, no default value, which means this property must be * specified either by property setting or by constructor.</li> * </ul> - * </p> */ public class MemoryHandler extends Handler { - //default maximum buffered number of LogRecord + // default maximum buffered number of LogRecord private static final int DEFAULT_SIZE = 1000; - //target handler + + // target handler private Handler target; - - //buffer size + + // buffer size private int size = DEFAULT_SIZE; - - //push level + + // push level private Level push = Level.SEVERE; - //LogManager instance for convenience + // LogManager instance for convenience private final LogManager manager = LogManager.getLogManager(); - - //buffer + + // buffer private LogRecord[] buffer; - - //current position in buffer + + // current position in buffer private int cursor; - + /** * Default constructor, construct and init a {@code MemoryHandler} using * {@code LogManager} properties or default values. - * + * * @throws RuntimeException * if property value are invalid and no default value could be * used. @@ -99,55 +91,57 @@ public class MemoryHandler extends Handler { public MemoryHandler() { super(); String className = this.getClass().getName(); - //init target - final String targetName = manager.getProperty(className+".target"); //$NON-NLS-1$ + // init target + final String targetName = manager.getProperty(className + ".target"); //$NON-NLS-1$ try { - Class<?> targetClass = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>(){ - public Class<?> run() throws Exception{ - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if(loader == null){ - loader = ClassLoader.getSystemClassLoader(); - } - return loader.loadClass(targetName); - } - }); + Class<?> targetClass = AccessController + .doPrivileged(new PrivilegedExceptionAction<Class<?>>() { + public Class<?> run() throws Exception { + ClassLoader loader = Thread.currentThread() + .getContextClassLoader(); + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + return loader.loadClass(targetName); + } + }); target = (Handler) targetClass.newInstance(); } catch (Exception e) { // logging.10=Cannot load target handler:{0} throw new RuntimeException(Messages.getString("logging.10", //$NON-NLS-1$ targetName)); } - //init size - String sizeString = manager.getProperty(className+".size"); //$NON-NLS-1$ + // init size + String sizeString = manager.getProperty(className + ".size"); //$NON-NLS-1$ if (null != sizeString) { try { size = Integer.parseInt(sizeString); - if(size <= 0){ + if (size <= 0) { size = DEFAULT_SIZE; } } catch (Exception e) { - printInvalidPropMessage(className+".size", sizeString, e); //$NON-NLS-1$ + printInvalidPropMessage(className + ".size", sizeString, e); //$NON-NLS-1$ } } - //init push level - String pushName = manager.getProperty(className+".push"); //$NON-NLS-1$ + // init push level + String pushName = manager.getProperty(className + ".push"); //$NON-NLS-1$ if (null != pushName) { try { push = Level.parse(pushName); } catch (Exception e) { - printInvalidPropMessage(className+".push", pushName, e); //$NON-NLS-1$ + printInvalidPropMessage(className + ".push", pushName, e); //$NON-NLS-1$ } } - //init other properties which are common for all Handler - initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ + // init other properties which are common for all Handler + initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ buffer = new LogRecord[size]; } - + /** * Construct and init a {@code MemoryHandler} using given target, size and * push level, other properties using {@code LogManager} properties or * default values. - * + * * @param target * the given {@code Handler} to output * @param size @@ -156,7 +150,7 @@ public class MemoryHandler extends Handler { * @param pushLevel * the push level * @throws IllegalArgumentException - * if {@code size}<=0 + * if {@code size <= 0} * @throws RuntimeException * if property value are invalid and no default value could be * used. @@ -171,13 +165,13 @@ public class MemoryHandler extends Handler { this.target = target; this.size = size; this.push = pushLevel; - initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ + initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ buffer = new LogRecord[size]; } - + /** * Close this handler and target handler, free all associated resources. - * + * * @throws SecurityException * if security manager exists and it determines that caller does * not have the required permissions to control this handler. @@ -204,7 +198,7 @@ public class MemoryHandler extends Handler { * Furthermore if the record's level is not less than the push level, the * push action is triggered to output all the buffered records to the target * handler, and the target handler will publish them. - * + * * @param record * the log record */ @@ -225,7 +219,7 @@ public class MemoryHandler extends Handler { /** * Return the push level. - * + * * @return the push level */ public Level getPushLevel() { @@ -233,18 +227,14 @@ public class MemoryHandler extends Handler { } /** - * <p> * Check if given {@code LogRecord} would be put into this * {@code MemoryHandler}'s internal buffer. - * </p> * <p> * The given {@code LogRecord} is loggable if and only if it has appropriate * level and it pass any associated filter's check. - * </p> * <p> * Note that the push level is not used for this check. - * </p> - * + * * @param record * the given {@code LogRecord} * @return the given {@code LogRecord} if it should be logged, {@code false} @@ -261,13 +251,13 @@ public class MemoryHandler extends Handler { */ public void push() { for (int i = cursor; i < size; i++) { - if(null != buffer[i]) { + if (null != buffer[i]) { target.publish(buffer[i]); } buffer[i] = null; } for (int i = 0; i < cursor; i++) { - if(null != buffer[i]) { + if (null != buffer[i]) { target.publish(buffer[i]); } buffer[i] = null; @@ -276,15 +266,15 @@ public class MemoryHandler extends Handler { } /** - * Set the push level. The push level is used to check the push action + * Set the push level. The push level is used to check the push action * triggering. When a new {@code LogRecord} is put into the internal - * buffer and its level is not less than the push level, the push action + * buffer and its level is not less than the push level, the push action * will be triggered. Note that set new push level won't trigger push action. - * + * * @param newLevel * the new level to set. * @throws SecurityException - * if security manager exists and it determines that caller + * if security manager exists and it determines that caller * does not have the required permissions to control this handler. */ public void setPushLevel(Level newLevel) { diff --git a/logging/src/main/java/java/util/logging/SimpleFormatter.java b/logging/src/main/java/java/util/logging/SimpleFormatter.java index 1595796..def4ad3 100644 --- a/logging/src/main/java/java/util/logging/SimpleFormatter.java +++ b/logging/src/main/java/java/util/logging/SimpleFormatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.PrintWriter; @@ -26,13 +25,10 @@ import java.util.Date; /** * {@code SimpleFormatter} can be used to print a summary of the information * contained in a {@code LogRecord} object in a human readable format. - * @since Android 1.0 */ public class SimpleFormatter extends Formatter { /** * Constructs a new {@code SimpleFormatter}. - * - * @since Android 1.0 */ public SimpleFormatter() { super(); @@ -41,11 +37,10 @@ public class SimpleFormatter extends Formatter { /** * Converts a {@link LogRecord} object into a human readable string * representation. - * + * * @param r * the log record to be formatted into a string. * @return the formatted string. - * @since Android 1.0 */ @Override public String format(LogRecord r) { @@ -53,7 +48,8 @@ public class SimpleFormatter extends Formatter { sb.append(MessageFormat.format("{0, date} {0, time} ", //$NON-NLS-1$ new Object[] { new Date(r.getMillis()) })); sb.append(r.getSourceClassName()).append(" "); //$NON-NLS-1$ - sb.append(r.getSourceMethodName()).append(LogManager.getSystemLineSeparator()); //$NON-NLS-1$ + sb.append(r.getSourceMethodName()).append( + LogManager.getSystemLineSeparator()); sb.append(r.getLevel().getName()).append(": "); //$NON-NLS-1$ sb.append(formatMessage(r)).append(LogManager.getSystemLineSeparator()); if (null != r.getThrown()) { @@ -66,7 +62,7 @@ public class SimpleFormatter extends Formatter { t.printStackTrace(pw); sb.append(sw.toString()); } finally { - if(pw != null){ + if (pw != null) { try { pw.close(); } catch (Exception e) { @@ -78,4 +74,3 @@ public class SimpleFormatter extends Formatter { return sb.toString(); } } - diff --git a/logging/src/main/java/java/util/logging/SocketHandler.java b/logging/src/main/java/java/util/logging/SocketHandler.java index 8626007..38cfd64 100644 --- a/logging/src/main/java/java/util/logging/SocketHandler.java +++ b/logging/src/main/java/java/util/logging/SocketHandler.java @@ -15,12 +15,11 @@ * limitations under the License. */ - package java.util.logging; -import java.net.Socket; import java.io.BufferedOutputStream; import java.io.IOException; +import java.net.Socket; import org.apache.harmony.logging.internal.nls.Messages; @@ -48,16 +47,14 @@ import org.apache.harmony.logging.internal.nls.Messages; * <li>java.util.logging.SocketHandler.encoding specifies the port number that * this handler should connect to. There's no default value for this property. * </ul> - * </p> * <p> * This handler buffers the outgoing messages, but flushes each time a log * record has been published. - * </p> * <p> * This class is not thread-safe. - * </p> */ public class SocketHandler extends StreamHandler { + // default level private static final String DEFAULT_LEVEL = "ALL"; //$NON-NLS-1$ @@ -71,7 +68,7 @@ public class SocketHandler extends StreamHandler { * Constructs a {@code SocketHandler} object using the properties read by * the log manager, including the host name and port number. Default * formatting uses the XMLFormatter class and level is set to ALL. - * + * * @throws IOException * if failed to connect to the specified host and port. * @throws IllegalArgumentException @@ -92,7 +89,7 @@ public class SocketHandler extends StreamHandler { * Constructs a {@code SocketHandler} object using the specified host name * and port number together with other properties read by the log manager. * Default formatting uses the XMLFormatter class and level is set to ALL. - * + * * @param host * the host name * @param port @@ -146,7 +143,7 @@ public class SocketHandler extends StreamHandler { /** * Closes this handler. The network connection to the host is also closed. - * + * * @throws SecurityException * If a security manager determines that the caller does not * have the required permission to control this handler. @@ -168,7 +165,7 @@ public class SocketHandler extends StreamHandler { /** * Logs a record if necessary. A flush operation will be done afterwards. - * + * * @param record * the log record to be logged. */ @@ -177,5 +174,4 @@ public class SocketHandler extends StreamHandler { super.publish(record); super.flush(); } - } diff --git a/logging/src/main/java/java/util/logging/StreamHandler.java b/logging/src/main/java/java/util/logging/StreamHandler.java index ee12190..7049d45 100644 --- a/logging/src/main/java/java/util/logging/StreamHandler.java +++ b/logging/src/main/java/java/util/logging/StreamHandler.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.OutputStream; @@ -30,30 +29,26 @@ import org.apache.harmony.logging.internal.nls.Messages; * is, objects of the class {@link java.io.OutputStream}. * <p> * A {@code StreamHandler} object reads the following properties from the log - * manager to initialize itself: + * manager to initialize itself. A default value will be used if a property is + * not found or has an invalid value. * <ul> - * <li>java.util.logging.StreamHandler.level specifies the logging level, - * defaults to {@code Level.INFO} if this property is not found or has an - * invalid value. + * <li>java.util.logging.StreamHandler.encoding specifies the encoding this + * handler will use to encode log messages. Default is the encoding used by the + * current platform. * <li>java.util.logging.StreamHandler.filter specifies the name of the filter - * class to be associated with this handler, defaults to {@code null} if this - * property is not found or has an invalid value. + * class to be associated with this handler. No <code>Filter</code> is used by + * default. * <li>java.util.logging.StreamHandler.formatter specifies the name of the - * formatter class to be associated with this handler, defaults to - * {@code java.util.logging.SimpleFormatter} if this property is not found or - * has an invalid value. - * <li>java.util.logging.StreamHandler.encoding specifies the encoding this - * handler will use to encode log messages, defaults to {@code null} if this - * property is not found or has an invalid value. + * formatter class to be associated with this handler. Default is + * {@code java.util.logging.SimpleFormatter}. + * <li>java.util.logging.StreamHandler.level specifies the logging level. + * Defaults is {@code Level.INFO}. * </ul> - * </p> * <p> * This class is not thread-safe. - * </p> - * - * @since Android 1.0 */ public class StreamHandler extends Handler { + // the output stream this handler writes to private OutputStream os; @@ -66,11 +61,9 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object. The new stream handler * does not have an associated output stream. - * - * @since Android 1.0 */ public StreamHandler() { - initProperties("INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$ + initProperties("INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$ null); this.os = null; this.writer = null; @@ -80,7 +73,7 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object with the supplied output * stream. Default properties are read. - * + * * @param os * the output stream this handler writes to. */ @@ -106,14 +99,13 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object with the supplied output stream * and formatter. - * + * * @param os * the output stream this handler writes to. * @param formatter * the formatter this handler uses to format the output. * @throws NullPointerException * if {@code os} or {@code formatter} is {@code null}. - * @since Android 1.0 */ public StreamHandler(OutputStream os, Formatter formatter) { this(); @@ -160,7 +152,7 @@ public class StreamHandler extends Handler { /** * Sets the output stream this handler writes to. Note it does nothing else. - * + * * @param newOs * the new output stream */ @@ -168,13 +160,12 @@ public class StreamHandler extends Handler { this.os = newOs; } - /** * Sets the output stream this handler writes to. If there's an existing * output stream, the tail string of the associated formatter will be - * written to it. Then it will be flushed, closed and replaced with + * written to it. Then it will be flushed, closed and replaced with * {@code os}. - * + * * @param os * the new output stream. * @throws SecurityException @@ -197,7 +188,7 @@ public class StreamHandler extends Handler { /** * Sets the character encoding used by this handler. A {@code null} value * indicates that the default encoding should be used. - * + * * @param encoding * the character encoding to set. * @throws SecurityException @@ -205,12 +196,11 @@ public class StreamHandler extends Handler { * have the required permission. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ @Override public void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException { - //flush first before set new encoding + // flush first before set new encoding this.flush(); super.setEncoding(encoding); // renew writer only if the writer exists @@ -234,7 +224,7 @@ public class StreamHandler extends Handler { /** * Closes this handler, but the underlying output stream is only closed if * {@code closeStream} is {@code true}. Security is not checked. - * + * * @param closeStream * whether to close the underlying output stream. */ @@ -264,11 +254,10 @@ public class StreamHandler extends Handler { * this handler is written out. A flush operation and a subsequent close * operation is then performed upon the output stream. Client applications * should not use a handler after closing it. - * + * * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ @Override public void close() { @@ -278,8 +267,6 @@ public class StreamHandler extends Handler { /** * Flushes any buffered output. - * - * @since Android 1.0 */ @Override public void flush() { @@ -291,7 +278,8 @@ public class StreamHandler extends Handler { this.os.flush(); } } catch (Exception e) { - // logging.16=Exception occurred while flushing the output stream. + // logging.16=Exception occurred while flushing the output + // stream. getErrorManager().error(Messages.getString("logging.16"), //$NON-NLS-1$ e, ErrorManager.FLUSH_FAILURE); } @@ -309,10 +297,9 @@ public class StreamHandler extends Handler { * </ul> * If it is the first time a log record is written out, the head string of * the formatter associated with this handler is written out first. - * + * * @param record * the log record to be logged. - * @since Android 1.0 */ @Override public synchronized void publish(LogRecord record) { @@ -325,7 +312,8 @@ public class StreamHandler extends Handler { try { msg = getFormatter().format(record); } catch (Exception e) { - // logging.17=Exception occurred while formatting the log record. + // logging.17=Exception occurred while formatting the log + // record. getErrorManager().error(Messages.getString("logging.17"), //$NON-NLS-1$ e, ErrorManager.FORMAT_FAILURE); } @@ -345,13 +333,11 @@ public class StreamHandler extends Handler { * {@code false}. * <p> * Notice : Case of no output stream will return {@code false}. - * </p> - * + * * @param record * the log record to be checked. * @return {@code true} if {@code record} needs to be logged, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean isLoggable(LogRecord record) { @@ -363,5 +349,4 @@ public class StreamHandler extends Handler { } return false; } - } diff --git a/logging/src/main/java/java/util/logging/XMLFormatter.java b/logging/src/main/java/java/util/logging/XMLFormatter.java index 6279d8c..ff96813 100644 --- a/logging/src/main/java/java/util/logging/XMLFormatter.java +++ b/logging/src/main/java/java/util/logging/XMLFormatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.security.AccessController; @@ -30,8 +29,6 @@ import java.util.ResourceBundle; * {@code XMLFormatter} uses the output handler's encoding if it is specified, * otherwise the default platform encoding is used instead. UTF-8 is the * recommended encoding. - * - * @since Android 1.0 */ public class XMLFormatter extends Formatter { @@ -42,8 +39,6 @@ public class XMLFormatter extends Formatter { /** * Constructs a new {@code XMLFormatter}. - * - * @since Android 1.0 */ public XMLFormatter() { super(); @@ -51,61 +46,63 @@ public class XMLFormatter extends Formatter { /** * Converts a {@code LogRecord} into an XML string. - * + * * @param r * the log record to be formatted. * @return the log record formatted as an XML string. - * @since Android 1.0 */ + @SuppressWarnings("nls") @Override public String format(LogRecord r) { - //call a method of LogRecord to ensure not null + // call a method of LogRecord to ensure not null long time = r.getMillis(); - //format to date - String date = MessageFormat.format("{0, date} {0, time}", //$NON-NLS-1$ + // format to date + String date = MessageFormat.format("{0, date} {0, time}", new Object[] { new Date(time) }); StringBuilder sb = new StringBuilder(); - sb.append(("<record>")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("<date>")).append(date).append(("</date>")) //$NON-NLS-1$ //$NON-NLS-2$ + sb.append(("<record>")).append(lineSeperator); + sb.append(indent).append(("<date>")).append(date).append(("</date>")) .append(lineSeperator); - sb.append(indent).append(("<millis>")).append(time).append( //$NON-NLS-1$ - ("</millis>")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("<sequence>")).append(r.getSequenceNumber()) //$NON-NLS-1$ - .append(("</sequence>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<millis>")).append(time).append( + ("</millis>")).append(lineSeperator); + sb.append(indent).append(("<sequence>")).append(r.getSequenceNumber()) + .append(("</sequence>")).append(lineSeperator); if (null != r.getLoggerName()) { - sb.append(indent).append(("<logger>")).append(r.getLoggerName()) //$NON-NLS-1$ - .append(("</logger>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<logger>")).append(r.getLoggerName()) + .append(("</logger>")).append(lineSeperator); } - sb.append(indent).append(("<level>")).append(r.getLevel().getName()) //$NON-NLS-1$ - .append(("</level>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<level>")).append(r.getLevel().getName()) + .append(("</level>")).append(lineSeperator); if (null != r.getSourceClassName()) { - sb.append(indent).append(("<class>")) //$NON-NLS-1$ - .append(r.getSourceClassName()).append(("</class>")) //$NON-NLS-1$ + sb.append(indent).append(("<class>")) + .append(r.getSourceClassName()).append(("</class>")) .append(lineSeperator); } if (null != r.getSourceMethodName()) { - sb.append(indent).append(("<method>")).append( //$NON-NLS-1$ - r.getSourceMethodName()).append(("</method>")).append( //$NON-NLS-1$ + sb.append(indent).append(("<method>")).append( + r.getSourceMethodName()).append(("</method>")).append( lineSeperator); } - sb.append(indent).append(("<thread>")).append(r.getThreadID()).append( //$NON-NLS-1$ - ("</thread>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<thread>")).append(r.getThreadID()).append( + ("</thread>")).append(lineSeperator); formatMessages(r, sb); Object[] params; if ((params = r.getParameters()) != null) { for (Object element : params) { - sb.append(indent).append(("<param>")).append(element).append( //$NON-NLS-1$ - ("</param>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<param>")).append(element).append( + ("</param>")).append(lineSeperator); } } formatThrowable(r, sb); - sb.append(("</record>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(("</record>")).append(lineSeperator); return sb.toString(); } + @SuppressWarnings("nls") private void formatMessages(LogRecord r, StringBuilder sb) { - //get localized message if has, but don't call Formatter.formatMessage to parse pattern string + // get localized message if has, but don't call Formatter.formatMessage + // to parse pattern string ResourceBundle rb = r.getResourceBundle(); String pattern = r.getMessage(); if (null != rb && null != pattern) { @@ -118,49 +115,50 @@ public class XMLFormatter extends Formatter { if (message == null) { message = pattern; - sb.append(indent).append(("<message>")).append(message).append( //$NON-NLS-1$ - ("</message>")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("<message>")).append(message).append( + ("</message>")).append(lineSeperator); } else { - sb.append(indent).append(("<message>")).append(message).append( //$NON-NLS-1$ - ("</message>")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("<key>")).append(pattern).append( //$NON-NLS-1$ - ("</key>")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("<catalog>")).append( //$NON-NLS-1$ - r.getResourceBundleName()).append(("</catalog>")) //$NON-NLS-1$ + sb.append(indent).append(("<message>")).append(message).append( + ("</message>")).append(lineSeperator); + sb.append(indent).append(("<key>")).append(pattern).append( + ("</key>")).append(lineSeperator); + sb.append(indent).append(("<catalog>")).append( + r.getResourceBundleName()).append(("</catalog>")) .append(lineSeperator); } - } else if(null != pattern){ - sb.append(indent).append(("<message>")).append(pattern).append( //$NON-NLS-1$ - ("</message>")).append(lineSeperator); //$NON-NLS-1$ - } else{ - sb.append(indent).append(("<message/>")); //$NON-NLS-1$ + } else if (null != pattern) { + sb.append(indent).append(("<message>")).append(pattern).append( + ("</message>")).append(lineSeperator); + } else { + sb.append(indent).append(("<message/>")); } } + @SuppressWarnings("nls") private void formatThrowable(LogRecord r, StringBuilder sb) { Throwable t; if ((t = r.getThrown()) != null) { - sb.append(indent).append("<exception>").append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(indent).append("<message>").append( //$NON-NLS-1$ - t.toString()).append("</message>").append(lineSeperator); //$NON-NLS-1$ - //format throwable's stack trace + sb.append(indent).append("<exception>").append(lineSeperator); + sb.append(indent).append(indent).append("<message>").append( + t.toString()).append("</message>").append(lineSeperator); + // format throwable's stack trace StackTraceElement[] elements = t.getStackTrace(); for (StackTraceElement e : elements) { - sb.append(indent).append(indent).append("<frame>").append( //$NON-NLS-1$ + sb.append(indent).append(indent).append("<frame>").append( lineSeperator); sb.append(indent).append(indent).append(indent).append( - "<class>").append(e.getClassName()).append("</class>") //$NON-NLS-1$//$NON-NLS-2$ + "<class>").append(e.getClassName()).append("</class>") .append(lineSeperator); sb.append(indent).append(indent).append(indent).append( - "<method>").append(e.getMethodName()).append( //$NON-NLS-1$ - "</method>").append(lineSeperator); //$NON-NLS-1$ + "<method>").append(e.getMethodName()).append( + "</method>").append(lineSeperator); sb.append(indent).append(indent).append(indent) - .append("<line>").append(e.getLineNumber()).append( //$NON-NLS-1$ - "</line>").append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(indent).append("</frame>").append( //$NON-NLS-1$ + .append("<line>").append(e.getLineNumber()).append( + "</line>").append(lineSeperator); + sb.append(indent).append(indent).append("</frame>").append( lineSeperator); } - sb.append(indent).append("</exception>").append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append("</exception>").append(lineSeperator); } } @@ -168,54 +166,48 @@ public class XMLFormatter extends Formatter { * Returns the header string for a set of log records formatted as XML * strings, using the output handler's encoding if it is defined, otherwise * using the default platform encoding. - * + * * @param h * the output handler, may be {@code null}. * @return the header string for log records formatted as XML strings. - * @since Android 1.0 */ + @SuppressWarnings("nls") @Override public String getHead(Handler h) { String encoding = null; - if(null != h) { + if (null != h) { encoding = h.getEncoding(); } if (null == encoding) { - encoding = getSystemProperty("file.encoding"); //$NON-NLS-1$ + encoding = getSystemProperty("file.encoding"); } StringBuilder sb = new StringBuilder(); - sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding).append( //$NON-NLS-1$ - "\" standalone=\"no\"?>").append(lineSeperator); //$NON-NLS-1$ - sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">").append(lineSeperator); //$NON-NLS-1$ - sb.append(("<log>")); //$NON-NLS-1$ + sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding).append( + "\" standalone=\"no\"?>").append(lineSeperator); + sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">").append(lineSeperator); + sb.append(("<log>")); return sb.toString(); } /** * Returns the tail string for a set of log records formatted as XML * strings. - * + * * @param h * the output handler, may be {@code null}. * @return the tail string for log records formatted as XML strings. - * @since Android 1.0 */ @Override - @SuppressWarnings("unused") public String getTail(Handler h) { return "</log>"; //$NON-NLS-1$ } - //use privilege code to get system property + // use privilege code to get system property private static String getSystemProperty(final String key) { - return AccessController.doPrivileged( - new PrivilegedAction<String>() { + return AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty(key); } }); } - } - - diff --git a/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java b/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java index 3a06078..f2bd62d 100644 --- a/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java +++ b/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java @@ -29,6 +29,8 @@ import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.LoggingPermission; +import java.io.File; +import java.io.FileInputStream; import junit.framework.TestCase; @@ -4628,6 +4630,26 @@ public class LoggerTest extends TestCase { } } + /* + * test initHandler + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "initHandler", + args = {} + ) + public void test_initHandler() throws Exception { + File logProps = new File(LOGGING_CONFIG_FILE); + LogManager lm = LogManager.getLogManager(); + lm.readConfiguration(new FileInputStream(logProps)); + + Logger log = Logger.getLogger(""); + // can log properly + Handler[] handlers = log.getHandlers(); + assertEquals(2, handlers.length); + } + /* * A mock logger, used to test the protected constructors and fields. diff --git a/logging/src/test/resources/config/java/util/logging/logging.config b/logging/src/test/resources/config/java/util/logging/logging.config index f4c5146..6e7394b 100644 --- a/logging/src/test/resources/config/java/util/logging/logging.config +++ b/logging/src/test/resources/config/java/util/logging/logging.config @@ -1,3 +1,3 @@ -handlers=org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler java.util.logging.ConsoleHandler +handlers=org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler , java.util.logging.ConsoleHandler .level=ALL org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler.level=OFF
\ No newline at end of file diff --git a/luni-kernel/src/main/java/java/lang/Thread.java b/luni-kernel/src/main/java/java/lang/Thread.java index 3cde7e1..89d7ed6 100644 --- a/luni-kernel/src/main/java/java/lang/Thread.java +++ b/luni-kernel/src/main/java/java/lang/Thread.java @@ -73,6 +73,8 @@ import org.apache.harmony.security.fortress.SecurityUtils; */ public class Thread implements Runnable { + private static final int NANOS_PER_MILLI = 1000000; + /** Park states */ private static class ParkState { /** park state indicating unparked */ @@ -973,7 +975,16 @@ public class Thread implements Runnable { * @since Android 1.0 */ public final void join() throws InterruptedException { - join(0, 0); + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + while (isAlive()) { + t.wait(); + } + } } /** @@ -1008,18 +1019,45 @@ public class Thread implements Runnable { * @since Android 1.0 */ public final void join(long millis, int nanos) throws InterruptedException { - if (millis < 0 || nanos < 0 || nanos > 999999) { + if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) { throw new IllegalArgumentException(); } - VMThread t; + // avoid overflow: if total > 292,277 years, just wait forever + boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI; + boolean forever = (millis | nanos) == 0; + if (forever | overflow) { + join(); + return; + } - t = this.vmThread; + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + if (!isAlive()) { + return; + } + + // guaranteed not to overflow + long nanosToWait = millis * NANOS_PER_MILLI + nanos; - if (t != null) { - synchronized (t) { - if (isAlive()) - t.wait(millis, nanos); + // wait until this thread completes or the timeout has elapsed + long start = System.nanoTime(); + while (true) { + t.wait(millis, nanos); + if (!isAlive()) { + break; + } + long nanosElapsed = System.nanoTime() - start; + long nanosRemaining = nanosToWait - nanosElapsed; + if (nanosRemaining <= 0) { + break; + } + millis = nanosRemaining / NANOS_PER_MILLI; + nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI); } } } @@ -1491,8 +1529,8 @@ public class Thread implements Runnable { break; } case ParkState.UNPARKED: { - long millis = nanos / 1000000; - nanos %= 1000000; + long millis = nanos / NANOS_PER_MILLI; + nanos %= NANOS_PER_MILLI; parkState = ParkState.PARKED; try { @@ -1554,7 +1592,7 @@ public class Thread implements Runnable { if (delayMillis <= 0) { parkState = ParkState.UNPARKED; } else { - parkFor(delayMillis * 1000000); + parkFor(delayMillis * NANOS_PER_MILLI); } } } diff --git a/luni-kernel/src/main/native/java_lang_ProcessManager.c b/luni-kernel/src/main/native/java_lang_ProcessManager.c index fcff1df..eaefc9f 100644 --- a/luni-kernel/src/main/native/java_lang_ProcessManager.c +++ b/luni-kernel/src/main/native/java_lang_ProcessManager.c @@ -91,7 +91,8 @@ static void java_lang_ProcessManager_watchChildren(JNIEnv* env, jobject o) { while (1) { int status; - pid_t pid = wait(&status); + /* wait for children in our process group */ + pid_t pid = waitpid(0, &status, 0); if (pid >= 0) { // Extract real status. diff --git a/luni/src/main/java/java/util/ComparableTimSort.java b/luni/src/main/java/java/util/ComparableTimSort.java index 882add1..cda4b12 100644 --- a/luni/src/main/java/java/util/ComparableTimSort.java +++ b/luni/src/main/java/java/util/ComparableTimSort.java @@ -150,7 +150,7 @@ class ComparableTimSort { // If array is small, do a "mini-TimSort" with no merges if (nRemaining < MIN_MERGE) { - int initRunLen = countRunAndMakeAscending(a, lo, nRemaining); + int initRunLen = countRunAndMakeAscending(a, lo, hi); binarySort(a, lo, hi, lo + initRunLen); return; } diff --git a/luni/src/main/java/java/util/TimSort.java b/luni/src/main/java/java/util/TimSort.java index 9c27ddc..4a9c05b 100644 --- a/luni/src/main/java/java/util/TimSort.java +++ b/luni/src/main/java/java/util/TimSort.java @@ -182,7 +182,7 @@ class TimSort<T> { // If array is small, do a "mini-TimSort" with no merges if (nRemaining < MIN_MERGE) { - int initRunLen = countRunAndMakeAscending(a, lo, nRemaining, c); + int initRunLen = countRunAndMakeAscending(a, lo, hi, c); binarySort(a, lo, hi, lo + initRunLen, c); return; } diff --git a/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java b/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java new file mode 100644 index 0000000..d7c2ab8 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * The class contains static {@link java.io.InputStream} utilities. + */ +public class InputStreamExposer { + + /** + * Provides access to a protected underlying buffer of + * <code>ByteArrayInputStream</code>. + */ + private static final Field BAIS_BUF; + + /** + * Provides access to a protected position in the underlying buffer of + * <code>ByteArrayInputStream</code>. + */ + private static final Field BAIS_POS; + + static { + final Field[] f = new Field[2]; + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + try { + f[0] = ByteArrayInputStream.class.getDeclaredField("buf"); + f[0].setAccessible(true); + f[1] = ByteArrayInputStream.class.getDeclaredField("pos"); + f[1].setAccessible(true); + } catch (NoSuchFieldException nsfe) { + throw new InternalError(nsfe.getLocalizedMessage()); + } + return null; + } + }); + BAIS_BUF = f[0]; + BAIS_POS = f[1]; + } + + /** + * Reads all bytes from {@link java.io.ByteArrayInputStream} using its + * underlying buffer directly. + * + * @return an underlying buffer, if a current position is at the buffer + * beginning, and an end position is at the buffer end, or a copy of + * the underlying buffer part. + */ + private static byte[] expose(ByteArrayInputStream bais) { + byte[] buffer, buf; + int pos; + synchronized (bais) { + int available = bais.available(); + try { + buf = (byte[]) BAIS_BUF.get(bais); + pos = BAIS_POS.getInt(bais); + } catch (IllegalAccessException iae) { + throw new InternalError(iae.getLocalizedMessage()); + } + if (pos == 0 && available == buf.length) { + buffer = buf; + } else { + buffer = new byte[available]; + System.arraycopy(buf, pos, buffer, 0, available); + } + bais.skip(available); + } + return buffer; + } + + /** + * The utility method for reading the whole input stream into a snapshot + * buffer. To speed up the access it works with an underlying buffer for a + * given {@link java.io.ByteArrayInputStream}. + * + * @param is + * the stream to be read. + * @return the snapshot wrapping the buffer where the bytes are read to. + * @throws UnsupportedOperationException if the input stream data cannot be exposed + */ + public static byte[] expose(InputStream is) throws IOException, UnsupportedOperationException { + // BEGIN android-changed + // if (is instanceof ExposedByteArrayInputStream) { + // return ((ExposedByteArrayInputStream) is).expose(); + // } + + if (is.getClass().equals(ByteArrayInputStream.class)) { + return expose((ByteArrayInputStream) is); + } + + // We don't know how to do this + throw new UnsupportedOperationException(); + } +} diff --git a/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java b/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java new file mode 100644 index 0000000..cdfe0c8 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + +import java.lang.ref.SoftReference; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +/** + * The class extends the functionality of {@link java.lang.ThreadLocal} with + * possibility of discarding the thread local storage content when a heap is + * exhausted. + */ +public class ThreadLocalCache<T> { + + private SoftReference<ThreadLocal<T>> storage = new SoftReference<ThreadLocal<T>>( + null); + + private ThreadLocal<T> getThreadLocal() { + ThreadLocal<T> tls = storage.get(); + if (tls == null) { + tls = new ThreadLocal<T>() { + public T initialValue() { + return ThreadLocalCache.this.initialValue(); + } + }; + storage = new SoftReference<ThreadLocal<T>>(tls); + } + return tls; + } + + /** + * Returns the initial value for the cache for the current thread. + */ + protected T initialValue() { + return null; + } + + /** + * Returns the thread local value of this object. + */ + public T get() { + return getThreadLocal().get(); + } + + /** + * Sets the value of this variable for the current thread. Might be useful + * for expanding the thread local cache. + */ + public void set(T value) { + getThreadLocal().set(value); + } + + /** + * Discards the cache for all threads. + */ + public void remove() { + storage.clear(); + } + + public static ThreadLocalCache<CharsetDecoder> utf8Decoder = new ThreadLocalCache<CharsetDecoder>() { + protected CharsetDecoder initialValue() { + return Charset.forName("UTF-8").newDecoder(); + } + }; + + public static ThreadLocalCache<CharsetEncoder> utf8Encoder = new ThreadLocalCache<CharsetEncoder>() { + protected CharsetEncoder initialValue() { + return Charset.forName("UTF-8").newEncoder(); + } + }; + + public static ThreadLocalCache<java.nio.ByteBuffer> byteBuffer = new ThreadLocalCache<java.nio.ByteBuffer>() { + protected java.nio.ByteBuffer initialValue() { + return java.nio.ByteBuffer.allocate(72); // >= + // Manifest.LINE_LENGTH_LIMIT + } + }; + + public static ThreadLocalCache<CharBuffer> charBuffer = new ThreadLocalCache<CharBuffer>() { + protected CharBuffer initialValue() { + return CharBuffer.allocate(72); // no specific requirement + } + }; + +} 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 5bd7907..3f7b0b1 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 @@ -14,14 +14,26 @@ * limitations under the License. */ +#define LOG_TAG "OSMemory" #include "JNIHelp.h" #include "AndroidSystemNatives.h" #include "utils/misc.h" +#include "utils/Log.h" #include <sys/mman.h> #include <stdlib.h> #include <string.h> #include <errno.h> +/* + * Cached dalvik.system.VMRuntime pieces. + */ +static struct { + jmethodID method_trackExternalAllocation; + jmethodID method_trackExternalFree; + + jobject runtimeInstance; +} gIDCache; + #undef MMAP_READ_ONLY #define MMAP_READ_ONLY 1L #undef MMAP_READ_WRITE @@ -55,11 +67,28 @@ static jint harmony_nio_getPointerSizeImpl(JNIEnv *_env, jclass _this) { * Signature: (I)I */ static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) { - void *returnValue = malloc(size); - if(returnValue == NULL) { - jniThrowException(_env, "java.lang.OutOfMemoryError", ""); + jboolean allowed = _env->CallBooleanMethod(gIDCache.runtimeInstance, + gIDCache.method_trackExternalAllocation, (jlong) size); + if (!allowed) { + LOGW("External allocation of %d bytes was rejected\n", size); + jniThrowException(_env, "java/lang/OutOfMemoryError", NULL); + return 0; } - return (jint)returnValue; + + LOGV("OSMemory alloc %d\n", size); + void *returnValue = malloc(size + sizeof(jlong)); + if (returnValue == NULL) { + jniThrowException(_env, "java/lang/OutOfMemoryError", NULL); + return 0; + } + + /* + * Tuck a copy of the size at the head of the buffer. We need this + * so harmony_nio_freeImpl() knows how much memory is being freed. + */ + jlong* adjptr = (jlong*) returnValue; + *adjptr++ = size; + return (jint)adjptr; } /* @@ -68,7 +97,12 @@ static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) { * Signature: (I)V */ static void harmony_nio_freeImpl(JNIEnv *_env, jobject _this, jint pointer) { - free((void *)pointer); + jlong* adjptr = (jlong*) pointer; + jint size = *--adjptr; + LOGV("OSMemory free %d\n", size); + _env->CallVoidMethod(gIDCache.runtimeInstance, + gIDCache.method_trackExternalFree, (jlong) size); + free((void *)adjptr); } /* @@ -577,6 +611,46 @@ static JNINativeMethod gMethods[] = { { "flushImpl", "(IJ)I", (void*) harmony_nio_flushImpl } }; int register_org_apache_harmony_luni_platform_OSMemory(JNIEnv *_env) { - return jniRegisterNativeMethods(_env, "org/apache/harmony/luni/platform/OSMemory", + /* + * We need to call VMRuntime.trackExternal{Allocation,Free}. Cache + * method IDs and a reference to the singleton. + */ + static const char* kVMRuntimeName = "dalvik/system/VMRuntime"; + jmethodID method_getRuntime; + jclass clazz; + + clazz = _env->FindClass(kVMRuntimeName); + if (clazz == NULL) { + LOGE("Unable to find class %s\n", kVMRuntimeName); + return -1; + } + gIDCache.method_trackExternalAllocation = _env->GetMethodID(clazz, + "trackExternalAllocation", "(J)Z"); + gIDCache.method_trackExternalFree = _env->GetMethodID(clazz, + "trackExternalFree", "(J)V"); + method_getRuntime = _env->GetStaticMethodID(clazz, + "getRuntime", "()Ldalvik/system/VMRuntime;"); + + if (gIDCache.method_trackExternalAllocation == NULL || + gIDCache.method_trackExternalFree == NULL || + method_getRuntime == NULL) + { + LOGE("Unable to find VMRuntime methods\n"); + return -1; + } + + jobject instance = _env->CallStaticObjectMethod(clazz, method_getRuntime); + if (instance == NULL) { + LOGE("Unable to obtain VMRuntime instance\n"); + return -1; + } + gIDCache.runtimeInstance = _env->NewGlobalRef(instance); + + /* + * Register methods. + */ + return jniRegisterNativeMethods(_env, + "org/apache/harmony/luni/platform/OSMemory", gMethods, NELEM(gMethods)); } + 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 f79c019..e105f5c 100755..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 @@ -33,9 +33,13 @@ #include <cutils/properties.h> #include <cutils/adb_networking.h> -#include <utils/LogSocket.h> #include "AndroidSystemNatives.h" +// Temporary hack to build on systems that don't have up-to-date libc headers. +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif + /** * @name Socket Errors * Error codes for socket operations @@ -145,6 +149,9 @@ // wait for 500000 usec = 0.5 second #define SEND_RETRY_TIME 500000 +// Local constants for getOrSetSocketOption +#define SOCKOPT_GET 1 +#define SOCKOPT_SET 2 struct CachedFields { jfieldID fd_descriptor; @@ -238,30 +245,59 @@ static int javaAddressToStructIn( } /** - * Converts a native address structure to a 4-byte array. Throws a + * Converts a native address structure to a Java byte array. 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. + * + * @param address the sockaddr_storage structure to convert + * + * @exception SocketException the address family is unknown, or out of memory + * */ -static int structInToJavaAddress( - JNIEnv *env, struct in_addr *address, jbyteArray java_address) { - - if (java_address == NULL) { - return -1; +static jbyteArray socketAddressToAddressBytes(JNIEnv *env, + struct sockaddr_storage *address) { + + void *rawAddress; + size_t addressLength; + if (address->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) address; + rawAddress = &sin->sin_addr.s_addr; + addressLength = 4; + } else if (address->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; + rawAddress = &sin6->sin6_addr.s6_addr; + addressLength = 16; + } else { + throwSocketException(env, SOCKERR_BADAF); + return NULL; } - if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { - return -1; + jbyteArray byteArray = env->NewByteArray(addressLength); + if (byteArray == NULL) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return NULL; } + env->SetByteArrayRegion(byteArray, 0, addressLength, (jbyte *) rawAddress); - jbyte *java_address_bytes; - - java_address_bytes = env->GetByteArrayElements(java_address, NULL); - - memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); - - env->ReleaseByteArrayElements(java_address, java_address_bytes, 0); + return byteArray; +} - return 0; +/** + * Returns the port number in a sockaddr_storage structure. + * + * @param address the sockaddr_storage structure to get the port from + * + * @return the port number, or -1 if the address family is unknown. + */ +static int getSocketAddressPort(struct sockaddr_storage *address) { + switch (address->ss_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *) address)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *) address)->sin6_port); + default: + return -1; + } } /** @@ -269,68 +305,79 @@ static int structInToJavaAddress( * 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. + * + * @param sockaddress the sockaddr_storage structure to convert + * + * @return a jobject representing an InetAddress */ -static int socketAddressToInetAddress(JNIEnv *env, - struct sockaddr_in *sockaddress, jobject inetaddress, int *port) { +static jobject socketAddressToInetAddress(JNIEnv *env, + struct sockaddr_storage *sockaddress) { - jbyteArray ipaddress; - int result; - - ipaddress = (jbyteArray)env->GetObjectField(inetaddress, - gCachedFields.iaddr_ipaddress); - - if (structInToJavaAddress(env, &sockaddress->sin_addr, ipaddress) < 0) { - return -1; - } - - *port = ntohs(sockaddress->sin_port); + jbyteArray byteArray = socketAddressToAddressBytes(env, sockaddress); + if (byteArray == NULL) // Exception has already been thrown. + return NULL; - return 0; + return env->CallStaticObjectMethod(gCachedFields.iaddr_class, + gCachedFields.iaddr_getbyaddress, byteArray); } /** - * Converts an InetAddress object to a native address structure. - * Throws a NullPointerException or an IOException in case of + * Converts an InetAddress object and port number to a native address structure. + * Throws a NullPointerException or a SocketException in case of * error. This is signaled by a return value of -1. The normal * return value is 0. + * + * @param inetaddress the InetAddress object to convert + * @param port the port number + * @param sockaddress the sockaddr_storage structure to write to + * + * @return 0 on success, -1 on failure + * + * @exception SocketError if the address family is unknown */ static int inetAddressToSocketAddress(JNIEnv *env, - jobject inetaddress, int port, struct sockaddr_in *sockaddress) { - - jbyteArray ipaddress; - int result; + jobject inetaddress, int port, struct sockaddr_storage *sockaddress) { - ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + // Get the byte array that stores the IP address bytes in the InetAddress. + jbyteArray addressByteArray; + addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, gCachedFields.iaddr_ipaddress); - - memset(sockaddress, 0, sizeof(sockaddress)); - - sockaddress->sin_family = AF_INET; - sockaddress->sin_port = htons(port); - - if (javaAddressToStructIn(env, ipaddress, &(sockaddress->sin_addr)) < 0) { - return -1; + if (addressByteArray == NULL) { + throwNullPointerException(env); + return -1; } - return 0; -} - -static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { - jbyteArray bytes; - int success; - - bytes = env->NewByteArray(4); - - if (bytes == NULL) { - return NULL; + // Get the raw IP address bytes. + jbyte *addressBytes = env->GetByteArrayElements(addressByteArray, NULL); + if (addressBytes == NULL) { + throwNullPointerException(env); + return -1; } - if (structInToJavaAddress(env, address, bytes) < 0) { - return NULL; + // Convert the IP address bytes to the proper IP address type. + size_t addressLength = env->GetArrayLength(addressByteArray); + int result = 0; + if (addressLength == 4) { + // IPv4 address. + struct sockaddr_in *sin = (struct sockaddr_in *) sockaddress; + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + memcpy(&sin->sin_addr.s_addr, addressBytes, 4); + } else if (addressLength == 16) { + // IPv6 address. + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddress; + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + memcpy(&sin6->sin6_addr.s6_addr, addressBytes, 16); + } else { + // Unknown address family. + throwSocketException(env, SOCKERR_BADAF); + result = -1; } - - return env->CallStaticObjectMethod(gCachedFields.iaddr_class, - gCachedFields.iaddr_getbyaddress, bytes); + env->ReleaseByteArrayElements(addressByteArray, addressBytes, 0); + return result; } /** @@ -427,15 +474,77 @@ static int time_msec_clock() { } /** - * check if the passed sockaddr_in struct contains a localhost address + * Check if the passed sockaddr_storage struct contains a localhost address * - * @param[in] address pointer to the address to check + * @param address address pointer to the address to check * * @return 0 if the passed address isn't a localhost address */ -static int isLocalhost(struct sockaddr_in *address) { - // return address == 127.0.0.1 - return (unsigned int) address->sin_addr.s_addr == 16777343; +static int isLocalHost(struct sockaddr_storage *address) { + if (address->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) address; + return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)); + } else if (address->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; + return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr); + } else { + return 0; + } +} + +/** + * Decide whether to use ADB networking for the given socket address. + * + * @param address pointer to sockaddr_storage structure to check + * + * @return true if ADB networking should be used, false otherwise. + */ +static bool useAdbNetworkingForAddress(struct sockaddr_storage *address) { + return useAdbNetworking && !isLocalHost(address) && + address->ss_family == AF_INET; +} + +/** + * Convert a sockaddr_storage structure to a string for logging purposes. + * + * @param address pointer to sockaddr_storage structure to print + * + * @return a string with the textual representation of the address. + * + * @note Returns a statically allocated buffer, so is not thread-safe. + */ +static char *socketAddressToString(struct sockaddr_storage *address) { + static char invalidString[] = "<invalid>"; + static char ipString[INET6_ADDRSTRLEN + sizeof("[]:65535")]; + + char tmp[INET6_ADDRSTRLEN]; + int port; + // TODO: getnameinfo seems to want its length parameter to be exactly + // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an + // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and + // then remove this hack. + int size = (address->ss_family == AF_INET) ? + sizeof(sockaddr_in) : sizeof(sockaddr_in6); + int result = getnameinfo((struct sockaddr *)address, + size, tmp, sizeof(tmp), NULL, 0, + NI_NUMERICHOST); + + if (result != 0) + return invalidString; + + if (address->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; + port = ntohs(sin6->sin6_port); + sprintf(ipString, "[%s]:%d", tmp, port); + return ipString; + } else if (address->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) address; + port = ntohs(sin->sin_port); + sprintf(ipString, "%s:%d", tmp, port); + return ipString; + } else { + return invalidString; + } } /** @@ -728,7 +837,6 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int } } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); } poll = 0; @@ -745,7 +853,6 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int continue; // try again } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); } poll = 0; @@ -756,6 +863,24 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int } /** + * Obtain the socket address family from an existing socket. + * + * @param socket the filedescriptor of the socket to examine + * + * @return an integer, the address family of the socket + */ +static int getSocketAddressFamily(int socket) { + struct sockaddr_storage ss; + socklen_t namelen = sizeof(ss); + int ret = getsockname(socket, (struct sockaddr*) &ss, &namelen); + if (ret != 0) { + return AF_UNSPEC; + } else { + return ss.ss_family; + } +} + +/** * A helper method, to set the connect context to a Long object. * * @param env pointer to the JNI library @@ -803,6 +928,77 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { } /** + * Converts an IPv4 address to an IPv4-mapped IPv6 address. Performs no error + * checking. + * + * @param address the address to convert. Must contain an IPv4 address. + * @param outputAddress the converted address. Will contain an IPv6 address. + * @param mapUnspecified if true, convert 0.0.0.0 to ::ffff:0:0; if false, to :: + */ +static void ipv4ToMappedAddress(struct sockaddr_storage *address, + struct sockaddr_storage *outputAddress, bool mapUnspecified) { + memset(outputAddress, 0, sizeof(struct sockaddr_storage)); + const struct sockaddr_in *sin = ((struct sockaddr_in *) address); + struct sockaddr_in6 *sin6 = ((struct sockaddr_in6 *) outputAddress); + sin6->sin6_family = AF_INET6; + sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + if (sin->sin_addr.s_addr != 0 || mapUnspecified) { + sin6->sin6_addr.s6_addr32[2] = htonl(0xffff); + } + sin6->sin6_port = sin->sin_port; +} + +/** + * Wrapper for connect() that converts IPv4 addresses to IPv4-mapped IPv6 + * addresses if necessary. + * + * @param socket the filedescriptor of the socket to connect + * @param socketAddress the address to connect to + */ +static int doConnect(int socket, struct sockaddr_storage *socketAddress) { + struct sockaddr_storage mappedAddress; + struct sockaddr_storage *realAddress; + if (socketAddress->ss_family == AF_INET && + getSocketAddressFamily(socket) == AF_INET6) { + ipv4ToMappedAddress(socketAddress, &mappedAddress, true); + realAddress = &mappedAddress; + } else { + realAddress = socketAddress; + } + int ret; + do { + ret = connect(socket, (struct sockaddr *) realAddress, + sizeof(struct sockaddr_storage)); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/** + * Wrapper for bind() that converts IPv4 addresses to IPv4-mapped IPv6 + * addresses if necessary. + * + * @param socket the filedescriptor of the socket to connect + * @param socketAddress the address to connect to + */ +static int doBind(int socket, struct sockaddr_storage *socketAddress) { + struct sockaddr_storage mappedAddress; + struct sockaddr_storage *realAddress; + if (socketAddress->ss_family == AF_INET && + getSocketAddressFamily(socket) == AF_INET6) { + ipv4ToMappedAddress(socketAddress, &mappedAddress, false); + realAddress = &mappedAddress; + } else { + realAddress = socketAddress; + } + int ret; + do { + ret = bind(socket, (struct sockaddr *) realAddress, + sizeof(struct sockaddr_storage)); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/** * Establish a connection to a peer with a timeout. This function is called * repeatedly in order to carry out the connect and to allow other tasks to * proceed on certain platforms. The caller must first call with @@ -823,7 +1019,7 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { * * @return 0, if no errors occurred, otherwise the (negative) error code. */ -static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, +static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, unsigned int timeout, unsigned int step, jbyte *ctxt) { int rc = 0; struct timeval passedTimeout; @@ -838,16 +1034,14 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, context->sock = handle; context->nfds = handle + 1; - if (useAdbNetworking && !isLocalhost(&addr)) { + if (useAdbNetworkingForAddress(&addr)) { // LOGD("+connect to address 0x%08x (via adb)", // addr.sin_addr.s_addr); - rc = adb_networking_connect_fd(handle, &addr); + rc = adb_networking_connect_fd(handle, (struct sockaddr_in *) &addr); // LOGD("-connect ret %d errno %d (via adb)", rc, errno); } else { - log_socket_connect(handle, ntohl(addr.sin_addr.s_addr), - ntohs(addr.sin_port)); /* set the socket to non-blocking */ int block = JNI_TRUE; rc = ioctl(handle, FIONBIO, &block); @@ -857,10 +1051,7 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, // LOGD("+connect to address 0x%08x (via normal) on handle %d", // addr.sin_addr.s_addr, handle); - do { - rc = connect(handle, (struct sockaddr *) &addr, - sizeof(struct sockaddr)); - } while (rc < 0 && errno == EINTR); + rc = doConnect(handle, &addr); // LOGD("-connect to address 0x%08x (via normal) returned %d", // addr.sin_addr.s_addr, (int) rc); @@ -971,6 +1162,89 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, return SOCKERR_ARGSINVALID; } + +/** + * Helper method to get or set socket options + * + * @param action SOCKOPT_GET to get an option, SOCKOPT_SET to set it + * @param socket the file descriptor of the socket to use + * @param ipv4Option the option value to use for an IPv4 socket + * @param ipv6Option the option value to use for an IPv6 socket + * @param optionValue the value of the socket option to get or set + * @param optionLength the length of the socket option to get or set + * + * @return the value of the socket call, or -1 on failure inside this function + * + * @note on internal failure, the errno variable will be set appropriately + */ +static int getOrSetSocketOption(int action, int socket, int ipv4Option, + int ipv6Option, void *optionValue, socklen_t *optionLength) { + int option; + int protocol; + int family = getSocketAddressFamily(socket); + switch (family) { + case AF_INET: + option = ipv4Option; + protocol = IPPROTO_IP; + break; + case AF_INET6: + option = ipv6Option; + protocol = IPPROTO_IPV6; + break; + default: + errno = EAFNOSUPPORT; + return -1; + } + if (action == SOCKOPT_GET) { + return getsockopt(socket, protocol, option, &optionValue, optionLength); + } else if (action == SOCKOPT_SET) { + return setsockopt(socket, protocol, option, &optionValue, + *optionLength); + } else { + errno = EINVAL; + return -1; + } +} + +/* + * Find the interface index that was set for this socket by the IP_MULTICAST_IF + * or IPV6_MULTICAST_IF socket option. + * + * @param socket the socket to examine + * + * @return the interface index, or -1 on failure + * + * @note on internal failure, the errno variable will be set appropriately + */ +static int interfaceIndexFromMulticastSocket(int socket) { + int family = getSocketAddressFamily(socket); + socklen_t requestLength; + int interfaceIndex; + int result; + if (family == AF_INET) { + // IP_MULTICAST_IF returns a pointer to a struct ip_mreqn. + struct ip_mreqn tempRequest; + requestLength = sizeof(tempRequest); + result = getsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &tempRequest, + &requestLength); + interfaceIndex = tempRequest.imr_ifindex; + } else if (family == AF_INET6) { + // IPV6_MULTICAST_IF returns a pointer to an integer. + requestLength = sizeof(interfaceIndex); + result = getsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &interfaceIndex, &requestLength); + } else { + errno = EAFNOSUPPORT; + return -1; + } + + if (result == 0) + return interfaceIndex; + else + return -1; +} + + /** * Join/Leave the nominated multicast group on the specified socket. * Implemented by setting the multicast 'add membership'/'drop membership' @@ -994,103 +1268,112 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, */ static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, int ignoreIF, int setSockOptVal) { + struct sockaddr_storage sockaddrP; int result; - struct ip_mreq ipmreqP; - struct sockaddr_in sockaddrP; - int length = sizeof(struct ip_mreq); - socklen_t lengthIF = sizeof(struct sockaddr_in); - - /* - * JNI objects needed to access the information in the optVal oject - * passed in. The object passed in is a GenericIPMreq object - */ - jclass cls; - jfieldID multiaddrID; - jfieldID interfaceAddrID; - jobject multiaddr; - jobject interfaceAddr; + // By default, let the system decide which interface to use. + int interfaceIndex = 0; /* - * check whether we are getting an InetAddress or an Generic IPMreq, for now - * we support both so that we will not break the tests + * Check whether we are getting an InetAddress or an Generic IPMreq. For now + * we support both so that we will not break the tests. If an InetAddress + * is passed in, only support IPv4 as obtaining an interface from an + * InetAddress is complex and should be done by the Java caller. */ if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) { + /* + * optVal is an InetAddress. Construct a multicast request structure + * from this address. Support IPv4 only. + */ + struct ip_mreqn multicastRequest; + socklen_t length = sizeof(multicastRequest); + memset(&multicastRequest, 0, length); - ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + // If ignoreIF is false, determine the index of the interface to use. if (!ignoreIF) { - - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockaddrP, - &lengthIF); - - if (0 != result) { - throwSocketException (env, convertError(errno)); + interfaceIndex = interfaceIndexFromMulticastSocket(handle); + multicastRequest.imr_ifindex = interfaceIndex; + if (interfaceIndex == -1) { + throwSocketException(env, convertError(errno)); return; } - - memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); } + // Convert the inetAddress to an IPv4 address structure. result = inetAddressToSocketAddress(env, optVal, 0, &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + if (result < 0) // Exception has already been thrown. + return; + if (sockaddrP.ss_family != AF_INET) { + throwSocketException(env, SOCKERR_BADAF); return; } + struct sockaddr_in *sin = (struct sockaddr_in *) &sockaddrP; + multicastRequest.imr_multiaddr = sin->sin_addr; - memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); - - result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, + &multicastRequest, length); if (0 != result) { throwSocketException (env, convertError(errno)); return; } - } else { + /* + * optVal is a GenericIPMreq object. Extract the relevant fields from + * it and construct a multicast request structure from these. Support + * both IPv4 and IPv6. + */ + jclass cls; + jfieldID multiaddrID; + jfieldID interfaceIdxID; + jobject multiaddr; - /* we need the multicast address regardless of the type of address */ + // Get the multicast address to join or leave. cls = env->GetObjectClass(optVal); multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;"); multiaddr = env->GetObjectField(optVal, multiaddrID); - result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; + // Get the interface index to use. + if (! ignoreIF) { + interfaceIdxID = env->GetFieldID(cls, "interfaceIdx", "I"); + interfaceIndex = env->GetIntField(optVal, interfaceIdxID); } - memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); - - /* we need to use an IP_MREQ as it is an IPV4 address */ - interfaceAddrID = env->GetFieldID(cls, "interfaceAddr", - "Ljava/net/InetAddress;"); - interfaceAddr = env->GetObjectField(optVal, interfaceAddrID); - - ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); - - /* - * if an interfaceAddr was passed then use that value, otherwise set the - * interface to all 0 to indicate the system should select the interface - * used - */ - if (!ignoreIF) { - if (NULL != interfaceAddr) { - - result = inetAddressToSocketAddress(env, interfaceAddr, 0, - &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; - } - - memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); + if (result < 0) // Exception has already been thrown. + return; - } + struct ip_mreqn ipv4Request; + struct ipv6_mreq ipv6Request; + void *multicastRequest; + socklen_t requestLength; + int level; + int family = getSocketAddressFamily(handle); + switch (family) { + case AF_INET: + requestLength = sizeof(ipv4Request); + memset(&ipv4Request, 0, requestLength); + ipv4Request.imr_multiaddr = + ((struct sockaddr_in *) &sockaddrP)->sin_addr; + ipv4Request.imr_ifindex = interfaceIndex; + multicastRequest = &ipv4Request; + level = IPPROTO_IP; + break; + case AF_INET6: + requestLength = sizeof(ipv6Request); + memset(&ipv6Request, 0, requestLength); + ipv6Request.ipv6mr_multiaddr = + ((struct sockaddr_in6 *) &sockaddrP)->sin6_addr; + ipv6Request.ipv6mr_interface = interfaceIndex; + multicastRequest = &ipv6Request; + level = IPPROTO_IPV6; + break; + default: + throwSocketException (env, SOCKERR_BADAF); + return; } /* join/drop the multicast address */ - result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + result = setsockopt(handle, level, setSockOptVal, multicastRequest, + requestLength); if (0 != result) { throwSocketException (env, convertError(errno)); return; @@ -1339,38 +1622,48 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, } -static void osNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, - jobject fileDescriptor, jboolean preferIPv4Stack) { - // LOGD("ENTER createSocketImpl"); - - int ret = socket(PF_INET, SOCK_STREAM, 0); +/** + * Helper function to create a socket of the specified type and bind it to a + * Java file descriptor. + * + * @param fileDescriptor the file descriptor to bind the socket to + * @param type the socket type to create, e.g., SOCK_STREAM + * + * @return the socket file descriptor, or -1 on failure + * + */ +static int createSocketFileDescriptor(JNIEnv* env, jobject fileDescriptor, + int type) { + if (fileDescriptor == NULL) { + throwNullPointerException(env); + errno = EBADF; + return -1; + } - if (ret < 0) { + int sock; + sock = socket(PF_INET6, type, 0); + if (sock < 0 && errno == EAFNOSUPPORT) { + sock = socket(PF_INET, type, 0); + } + if (sock < 0) { int err = convertError(errno); throwSocketException(env, err); - return; } + jniSetFileDescriptorOfFD(env, fileDescriptor, sock); + return sock; +} - jniSetFileDescriptorOfFD(env, fileDescriptor, ret); - return; +static void osNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createSocketImpl"); + createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM); } static void osNetworkSystem_createDatagramSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createDatagramSocketImpl"); - - int ret = socket(PF_INET, SOCK_DGRAM, 0); - - if (ret < 0) { - int err = convertError(errno); - throwSocketException(env, err); - return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, ret); - - return; + createSocketFileDescriptor(env, fileDescriptor, SOCK_DGRAM); } static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, @@ -1379,7 +1672,7 @@ static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER readSocketDirectImpl"); int handle; - jbyte *message = (jbyte *)address; + jbyte *message = (jbyte *)address + offset; int result, ret, localCount; handle = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -1405,11 +1698,9 @@ static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, return -1; } else if (ret == -1) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - add_recv_stats(handle, ret); return ret; } @@ -1437,7 +1728,7 @@ static jint osNetworkSystem_readSocketImpl(JNIEnv* env, jclass clazz, } result = osNetworkSystem_readSocketDirectImpl(env, clazz, fileDescriptor, - (jint) message, offset, count, timeout); + (jint) message, 0, localCount, timeout); if (result > 0) { env->SetByteArrayRegion(data, offset, result, (jbyte *)message); @@ -1455,7 +1746,7 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER writeSocketDirectImpl"); int handle; - jbyte *message = (jbyte *)address; + jbyte *message = (jbyte *)address + offset; int result = 0, sent = 0; if (count <= 0) { @@ -1472,7 +1763,6 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); if (SOCKERR_WOULDBLOCK == err){ jclass socketExClass,errorCodeExClass; @@ -1499,7 +1789,7 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, if (!socketExConstructor) { return 0; } - socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); env->Throw((jthrowable)socketEx); @@ -1509,7 +1799,6 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, return 0; } - add_send_stats(handle, result); return result; } @@ -1539,13 +1828,13 @@ static jint osNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, env->GetByteArrayRegion(data, offset, count, message); result = osNetworkSystem_writeSocketDirectImpl(env, clazz, fileDescriptor, - (jint) message, offset, count); + (jint) message, 0, count); if (( jbyte *)message != internalBuffer) { - free(( jbyte *)message); + free(( jbyte *)message); } #undef INTERNAL_SEND_BUFFER_MAX - return result; + return result; } static void osNetworkSystem_setNonBlockingImpl(JNIEnv* env, jclass clazz, @@ -1581,36 +1870,25 @@ static jint osNetworkSystem_connectWithTimeoutSocketImpl(JNIEnv* env, int handle; int result = 0; - struct sockaddr_in address; + struct sockaddr_storage address; jbyte *context = NULL; - memset(&address, 0, sizeof(address)); - - address.sin_family = AF_INET; - - result = inetAddressToSocketAddress(env, inetAddr, port, - (struct sockaddr_in *) &address); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + result = inetAddressToSocketAddress(env, inetAddr, port, &address); + if (result < 0) return result; - } // Check if we're using adb networking and redirect in case it is used. - if (useAdbNetworking && !isLocalhost(&address)) { + if (useAdbNetworkingForAddress(&address)) { return osNetworkSystem_connectSocketImpl(env, clazz, fileDescriptor, trafficClass, inetAddr, port); } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return -1; } - address.sin_port = htons(port); - context = (jbyte *)env->GetPrimitiveArrayCritical(passContext, NULL); switch (step) { @@ -1651,7 +1929,7 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, int result = 0; int handle; - struct sockaddr_in address; + struct sockaddr_storage address; jbyte *context = NULL; int remainingTimeout = timeout; int passedTimeout = 0; @@ -1664,53 +1942,117 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, finishTime = time_msec_clock() + (int) timeout; } - handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; - } else { - result = inetAddressToSocketAddress(env, inetAddr, remotePort, - (struct sockaddr_in *) &address); + } - if (result < 0) { + result = inetAddressToSocketAddress(env, inetAddr, remotePort, &address); + if (result < 0) // Exception has already been thrown. + return; + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworkingForAddress(&address)) { + int retVal = osNetworkSystem_connectSocketImpl(env, clazz, + fileDescriptor, trafficClass, inetAddr, remotePort); + if (retVal != 0) { throwSocketException(env, SOCKERR_BADSOCKET); - return; } + return; + } - // Check if we're using adb networking and redirect in case it is used. - if (useAdbNetworking && !isLocalhost(&address)) { - int retVal = osNetworkSystem_connectSocketImpl(env, clazz, - fileDescriptor, trafficClass, inetAddr, remotePort); - if (retVal != 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - } - return; + /* + * we will be looping checking for when we are connected so allocate + * the descriptor sets that we will use + */ + context =(jbyte *) malloc(sizeof(struct selectFDSet)); + if (NULL == context) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return; + } + + result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + if (0 == result) { + /* ok we connected right away so we are done */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + goto bail; + } else if (result != SOCKERR_NOTCONNECTED) { + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, + context); + /* we got an error other than NOTCONNECTED so we cannot continue */ + if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); } + goto bail; + } + + while (SOCKERR_NOTCONNECTED == result) { + passedTimeout = remainingTimeout; /* - * we will be looping checking for when we are connected so allocate - * the descriptor sets that we will use + * ok now try and connect. Depending on the platform this may sleep + * for up to passedTimeout milliseconds */ - context =(jbyte *) malloc(sizeof(struct selectFDSet)); + result = sockConnectWithTimeout(handle, address, passedTimeout, + SOCKET_STEP_CHECK, context); - if (NULL == context) { - throwSocketException(env, SOCKERR_NOBUFFERS); - return; + /* + * now check if the socket is still connected. + * Do it here as some platforms seem to think they + * are connected if the socket is closed on them. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + throwSocketException(env, SOCKERR_BADSOCKET); + goto bail; } - result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + /* + * check if we are now connected, + * if so we can finish the process and return + */ if (0 == result) { - /* ok we connected right away so we are done */ - sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); goto bail; - } else if (result != SOCKERR_NOTCONNECTED) { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, - context); - /* we got an error other than NOTCONNECTED so we cannot continue */ - if (SOCKERR_EACCES == result) { + } + + /* + * if the error is SOCKERR_NOTCONNECTED then we have not yet + * connected and we may not be done yet + */ + if (SOCKERR_NOTCONNECTED == result) { + /* check if the timeout has expired */ + if (hasTimeout) { + remainingTimeout = finishTime - time_msec_clock(); + if (remainingTimeout <= 0) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + jniThrowException(env, + "java/net/SocketTimeoutException", + netLookupErrorString(result)); + goto bail; + } + } else { + remainingTimeout = 100; + } + } else { + sockConnectWithTimeout(handle, address, remainingTimeout, + SOCKET_STEP_DONE, context); + if ((SOCKERR_CONNRESET == result) || + (SOCKERR_CONNECTION_REFUSED == result) || + (SOCKERR_ADDRNOTAVAIL == result) || + (SOCKERR_ADDRINUSE == result) || + (SOCKERR_ENETUNREACH == result)) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } else if (SOCKERR_EACCES == result) { jniThrowException(env, "java/lang/SecurityException", netLookupErrorString(result)); } else { @@ -1718,81 +2060,6 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, } goto bail; } - - while (SOCKERR_NOTCONNECTED == result) { - passedTimeout = remainingTimeout; - - /* - * ok now try and connect. Depending on the platform this may sleep - * for up to passedTimeout milliseconds - */ - result = sockConnectWithTimeout(handle, address, passedTimeout, - SOCKET_STEP_CHECK, context); - - /* - * now check if the socket is still connected. - * Do it here as some platforms seem to think they - * are connected if the socket is closed on them. - */ - handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - - if (handle == 0 || handle == -1) { - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - throwSocketException(env, SOCKERR_BADSOCKET); - goto bail; - } - - /* - * check if we are now connected, - * if so we can finish the process and return - */ - if (0 == result) { - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - goto bail; - } - - /* - * if the error is SOCKERR_NOTCONNECTED then we have not yet - * connected and we may not be done yet - */ - if (SOCKERR_NOTCONNECTED == result) { - /* check if the timeout has expired */ - if (hasTimeout) { - remainingTimeout = finishTime - time_msec_clock(); - if (remainingTimeout <= 0) { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - jniThrowException(env, - "java/net/SocketTimeoutException", - netLookupErrorString(result)); - goto bail; - } - } else { - remainingTimeout = 100; - } - } else { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, remainingTimeout, - SOCKET_STEP_DONE, context); - if ((SOCKERR_CONNRESET == result) || - (SOCKERR_CONNECTION_REFUSED == result) || - (SOCKERR_ADDRNOTAVAIL == result) || - (SOCKERR_ADDRINUSE == result) || - (SOCKERR_ENETUNREACH == result)) { - jniThrowException(env, "java/net/ConnectException", - netLookupErrorString(result)); - } else if (SOCKERR_EACCES == result) { - jniThrowException(env, "java/lang/SecurityException", - netLookupErrorString(result)); - } else { - throwSocketException(env, result); - } - goto bail; - } - } } bail: @@ -1807,37 +2074,25 @@ static jint osNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port) { //LOGD("ENTER direct-call connectSocketImpl\n"); - struct sockaddr_in address; + struct sockaddr_storage address; int ret; int handle; - jbyteArray java_in_addr; - - memset(&address, 0, sizeof(address)); - address.sin_family = AF_INET; - - ret = inetAddressToSocketAddress(env, inetAddr, port, - (struct sockaddr_in *) &address); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddr, port, &address); + if (ret < 0) return ret; - } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return -1; } - address.sin_port = htons(port); - - if (useAdbNetworking && !isLocalhost(&address)) { + if (useAdbNetworkingForAddress(&address)) { // LOGD("+connect to address 0x%08x port %d (via adb)", // address.sin_addr.s_addr, (int) port); - ret = adb_networking_connect_fd(handle, &address); + ret = adb_networking_connect_fd(handle, (struct sockaddr_in *) &address); // LOGD("-connect ret %d errno %d (via adb)", ret, errno); } else { @@ -1866,27 +2121,21 @@ static void osNetworkSystem_socketBindImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jint port, jobject inetAddress) { // LOGD("ENTER socketBindImpl"); - struct sockaddr_in sockaddress; + struct sockaddr_storage sockaddress; int ret; int handle; - ret = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddress); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockaddress); + if (ret < 0) return; - } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } - ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); - + ret = doBind(handle, &sockaddress); if (ret < 0) { jniThrowException(env, "java/net/BindException", netLookupErrorString(convertError(errno))); @@ -1912,7 +2161,6 @@ static void osNetworkSystem_listenStreamSocketImpl(JNIEnv* env, jclass clazz, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -1928,9 +2176,8 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, int result; handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } @@ -1943,7 +2190,6 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, } else if (SOCKERR_INTERRUPTED == result) { continue; } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); return 0; } @@ -1953,11 +2199,9 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, if (0 > result) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - add_recv_stats(handle, result); return result; } @@ -1965,10 +2209,7 @@ static void osNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, jobject fdServer, jobject newSocket, jobject fdnewSocket, jint timeout) { // LOGD("ENTER acceptSocketImpl"); - union { - struct sockaddr address; - struct sockaddr_in in_address; - } sa; + struct sockaddr_storage sa; int ret; int retFD; @@ -1982,55 +2223,47 @@ static void osNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, } result = pollSelectWait(env, fdServer, timeout, SELECT_READ_TYPE); - if (0 > result) { return; } handle = jniGetFDFromFileDescriptor(env, fdServer); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } do { addrlen = sizeof(sa); - ret = accept(handle, &(sa.address), &addrlen); + ret = accept(handle, (struct sockaddr *) &sa, &addrlen); } while (ret < 0 && errno == EINTR); if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } retFD = ret; - /* For AF_INET / inetOrLocal == true only: put - * peer address and port in instance variables - * We don't bother for UNIX domain sockets, since most peers are - * anonymous anyway + /* + * For network sockets, put the peer address and port in instance variables. + * We don't bother to do this for UNIX domain sockets, since most peers are + * anonymous anyway. */ - if (sa.address.sa_family == AF_INET) { - // inetOrLocal should also be true - - jobject inetAddress; - - inetAddress = structInToInetAddress(env, &(sa.in_address.sin_addr)); - - if (inetAddress == NULL) { + if (sa.ss_family == AF_INET || sa.ss_family == AF_INET6) { + jobject inetAddress = socketAddressToInetAddress(env, &sa); + if (ret == -1) { close(retFD); - newSocket = NULL; + newSocket = NULL; // Exception has already been thrown. return; } env->SetObjectField(newSocket, gCachedFields.socketimpl_address, inetAddress); - env->SetIntField(newSocket, gCachedFields.socketimpl_port, - ntohs(sa.in_address.sin_port)); + int port = getSocketAddressPort(&sa); + env->SetIntField(newSocket, gCachedFields.socketimpl_port, port); } jniSetFileDescriptorOfFD(env, fdnewSocket, retFD); @@ -2066,7 +2299,6 @@ static void osNetworkSystem_sendUrgentDataImpl(JNIEnv* env, jclass clazz, result = send(handle, (jbyte *) &value, 1, MSG_OOB); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } @@ -2077,20 +2309,16 @@ static void osNetworkSystem_connectDatagramImpl2(JNIEnv* env, jclass clazz, int handle = jniGetFDFromFileDescriptor(env, fd); - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; int ret; ret = inetAddressToSocketAddress(env, inetAddress, port, &sockAddr); + if (ret < 0) // Exception has already been thrown. + return; + ret = doConnect(handle, &sockAddr); if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; - } - log_socket_connect(handle, ntohl(sockAddr.sin_addr.s_addr), port); - int result = connect(handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); - if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } @@ -2101,18 +2329,13 @@ static void osNetworkSystem_disconnectDatagramImpl(JNIEnv* env, jclass clazz, int handle = jniGetFDFromFileDescriptor(env, fd); - struct sockaddr_in *sockAddr; - socklen_t sockAddrLen = sizeof(struct sockaddr_in); - sockAddr = (struct sockaddr_in *) malloc(sockAddrLen); - memset(sockAddr, 0, sockAddrLen); - - sockAddr->sin_family = AF_UNSPEC; - int result = connect(handle, (struct sockaddr *)sockAddr, sockAddrLen); - free(sockAddr); + struct sockaddr_storage sockAddr; + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.ss_family = AF_UNSPEC; + int result = doConnect(handle, &sockAddr); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } @@ -2122,29 +2345,23 @@ static jboolean osNetworkSystem_socketBindImpl2(JNIEnv* env, jclass clazz, jobject inetAddress) { // LOGD("ENTER socketBindImpl2"); - struct sockaddr_in sockaddress; + struct sockaddr_storage sockaddress; int ret; int handle; - ret = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddress); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockaddress); + if (ret < 0) // Exception has already been thrown. return 0; - } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } - ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); - + ret = doBind(handle, &sockaddress); if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); jniThrowException(env, "java/net/BindException", netLookupErrorString(err)); return 0; } @@ -2164,30 +2381,29 @@ static jint osNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, } int handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; socklen_t sockAddrLen = sizeof(sockAddr); - - int length = recvfrom(handle, NULL, 0, MSG_PEEK, - (struct sockaddr *)&sockAddr, &sockAddrLen); - + ssize_t length; + do { + length = recvfrom(handle, NULL, 0, MSG_PEEK, + (struct sockaddr *)&sockAddr, &sockAddrLen); + } while (length < 0 && errno == EINTR); if (length < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - if (socketAddressToInetAddress(env, &sockAddr, sender, &port) < 0) { - throwIOExceptionStr(env, "Address conversion failed"); + sender = socketAddressToInetAddress(env, &sockAddr); + if (sender == NULL) // Exception has already been thrown. return -1; - } - add_recv_stats(handle, length); + + port = getSocketAddressPort(&sockAddr); return port; } @@ -2202,44 +2418,41 @@ static jint osNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz, } int handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; socklen_t sockAddrLen = sizeof(sockAddr); int mode = peek ? MSG_PEEK : 0; - int actualLength = recvfrom(handle, (char*)(address + offset), length, mode, - (struct sockaddr *)&sockAddr, &sockAddrLen); - + ssize_t actualLength; + do { + actualLength = recvfrom(handle, (char*)(address + offset), length, mode, + (struct sockaddr *)&sockAddr, &sockAddrLen); + } while (actualLength < 0 && errno == EINTR); if (actualLength < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } if (packet != NULL) { - int port = ntohs(sockAddr.sin_port); - jbyteArray addr = env->NewByteArray(sizeof(struct in_addr)); - if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { - jniThrowException(env, "java/net/SocketException", - "Could not set address of packet."); + jbyteArray addr = socketAddressToAddressBytes(env, &sockAddr); + if (addr == NULL) // Exception has already been thrown. return 0; - } + int port = getSocketAddressPort(&sockAddr); jobject sender = env->CallStaticObjectMethod( gCachedFields.iaddr_class, gCachedFields.iaddr_getbyaddress, addr); env->SetObjectField(packet, gCachedFields.dpack_address, sender); env->SetIntField(packet, gCachedFields.dpack_port, port); - env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + env->SetIntField(packet, gCachedFields.dpack_length, + (jint) actualLength); } - add_recv_stats(handle, actualLength); - return actualLength; + return (jint) actualLength; } static jint osNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, @@ -2256,7 +2469,7 @@ static jint osNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, } int actualLength = osNetworkSystem_receiveDatagramDirectImpl(env, clazz, fd, - packet, (jint)bytes, offset, localLength, receiveTimeout, peek); + packet, (jint)bytes, 0, localLength, receiveTimeout, peek); if (actualLength > 0) { env->SetByteArrayRegion(data, offset, actualLength, bytes); @@ -2297,7 +2510,6 @@ static jint osNetworkSystem_recvConnectedDatagramDirectImpl(JNIEnv* env, if ( packet != NULL) { env->SetIntField(packet, gCachedFields.dpack_length, actualLength); } - add_recv_stats(handle, actualLength); return actualLength; } @@ -2315,7 +2527,7 @@ static jint osNetworkSystem_recvConnectedDatagramImpl(JNIEnv* env, jclass clazz, } int actualLength = osNetworkSystem_recvConnectedDatagramDirectImpl(env, - clazz, fd, packet, (jint)bytes, offset, localLength, + clazz, fd, packet, (jint)bytes, 0, localLength, receiveTimeout, peek); if (actualLength > 0) { @@ -2331,8 +2543,6 @@ static jint osNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, jboolean bindToDevice, jint trafficClass, jobject inetAddress) { // LOGD("ENTER sendDatagramDirectImpl"); - int result = 0; - int handle = jniGetFDFromFileDescriptor(env, fd); if (handle == 0 || handle == -1) { @@ -2340,29 +2550,28 @@ static jint osNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, return 0; } - struct sockaddr_in receiver; - + struct sockaddr_storage receiver; if (inetAddressToSocketAddress(env, inetAddress, port, &receiver) < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + // Exception has already been thrown. return 0; } - result = sendto(handle, (char*)(address + offset), length, SOCKET_NOFLAGS, - (struct sockaddr*)&receiver, sizeof(receiver)); - + ssize_t result = 0; + do { + result = sendto(handle, (char*)(address + offset), length, + SOCKET_NOFLAGS, (struct sockaddr*)&receiver, sizeof(receiver)); + } while (result < 0 && errno == EINTR); if (result < 0) { int err = convertError(errno); if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { return 0; } else { - log_socket_close(handle, err); throwSocketException(env, err); return 0; } } - add_send_stats(handle, result); - return result; + return (jint) result; } static jint osNetworkSystem_sendDatagramImpl(JNIEnv* env, jclass clazz, @@ -2398,12 +2607,10 @@ static jint osNetworkSystem_sendConnectedDatagramDirectImpl(JNIEnv* env, if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { return 0; } else { - log_socket_close(handle, err); throwSocketException(env, err); return 0; } } - add_send_stats(handle, length); return result; } @@ -2424,23 +2631,11 @@ static void osNetworkSystem_createServerStreamSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createServerStreamSocketImpl"); - if (fileDescriptor == NULL) { - throwNullPointerException(env); - return; - } - - int handle = socket(PF_INET, SOCK_STREAM, 0); - - if (handle < 0) { - int err = convertError(errno); - throwSocketException(env, err); + int handle = createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM); + if (handle < 0) return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, handle); int value = 1; - setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); } @@ -2448,18 +2643,11 @@ static void osNetworkSystem_createMulticastSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createMulticastSocketImpl"); - int handle = socket(PF_INET, SOCK_DGRAM, 0); - - if (handle < 0) { - int err = convertError(errno); - throwSocketException(env, err); + int handle = createSocketFileDescriptor(env, fileDescriptor, SOCK_DGRAM); + if (handle < 0) return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, handle); int value = 1; - // setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(jbyte)); setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); } @@ -2505,7 +2693,6 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, * to the Java input stream */ if (0 < result) { - add_recv_stats(handle, result); return result; } else if (0 == result) { return -1; @@ -2516,7 +2703,6 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, netLookupErrorString(SOCKERR_TIMEOUT)); } else { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } return 0; @@ -2560,7 +2746,6 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, } env->ReleaseByteArrayElements(data, message, 0); int err = convertError(result); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } @@ -2568,7 +2753,6 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, } env->ReleaseByteArrayElements(data, message, 0); - add_send_stats(handle, sent); return sent; } @@ -2590,7 +2774,6 @@ static void osNetworkSystem_shutdownInputImpl(JNIEnv* env, jobject obj, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -2613,7 +2796,6 @@ static void osNetworkSystem_shutdownOutputImpl(JNIEnv* env, jobject obj, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -2625,26 +2807,19 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, // LOGD("ENTER sendDatagramImpl2"); jbyte *message; - jbyte nhostAddrBytes[4]; unsigned short nPort; - int result = 0, sent = 0; + int ret = 0, sent = 0; int handle = 0; - struct sockaddr_in sockaddrP; + struct sockaddr_storage sockaddrP; if (inetAddress != NULL) { - - result = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockaddrP); + if (ret < 0) // Exception has already been thrown. return 0; - } handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } } @@ -2663,18 +2838,19 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, if (handle == 0 || handle == -1) { throwSocketException(env, - sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + sent == 0 ? SOCKERR_BADDESC : SOCKERR_INTERRUPTED); free(message); return 0; } - result = sendto(handle, (char *) (message + sent), - (int) (length - sent), SOCKET_NOFLAGS, - (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); - + ssize_t result; + do { + result = sendto(handle, (char *) (message + sent), + (int) (length - sent), SOCKET_NOFLAGS, + (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); + } while (result < 0 && errno == EINTR); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); free(message); return 0; @@ -2684,7 +2860,6 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, } free(message); - add_send_stats(handle, sent); return sent; } @@ -2757,10 +2932,10 @@ static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, } if (0 < result) { - /*output the result to a int array*/ - flagArray = env->GetIntArrayElements(outFlags, &isCopy); + /*output the result to a int array*/ + flagArray = env->GetIntArrayElements(outFlags, &isCopy); - for (val=0; val<countReadC; val++) { + for (val=0; val<countReadC; val++) { gotFD = env->GetObjectArrayElement(readFDArray,val); handle = jniGetFDFromFileDescriptor(env, gotFD); @@ -2799,33 +2974,30 @@ static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { // LOGD("ENTER getSocketLocalAddressImpl"); - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); memset(&addr, 0, addrLen); - int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - - int result; + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { throwSocketException(env, SOCKERR_UNKNOWNSOCKET); return NULL; } + int result; result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); // Spec says ignore all errors - - return structInToInetAddress(env, &(addr.sin_addr)); - + return socketAddressToInetAddress(env, &addr); } static jint osNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { // LOGD("ENTER getSocketLocalPortImpl"); - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -2842,7 +3014,7 @@ static jint osNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, // The java spec does not indicate any exceptions on this call return 0; } else { - return ntohs(addr.sin_port); + return getSocketAddressPort(&addr); } } @@ -2856,12 +3028,12 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, unsigned char byteValue = 0; socklen_t byteSize = sizeof(unsigned char); int result; - struct sockaddr_in sockVal; + struct sockaddr_storage sockVal; socklen_t sockSize = sizeof(sockVal); handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return NULL; } @@ -2896,7 +3068,9 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return newJavaLangByte(env, 0); } - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteValue, &byteSize); + result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, &byteValue, + &byteSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -2912,7 +3086,42 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, throwSocketException(env, convertError(errno)); return NULL; } - return structInToInetAddress(env, &(sockVal.sin_addr)); + // This option is IPv4-only. + sockVal.ss_family = AF_INET; + return socketAddressToInetAddress(env, &sockVal); + } + case JAVASOCKOPT_IP_MULTICAST_IF2: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return NULL; + } + struct ip_mreqn multicastRequest; + int interfaceIndex; + socklen_t optionLength; + int addressFamily = getSocketAddressFamily(handle); + switch (addressFamily) { + case AF_INET: + optionLength = sizeof(multicastRequest); + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, + &multicastRequest, &optionLength); + if (result == 0) + interfaceIndex = multicastRequest.imr_ifindex; + break; + case AF_INET6: + optionLength = sizeof(interfaceIndex); + result = getsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &interfaceIndex, &optionLength); + break; + default: + throwSocketException(env, SOCKERR_BADAF); + return NULL; + } + + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + + return newJavaLangInteger(env, interfaceIndex); } case JAVASOCKOPT_SO_SNDBUF: { result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize); @@ -2963,7 +3172,10 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, return newJavaLangBoolean(env, intValue); } case JAVASOCKOPT_IP_MULTICAST_LOOP: { - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intValue, &intSize); + result = getOrSetSocketOption(SOCKOPT_GET, handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, &intValue, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -2971,7 +3183,8 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, return newJavaLangBoolean(env, intValue); } case JAVASOCKOPT_IP_TOS: { - result = getsockopt(handle, IPPROTO_IP, IP_TOS, &intValue, &intSize); + result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_TOS, + IPV6_TCLASS, &intValue, &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -3001,9 +3214,11 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER setSocketOptionImpl"); int handle, result; - int intVal, intSize = sizeof(int); - unsigned char byteVal, byteSize = sizeof(unsigned char); - struct sockaddr_in sockVal; + int intVal; + socklen_t intSize = sizeof(int); + unsigned char byteVal; + socklen_t byteSize = sizeof(unsigned char); + struct sockaddr_storage sockVal; int sockSize = sizeof(sockVal); if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) { @@ -3014,7 +3229,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value); } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) { if (inetAddressToSocketAddress(env, optVal, 0, &sockVal) < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + // Exception has already been thrown. return; } } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) { @@ -3026,7 +3241,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } @@ -3056,11 +3271,13 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, break; } - case JAVASOCKOPT_MCAST_TTL: { + case JAVASOCKOPT_MCAST_TTL: { if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return; } - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteVal, byteSize); + result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, &byteVal, + &byteSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3071,23 +3288,28 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: { mcastAddDropMembership(env, handle, optVal, (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP); - return; + break; } case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: { mcastAddDropMembership(env, handle, optVal, (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP); - return; + break; } case JAVASOCKOPT_MCAST_INTERFACE: { if ((anOption >> 16) & BROKEN_MULTICAST_IF) { return; } + // This call is IPv4 only. + if (getSocketAddressFamily(handle) != AF_INET) { + throwSocketException(env, SOCKERR_BADAF); + return; + } struct ip_mreqn mcast_req; memset(&mcast_req, 0, sizeof(mcast_req)); - memcpy(&(mcast_req.imr_address), &(sockVal.sin_addr), - sizeof(struct in_addr)); + struct sockaddr_in *sin = (struct sockaddr_in *) &sockVal; + mcast_req.imr_address = sin->sin_addr; result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &mcast_req, sizeof(mcast_req)); if (0 != result) { @@ -3097,6 +3319,42 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, break; } + case JAVASOCKOPT_IP_MULTICAST_IF2: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return; + } + int addressFamily = getSocketAddressFamily(handle); + int interfaceIndex = intVal; + void *optionValue; + socklen_t optionLength; + struct ip_mreqn multicastRequest; + switch (addressFamily) { + case AF_INET: + // IP_MULTICAST_IF expects a pointer to a struct ip_mreqn. + memset(&multicastRequest, 0, sizeof(multicastRequest)); + multicastRequest.imr_ifindex = interfaceIndex; + optionValue = &multicastRequest; + optionLength = sizeof(multicastRequest); + break; + case AF_INET6: + // IPV6_MULTICAST_IF expects a pointer to an integer. + optionValue = &interfaceIndex; + optionLength = sizeof(interfaceIndex); + break; + default: + throwSocketException(env, SOCKERR_BADAF); + return; + } + result = getOrSetSocketOption(SOCKOPT_SET, handle, + IP_MULTICAST_IF, IPV6_MULTICAST_IF, optionValue, + &optionLength); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + case JAVASOCKOPT_SO_SNDBUF: { result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize); if (0 != result) { @@ -3151,7 +3409,10 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_IP_MULTICAST_LOOP: { - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal, intSize); + result = getOrSetSocketOption(SOCKOPT_SET, handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, &intVal, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3160,7 +3421,8 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_IP_TOS: { - result = setsockopt(handle, IPPROTO_IP, IP_TOS, &intVal, intSize); + result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_TOS, + IPV6_TCLASS, &intVal, &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3215,8 +3477,6 @@ static void osNetworkSystem_socketCloseImpl(JNIEnv* env, jclass clazz, return; } - log_socket_close(handle, SOCKET_CLOSE_LOCAL); - jniSetFileDescriptorOfFD(env, fileDescriptor, -1); close(handle); @@ -3311,6 +3571,7 @@ static void osNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, env->SetObjectField(sender, gCachedFields.iaddr_ipaddress, address); } +// TODO: rewrite this method in Java and make it support IPv6. static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { // LOGD("ENTER inheritedChannelImpl"); diff --git a/luni/src/test/java/com/google/coretests/CoreTestResult.java b/luni/src/test/java/com/google/coretests/CoreTestResult.java index 9a821cd..802eff8 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestResult.java +++ b/luni/src/test/java/com/google/coretests/CoreTestResult.java @@ -131,6 +131,7 @@ public class CoreTestResult extends TestResult { // Ignored } if (thread.isAlive()) { + StackTraceElement[] trace = thread.getStackTrace(); runnable.stop(); thread.stop(); try { @@ -138,8 +139,10 @@ public class CoreTestResult extends TestResult { } catch (InterruptedException ex) { // Ignored } - - addError(test, new CoreTestTimeout("Test timed out")); + + CoreTestTimeout timeout = new CoreTestTimeout("Test timed out"); + timeout.setStackTrace(trace); + addError(test, timeout); } } else { runnable.run(); diff --git a/luni/src/test/java/com/google/coretests/CoreTestRunnable.java b/luni/src/test/java/com/google/coretests/CoreTestRunnable.java index ab49e47..ed7797e 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestRunnable.java +++ b/luni/src/test/java/com/google/coretests/CoreTestRunnable.java @@ -137,18 +137,18 @@ public class CoreTestRunnable implements Runnable { Throwable throwable = null; File file = File.createTempFile("isolation", ".tmp"); - - fProcess = Runtime.getRuntime().exec( - (IS_DALVIK ? "dalvikvm" : "java") + + + String program = (IS_DALVIK ? "dalvikvm" : "java") + " -classpath " + System.getProperty("java.class.path") + " -Djava.home=" + System.getProperty("java.home") + " -Duser.home=" + System.getProperty("user.home") + - " -Djava.io.tmpdir=" + System.getProperty("user.home") + + " -Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir") + " com.google.coretests.CoreTestIsolator" + " " + fTest.getClass().getName() + " " + fTest.getName() + - " " + file.getAbsolutePath()); - + " " + file.getAbsolutePath(); + fProcess = Runtime.getRuntime().exec(program); + int result = fProcess.waitFor(); if (result != TestRunner.SUCCESS_EXIT) { @@ -158,7 +158,7 @@ public class CoreTestRunnable implements Runnable { throwable = (Throwable)ois.readObject(); ois.close(); } catch (Exception ex) { - throwable = new RuntimeException("Error isolating test", ex); + throwable = new RuntimeException("Error isolating test: " + program, ex); } } diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java index 47eb166..a24f457 100644 --- a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java +++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java @@ -23,9 +23,10 @@ import java.lang.Thread.UncaughtExceptionHandler; import java.security.Permission; import java.util.Map; import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.atomic.AtomicReference; import dalvik.annotation.AndroidOnly; -import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; @@ -913,7 +914,7 @@ public class ThreadTest extends junit.framework.TestCase { } catch (InterruptedException e) { fail("Join failed "); } - assertTrue("Joined thread is still alive", !st.isAlive()); + assertFalse("Joined thread is still alive", st.isAlive()); boolean result = true; Thread th = new Thread("test"); try { @@ -938,6 +939,44 @@ public class ThreadTest extends junit.framework.TestCase { st.start(); } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Regression test for when join() failed due to spurious wakeups", + method = "join", + args = {long.class, int.class} + ) + public void test_joinWithSpuriousInterruption() throws InterruptedException { + final Thread parker = new Thread() { + @Override + public void run() { + for (int i = 0; i < 10; i++) { + // we used to get spurious wakeups upon unparking + LockSupport.park(); + } + } + }; + Thread unparker = new Thread() { + @Override + public void run() { + for (int i = 0; i < 10; i++) { + try { + Thread.sleep(100); + LockSupport.unpark(parker); + } catch (InterruptedException ignored) { + } + } + } + }; + + long startNanos = System.nanoTime(); + parker.start(); + unparker.start(); + parker.join(500, 500000); + long netWaitTime = System.nanoTime() - startNanos; + assertTrue("Expected to wait at least 500000000ns, but was " + netWaitTime + "ns", + netWaitTime > 500000000); + } + /** * @tests java.lang.Thread#join(long) */ @@ -1919,23 +1958,33 @@ public class ThreadTest extends junit.framework.TestCase { public void run() { while (!sem.hasQueuedThreads()) {} sem.release(); + + // RUNNABLE while (run) {} + try { + // WAITING sem.acquire(); } catch (InterruptedException e) { fail("InterruptedException was thrown."); } + + // BLOCKED synchronized (lock) { lock.equals(new Object()); } synchronized (lock) { try { sem.release(); + + // TIMED_WAITING lock.wait(Long.MAX_VALUE); } catch (InterruptedException e) { // expected } } + + // TERMINATED upon return } }; assertEquals(Thread.State.NEW, th.getState()); @@ -1976,7 +2025,7 @@ public class ThreadTest extends junit.framework.TestCase { } assertEquals(Thread.State.TERMINATED, th.getState()); } - boolean run = true; + volatile boolean run = true; /** * @tests java.lang.Thread#getUncaughtExceptionHandler diff --git a/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java b/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java index bb3f8f5..748105a 100644 --- a/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java +++ b/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java @@ -726,10 +726,12 @@ public class InputStreamReaderTest extends TestCase { char[] chars = new char[8192]; int at = 0; - outer: for (;;) { int amt = isr.read(chars); - if (amt <= 0) break; + if (amt <= 0) { + break; + } + for (int i = 0; i < amt; i++) { char c = chars[i]; if (at < prefixLength) { diff --git a/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java b/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java index b7a1012..ce34dba 100755 --- a/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java +++ b/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java @@ -29,6 +29,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.BindException; import java.net.ConnectException; +import java.net.Inet4Address; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; @@ -4114,6 +4116,95 @@ public class SocketChannelTest extends TestCase { } } + /** + * @throws IOException + * @tests java.nio.channels.SocketChannel#read(ByteBuffer) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "read", + args = {java.nio.ByteBuffer[].class} + ) + public void test_socketChannel_read_DirectByteBuffer() throws InterruptedException, IOException { + + ServerThread server = new ServerThread(); + server.start(); + Thread.currentThread().sleep(1000); + + InetSocketAddress address = new InetSocketAddress(InetAddress + .getByName("localhost"), port); + + // First test with array based byte buffer + SocketChannel sc = SocketChannel.open(); + sc.connect(address); + + ByteBuffer buf = ByteBuffer.allocate(data.length); + buf.limit(data.length / 2); + sc.read(buf); + + buf.limit(buf.capacity()); + sc.read(buf); + sc.close(); + + // Make sure the buffer is filled correctly + buf.rewind(); + assertSameContent(data, buf); + + // Now test with direct byte buffer + sc = SocketChannel.open(); + sc.connect(address); + + buf = ByteBuffer.allocateDirect(data.length); + buf.limit(data.length / 2); + sc.read(buf); + + buf.limit(buf.capacity()); + sc.read(buf); + sc.close(); + + // Make sure the buffer is filled correctly + buf.rewind(); + assertSameContent(data, buf); + } + + private void assertSameContent(byte[] data, ByteBuffer buf) { + for (byte b : data) { + if (b != buf.get()) { + int pos = buf.position() - 1; + fail("Content not equal. Buffer position: " + + (pos) + " expected: " + b + " was: " + buf.get(pos)); + } + } + } + + public static boolean done = false; + public static int port = Support_PortManager.getNextPort(); + public static byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + static class ServerThread extends Thread { + @Override + public void run() { + try { + ServerSocketChannel ssc = ServerSocketChannel.open(); + InetSocketAddress addr = new InetSocketAddress(InetAddress + .getByAddress(new byte[] {0, 0, 0, 0}), port); + ssc.socket().bind(addr, 0); + + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(data); + + while (!done) { + SocketChannel sc = ssc.accept(); + buf.rewind(); + sc.write(buf); + } + } catch (Exception e) { + // ignore + } + } + } + class MockSocketChannel extends SocketChannel { private boolean isWriteCalled = false; diff --git a/prefs/src/main/java/java/util/prefs/Preferences.java b/prefs/src/main/java/java/util/prefs/Preferences.java index b7a0c70..719c89a 100644 --- a/prefs/src/main/java/java/util/prefs/Preferences.java +++ b/prefs/src/main/java/java/util/prefs/Preferences.java @@ -1006,15 +1006,11 @@ public abstract class Preferences { //parse node's absolute path from class instance private static String getNodeName(Class<?> c){ - // ??? PREFS TODO change back to harmony code once getPackage - // delivers the correct results - // Package p = c.getPackage(); - // if(null == p){ - // return "/<unnamed>"; //$NON-NLS-1$ - // } - // return "/"+p.getName().replace('.', '/'); //$NON-NLS-1$ - int dotIndex = c.getName().lastIndexOf("."); - return "/" + c.getName().substring(0, dotIndex).replace(".", "/"); + Package p = c.getPackage(); + if(null == p){ + return "/<unnamed>"; //$NON-NLS-1$ + } + return "/"+p.getName().replace('.', '/'); //$NON-NLS-1$ } /** diff --git a/text/src/main/java/java/text/RuleBasedCollator.java b/text/src/main/java/java/text/RuleBasedCollator.java index 41a51e2..6418962 100644 --- a/text/src/main/java/java/text/RuleBasedCollator.java +++ b/text/src/main/java/java/text/RuleBasedCollator.java @@ -390,7 +390,7 @@ public class RuleBasedCollator extends Collator { /** * Returns the collation rules of this collator. These {@code rules} can be - * fed into the {@link #RuleBasedCollator(String)} constructor. + * fed into the {@code RuleBasedCollator(String)} constructor. * <p> * Note that the {@code rules} are actually interpreted as a delta to the * standard Unicode Collation Algorithm (UCA). Hence, an empty {@code rules} diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp index 250cf83..8f36632 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp @@ -36,8 +36,6 @@ #include <openssl/rand.h> #include <openssl/ssl.h> -#include <utils/LogSocket.h> - #include "org_apache_harmony_xnet_provider_jsse_common.h" /** @@ -670,8 +668,11 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, // LOGD("Doing SSL_Read()"); int result = SSL_read(ssl, buf, len); - int error = SSL_get_error(ssl, result); - freeSslErrorState(); + int error = SSL_ERROR_NONE; + if (result <= 0) { + error = SSL_get_error(ssl, result); + freeSslErrorState(); + } // LOGD("Returned from SSL_Read() with result %d, error code %d", result, error); // If we have been successful in moving data around, check whether it @@ -693,7 +694,6 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, switch (error) { // Sucessfully read at least one byte. case SSL_ERROR_NONE: { - add_recv_stats(fd, result); return result; } @@ -786,8 +786,11 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, // LOGD("Doing SSL_write() with %d bytes to go", len); int result = SSL_write(ssl, buf, len); - int error = SSL_get_error(ssl, result); - freeSslErrorState(); + int error = SSL_ERROR_NONE; + if (result <= 0) { + error = SSL_get_error(ssl, result); + freeSslErrorState(); + } // LOGD("Returned from SSL_write() with result %d, error code %d", result, error); // If we have been successful in moving data around, check whether it @@ -861,7 +864,6 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, } } } - add_send_stats(fd, count); // LOGD("Successfully wrote %d bytes", count); return count; |