summaryrefslogtreecommitdiffstats
path: root/luni-kernel/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:47 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:47 -0800
commitadc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch)
tree6aed8b4923ca428942cbaa7e848d50237a3d31e0 /luni-kernel/src
parent1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff)
downloadlibcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.zip
libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.gz
libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'luni-kernel/src')
-rw-r--r--luni-kernel/src/main/java/java/lang/Class.java1627
-rw-r--r--luni-kernel/src/main/java/java/lang/ClassCache.java702
-rw-r--r--luni-kernel/src/main/java/java/lang/ClassLoader.java1139
-rw-r--r--luni-kernel/src/main/java/java/lang/Compiler.java100
-rw-r--r--luni-kernel/src/main/java/java/lang/LangAccessImpl.java55
-rw-r--r--luni-kernel/src/main/java/java/lang/Object.java368
-rw-r--r--luni-kernel/src/main/java/java/lang/Package.java321
-rw-r--r--luni-kernel/src/main/java/java/lang/ProcessManager.java384
-rw-r--r--luni-kernel/src/main/java/java/lang/Runtime.java889
-rw-r--r--luni-kernel/src/main/java/java/lang/StackTraceElement.java245
-rw-r--r--luni-kernel/src/main/java/java/lang/System.java755
-rw-r--r--luni-kernel/src/main/java/java/lang/Thread.java1559
-rw-r--r--luni-kernel/src/main/java/java/lang/ThreadGroup.java926
-rw-r--r--luni-kernel/src/main/java/java/lang/Throwable.java410
-rw-r--r--luni-kernel/src/main/java/java/lang/ref/PhantomReference.java81
-rw-r--r--luni-kernel/src/main/java/java/lang/ref/Reference.java162
-rw-r--r--luni-kernel/src/main/java/java/lang/ref/SoftReference.java135
-rw-r--r--luni-kernel/src/main/java/java/lang/ref/WeakReference.java119
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java465
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/Array.java820
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/Constructor.java491
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/Field.java939
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/Method.java595
-rw-r--r--luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java54
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java129
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java63
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java63
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java289
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java323
-rw-r--r--luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java387
-rw-r--r--luni-kernel/src/main/native/java_lang_ProcessManager.c438
-rw-r--r--luni-kernel/src/main/native/java_lang_System.c106
-rw-r--r--luni-kernel/src/main/native/sub.mk17
-rw-r--r--luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java298
34 files changed, 15454 insertions, 0 deletions
diff --git a/luni-kernel/src/main/java/java/lang/Class.java b/luni-kernel/src/main/java/java/lang/Class.java
new file mode 100644
index 0000000..70ae3c1
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Class.java
@@ -0,0 +1,1627 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import dalvik.system.VMStack;
+
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.net.URL;
+import java.security.ProtectionDomain;
+
+import static java.lang.ClassCache.REFLECT;
+import static java.lang.ClassCache.compareClassLists;
+import static java.lang.ClassCache.findMethodByName;
+import static java.lang.ClassCache.findFieldByName;
+
+/**
+ * The in-memory representation of a Java class. This representation serves as
+ * the starting point for querying class-related information, a process usually
+ * called "reflection". There are basically three types of {@code Class}
+ * instances: those representing real classes and interfaces, those representing
+ * primitive types, and those representing array classes.
+ *
+ * <h4>Class instances representing object types (classes or interfaces)</h4>
+ * <p>
+ * These represent an ordinary class or interface as found in the class
+ * hierarchy. The name associated with these {@code Class} instances is simply
+ * the fully qualified class name of the class or interface that it represents.
+ * In addition to this human-readable name, each class is also associated by a
+ * so-called <em>signature</em>, which is the letter "L", followed by the
+ * class name and a semicolon (";"). The signature is what the runtime system
+ * uses internally for identifying the class (for example in a DEX file).
+ * </p>
+ * <h4>Classes representing primitive types</h4>
+ * <p>
+ * These represent the standard Java primitive types and hence share their
+ * names (for example "int" for the {@code int} primitive type). Although it is
+ * not possible to create new instances based on these {@code Class} instances,
+ * they are still useful for providing reflection information, and as the
+ * component type of array classes. There is one {@code Class} instance for each
+ * primitive type, and their signatures are:
+ * </p>
+ * <ul>
+ * <li>{@code B} representing the {@code byte} primitive type</li>
+ * <li>{@code S} representing the {@code short} primitive type</li>
+ * <li>{@code I} representing the {@code int} primitive type</li>
+ * <li>{@code J} representing the {@code long} primitive type</li>
+ * <li>{@code F} representing the {@code float} primitive type</li>
+ * <li>{@code D} representing the {@code double} primitive type</li>
+ * <li>{@code C} representing the {@code char} primitive type</li>
+ * <li>{@code Z} representing the {@code boolean} primitive type</li>
+ * <li>{@code V} representing void function return values</li>
+ * </ul>
+ * <p>
+ * <h4>Classes representing array classes</h4>
+ * <p>
+ * These represent the classes of Java arrays. There is one such {@code Class}
+ * instance per combination of array leaf component type and arity (number of
+ * dimensions). In this case, the name associated with the {@code Class}
+ * consists of one or more left square brackets (one per dimension in the array)
+ * followed by the signature of the class representing the leaf component type,
+ * which can be either an object type or a primitive type. The signature of a
+ * {@code Class} representing an array type is the same as its name. Examples
+ * of array class signatures are:
+ * </p>
+ * <ul>
+ * <li>{@code [I} representing the {@code int[]} type</li>
+ * <li>{@code [Ljava/lang/String;} representing the {@code String[]} type</li>
+ * <li>{@code [[[C} representing the {@code char[][][]} type (three dimensions!)</li>
+ * </ul>
+ *
+ * @since Android 1.0
+ */
+public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type {
+
+ private static final long serialVersionUID = 3206093459760846163L;
+
+ // TODO How is this field being initialized? What's it being used for?
+ private ProtectionDomain pd;
+
+ /**
+ * null-ok; cache of reflective information, wrapped in a soft
+ * reference
+ */
+ private volatile SoftReference<ClassCache<T>> cacheRef;
+
+ private Class() {
+ // Prevent this class to be instantiated, instance
+ // should be created by JVM only
+ }
+
+ /**
+ * Get the Signature attribute for this class. Returns null if not found.
+ */
+ private String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation();
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Get the Signature annotation for this class. Returns null if not found.
+ */
+ native private Object[] getSignatureAnnotation();
+
+ /**
+ * Returns a {@code Class} object which represents the class with the
+ * specified name. The name should be the name of a class as described in
+ * the {@link Class class definition}; however, {@code Class}es representing
+ * primitive types can not be found using this method.
+ * <p>
+ * If the class has not been loaded so far, it is being loaded and linked
+ * first. This is done through either the class loader of the calling class
+ * or one of its parent class loaders. The class is also being initialized,
+ * which means that a possible static initializer block is executed.
+ *
+ * @param className
+ * the name of the non-primitive-type class to find.
+ * @return the named {@code Class} instance.
+ * @throws ClassNotFoundException
+ * if the requested class can not be found.
+ * @throws LinkageError
+ * if an error occurs during linkage
+ * @throws ExceptionInInitializerError
+ * if an exception occurs during static initialization of a
+ * class.
+ * @since Android 1.0
+ */
+ public static Class<?> forName(String className) throws ClassNotFoundException {
+ return forName(className, true, VMStack.getCallingClassLoader());
+ }
+
+ /**
+ * Returns a {@code Class} object which represents the class with the
+ * specified name. The name should be the name of a class as described in
+ * the {@link Class class definition}, however {@code Class}es representing
+ * primitive types can not be found using this method. Security rules will
+ * be obeyed.
+ * <p>
+ * If the class has not been loaded so far, it is being loaded and linked
+ * first. This is done through either the specified class loader or one of
+ * its parent class loaders. The caller can also request the class to be
+ * initialized, which means that a possible static initializer block is
+ * executed.
+ *
+ * @param className
+ * the name of the non-primitive-type class to find.
+ * @param initializeBoolean
+ * indicates whether the class should be initialized.
+ * @param classLoader
+ * the class loader to use to load the class.
+ * @return the named {@code Class} instance.
+ * @throws ClassNotFoundException
+ * if the requested class can not be found.
+ * @throws LinkageError
+ * if an error occurs during linkage
+ * @throws ExceptionInInitializerError
+ * if an exception occurs during static initialization of a
+ * class.
+ * @since Android 1.0
+ */
+ public static Class<?> forName(String className, boolean initializeBoolean,
+ ClassLoader classLoader) throws ClassNotFoundException {
+
+ if (classLoader == null) {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ ClassLoader calling = VMStack.getCallingClassLoader();
+ if (calling != null) {
+ smgr.checkPermission(new RuntimePermission("getClassLoader"));
+ }
+ }
+
+ classLoader = ClassLoader.getSystemClassLoader();
+ }
+ // Catch an Exception thrown by the underlying native code. It wraps
+ // up everything inside a ClassNotFoundException, even if e.g. an
+ // Error occurred during initialization. This as a workaround for
+ // an ExceptionInInitilaizerError that's also wrapped. It is actually
+ // expected to be thrown. Maybe the same goes for other errors.
+ // Not wrapping up all the errors will break android though.
+ Class<?> result;
+ try {
+ result = classForName(className, initializeBoolean,
+ classLoader);
+ } catch (ClassNotFoundException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof ExceptionInInitializerError) {
+ throw (ExceptionInInitializerError) cause;
+ }
+ throw e;
+ }
+ return result;
+ }
+
+ /*
+ * Returns a class by name without any security checks.
+ *
+ * @param className The name of the non-primitive type class to find
+ * @param initializeBoolean A boolean indicating whether the class should be
+ * initialized
+ * @param classLoader The class loader to use to load the class
+ * @return the named class.
+ * @throws ClassNotFoundException If the class could not be found
+ */
+ static native Class<?> classForName(String className, boolean initializeBoolean,
+ ClassLoader classLoader) throws ClassNotFoundException;
+
+ /**
+ * Returns an array containing {@code Class} objects for all public classes
+ * and interfaces that are members of this class. This includes public
+ * members inherited from super classes and interfaces. If there are no such
+ * class members or if this object represents a primitive type then an array
+ * of length 0 is returned.
+ *
+ * @return the public class members of the class represented by this object.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @since Android 1.0
+ */
+ public Class[] getClasses() {
+ // BEGIN android-note
+ // trying to get closer to the RI which returns a raw class array.
+ // copied from newer version of harmony
+ // END android-note
+ checkPublicMemberAccess();
+ return getFullListOfClasses(true);
+ }
+
+ /**
+ * Returns the annotation of the given type. If there is no such annotation
+ * then the method returns {@code null}.
+ *
+ * @param annotationClass
+ * the annotation type.
+ * @return the annotation of the given type, or {@code null} if there is no
+ * such annotation.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
+ Annotation[] list = getAnnotations();
+ for (int i = 0; i < list.length; i++) {
+ if (annotationClass.isInstance(list[i])) {
+ return (A)list[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns all the annotations of this class. If there are no annotations
+ * then an empty array is returned.
+ *
+ * @return a copy of the array containing this class' annotations.
+ * @see #getDeclaredAnnotations()
+ * @since Android 1.0
+ */
+ public Annotation[] getAnnotations() {
+ /*
+ * We need to get the annotations declared on this class, plus the
+ * annotations from superclasses that have the "@Inherited" annotation
+ * set. We create a temporary map to use while we accumulate the
+ * annotations and convert it to an array at the end.
+ *
+ * It's possible to have duplicates when annotations are inherited.
+ * We use a Map to filter those out.
+ *
+ * HashMap might be overkill here.
+ */
+ HashMap<Class, Annotation> map = new HashMap<Class, Annotation>();
+ Annotation[] annos = getDeclaredAnnotations();
+
+ for (int i = annos.length-1; i >= 0; --i)
+ map.put(annos[i].annotationType(), annos[i]);
+
+ for (Class sup = getSuperclass(); sup != null;
+ sup = sup.getSuperclass()) {
+ annos = sup.getDeclaredAnnotations();
+ for (int i = annos.length-1; i >= 0; --i) {
+ Class clazz = annos[i].annotationType();
+ if (!map.containsKey(clazz) &&
+ clazz.isAnnotationPresent(Inherited.class)) {
+ map.put(clazz, annos[i]);
+ }
+ }
+ }
+
+ /* convert annotation values from HashMap to array */
+ Collection<Annotation> coll = map.values();
+ return coll.toArray(new Annotation[coll.size()]);
+ }
+
+ /**
+ * Returns the canonical name of this class. If this class does not have a
+ * canonical name as defined in the Java Language Specification, then the
+ * method returns {@code null}.
+ *
+ * @return this class' canonical name, or {@code null} if it does not have a
+ * canonical name.
+ * @since Android 1.0
+ */
+ public String getCanonicalName() {
+ if (isLocalClass() || isAnonymousClass())
+ return null;
+
+ if (isArray()) {
+ /*
+ * The canonical name of an array type depends on the (existence of)
+ * the component type's canonical name.
+ */
+ String name = getComponentType().getCanonicalName();
+ if (name != null) {
+ return name + "[]";
+ }
+ } else if (isMemberClass()) {
+ /*
+ * The canonical name of an inner class depends on the (existence
+ * of) the declaring class' canonical name.
+ */
+ String name = getDeclaringClass().getCanonicalName();
+ if (name != null) {
+ return name + "." + getSimpleName();
+ }
+ } else {
+ /*
+ * The canonical name of a top-level class or primitive type is
+ * equal to the fully qualified name.
+ */
+ return getName();
+ }
+
+ /*
+ * Other classes don't have a canonical name.
+ */
+ return null;
+ }
+
+ /**
+ * Returns the class loader which was used to load the class represented by
+ * this {@code Class}. Implementations are free to return {@code null} for
+ * classes that were loaded by the bootstrap class loader. The Android
+ * reference implementation, though, returns a reference to an actual
+ * representation of the bootstrap class loader.
+ *
+ * @return the class loader for the represented class.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow accessing
+ * the class loader.
+ * @see ClassLoader
+ * @since Android 1.0
+ */
+ public ClassLoader getClassLoader() {
+ SecurityManager smgr = System.getSecurityManager();
+ ClassLoader loader = getClassLoaderImpl();
+ if (smgr != null && loader != null) {
+ ClassLoader calling = VMStack.getCallingClassLoader();
+
+ if (calling != null && !calling.isAncestorOf(loader)) {
+ smgr.checkPermission(new RuntimePermission("getClassLoader"));
+ }
+ }
+
+ if (this.isPrimitive()) {
+ return null;
+ }
+
+ if (loader == null) {
+ loader = BootClassLoader.getInstance();
+ }
+
+ return loader;
+ }
+
+ /**
+ * This must be provided by the VM vendor, as it is used by other provided
+ * class implementations in this package. Outside of this class, it is used
+ * by SecurityManager.checkMemberAccess(), classLoaderDepth(),
+ * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for
+ * this Class without doing any security checks. The bootstrap ClassLoader
+ * is returned, unlike getClassLoader() which returns null in place of the
+ * bootstrap ClassLoader.
+ *
+ * @return the ClassLoader
+ * @see ClassLoader#isSystemClassLoader()
+ */
+ ClassLoader getClassLoaderImpl() {
+ return getClassLoader(this);
+ }
+
+ /*
+ * Returns the defining class loader for the given class.
+ *
+ * @param clazz the class the class loader of which we want
+ * @return the class loader
+ */
+ private static native ClassLoader getClassLoader(Class<?> clazz);
+
+ /**
+ * Returns a {@code Class} object which represents the component type if
+ * this class represents an array type. Returns {@code null} if this class
+ * does not represent an array type. The component type of an array type is
+ * the type of the elements of the array.
+ *
+ * @return the component type of this class.
+ * @since Android 1.0
+ */
+ public native Class<?> getComponentType();
+
+ /**
+ * Returns a {@code Constructor} object which represents the public
+ * constructor matching the specified parameter types.
+ *
+ * @param parameterTypes
+ * the parameter types of the requested constructor.
+ * @return the constructor described by {@code parameterTypes}.
+ * @throws NoSuchMethodException
+ * if the constructor can not be found.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredConstructor(Class...)
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public Constructor<T> getConstructor(Class... parameterTypes) throws NoSuchMethodException,
+ SecurityException {
+ checkPublicMemberAccess();
+ return getMatchingConstructor(getDeclaredConstructors(this, true), parameterTypes);
+ }
+
+ /**
+ * Returns an array containing {@code Constructor} objects for all public
+ * constructors for the class represented by this {@code Class}. If there
+ * are no public constructors or if this {@code Class} represents an array
+ * class, a primitive type or void then an empty array is returned.
+ *
+ * @return an array with the public constructors of the class represented by
+ * this {@code Class}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredConstructors()
+ * @since Android 1.0
+ */
+ public Constructor[] getConstructors() throws SecurityException {
+ // BEGIN android-note
+ // trying to get closer to the RI which returns a raw constructor array.
+ // copied from newer version of harmony
+ // END android-note
+ checkPublicMemberAccess();
+ return getDeclaredConstructors(this, true);
+ }
+
+ /**
+ * Returns the annotations that are directly defined on the class
+ * represented by this {@code Class}. Annotations that are inherited are not
+ * included in the result. If there are no annotations at all, an empty
+ * array is returned.
+ *
+ * @return a copy of the array containing the annotations defined for the
+ * class that this {@code Class} represents.
+ * @see #getAnnotations()
+ * @since Android 1.0
+ */
+ native public Annotation[] getDeclaredAnnotations();
+
+ /**
+ * Returns an array containing {@code Class} objects for all classes and
+ * interfaces that are declared as members of the class which this {@code
+ * Class} represents. If there are no classes or interfaces declared or if
+ * this class represents an array class, a primitive type or void, then an
+ * empty array is returned.
+ *
+ * @return an array with {@code Class} objects for all the classes and
+ * interfaces that are used in member declarations.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @since Android 1.0
+ */
+ public Class[] getDeclaredClasses() throws SecurityException {
+ // BEGIN android-note
+ // trying to get closer to the RI which returns a raw class array.
+ // copied from newer version of harmony
+ // END android-note
+ checkDeclaredMemberAccess();
+ return getDeclaredClasses(this, false);
+ }
+
+ /*
+ * Returns the list of member classes without performing any security checks
+ * first. This includes the member classes inherited from superclasses. If no
+ * member classes exist at all, an empty array is returned.
+ *
+ * @param publicOnly reflects whether we want only public members or all of them
+ * @return the list of classes
+ */
+ private Class<?>[] getFullListOfClasses(boolean publicOnly) {
+ Class<?>[] result = getDeclaredClasses(this, publicOnly);
+
+ // Traverse all superclasses
+ Class<?> clazz = this.getSuperclass();
+ while (clazz != null) {
+ Class<?>[] temp = getDeclaredClasses(clazz, publicOnly);
+ if (temp.length != 0) {
+ result = arraycopy(new Class[result.length + temp.length], result, temp);
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+
+ return result;
+ }
+
+ /*
+ * Returns the list of member classes of the given class. No security checks
+ * are performed. If no members exist, an empty array is returned.
+ *
+ * @param clazz the class the members of which we want
+ * @param publicOnly reflects whether we want only public member or all of them
+ * @return the class' class members
+ */
+ native private static Class<?>[] getDeclaredClasses(Class<?> clazz,
+ boolean publicOnly);
+
+ /**
+ * Returns a {@code Constructor} object which represents the constructor
+ * matching the specified parameter types that is declared by the class
+ * represented by this {@code Class}.
+ *
+ * @param parameterTypes
+ * the parameter types of the requested constructor.
+ * @return the constructor described by {@code parameterTypes}.
+ * @throws NoSuchMethodException
+ * if the requested constructor can not be found.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getConstructor(Class...)
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public Constructor<T> getDeclaredConstructor(Class... parameterTypes)
+ throws NoSuchMethodException, SecurityException {
+ checkDeclaredMemberAccess();
+ return getMatchingConstructor(getDeclaredConstructors(this, false), parameterTypes);
+ }
+
+ /**
+ * Returns an array containing {@code Constructor} objects for all
+ * constructors declared in the class represented by this {@code Class}. If
+ * there are no constructors or if this {@code Class} represents an array
+ * class, a primitive type or void then an empty array is returned.
+ *
+ * @return an array with the constructors declared in the class represented
+ * by this {@code Class}.
+ *
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getConstructors()
+ * @since Android 1.0
+ */
+ public Constructor[] getDeclaredConstructors() throws SecurityException {
+ // BEGIN android-note
+ // trying to get closer to the RI which returns a raw constructor array.
+ // copied from newer version of harmony
+ // END android-note
+ checkDeclaredMemberAccess();
+ return getDeclaredConstructors(this, false);
+ }
+
+ /*
+ * Returns the list of constructors without performing any security checks
+ * first. If no constructors exist, an empty array is returned.
+ *
+ * @param clazz the class of interest
+ * @param publicOnly reflects whether we want only public constructors or all of them
+ * @return the list of constructors
+ */
+ private static native <T> Constructor<T>[] getDeclaredConstructors(Class<T> clazz, boolean publicOnly);
+
+ /*
+ * Finds a constructor with a given signature.
+ *
+ * @param list the list of constructors to search through
+ * @param parameterTypes the formal parameter list
+ * @return the matching constructor
+ * @throws NoSuchMethodException if the constructor does not exist.
+ */
+ private Constructor<T> getMatchingConstructor(
+ Constructor<T>[] list, Class<?>[] parameterTypes)
+ throws NoSuchMethodException {
+ for (int i = 0; i < list.length; i++) {
+ if (compareClassLists(list[i].getParameterTypes(), parameterTypes)) {
+ return list[i];
+ }
+ }
+
+ // BEGIN android-changed
+ StringBuilder sb = new StringBuilder();
+ sb.append(getSimpleName());
+ sb.append('(');
+ boolean first = true;
+ for (Class<?> p : parameterTypes) {
+ if (!first) {
+ sb.append(',');
+ }
+ first = false;
+ sb.append(p.getSimpleName());
+ }
+ sb.append(')');
+ throw new NoSuchMethodException(sb.toString());
+ // END android-changed
+ }
+
+ /**
+ * Returns a {@code Field} object for the field with the specified name
+ * which is declared in the class represented by this {@code Class}.
+ *
+ * @param name
+ * the name of the requested field.
+ * @return the requested field in the class represented by this class.
+ * @throws NoSuchFieldException
+ * if the requested field can not be found.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getField(String)
+ * @since Android 1.0
+ */
+ public Field getDeclaredField(String name)
+ throws NoSuchFieldException, SecurityException {
+ checkDeclaredMemberAccess();
+
+ Field[] fields = getClassCache().getDeclaredFields();
+ Field field = findFieldByName(fields, name);
+
+ /*
+ * Make a copy of the private (to the package) object, so that
+ * setAccessible() won't alter the private instance.
+ */
+ return REFLECT.clone(field);
+ }
+
+ /**
+ * Returns an array containing {@code Field} objects for all fields declared
+ * in the class represented by this {@code Class}. If there are no fields or
+ * if this {@code Class} represents an array class, a primitive type or void
+ * then an empty array is returned.
+ *
+ * @return an array with the fields declared in the class represented by
+ * this class.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getFields()
+ * @since Android 1.0
+ */
+ public Field[] getDeclaredFields() throws SecurityException {
+ checkDeclaredMemberAccess();
+
+ // Return a copy of the private (to the package) array.
+ Field[] fields = getClassCache().getDeclaredFields();
+ return ClassCache.deepCopy(fields);
+ }
+
+ /*
+ * Returns the list of fields without performing any security checks
+ * first. If no fields exist at all, an empty array is returned.
+ *
+ * @param clazz the class of interest
+ * @param publicOnly reflects whether we want only public fields or all of them
+ * @return the list of fields
+ */
+ static native Field[] getDeclaredFields(Class<?> clazz, boolean publicOnly);
+
+ /**
+ * Returns a {@code Method} object which represents the method matching the
+ * specified name and parameter types that is declared by the class
+ * represented by this {@code Class}.
+ *
+ * @param name
+ * the requested method's name.
+ * @param parameterTypes
+ * the parameter types of the requested method.
+ * @return the method described by {@code name} and {@code parameterTypes}.
+ * @throws NoSuchMethodException
+ * if the requested constructor can not be found.
+ * @throws NullPointerException
+ * if {@code name} is {@code null}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getMethod(String, Class...)
+ * @since Android 1.0
+ */
+ public Method getDeclaredMethod(String name, Class... parameterTypes)
+ throws NoSuchMethodException, SecurityException {
+ checkDeclaredMemberAccess();
+
+ Method[] methods = getClassCache().getDeclaredMethods();
+ Method method = findMethodByName(methods, name, parameterTypes);
+
+ /*
+ * Make a copy of the private (to the package) object, so that
+ * setAccessible() won't alter the private instance.
+ */
+ return REFLECT.clone(method);
+ }
+
+ /**
+ * Returns an array containing {@code Method} objects for all methods
+ * declared in the class represented by this {@code Class}. If there are no
+ * methods or if this {@code Class} represents an array class, a primitive
+ * type or void then an empty array is returned.
+ *
+ * @return an array with the methods declared in the class represented by
+ * this {@code Class}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getMethods()
+ * @since Android 1.0
+ */
+ public Method[] getDeclaredMethods() throws SecurityException {
+ checkDeclaredMemberAccess();
+
+ // Return a copy of the private (to the package) array.
+ Method[] methods = getClassCache().getDeclaredMethods();
+ return ClassCache.deepCopy(methods);
+ }
+
+ /**
+ * Returns the list of methods without performing any security checks
+ * first. If no methods exist, an empty array is returned.
+ */
+ static native Method[] getDeclaredMethods(Class<?> clazz, boolean publicOnly);
+
+ /**
+ * Gets the {@link ClassCache} for this instance.
+ *
+ * @return non-null; the cache object
+ */
+ /*package*/ ClassCache<T> getClassCache() {
+ /*
+ * Note: It is innocuous if two threads try to simultaneously
+ * create the cache, so we don't bother protecting against that.
+ */
+ ClassCache<T> cache = null;
+
+ if (cacheRef != null) {
+ cache = cacheRef.get();
+ }
+
+ if (cache == null) {
+ cache = new ClassCache<T>(this);
+ cacheRef = new SoftReference<ClassCache<T>>(cache);
+ }
+
+ return cache;
+ }
+
+ /**
+ * Returns the declaring {@code Class} of this {@code Class}. Returns
+ * {@code null} if the class is not a member of another class or if this
+ * {@code Class} represents an array class, a primitive type or void.
+ *
+ * @return the declaring {@code Class} or {@code null}.
+ * @since Android 1.0
+ */
+ native public Class<?> getDeclaringClass();
+
+ /**
+ * Returns the enclosing {@code Class} of this {@code Class}. If there is no
+ * enclosing class the method returns {@code null}.
+ *
+ * @return the enclosing {@code Class} or {@code null}.
+ * @since Android 1.0
+ */
+ native public Class<?> getEnclosingClass();
+
+ /**
+ * Gets the enclosing {@code Constructor} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ *
+ * @return the enclosing {@code Constructor} instance or {@code null}.
+ * @since Android 1.0
+ */
+ native public Constructor<?> getEnclosingConstructor();
+
+ /**
+ * Gets the enclosing {@code Method} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ *
+ * @return the enclosing {@code Method} instance or {@code null}.
+ * @since Android 1.0
+ */
+ native public Method getEnclosingMethod();
+
+ /**
+ * Gets the {@code enum} constants associated with this {@code Class}.
+ * Returns {@code null} if this {@code Class} does not represent an {@code
+ * enum} type.
+ *
+ * @return an array with the {@code enum} constants or {@code null}.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public T[] getEnumConstants() {
+ if (isEnum()) {
+ checkPublicMemberAccess();
+ T[] values = getClassCache().getEnumValuesInOrder();
+
+ // Copy the private (to the package) array.
+ return (T[]) values.clone();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@code Field} object which represents the public field with the
+ * specified name. This method first searches the class C represented by
+ * this {@code Class}, then the interfaces implemented by C and finally the
+ * superclasses of C.
+ *
+ * @param name
+ * the name of the requested field.
+ * @return the public field specified by {@code name}.
+ * @throws NoSuchFieldException
+ * if the field can not be found.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredField(String)
+ * @since Android 1.0
+ */
+ public Field getField(String name) throws NoSuchFieldException, SecurityException {
+ checkPublicMemberAccess();
+
+ Field[] fields = getClassCache().getAllPublicFields();
+ Field field = findFieldByName(fields, name);
+
+ /*
+ * Make a copy of the private (to the package) object, so that
+ * setAccessible() won't alter the private instance.
+ */
+ return REFLECT.clone(field);
+ }
+
+ /**
+ * Returns an array containing {@code Field} objects for all public fields
+ * for the class C represented by this {@code Class}. Fields may be declared
+ * in C, the interfaces it implements or in the superclasses of C. The
+ * elements in the returned array are in no particular order.
+ * <p>
+ * If there are no public fields or if this class represents an array class,
+ * a primitive type or {@code void} then an empty array is returned.
+ * </p>
+ *
+ * @return an array with the public fields of the class represented by this
+ * {@code Class}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredFields()
+ * @since Android 1.0
+ */
+ public Field[] getFields() throws SecurityException {
+ checkPublicMemberAccess();
+
+ // Return a copy of the private (to the package) array.
+ Field[] fields = getClassCache().getAllPublicFields();
+ return ClassCache.deepCopy(fields);
+ }
+
+ /**
+ * Gets the {@link Type}s of the interfaces that this {@code Class} directly
+ * implements. If the {@code Class} represents a primitive type or {@code
+ * void} then an empty array is returned.
+ *
+ * @return an array of {@link Type} instances directly implemented by the
+ * class represented by this {@code class}.
+ * @since Android 1.0
+ */
+ public Type[] getGenericInterfaces() {
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForClass(this, getSignatureAttribute());
+ return Types.getClonedTypeArray(parser.interfaceTypes);
+ }
+
+ /**
+ * Gets the {@code Type} that represents the superclass of this {@code
+ * class}.
+ *
+ * @return an instance of {@code Type} representing the superclass.
+ * @since Android 1.0
+ */
+ public Type getGenericSuperclass() {
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForClass(this, getSignatureAttribute());
+ return Types.getType(parser.superclassType);
+ }
+
+ /**
+ * Returns an array of {@code Class} objects that match the interfaces
+ * specified in the {@code implements} declaration of the class represented
+ * by this {@code Class}. The order of the elements in the array is
+ * identical to the order in the original class declaration. If the class
+ * does not implement any interfaces, an empty array is returned.
+ *
+ * @return an array with the interfaces of the class represented by this
+ * class.
+ * @since Android 1.0
+ */
+ public native Class[] getInterfaces();
+ // BEGIN android-note
+ // trying to get closer to the RI which returns a raw class array.
+ // copied from newer version of harmony
+ // END android-note
+
+ // Changed to raw type to be closer to the RI
+ /**
+ * Returns a {@code Method} object which represents the public method with
+ * the specified name and parameter types. This method first searches the
+ * class C represented by this {@code Class}, then the superclasses of C and
+ * finally the interfaces implemented by C and finally the superclasses of C
+ * for a method with matching name.
+ *
+ * @param name
+ * the requested method's name.
+ * @param parameterTypes
+ * the parameter types of the requested method.
+ * @return the public field specified by {@code name}.
+ * @throws NoSuchMethodException
+ * if the method can not be found.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredMethod(String, Class...)
+ * @since Android 1.0
+ */
+ public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException,
+ SecurityException {
+ checkPublicMemberAccess();
+
+ Method[] methods = getClassCache().getAllPublicMethods();
+ Method method = findMethodByName(methods, name, parameterTypes);
+
+ /*
+ * Make a copy of the private (to the package) object, so that
+ * setAccessible() won't alter the private instance.
+ */
+ return REFLECT.clone(method);
+ }
+
+ /**
+ * Returns an array containing {@code Method} objects for all public methods
+ * for the class C represented by this {@code Class}. Methods may be
+ * declared in C, the interfaces it implements or in the superclasses of C.
+ * The elements in the returned array are in no particular order.
+ * <p>
+ * If there are no public methods or if this {@code Class} represents a
+ * primitive type or {@code void} then an empty array is returned.
+ * </p>
+ *
+ * @return an array with the methods of the class represented by this
+ * {@code Class}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ * @see #getDeclaredMethods()
+ * @since Android 1.0
+ */
+ public Method[] getMethods() throws SecurityException {
+ checkPublicMemberAccess();
+
+ // Return a copy of the private (to the package) array.
+ Method[] methods = getClassCache().getAllPublicMethods();
+ return ClassCache.deepCopy(methods);
+ }
+
+ /**
+ * Performs the security checks regarding the access of a public
+ * member of this {@code Class}.
+ *
+ * <p><b>Note:</b> Because of the {@code getCallingClassLoader2()}
+ * check, this method must be called exactly one level deep into a
+ * public method on this instance.</p>
+ */
+ /*package*/ void checkPublicMemberAccess() {
+ SecurityManager smgr = System.getSecurityManager();
+
+ if (smgr != null) {
+ smgr.checkMemberAccess(this, Member.PUBLIC);
+
+ ClassLoader calling = VMStack.getCallingClassLoader2();
+ ClassLoader current = getClassLoader();
+
+ if (calling != null && !calling.isAncestorOf(current)) {
+ smgr.checkPackageAccess(this.getPackage().getName());
+ }
+ }
+ }
+
+ /**
+ * Performs the security checks regarding the access of a declared
+ * member of this {@code Class}.
+ *
+ * <p><b>Note:</b> Because of the {@code getCallingClassLoader2()}
+ * check, this method must be called exactly one level deep into a
+ * public method on this instance.</p>
+ */
+ private void checkDeclaredMemberAccess() {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkMemberAccess(this, Member.DECLARED);
+
+ ClassLoader calling = VMStack.getCallingClassLoader2();
+ ClassLoader current = getClassLoader();
+
+ if (calling != null && !calling.isAncestorOf(current)) {
+ smgr.checkPackageAccess(this.getPackage().getName());
+ }
+ }
+ }
+
+ /**
+ * Returns an integer that represents the modifiers of the class represented
+ * by this {@code Class}. The returned value is a combination of bits
+ * defined by constants in the {@link Modifier} class.
+ *
+ * @return the modifiers of the class represented by this {@code Class}.
+ * @since Android 1.0
+ */
+ public int getModifiers() {
+ return getModifiers(this, false);
+ }
+
+ /*
+ * Return the modifiers for the given class.
+ *
+ * @param clazz the class of interest
+ * @ignoreInnerClassesAttrib determines whether we look for and use the
+ * flags from an "inner class" attribute
+ */
+ private static native int getModifiers(Class<?> clazz, boolean ignoreInnerClassesAttrib);
+
+ /**
+ * Returns the name of the class represented by this {@code Class}. For a
+ * description of the format which is used, see the class definition of
+ * {@link Class}.
+ *
+ * @return the name of the class represented by this {@code Class}.
+ * @since Android 1.0
+ */
+ public native String getName();
+
+ /**
+ * Returns the simple name of the class represented by this {@code Class} as
+ * defined in the source code. If there is no name (that is, the class is
+ * anonymous) then an empty string is returned. If the receiver is an array
+ * then the name of the underlying type with square braces appended (for
+ * example {@code &quot;Integer[]&quot;}) is returned.
+ *
+ * @return the simple name of the class represented by this {@code Class}.
+ * @since Android 1.0
+ */
+ public String getSimpleName() {
+ if (isArray()) {
+ return getComponentType().getSimpleName() + "[]";
+ }
+
+ String name = getName();
+
+ if (isAnonymousClass()) {
+ return "";
+ }
+
+ if (isMemberClass() || isLocalClass()) {
+ return getInnerClassName();
+ }
+
+ int dot = name.lastIndexOf('.');
+ if (dot != -1) {
+ return name.substring(dot + 1);
+ }
+
+ return name;
+ }
+
+ /*
+ * Returns the simple name of a member or local class, or null otherwise.
+ *
+ * @return The name.
+ */
+ private native String getInnerClassName();
+
+ /**
+ * Returns the {@code ProtectionDomain} of the class represented by this
+ * class.
+ * <p>
+ * Note: In order to conserve space in an embedded target like Android, we
+ * allow this method to return {@code null} for classes in the system
+ * protection domain (that is, for system classes). System classes are
+ * always given full permissions (that is, AllPermission). This can not be
+ * changed through the {@link java.security.Policy} class.
+ * </p>
+ *
+ * @return the {@code ProtectionDomain} of the class represented by this
+ * class.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow member
+ * access.
+ */
+ public ProtectionDomain getProtectionDomain() {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ // Security check is independent of calling class loader.
+ smgr.checkPermission(new RuntimePermission("getProtectionDomain"));
+ }
+
+ return pd;
+ }
+
+ /**
+ * Returns the URL of the resource specified by {@code resName}. The mapping
+ * between the resource name and the URL is managed by the class' class
+ * loader.
+ *
+ * @param resName
+ * the name of the resource.
+ * @return the requested resource's {@code URL} object or {@code null} if
+ * the resource can not be found.
+ * @see ClassLoader
+ * @since Android 1.0
+ */
+ public URL getResource(String resName) {
+ // Get absolute resource name, but without the leading slash
+ if (resName.startsWith("/")) {
+ resName = resName.substring(1);
+ } else {
+ String pkg = getName();
+ int dot = pkg.lastIndexOf('.');
+ if (dot != -1) {
+ pkg = pkg.substring(0, dot).replace('.', '/');
+ } else {
+ pkg = "";
+ }
+
+ resName = pkg + "/" + resName;
+ }
+
+ // Delegate to proper class loader
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ return loader.getResource(resName);
+ } else {
+ return ClassLoader.getSystemResource(resName);
+ }
+ }
+
+ /**
+ * Returns a read-only stream for the contents of the resource specified by
+ * {@code resName}. The mapping between the resource name and the stream is
+ * managed by the class' class loader.
+ *
+ * @param resName
+ * the name of the resource.
+ * @return a stream for the requested resource or {@code null} if no
+ * resource with the specified name can be found.
+ * @see ClassLoader
+ * @since Android 1.0
+ */
+ public InputStream getResourceAsStream(String resName) {
+ // Get absolute resource name, but without the leading slash
+ if (resName.startsWith("/")) {
+ resName = resName.substring(1);
+ } else {
+ String pkg = getName();
+ int dot = pkg.lastIndexOf('.');
+ if (dot != -1) {
+ pkg = pkg.substring(0, dot).replace('.', '/');
+ } else {
+ pkg = "";
+ }
+
+ resName = pkg + "/" + resName;
+ }
+
+ // Delegate to proper class loader
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ return loader.getResourceAsStream(resName);
+ } else {
+ return ClassLoader.getSystemResourceAsStream(resName);
+ }
+ }
+
+ /**
+ * Returns the signers for the class represented by this {@code Class} or
+ * {@code null} if either there are no signers or this {@code Class}
+ * represents a primitive type or void.
+ *
+ * @return the signers of the class represented by this {@code Class}.
+ * @since Android 1.0
+ */
+ public Object[] getSigners() {
+ // TODO Delegate this to class loader somehow? What are these signers?
+ return null;
+ }
+
+ /**
+ * Returns the {@code Class} object which represents the superclass of the
+ * class represented by this {@code Class}. If this {@code Class} represents
+ * the {@code Object} class, a primitive type, an interface or void then the
+ * method returns {@code null}. If this {@code Class} represents an array
+ * class then the {@code Object} class is returned.
+ *
+ * @return the superclass of the class represented by this {@code Class}.
+ * @since Android 1.0
+ */
+ public native Class<? super T> getSuperclass();
+
+ /**
+ * Returns an array containing {@code TypeVariable} objects for type
+ * variables declared by the generic class represented by this {@code
+ * Class}. Returns an empty array if the class is not generic.
+ *
+ * @return an array with the type variables of the class represented by this
+ * class.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized TypeVariable<Class<T>>[] getTypeParameters() {
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForClass(this, getSignatureAttribute());
+ return parser.formalTypeParameters.clone();
+ }
+
+ /**
+ * Indicates whether this {@code Class} represents an annotation class.
+ *
+ * @return {@code true} if this {@code Class} represents an annotation
+ * class; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isAnnotation() {
+ final int ACC_ANNOTATION = 0x2000; // not public in reflect.Modifiers
+ int mod = getModifiers(this, true);
+ return (mod & ACC_ANNOTATION) != 0;
+ }
+
+ /**
+ * Indicates whether the specified annotation is present for the class
+ * represented by this {@code Class}.
+ *
+ * @param annotationClass
+ * the annotation to look for.
+ * @return {@code true} if the class represented by this {@code Class} is
+ * annotated with {@code annotationClass}; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+ return getAnnotation(annotationClass) != null;
+ }
+
+ /**
+ * Indicates whether the class represented by this {@code Class} is
+ * anonymously declared.
+ *
+ * @return {@code true} if the class represented by this {@code Class} is
+ * anonymous; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ native public boolean isAnonymousClass();
+
+ /**
+ * Indicates whether the class represented by this {@code Class} is an array
+ * class.
+ *
+ * @return {@code true} if the class represented by this {@code Class} is an
+ * array class; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isArray() {
+ return getComponentType() != null;
+ }
+
+ /**
+ * Indicates whether the specified class type can be converted to the class
+ * represented by this {@code Class}. Conversion may be done via an identity
+ * conversion or a widening reference conversion (if either the receiver or
+ * the argument represent primitive types, only the identity conversion
+ * applies).
+ *
+ * @param cls
+ * the class to check.
+ * @return {@code true} if {@code cls} can be converted to the class
+ * represented by this {@code Class}; {@code false} otherwise.
+ * @throws NullPointerException
+ * if {@code cls} is {@code null}.
+ * @since Android 1.0
+ */
+ public native boolean isAssignableFrom(Class<?> cls);
+
+ /**
+ * Indicates whether the class represented by this {@code Class} is an
+ * {@code enum}.
+ *
+ * @return {@code true} if the class represented by this {@code Class} is an
+ * {@code enum}; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isEnum() {
+ return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class);
+ }
+
+ /**
+ * Indicates whether the specified object can be cast to the class
+ * represented by this {@code Class}. This is the runtime version of the
+ * {@code instanceof} operator.
+ *
+ * @param object
+ * the object to check.
+ * @return {@code true} if {@code object} can be cast to the type
+ * represented by this {@code Class}; {@code false} if {@code
+ * object} is {@code null} or cannot be cast.
+ * @since Android 1.0
+ */
+ public native boolean isInstance(Object object);
+
+ /**
+ * Indicates whether this {@code Class} represents an interface.
+ *
+ * @return {@code true} if this {@code Class} represents an interface;
+ * {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public native boolean isInterface();
+
+ /**
+ * Indicates whether the class represented by this {@code Class} is defined
+ * locally.
+ *
+ * @return {@code true} if the class represented by this {@code Class} is
+ * defined locally; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isLocalClass() {
+ boolean enclosed = (getEnclosingMethod() != null ||
+ getEnclosingConstructor() != null);
+ return enclosed && !isAnonymousClass();
+ }
+
+ /**
+ * Indicates whether the class represented by this {@code Class} is a member
+ * class.
+ *
+ * @return {@code true} if the class represented by this {@code Class} is a
+ * member class; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isMemberClass() {
+ return getDeclaringClass() != null;
+ }
+
+ /**
+ * Indicates whether this {@code Class} represents a primitive type.
+ *
+ * @return {@code true} if this {@code Class} represents a primitive type;
+ * {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public native boolean isPrimitive();
+
+ /**
+ * Indicates whether this {@code Class} represents a synthetic type.
+ *
+ * @return {@code true} if this {@code Class} represents a synthetic type;
+ * {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isSynthetic() {
+ final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifiers
+ int mod = getModifiers(this, true);
+ return (mod & ACC_SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns a new instance of the class represented by this {@code Class},
+ * created by invoking the default (that is, zero-argument) constructor. If
+ * there is no such constructor, or if the creation fails (either because of
+ * a lack of available memory or because an exception is thrown by the
+ * constructor), an {@code InstantiationException} is thrown. If the default
+ * constructor exists but is not accessible from the context where this
+ * method is invoked, an {@code IllegalAccessException} is thrown.
+ *
+ * @return a new instance of the class represented by this {@code Class}.
+ * @throws IllegalAccessException
+ * if the default constructor is not visible.
+ * @throws InstantiationException
+ * if the instance can not be created.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow creating
+ * new instances.
+ * @since Android 1.0
+ */
+ public T newInstance() throws IllegalAccessException,
+ InstantiationException {
+ checkPublicMemberAccess();
+ return newInstanceImpl();
+ }
+
+ private native T newInstanceImpl() throws IllegalAccessException,
+ InstantiationException;
+
+ @Override
+ public String toString() {
+ if (isPrimitive()) {
+ return getSimpleName().toLowerCase();
+ } else {
+ return (isInterface() ? "interface " : "class ") + getName();
+ }
+ }
+
+ /**
+ * Returns the {@code Package} of which the class represented by this
+ * {@code Class} is a member. Returns {@code null} if no {@code Package}
+ * object was created by the class loader of the class.
+ *
+ * @return Package the {@code Package} of which this {@code Class} is a
+ * member or {@code null}.
+ * @since Android 1.0
+ */
+ public Package getPackage() {
+ // TODO This might be a hack, but the VM doesn't have the necessary info.
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ String name = getName();
+ int dot = name.lastIndexOf('.');
+ return (dot != -1 ? ClassLoader.getPackage(loader, name.substring(0, dot)) : null);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the assertion status for the class represented by this {@code
+ * Class}. Assertion is enabled / disabled based on the class loader,
+ * package or class default at runtime.
+ *
+ * @return the assertion status for the class represented by this {@code
+ * Class}.
+ * @since Android 1.0
+ */
+ public native boolean desiredAssertionStatus();
+
+ /**
+ * Casts this {@code Class} to represent a subclass of the specified class.
+ * If successful, this {@code Class} is returned; otherwise a {@code
+ * ClassCastException} is thrown.
+ *
+ * @param clazz
+ * the required type.
+ * @return this {@code Class} cast as a subclass of the given type.
+ * @throws ClassCastException
+ * if this {@code Class} cannot be cast to the specified type.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public <U> Class<? extends U> asSubclass(Class<U> clazz) {
+ if (clazz.isAssignableFrom(this)) {
+ return (Class<? extends U>)this;
+ }
+ throw new ClassCastException();
+ }
+
+ /**
+ * Casts the specified object to the type represented by this {@code Class}.
+ * If the object is {@code null} then the result is also {@code null}.
+ *
+ * @param obj
+ * the object to cast.
+ * @return the object that has been cast.
+ * @throws ClassCastException
+ * if the object cannot be cast to the specified type.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public T cast(Object obj) {
+ if (obj == null) {
+ return null;
+ } else if (this.isInstance(obj)) {
+ return (T)obj;
+ }
+ throw new ClassCastException();
+ }
+
+ /**
+ * Set the "accessible" flag of the given object, without doing any
+ * access checks.
+ *
+ * <p><b>Note:</b> This method is implemented in native code, and,
+ * as such, is less efficient than using {@link ClassCache#REFLECT}
+ * to achieve the same goal. This method exists solely to help
+ * bootstrap the reflection bridge.</p>
+ *
+ * @param ao non-null; the object to modify
+ * @param flag the new value for the accessible flag
+ */
+ /*package*/ static native void setAccessibleNoCheck(AccessibleObject ao,
+ boolean flag);
+
+ /**
+ * Copies two arrays into one. Assumes that the destination array is large
+ * enough.
+ *
+ * @param result the destination array
+ * @param head the first source array
+ * @param tail the second source array
+ * @return the destination array, that is, result
+ */
+ private static <T extends Object> T[] arraycopy(T[] result, T[] head, T[] tail) {
+ System.arraycopy(head, 0, result, 0, head.length);
+ System.arraycopy(tail, 0, result, head.length, tail.length);
+ return result;
+ }
+
+ /**
+ * This must be provided by the vm vendor, as it is used by other provided
+ * class implementations in this package. This method is used by
+ * SecurityManager.classDepth(), and getClassContext() which use the
+ * parameters (-1, false) and SecurityManager.classLoaderDepth(),
+ * currentClassLoader(), and currentLoadedClass() which use the parameters
+ * (-1, true). Walk the stack and answer an array containing the maxDepth
+ * most recent classes on the stack of the calling thread. Starting with the
+ * caller of the caller of getStackClasses(), return an array of not more
+ * than maxDepth Classes representing the classes of running methods on the
+ * stack (including native methods). Frames representing the VM
+ * implementation of java.lang.reflect are not included in the list. If
+ * stopAtPrivileged is true, the walk will terminate at any frame running
+ * one of the following methods: <code><ul>
+ * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object;</li>
+ * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;</li>
+ * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li>
+ * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li>
+ * </ul></code> If one of the doPrivileged methods is found, the walk terminate
+ * and that frame is NOT included in the returned array. Notes:
+ * <ul>
+ * <li>This method operates on the defining classes of methods on stack.
+ * NOT the classes of receivers.</li>
+ * <li>The item at index zero in the result array describes the caller of
+ * the caller of this method.</li>
+ * </ul>
+ *
+ * @param maxDepth
+ * maximum depth to walk the stack, -1 for the entire stack
+ * @param stopAtPrivileged
+ * stop at privileged classes
+ * @return the array of the most recent classes on the stack
+ */
+ static final Class<?>[] getStackClasses(int maxDepth, boolean stopAtPrivileged) {
+ return VMStack.getClasses(maxDepth, stopAtPrivileged);
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/ClassCache.java b/luni-kernel/src/main/java/java/lang/ClassCache.java
new file mode 100644
index 0000000..5ea6992
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ClassCache.java
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import org.apache.harmony.kernel.vm.LangAccess;
+import org.apache.harmony.kernel.vm.ReflectionAccess;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+
+/**
+ * Cache of per-class data, meant to help the performance of reflection
+ * methods.
+ *
+ * <p><b>Note:</b> None of the methods perform access checks. It is up
+ * to the (package internal) clients of this code to perform such
+ * checks as necessary.</p>
+ *
+ * <p><b>Also Note:</b> None of the returned array values are
+ * protected in any way. It is up to the (again, package internal)
+ * clients of this code to protect the arrays if they should ever
+ * escape the package.</p>
+ */
+/*package*/ class ClassCache<T> {
+ // TODO: Add caching for constructors and fields.
+
+ /** non-null; comparator used for enumerated values */
+ private static final EnumComparator ENUM_COMPARATOR =
+ new EnumComparator();
+
+ /** non-null; reflection access bridge */
+ /*package*/ static final ReflectionAccess REFLECT = getReflectionAccess();
+
+ /** non-null; class that this instance represents */
+ private final Class<T> clazz;
+
+ /** null-ok; list of all declared methods */
+ private volatile Method[] declaredMethods;
+
+ /** null-ok; list of all public declared methods */
+ private volatile Method[] declaredPublicMethods;
+
+ /** null-ok; list of all methods, both direct and inherited */
+ private volatile Method[] allMethods;
+
+ /** null-ok; list of all public methods, both direct and inherited */
+ private volatile Method[] allPublicMethods;
+
+ /** null-ok; list of all declared fields */
+ private volatile Field[] declaredFields;
+
+ /** null-ok; list of all public declared fields */
+ private volatile Field[] declaredPublicFields;
+
+ /** null-ok; list of all fields, both direct and inherited */
+ private volatile Field[] allFields;
+
+ /** null-ok; list of all public fields, both direct and inherited */
+ private volatile Field[] allPublicFields;
+
+ /**
+ * null-ok; array of enumerated values in their original order, if this
+ * instance's class is an enumeration
+ */
+ private volatile T[] enumValuesInOrder;
+
+ /**
+ * null-ok; array of enumerated values sorted by name, if this
+ * instance's class is an enumeration
+ */
+ private volatile T[] enumValuesByName;
+
+ static {
+ /*
+ * Provide access to this package from java.util as part of
+ * bootstrap. TODO: See if this can be removed in favor of the
+ * simpler mechanism below. (That is, see if EnumSet will be
+ * happy calling LangAccess.getInstance().)
+ */
+ Field field;
+
+ try {
+ field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP");
+ REFLECT.setAccessibleNoCheck(field, true);
+ } catch (NoSuchFieldException ex) {
+ // This shouldn't happen because the field is in fact defined.
+ throw new AssertionError(ex);
+ }
+
+ try {
+ field.set(null, LangAccessImpl.THE_ONE);
+ } catch (IllegalAccessException ex) {
+ // This shouldn't happen because we made the field accessible.
+ throw new AssertionError(ex);
+ }
+
+ // Also set up the bootstrap-classpath-wide access mechanism.
+ LangAccess.setInstance(LangAccessImpl.THE_ONE);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param clazz non-null; class that this instance represents
+ */
+ /*package*/ ClassCache(Class<T> clazz) {
+ if (clazz == null) {
+ throw new NullPointerException("clazz == null");
+ }
+
+ this.clazz = clazz;
+ this.declaredMethods = null;
+ this.declaredPublicMethods = null;
+ this.allMethods = null;
+ this.allPublicMethods = null;
+ this.enumValuesInOrder = null;
+ this.enumValuesByName = null;
+ this.declaredFields = null;
+ this.declaredPublicFields = null;
+ this.allFields = null;
+ this.allPublicFields = null;
+ }
+
+ /**
+ * Gets the list of all declared methods.
+ *
+ * @return non-null; the list of all declared methods
+ */
+ public Method[] getDeclaredMethods() {
+ if (declaredMethods == null) {
+ declaredMethods = Class.getDeclaredMethods(clazz, false);
+ }
+
+ return declaredMethods;
+ }
+
+ /**
+ * Gets the list of all declared public methods.
+ *
+ * @return non-null; the list of all declared public methods
+ */
+ public Method[] getDeclaredPublicMethods() {
+ if (declaredPublicMethods == null) {
+ declaredPublicMethods = Class.getDeclaredMethods(clazz, true);
+ }
+
+ return declaredPublicMethods;
+ }
+
+ /**
+ * Gets either the list of declared methods or the list of declared
+ * public methods.
+ *
+ * @param publicOnly whether to only return public methods
+ */
+ public Method[] getDeclaredMethods(boolean publicOnly) {
+ return publicOnly ? getDeclaredPublicMethods() : getDeclaredMethods();
+ }
+
+ /**
+ * Gets the list of all methods, both directly
+ * declared and inherited.
+ *
+ * @return non-null; the list of all methods
+ */
+ public Method[] getAllMethods() {
+ if (allMethods == null) {
+ allMethods = getFullListOfMethods(false);
+ }
+
+ return allMethods;
+ }
+
+ /**
+ * Gets the list of all public methods, both directly
+ * declared and inherited.
+ *
+ * @return non-null; the list of all public methods
+ */
+ public Method[] getAllPublicMethods() {
+ if (allPublicMethods == null) {
+ allPublicMethods = getFullListOfMethods(true);
+ }
+
+ return allPublicMethods;
+ }
+
+ /*
+ * Returns the list of methods without performing any security checks
+ * first. This includes the methods inherited from superclasses. If no
+ * methods exist at all, an empty array is returned.
+ *
+ * @param publicOnly reflects whether we want only public methods
+ * or all of them
+ * @return the list of methods
+ */
+ private Method[] getFullListOfMethods(boolean publicOnly) {
+ ArrayList<Method> methods = new ArrayList<Method>();
+ HashSet<String> seen = new HashSet<String>();
+
+ findAllMethods(clazz, methods, seen, publicOnly);
+
+ return methods.toArray(new Method[methods.size()]);
+ }
+
+ /**
+ * Collects the list of methods without performing any security checks
+ * first. This includes the methods inherited from superclasses and from
+ * all implemented interfaces. The latter may also implement multiple
+ * interfaces, so we (potentially) recursively walk through a whole tree of
+ * classes. If no methods exist at all, an empty array is returned.
+ *
+ * @param clazz non-null; class to inspect
+ * @param methods non-null; the target list to add the results to
+ * @param seen non-null; a set of signatures we've already seen
+ * @param publicOnly reflects whether we want only public methods
+ * or all of them
+ */
+ private static void findAllMethods(Class<?> clazz,
+ ArrayList<Method> methods, HashSet<String> seen,
+ boolean publicOnly) {
+ StringBuilder builder = new StringBuilder();
+ Class<?> origClass = clazz;
+
+ // Traverse class and superclasses, get rid of dupes by signature
+ while (clazz != null) {
+ Method[] declaredMethods =
+ clazz.getClassCache().getDeclaredMethods(publicOnly);
+ int length = declaredMethods.length;
+ if (length != 0) {
+ for (Method method : declaredMethods) {
+ builder.setLength(0);
+ builder.append(method.getName());
+ builder.append('(');
+ Class<?>[] types = method.getParameterTypes();
+ if (types.length != 0) {
+ builder.append(types[0].getName());
+ for (int j = 1; j < types.length; j++) {
+ builder.append(',');
+ builder.append(types[j].getName());
+ }
+ }
+ builder.append(')');
+
+ String signature = builder.toString();
+ if (!seen.contains(signature)) {
+ methods.add(method);
+ seen.add(signature);
+ }
+ }
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+
+ // Traverse all interfaces, and do the same recursively.
+ Class<?>[] interfaces = origClass.getInterfaces();
+ for (Class<?> intf : interfaces) {
+ findAllMethods(intf, methods, seen, publicOnly);
+ }
+ }
+
+ /**
+ * Finds and returns a method with a given name and signature. Use
+ * this with one of the method lists returned by instances of this class.
+ *
+ * @param list non-null; the list of methods to search through
+ * @param parameterTypes non-null; the formal parameter list
+ * @return non-null; the matching method
+ * @throws NoSuchMethodException thrown if the method does not exist
+ */
+ public static Method findMethodByName(Method[] list, String name,
+ Class<?>[] parameterTypes) throws NoSuchMethodException {
+ if (name == null) {
+ throw new NullPointerException("Method name must not be null.");
+ }
+ for (int i = list.length - 1; i >= 0; i--) {
+ Method method = list[i];
+ if (method.getName().equals(name)
+ && compareClassLists(
+ method.getParameterTypes(), parameterTypes)) {
+ return method;
+ }
+ }
+
+ throw new NoSuchMethodException(name);
+ }
+
+ /**
+ * Compares two class lists for equality. Empty and
+ * <code>null</code> lists are considered equal. This is useful
+ * for matching methods and constructors.
+ *
+ * <p>TODO: Take into account assignment compatibility?</p>
+ *
+ * @param a null-ok; the first list of types
+ * @param b null-ok; the second list of types
+ * @return true if and only if the lists are equal
+ */
+ public static boolean compareClassLists(Class<?>[] a, Class<?>[] b) {
+ if (a == null) {
+ return (b == null) || (b.length == 0);
+ }
+
+ int length = a.length;
+
+ if (b == null) {
+ return (length == 0);
+ }
+
+ if (length != b.length) {
+ return false;
+ }
+
+ for (int i = length - 1; i >= 0; i--) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Makes a deep copy of the given array of methods. This is useful
+ * when handing out arrays from the public API.
+ *
+ * <p><b>Note:</b> In such cases, it is insufficient to just make
+ * a shallow copy of the array, since method objects aren't
+ * immutable due to the existence of {@link
+ * AccessibleObject#setAccessible}.</p>
+ *
+ * @param orig non-null; array to copy
+ * @return non-null; a deep copy of the given array
+ */
+ public static Method[] deepCopy(Method[] orig) {
+ int length = orig.length;
+ Method[] result = new Method[length];
+
+ for (int i = length - 1; i >= 0; i--) {
+ result[i] = REFLECT.clone(orig[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the list of all declared fields.
+ *
+ * @return non-null; the list of all declared fields
+ */
+ public Field[] getDeclaredFields() {
+ if (declaredFields == null) {
+ declaredFields = Class.getDeclaredFields(clazz, false);
+ }
+
+ return declaredFields;
+ }
+
+ /**
+ * Gets the list of all declared public fields.
+ *
+ * @return non-null; the list of all declared public fields
+ */
+ public Field[] getDeclaredPublicFields() {
+ if (declaredPublicFields == null) {
+ declaredPublicFields = Class.getDeclaredFields(clazz, true);
+ }
+
+ return declaredPublicFields;
+ }
+
+ /**
+ * Gets either the list of declared fields or the list of declared
+ * public fields.
+ *
+ * @param publicOnly whether to only return public fields
+ */
+ public Field[] getDeclaredFields(boolean publicOnly) {
+ return publicOnly ? getDeclaredPublicFields() : getDeclaredFields();
+ }
+
+ /**
+ * Gets the list of all fields, both directly
+ * declared and inherited.
+ *
+ * @return non-null; the list of all fields
+ */
+ public Field[] getAllFields() {
+ if (allFields == null) {
+ allFields = getFullListOfFields(false);
+ }
+
+ return allFields;
+ }
+
+ /**
+ * Gets the list of all public fields, both directly
+ * declared and inherited.
+ *
+ * @return non-null; the list of all public fields
+ */
+ public Field[] getAllPublicFields() {
+ if (allPublicFields == null) {
+ allPublicFields = getFullListOfFields(true);
+ }
+
+ return allPublicFields;
+ }
+
+ /*
+ * Returns the list of fields without performing any security checks
+ * first. This includes the fields inherited from superclasses. If no
+ * fields exist at all, an empty array is returned.
+ *
+ * @param publicOnly reflects whether we want only public fields
+ * or all of them
+ * @return the list of fields
+ */
+ private Field[] getFullListOfFields(boolean publicOnly) {
+ ArrayList<Field> fields = new ArrayList<Field>();
+ HashSet<String> seen = new HashSet<String>();
+
+ findAllfields(clazz, fields, seen, publicOnly);
+
+ return fields.toArray(new Field[fields.size()]);
+ }
+
+ /**
+ * Collects the list of fields without performing any security checks
+ * first. This includes the fields inherited from superclasses and from
+ * all implemented interfaces. The latter may also implement multiple
+ * interfaces, so we (potentially) recursively walk through a whole tree of
+ * classes. If no fields exist at all, an empty array is returned.
+ *
+ * @param clazz non-null; class to inspect
+ * @param fields non-null; the target list to add the results to
+ * @param seen non-null; a set of signatures we've already seen
+ * @param publicOnly reflects whether we want only public fields
+ * or all of them
+ */
+ private static void findAllfields(Class<?> clazz,
+ ArrayList<Field> fields, HashSet<String> seen,
+ boolean publicOnly) {
+
+ // Traverse class and superclasses, get rid of dupes by signature
+ while (clazz != null) {
+ Field[] declaredFields =
+ clazz.getClassCache().getDeclaredFields(publicOnly);
+ for (Field field : declaredFields) {
+ String signature = field.toString();
+ if (!seen.contains(signature)) {
+ fields.add(field);
+ seen.add(signature);
+ }
+ }
+
+ // Traverse all interfaces, and do the same recursively.
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for (Class<?> intf : interfaces) {
+ findAllfields(intf, fields, seen, publicOnly);
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ /**
+ * Finds and returns a field with a given name and signature. Use
+ * this with one of the field lists returned by instances of this class.
+ *
+ * @param list non-null; the list of fields to search through
+ * @return non-null; the matching field
+ * @throws NoSuchFieldException thrown if the field does not exist
+ */
+ public static Field findFieldByName(Field[] list, String name)
+ throws NoSuchFieldException {
+ if (name == null) {
+ throw new NullPointerException("Field name must not be null.");
+ }
+ for (int i = 0; i < list.length; i++) {
+ Field field = list[i];
+ if (field.getName().equals(name)) {
+ return field;
+ }
+ }
+
+ throw new NoSuchFieldException(name);
+ }
+
+ /**
+ * Makes a deep copy of the given array of fields. This is useful
+ * when handing out arrays from the public API.
+ *
+ * <p><b>Note:</b> In such cases, it is insufficient to just make
+ * a shallow copy of the array, since field objects aren't
+ * immutable due to the existence of {@link
+ * AccessibleObject#setAccessible}.</p>
+ *
+ * @param orig non-null; array to copy
+ * @return non-null; a deep copy of the given array
+ */
+ public static Field[] deepCopy(Field[] orig) {
+ int length = orig.length;
+ Field[] result = new Field[length];
+
+ for (int i = length - 1; i >= 0; i--) {
+ result[i] = REFLECT.clone(orig[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the enumerated value with a given name.
+ *
+ * @param name non-null; name of the value
+ * @return null-ok; the named enumerated value or <code>null</code>
+ * if this instance's class doesn't have such a value (including
+ * if this instance isn't in fact an enumeration)
+ */
+ @SuppressWarnings("unchecked")
+ public T getEnumValue(String name) {
+ Enum[] values = (Enum[]) getEnumValuesByName();
+
+ if (values == null) {
+ return null;
+ }
+
+ // Binary search.
+
+ int min = 0;
+ int max = values.length - 1;
+
+ while (min <= max) {
+ /*
+ * The guessIdx calculation is equivalent to ((min + max)
+ * / 2) but won't go wonky when min and max are close to
+ * Integer.MAX_VALUE.
+ */
+ int guessIdx = min + ((max - min) >> 1);
+ Enum guess = values[guessIdx];
+ int cmp = name.compareTo(guess.name());
+
+ if (cmp < 0) {
+ max = guessIdx - 1;
+ } else if (cmp > 0) {
+ min = guessIdx + 1;
+ } else {
+ return (T) guess;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the array of enumerated values, sorted by name.
+ *
+ * @return null-ok; the value array, or <code>null</code> if this
+ * instance's class isn't in fact an enumeration
+ */
+ public T[] getEnumValuesByName() {
+ if (enumValuesByName == null) {
+ T[] values = getEnumValuesInOrder();
+
+ if (values != null) {
+ values = (T[]) values.clone();
+ Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR);
+
+ /*
+ * Note: It's only safe (concurrency-wise) to set the
+ * instance variable after the array is properly sorted.
+ */
+ enumValuesByName = values;
+ }
+ }
+
+ return enumValuesByName;
+ }
+
+ /**
+ * Gets the array of enumerated values, in their original declared
+ * order.
+ *
+ * @return null-ok; the value array, or <code>null</code> if this
+ * instance's class isn't in fact an enumeration
+ */
+ public T[] getEnumValuesInOrder() {
+ if ((enumValuesInOrder == null) && clazz.isEnum()) {
+ enumValuesInOrder = callEnumValues();
+ }
+
+ return enumValuesInOrder;
+ }
+
+ /**
+ * Calls the static method <code>values()</code> on this
+ * instance's class, which is presumed to be a properly-formed
+ * enumeration class, using proper privilege hygiene.
+ *
+ * @return non-null; the array of values as reported by
+ * <code>value()</code>
+ */
+ @SuppressWarnings("unchecked")
+ private T[] callEnumValues() {
+ Method method;
+
+ try {
+ Method[] methods = getDeclaredPublicMethods();
+ method = findMethodByName(methods, "values", (Class[]) null);
+ method = REFLECT.accessibleClone(method);
+ } catch (NoSuchMethodException ex) {
+ // This shouldn't happen if the class is a well-formed enum.
+ throw new UnsupportedOperationException(ex);
+ }
+
+ try {
+ return (T[]) method.invoke((Object[]) null);
+ } catch (IllegalAccessException ex) {
+ // This shouldn't happen because the method is "accessible."
+ throw new Error(ex);
+ } catch (InvocationTargetException ex) {
+ Throwable te = ex.getTargetException();
+ if (te instanceof RuntimeException) {
+ throw (RuntimeException) te;
+ } else if (te instanceof Error) {
+ throw (Error) te;
+ } else {
+ throw new Error(te);
+ }
+ }
+ }
+
+ /**
+ * Gets the reflection access object. This uses reflection to do
+ * so. My head is spinning.
+ *
+ * @return non-null; the reflection access object
+ */
+ private static ReflectionAccess getReflectionAccess() {
+ /*
+ * Note: We can't do AccessibleObject.class.getCache() to
+ * get the cache, since that would cause a circularity in
+ * initialization. So instead, we do a direct call into the
+ * native side.
+ */
+ Method[] methods =
+ Class.getDeclaredMethods(AccessibleObject.class, false);
+
+ try {
+ Method method = findMethodByName(methods, "getReflectionAccess",
+ (Class[]) null);
+ Class.setAccessibleNoCheck(method, true);
+ return (ReflectionAccess) method.invoke((Object[]) null);
+ } catch (NoSuchMethodException ex) {
+ /*
+ * This shouldn't happen because the method
+ * AccessibleObject.getReflectionAccess() really is defined
+ * in this module.
+ */
+ throw new Error(ex);
+ } catch (IllegalAccessException ex) {
+ // This shouldn't happen because the method is "accessible."
+ throw new Error(ex);
+ } catch (InvocationTargetException ex) {
+ throw new Error(ex);
+ }
+ }
+
+ /**
+ * Comparator class for enumerated values. It compares strictly
+ * by name.
+ */
+ private static class EnumComparator implements Comparator<Enum<?>> {
+ public int compare(Enum<?> e1, Enum<?> e2) {
+ return e1.name().compareTo(e2.name());
+ }
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/ClassLoader.java b/luni-kernel/src/main/java/java/lang/ClassLoader.java
new file mode 100644
index 0000000..822fade
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ClassLoader.java
@@ -0,0 +1,1139 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.security.ProtectionDomain;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.HashMap;
+
+import dalvik.system.PathClassLoader;
+import dalvik.system.VMStack;
+
+/**
+ * Loads classes and resources from a repository. One or more class loaders are
+ * installed at runtime. These are consulted whenever the runtime system needs a
+ * specific class that is not yet available in-memory. Typically, class loaders
+ * are grouped into a tree where child class loaders delegate all requests to
+ * parent class loaders. Only if the parent class loader cannot satisfy the
+ * request, the child class loader itself tries to handle it.
+ * <p>
+ * {@code ClassLoader} is an abstract class that implements the common
+ * infrastructure required by all class loaders. Android provides several
+ * concrete implementations of the class, with
+ * {@link dalvik.system.PathClassLoader} being the one typically used. Other
+ * applications may implement subclasses of {@code ClassLoader} to provide
+ * special ways for loading classes.
+ * </p>
+ *
+ * @since Android 1.0
+ * @see Class
+ */
+public abstract class ClassLoader {
+
+ // BEGIN android-note
+ /*
+ * Because of a potential class initialization race between ClassLoader and
+ * java.lang.System, reproducible when using JDWP with "suspend=y", we defer
+ * creation of the system class loader until first use. We use a static
+ * inner class to get synchronization at init time without having to sync on
+ * every access.
+ */
+ // END android-note
+ /**
+ * The 'System' ClassLoader - the one that is responsible for loading
+ * classes from the classpath. It is not equal to the bootstrap class loader -
+ * that one handles the built-in classes.
+ *
+ * @see #getSystemClassLoader()
+ */
+ static private class SystemClassLoader {
+ public static ClassLoader loader = ClassLoader.createSystemClassLoader();
+ }
+
+ /**
+ * The parent ClassLoader.
+ */
+ private ClassLoader parent;
+
+ /**
+ * The packages known to the class loader.
+ */
+ private Map<String, Package> packages = new HashMap<String, Package>();
+
+ /**
+ * Create the system class loader. Note this is NOT the bootstrap class
+ * loader (which is managed by the VM). We use a null value for the parent
+ * to indicate that the bootstrap loader is our parent.
+ */
+ private static ClassLoader createSystemClassLoader() {
+ String classPath = System.getProperty("java.class.path", ".");
+
+ // String[] paths = classPath.split(":");
+ // URL[] urls = new URL[paths.length];
+ // for (int i = 0; i < paths.length; i++) {
+ // try {
+ // urls[i] = new URL("file://" + paths[i]);
+ // }
+ // catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ // }
+ //
+ // return new java.net.URLClassLoader(urls, null);
+
+ // TODO Make this a java.net.URLClassLoader once we have those?
+ return new PathClassLoader(classPath, BootClassLoader.getInstance());
+ }
+
+ /**
+ * Returns the system class loader. This is the parent for new
+ * {@code ClassLoader} instances and is typically the class loader used to
+ * start the application. If a security manager is present and the caller's
+ * class loader is neither {@code null} nor the same as or an ancestor of
+ * the system class loader, then this method calls the security manager's
+ * checkPermission method with a RuntimePermission("getClassLoader")
+ * permission to ensure that it is ok to access the system class loader. If
+ * not, a {@code SecurityException} is thrown.
+ *
+ * @return the system class loader.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow access to
+ * the system class loader.
+ * @since Android 1.0
+ */
+ public static ClassLoader getSystemClassLoader() {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ ClassLoader caller = VMStack.getCallingClassLoader();
+ if (caller != null && !caller.isAncestorOf(SystemClassLoader.loader)) {
+ smgr.checkPermission(new RuntimePermission("getClassLoader"));
+ }
+ }
+
+ return SystemClassLoader.loader;
+ }
+
+ /**
+ * Finds the URL of the resource with the specified name. The system class
+ * loader's resource lookup algorithm is used to find the resource.
+ *
+ * @return the {@code URL} object for the requested resource or {@code null}
+ * if the resource can not be found.
+ * @param resName
+ * the name of the resource to find.
+ * @see Class#getResource
+ * @since Android 1.0
+ */
+ public static URL getSystemResource(String resName) {
+ return SystemClassLoader.loader.getResource(resName);
+ }
+
+ /**
+ * Returns an enumeration of URLs for the resource with the specified name.
+ * The system class loader's resource lookup algorithm is used to find the
+ * resource.
+ *
+ * @return an enumeration of {@code URL} objects containing the requested
+ * resources.
+ * @param resName
+ * the name of the resource to find.
+ * @throws IOException
+ * if an I/O error occurs.
+ * @since Android 1.0
+ */
+ public static Enumeration<URL> getSystemResources(String resName) throws IOException {
+ return SystemClassLoader.loader.getResources(resName);
+ }
+
+ /**
+ * Returns a stream for the resource with the specified name. The system
+ * class loader's resource lookup algorithm is used to find the resource.
+ * Basically, the contents of the java.class.path are searched in order,
+ * looking for a path which matches the specified resource.
+ *
+ * @return a stream for the resource or {@code null}.
+ * @param resName
+ * the name of the resource to find.
+ * @see Class#getResourceAsStream
+ * @since Android 1.0
+ */
+ public static InputStream getSystemResourceAsStream(String resName) {
+ return SystemClassLoader.loader.getResourceAsStream(resName);
+ }
+
+ /**
+ * Constructs a new instance of this class with the system class loader as
+ * its parent.
+ *
+ * @throws SecurityException
+ * if a security manager exists and it does not allow the
+ * creation of a new {@code ClassLoader}.
+ * @since Android 1.0
+ */
+ protected ClassLoader() {
+ this(getSystemClassLoader(), false);
+ }
+
+ /**
+ * Constructs a new instance of this class with the specified class loader
+ * as its parent.
+ *
+ * @param parentLoader
+ * The {@code ClassLoader} to use as the new class loader's
+ * parent.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow the
+ * creation of new a new {@code ClassLoader}.
+ * @since Android 1.0
+ */
+ protected ClassLoader(ClassLoader parentLoader) {
+ this(parentLoader, false);
+ }
+
+ /*
+ * constructor for the BootClassLoader which needs parent to be null.
+ */
+ ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkCreateClassLoader();
+ }
+
+ if (parentLoader == null && !nullAllowed) {
+ throw new NullPointerException(
+ "Parent ClassLoader may not be null");
+ }
+
+ parent = parentLoader;
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format.
+ *
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ * @deprecated Use {@link #defineClass(String, byte[], int, int)}
+ * @since Android 1.0
+ */
+ @Deprecated
+ protected final Class<?> defineClass(byte[] classRep, int offset, int length)
+ throws ClassFormatError {
+
+ return VMClassLoader.defineClass(this, classRep, offset, length, null);
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format.
+ *
+ * @param className
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ * @since Android 1.0
+ */
+ protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length)
+ throws ClassFormatError {
+
+ // TODO Define a default ProtectionDomain on first use
+ return defineClass(className, classRep, offset, length, null);
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format and assigns the specified protection
+ * domain to the new class. If the provided protection domain is
+ * {@code null} then a default protection domain is assigned to the class.
+ *
+ * @param className
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @param protectionDomain
+ * the protection domain to assign to the loaded class, may be
+ * {@code null}.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ * @throws NoClassDefFoundError
+ * if {@code className} is not equal to the name of the class
+ * contained in {@code classRep}.
+ * @since Android 1.0
+ */
+ protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length,
+ ProtectionDomain protectionDomain) throws java.lang.ClassFormatError {
+
+ return VMClassLoader.defineClass(this, className, classRep, offset, length,
+ protectionDomain);
+ }
+
+ /**
+ * Defines a new class with the specified name, byte code from the byte
+ * buffer and the optional protection domain. If the provided protection
+ * domain is {@code null} then a default protection domain is assigned to
+ * the class.
+ *
+ * @param name
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param b
+ * the byte buffer containing the byte code of the new class.
+ * @param protectionDomain
+ * the protection domain to assign to the loaded class, may be
+ * {@code null}.
+ * @return the {@code Class} object created from the data in {@code b}.
+ * @throws ClassFormatError
+ * if {@code b} does not contain a valid class.
+ * @throws NoClassDefFoundError
+ * if {@code className} is not equal to the name of the class
+ * contained in {@code b}.
+ * @since Android 1.0
+ */
+ protected final Class<?> defineClass(String name, ByteBuffer b,
+ ProtectionDomain protectionDomain) throws ClassFormatError {
+
+ byte[] temp = new byte[b.remaining()];
+ b.get(temp);
+ return defineClass(name, temp, 0, temp.length, protectionDomain);
+ }
+
+ /**
+ * Overridden by subclasses, throws a {@code ClassNotFoundException} by
+ * default. This method is called by {@code loadClass} after the parent
+ * {@code ClassLoader} has failed to find a loaded class of the same name.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object that is found.
+ * @throws ClassNotFoundException
+ * if the class cannot be found.
+ * @since Android 1.0
+ */
+ protected Class<?> findClass(String className) throws ClassNotFoundException {
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * Returns the class with the specified name if it has already been loaded
+ * by the virtual machine or {@code null} if it has not yet been loaded.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object or {@code null} if the requested class
+ * has not been loaded.
+ * @since Android 1.0
+ */
+ protected final Class<?> findLoadedClass(String className) {
+ ClassLoader loader;
+ if (this == BootClassLoader.getInstance())
+ loader = null;
+ else
+ loader = this;
+ return VMClassLoader.findLoadedClass(loader, className);
+ }
+
+ /**
+ * Finds the class with the specified name, loading it using the system
+ * class loader if necessary.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object with the requested {@code className}.
+ * @throws ClassNotFoundException
+ * if the class can not be found.
+ * @since Android 1.0
+ */
+ protected final Class<?> findSystemClass(String className) throws ClassNotFoundException {
+ return Class.forName(className, false, getSystemClassLoader());
+ }
+
+ /**
+ * Returns this class loader's parent.
+ *
+ * @return this class loader's parent or {@code null}.
+ * @throws SecurityException
+ * if a security manager exists and it does not allow to
+ * retrieve the parent class loader.
+ * @since Android 1.0
+ */
+ public final ClassLoader getParent() {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkPermission(new RuntimePermission("getClassLoader"));
+ }
+
+ return parent;
+ }
+
+ /**
+ * Returns the URL of the resource with the specified name. This
+ * implementation first tries to use the parent class loader to find the
+ * resource; if this fails then {@link #findResource(String)} is called to
+ * find the requested resource.
+ *
+ * @param resName
+ * the name of the resource to find.
+ * @return the {@code URL} object for the requested resource or {@code null}
+ * if either the resource can not be found or a security manager
+ * does not allow to access the resource.
+ * @see Class#getResource
+ * @since Android 1.0
+ */
+ public URL getResource(String resName) {
+ URL resource = null;
+
+ resource = parent.getResource(resName);
+
+ if (resource == null) {
+ resource = findResource(resName);
+ }
+
+ return resource;
+ }
+
+ /**
+ * Returns an enumeration of URLs for the resource with the specified name.
+ * This implementation first uses this class loader's parent to find the
+ * resource, then it calls {@link #findResources(String)} to get additional
+ * URLs. The returned enumeration contains the {@code URL} objects of both
+ * find operations.
+ *
+ * @return an enumeration of {@code URL} objects for the requested resource.
+ * @param resName
+ * the name of the resource to find.
+ * @throws IOException
+ * if an I/O error occurs.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public Enumeration<URL> getResources(String resName) throws IOException {
+
+ Enumeration first = parent.getResources(resName);
+ Enumeration second = findResources(resName);
+
+ return new TwoEnumerationsInOne(first, second);
+ }
+
+ /**
+ * Returns a stream for the resource with the specified name. See
+ * {@link #getResource(String)} for a description of the lookup algorithm
+ * used to find the resource.
+ *
+ * @return a stream for the resource or {@code null} if either the resource
+ * can not be found or a security manager does not allow to access
+ * the resource.
+ * @param resName
+ * the name of the resource to find.
+ * @see Class#getResourceAsStream
+ * @since Android 1.0
+ */
+ public InputStream getResourceAsStream(String resName) {
+ try {
+ URL url = getResource(resName);
+ if (url != null) {
+ return url.openStream();
+ }
+ } catch (IOException ex) {
+ // Don't want to see the exception.
+ }
+
+ return null;
+ }
+
+ /**
+ * Loads the class with the specified name. Invoking this method is
+ * equivalent to calling {@code loadClass(className, false)}.
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, the
+ * second parameter of {@link #loadClass(String, boolean)} is ignored
+ * anyway.
+ * </p>
+ *
+ * @return the {@code Class} object.
+ * @param className
+ * the name of the class to look for.
+ * @throws ClassNotFoundException
+ * if the class can not be found.
+ * @since Android 1.0
+ */
+ public Class<?> loadClass(String className) throws ClassNotFoundException {
+ return loadClass(className, false);
+ }
+
+ /**
+ * Loads the class with the specified name, optionally linking it after
+ * loading. The following steps are performed:
+ * <ol>
+ * <li> Call {@link #findLoadedClass(String)} to determine if the requested
+ * class has already been loaded.</li>
+ * <li>If the class has not yet been loaded: Invoke this method on the
+ * parent class loader.</li>
+ * <li>If the class has still not been loaded: Call
+ * {@link #findClass(String)} to find the class.</li>
+ * </ol>
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, the
+ * {@code resolve} parameter is ignored; classes are never linked.
+ * </p>
+ *
+ * @return the {@code Class} object.
+ * @param className
+ * the name of the class to look for.
+ * @param resolve
+ * Indicates if the class should be resolved after loading. This
+ * parameter is ignored on the Android reference implementation;
+ * classes are not resolved.
+ * @throws ClassNotFoundException
+ * if the class can not be found.
+ * @since Android 1.0
+ */
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ Class<?> clazz = findLoadedClass(className);
+
+ if (clazz == null) {
+ try {
+ clazz = parent.loadClass(className, false);
+ } catch (ClassNotFoundException e) {
+ // Don't want to see this.
+ }
+
+ if (clazz == null) {
+ clazz = findClass(className);
+ }
+ }
+
+ return clazz;
+ }
+
+ /**
+ * Forces a class to be linked (initialized). If the class has already been
+ * linked this operation has no effect.
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, this
+ * method has no effect.
+ * </p>
+ *
+ * @param clazz
+ * the class to link.
+ * @since Android 1.0
+ */
+ protected final void resolveClass(Class<?> clazz) {
+ // no-op, doesn't make sense on android.
+ }
+
+ /**
+ * Indicates whether this class loader is the system class loader. This
+ * method must be provided by the virtual machine vendor, as it is used by
+ * other provided class implementations in this package. A sample
+ * implementation of this method is provided by the reference
+ * implementation. This method is used by
+ * SecurityManager.classLoaderDepth(), currentClassLoader() and
+ * currentLoadedClass(). Returns true if the receiver is a system class
+ * loader.
+ * <p>
+ * Note that this method has package visibility only. It is defined here to
+ * avoid the security manager check in getSystemClassLoader, which would be
+ * required to implement this method anywhere else.
+ * </p>
+ *
+ * @return {@code true} if the receiver is a system class loader
+ * @see Class#getClassLoaderImpl()
+ */
+ final boolean isSystemClassLoader() {
+ return false;
+ }
+
+ /**
+ * <p>
+ * Returns true if the receiver is ancestor of another class loader. It also
+ * returns true if the two class loader are equal.
+ * </p>
+ * <p>
+ * Note that this method has package visibility only. It is defined here to
+ * avoid the security manager check in getParent, which would be required to
+ * implement this method anywhere else. The method is also required in other
+ * places where class loaders are accesses.
+ * </p>
+ *
+ * @param child
+ * A child candidate
+ * @return {@code true} if the receiver is ancestor of, or equal to,
+ * the parameter
+ */
+ final boolean isAncestorOf(ClassLoader child) {
+ for (ClassLoader current = child; current != null;
+ current = child.parent) {
+ if (current == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Finds the URL of the resource with the specified name. This
+ * implementation just returns {@code null}; it should be overridden in
+ * subclasses.
+ *
+ * @param resName
+ * the name of the resource to find.
+ * @return the {@code URL} object for the requested resource.
+ * @since Android 1.0
+ */
+ protected URL findResource(String resName) {
+ return null;
+ }
+
+ /**
+ * Finds an enumeration of URLs for the resource with the specified name.
+ * This implementation just returns an empty {@code Enumeration}; it should
+ * be overridden in subclasses.
+ *
+ * @param resName
+ * the name of the resource to find.
+ * @return an enumeration of {@code URL} objects for the requested resource.
+ * @throws IOException
+ * if an I/O error occurs.
+ * @since Android 1.0
+ */
+ @SuppressWarnings( {
+ "unchecked", "unused"
+ })
+ protected Enumeration<URL> findResources(String resName) throws IOException {
+ return EmptyEnumeration.getInstance();
+ }
+
+ /**
+ * Returns the absolute path of the native library with the specified name,
+ * or {@code null}. If this method returns {@code null} then the virtual
+ * machine searches the directories specified by the system property
+ * "java.library.path".
+ * <p>
+ * This implementation always returns {@code null}.
+ * </p>
+ *
+ * @param libName
+ * the name of the library to find.
+ * @return the absolute path of the library.
+ * @since Android 1.0
+ */
+ protected String findLibrary(String libName) {
+ return null;
+ }
+
+ /**
+ * Returns the package with the specified name. Package information is
+ * searched in this class loader.
+ *
+ * @param name
+ * the name of the package to find.
+ * @return the package with the requested name; {@code null} if the package
+ * can not be found.
+ * @since Android 1.0
+ */
+ protected Package getPackage(String name) {
+ synchronized (packages) {
+ Package p = packages.get(name);
+ return p;
+ }
+ }
+
+ /**
+ * Gets the package with the specified name, searching it in the specified
+ * class loader.
+ *
+ * @param loader
+ * the class loader to search the package in.
+ * @param name
+ * the name of the package to find.
+ * @return the package with the requested name; {@code null} if the package
+ * can not be found.
+ * @since Android 1.0
+ */
+ static Package getPackage(ClassLoader loader, String name) {
+ return loader.getPackage(name);
+ }
+
+ /**
+ * Returns all the packages known to this class loader.
+ *
+ * @return an array with all packages known to this class loader.
+ * @since Android 1.0
+ */
+ protected Package[] getPackages() {
+ synchronized (packages) {
+ Collection<Package> col = packages.values();
+ Package[] result = new Package[col.size()];
+ col.toArray(result);
+ return result;
+ }
+ }
+
+ /**
+ * Defines and returns a new {@code Package} using the specified
+ * information. If {@code sealBase} is {@code null}, the package is left
+ * unsealed. Otherwise, the package is sealed using this URL.
+ *
+ * @param name
+ * the name of the package.
+ * @param specTitle
+ * the title of the specification.
+ * @param specVersion
+ * the version of the specification.
+ * @param specVendor
+ * the vendor of the specification.
+ * @param implTitle
+ * the implementation title.
+ * @param implVersion
+ * the implementation version.
+ * @param implVendor
+ * the specification vendor.
+ * @param sealBase
+ * the URL used to seal this package or {@code null} to leave the
+ * package unsealed.
+ * @return the {@code Package} object that has been created.
+ * @throws IllegalArgumentException
+ * if a package with the specified name already exists.
+ * @since Android 1.0
+ */
+ protected Package definePackage(String name, String specTitle, String specVersion,
+ String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase)
+ throws IllegalArgumentException {
+
+ synchronized (packages) {
+ if (packages.containsKey(name)) {
+ throw new IllegalArgumentException("Package " + name + " already defined");
+ }
+
+ Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle,
+ implVersion, implVendor, sealBase);
+
+ packages.put(name, newPackage);
+
+ return newPackage;
+ }
+ }
+
+ /**
+ * Gets the signers of the specified class. This implementation returns
+ * {@code null}.
+ *
+ * @param c
+ * the {@code Class} object for which to get the signers.
+ * @return signers the signers of {@code c}.
+ * @since Android 1.0
+ */
+ final Object[] getSigners(Class<?> c) {
+ return null;
+ }
+
+ /**
+ * Sets the signers of the specified class. This implementation does
+ * nothing.
+ *
+ * @param c
+ * the {@code Class} object for which to set the signers.
+ * @param signers
+ * the signers for {@code c}.
+ * @since Android 1.0
+ */
+ protected final void setSigners(Class<?> c, Object[] signers) {
+ return;
+ }
+
+ /**
+ * <p>
+ * This must be provided by the VM vendor. It is used by
+ * SecurityManager.checkMemberAccess() with depth = 3. Note that
+ * checkMemberAccess() assumes the following stack when called:<br>
+ * </p>
+ *
+ * <pre>
+ * &lt; user code &amp;gt; &lt;- want this class
+ * Class.getDeclared*();
+ * Class.checkMemberAccess();
+ * SecurityManager.checkMemberAccess(); &lt;- current frame
+ * </pre>
+ *
+ * <p>
+ * Returns the ClassLoader of the method (including natives) at the
+ * specified depth on the stack of the calling thread. Frames representing
+ * the VM implementation of java.lang.reflect are not included in the list.
+ * </p>
+ * Notes:
+ * <ul>
+ * <li>This method operates on the defining classes of methods on stack.
+ * NOT the classes of receivers.</li>
+ * <li>The item at depth zero is the caller of this method</li>
+ * </ul>
+ *
+ * @param depth
+ * the stack depth of the requested ClassLoader
+ * @return the ClassLoader at the specified depth
+ */
+ static final ClassLoader getStackClassLoader(int depth) {
+ Class<?>[] stack = VMStack.getClasses(depth + 1, false);
+ if(stack.length < depth + 1) {
+ return null;
+ }
+ return stack[depth].getClassLoader();
+ }
+
+ /**
+ * This method must be provided by the VM vendor, as it is called by
+ * java.lang.System.loadLibrary(). System.loadLibrary() cannot call
+ * Runtime.loadLibrary() because this method loads the library using the
+ * ClassLoader of the calling method. Loads and links the library specified
+ * by the argument.
+ *
+ * @param libName
+ * the name of the library to load
+ * @param loader
+ * the classloader in which to load the library
+ * @throws UnsatisfiedLinkError
+ * if the library could not be loaded
+ * @throws SecurityException
+ * if the library was not allowed to be loaded
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ */
+ static void loadLibraryWithClassLoader(String libName, ClassLoader loader) {
+ return;
+ }
+
+ /**
+ * This method must be provided by the VM vendor, as it is called by
+ * java.lang.System.load(). System.load() cannot call Runtime.load() because
+ * the library is loaded using the ClassLoader of the calling method. Loads
+ * and links the library specified by the argument. No security check is
+ * done.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @param libName
+ * the name of the library to load
+ * @param loader
+ * the classloader in which to load the library
+ * @param libraryPath
+ * the library path to search, or null
+ * @throws UnsatisfiedLinkError
+ * if the library could not be loaded
+ */
+ static void loadLibraryWithPath(String libName, ClassLoader loader, String libraryPath) {
+ return;
+ }
+
+ /**
+ * Sets the assertion status of the class with the specified name.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @param cname
+ * the name of the class for which to set the assertion status.
+ * @param enable
+ * the new assertion status.
+ * @since Android 1.0
+ */
+ public void setClassAssertionStatus(String cname, boolean enable) {
+ return;
+ }
+
+ /**
+ * Sets the assertion status of the package with the specified name.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @param pname
+ * the name of the package for which to set the assertion status.
+ * @param enable
+ * the new assertion status.
+ * @since Android 1.0
+ */
+ public void setPackageAssertionStatus(String pname, boolean enable) {
+ return;
+ }
+
+ /**
+ * Sets the default assertion status for this class loader.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @param enable
+ * the new assertion status.
+ * @since Android 1.0
+ */
+ public void setDefaultAssertionStatus(boolean enable) {
+ return;
+ }
+
+ /**
+ * Sets the default assertion status for this class loader to {@code false}
+ * and removes any package default and class assertion status settings.
+ * <p>
+ * <strong>Note:</strong> This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @since Android 1.0
+ */
+ public void clearAssertionStatus() {
+ return;
+ }
+
+ /**
+ * Returns the assertion status of the named class Returns the assertion
+ * status of the class or nested class if it has been set. Otherwise returns
+ * the assertion status of its package or superpackage if that has been set.
+ * Otherwise returns the default assertion status. Returns 1 for enabled and
+ * 0 for disabled.
+ *
+ * @return the assertion status.
+ * @param cname
+ * the name of class.
+ */
+ boolean getClassAssertionStatus(String cname) {
+ return false;
+ }
+
+ /**
+ * Returns the assertion status of the named package Returns the assertion
+ * status of the named package or superpackage if that has been set.
+ * Otherwise returns the default assertion status. Returns 1 for enabled and
+ * 0 for disabled.
+ *
+ * @return the assertion status.
+ * @param pname
+ * the name of package.
+ */
+ boolean getPackageAssertionStatus(String pname) {
+ return false;
+ }
+
+ /**
+ * Returns the default assertion status
+ *
+ * @return the default assertion status.
+ */
+ boolean getDefaultAssertionStatus() {
+ return false;
+ }
+}
+
+/*
+ * Provides a helper class that combines two existing URL enumerations into one.
+ * It is required for the getResources() methods. Items are fetched from the
+ * first enumeration until it's empty, then from the second one.
+ */
+class TwoEnumerationsInOne implements Enumeration<URL> {
+
+ private Enumeration<URL> first;
+
+ private Enumeration<URL> second;
+
+ public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public boolean hasMoreElements() {
+ return first.hasMoreElements() || second.hasMoreElements();
+ }
+
+ public URL nextElement() {
+ if (first.hasMoreElements()) {
+ return first.nextElement();
+ } else {
+ return second.nextElement();
+ }
+ }
+
+}
+
+/**
+ * Provides an explicit representation of the boot class loader. It sits at the
+ * head of the class loader chain and delegates requests to the VM's internal
+ * class loading mechanism.
+ */
+class BootClassLoader extends ClassLoader {
+
+ static BootClassLoader instance;
+
+ public static BootClassLoader getInstance() {
+ if (instance == null) {
+ instance = new BootClassLoader();
+ }
+
+ return instance;
+ }
+
+ public BootClassLoader() {
+ super(null, true);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return VMClassLoader.loadClass(name, false);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return VMClassLoader.getResource(name);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected Enumeration<URL> findResources(String resName) throws IOException {
+ Enumeration<URL> result = VMClassLoader.getResources(resName);
+
+ // VMClassLoader doesn't keep the contract for getResources()
+ if (result == null) {
+ result = EmptyEnumeration.getInstance();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns package information for the given package. Unfortunately, the
+ * Android BootClassLoader doesn't really have this information, and as a
+ * non-secure ClassLoader, it isn't even required to, according to the spec.
+ * Yet, we want to provide it, in order to make all those hopeful callers of
+ * {@code myClass.getPackage().getName()} happy. Thus we construct a Package
+ * object the first time it is being requested and fill most of the fields
+ * with dummy values. The Package object is then put into the ClassLoader's
+ * Package cache, so we see the same one next time. We don't create Package
+ * objects for null arguments or for the default package.
+ * <p>
+ * There a limited chance that we end up with multiple Package objects
+ * representing the same package: It can happen when when a package is
+ * scattered across different JAR files being loaded by different
+ * ClassLoaders. Rather unlikely, and given that this whole thing is more or
+ * less a workaround, probably not worth the effort.
+ */
+ @Override
+ protected Package getPackage(String name) {
+ if (name != null && !"".equals(name)) {
+ synchronized (this) {
+ Package pack = super.getPackage(name);
+
+ if (pack == null) {
+ pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0",
+ "Unknown", null);
+ }
+
+ return pack;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public URL getResource(String resName) {
+ return findResource(resName);
+ }
+
+ @Override
+ protected Class<?> loadClass(String className, boolean resolve)
+ throws ClassNotFoundException {
+ Class<?> clazz = findLoadedClass(className);
+
+ if (clazz == null) {
+ clazz = findClass(className);
+ }
+
+ return clazz;
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String resName) throws IOException {
+ return findResources(resName);
+ }
+}
+
+/**
+ * TODO Open issues - Missing / empty methods - Signer stuff - Protection
+ * domains - Assertions
+ */
diff --git a/luni-kernel/src/main/java/java/lang/Compiler.java b/luni-kernel/src/main/java/java/lang/Compiler.java
new file mode 100644
index 0000000..cf41f06
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Compiler.java
@@ -0,0 +1,100 @@
+/*
+ * 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 java.lang;
+
+/**
+ * Placeholder class for environments which explicitly manage the action of a
+ * <em>Just In Time (JIT)</em> compiler. This class is usually implemented by
+ * the virtual machine vendor. The Android reference implementation does not
+ * (yet) contain such a JIT compiler, though other implementations may choose to
+ * provide one.
+ *
+ * @since Android 1.0
+ */
+public final class Compiler {
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Compiler(){
+ //do nothing
+ }
+
+ /**
+ * Executes an operation according to the specified command object. This
+ * method is the low-level interface to the JIT compiler. It may return any
+ * object or {@code null} if no JIT compiler is available.
+ *
+ * @param cmd
+ * the command object for the JIT compiler.
+ * @return the result of executing command or {@code null}.
+ * @since Android 1.0
+ */
+ public static Object command(Object cmd) {
+ return null;
+ }
+
+ /**
+ * Compiles the specified class using the JIT compiler and indicates if
+ * compilation has been successful.
+ *
+ * @param classToCompile
+ * java.lang.Class the class to JIT compile
+ * @return {@code true} if the compilation has been successful;
+ * {@code false} if it has failed or if there is no JIT compiler
+ * available.
+ * @since Android 1.0
+ */
+ public static boolean compileClass(Class<?> classToCompile) {
+ return false;
+ }
+
+ /**
+ * Compiles all classes whose name matches the specified name using the JIT
+ * compiler and indicates if compilation has been successful.
+ *
+ * @param nameRoot
+ * the string to match class names with.
+ * @return {@code true} if the compilation has been successful;
+ * {@code false} if it has failed or if there is no JIT compiler
+ * available.
+ * @since Android 1.0
+ */
+ public static boolean compileClasses(String nameRoot) {
+ return false;
+ }
+
+ /**
+ * Disables the JIT compiler.
+ *
+ * @since Android 1.0
+ */
+ public static void disable() {
+ return;
+ }
+
+ /**
+ * Enables the JIT compiler.
+ *
+ * @since Android 1.0
+ */
+ public static void enable() {
+ return;
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/LangAccessImpl.java b/luni-kernel/src/main/java/java/lang/LangAccessImpl.java
new file mode 100644
index 0000000..44d4aac
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/LangAccessImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import org.apache.harmony.kernel.vm.LangAccess;
+
+/**
+ * Implementation of bridge into <code>java.lang</code>.
+ */
+/*package*/ final class LangAccessImpl extends LangAccess {
+ /** non-null; unique instance of this class */
+ /*package*/ static final LangAccessImpl THE_ONE = new LangAccessImpl();
+
+ /**
+ * This class is not publicly instantiable. Use {@link #THE_ONE}.
+ */
+ private LangAccessImpl() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public <T> T[] getEnumValuesInOrder(Class<T> clazz) {
+ ClassCache<T> cache = clazz.getClassCache();
+ return cache.getEnumValuesInOrder();
+ }
+
+ /** {@inheritDoc} */
+ public void unpark(Thread thread) {
+ thread.unpark();
+ }
+
+ /** {@inheritDoc} */
+ public void parkFor(long nanos) {
+ Thread.currentThread().parkFor(nanos);
+ }
+
+ /** {@inheritDoc} */
+ public void parkUntil(long time) {
+ Thread.currentThread().parkUntil(time);
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/Object.java b/luni-kernel/src/main/java/java/lang/Object.java
new file mode 100644
index 0000000..4fef609
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Object.java
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+/**
+ * The root class of the Java class hierarchy. All non-primitive types
+ * (including arrays) inherit either directly or indirectly from this class.
+ * <p>
+ * {@code Object} provides some fundamental methods for accessing the
+ * {@link Class} of an object, getting its {@link #hashCode()}, or checking
+ * whether one object {@link #equals(Object)} another. The {@link #toString()}
+ * method can be used to convert an object reference into a printable string and
+ * is often overridden in subclasses.
+ * <p>
+ * The {@link #wait()} and {@link #notify()} methods provide a foundation for
+ * synchronization, acquiring and releasing an internal monitor associated with
+ * each {@code Object}.
+ *
+ * @since Android 1.0
+ */
+public class Object {
+
+ /**
+ * Constructs a new instance of {@code Object}.
+ *
+ * @since Android 1.0
+ */
+ public Object() {
+ }
+
+ /**
+ * Creates and returns a copy of this {@code Object}. The default
+ * implementation returns a so-called "shallow" copy: It creates a new
+ * instance of the same class and then copies the field values (including
+ * object references) from this instance to the new instance. A "deep" copy,
+ * in contrast, would also recursively clone nested objects. A subclass that
+ * needs to implement this kind of cloning should call {@code super.clone()}
+ * to create the new instance and then create deep copies of the nested,
+ * mutable objects.
+ *
+ * @return a copy of this object.
+ * @throws CloneNotSupportedException
+ * if this object's class does not implement the {@code
+ * Cloneable} interface.
+ * @since Android 1.0
+ */
+ protected Object clone() throws CloneNotSupportedException {
+ if (!(this instanceof Cloneable)) {
+ throw new CloneNotSupportedException("Class doesn't implement Cloneable");
+ }
+
+ return internalClone((Cloneable) this);
+ }
+
+ /*
+ * Native helper method for cloning.
+ */
+ private native Object internalClone(Cloneable o);
+
+ /**
+ * Compares this instance with the specified object and indicates if they
+ * are equal. In order to be equal, {@code o} must represent the same object
+ * as this instance using a class-specific comparison. The general contract
+ * is that this comparison should be both transitive and reflexive.
+ * <p>
+ * The implementation in {@code Object} returns {@code true} only if {@code
+ * o} is the exact same object as the receiver (using the == operator for
+ * comparison). Subclasses often implement {@code equals(Object)} so that
+ * it takes into account the two object's types and states.
+ * </p>
+ * <p>
+ * The general contract for the {@code equals(Object)} and {@link
+ * #hashCode()} methods is that if {@code equals} returns {@code true} for
+ * any two objects, then {@code hashCode()} must return the same value for
+ * these objects. This means that subclasses of {@code Object} usually
+ * override either both methods or none of them.
+ * </p>
+ *
+ * @param o
+ * the object to compare this instance with.
+ * @return {@code true} if the specified object is equal to this {@code
+ * Object}; {@code false} otherwise.
+ * @see #hashCode
+ * @since Android 1.0
+ */
+ public boolean equals(Object o) {
+ return this == o;
+ }
+
+ /**
+ * Is called before the object's memory is being reclaimed by the VM. This
+ * can only happen once the VM has detected, during a run of the garbage
+ * collector, that the object is no longer reachable by any thread of the
+ * running application.
+ * <p>
+ * The method can be used to free system resources or perform other cleanup
+ * before the object is garbage collected. The default implementation of the
+ * method is empty, which is also expected by the VM, but subclasses can
+ * override {@code finalize()} as required. Uncaught exceptions which are
+ * thrown during the execution of this method cause it to terminate
+ * immediately but are otherwise ignored.
+ * <p>
+ * Note that the VM does guarantee that {@code finalize()} is called at most
+ * once for any object, but it doesn't guarantee when (if at all) {@code
+ * finalize()} will be called. For example, object B's {@code finalize()}
+ * can delay the execution of object A's {@code finalize()} method and
+ * therefore it can delay the reclamation of A's memory. To be safe, use a
+ * {@link java.lang.ref.ReferenceQueue}, because it provides more control
+ * over the way the VM deals with references during garbage collection.
+ * </p>
+ *
+ * @throws Throwable
+ * any exception which is raised during finalization; these are
+ * ignored by the virtual machine.
+ * @since Android 1.0
+ */
+ protected void finalize() throws Throwable {
+ }
+
+ /**
+ * Returns the unique instance of {@link Class} which represents this
+ * object's class. Note that {@code getClass()} is a special case in that it
+ * actually returns {@code Class<? extends Foo>} where {@code Foo} is the
+ * erasure of the type of expression {@code getClass()} was called upon.
+ * <p>
+ * As an example, the following code actually compiles, although one might
+ * think it shouldn't:
+ * <p>
+ * <pre>
+ * List<Integer> l = new ArrayList<Integer>();
+ * Class<? extends List> c = l.getClass();
+ * </pre>
+ *
+ * @return this object's {@code Class} instance.
+ * @since Android 1.0
+ */
+ public final native Class<? extends Object> getClass();
+
+ /**
+ * Returns an integer hash code for this object. By contract, any two
+ * objects for which {@code equals(Object)} returns {@code true} must return
+ * the same hash code value. This means that subclasses of {@code Object}
+ * usually override both methods or neither method.
+ *
+ * @return this object's hash code.
+ * @see #equals
+ * @since Android 1.0
+ */
+ public native int hashCode();
+
+ /**
+ * Causes a thread which is waiting on this object's monitor (by means of
+ * calling one of the {@code wait()} methods) to be woken up. If more than
+ * one thread is waiting, one of them is chosen at the discretion of the
+ * virtual machine. The chosen thread will not run immediately. The thread
+ * that called {@code notify()} has to release the object's monitor first.
+ * Also, the chosen thread still has to compete against other threads that
+ * try to synchronize on the same object.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ * @since Android 1.0
+ */
+ public final native void notify();
+
+ /**
+ * Causes all threads which are waiting on this object's monitor (by means
+ * of calling one of the {@code wait()} methods) to be woken up. The threads
+ * will not run immediately. The thread that called {@code notify()} has to
+ * release the object's monitor first. Also, the threads still have to
+ * compete against other threads that try to synchronize on the same object.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @see #notify
+ * @see #wait()
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final native void notifyAll();
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * object. Subclasses are encouraged to override this method and provide an
+ * implementation that takes into account the object's type and data. The
+ * default implementation simply concatenates the class name, the '@' sign
+ * and a hexadecimal representation of the object's {@link #hashCode()},
+ * that is, it is equivalent to the following expression:
+ *
+ * <pre>
+ * getClass().getName() + '@' + Integer.toHexString(hashCode())
+ * </pre>
+ *
+ * @return a printable representation of this object.
+ * @since Android 1.0
+ */
+ public String toString() {
+ return getClass().getName() + '@' + Integer.toHexString(hashCode());
+ }
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object. This method can
+ * only be invoked by a thread which owns this object's monitor; see
+ * {@link #notify()} on how a thread can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ * @since Android 1.0
+ */
+ public final void wait() throws InterruptedException {
+ wait(0 ,0);
+ }
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object or until the
+ * specified timeout expires. This method can only be invoked by a thread
+ * which owns this object's monitor; see {@link #notify()} on how a thread
+ * can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @param millis
+ * the maximum time to wait in milliseconds.
+ * @throws IllegalArgumentException
+ * if {@code millis < 0}.
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ * @since Android 1.0
+ */
+ public final void wait(long millis) throws InterruptedException {
+ wait(millis, 0);
+ }
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object or until the
+ * specified timeout expires. This method can only be invoked by a thread
+ * that owns this object's monitor; see {@link #notify()} on how a thread
+ * can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @param millis
+ * the maximum time to wait in milliseconds.
+ * @param nanos
+ * the fraction of a millisecond to wait, specified in
+ * nanoseconds.
+ * @throws IllegalArgumentException
+ * if {@code millis < 0}, {@code nanos < 0} or {@code nanos >
+ * 999999}.
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ * @since Android 1.0
+ */
+ public final native void wait(long millis, int nanos) throws InterruptedException;
+}
diff --git a/luni-kernel/src/main/java/java/lang/Package.java b/luni-kernel/src/main/java/java/lang/Package.java
new file mode 100644
index 0000000..2817404
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Package.java
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import dalvik.system.VMStack;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.net.URL;
+
+/**
+ * Contains information about a Java package. This includes implementation and
+ * specification versions. Typically this information is retrieved from the
+ * manifest.
+ * <p>
+ * Packages are managed by class loaders. All classes loaded by the same loader
+ * from the same package share a {@code Package} instance.
+ * </p>
+ * @since Android 1.0
+ *
+ * @see java.lang.ClassLoader
+ */
+public class Package implements AnnotatedElement {
+
+ private final String name, specTitle, specVersion, specVendor, implTitle,
+ implVersion, implVendor;
+ private final URL sealBase;
+
+ Package(String name, String specTitle, String specVersion, String specVendor,
+ String implTitle, String implVersion, String implVendor, URL sealBase) {
+ this.name = name;
+ this.specTitle = specTitle;
+ this.specVersion = specVersion;
+ this.specVendor = specVendor;
+ this.implTitle = implTitle;
+ this.implVersion = implVersion;
+ this.implVendor = implVendor;
+ this.sealBase = sealBase;
+ }
+
+ /**
+ * Gets the annotation associated with the specified annotation type and
+ * this package, if present.
+ *
+ * @param annotationType
+ * the annotation type to look for.
+ * @return an instance of {@link Annotation} or {@code null}.
+ * @see java.lang.reflect.AnnotatedElement#getAnnotation(java.lang.Class)
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ Annotation[] list = getAnnotations();
+ for (int i = 0; i < list.length; i++) {
+ if (annotationType.isInstance(list[i])) {
+ return (T)list[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets all annotations associated with this package, if any.
+ *
+ * @return an array of {@link Annotation} instances, which may be empty.
+ * @see java.lang.reflect.AnnotatedElement#getAnnotations()
+ * @since Android 1.0
+ */
+ public Annotation[] getAnnotations() {
+ return getDeclaredAnnotations(this, true);
+ }
+
+ /**
+ * Gets all annotations directly declared on this package, if any.
+ *
+ * @return an array of {@link Annotation} instances, which may be empty.
+ * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations()
+ * @since Android 1.0
+ */
+ public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(this, false);
+ }
+
+ /*
+ * Returns the list of declared annotations of the given package.
+ * If no annotations exist, an empty array is returned.
+ *
+ * @param pkg the package of interest
+ * @param publicOnly reflects whether we want only public annotation or all
+ * of them.
+ * @return the list of annotations
+ */
+ // TODO(Google) Provide proper (native) implementation.
+ private static native Annotation[] getDeclaredAnnotations(Package pkg,
+ boolean publicOnly);
+
+ /**
+ * Indicates whether the specified annotation is present.
+ *
+ * @param annotationType
+ * the annotation type to look for.
+ * @return {@code true} if the annotation is present; {@code false}
+ * otherwise.
+ * @see java.lang.reflect.AnnotatedElement#isAnnotationPresent(java.lang.Class)
+ * @since Android 1.0
+ */
+ public boolean isAnnotationPresent(
+ Class<? extends Annotation> annotationType) {
+ return getAnnotation(annotationType) != null;
+ }
+
+ /**
+ * Returns the title of the implementation of this package, or {@code null}
+ * if this is unknown. The format of this string is unspecified.
+ *
+ * @return the implementation title, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getImplementationTitle() {
+ return implTitle;
+ }
+
+ /**
+ * Returns the name of the vendor or organization that provides this
+ * implementation of the package, or {@code null} if this is unknown. The
+ * format of this string is unspecified.
+ *
+ * @return the implementation vendor name, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getImplementationVendor() {
+ return implVendor;
+ }
+
+ /**
+ * Returns the version of the implementation of this package, or {@code
+ * null} if this is unknown. The format of this string is unspecified.
+ *
+ * @return the implementation version, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getImplementationVersion() {
+ return implVersion;
+ }
+
+ /**
+ * Returns the name of this package in the standard dot notation; for
+ * example: "java.lang".
+ *
+ * @return the name of this package.
+ * @since Android 1.0
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Attempts to locate the requested package in the caller's class loader. If
+ * no package information can be located, {@code null} is returned.
+ *
+ * @param packageName
+ * the name of the package to find.
+ * @return the requested package, or {@code null}.
+ * @see ClassLoader#getPackage(java.lang.String)
+ * @since Android 1.0
+ */
+ public static Package getPackage(String packageName) {
+ ClassLoader classloader = VMStack.getCallingClassLoader();
+ return classloader.getPackage(packageName);
+ }
+
+ /**
+ * Returns all the packages known to the caller's class loader.
+ *
+ * @return all the packages known to the caller's class loader.
+ * @see ClassLoader#getPackages
+ * @since Android 1.0
+ */
+ public static Package[] getPackages() {
+ ClassLoader classloader = VMStack.getCallingClassLoader();
+ return classloader.getPackages();
+ }
+
+ /**
+ * Returns the title of the specification this package implements, or
+ * {@code null} if this is unknown.
+ *
+ * @return the specification title, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getSpecificationTitle() {
+ return specTitle;
+ }
+
+ /**
+ * Returns the name of the vendor or organization that owns and maintains
+ * the specification this package implements, or {@code null} if this is
+ * unknown.
+ *
+ * @return the specification vendor name, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getSpecificationVendor() {
+ return specVendor;
+ }
+
+ /**
+ * Returns the version of the specification this package implements, or
+ * {@code null} if this is unknown. The version string is a sequence of
+ * non-negative integers separated by dots; for example: "1.2.3".
+ *
+ * @return the specification version string, may be {@code null}.
+ * @since Android 1.0
+ */
+ public String getSpecificationVersion() {
+ return specVersion;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Indicates whether this package's specification version is compatible with
+ * the specified version string. Version strings are compared by comparing
+ * each dot separated part of the version as an integer.
+ *
+ * @param version
+ * the version string to compare against.
+ * @return {@code true} if the package versions are compatible; {@code
+ * false} otherwise.
+ * @throws NumberFormatException
+ * if this package's version string or the one provided are not
+ * in the correct format.
+ * @since Android 1.0
+ */
+ public boolean isCompatibleWith(String version)
+ throws NumberFormatException {
+ String[] requested = version.split(".");
+ String[] provided = specVersion.split(".");
+
+ for (int i = 0; i < Math.min(requested.length, provided.length); i++) {
+ int reqNum = Integer.parseInt(requested[i]);
+ int provNum = Integer.parseInt(provided[i]);
+
+ if (reqNum > provNum) {
+ return false;
+ } else if (reqNum < provNum) {
+ return true;
+ }
+ }
+
+ if (requested.length > provided.length) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Indicates whether this package is sealed.
+ *
+ * @return {@code true} if this package is sealed; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isSealed() {
+ return sealBase != null;
+ }
+
+ /**
+ * Indicates whether this package is sealed with respect to the specified
+ * URL.
+ *
+ * @param url
+ * the URL to check.
+ * @return {@code true} if this package is sealed with {@code url}; {@code
+ * false} otherwise
+ * @since Android 1.0
+ */
+ public boolean isSealed(URL url) {
+ return sealBase != null && sealBase.sameFile(url);
+ }
+
+ @Override
+ public String toString() {
+ return "package " + name;
+ }
+}
+
diff --git a/luni-kernel/src/main/java/java/lang/ProcessManager.java b/luni-kernel/src/main/java/java/lang/ProcessManager.java
new file mode 100644
index 0000000..1e21b57
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ProcessManager.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * Manages child processes.
+ *
+ * <p>Harmony's native implementation (for comparison purposes):
+ * http://tinyurl.com/3ytwuq
+ */
+final class ProcessManager {
+
+ /**
+ * constant communicated from native code indicating that a
+ * child died, but it was unable to determine the status
+ */
+ private static final int WAIT_STATUS_UNKNOWN = -1;
+
+ /**
+ * constant communicated from native code indicating that there
+ * are currently no children to wait for
+ */
+ private static final int WAIT_STATUS_NO_CHILDREN = -2;
+
+ /**
+ * constant communicated from native code indicating that a wait()
+ * call returned -1 and set an undocumented (and hence unexpected) errno
+ */
+ private static final int WAIT_STATUS_STRANGE_ERRNO = -3;
+
+ /**
+ * Initializes native static state.
+ */
+ static native void staticInitialize();
+ static {
+ staticInitialize();
+ }
+
+ /**
+ * Map from pid to Process. We keep weak references to the Process objects
+ * and clean up the entries when no more external references are left. The
+ * process objects themselves don't require much memory, but file
+ * descriptors (associated with stdin/out/err in this case) can be
+ * a scarce resource.
+ */
+ private final Map<Integer, ProcessReference> processReferences
+ = new HashMap<Integer, ProcessReference>();
+
+ /** Keeps track of garbage-collected Processes. */
+ private final ProcessReferenceQueue referenceQueue
+ = new ProcessReferenceQueue();
+
+ private ProcessManager() {
+ // Spawn a thread to listen for signals from child processes.
+ Thread processThread = new Thread(ProcessManager.class.getName()) {
+ @Override
+ public void run() {
+ watchChildren();
+ }
+ };
+ processThread.setDaemon(true);
+ processThread.start();
+ }
+
+ /**
+ * Kills the process with the given ID.
+ *
+ * @parm pid ID of process to kill
+ */
+ private static native void kill(int pid) throws IOException;
+
+ /**
+ * Cleans up after garbage collected processes. Requires the lock on the
+ * map.
+ */
+ void cleanUp() {
+ ProcessReference reference;
+ while ((reference = referenceQueue.poll()) != null) {
+ synchronized (processReferences) {
+ processReferences.remove(reference.processId);
+ }
+ }
+ }
+
+ /**
+ * Listens for signals from processes and calls back to
+ * {@link #onExit(int,int)}.
+ */
+ native void watchChildren();
+
+ /**
+ * Called by {@link #watchChildren()} when a child process exits.
+ *
+ * @param pid ID of process that exited
+ * @param exitValue value the process returned upon exit
+ */
+ void onExit(int pid, int exitValue) {
+ ProcessReference processReference = null;
+
+ synchronized (processReferences) {
+ cleanUp();
+ if (pid >= 0) {
+ processReference = processReferences.remove(pid);
+ } else if (exitValue == WAIT_STATUS_NO_CHILDREN) {
+ if (processReferences.isEmpty()) {
+ /*
+ * There are no eligible children; wait for one to be
+ * added. The wait() will return due to the
+ * notifyAll() call below.
+ */
+ try {
+ processReferences.wait();
+ } catch (InterruptedException ex) {
+ // This should never happen.
+ throw new AssertionError("unexpected interrupt");
+ }
+ } else {
+ /*
+ * A new child was spawned just before we entered
+ * the synchronized block. We can just fall through
+ * without doing anything special and land back in
+ * the native wait().
+ */
+ }
+ } else {
+ // Something weird is happening; abort!
+ throw new AssertionError("unexpected wait() behavior");
+ }
+ }
+
+ if (processReference != null) {
+ ProcessImpl process = processReference.get();
+ if (process != null) {
+ process.setExitValue(exitValue);
+ }
+ }
+ }
+
+ /**
+ * Executes a native process. Fills in in, out, and err and returns the
+ * new process ID upon success.
+ */
+ static native int exec(String[] commands, String[] environment,
+ String workingDirectory, FileDescriptor in, FileDescriptor out,
+ FileDescriptor err) throws IOException;
+
+ /**
+ * Executes a process and returns an object representing it.
+ */
+ Process exec(String[] commands, String[] environment,
+ File workingDirectory) throws IOException {
+ FileDescriptor in = new FileDescriptor();
+ FileDescriptor out = new FileDescriptor();
+ FileDescriptor err = new FileDescriptor();
+
+ String workingPath = (workingDirectory == null)
+ ? null
+ : workingDirectory.getPath();
+
+ // Ensure onExit() doesn't access the process map before we add our
+ // entry.
+ synchronized (processReferences) {
+ int pid;
+ try {
+ pid = exec(commands, environment, workingPath, in, out, err);
+ } catch (IOException e) {
+ IOException wrapper = new IOException("Error running exec()."
+ + " Commands: " + Arrays.toString(commands)
+ + " Working Directory: " + workingDirectory
+ + " Environment: " + Arrays.toString(environment));
+ wrapper.initCause(e);
+ throw wrapper;
+ }
+ ProcessImpl process = new ProcessImpl(pid, in, out, err);
+ ProcessReference processReference
+ = new ProcessReference(process, referenceQueue);
+ processReferences.put(pid, processReference);
+
+ /*
+ * This will wake up the child monitor thread in case there
+ * weren't previously any children to wait on.
+ */
+ processReferences.notifyAll();
+
+ return process;
+ }
+ }
+
+ static class ProcessImpl extends Process {
+
+ /** Process ID. */
+ final int id;
+
+ final InputStream errorStream;
+
+ /** Reads output from process. */
+ final InputStream inputStream;
+
+ /** Sends output to process. */
+ final OutputStream outputStream;
+
+ /** The process's exit value. */
+ Integer exitValue = null;
+ final Object exitValueMutex = new Object();
+
+ ProcessImpl(int id, FileDescriptor in, FileDescriptor out,
+ FileDescriptor err) {
+ this.id = id;
+
+ this.errorStream = new ProcessInputStream(err);
+ this.inputStream = new ProcessInputStream(in);
+ this.outputStream = new ProcessOutputStream(out);
+ }
+
+ public void destroy() {
+ try {
+ kill(this.id);
+ } catch (IOException e) {
+ Logger.getLogger(Runtime.class.getName()).log(Level.FINE,
+ "Failed to destroy process " + id + ".", e);
+ }
+ }
+
+ public int exitValue() {
+ synchronized (exitValueMutex) {
+ if (exitValue == null) {
+ throw new IllegalThreadStateException(
+ "Process has not yet terminated.");
+ }
+
+ return exitValue;
+ }
+ }
+
+ public InputStream getErrorStream() {
+ return this.errorStream;
+ }
+
+ public InputStream getInputStream() {
+ return this.inputStream;
+ }
+
+ public OutputStream getOutputStream() {
+ return this.outputStream;
+ }
+
+ public int waitFor() throws InterruptedException {
+ synchronized (exitValueMutex) {
+ while (exitValue == null) {
+ exitValueMutex.wait();
+ }
+ return exitValue;
+ }
+ }
+
+ void setExitValue(int exitValue) {
+ synchronized (exitValueMutex) {
+ this.exitValue = exitValue;
+ exitValueMutex.notifyAll();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Process[id=" + id + "]";
+ }
+ }
+
+ static class ProcessReference extends WeakReference<ProcessImpl> {
+
+ final int processId;
+
+ public ProcessReference(ProcessImpl referent,
+ ProcessReferenceQueue referenceQueue) {
+ super(referent, referenceQueue);
+ this.processId = referent.id;
+ }
+ }
+
+ static class ProcessReferenceQueue extends ReferenceQueue<ProcessImpl> {
+
+ @Override
+ public ProcessReference poll() {
+ // Why couldn't they get the generics right on ReferenceQueue? :(
+ Object reference = super.poll();
+ return (ProcessReference) reference;
+ }
+ }
+
+ static final ProcessManager instance = new ProcessManager();
+
+ /** Gets the process manager. */
+ static ProcessManager getInstance() {
+ return instance;
+ }
+
+ /** Automatically closes fd when collected. */
+ private static class ProcessInputStream extends FileInputStream {
+
+ private FileDescriptor fd;
+
+ private ProcessInputStream(FileDescriptor fd) {
+ super(fd);
+ this.fd = fd;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ super.close();
+ } finally {
+ synchronized (this) {
+ if (fd != null && fd.valid()) {
+ try {
+ ProcessManager.close(fd);
+ } finally {
+ fd = null;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** Automatically closes fd when collected. */
+ private static class ProcessOutputStream extends FileOutputStream {
+
+ private FileDescriptor fd;
+
+ private ProcessOutputStream(FileDescriptor fd) {
+ super(fd);
+ this.fd = fd;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ super.close();
+ } finally {
+ synchronized (this) {
+ if (fd != null && fd.valid()) {
+ try {
+ ProcessManager.close(fd);
+ } finally {
+ fd = null;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** Closes the given file descriptor. */
+ private static native void close(FileDescriptor fd) throws IOException;
+}
diff --git a/luni-kernel/src/main/java/java/lang/Runtime.java b/luni-kernel/src/main/java/java/lang/Runtime.java
new file mode 100644
index 0000000..28cc96f
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Runtime.java
@@ -0,0 +1,889 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.Reader;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import java.util.StringTokenizer;
+import java.util.List;
+import java.util.ArrayList;
+
+import dalvik.system.VMDebug;
+import dalvik.system.VMStack;
+
+/**
+ * Allows Java applications to interface with the environment in which they are
+ * running. Applications can not create an instance of this class, but they can
+ * get a singleton instance by invoking {@link #getRuntime()}.
+ *
+ * @see System
+ *
+ * @since Android 1.0
+ */
+public class Runtime {
+
+ /**
+ * Holds the Singleton global instance of Runtime.
+ */
+ private static final Runtime mRuntime = new Runtime();
+
+ /**
+ * Holds the library paths, used for native library lookup.
+ */
+ private final String[] mLibPaths;
+
+ /**
+ * Holds the list of threads to run when the VM terminates
+ */
+ private List<Thread> shutdownHooks = new ArrayList<Thread>();
+
+ /**
+ * Reflects whether finalization should be run for all objects
+ * when the VM terminates.
+ */
+ private static boolean finalizeOnExit;
+
+ /**
+ * Reflects whether we are already shutting down the VM.
+ */
+ private boolean shuttingDown;
+
+ /**
+ * Reflects whether we are tracing method calls.
+ */
+ private boolean tracingMethods;
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Runtime(){
+ String pathList = System.getProperty("java.library.path", ".");
+ String pathSep = System.getProperty("path.separator", ":");
+ String fileSep = System.getProperty("file.separator", "/");
+
+ mLibPaths = pathList.split(pathSep);
+
+ int i;
+
+ if (false)
+ System.out.println("Runtime paths:");
+
+ // Add a '/' to the end so we don't have to do the property lookup
+ // and concatenation later.
+ for (i = 0; i < mLibPaths.length; i++) {
+ if (!mLibPaths[i].endsWith(fileSep))
+ mLibPaths[i] += fileSep;
+ if (false)
+ System.out.println(" " + mLibPaths[i]);
+ }
+ }
+
+ /**
+ * Executes the specified command and its arguments in a separate native
+ * process. The new process inherits the environment of the caller. Calling
+ * this method is equivalent to calling {@code exec(progArray, null, null)}.
+ *
+ * @param progArray
+ * the array containing the program to execute as well as any
+ * arguments to the program.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String[] progArray) throws java.io.IOException {
+ return exec(progArray, null, null);
+ }
+
+ /**
+ * Executes the specified command and its arguments in a separate native
+ * process. The new process uses the environment provided in {@code envp}.
+ * Calling this method is equivalent to calling
+ * {@code exec(progArray, envp, null)}.
+ *
+ * @param progArray
+ * the array containing the program to execute as well as any
+ * arguments to the program.
+ * @param envp
+ * the array containing the environment to start the new process
+ * in.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String[] progArray, String[] envp) throws java.io.IOException {
+ return exec(progArray, envp, null);
+ }
+
+ /**
+ * Executes the specified command and its arguments in a separate native
+ * process. The new process uses the environment provided in {@code envp}
+ * and the working directory specified by {@code directory}.
+ *
+ * @param progArray
+ * the array containing the program to execute as well as any
+ * arguments to the program.
+ * @param envp
+ * the array containing the environment to start the new process
+ * in.
+ * @param directory
+ * the directory in which to execute the program. If {@code null},
+ * execute if in the same directory as the parent process.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String[] progArray, String[] envp, File directory)
+ throws java.io.IOException {
+
+ // Sanity checks
+ if (progArray == null) {
+ throw new NullPointerException();
+ } else if (progArray.length == 0) {
+ throw new IndexOutOfBoundsException();
+ } else {
+ for (int i = 0; i < progArray.length; i++) {
+ if (progArray[i] == null) {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ if (envp != null) {
+ for (int i = 0; i < envp.length; i++) {
+ if (envp[i] == null) {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ // Security checks
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkExec(progArray[0]);
+ }
+
+ // Delegate the execution
+ return ProcessManager.getInstance().exec(progArray, envp, directory);
+ }
+
+ /**
+ * Executes the specified program in a separate native process. The new
+ * process inherits the environment of the caller. Calling this method is
+ * equivalent to calling {@code exec(prog, null, null)}.
+ *
+ * @param prog
+ * the name of the program to execute.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String prog) throws java.io.IOException {
+ return exec(prog, null, null);
+ }
+
+ /**
+ * Executes the specified program in a separate native process. The new
+ * process uses the environment provided in {@code envp}. Calling this
+ * method is equivalent to calling {@code exec(prog, envp, null)}.
+ *
+ * @param prog
+ * the name of the program to execute.
+ * @param envp
+ * the array containing the environment to start the new process
+ * in.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String prog, String[] envp) throws java.io.IOException {
+ return exec(prog, envp, null);
+ }
+
+ /**
+ * Executes the specified program in a separate native process. The new
+ * process uses the environment provided in {@code envp} and the working
+ * directory specified by {@code directory}.
+ *
+ * @param prog
+ * the name of the program to execute.
+ * @param envp
+ * the array containing the environment to start the new process
+ * in.
+ * @param directory
+ * the directory in which to execute the program. If {@code null},
+ * execute if in the same directory as the parent process.
+ * @return the new {@code Process} object that represents the native
+ * process.
+ * @throws IOException
+ * if the requested program can not be executed.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} disallows program
+ * execution.
+ * @see SecurityManager#checkExec
+ * @since Android 1.0
+ */
+ public Process exec(String prog, String[] envp, File directory) throws java.io.IOException {
+ // Sanity checks
+ if (prog == null) {
+ throw new NullPointerException();
+ } else if (prog.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ // Break down into tokens, as described in Java docs
+ StringTokenizer tokenizer = new StringTokenizer(prog);
+ int length = tokenizer.countTokens();
+ String[] progArray = new String[length];
+ for (int i = 0; i < length; i++) {
+ progArray[i] = tokenizer.nextToken();
+ }
+
+ // Delegate
+ return exec(progArray, envp, directory);
+ }
+
+ /**
+ * Causes the virtual machine to stop running and the program to exit. If
+ * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a
+ * {@code true} argument, then all all objects will be properly
+ * garbage-collected and finalized first.
+ *
+ * @param code
+ * the return code. By convention, non-zero return codes indicate
+ * abnormal terminations.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} does not allow the
+ * running thread to terminate the virtual machine.
+ * @see SecurityManager#checkExit
+ * @since Android 1.0
+ */
+ public void exit(int code) {
+ // Security checks
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkExit(code);
+ }
+
+ // Make sure we don't try this several times
+ synchronized(this) {
+ if (!shuttingDown) {
+ shuttingDown = true;
+
+ Thread[] hooks;
+ synchronized (shutdownHooks) {
+ // create a copy of the hooks
+ hooks = new Thread[shutdownHooks.size()];
+ shutdownHooks.toArray(hooks);
+ }
+
+ // Start all shutdown hooks concurrently
+ for (int i = 0; i < hooks.length; i++) {
+ hooks[i].start();
+ }
+
+ // Wait for all shutdown hooks to finish
+ for (Thread hook : hooks) {
+ try {
+ hook.join();
+ } catch (InterruptedException ex) {
+ // Ignore, since we are at VM shutdown.
+ }
+ }
+
+ // Ensure finalization on exit, if requested
+ if (finalizeOnExit) {
+ runFinalization(true);
+ }
+
+ // Get out of here finally...
+ nativeExit(code, true);
+ }
+ }
+ }
+
+ /**
+ * Returns the amount of free memory resources which are available to the
+ * running program.
+ *
+ * @return the approximate amount of free memory, measured in bytes.
+ * @since Android 1.0
+ */
+ public native long freeMemory();
+
+ /**
+ * Indicates to the virtual machine that it would be a good time to run the
+ * garbage collector. Note that this is a hint only. There is no guarantee
+ * that the garbage collector will actually be run.
+ *
+ * @since Android 1.0
+ */
+ public native void gc();
+
+ /**
+ * Returns the single {@code Runtime} instance.
+ *
+ * @return the {@code Runtime} object for the current application.
+ * @since Android 1.0
+ */
+ public static Runtime getRuntime() {
+ return mRuntime;
+ }
+
+ /**
+ * Loads and links the dynamic library that is identified through the
+ * specified path. This method is similar to {@link #loadLibrary(String)},
+ * but it accepts a full path specification whereas {@code loadLibrary} just
+ * accepts the name of the library to load.
+ *
+ * @param pathName
+ * the absolute (platform dependent) path to the library to load.
+ * @throws UnsatisfiedLinkError
+ * if the library can not be loaded.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} does not allow to load
+ * the library.
+ * @see SecurityManager#checkLink
+ * @since Android 1.0
+ */
+ public void load(String pathName) {
+ // Security checks
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkLink(pathName);
+ }
+
+ load(pathName, VMStack.getCallingClassLoader());
+ }
+
+ /*
+ * Loads and links a library without security checks.
+ */
+ void load(String filename, ClassLoader loader) {
+ if (filename == null) {
+ throw new NullPointerException("library path was null.");
+ }
+ if (!nativeLoad(filename, loader)) {
+ throw new UnsatisfiedLinkError(
+ "Library " + filename + " not found");
+ }
+ }
+
+ /**
+ * Loads and links the library with the specified name. The mapping of the
+ * specified library name to the full path for loading the library is
+ * implementation-dependent.
+ *
+ * @param libName
+ * the name of the library to load.
+ * @throws UnsatisfiedLinkError
+ * if the library can not be loaded.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} does not allow to load
+ * the library.
+ * @see SecurityManager#checkLink
+ * @since Android 1.0
+ */
+ public void loadLibrary(String libName) {
+ // Security checks
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkLink(libName);
+ }
+
+ loadLibrary(libName, VMStack.getCallingClassLoader());
+ }
+
+ /*
+ * Loads and links a library without security checks.
+ */
+ void loadLibrary(String libname, ClassLoader loader) {
+ String filename;
+ int i;
+
+ if (loader != null) {
+ filename = loader.findLibrary(libname);
+ if (filename != null && nativeLoad(filename, loader))
+ return;
+ // else fall through to exception
+ } else {
+ filename = System.mapLibraryName(libname);
+ for (i = 0; i < mLibPaths.length; i++) {
+ if (false)
+ System.out.println("Trying " + mLibPaths[i] + filename);
+ if (nativeLoad(mLibPaths[i] + filename, loader))
+ return;
+ }
+ }
+
+ throw new UnsatisfiedLinkError("Library " + libname + " not found");
+ }
+
+ private static native void nativeExit(int code, boolean isExit);
+
+ private static native boolean nativeLoad(String filename,
+ ClassLoader loader);
+
+ /**
+ * Requests proper finalization for all Objects on the heap.
+ *
+ * @param forced Decides whether the VM really needs to do this (true)
+ * or if this is just a suggestion that can safely be ignored
+ * (false).
+ */
+ private native void runFinalization(boolean forced);
+
+ /**
+ * Provides a hint to the virtual machine that it would be useful to attempt
+ * to perform any outstanding object finalizations.
+ *
+ * @since Android 1.0
+ */
+ public void runFinalization() {
+ runFinalization(false);
+ }
+
+ /**
+ * Sets the flag that indicates whether all objects are finalized when the
+ * virtual machine is about to exit. Note that all finalization which occurs
+ * when the system is exiting is performed after all running threads have
+ * been terminated.
+ *
+ * @param run
+ * {@code true} to enable finalization on exit, {@code false} to
+ * disable it.
+ * @deprecated This method is unsafe.
+ * @since Android 1.0
+ */
+ @Deprecated
+ public static void runFinalizersOnExit(boolean run) {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkExit(0);
+ }
+ finalizeOnExit = run;
+ }
+
+ /**
+ * Returns the total amount of memory which is available to the running
+ * program.
+ *
+ * @return the total amount of memory, measured in bytes.
+ * @since Android 1.0
+ */
+ public native long totalMemory();
+
+ /**
+ * Switches the output of debug information for instructions on or off.
+ * For the Android 1.0 reference implementation, this method does nothing.
+ *
+ * @param enable
+ * {@code true} to switch tracing on, {@code false} to switch it
+ * off.
+ * @since Android 1.0
+ */
+ public void traceInstructions(boolean enable) {
+ // TODO(Google) Provide some implementation for this.
+ return;
+ }
+
+ /**
+ * Switches the output of debug information for methods on or off.
+ *
+ * @param enable
+ * {@code true} to switch tracing on, {@code false} to switch it
+ * off.
+ * @since Android 1.0
+ */
+ public void traceMethodCalls(boolean enable) {
+ if (enable != tracingMethods) {
+ if (enable) {
+ VMDebug.startMethodTracing();
+ } else {
+ VMDebug.stopMethodTracing();
+ }
+ tracingMethods = enable;
+ }
+ }
+
+ /**
+ * Returns the localized version of the specified input stream. The input
+ * stream that is returned automatically converts all characters from the
+ * local character set to Unicode after reading them from the underlying
+ * stream.
+ *
+ * @param stream
+ * the input stream to localize.
+ * @return the localized input stream.
+ * @deprecated Use {@link InputStreamReader}.
+ * @since Android 1.0
+ */
+ @Deprecated
+ public InputStream getLocalizedInputStream(InputStream stream) {
+ if (System.getProperty("file.encoding", "UTF-8").equals("UTF-8")) {
+ return stream;
+ }
+ return new ReaderInputStream(stream);
+ }
+
+ /**
+ * Returns the localized version of the specified output stream. The output
+ * stream that is returned automatically converts all characters from
+ * Unicode to the local character set before writing them to the underlying
+ * stream.
+ *
+ * @param stream
+ * the output stream to localize.
+ * @return the localized output stream.
+ * @deprecated Use {@link OutputStreamWriter}.
+ * @since Android 1.0
+ */
+ @Deprecated
+ public OutputStream getLocalizedOutputStream(OutputStream stream) {
+ if (System.getProperty("file.encoding", "UTF-8").equals("UTF-8")) {
+ return stream;
+ }
+ return new WriterOutputStream(stream );
+ }
+
+ /**
+ * Registers a virtual-machine shutdown hook. A shutdown hook is a
+ * {@code Thread} that is ready to run, but has not yet been started. All
+ * registered shutdown hooks will be executed once the virtual machine shuts
+ * down properly. A proper shutdown happens when either the
+ * {@link #exit(int)} method is called or the surrounding system decides to
+ * terminate the application, for example in response to a {@code CTRL-C} or
+ * a system-wide shutdown. A termination of the virtual machine due to the
+ * {@link #halt(int)} method, an {@link Error} or a {@code SIGKILL}, in
+ * contrast, is not considered a proper shutdown. In these cases the
+ * shutdown hooks will not be run.
+ * <p>
+ * Shutdown hooks are run concurrently and in an unspecified order. Hooks
+ * failing due to an unhandled exception are not a problem, but the stack
+ * trace might be printed to the console. Once initiated, the whole shutdown
+ * process can only be terminated by calling {@code halt()}.
+ * <p>
+ * If {@link #runFinalizersOnExit(boolean)} has been called with a {@code
+ * true} argument, garbage collection and finalization will take place after
+ * all hooks are either finished or have failed. Then the virtual machine
+ * terminates.
+ * <p>
+ * It is recommended that shutdown hooks do not do any time-consuming
+ * activities, in order to not hold up the shutdown process longer than
+ * necessary.
+ *
+ * @param hook
+ * the shutdown hook to register.
+ * @throws IllegalArgumentException
+ * if the hook has already been started or if it has already
+ * been registered.
+ * @throws IllegalStateException
+ * if the virtual machine is already shutting down.
+ * @throws SecurityException
+ * if a SecurityManager is registered and the calling code
+ * doesn't have the RuntimePermission("shutdownHooks").
+ */
+ public void addShutdownHook(Thread hook) {
+ // Sanity checks
+ if (hook == null) {
+ throw new NullPointerException("Hook may not be null.");
+ }
+
+ if (shuttingDown) {
+ throw new IllegalStateException("VM already shutting down");
+ }
+
+ if (hook.hasBeenStarted) {
+ throw new IllegalArgumentException("Hook has already been started");
+ }
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("shutdownHooks"));
+ }
+
+ synchronized (shutdownHooks) {
+ if (shutdownHooks.contains(hook)) {
+ throw new IllegalArgumentException("Hook already registered.");
+ }
+
+ shutdownHooks.add(hook);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered virtual machine shutdown hook.
+ *
+ * @param hook
+ * the shutdown hook to remove.
+ * @return {@code true} if the hook has been removed successfully; {@code
+ * false} otherwise.
+ * @throws IllegalStateException
+ * if the virtual machine is already shutting down.
+ * @throws SecurityException
+ * if a SecurityManager is registered and the calling code
+ * doesn't have the RuntimePermission("shutdownHooks").
+ */
+ public boolean removeShutdownHook(Thread hook) {
+ // Sanity checks
+ if (hook == null) {
+ throw new NullPointerException("Hook may not be null.");
+ }
+
+ if (shuttingDown) {
+ throw new IllegalStateException("VM already shutting down");
+ }
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("shutdownHooks"));
+ }
+
+ synchronized (shutdownHooks) {
+ return shutdownHooks.remove(hook);
+ }
+ }
+
+ /**
+ * Causes the virtual machine to stop running, and the program to exit.
+ * Neither shutdown hooks nor finalizers are run before.
+ *
+ * @param code
+ * the return code. By convention, non-zero return codes indicate
+ * abnormal terminations.
+ * @throws SecurityException
+ * if the current {@code SecurityManager} does not allow the
+ * running thread to terminate the virtual machine.
+ * @see SecurityManager#checkExit
+ * @see #addShutdownHook(Thread)
+ * @see #removeShutdownHook(Thread)
+ * @see #runFinalizersOnExit(boolean)
+ * @since Android 1.0
+ */
+ public void halt(int code) {
+ // Security checks
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkExit(code);
+ }
+
+ // Get out of here...
+ nativeExit(code, false);
+ }
+
+ /**
+ * Returns the number of processors available to the virtual machine. The
+ * Android reference implementation (currently) always returns 1.
+ *
+ * @return the number of available processors, at least 1.
+ * @since Android 1.0
+ */
+ public int availableProcessors() {
+ return 1;
+ }
+
+ /**
+ * Returns the maximum amount of memory that may be used by the virtual
+ * machine, or {@code Long.MAX_VALUE} if there is no such limit.
+ *
+ * @return the maximum amount of memory that the virtual machine will try to
+ * allocate, measured in bytes.
+ * @since Android 1.0
+ */
+ public native long maxMemory();
+
+}
+
+/*
+ * Internal helper class for creating a localized InputStream. A reader
+ * wrapped in an InputStream.
+ */
+class ReaderInputStream extends InputStream {
+
+ private Reader reader;
+
+ private Writer writer;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream(256);
+
+ private byte[] bytes;
+
+ private int nextByte;
+
+ private int numBytes;
+
+ String encoding = System.getProperty("file.encoding", "UTF-8");
+
+ public ReaderInputStream(InputStream stream) {
+ try {
+ reader = new InputStreamReader(stream, "UTF-8");
+ writer = new OutputStreamWriter(out, encoding);
+ } catch (UnsupportedEncodingException e) {
+ // Should never happen, since UTF-8 and platform encoding must be
+ // supported.
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (nextByte >= numBytes) {
+ readBuffer();
+ }
+
+ return (numBytes < 0) ? -1 : bytes[nextByte++];
+ }
+
+ private void readBuffer() throws IOException {
+ char[] chars = new char[128];
+ int read = reader.read(chars);
+ if (read < 0) {
+ numBytes = read;
+ return;
+ }
+
+ writer.write(chars, 0, read);
+ writer.flush();
+ bytes = out.toByteArray();
+ numBytes = bytes.length;
+ nextByte = 0;
+ }
+
+}
+
+/*
+ * Internal helper class for creating a localized OutputStream. A writer
+ * wrapped in an OutputStream. Bytes are written to characters in big-endian
+ * fashion.
+ */
+class WriterOutputStream extends OutputStream {
+
+ private Reader reader;
+
+ private Writer writer;
+
+ private PipedOutputStream out;
+
+ private PipedInputStream pipe;
+
+ private int numBytes;
+
+ private String enc = System.getProperty("file.encoding", "UTF-8");
+
+ public WriterOutputStream(OutputStream stream) {
+ try {
+ // sink
+ this.writer = new OutputStreamWriter(stream, enc);
+
+ // transcriber
+ out = new PipedOutputStream();
+ pipe = new PipedInputStream(out);
+ this.reader = new InputStreamReader(pipe, "UTF-8");
+
+ } catch (UnsupportedEncodingException e) {
+ // Should never happen, since platform encoding must be supported.
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ if( ++numBytes > 256) {
+ flush();
+ numBytes = 0;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ out.flush();
+ char[] chars = new char[128];
+ if (pipe.available() > 0) {
+ int read = reader.read(chars);
+ if (read > 0) {
+ writer.write(chars, 0, read);
+ }
+ }
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ out.close();
+ flush();
+ writer.close();
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/StackTraceElement.java b/luni-kernel/src/main/java/java/lang/StackTraceElement.java
new file mode 100644
index 0000000..5394ae9
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/StackTraceElement.java
@@ -0,0 +1,245 @@
+/*
+ * 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 java.lang;
+
+import java.io.Serializable;
+
+/**
+ * A representation of a single stack frame. Arrays of {@code StackTraceElement}
+ * are stored in {@link Throwable} objects to represent the whole state of the
+ * call stack at the time a {@code Throwable} gets thrown.
+ *
+ * @see Throwable#getStackTrace()
+ * @since Android 1.0
+ */
+public final class StackTraceElement implements Serializable {
+
+ private static final long serialVersionUID = 6992337162326171013L;
+
+ // BEGIN android-added
+ private static final int NATIVE_LINE_NUMBER = -2;
+ // END android-added
+
+ String declaringClass;
+
+ String methodName;
+
+ String fileName;
+
+ int lineNumber;
+
+ /**
+ * Constructs a new {@code StackTraceElement} for a specified execution
+ * point.
+ *
+ * @param cls
+ * the fully qualified name of the class where execution is at.
+ * @param method
+ * the name of the method where execution is at.
+ * @param file
+ * The name of the file where execution is at or {@code null}.
+ * @param line
+ * the line of the file where execution is at, a negative number
+ * if unknown or {@code -2} if the execution is in a native
+ * method.
+ * @throws NullPointerException
+ * if {@code cls} or {@code method} is {@code null}.
+ * @since Android 1.0
+ */
+ public StackTraceElement(String cls, String method, String file, int line) {
+ super();
+ if (cls == null || method == null) {
+ throw new NullPointerException();
+ }
+ declaringClass = cls;
+ methodName = method;
+ fileName = file;
+ lineNumber = line;
+ }
+
+ /**
+ * <p>
+ * Private, nullary constructor for VM use only.
+ * </p>
+ */
+ private StackTraceElement() {
+ super();
+ }
+
+ /**
+ * Compares this instance with the specified object and indicates if they
+ * are equal. In order to be equal, the following conditions must be
+ * fulfilled:
+ * <ul>
+ * <li>{@code obj} must be a stack trace element,</li>
+ * <li>the method names of this stack trace element and of {@code obj} must
+ * not be {@code null},</li>
+ * <li>the class, method and file names as well as the line number of this
+ * stack trace element and of {@code obj} must be equal.</li>
+ * </ul>
+ *
+ * @param obj
+ * the object to compare this instance with.
+ * @return {@code true} if the specified object is equal to this
+ * {@code StackTraceElement}; {@code false} otherwise.
+ * @see #hashCode
+ * @since Android 1.0
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StackTraceElement)) {
+ return false;
+ }
+ StackTraceElement castObj = (StackTraceElement) obj;
+
+ /*
+ * Unknown methods are never equal to anything (not strictly to spec,
+ * but spec does not allow null method/class names)
+ */
+ if ((methodName == null) || (castObj.methodName == null)) {
+ return false;
+ }
+
+ if (!getMethodName().equals(castObj.getMethodName())) {
+ return false;
+ }
+ if (!getClassName().equals(castObj.getClassName())) {
+ return false;
+ }
+ String localFileName = getFileName();
+ if (localFileName == null) {
+ if (castObj.getFileName() != null) {
+ return false;
+ }
+ } else {
+ if (!localFileName.equals(castObj.getFileName())) {
+ return false;
+ }
+ }
+ if (getLineNumber() != castObj.getLineNumber()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the fully qualified name of the class belonging to this
+ * {@code StackTraceElement}.
+ *
+ * @return the fully qualified type name of the class
+ * @since Android 1.0
+ */
+ public String getClassName() {
+ return (declaringClass == null) ? "<unknown class>" : declaringClass;
+ }
+
+ /**
+ * Returns the name of the Java source file containing class belonging to
+ * this {@code StackTraceElement}.
+ *
+ * @return the name of the file, or {@code null} if this information is not
+ * available.
+ * @since Android 1.0
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Returns the line number in the source for the class belonging to this
+ * {@code StackTraceElement}.
+ *
+ * @return the line number, or a negative number if this information is not
+ * available.
+ * @since Android 1.0
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Returns the name of the method belonging to this {@code
+ * StackTraceElement}.
+ *
+ * @return the name of the method, or "<unknown method>" if this information
+ * is not available.
+ * @since Android 1.0
+ */
+ public String getMethodName() {
+ return (methodName == null) ? "<unknown method>" : methodName;
+ }
+
+ @Override
+ public int hashCode() {
+ /*
+ * Either both methodName and declaringClass are null, or neither are
+ * null.
+ */
+ if (methodName == null) {
+ // all unknown methods hash the same
+ return 0;
+ }
+ // declaringClass never null if methodName is non-null
+ return methodName.hashCode() ^ declaringClass.hashCode();
+ }
+
+ /**
+ * Indicates if the method name returned by {@link #getMethodName()} is
+ * implemented as a native method.
+ *
+ * @return {@code true} if the method in which this stack trace element is
+ * executing is a native method; {@code false} otherwise.
+ * @since Android 1.0
+ */
+ public boolean isNativeMethod() {
+ // BEGIN android-changed
+ return lineNumber == NATIVE_LINE_NUMBER;
+ // END android-changed
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(80);
+
+ buf.append(getClassName());
+ buf.append('.');
+ buf.append(getMethodName());
+
+ if (isNativeMethod()) {
+ buf.append("(Native Method)");
+ } else {
+ String fName = getFileName();
+
+ if (fName == null) {
+ buf.append("(Unknown Source)");
+ } else {
+ int lineNum = getLineNumber();
+
+ buf.append('(');
+ buf.append(fName);
+ if (lineNum >= 0) {
+ buf.append(':');
+ buf.append(lineNum);
+ }
+ buf.append(')');
+ }
+ }
+ return buf.toString();
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/System.java b/luni-kernel/src/main/java/java/lang/System.java
new file mode 100644
index 0000000..b97a75a
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/System.java
@@ -0,0 +1,755 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.channels.Channel;
+import java.nio.channels.spi.SelectorProvider;
+import java.security.SecurityPermission;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.PropertyPermission;
+import java.util.Set;
+
+import dalvik.system.VMStack;
+
+/**
+ * Provides access to system-related information and resources including
+ * standard input and output. Enables clients to dynamically load native
+ * libraries. All methods of this class are accessed in a static way and the
+ * class itself can not be instantiated.
+ *
+ * @see Runtime
+ *
+ * @since Android 1.0
+ */
+public final class System {
+
+ /**
+ * Default input stream.
+ *
+ * @since Android 1.0
+ */
+ public static final InputStream in;
+
+ /**
+ * Default output stream.
+ *
+ * @since Android 1.0
+ */
+ public static final PrintStream out;
+
+ /**
+ * Default error output stream.
+ *
+ * @since Android 1.0
+ */
+ public static final PrintStream err;
+
+ /**
+ * The System Properties table.
+ */
+ private static Properties systemProperties;
+
+ /**
+ * The System default SecurityManager.
+ */
+ private static SecurityManager securityManager;
+
+ /**
+ * Initialize all the slots in System on first use.
+ */
+ static {
+ /*
+ * Set up standard in, out, and err. TODO err and out are
+ * String.ConsolePrintStream. All three are buffered in Harmony. Check
+ * and possibly change this later.
+ */
+ err = new PrintStream(new FileOutputStream(FileDescriptor.err));
+ out = new PrintStream(new FileOutputStream(FileDescriptor.out));
+ in = new FileInputStream(FileDescriptor.in);
+ }
+
+ /**
+ * Sets the standard input stream to the given user defined input stream.
+ *
+ * @param newIn
+ * the user defined input stream to set as the standard input
+ * stream.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPermission()} method does not allow the change of the
+ * stream.
+ * @since Android 1.0
+ */
+ public static void setIn(InputStream newIn) {
+ SecurityManager secMgr = System.getSecurityManager();
+ if(secMgr != null) {
+ secMgr.checkPermission(RuntimePermission.permissionToSetIO);
+ }
+ setFieldImpl("in", "Ljava/io/InputStream;", newIn);
+ }
+
+ /**
+ * Sets the standard output stream to the given user defined output stream.
+ *
+ * @param newOut
+ * the user defined output stream to set as the standard output
+ * stream.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPermission()} method does not allow the change of the
+ * stream.
+ * @since Android 1.0
+ */
+ public static void setOut(java.io.PrintStream newOut) {
+ SecurityManager secMgr = System.getSecurityManager();
+ if(secMgr != null) {
+ secMgr.checkPermission(RuntimePermission.permissionToSetIO);
+ }
+ setFieldImpl("out", "Ljava/io/PrintStream;", newOut);
+ }
+
+ /**
+ * Sets the standard error output stream to the given user defined output
+ * stream.
+ *
+ * @param newErr
+ * the user defined output stream to set as the standard error
+ * output stream.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPermission()} method does not allow the change of the
+ * stream.
+ * @since Android 1.0
+ */
+ public static void setErr(java.io.PrintStream newErr) {
+ SecurityManager secMgr = System.getSecurityManager();
+ if(secMgr != null) {
+ secMgr.checkPermission(RuntimePermission.permissionToSetIO);
+ }
+ setFieldImpl("err", "Ljava/io/PrintStream;", newErr);
+ }
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private System() {
+ }
+
+ /**
+ * Copies the number of {@code length} elements of the Array {@code src}
+ * starting at the offset {@code srcPos} into the Array {@code dest} at
+ * the position {@code destPos}.
+ *
+ * @param src
+ * the source array to copy the content.
+ * @param srcPos
+ * the starting index of the content in {@code src}.
+ * @param dest
+ * the destination array to copy the data into.
+ * @param destPos
+ * the starting index for the copied content in {@code dest}.
+ * @param length
+ * the number of elements of the {@code array1} content they have
+ * to be copied.
+ * @since Android 1.0
+ */
+ public static native void arraycopy(Object src, int srcPos, Object dest,
+ int destPos, int length);
+
+ /**
+ * Returns the current system time in milliseconds since January 1, 1970
+ * 00:00:00 UTC. This method shouldn't be used for measuring timeouts or
+ * other elapsed time measurements, as changing the system time can affect
+ * the results.
+ *
+ * @return the local system time in milliseconds.
+ * @since Android 1.0
+ */
+ public static native long currentTimeMillis();
+
+ /**
+ * Returns the current timestamp of the most precise timer available on the
+ * local system. This timestamp can only be used to measure an elapsed
+ * period by comparing it against another timestamp. It cannot be used as a
+ * very exact system time expression.
+ *
+ * @return the current timestamp in nanoseconds.
+ * @since Android 1.0
+ */
+ public static native long nanoTime();
+
+ /**
+ * Causes the virtual machine to stop running and the program to exit. If
+ * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a
+ * {@code true} argument, then all all objects will be properly
+ * garbage-collected and finalized first.
+ *
+ * @param code
+ * the return code.
+ * @throws SecurityException
+ * if the running thread has not enough permission to exit the
+ * virtual machine.
+ * @see SecurityManager#checkExit
+ * @since Android 1.0
+ */
+ public static void exit(int code) {
+ Runtime.getRuntime().exit(code);
+ }
+
+ /**
+ * Indicates to the virtual machine that it would be a good time to run the
+ * garbage collector. Note that this is a hint only. There is no guarantee
+ * that the garbage collector will actually be run.
+ *
+ * @since Android 1.0
+ */
+ public static void gc() {
+ Runtime.getRuntime().gc();
+ }
+
+ /**
+ * Returns the value of the environment variable with the given name {@code
+ * var}.
+ *
+ * @param name
+ * the name of the environment variable.
+ * @return the value of the specified environment variable or {@code null}
+ * if no variable exists with the given name.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPermission()} method does not allow the querying of
+ * single environment variables.
+ *
+ * @since Android 1.0
+ */
+ public static String getenv(String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPermission(new RuntimePermission("getenv." + name));
+ }
+
+ return getEnvByName(name);
+ }
+
+ /*
+ * Returns an environment variable. No security checks are performed.
+ * @param var the name of the environment variable
+ * @return the value of the specified environment variable
+ */
+ private static native String getEnvByName(String name);
+
+ /**
+ * Returns an unmodifiable map of all available environment variables.
+ *
+ * @return the map representing all environment variables.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPermission()} method does not allow the querying of
+ * all environment variables.
+ * @since Android 1.0
+ */
+ public static Map<String, String> getenv() {
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPermission(new RuntimePermission("getenv.*"));
+ }
+
+ Map<String, String> map = new HashMap<String, String>();
+
+ int index = 0;
+ String entry = getEnvByIndex(index++);
+ while (entry != null) {
+ int pos = entry.indexOf('=');
+ if (pos != -1) {
+ map.put(entry.substring(0, pos), entry.substring(pos + 1));
+ }
+
+ entry = getEnvByIndex(index++);
+ }
+
+ return new SystemEnvironment(map);
+ }
+
+ /*
+ * Returns an environment variable. No security checks are performed. The
+ * safe way of traversing the environment is to start at index zero and
+ * count upwards until a null pointer is encountered. This marks the end of
+ * the Unix environment.
+ * @param index the index of the environment variable
+ * @return the value of the specified environment variable
+ */
+ private static native String getEnvByIndex(int index);
+
+ /**
+ * Returns the inherited channel from the creator of the current virtual
+ * machine.
+ *
+ * @return the inherited {@link Channel} or {@code null} if none exists.
+ * @throws IOException
+ * if an I/O error occurred.
+ * @see SelectorProvider
+ * @see SelectorProvider#inheritedChannel()
+ * @since Android 1.0
+ */
+ public static Channel inheritedChannel() throws IOException {
+ return SelectorProvider.provider().inheritedChannel();
+ }
+
+ /**
+ * Returns the system properties. Note that this is not a copy, so that
+ * changes made to the returned Properties object will be reflected in
+ * subsequent calls to getProperty and getProperties.
+ *
+ * @return the system properties.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPropertiesAccess()} method does not allow the operation.
+ * @since Android 1.0
+ */
+ public static Properties getProperties() {
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPropertiesAccess();
+ }
+
+ return internalGetProperties();
+ }
+
+ /**
+ * Returns the system properties without any security checks. This is used
+ * for access from within java.lang.
+ *
+ * @return the system properties
+ */
+ static Properties internalGetProperties() {
+ if (System.systemProperties == null) {
+ SystemProperties props = new SystemProperties();
+ props.preInit();
+ props.postInit();
+ System.systemProperties = props;
+ }
+
+ return systemProperties;
+ }
+
+ /**
+ * Returns the value of a particular system property or {@code null} if no
+ * such property exists.
+ * <p>
+ * The properties currently provided by the virtual machine are:
+ *
+ * <pre>
+ * java.vendor.url
+ * java.class.path
+ * user.home
+ * java.class.version
+ * os.version
+ * java.vendor
+ * user.dir
+ * user.timezone
+ * path.separator
+ * os.name
+ * os.arch
+ * line.separator
+ * file.separator
+ * user.name
+ * java.version
+ * java.home
+ * </pre>
+ *
+ * @param prop
+ * the name of the system property to look up.
+ * @return the value of the specified system property or {@code null} if the
+ * property doesn't exist.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPropertyAccess()} method does not allow the operation.
+ * @since Android 1.0
+ */
+ public static String getProperty(String prop) {
+ return getProperty(prop, null);
+ }
+
+ /**
+ * Returns the value of a particular system property. The {@code
+ * defaultValue} will be returned if no such property has been found.
+ *
+ * @param prop
+ * the name of the system property to look up.
+ * @param defaultValue
+ * the return value if the system property with the given name
+ * does not exist.
+ * @return the value of the specified system property or the {@code
+ * defaultValue} if the property does not exist.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPropertyAccess()} method does not allow the operation.
+ * @since Android 1.0
+ */
+ public static String getProperty(String prop, String defaultValue) {
+ if (prop.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPropertyAccess(prop);
+ }
+
+ return internalGetProperties().getProperty(prop, defaultValue);
+ }
+
+ /**
+ * Sets the value of a particular system property.
+ *
+ * @param prop
+ * the name of the system property to be changed.
+ * @param value
+ * the value to associate with the given property {@code prop}.
+ * @return the old value of the property or {@code null} if the property
+ * didn't exist.
+ * @throws SecurityException
+ * if a security manager exists and write access to the
+ * specified property is not allowed.
+ * @since Android 1.0
+ */
+ public static String setProperty(String prop, String value) {
+ if (prop.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPermission(new PropertyPermission(prop, "write"));
+ }
+ return (String)internalGetProperties().setProperty(prop, value);
+ }
+
+ /**
+ * Removes a specific system property.
+ *
+ * @param key
+ * the name of the system property to be removed.
+ * @return the property value or {@code null} if the property didn't exist.
+ * @throws NullPointerException
+ * if the argument {@code key} is {@code null}.
+ * @throws IllegalArgumentException
+ * if the argument {@code key} is empty.
+ * @throws SecurityException
+ * if a security manager exists and write access to the
+ * specified property is not allowed.
+ * @since Android 1.0
+ */
+ public static String clearProperty(String key) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ if (key.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPermission(new PropertyPermission(key, "write"));
+ }
+ return (String)internalGetProperties().remove(key);
+ }
+
+ /**
+ * Returns the active security manager.
+ *
+ * @return the system security manager object.
+ * @since Android 1.0
+ */
+ public static SecurityManager getSecurityManager() {
+ return securityManager;
+ }
+
+ /**
+ * Returns an integer hash code for the parameter. The hash code returned is
+ * the same one that would be returned by the method {@code
+ * java.lang.Object.hashCode()}, whether or not the object's class has
+ * overridden hashCode(). The hash code for {@code null} is {@code 0}.
+ *
+ * @param anObject
+ * the object to calculate the hash code.
+ * @return the hash code for the given object.
+ * @see java.lang.Object#hashCode
+ * @since Android 1.0
+ */
+ public static native int identityHashCode(Object anObject);
+
+ /**
+ * Loads the specified file as a dynamic library.
+ *
+ * @param pathName
+ * the path of the file to be loaded.
+ * @throws SecurityException
+ * if the library was not allowed to be loaded.
+ * @since Android 1.0
+ */
+ public static void load(String pathName) {
+ SecurityManager smngr = System.getSecurityManager();
+ if (smngr != null) {
+ smngr.checkLink(pathName);
+ }
+ Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
+ }
+
+ /**
+ * Loads and links the shared library with the given name {@code libName}.
+ * The file will be searched in the default directory for shared libraries
+ * of the local system.
+ *
+ * @param libName
+ * the name of the library to load.
+ * @throws UnsatisfiedLinkError
+ * if the library could not be loaded.
+ * @throws SecurityException
+ * if the library was not allowed to be loaded.
+ * @since Android 1.0
+ */
+ public static void loadLibrary(String libName) {
+ SecurityManager smngr = System.getSecurityManager();
+ if (smngr != null) {
+ smngr.checkLink(libName);
+ }
+ Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
+ }
+
+ /**
+ * Provides a hint to the virtual machine that it would be useful to attempt
+ * to perform any outstanding object finalizations.
+ *
+ * @since Android 1.0
+ */
+ public static void runFinalization() {
+ Runtime.getRuntime().runFinalization();
+ }
+
+ /**
+ * Ensures that, when the virtual machine is about to exit, all objects are
+ * finalized. Note that all finalization which occurs when the system is
+ * exiting is performed after all running threads have been terminated.
+ *
+ * @param flag
+ * the flag determines if finalization on exit is enabled.
+ * @deprecated this method is unsafe.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public static void runFinalizersOnExit(boolean flag) {
+ Runtime.runFinalizersOnExit(flag);
+ }
+
+ /**
+ * Sets all system properties.
+ *
+ * @param p
+ * the new system property.
+ * @throws SecurityException
+ * if a {@link SecurityManager} is installed and its {@code
+ * checkPropertiesAccess()} method does not allow the operation.
+ * @since Android 1.0
+ */
+ public static void setProperties(Properties p) {
+ SecurityManager secMgr = System.getSecurityManager();
+ if (secMgr != null) {
+ secMgr.checkPropertiesAccess();
+ }
+
+ systemProperties = p;
+ }
+
+ /**
+ * Sets the active security manager. Note that once the security manager has
+ * been set, it can not be changed. Attempts to do that will cause a
+ * security exception.
+ *
+ * @param sm
+ * the new security manager.
+ * @throws SecurityException
+ * if the security manager has already been set and if its
+ * checkPermission method does not allow to redefine the
+ * security manager.
+ * @since Android 1.0
+ */
+ public static void setSecurityManager(final SecurityManager sm) {
+ if (securityManager != null) {
+ securityManager.checkPermission(new java.lang.RuntimePermission("setSecurityManager"));
+ }
+
+ if (sm != null) {
+ // before the new manager assumed office, make a pass through
+ // the common operations and let it load needed classes (if any),
+ // to avoid infinite recursion later on
+ try {
+ sm.checkPermission(new SecurityPermission("getProperty.package.access"));
+ } catch (Exception ignore) {
+ }
+ try {
+ sm.checkPackageAccess("java.lang");
+ } catch (Exception ignore) {
+ }
+ }
+
+ securityManager = sm;
+ }
+
+ /**
+ * Returns the platform specific file name format for the shared library
+ * named by the argument.
+ *
+ * @param userLibName
+ * the name of the library to look up.
+ * @return the platform specific filename for the library.
+ * @since Android 1.0
+ */
+ public static native String mapLibraryName(String userLibName);
+
+ /**
+ * Sets the value of the named static field in the receiver to the passed in
+ * argument.
+ *
+ * @param fieldName
+ * the name of the field to set, one of in, out, or err
+ * @param stream
+ * the new value of the field
+ */
+ private static native void setFieldImpl(String fieldName, String signature, Object stream);
+
+}
+
+/**
+ * Internal class holding the System properties. Needed by the Dalvik VM for the
+ * two native methods. Must not be a local class, since we don't have a System
+ * instance.
+ */
+class SystemProperties extends Properties {
+ // Dummy, just to make the compiler happy.
+
+ native void preInit();
+
+ native void postInit();
+}
+
+/**
+ * Internal class holding the System environment variables. The Java spec
+ * mandates that this map be read-only, so we wrap our real map into this one
+ * and make sure no one touches the contents. We also check for null parameters
+ * and do some (seemingly unnecessary) type casts to fulfill the contract layed
+ * out in the spec.
+ */
+class SystemEnvironment implements Map {
+
+ private Map<String, String> map;
+
+ public SystemEnvironment(Map<String, String> map) {
+ this.map = map;
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("Can't modify environment");
+ }
+
+ @SuppressWarnings("cast")
+ public boolean containsKey(Object key) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+
+ return map.containsKey((String)key);
+ }
+
+ @SuppressWarnings("cast")
+ public boolean containsValue(Object value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ return map.containsValue((String)value);
+ }
+
+ public Set entrySet() {
+ return map.entrySet();
+ }
+
+ @SuppressWarnings("cast")
+ public String get(Object key) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+
+ return map.get((String)key);
+ }
+
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ public Set<String> keySet() {
+ return map.keySet();
+ }
+
+ public String put(Object key, Object value) {
+ throw new UnsupportedOperationException("Can't modify environment");
+ }
+
+ public void putAll(Map map) {
+ throw new UnsupportedOperationException("Can't modify environment");
+ }
+
+ public String remove(Object key) {
+ throw new UnsupportedOperationException("Can't modify environment");
+ }
+
+ public int size() {
+ return map.size();
+ }
+
+ public Collection values() {
+ return map.values();
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/Thread.java b/luni-kernel/src/main/java/java/lang/Thread.java
new file mode 100644
index 0000000..d3795f8
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Thread.java
@@ -0,0 +1,1559 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang;
+
+import dalvik.system.VMStack;
+
+import java.security.AccessController;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.harmony.security.fortress.SecurityUtils;
+
+/**
+ * A {@code Thread} is a concurrent unit of execution. It has its own call stack
+ * for methods being invoked, their arguments and local variables. Each virtual
+ * machine instance has at least one main {@code Thread} running when it is
+ * started; typically, there are several others for housekeeping. The
+ * application might decide to launch additional {@code Thread}s for specific
+ * purposes.
+ * <p>
+ * {@code Thread}s in the same VM interact and synchronize by the use of shared
+ * objects and monitors associated with these objects. Synchronized methods and
+ * part of the API in {@link Object} also allow {@code Thread}s to cooperate.
+ * <p>
+ * There are basically two main ways of having a {@code Thread} execute
+ * application code. One is providing a new class that extends {@code Thread}
+ * and overriding its {@link #run()} method. The other is providing a new
+ * {@code Thread} instance with a {@link Runnable} object during its creation.
+ * In both cases, the {@link #start()} method must be called to actually execute
+ * the new {@code Thread}.
+ * <p>
+ * Each {@code Thread} has an integer priority that basically determines the
+ * amount of CPU time the {@code Thread} gets. It can be set using the
+ * {@link #setPriority(int)} method. A {@code Thread} can also be made a daemon,
+ * which makes it run in the background. The latter also affects VM termination
+ * behavior: the VM does not terminate automatically as long as there are
+ * non-daemon threads running.
+ *
+ * @see java.lang.Object
+ * @see java.lang.ThreadGroup
+ *
+ * @since Android 1.0
+ */
+public class Thread implements Runnable {
+
+ /** Park states */
+ private static class ParkState {
+ /** park state indicating unparked */
+ private static final int UNPARKED = 1;
+
+ /** park state indicating preemptively unparked */
+ private static final int PREEMPTIVELY_UNPARKED = 2;
+
+ /** park state indicating parked */
+ private static final int PARKED = 3;
+ }
+
+ /**
+ * A representation of a thread's state. A given thread may only be in one
+ * state at a time.
+ *
+ * @since Android 1.0
+ */
+ public enum State {
+ /**
+ * The thread has been created, but has never been started.
+ */
+ NEW,
+ /**
+ * The thread may be run.
+ */
+ RUNNABLE,
+ /**
+ * The thread is blocked and waiting for a lock.
+ */
+ BLOCKED,
+ /**
+ * The thread is waiting.
+ */
+ WAITING,
+ /**
+ * The thread is waiting for a specified amount of time.
+ */
+ TIMED_WAITING,
+ /**
+ * The thread has been terminated.
+ */
+ TERMINATED
+ }
+
+ /**
+ * The maximum priority value allowed for a thread.
+ *
+ * @since Android 1.0
+ */
+ public final static int MAX_PRIORITY = 10;
+
+ /**
+ * The minimum priority value allowed for a thread.
+ *
+ * @since Android 1.0
+ */
+ public final static int MIN_PRIORITY = 1;
+
+ /**
+ * The normal (default) priority value assigned to threads.
+ *
+ * @since Android 1.0
+ */
+ public final static int NORM_PRIORITY = 5;
+
+ /* some of these are accessed directly by the VM; do not rename them */
+ volatile VMThread vmThread;
+ volatile ThreadGroup group;
+ volatile boolean daemon;
+ volatile String name;
+ volatile int priority;
+ volatile long stackSize;
+ Runnable target;
+ private static int count = 0;
+
+ /**
+ * Holds the thread's ID. We simply count upwards, so
+ * each Thread has a unique ID.
+ */
+ private long id;
+
+ /**
+ * Normal thread local values.
+ */
+ ThreadLocal.Values localValues;
+
+ /**
+ * Inheritable thread local values.
+ */
+ ThreadLocal.Values inheritableValues;
+
+ /**
+ * Holds the interrupt action for this Thread, if any.
+ * <p>
+ * This is required internally by NIO, so even if it looks like it's
+ * useless, don't delete it!
+ */
+ private Runnable interruptAction;
+
+ /**
+ * Holds the class loader for this Thread, in case there is one.
+ */
+ private ClassLoader contextClassLoader;
+
+ /**
+ * Holds the handler for uncaught exceptions in this Thread,
+ * in case there is one.
+ */
+ private UncaughtExceptionHandler uncaughtHandler;
+
+ /**
+ * Holds the default handler for uncaught exceptions, in case there is one.
+ */
+ private static UncaughtExceptionHandler defaultUncaughtHandler;
+
+ /**
+ * Reflects whether this Thread has already been started. A Thread
+ * can only be started once (no recycling). Also, we need it to deduce
+ * the proper Thread status.
+ */
+ boolean hasBeenStarted = false;
+
+ /** the park state of the thread */
+ private int parkState = ParkState.UNPARKED;
+
+ /**
+ * Constructs a new {@code Thread} with no {@code Runnable} object and a
+ * newly generated name. The new {@code Thread} will belong to the same
+ * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ *
+ * @since Android 1.0
+ */
+ public Thread() {
+ create(null, null, null, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object and a
+ * newly generated name. The new {@code Thread} will belong to the same
+ * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @param runnable
+ * a {@code Runnable} whose method <code>run</code> will be
+ * executed by the new {@code Thread}
+ *
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ *
+ * @since Android 1.0
+ */
+ public Thread(Runnable runnable) {
+ create(null, runnable, null, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object and name
+ * provided. The new {@code Thread} will belong to the same {@code
+ * ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @param runnable
+ * a {@code Runnable} whose method <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @param threadName
+ * the name for the {@code Thread} being created
+ *
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ *
+ * @since Android 1.0
+ */
+ public Thread(Runnable runnable, String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+
+ create(null, runnable, threadName, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with no {@code Runnable} object and the
+ * name provided. The new {@code Thread} will belong to the same {@code
+ * ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @param threadName
+ * the name for the {@code Thread} being created
+ *
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ *
+ * @since Android 1.0
+ */
+ public Thread(String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+
+ create(null, null, threadName, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object and a
+ * newly generated name. The new {@code Thread} will belong to the {@code
+ * ThreadGroup} passed as parameter.
+ *
+ * @param group
+ * {@code ThreadGroup} to which the new {@code Thread} will
+ * belong
+ * @param runnable
+ * a {@code Runnable} whose method <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @throws SecurityException
+ * if <code>group.checkAccess()</code> fails with a
+ * SecurityException
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> has already been done
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public Thread(ThreadGroup group, Runnable runnable) {
+ create(group, runnable, null, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object, the given
+ * name and belonging to the {@code ThreadGroup} passed as parameter.
+ *
+ * @param group
+ * ThreadGroup to which the new {@code Thread} will belong
+ * @param runnable
+ * a {@code Runnable} whose method <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @param threadName
+ * the name for the {@code Thread} being created
+ * @throws SecurityException
+ * if <code>group.checkAccess()</code> fails with a
+ * SecurityException
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> has already been done
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public Thread(ThreadGroup group, Runnable runnable, String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+
+ create(group, runnable, threadName, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with no {@code Runnable} object, the
+ * given name and belonging to the {@code ThreadGroup} passed as parameter.
+ *
+ * @param group
+ * {@code ThreadGroup} to which the new {@code Thread} will belong
+ * @param threadName
+ * the name for the {@code Thread} being created
+ * @throws SecurityException
+ * if <code>group.checkAccess()</code> fails with a
+ * SecurityException
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> has already been done
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public Thread(ThreadGroup group, String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+
+ create(group, null, threadName, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object, the given
+ * name and belonging to the {@code ThreadGroup} passed as parameter.
+ *
+ * @param group
+ * {@code ThreadGroup} to which the new {@code Thread} will
+ * belong
+ * @param runnable
+ * a {@code Runnable} whose method <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @param threadName
+ * the name for the {@code Thread} being created
+ * @param stackSize
+ * a stack size for the new {@code Thread}. This has a highly
+ * platform-dependent interpretation. It may even be ignored
+ * completely.
+ * @throws SecurityException
+ * if <code>group.checkAccess()</code> fails with a
+ * SecurityException
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> has already been done
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+ create(group, runnable, threadName, stackSize);
+ }
+
+ /**
+ * Package-scope method invoked by Dalvik VM to create "internal"
+ * threads or attach threads created externally.
+ *
+ * Don't call Thread.currentThread(), since there may not be such
+ * a thing (e.g. for Main).
+ */
+ Thread(ThreadGroup group, String name, int priority, boolean daemon) {
+ synchronized (Thread.class) {
+ id = ++Thread.count;
+ }
+
+ if (name == null) {
+ this.name = "Thread-" + id;
+ } else
+ this.name = name;
+
+ if (group == null) {
+ throw new InternalError("group not specified");
+ }
+
+ this.group = group;
+
+ this.target = null;
+ this.stackSize = 0;
+ this.priority = priority;
+ this.daemon = daemon;
+
+ /* add ourselves to our ThreadGroup of choice */
+ this.group.addThread(this);
+ }
+
+ /**
+ * Initializes a new, existing Thread object with a runnable object,
+ * the given name and belonging to the ThreadGroup passed as parameter.
+ * This is the method that the several public constructors delegate their
+ * work to.
+ *
+ * @param group ThreadGroup to which the new Thread will belong
+ * @param runnable a java.lang.Runnable whose method <code>run</code> will
+ * be executed by the new Thread
+ * @param threadName Name for the Thread being created
+ * @param stackSize Platform dependent stack size
+ * @throws SecurityException if <code>group.checkAccess()</code> fails
+ * with a SecurityException
+ * @throws IllegalThreadStateException if <code>group.destroy()</code> has
+ * already been done
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ */
+ private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ if (group == null) {
+ group = smgr.getThreadGroup();
+ }
+
+ /*
+ * Freaky security requirement: If the Thread's class is actually
+ * a subclass of Thread and it tries to override either
+ * getContextClassLoader() or setContextClassLoader(), the
+ * SecurityManager has to allow this.
+ */
+ if (getClass() != Thread.class) {
+ Class[] signature = new Class[] { ClassLoader.class };
+
+ try {
+ getClass().getDeclaredMethod("getContextClassLoader", signature);
+ smgr.checkPermission(new RuntimePermission("enableContextClassLoaderOverride"));
+ } catch (NoSuchMethodException ex) {
+ // Ignore. Just interested in the method's existence.
+ }
+
+ try {
+ getClass().getDeclaredMethod("setContextClassLoader", signature);
+ smgr.checkPermission(new RuntimePermission("enableContextClassLoaderOverride"));
+ } catch (NoSuchMethodException ex) {
+ // Ignore. Just interested in the method's existence.
+ }
+ }
+ }
+
+ Thread currentThread = Thread.currentThread();
+ if (group == null) {
+ group = currentThread.getThreadGroup();
+ }
+
+ group.checkAccess();
+ if (group.isDestroyed()) {
+ throw new IllegalThreadStateException("Group already destroyed");
+ }
+
+ this.group = group;
+
+ synchronized (Thread.class) {
+ id = ++Thread.count;
+ }
+
+ if (threadName == null) {
+ this.name = "Thread-" + id;
+ } else {
+ this.name = threadName;
+ }
+
+ this.target = runnable;
+ this.stackSize = stackSize;
+
+ this.priority = currentThread.getPriority();
+
+ // Transfer over InheritableThreadLocals.
+ if (currentThread.inheritableValues != null) {
+ inheritableValues
+ = new ThreadLocal.Values(currentThread.inheritableValues);
+ }
+
+ // store current AccessControlContext as inherited context for this thread
+ SecurityUtils.putContext(this, AccessController.getContext());
+
+ // add ourselves to our ThreadGroup of choice
+ this.group.addThread(this);
+ }
+
+ /**
+ * Returns the number of active {@code Thread}s in the running {@code
+ * Thread}'s group and its subgroups.
+ *
+ * @return the number of {@code Thread}s
+ *
+ * @since Android 1.0
+ */
+ public static int activeCount() {
+ return currentThread().getThreadGroup().activeCount();
+ }
+
+ /**
+ * Is used for operations that require approval from a SecurityManager. If
+ * there's none installed, this method is a no-op. If there's a
+ * SecurityManager installed, {@link SecurityManager#checkAccess(Thread)} is
+ * called for that SecurityManager.
+ *
+ * @throws SecurityException
+ * if a SecurityManager is installed and it does not allow
+ * access to the Thread.
+ *
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public final void checkAccess() {
+ // Forwards the message to the SecurityManager (if there's one) passing
+ // the receiver as parameter
+
+ SecurityManager currentManager = System.getSecurityManager();
+ if (currentManager != null) {
+ currentManager.checkAccess(this);
+ }
+ }
+
+ /**
+ * Returns the number of stack frames in this thread.
+ *
+ * @return Number of stack frames
+ * @deprecated The results of this call were never well defined. To make
+ * things worse, it would depend on whether the Thread was
+ * suspended or not, and suspend was deprecated too.
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public int countStackFrames() {
+ return getStackTrace().length;
+ }
+
+ /**
+ * Returns the Thread of the caller, that is, the current Thread.
+ *
+ * @return the current Thread.
+ *
+ * @since Android 1.0
+ */
+ public static Thread currentThread() {
+ return VMThread.currentThread();
+ }
+
+ /**
+ * Destroys the receiver without any monitor cleanup.
+ *
+ * @deprecated Not implemented.
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public void destroy() {
+ throw new NoSuchMethodError("Thread.destroy()"); // TODO Externalize???
+ }
+
+ /**
+ * Prints to the standard error stream a text representation of the current
+ * stack for this Thread.
+ *
+ * @see Throwable#printStackTrace()
+ *
+ * @since Android 1.0
+ */
+ public static void dumpStack() {
+ new Throwable("stack dump").printStackTrace();
+ }
+
+ /**
+ * Copies an array with all Threads which are in the same ThreadGroup as the
+ * receiver - and subgroups - into the array <code>threads</code> passed as
+ * parameter. If the array passed as parameter is too small no exception is
+ * thrown - the extra elements are simply not copied.
+ *
+ * @param threads
+ * array into which the Threads will be copied
+ * @return How many Threads were copied over
+ * @throws SecurityException
+ * if the installed SecurityManager fails
+ * {@link SecurityManager#checkAccess(Thread)}
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ *
+ * @since Android 1.0
+ */
+ public static int enumerate(Thread[] threads) {
+ Thread thread = Thread.currentThread();
+ thread.checkAccess();
+ return thread.getThreadGroup().enumerate(threads);
+ }
+
+ /**
+ * <p>
+ * Returns the stack traces of all the currently live threads and puts them
+ * into the given map.
+ * </p>
+ *
+ * @return A Map of current Threads to StackTraceElement arrays.
+ * @throws SecurityException
+ * if the current SecurityManager fails the
+ * {@link SecurityManager#checkPermission(java.security.Permission)}
+ * call.
+ *
+ * @since Android 1.0
+ */
+ public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkPermission(new RuntimePermission("getStackTrace"));
+ securityManager.checkPermission(new RuntimePermission("modifyThreadGroup"));
+ }
+
+ Map<Thread, StackTraceElement[]> map = new HashMap<Thread, StackTraceElement[]>();
+
+ // Find out how many live threads we have. Allocate a bit more
+ // space than needed, in case new ones are just being created.
+ int count = ThreadGroup.mSystem.activeCount();
+ Thread[] threads = new Thread[count + count / 2];
+
+ // Enumerate the threads and collect the stacktraces.
+ count = ThreadGroup.mSystem.enumerate(threads);
+ for (int i = 0; i < count; i++) {
+ map.put(threads[i], threads[i].getStackTrace());
+ }
+
+ return map;
+ }
+
+ /**
+ * Returns the context ClassLoader for this Thread.
+ * <p>
+ * If the conditions
+ * <ol>
+ * <li>there is a security manager
+ * <li>the caller's class loader is not null
+ * <li>the caller's class loader is not the same as the requested
+ * context class loader and not an ancestor thereof
+ * </ol>
+ * are satisfied, a security check for
+ * <code>RuntimePermission("getClassLoader")</code> is performed first.
+ *
+ * @return ClassLoader The context ClassLoader
+ * @see java.lang.ClassLoader
+ * @see #getContextClassLoader()
+ *
+ * @throws SecurityException
+ * if the aforementioned security check fails.
+ *
+ * @since Android 1.0
+ */
+ public ClassLoader getContextClassLoader() {
+ // First, if the conditions
+ // 1) there is a security manager
+ // 2) the caller's class loader is not null
+ // 3) the caller's class loader is not the same as the context
+ // class loader and not an ancestor thereof
+ // are satisfied we should perform a security check.
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ ClassLoader calling = VMStack.getCallingClassLoader();
+
+ if (calling != null && !calling.isAncestorOf(contextClassLoader)) {
+ sm.checkPermission(new RuntimePermission("getClassLoader"));
+ }
+ }
+
+ return contextClassLoader;
+ }
+
+ /**
+ * Returns the default exception handler that's executed when uncaught
+ * exception terminates a thread.
+ *
+ * @return an {@link UncaughtExceptionHandler} or <code>null</code> if
+ * none exists.
+ *
+ * @since Android 1.0
+ */
+ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
+ return defaultUncaughtHandler;
+ }
+
+ /**
+ * Returns the thread's identifier. The ID is a positive <code>long</code>
+ * generated on thread creation, is unique to the thread, and doesn't change
+ * during the lifetime of the thread; the ID may be reused after the thread
+ * has been terminated.
+ *
+ * @return the thread's ID.
+ *
+ * @since Android 1.0
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the name of the Thread.
+ *
+ * @return the Thread's name
+ *
+ * @since Android 1.0
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the priority of the Thread.
+ *
+ * @return the Thread's priority
+ * @see Thread#setPriority
+ *
+ * @since Android 1.0
+ */
+ public final int getPriority() {
+ return priority;
+ }
+
+ /**
+ * Returns the a stack trace representing the current execution state of
+ * this Thread.
+ * <p>
+ * The <code>RuntimePermission("getStackTrace")</code> is checked before
+ * returning a result.
+ * </p>
+ *
+ * @return an array of StackTraceElements.
+ * @throws SecurityException
+ * if the current SecurityManager fails the
+ * {@link SecurityManager#checkPermission(java.security.Permission)}
+ * call.
+ *
+ * @since Android 1.0
+ */
+ public StackTraceElement[] getStackTrace() {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkPermission(new RuntimePermission("getStackTrace"));
+ }
+
+ StackTraceElement ste[] = VMStack.getThreadStackTrace(this);
+ return ste != null ? ste : new StackTraceElement[0];
+ }
+
+ /**
+ * Returns the current state of the Thread. This method is useful for
+ * monitoring purposes.
+ *
+ * @return a {@link State} value.
+ *
+ * @since Android 1.0
+ */
+ public State getState() {
+ // TODO This is ugly and should be implemented better.
+ VMThread vmt = this.vmThread;
+
+ // Make sure we have a valid reference to an object. If native code
+ // deletes the reference we won't run into a null reference later.
+ VMThread thread = vmThread;
+ if (thread != null) {
+ // If the Thread Object became invalid or was not yet started,
+ // getStatus() will return -1.
+ int state = thread.getStatus();
+ if(state != -1) {
+ return VMThread.STATE_MAP[state];
+ }
+ }
+ return hasBeenStarted ? Thread.State.TERMINATED : Thread.State.NEW;
+ }
+
+ /**
+ * Returns the ThreadGroup to which this Thread belongs.
+ *
+ * @return the Thread's ThreadGroup
+ *
+ * @since Android 1.0
+ */
+ public final ThreadGroup getThreadGroup() {
+ // TODO This should actually be done at native termination.
+ if (getState() == Thread.State.TERMINATED) {
+ return null;
+ } else {
+ return group;
+ }
+ }
+
+ /**
+ * Returns the thread's uncaught exception handler. If not explicitly set,
+ * then the ThreadGroup's handler is returned. If the thread is terminated,
+ * then <code>null</code> is returned.
+ *
+ * @return an {@link UncaughtExceptionHandler} instance or {@code null}.
+ *
+ * @since Android 1.0
+ */
+ public UncaughtExceptionHandler getUncaughtExceptionHandler() {
+ if (uncaughtHandler != null)
+ return uncaughtHandler;
+ else
+ return group; // ThreadGroup is instance of UEH
+ }
+
+ /**
+ * Posts an interrupt request to this {@code Thread}. Unless the caller is
+ * the {@link #currentThread()}, the method {@code checkAccess()} is called
+ * for the installed {@code SecurityManager}, if any. This may result in a
+ * {@code SecurityException} being thrown. The further behavior depends on
+ * the state of this {@code Thread}:
+ * <ul>
+ * <li>
+ * {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods
+ * or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will
+ * be woken up, their interrupt status will be cleared, and they receive an
+ * {@link InterruptedException}.
+ * <li>
+ * {@code Thread}s blocked in an I/O operation of an
+ * {@link java.nio.channels.InterruptibleChannel} will have their interrupt
+ * status set and receive an
+ * {@link java.nio.channels.ClosedByInterruptException}. Also, the channel
+ * will be closed.
+ * <li>
+ * {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have
+ * their interrupt status set and return immediately. They don't receive an
+ * exception in this case.
+ * <ul>
+ *
+ * @throws SecurityException
+ * if <code>checkAccess()</code> fails with a SecurityException
+ * @see java.lang.SecurityException
+ * @see java.lang.SecurityManager
+ * @see Thread#interrupted
+ * @see Thread#isInterrupted
+ *
+ * @since Android 1.0
+ */
+ public void interrupt() {
+ checkAccess();
+
+ if (interruptAction != null) {
+ interruptAction.run();
+ }
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.interrupt();
+ }
+ }
+
+ /**
+ * Returns a <code>boolean</code> indicating whether the current Thread (
+ * <code>currentThread()</code>) has a pending interrupt request (<code>
+ * true</code>) or not (<code>false</code>). It also has the side-effect of
+ * clearing the flag.
+ *
+ * @return a <code>boolean</code> indicating the interrupt status
+ * @see Thread#currentThread
+ * @see Thread#interrupt
+ * @see Thread#isInterrupted
+ *
+ * @since Android 1.0
+ */
+ public static boolean interrupted() {
+ return VMThread.interrupted();
+ }
+
+ /**
+ * Returns <code>true</code> if the receiver has already been started and
+ * still runs code (hasn't died yet). Returns <code>false</code> either if
+ * the receiver hasn't been started yet or if it has already started and run
+ * to completion and died.
+ *
+ * @return a <code>boolean</code> indicating the lifeness of the Thread
+ * @see Thread#start
+ *
+ * @since Android 1.0
+ */
+ public final boolean isAlive() {
+ Thread.State state = getState();
+
+ return (state != Thread.State.TERMINATED && state != Thread.State.NEW);
+ }
+
+ /**
+ * Returns a <code>boolean</code> indicating whether the receiver is a
+ * daemon Thread (<code>true</code>) or not (<code>false</code>) A
+ * daemon Thread only runs as long as there are non-daemon Threads running.
+ * When the last non-daemon Thread ends, the whole program ends no matter if
+ * it had daemon Threads still running or not.
+ *
+ * @return a <code>boolean</code> indicating whether the Thread is a daemon
+ * @see Thread#setDaemon
+ *
+ * @since Android 1.0
+ */
+ public final boolean isDaemon() {
+ return daemon;
+ }
+
+ /**
+ * Returns a <code>boolean</code> indicating whether the receiver has a
+ * pending interrupt request (<code>true</code>) or not (
+ * <code>false</code>)
+ *
+ * @return a <code>boolean</code> indicating the interrupt status
+ * @see Thread#interrupt
+ * @see Thread#interrupted
+ *
+ * @since Android 1.0
+ */
+ public boolean isInterrupted() {
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ return vmt.isInterrupted();
+ }
+
+ return false;
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies.
+ *
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ *
+ * @since Android 1.0
+ */
+ public final void join() throws InterruptedException {
+ join(0, 0);
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ *
+ * @since Android 1.0
+ */
+ public final void join(long millis) throws InterruptedException {
+ join(millis, 0);
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @param nanos Extra nanosecond precision
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ *
+ * @since Android 1.0
+ */
+ public final void join(long millis, int nanos) throws InterruptedException {
+ if (millis < 0 || nanos < 0 || nanos > 999999) {
+ throw new IllegalArgumentException();
+ }
+
+ VMThread t;
+
+ t = this.vmThread;
+
+ if (t != null) {
+ synchronized (t) {
+ if (isAlive())
+ t.wait(millis, nanos);
+ }
+ }
+ }
+
+ /**
+ * Resumes a suspended Thread. This is a no-op if the receiver was never
+ * suspended, or suspended and already resumed. If the receiver is
+ * suspended, however, makes it resume to the point where it was when it was
+ * suspended.
+ *
+ * @throws SecurityException
+ * if <code>checkAccess()</code> fails with a SecurityException
+ * @see Thread#suspend()
+ * @deprecated Used with deprecated method {@link Thread#suspend}
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public final void resume() {
+ checkAccess();
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.resume();
+ }
+ }
+
+ /**
+ * Calls the <code>run()</code> method of the Runnable object the receiver
+ * holds. If no Runnable is set, does nothing.
+ *
+ * @see Thread#start
+ *
+ * @since Android 1.0
+ */
+ public void run() {
+ if (target != null) {
+ target.run();
+ }
+ }
+
+ /**
+ * Set the context ClassLoader for the receiver.
+ * <p>
+ * The <code>RuntimePermission("setContextClassLoader")</code>
+ * is checked prior to setting the handler.
+ * </p>
+ *
+ * @param cl The context ClassLoader
+ * @throws SecurityException if the current SecurityManager fails the
+ * checkPermission call.
+ * @see java.lang.ClassLoader
+ * @see #getContextClassLoader()
+ *
+ * @since Android 1.0
+ */
+ public void setContextClassLoader(ClassLoader cl) {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkPermission(new RuntimePermission("setContextClassLoader"));
+ }
+
+ contextClassLoader = cl;
+ }
+
+ /**
+ * Set if the receiver is a daemon Thread or not. This can only be done
+ * before the Thread starts running.
+ *
+ * @param isDaemon
+ * indicates whether the Thread should be daemon or not
+ * @throws SecurityException
+ * if <code>checkAccess()</code> fails with a SecurityException
+ * @see Thread#isDaemon
+ *
+ * @since Android 1.0
+ */
+ public final void setDaemon(boolean isDaemon) {
+ checkAccess();
+
+ if (hasBeenStarted) {
+ throw new IllegalThreadStateException("Thread already started."); // TODO Externalize?
+ }
+
+ if (vmThread == null) {
+ daemon = isDaemon;
+ }
+ }
+
+ /**
+ * <p>
+ * Sets the default uncaught exception handler. This handler is invoked in
+ * case any Thread dies due to an unhandled exception.
+ * </p>
+ * <p>
+ * The <code>RuntimePermission("setDefaultUncaughtExceptionHandler")</code>
+ * is checked prior to setting the handler.
+ * </p>
+ *
+ * @param handler
+ * The handler to set or <code>null</code>.
+ * @throws SecurityException
+ * if the current SecurityManager fails the checkPermission
+ * call.
+ *
+ * @since Android 1.0
+ */
+ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkPermission(new RuntimePermission ("setDefaultUncaughtExceptionHandler"));
+ }
+
+ Thread.defaultUncaughtHandler = handler;
+ }
+
+ /**
+ * Set the action to be executed when interruption, which is probably be
+ * used to implement the interruptible channel. The action is null by
+ * default. And if this method is invoked by passing in a non-null value,
+ * this action's run() method will be invoked in <code>interrupt()</code>.
+ * <p>
+ * This is required internally by NIO, so even if it looks like it's
+ * useless, don't delete it!
+ *
+ * @param action the action to be executed when interruption
+ */
+ @SuppressWarnings("unused")
+ private void setInterruptAction(Runnable action) {
+ this.interruptAction = action;
+ }
+
+ /**
+ * Sets the name of the Thread.
+ *
+ * @param threadName the new name for the Thread
+ * @throws SecurityException if <code>checkAccess()</code> fails with a
+ * SecurityException
+ * @see Thread#getName
+ *
+ * @since Android 1.0
+ */
+ public final void setName(String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException();
+ }
+
+ checkAccess();
+
+ name = threadName;
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ /* notify the VM that the thread name has changed */
+ vmt.nameChanged(threadName);
+ }
+ }
+
+ /**
+ * Sets the priority of the Thread. Note that the final priority set may not
+ * be the parameter that was passed - it will depend on the receiver's
+ * ThreadGroup. The priority cannot be set to be higher than the receiver's
+ * ThreadGroup's maxPriority().
+ *
+ * @param priority
+ * new priority for the Thread
+ * @throws SecurityException
+ * if <code>checkAccess()</code> fails with a SecurityException
+ * @throws IllegalArgumentException
+ * if the new priority is greater than Thread.MAX_PRIORITY or
+ * less than Thread.MIN_PRIORITY
+ * @see Thread#getPriority
+ *
+ * @since Android 1.0
+ */
+ public final void setPriority(int priority) {
+ checkAccess();
+
+ if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+ throw new IllegalArgumentException("Prioritiy out of range"); // TODO Externalize?
+ }
+
+ if (priority > group.getMaxPriority()) {
+ priority = group.getMaxPriority();
+ }
+
+ this.priority = priority;
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.setPriority(priority);
+ }
+ }
+
+ /**
+ * <p>
+ * Sets the uncaught exception handler. This handler is invoked in case this
+ * Thread dies due to an unhandled exception.
+ * </p>
+ *
+ * @param handler
+ * The handler to set or <code>null</code>.
+ * @throws SecurityException
+ * if the current SecurityManager fails the checkAccess call.
+ *
+ * @since Android 1.0
+ */
+ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ checkAccess();
+
+ uncaughtHandler = handler;
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds). The precision is not guaranteed - the
+ * Thread may sleep more or less than requested.
+ *
+ * @param time
+ * The time to sleep in milliseconds.
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ *
+ * @since Android 1.0
+ */
+ public static void sleep(long time) throws InterruptedException {
+ Thread.sleep(time, 0);
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds and nanoseconds). The precision is not
+ * guaranteed - the Thread may sleep more or less than requested.
+ *
+ * @param millis
+ * The time to sleep in milliseconds.
+ * @param nanos
+ * Extra nanosecond precision
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ *
+ * @since Android 1.0
+ */
+ public static void sleep(long millis, int nanos) throws InterruptedException {
+ VMThread.sleep(millis, nanos);
+ }
+
+ /**
+ * Starts the new Thread of execution. The <code>run()</code> method of
+ * the receiver will be called by the receiver Thread itself (and not the
+ * Thread calling <code>start()</code>).
+ *
+ * @throws IllegalThreadStateException if the Thread has been started before
+ *
+ * @see Thread#run
+ *
+ * @since Android 1.0
+ */
+ public synchronized void start() {
+ if (hasBeenStarted) {
+ throw new IllegalThreadStateException("Thread already started."); // TODO Externalize?
+ }
+
+ hasBeenStarted = true;
+
+ VMThread.create(this, stackSize);
+ }
+
+ /**
+ * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
+ * resumed if it was suspended and awakened if it was sleeping, so that it
+ * can proceed to throw ThreadDeath.
+ *
+ * @throws SecurityException if <code>checkAccess()</code> fails with a
+ * SecurityException
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public final void stop() {
+ stop(new ThreadDeath());
+ }
+
+ /**
+ * Requests the receiver Thread to stop and throw the
+ * <code>throwable()</code>. The Thread is resumed if it was suspended
+ * and awakened if it was sleeping, so that it can proceed to throw the
+ * <code>throwable()</code>.
+ *
+ * @param throwable Throwable object to be thrown by the Thread
+ * @throws SecurityException if <code>checkAccess()</code> fails with a
+ * SecurityException
+ * @throws NullPointerException if <code>throwable()</code> is
+ * <code>null</code>
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public final synchronized void stop(Throwable throwable) {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkAccess(this);
+ if (Thread.currentThread() != this) {
+ securityManager.checkPermission(new RuntimePermission("stopThread"));
+ }
+ }
+
+ if (throwable == null) {
+ throw new NullPointerException();
+ }
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.stop(throwable);
+ }
+ }
+
+ /**
+ * Suspends this Thread. This is a no-op if the receiver is suspended. If
+ * the receiver <code>isAlive()</code> however, suspended it until <code>
+ * resume()</code> is sent to it. Suspend requests are not queued, which
+ * means that N requests are equivalent to just one - only one resume
+ * request is needed in this case.
+ *
+ * @throws SecurityException
+ * if <code>checkAccess()</code> fails with a SecurityException
+ * @see Thread#resume()
+ * @deprecated May cause deadlocks.
+ *
+ * @since Android 1.0
+ */
+ @Deprecated
+ public final void suspend() {
+ checkAccess();
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.suspend();
+ }
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of the
+ * Thread. It includes the Thread's name, priority, and group name.
+ *
+ * @return a printable representation for the receiver.
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public String toString() {
+ return "Thread[" + name + "," + priority + "," + group.getName() + "]";
+ }
+
+ /**
+ * Causes the calling Thread to yield execution time to another Thread that
+ * is ready to run. The actual scheduling is implementation-dependent.
+ *
+ * @since Android 1.0
+ */
+ public static void yield() {
+ VMThread.yield();
+ }
+
+ /**
+ * Indicates whether the current Thread has a monitor lock on the specified
+ * object.
+ *
+ * @param object the object to test for the monitor lock
+ * @return true if the current thread has a monitor lock on the specified
+ * object; false otherwise
+ *
+ * @since Android 1.0
+ */
+ public static boolean holdsLock(Object object) {
+ return currentThread().vmThread.holdsLock(object);
+ }
+
+ /**
+ * Implemented by objects that want to handle cases where a thread is being
+ * terminated by an uncaught exception. Upon such termination, the handler
+ * is notified of the terminating thread and causal exception. If there is
+ * no explicit handler set then the thread's group is the default handler.
+ *
+ * @since Android 1.0
+ */
+ public static interface UncaughtExceptionHandler {
+ /**
+ * The thread is being terminated by an uncaught exception. Further
+ * exceptions thrown in this method are prevent the remainder of the
+ * method from executing, but are otherwise ignored.
+ *
+ * @param thread the thread that has an uncaught exception
+ * @param ex the exception that was thrown
+ *
+ * @since Android 1.0
+ */
+ void uncaughtException(Thread thread, Throwable ex);
+ }
+
+ /**
+ * Implementation of <code>unpark()</code>. See {@link LangAccessImpl}.
+ */
+ /*package*/ void unpark() {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ /*
+ * vmThread is null before the thread is start()ed. In
+ * this case, we just go ahead and set the state to
+ * PREEMPTIVELY_UNPARKED. Since this happens before the
+ * thread is started, we don't have to worry about
+ * synchronizing with it.
+ */
+ parkState = ParkState.PREEMPTIVELY_UNPARKED;
+ return;
+ }
+
+ synchronized (vmt) {
+ switch (parkState) {
+ case ParkState.PREEMPTIVELY_UNPARKED: {
+ /*
+ * Nothing to do in this case: By definition, a
+ * preemptively unparked thread is to remain in
+ * the preemptively unparked state if it is told
+ * to unpark.
+ */
+ break;
+ }
+ case ParkState.UNPARKED: {
+ parkState = ParkState.PREEMPTIVELY_UNPARKED;
+ break;
+ }
+ default /*parked*/: {
+ parkState = ParkState.UNPARKED;
+ vmt.notifyAll();
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of <code>parkFor()</code>. See {@link LangAccessImpl}.
+ * This method must only be called when <code>this</code> is the current
+ * thread.
+ *
+ * @param nanos number of nanoseconds to park for
+ */
+ /*package*/ void parkFor(long nanos) {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ // Running threads should always have an associated vmThread.
+ throw new AssertionError();
+ }
+
+ synchronized (vmt) {
+ switch (parkState) {
+ case ParkState.PREEMPTIVELY_UNPARKED: {
+ parkState = ParkState.UNPARKED;
+ break;
+ }
+ case ParkState.UNPARKED: {
+ long millis = nanos / 1000000;
+ nanos %= 1000000;
+
+ parkState = ParkState.PARKED;
+ try {
+ vmt.wait(millis, (int) nanos);
+ } catch (InterruptedException ex) {
+ interrupt();
+ } finally {
+ /*
+ * Note: If parkState manages to become
+ * PREEMPTIVELY_UNPARKED before hitting this
+ * code, it should left in that state.
+ */
+ if (parkState == ParkState.PARKED) {
+ parkState = ParkState.UNPARKED;
+ }
+ }
+ break;
+ }
+ default /*parked*/: {
+ throw new AssertionError(
+ "shouldn't happen: attempt to repark");
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of <code>parkUntil()</code>. See {@link LangAccessImpl}.
+ * This method must only be called when <code>this</code> is the current
+ * thread.
+ *
+ * @param time absolute milliseconds since the epoch to park until
+ */
+ /*package*/ void parkUntil(long time) {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ // Running threads should always have an associated vmThread.
+ throw new AssertionError();
+ }
+
+ synchronized (vmt) {
+ /*
+ * Note: This conflates the two time bases of "wall clock"
+ * time and "monotonic uptime" time. However, given that
+ * the underlying system can only wait on monotonic time,
+ * it is unclear if there is any way to avoid the
+ * conflation. The downside here is that if, having
+ * calculated the delay, the wall clock gets moved ahead,
+ * this method may not return until well after the wall
+ * clock has reached the originally designated time. The
+ * reverse problem (the wall clock being turned back)
+ * isn't a big deal, since this method is allowed to
+ * spuriously return for any reason, and this situation
+ * can safely be construed as just such a spurious return.
+ */
+ long delayMillis = time - System.currentTimeMillis();
+
+ if (delayMillis <= 0) {
+ parkState = ParkState.UNPARKED;
+ } else {
+ parkFor(delayMillis * 1000000);
+ }
+ }
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/ThreadGroup.java b/luni-kernel/src/main/java/java/lang/ThreadGroup.java
new file mode 100644
index 0000000..33fa31e
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ThreadGroup.java
@@ -0,0 +1,926 @@
+/*
+ * 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 java.lang;
+
+/**
+ * A {@code ThreadGroup} is a means of organizing {@link Thread}s into a
+ * hierarchical structure. A {@code ThreadGroup} can contain zero or more
+ * {@code Thread}s and zero or more other {@code ThreadGroup}s. Each {@code
+ * Thread} and each {@code ThreadGroup} (except the root group) has a unique
+ * parent {@code ThreadGroup}. The result is a tree whose inner nodes are
+ * {@code ThreadGroup}s and whose leaf nodes are {@code Threads}. The unique
+ * root of the tree is a {@code ThreadGroup} that is created at VM startup and
+ * has the name "system". The benefit of using {@code ThreadGroup}s, in addition
+ * to the mere housekeeping aspect, is that all {@code Thread}s in a {@code
+ * ThreadGroup} can be manipulated together, that is, the {@code ThreadGroup}
+ * has methods that delegate to all its all {@code Thread}s.
+ *
+ * @see Thread
+ * @see SecurityManager
+ *
+ * @since Android 1.0
+ */
+public class ThreadGroup implements Thread.UncaughtExceptionHandler {
+
+ // Name of this ThreadGroup
+ private String name;
+ // BEGIN android-note
+ // VM needs this field name for debugging.
+ // END android-note
+
+ // Maximum priority for Threads inside this ThreadGroup
+ private int maxPriority = Thread.MAX_PRIORITY;
+
+ // The ThreadGroup to which this ThreadGroup belongs
+ ThreadGroup parent;
+ // BEGIN android-note
+ // VM needs this field name for debugging.
+ // END android-note
+
+ int numThreads;
+
+ // The Threads this ThreadGroup contains
+ private Thread[] childrenThreads = new Thread[5];
+
+ // The number of children groups
+ int numGroups;
+
+ // The ThreadGroups this ThreadGroup contains
+ private ThreadGroup[] childrenGroups = new ThreadGroup[3];
+
+ // Locked when using the childrenGroups field
+ private class ChildrenGroupsLock {}
+ private Object childrenGroupsLock = new ChildrenGroupsLock();
+
+ // Locked when using the childrenThreads field
+ private class ChildrenThreadsLock {}
+ private Object childrenThreadsLock = new ChildrenThreadsLock();
+
+ // Whether this ThreadGroup is a daemon ThreadGroup or not
+ private boolean isDaemon;
+
+ // Whether this ThreadGroup has already been destroyed or not
+ private boolean isDestroyed;
+
+ // BEGIN android-added
+ /* the VM uses these directly; do not rename */
+ static ThreadGroup mSystem = new ThreadGroup();
+ static ThreadGroup mMain = new ThreadGroup(mSystem, "main");
+ // END android-added
+
+ // BEGIN android-removed
+ // /**
+ // * Used by the JVM to create the "system" ThreadGroup. Construct a
+ // * ThreadGroup instance, and assign the name "system".
+ // */
+ // private ThreadGroup() {
+ // name = "system";
+ // }
+ // END android-removed
+
+ /**
+ * Constructs a new ThreadGroup with the name provided. The new ThreadGroup
+ * will be child of the ThreadGroup to which the
+ * {@code Thread.currentThread()} belongs.
+ *
+ * @param name the name for the ThreadGroup being created
+ *
+ * @throws SecurityException if {@code checkAccess()} for the parent
+ * group fails with a SecurityException
+ *
+ * @see java.lang.Thread#currentThread
+ */
+
+ public ThreadGroup(String name) {
+ this(Thread.currentThread().getThreadGroup(), name);
+ }
+
+ /**
+ * Constructs a new ThreadGroup with the name provided, as child of the
+ * ThreadGroup {@code parent}.
+ *
+ * @param parent the parent ThreadGroup
+ * @param name the name for the ThreadGroup being created
+ *
+ * @throws NullPointerException if {@code parent} is
+ * {@code null}
+ * @throws SecurityException if {@code checkAccess()} for the parent
+ * group fails with a SecurityException
+ * @throws IllegalThreadStateException if {@code parent} has been
+ * destroyed already
+ */
+ public ThreadGroup(ThreadGroup parent, String name) {
+ super();
+ if (Thread.currentThread() != null) {
+ // If parent is null we must throw NullPointerException, but that
+ // will be done "for free" with the message send below
+ parent.checkAccess();
+ }
+
+ this.name = name;
+ this.setParent(parent);
+ if (parent != null) {
+ this.setMaxPriority(parent.getMaxPriority());
+ if (parent.isDaemon()) {
+ this.setDaemon(true);
+ }
+ }
+ }
+
+ /**
+ * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
+ * but we have an additional group above that in Android.
+ */
+ ThreadGroup() {
+ this.name = "system";
+ this.setParent(null);
+ }
+
+ /**
+ * Returns the number of Threads which are children of the receiver,
+ * directly or indirectly and are running.
+ *
+ * @return the number of children Threads
+ */
+
+ public int activeCount() {
+ // BEGIN android-changed
+ int count = 0;
+ // Lock the children thread list
+ synchronized (this.childrenThreadsLock) {
+ for (int i = 0; i < numThreads; i++) {
+ if(childrenThreads[i].isAlive()) {
+ count++;
+ }
+ }
+ }
+ // END android-changed
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ count += this.childrenGroups[i].activeCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns the number of ThreadGroups which are children of the receiver,
+ * directly or indirectly.
+ *
+ * @return the number of children ThreadGroups
+ */
+ public int activeGroupCount() {
+ int count = 0;
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ // One for this group & the subgroups
+ count += 1 + this.childrenGroups[i].activeGroupCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Adds a Thread to the receiver. This should only be visible to class
+ * java.lang.Thread, and should only be called when a new Thread is created
+ * and initialized by the constructor.
+ *
+ * @param thread Thread to add to the receiver
+ *
+ * @throws IllegalThreadStateException if the receiver has been destroyed
+ * already
+ *
+ * @see #remove(java.lang.Thread)
+ */
+ final void add(Thread thread) throws IllegalThreadStateException {
+ synchronized (this.childrenThreadsLock) {
+ if (!isDestroyed) {
+ if (childrenThreads.length == numThreads) {
+ Thread[] newThreads = new Thread[childrenThreads.length * 2];
+ System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads);
+ newThreads[numThreads++] = thread;
+ childrenThreads = newThreads;
+ } else {
+ childrenThreads[numThreads++] = thread;
+ }
+ } else {
+ throw new IllegalThreadStateException();
+ }
+ }
+ }
+
+ /**
+ * Adds a ThreadGroup to the receiver.
+ *
+ * @param g ThreadGroup to add to the receiver
+ *
+ * @throws IllegalThreadStateException if the receiver has been destroyed
+ * already
+ */
+ private void add(ThreadGroup g) throws IllegalThreadStateException {
+ synchronized (this.childrenGroupsLock) {
+ if (!isDestroyed) {
+ if (childrenGroups.length == numGroups) {
+ ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2];
+ System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups);
+ newGroups[numGroups++] = g;
+ childrenGroups = newGroups;
+ } else {
+ childrenGroups[numGroups++] = g;
+ }
+ } else {
+ throw new IllegalThreadStateException();
+ }
+ }
+ }
+
+ /**
+ * Does nothing. The definition of this method depends on the deprecated
+ * method {@link #suspend()}. The exact behavior of this call was never
+ * specified.
+ *
+ * @param b Used to control low memory implicit suspension
+ * @return {@code true} (always)
+ *
+ * @deprecated Required deprecated method suspend().
+ */
+ @Deprecated
+ public boolean allowThreadSuspension(boolean b) {
+ // Does not apply to this VM, no-op
+ return true;
+ }
+
+ /**
+ * Checks the accessibility of the ThreadGroup from the perspective of the
+ * caller. If there is a SecurityManager installed, calls
+ * {@code checkAccess} with the receiver as a parameter, otherwise does
+ * nothing.
+ */
+ public final void checkAccess() {
+ // Forwards the message to the SecurityManager (if there's one) passing
+ // the receiver as parameter
+ SecurityManager currentManager = System.getSecurityManager();
+ if (currentManager != null) {
+ currentManager.checkAccess(this);
+ }
+ }
+
+ /**
+ * Destroys the receiver and recursively all its subgroups. It is only legal
+ * to destroy a ThreadGroup that has no Threads in it. Any daemon
+ * ThreadGroup is destroyed automatically when it becomes empty (no Threads
+ * and no ThreadGroups in it).
+ *
+ * @throws IllegalThreadStateException if the receiver or any of its
+ * subgroups has been destroyed already or if it still contains
+ * threads.
+ * @throws SecurityException if {@code this.checkAccess()} fails with
+ * a SecurityException
+ */
+
+ public final void destroy() {
+ checkAccess();
+
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenThreadsLock) {
+ synchronized (this.childrenGroupsLock) {
+ // BEGIN android-added
+ if (this.isDestroyed) {
+ throw new IllegalThreadStateException(
+ "Thread group was already destroyed: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ if (this.numThreads > 0) {
+ throw new IllegalThreadStateException(
+ "Thread group still contains threads: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ // END android-added
+ int toDestroy = numGroups;
+ // Call recursively for subgroups
+ for (int i = 0; i < toDestroy; i++) {
+ // We always get the first element - remember, when the
+ // child dies it removes itself from our collection. See
+ // below.
+ this.childrenGroups[0].destroy();
+ }
+
+ if (parent != null) {
+ parent.remove(this);
+ }
+
+ // Now that the ThreadGroup is really destroyed it can be tagged
+ // as so
+ this.isDestroyed = true;
+ }
+ }
+ }
+
+ /*
+ * Auxiliary method that destroys the receiver and recursively all its
+ * subgroups if the receiver is a daemon ThreadGroup.
+ *
+ * @see #destroy
+ * @see #setDaemon
+ * @see #isDaemon
+ */
+ private void destroyIfEmptyDaemon() {
+ // Has to be non-destroyed daemon to make sense
+ synchronized (this.childrenThreadsLock) {
+ if (isDaemon && !isDestroyed && numThreads == 0) {
+ synchronized (this.childrenGroupsLock) {
+ if (numGroups == 0) {
+ destroy();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates over all active threads in this group (and its sub-groups) and
+ * stores the threads in the given array. Returns when the array is full or
+ * no more threads remain, whichever happens first.
+ *
+ * @param threads the array into which the Threads will be copied
+ * @return the number of Threads that were copied
+ */
+ public int enumerate(Thread[] threads) {
+ return enumerate(threads, true);
+ }
+
+ /**
+ * Iterates over all active threads in this group (and, optionally, its
+ * sub-groups) and stores the threads in the given array. Returns when the
+ * array is full or no more threads remain, whichever happens first.
+ *
+ * @param threads the array into which the Threads will be copied
+ * @param recurse indicates whether Threads in subgroups should be
+ * recursively copied as well
+ * @return the number of Threads that were copied
+ *
+ */
+ public int enumerate(Thread[] threads, boolean recurse) {
+ return enumerateGeneric(threads, recurse, 0, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and its sub-groups) and
+ * and stores the groups in the given array. Returns when the array is full
+ * or no more groups remain, whichever happens first.
+ *
+ * @param groups the array into which the ThreadGroups will be copied
+ * @return the number of ThreadGroups that were copied
+ *
+ */
+ public int enumerate(ThreadGroup[] groups) {
+ return enumerate(groups, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and, optionally, its
+ * sub-groups) and and stores the groups in the given array. Returns when
+ * the array is full or no more groups remain, whichever happens first.
+ *
+ * @param groups the array into which the ThreadGroups will be copied
+ * @param recurse indicates whether ThreadGroups in subgroups should be
+ * recursively copied as well or not
+ * @return the number of ThreadGroups that were copied
+ *
+ */
+ public int enumerate(ThreadGroup[] groups, boolean recurse) {
+ return enumerateGeneric(groups, recurse, 0, false);
+ }
+
+ /**
+ * Copies into <param>enumeration</param> starting at
+ * <param>enumerationIndex</param> all Threads or ThreadGroups in the
+ * receiver. If <param>recurse</param> is true, recursively enumerate the
+ * elements in subgroups.
+ *
+ * If the array passed as parameter is too small no exception is thrown -
+ * the extra elements are simply not copied.
+ *
+ * @param enumeration array into which the elements will be copied
+ * @param recurse Indicates whether subgroups should be enumerated or not
+ * @param enumerationIndex Indicates in which position of the enumeration
+ * array we are
+ * @param enumeratingThreads Indicates whether we are enumerating Threads or
+ * ThreadGroups
+ * @return How many elements were enumerated/copied over
+ */
+ private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
+ boolean enumeratingThreads) {
+ checkAccess();
+
+ Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads
+ : (Object[]) childrenGroups;
+ Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;
+
+ synchronized (syncLock) { // Lock this subpart of the tree as we walk
+ for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {
+ if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumeration[enumerationIndex++] = immediateCollection[i];
+ }
+ }
+ }
+
+ if (recurse) { // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse,
+ enumerationIndex, enumeratingThreads);
+ }
+ }
+ }
+ return enumerationIndex;
+ }
+
+ /**
+ * Returns the maximum allowed priority for a Thread in the receiver.
+ *
+ * @return the maximum priority
+ *
+ * @see #setMaxPriority
+ */
+ public final int getMaxPriority() {
+ return maxPriority;
+ }
+
+ /**
+ * Returns the name of the receiver.
+ *
+ * @return the receiver's name
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the receiver's parent ThreadGroup. It can be {@code null} if the
+ * receiver is the the root ThreadGroup.
+ *
+ * @return the parent ThreadGroup
+ *
+ */
+ public final ThreadGroup getParent() {
+ if (parent != null) {
+ parent.checkAccess();
+ }
+ return parent;
+ }
+
+ /**
+ * Interrupts every Thread in the receiver and recursively in all its
+ * subgroups.
+ *
+ * @throws SecurityException if {@code this.checkAccess()} fails with
+ * a SecurityException
+ *
+ * @see Thread#interrupt
+ */
+ public final void interrupt() {
+ checkAccess();
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenThreadsLock) {
+ for (int i = 0; i < numThreads; i++) {
+ this.childrenThreads[i].interrupt();
+ }
+ }
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ this.childrenGroups[i].interrupt();
+ }
+ }
+ }
+
+ /**
+ * Checks whether the receiver is a daemon ThreadGroup.
+ *
+ * @return true if (and only if) the receiver is a daemon ThreadGroup
+ *
+ * @see #setDaemon
+ * @see #destroy
+ */
+ public final boolean isDaemon() {
+ return isDaemon;
+ }
+
+ /**
+ * Checks whether the receiver has already been destroyed.
+ *
+ * @return true if (and only if) the receiver has already been destroyed
+ *
+ * @see #destroy
+ */
+ public synchronized boolean isDestroyed() {
+ return isDestroyed;
+ }
+
+ /**
+ * Outputs to {@code System.out} a text representation of the
+ * hierarchy of Threads and ThreadGroups in the receiver (and recursively).
+ * Proper indentation is done to suggest the nesting of groups inside groups
+ * and threads inside groups.
+ */
+ public void list() {
+ // We start in a fresh line
+ System.out.println();
+ list(0);
+ }
+
+ /*
+ * Outputs to {@code System.out}a text representation of the
+ * hierarchy of Threads and ThreadGroups in the receiver (and recursively).
+ * The indentation will be four spaces per level of nesting.
+ *
+ * @param levels How many levels of nesting, so that proper indentation can
+ * be output.
+ */
+ private void list(int levels) {
+ for (int i = 0; i < levels; i++) {
+ System.out.print(" "); // 4 spaces for each level
+ }
+
+ // Print the receiver
+ System.out.println(this.toString());
+
+ // Print the children threads, with 1 extra indentation
+ synchronized (this.childrenThreadsLock) {
+ for (int i = 0; i < numThreads; i++) {
+ // children get an extra indentation, 4 spaces for each level
+ for (int j = 0; j <= levels; j++) {
+ System.out.print(" ");
+ }
+ System.out.println(this.childrenThreads[i]);
+ }
+ }
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ this.childrenGroups[i].list(levels + 1);
+ }
+ }
+ }
+
+ /**
+ * Checks whether the receiver is a direct or indirect parent group of a
+ * given ThreadGroup.
+ *
+ * @param g the potential child ThreadGroup
+ *
+ * @return true if (and only if) the receiver is parent of {@code g}
+ *
+ */
+ public final boolean parentOf(ThreadGroup g) {
+ while (g != null) {
+ if (this == g) {
+ return true;
+ }
+ g = g.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a Thread from the receiver. This should only be visible to class
+ * java.lang.Thread, and should only be called when a Thread dies.
+ *
+ * @param thread Thread to remove from the receiver
+ *
+ * @see #add(Thread)
+ */
+ final void remove(java.lang.Thread thread) {
+ synchronized (this.childrenThreadsLock) {
+ for (int i = 0; i < numThreads; i++) {
+ if (childrenThreads[i].equals(thread)) {
+ numThreads--;
+ System
+ .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads
+ - i);
+ childrenThreads[numThreads] = null;
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+
+ /**
+ * Removes an immediate subgroup from the receiver.
+ *
+ * @param g ThreadGroup to remove from the receiver
+ *
+ * @see #add(Thread)
+ * @see #add(ThreadGroup)
+ */
+ private void remove(ThreadGroup g) {
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ if (childrenGroups[i].equals(g)) {
+ numGroups--;
+ System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i);
+ childrenGroups[numGroups] = null;
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+
+ /**
+ * Resumes every Thread in the receiver and recursively in all its
+ * subgroups.
+ *
+ * @throws SecurityException if {@code this.checkAccess()} fails with
+ * a SecurityException
+ *
+ * @see Thread#resume
+ * @see #suspend
+ *
+ * @deprecated Requires deprecated method Thread.resume().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void resume() {
+ checkAccess();
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenThreadsLock) {
+ for (int i = 0; i < numThreads; i++) {
+ this.childrenThreads[i].resume();
+ }
+ }
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ this.childrenGroups[i].resume();
+ }
+ }
+ }
+
+ /**
+ * Configures the receiver to be a daemon ThreadGroup or not. Daemon
+ * ThreadGroups are automatically destroyed when they become empty.
+ *
+ * @param isDaemon the new value defining if receiver should be daemon or
+ * not
+ *
+ * @throws SecurityException if {@code checkAccess()} for the parent
+ * group fails with a SecurityException
+ *
+ * @see #isDaemon
+ * @see #destroy
+ */
+ public final void setDaemon(boolean isDaemon) {
+ checkAccess();
+ this.isDaemon = isDaemon;
+ }
+
+ /**
+ * Configures the maximum allowed priority for a Thread in the receiver and
+ * recursively in all its subgroups.
+ *
+ * One can never change the maximum priority of a ThreadGroup to be higher
+ * than it was. Such an attempt will not result in an exception, it will
+ * simply leave the ThreadGroup with its current maximum priority.
+ *
+ * @param newMax the new maximum priority to be set
+ *
+ * @throws SecurityException if {@code checkAccess()} fails with a
+ * SecurityException
+ * @throws IllegalArgumentException if the new priority is greater than
+ * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
+ *
+ * @see #getMaxPriority
+ */
+ public final void setMaxPriority(int newMax) {
+ checkAccess();
+
+ if (newMax <= this.maxPriority) {
+ if (newMax < Thread.MIN_PRIORITY) {
+ newMax = Thread.MIN_PRIORITY;
+ }
+
+ int parentPriority = parent == null ? newMax : parent.getMaxPriority();
+ this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ // ??? why not maxPriority
+ for (int i = 0; i < numGroups; i++) {
+ this.childrenGroups[i].setMaxPriority(newMax);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the parent ThreadGroup of the receiver, and adds the receiver to the
+ * parent's collection of immediate children (if {@code parent} is
+ * not {@code null}).
+ *
+ * @param parent The parent ThreadGroup, or null if the receiver is to be
+ * the root ThreadGroup
+ *
+ * @see #getParent
+ * @see #parentOf
+ */
+ private void setParent(ThreadGroup parent) {
+ if (parent != null) {
+ parent.add(this);
+ }
+ this.parent = parent;
+ }
+
+ /**
+ * Stops every Thread in the receiver and recursively in all its subgroups.
+ *
+ * @throws SecurityException if {@code this.checkAccess()} fails with
+ * a SecurityException
+ *
+ * @see Thread#stop()
+ * @see Thread#stop(Throwable)
+ * @see ThreadDeath
+ *
+ * @deprecated Requires deprecated method Thread.stop().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void stop() {
+ if (stopHelper()) {
+ Thread.currentThread().stop();
+ }
+ }
+
+ /**
+ * @deprecated Requires deprecated method Thread.suspend().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ private final boolean stopHelper() {
+ checkAccess();
+
+ boolean stopCurrent = false;
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenThreadsLock) {
+ Thread current = Thread.currentThread();
+ for (int i = 0; i < numThreads; i++) {
+ if (this.childrenThreads[i] == current) {
+ stopCurrent = true;
+ } else {
+ this.childrenThreads[i].stop();
+ }
+ }
+ }
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ stopCurrent |= this.childrenGroups[i].stopHelper();
+ }
+ }
+ return stopCurrent;
+ }
+
+ /**
+ * Suspends every Thread in the receiver and recursively in all its
+ * subgroups.
+ *
+ * @throws SecurityException if {@code this.checkAccess()} fails with
+ * a SecurityException
+ *
+ * @see Thread#suspend
+ * @see #resume
+ *
+ * @deprecated Requires deprecated method Thread.suspend().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void suspend() {
+ if (suspendHelper()) {
+ Thread.currentThread().suspend();
+ }
+ }
+
+ /**
+ * @deprecated Requires deprecated method Thread.suspend().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ private final boolean suspendHelper() {
+ checkAccess();
+
+ boolean suspendCurrent = false;
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenThreadsLock) {
+ Thread current = Thread.currentThread();
+ for (int i = 0; i < numThreads; i++) {
+ if (this.childrenThreads[i] == current) {
+ suspendCurrent = true;
+ } else {
+ this.childrenThreads[i].suspend();
+ }
+ }
+ }
+ // Lock this subpart of the tree as we walk
+ synchronized (this.childrenGroupsLock) {
+ for (int i = 0; i < numGroups; i++) {
+ suspendCurrent |= this.childrenGroups[i].suspendHelper();
+ }
+ }
+ return suspendCurrent;
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of the
+ * receiver.
+ *
+ * @return a printable representation of the ThreadGroup
+ */
+ @Override
+ public String toString() {
+ return getClass().getName() + "[name=" + this.getName() + ",maxpri="
+ + this.getMaxPriority() + "]";
+ }
+
+ /**
+ * Handles uncaught exceptions. Any uncaught exception in any Thread
+ * is forwarded (by the VM) to the Thread's ThreadGroup by sending this
+ * message (uncaughtException). This allows users to define custom
+ * ThreadGroup classes and custom behavior for when a Thread has an
+ * uncaughtException or when it does (ThreadDeath).
+ *
+ * @param t the Thread that terminated with an uncaught exception
+ * @param e the uncaught exception itself
+ *
+ * @see Thread#stop()
+ * @see Thread#stop(Throwable)
+ * @see ThreadDeath
+ */
+ public void uncaughtException(Thread t, Throwable e) {
+ // BEGIN android-changed
+ if (parent != null) {
+ parent.uncaughtException(t, e);
+ } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
+ // TODO The spec is unclear regarding this. What do we do?
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
+ } else if (!(e instanceof ThreadDeath)) {
+ // No parent group, has to be 'system' Thread Group
+ e.printStackTrace(System.err);
+ }
+ // END android-changed
+ }
+
+ // BEGIN android-added
+ /**
+ * Non-standard method for adding a thread to a group, required by Dalvik.
+ *
+ * @param thread Thread to add to the receiver
+ *
+ * @throws IllegalThreadStateException if the receiver has been destroyed
+ * already
+ *
+ * @see #add(java.lang.Thread)
+ * @see #removeThread(java.lang.Thread)
+ */
+ void addThread(Thread thread) throws IllegalThreadStateException {
+ add(thread);
+ }
+
+ /**
+ * Non-standard method for adding a thread to a group, required by Dalvik.
+ *
+ * @param thread Thread to add to the receiver
+ *
+ * @throws IllegalThreadStateException if the receiver has been destroyed
+ * already
+ *
+ * @see #remove(java.lang.Thread)
+ * @see #addThread(java.lang.Thread)
+ */
+ void removeThread(Thread thread) throws IllegalThreadStateException {
+ remove(thread);
+ }
+ // END android-added
+}
diff --git a/luni-kernel/src/main/java/java/lang/Throwable.java b/luni-kernel/src/main/java/java/lang/Throwable.java
new file mode 100644
index 0000000..12b4e9b
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/Throwable.java
@@ -0,0 +1,410 @@
+/*
+ * 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 java.lang;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * The superclass of all classes which can be thrown by the virtual machine. The
+ * two direct subclasses are recoverable exceptions ({@code Exception}) and
+ * unrecoverable errors ({@code Error}). This class provides common methods for
+ * accessing a string message which provides extra information about the
+ * circumstances in which the {@code Throwable} was created (basically an error
+ * message in most cases), and for saving a stack trace (that is, a record of
+ * the call stack at a particular point in time) which can be printed later.
+ * <p>
+ * A {@code Throwable} can also include a cause, which is a nested {@code
+ * Throwable} that represents the original problem that led to this {@code
+ * Throwable}. It is often used for wrapping various types of errors into a
+ * common {@code Throwable} without losing the detailed original error
+ * information. When printing the stack trace, the trace of the cause is
+ * included.
+ *
+ * @see Error
+ * @see Exception
+ * @see RuntimeException
+ *
+ * @since Android 1.0
+ */
+public class Throwable implements java.io.Serializable {
+ private static final long serialVersionUID = -3042686055658047285L;
+
+ /**
+ * The message provided when the exception was created.
+ */
+ private String detailMessage;
+
+ /**
+ * The cause of this Throwable. Null when there is no cause.
+ */
+ private Throwable cause = this;
+
+ // BEGIN android-added
+ /**
+ * An intermediate representation of the stack trace. This field may
+ * be accessed by the VM; do not rename.
+ */
+ private volatile Object stackState;
+ // END android-added
+
+ /**
+ * A fully-expanded representation of the stack trace.
+ */
+ private StackTraceElement[] stackTrace;
+
+ /**
+ * Constructs a new {@code Throwable} that includes the current stack trace.
+ *
+ * @since Android 1.0
+ */
+ public Throwable() {
+ super();
+ fillInStackTrace();
+ }
+
+ /**
+ * Constructs a new {@code Throwable} with the current stack trace and the
+ * specified detail message.
+ *
+ * @param detailMessage
+ * the detail message for this {@code Throwable}.
+ * @since Android 1.0
+ */
+ public Throwable(String detailMessage) {
+ this();
+ this.detailMessage = detailMessage;
+ }
+
+ /**
+ * Constructs a new {@code Throwable} with the current stack trace, the
+ * specified detail message and the specified cause.
+ *
+ * @param detailMessage
+ * the detail message for this {@code Throwable}.
+ * @param throwable
+ * the cause of this {@code Throwable}.
+ * @since Android 1.0
+ */
+ public Throwable(String detailMessage, Throwable throwable) {
+ this();
+ this.detailMessage = detailMessage;
+ cause = throwable;
+ }
+
+ /**
+ * Constructs a new {@code Throwable} with the current stack trace and the
+ * specified cause.
+ *
+ * @param throwable
+ * the cause of this {@code Throwable}.
+ * @since Android 1.0
+ */
+ public Throwable(Throwable throwable) {
+ this();
+ this.detailMessage = throwable == null ? null : throwable.toString();
+ cause = throwable;
+ }
+
+ // BEGIN android-changed
+ /**
+ * Records the stack trace from the point where this method has been called
+ * to this {@code Throwable}. The method is public so that code which
+ * catches a {@code Throwable} and then re-throws it can adjust the stack
+ * trace to represent the location where the exception was re-thrown.
+ *
+ * @return this {@code Throwable} instance.
+ * @since Android 1.0
+ */
+ public Throwable fillInStackTrace() {
+ // Fill in the intermediate representation
+ stackState = nativeFillInStackTrace();
+ // Mark the full representation as empty
+ stackTrace = null;
+ return this;
+ }
+ // END android-changed
+
+ /**
+ * Returns the extra information message which was provided when this
+ * {@code Throwable} was created. Returns {@code null} if no message was
+ * provided at creation time.
+ *
+ * @return this {@code Throwable}'s detail message.
+ * @since Android 1.0
+ */
+ public String getMessage() {
+ return detailMessage;
+ }
+
+ /**
+ * Returns the extra information message which was provided when this
+ * {@code Throwable} was created. Returns {@code null} if no message was
+ * provided at creation time. Subclasses may override this method to return
+ * localized text for the message. The Android reference implementation
+ * returns the unlocalized detail message.
+ *
+ * @return this {@code Throwable}'s localized detail message.
+ * @since Android 1.0
+ */
+ public String getLocalizedMessage() {
+ return getMessage();
+ }
+
+ /**
+ * Returns the array of stack trace elements of this {@code Throwable}. Each
+ * {@code StackTraceElement} represents an entry in the call stack. The
+ * element at position 0 is the top of the stack, that is, the stack frame
+ * where this {@code Throwable} is thrown.
+ *
+ * @return a copy of the array of {@code StackTraceElement}s representing
+ * the call stack. Changes in the array obtained from this call will
+ * not change the call stack stored in this {@code Throwable}.
+ * @see #printStackTrace()
+ * @since Android 1.0
+ */
+ public StackTraceElement[] getStackTrace() {
+ return getInternalStackTrace().clone();
+ }
+
+ /**
+ * Sets the array of stack trace elements. Each {@code StackTraceElement}
+ * represents an entry in the call stack. A copy of the specified array is
+ * stored in this {@code Throwable}. will be returned by {@code
+ * getStackTrace()} and printed by {@code printStackTrace()}.
+ *
+ * @param trace
+ * the new array of {@code StackTraceElement}s. A copy of the
+ * array is stored in this {@code Throwable}, so subsequent
+ * changes to {@code trace} will not change the call stack stored
+ * in this {@code Throwable}.
+ * @throws NullPointerException
+ * if any element in {@code trace} is {@code null}.
+ * @see #printStackTrace()
+ * @since Android 1.0
+ */
+ public void setStackTrace(StackTraceElement[] trace) {
+ StackTraceElement[] newTrace = trace.clone();
+ for (java.lang.StackTraceElement element : newTrace) {
+ if (element == null) {
+ throw new NullPointerException();
+ }
+ }
+ stackTrace = newTrace;
+ }
+
+ /**
+ * Writes a printable representation of this {@code Throwable}'s stack trace
+ * to the {@code System.err} stream.
+ *
+ * @since Android 1.0
+ */
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Counts the number of duplicate stack frames, starting from the
+ * end of the stack.
+ *
+ * @param currentStack a stack to compare
+ * @param parentStack a stack to compare
+ *
+ * @return the number of duplicate stack frames.
+ */
+ private static int countDuplicates(StackTraceElement[] currentStack,
+ StackTraceElement[] parentStack) {
+ int duplicates = 0;
+ int parentIndex = parentStack.length;
+ for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) {
+ StackTraceElement parentFrame = parentStack[parentIndex];
+ if (parentFrame.equals(currentStack[i])) {
+ duplicates++;
+ } else {
+ break;
+ }
+ }
+ return duplicates;
+ }
+
+ /**
+ * Returns an array of StackTraceElement. Each StackTraceElement
+ * represents a entry on the stack.
+ *
+ * @return an array of StackTraceElement representing the stack
+ */
+ private StackTraceElement[] getInternalStackTrace() {
+ if (stackTrace == null) {
+ // BEGIN android-changed
+ stackTrace = nativeGetStackTrace(stackState);
+ stackState = null; // Clean up intermediate representation
+ // END android-changed
+ }
+ return stackTrace;
+ }
+
+ /**
+ * Writes a printable representation of this {@code Throwable}'s stack trace
+ * to the specified print stream. If the {@code Throwable} contains a
+ * {@link #getCause() cause}, the method will be invoked recursively for
+ * the nested {@code Throwable}.
+ *
+ * @param err
+ * the stream to write the stack trace on.
+ * @since Android 1.0
+ */
+ public void printStackTrace(PrintStream err) {
+ err.println(toString());
+ // Don't use getStackTrace() as it calls clone()
+ // Get stackTrace, in case stackTrace is reassigned
+ StackTraceElement[] stack = getInternalStackTrace();
+ for (java.lang.StackTraceElement element : stack) {
+ err.println("\tat " + element);
+ }
+
+ StackTraceElement[] parentStack = stack;
+ Throwable throwable = getCause();
+ while (throwable != null) {
+ err.print("Caused by: ");
+ err.println(throwable);
+ StackTraceElement[] currentStack = throwable.getInternalStackTrace();
+ int duplicates = countDuplicates(currentStack, parentStack);
+ for (int i = 0; i < currentStack.length - duplicates; i++) {
+ err.println("\tat " + currentStack[i]);
+ }
+ if (duplicates > 0) {
+ err.println("\t... " + duplicates + " more");
+ }
+ parentStack = currentStack;
+ throwable = throwable.getCause();
+ }
+ }
+
+ /**
+ * Writes a printable representation of this {@code Throwable}'s stack trace
+ * to the specified print writer. If the {@code Throwable} contains a
+ * {@link #getCause() cause}, the method will be invoked recursively for the
+ * nested {@code Throwable}.
+ *
+ * @param err
+ * the writer to write the stack trace on.
+ * @since Android 1.0
+ */
+ public void printStackTrace(PrintWriter err) {
+ err.println(toString());
+ // Don't use getStackTrace() as it calls clone()
+ // Get stackTrace, in case stackTrace is reassigned
+ StackTraceElement[] stack = getInternalStackTrace();
+ for (java.lang.StackTraceElement element : stack) {
+ err.println("\tat " + element);
+ }
+
+ StackTraceElement[] parentStack = stack;
+ Throwable throwable = getCause();
+ while (throwable != null) {
+ err.print("Caused by: ");
+ err.println(throwable);
+ StackTraceElement[] currentStack = throwable.getInternalStackTrace();
+ int duplicates = countDuplicates(currentStack, parentStack);
+ for (int i = 0; i < currentStack.length - duplicates; i++) {
+ err.println("\tat " + currentStack[i]);
+ }
+ if (duplicates > 0) {
+ err.println("\t... " + duplicates + " more");
+ }
+ parentStack = currentStack;
+ throwable = throwable.getCause();
+ }
+ }
+
+ @Override
+ public String toString() {
+ String msg = getLocalizedMessage();
+ String name = getClass().getName();
+ if (msg == null) {
+ return name;
+ }
+ return new StringBuffer(name.length() + 2 + msg.length()).append(name).append(": ")
+ .append(msg).toString();
+ }
+
+ /**
+ * Initializes the cause of this {@code Throwable}. The cause can only be
+ * initialized once.
+ *
+ * @param throwable
+ * the cause of this {@code Throwable}.
+ * @return this {@code Throwable} instance.
+ * @throws IllegalArgumentException
+ * if {@code Throwable} is this object.
+ * @throws IllegalStateException
+ * if the cause has already been initialized.
+ * @since Android 1.0
+ */
+ public Throwable initCause(Throwable throwable) {
+ // BEGIN android-note
+ // removed synchronized modifier
+ // END android-note
+ if (cause == this) {
+ if (throwable != this) {
+ cause = throwable;
+ return this;
+ }
+ throw new IllegalArgumentException("Cause cannot be the receiver");
+ }
+ throw new IllegalStateException("Cause already initialized");
+ }
+
+ /**
+ * Returns the cause of this {@code Throwable}, or {@code null} if there is
+ * no cause.
+ *
+ * @return Throwable this {@code Throwable}'s cause.
+ * @since Android 1.0
+ */
+ public Throwable getCause() {
+ if (cause == this) {
+ return null;
+ }
+ return cause;
+ }
+
+ private void writeObject(ObjectOutputStream s) throws IOException {
+ // ensure the stackTrace field is initialized
+ getInternalStackTrace();
+ s.defaultWriteObject();
+ }
+
+ // BEGIN android-added
+ /*
+ * Creates a compact, VM-specific collection of goodies, suitable for
+ * storing in the "stackState" field, based on the current thread's
+ * call stack.
+ */
+ native private static Object nativeFillInStackTrace();
+
+ /*
+ * Creates an array of StackTraceElement objects from the data held
+ * in "stackState".
+ */
+ native private static StackTraceElement[] nativeGetStackTrace(Object stackState);
+ // END android-added
+}
+
diff --git a/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java b/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java
new file mode 100644
index 0000000..c33ab6e
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.ref;
+
+/**
+ * Implements a phantom reference, which is the weakest of the three types of
+ * references. Once the garbage collector decides that an object {@code obj} is
+ * <a href="package.html#definitions>phantom-reachable</a>, it is being enqueued
+ * on the corresponding queue, but its referent is not cleared. That is, the
+ * reference queue of the phantom reference must explicitly be processed by some
+ * application code. As a consequence, a phantom reference that is not
+ * registered with any reference queue does not make any sense.
+ * <p>
+ * Phantom references are useful for implementing cleanup operations that are
+ * necessary before an object gets garbage-collected. They are sometimes more
+ * flexible than the {@link Object#finalize()} method.
+ *
+ * @since Android 1.0
+ */
+public class PhantomReference<T> extends Reference<T> {
+
+ /**
+ * Constructs a new phantom reference and registers it with the given
+ * reference queue. The reference queue may be {@code null}, but this case
+ * does not make any sense, since the reference will never be enqueued, and
+ * the {@link #get()} method always returns {@code null}.
+ *
+ * @param r the referent to track
+ * @param q the queue to register the phantom reference object with
+ *
+ * @since Android 1.0
+ */
+ public PhantomReference(T r, ReferenceQueue<? super T> q) {
+ super();
+ referent = r;
+ queue = q;
+ }
+
+ /**
+ * Returns {@code null}. The referent of a phantom reference is not
+ * accessible.
+ *
+ * @return {@code null} (always)
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public T get() {
+ return null;
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/ref/Reference.java b/luni-kernel/src/main/java/java/lang/ref/Reference.java
new file mode 100644
index 0000000..ca7290b
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ref/Reference.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.ref;
+
+/**
+ * Provides an abstract class which describes behavior common to all reference
+ * objects. It is not possible to create immediate subclasses of
+ * {@code Reference} in addition to the ones provided by this package. It is
+ * also not desirable to do so, since references require very close cooperation
+ * with the system's garbage collector. The existing, specialized reference
+ * classes should be used instead.
+ *
+ * @since Android 1.0
+ */
+public abstract class Reference<T> {
+
+ /**
+ * The object to which this reference refers.
+ * VM requirement: this field <em>must</em> be called "referent"
+ * and be an object.
+ */
+ T referent;
+
+ /**
+ * If non-null, the queue on which this reference will be enqueued
+ * when the referent is appropriately reachable.
+ * VM requirement: this field <em>must</em> be called "queue"
+ * and be a java.lang.ref.ReferenceQueue.
+ */
+ @SuppressWarnings("unchecked")
+ ReferenceQueue queue;
+
+ /**
+ * Used internally by java.lang.ref.ReferenceQueue.
+ * VM requirement: this field <em>must</em> be called "queueNext"
+ * and be a java.lang.ref.Reference.
+ */
+ @SuppressWarnings("unchecked")
+ Reference queueNext;
+
+ /**
+ * Used internally by Dalvik.
+ * VM requirement: this field <em>must</em> be called "vmData"
+ * and be an int.
+ */
+ @SuppressWarnings("unused")
+ private int vmData;
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ Reference() {
+ super();
+ }
+
+ /**
+ * Makes the referent {@code null}. This does not force the reference
+ * object to be enqueued.
+ *
+ * @since Android 1.0
+ */
+ public void clear() {
+ referent = null;
+ }
+
+ /**
+ * An implementation of .enqueue() that is safe for the VM to call.
+ * If a Reference object is a subclass of any of the
+ * java.lang.ref.*Reference classes and that subclass overrides enqueue(),
+ * the VM may not call the overridden method.
+ * VM requirement: this method <em>must</em> be called "enqueueInternal",
+ * have the signature "()Z", and be private.
+ *
+ * @return {@code true} if this call has caused the {@code Reference} to
+ * become enqueued, or {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unchecked")
+ private synchronized boolean enqueueInternal() {
+ /* VM requirement:
+ * The VM assumes that this function only does work
+ * if "(queue != null && queueNext == null)".
+ * If that changes, Dalvik needs to change, too.
+ * (see MarkSweep.c:enqueueReference())
+ */
+ if (queue != null && queueNext == null) {
+ queue.enqueue(this);
+ queue = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Forces the reference object to be enqueued if it has been associated with
+ * a queue.
+ *
+ * @return {@code true} if this call has caused the {@code Reference} to
+ * become enqueued, or {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean enqueue() {
+ return enqueueInternal();
+ }
+
+ /**
+ * Returns the referent of the reference object.
+ *
+ * @return the referent to which reference refers, or {@code null} if the
+ * object has been cleared.
+ *
+ * @since Android 1.0
+ */
+ public T get() {
+ return referent;
+ }
+
+ /**
+ * Checks whether the reference object has been enqueued.
+ *
+ * @return {@code true} if the {@code Reference} has been enqueued, {@code
+ * false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isEnqueued() {
+ return queueNext != null;
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/ref/SoftReference.java b/luni-kernel/src/main/java/java/lang/ref/SoftReference.java
new file mode 100644
index 0000000..19253f1
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ref/SoftReference.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.ref;
+
+/**
+ * Implements a soft reference, which is the least-weak of the three types of
+ * references. Once the garbage collector has decided that an object {@code obj}
+ * is <a href="package.html#definitions>softly-reachable</a>, the following
+ * may happen, either immediately or at a later point:
+ *
+ * <ul>
+ * <li>
+ * A set {@code ref} of references is determined. {@code ref} contains the
+ * following elements:
+ * <ul>
+ * <li>
+ * All soft references pointing to {@code obj}.
+ * </li>
+ * <li>
+ * All soft references pointing to objects from which {@code obj} is
+ * strongly reachable.
+ * </li>
+ * </ul>
+ * </li>
+ * <li>
+ * All references in {@code ref} are atomically cleared.
+ * </li>
+ * <li>
+ * At the same time or some time in the future, all references in {@code
+ * ref} will be enqueued with their corresponding reference queues, if any.
+ * </li>
+ * </ul>
+ *
+ * The system may decide not to clear and enqueue soft references until a later
+ * time, yet all {@code SoftReference}s pointing to softly reachable objects are
+ * guaranteed to be cleared before the VM will throw an {@link
+ * java.lang.OutOfMemoryError}.
+ *
+ * Soft references are useful for caches that should automatically have
+ * their entries removed once they are not referenced any more (from outside),
+ * and there is a need for memory. The difference between a {@code
+ * SoftReference} and a {@code WeakReference} is the point of time at which the
+ * decision is made to clear and enqueue the reference:
+ *
+ * <ul>
+ * <li>
+ * A {@code SoftReference} should be cleared and enqueued <em>as late as
+ * possible</em>, that is, in case the VM is in danger of running out of
+ * memory.
+ * </li>
+ * <li>
+ * A {@code WeakReference} may be cleared and enqueued as soon as is
+ * known to be weakly-referenced.
+ * </li>
+ * </ul>
+ *
+ * @since Android 1.0
+ */
+public class SoftReference<T> extends Reference<T> {
+
+ /**
+ * Constructs a new soft reference to the given referent. The newly created
+ * reference is not registered with any reference queue.
+ *
+ * @param r the referent to track
+ *
+ * @since Android 1.0
+ */
+ public SoftReference(T r) {
+ super();
+ referent = r;
+ }
+
+ /**
+ * Constructs a new soft reference to the given referent. The newly created
+ * reference is registered with the given reference queue.
+ *
+ * @param r the referent to track
+ * @param q the queue to register to the reference object with. A null value
+ * results in a weak reference that is not associated with any
+ * queue.
+ *
+ * @since Android 1.0
+ */
+ public SoftReference(T r, ReferenceQueue<? super T> q) {
+ super();
+ referent = r;
+ queue = q;
+ }
+
+// BEGIN android-removed
+// /**
+// * Return the referent of the reference object.
+// *
+// * @return the referent to which reference refers, or {@code null} if the
+// * referent has been cleared.
+// *
+// * @since Android 1.0
+// */
+// @Override
+// public T get() {
+// return super.get();
+// }
+// END android-removed
+}
diff --git a/luni-kernel/src/main/java/java/lang/ref/WeakReference.java b/luni-kernel/src/main/java/java/lang/ref/WeakReference.java
new file mode 100644
index 0000000..813cc89
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/ref/WeakReference.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.ref;
+
+/**
+ * Implements a weak reference, which is the middle of the three types of
+ * references. Once the garbage collector decides that an object {@code obj} is
+ * is <a href="package.html#definitions>weakly-reachable</a>, the following
+ * happens:
+ *
+ * <ul>
+ * <li>
+ * A set {@code ref} of references is determined. {@code ref} contains the
+ * following elements:
+ * <ul>
+ * <li>
+ * All weak references pointing to {@code obj}.
+ * </li>
+ * <li>
+ * All weak references pointing to objects from which {@code obj} is
+ * either strongly or softly reachable.
+ * </li>
+ * </ul>
+ * </li>
+ * <li>
+ * All references in {@code ref} are atomically cleared.
+ * </li>
+ * <li>
+ * All objects formerly being referenced by {@code ref} become eligible for
+ * finalization.
+ * </li>
+ * <li>
+ * At some future point, all references in {@code ref} will be enqueued
+ * with their corresponding reference queues, if any.
+ * </li>
+ * </ul>
+ *
+ * Weak references are useful for mappings that should have their entries
+ * removed automatically once they are not referenced any more (from outside).
+ * The difference between a {@code SoftReference} and a {@code WeakReference} is
+ * the point of time at which the decision is made to clear and enqueue the
+ * reference:
+ *
+ * <ul>
+ * <li>
+ * A {@code SoftReference} should be cleared and enqueued <em>as late as
+ * possible</em>, that is, in case the VM is in danger of running out of
+ * memory.
+ * </li>
+ * <li>
+ * A {@code WeakReference} may be cleared and enqueued as soon as is
+ * known to be weakly-referenced.
+ * </li>
+ * </ul>
+ *
+ * @since Android 1.0
+ */
+public class WeakReference<T> extends Reference<T> {
+
+ /**
+ * Constructs a new weak reference to the given referent. The newly created
+ * reference is not registered with any reference queue.
+ *
+ * @param r the referent to track
+ *
+ * @since Android 1.0
+ */
+ public WeakReference(T r) {
+ super();
+ referent = r;
+ }
+
+ /**
+ * Constructs a new weak reference to the given referent. The newly created
+ * reference is registered with the given reference queue.
+ *
+ * @param r the referent to track
+ * @param q the queue to register to the reference object with. A null value
+ * results in a weak reference that is not associated with any
+ * queue.
+ *
+ * @since Android 1.0
+ */
+ public WeakReference(T r, ReferenceQueue<? super T> q) {
+ super();
+ referent = r;
+ queue = q;
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java b/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java
new file mode 100644
index 0000000..0178276
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+import java.util.Hashtable;
+
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.kernel.vm.ReflectionAccess;
+
+/**
+ * {@code AccessibleObject} is the superclass of all member reflection classes
+ * (Field, Constructor, Method). AccessibleObject provides the ability to toggle
+ * a flag controlling access checks for these objects. By default, accessing a
+ * member (for example, setting a field or invoking a method) checks the
+ * validity of the access (for example, invoking a private method from outside
+ * the defining class is prohibited) and throws IllegalAccessException if the
+ * operation is not permitted. If the accessible flag is set to true, these
+ * checks are omitted. This allows privileged code, such as Java object
+ * serialization, object inspectors, and debuggers to have complete access to
+ * objects.
+ *
+ * @see Field
+ * @see Constructor
+ * @see Method
+ * @see ReflectPermission
+ *
+ * @since Android 1.0
+ */
+public class AccessibleObject implements AnnotatedElement {
+
+ // If true, object is accessible, bypassing normal security checks
+ boolean flag = false;
+
+ /**
+ * one dimensional array
+ */
+ private static final String DIMENSION_1 = "[]";
+
+ /**
+ * two dimensional array
+ */
+ private static final String DIMENSION_2 = "[][]";
+
+ /**
+ * three dimensional array
+ */
+ private static final String DIMENSION_3 = "[][][]";
+
+ // Holds a mapping from Java type names to native type codes.
+ static Hashtable<String, String> trans;
+
+ static {
+ trans = new Hashtable<String, String>(9);
+ trans.put("byte", "B");
+ trans.put("char", "C");
+ trans.put("short", "S");
+ trans.put("int", "I");
+ trans.put("long", "J");
+ trans.put("float", "F");
+ trans.put("double", "D");
+ trans.put("void", "V");
+ trans.put("boolean", "Z");
+ }
+
+ /**
+ * Attempts to set the value of the accessible flag for all the objects in
+ * the array provided. Only one security check is performed. Setting this
+ * flag to {@code false} will enable access checks, setting to {@code true}
+ * will disable them. If there is a security manager, checkPermission is
+ * called with a {@code ReflectPermission("suppressAccessChecks")}.
+ *
+ * @param objects
+ * the accessible objects
+ * @param flag
+ * the new value for the accessible flag
+ *
+ * @throws SecurityException
+ * if the request is denied
+ *
+ * @see #setAccessible(boolean)
+ * @see ReflectPermission
+ *
+ * @since Android 1.0
+ */
+ public static void setAccessible(AccessibleObject[] objects, boolean flag)
+ throws SecurityException {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkPermission(new ReflectPermission("suppressAccessChecks"));
+ }
+
+ synchronized(AccessibleObject.class) {
+ for (int i = 0; i < objects.length; i++) {
+ objects[i].flag = flag;
+ }
+ }
+ }
+
+ /**
+ * Constructs a new {@code AccessibleObject} instance. {@code
+ * AccessibleObject} instances can only be constructed by the virtual
+ * machine.
+ *
+ * @since Android 1.0
+ */
+ protected AccessibleObject() {
+ super();
+ }
+
+ /**
+ * Indicates whether this object is accessible without security checks being
+ * performed. Returns the accessible flag.
+ *
+ * @return {@code true} if this object is accessible without security
+ * checks, {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isAccessible() {
+ return flag;
+ }
+
+ /**
+ * Attempts to set the value of the accessible flag. Setting this flag to
+ * {@code false} will enable access checks, setting to {@code true} will
+ * disable them. If there is a security manager, checkPermission is called
+ * with a {@code ReflectPermission("suppressAccessChecks")}.
+ *
+ * @param flag
+ * the new value for the accessible flag
+ *
+ * @throws SecurityException
+ * if the request is denied
+ *
+ * @see ReflectPermission
+ *
+ * @since Android 1.0
+ */
+ public void setAccessible(boolean flag) throws SecurityException {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ smgr.checkPermission(new ReflectPermission("suppressAccessChecks"));
+ }
+
+ this.flag = flag;
+ }
+
+ /**
+ * Sets the accessible flag on this instance without doing any checks.
+ *
+ * @param flag
+ * the new value for the accessible flag
+ */
+ /*package*/ void setAccessibleNoCheck(boolean flag) {
+ this.flag = flag;
+ }
+
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ return getAnnotation(annotationType) != null;
+ }
+
+ public Annotation[] getDeclaredAnnotations() {
+ throw new RuntimeException("subclass must override this method");
+ }
+
+ public Annotation[] getAnnotations() {
+ // for all but Class, getAnnotations == getDeclaredAnnotations
+ return getDeclaredAnnotations();
+ }
+
+ /* slow, but works for all sub-classes */
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException();
+ }
+ Annotation[] annos = getAnnotations();
+ for (int i = annos.length-1; i >= 0; --i) {
+ if (annos[i].annotationType() == annotationType) {
+ return (T) annos[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the signature for a class. This is the kind of signature used
+ * internally by the JVM, with one-character codes representing the basic
+ * types. It is not suitable for printing.
+ *
+ * @param clazz
+ * the class for which a signature is required
+ *
+ * @return The signature as a string
+ */
+ String getSignature(Class<?> clazz) {
+ String result = "";
+ String nextType = clazz.getName();
+
+ if(trans.containsKey(nextType)) {
+ result = trans.get(nextType);
+ } else {
+ if(clazz.isArray()) {
+ result = "[" + getSignature(clazz.getComponentType());
+ } else {
+ result = "L" + nextType + ";";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a printable String consisting of the canonical names of the
+ * classes contained in an array. The form is that used in parameter and
+ * exception lists, that is, the class or type names are separated by
+ * commas.
+ *
+ * @param types
+ * the array of classes
+ *
+ * @return The String of names
+ */
+ String toString(Class<?>[] types) {
+ StringBuilder result = new StringBuilder();
+
+ if (types.length != 0) {
+ result.append(types[0].getCanonicalName());
+ for (int i = 1; i < types.length; i++) {
+ result.append(',');
+ result.append(types[i].getCanonicalName());
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Gets the Signature attribute for this instance. Returns {@code null}
+ * if not found.
+ */
+ /*package*/ String getSignatureAttribute() {
+ /*
+ * Note: This method would have been declared abstract, but the
+ * standard API lists this class as concrete.
+ */
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Retrieve the signature attribute from an arbitrary class. This is
+ * the same as Class.getSignatureAttribute(), but it can be used from
+ * the java.lang.reflect package.
+ */
+ /*package*/ static String getClassSignatureAttribute(Class clazz) {
+ Object[] annotation = getClassSignatureAnnotation(clazz);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Retrieve the signature annotation from an arbitrary class. This is
+ * the same as Class.getSignatureAttribute(), but it can be used from
+ * the java.lang.reflect package.
+ */
+ private static native Object[] getClassSignatureAnnotation(Class clazz);
+
+ /**
+ * Gets the unique instance of {@link ReflectionAccessImpl}.
+ *
+ * @return non-null; the unique instance
+ */
+ static /*package*/ ReflectionAccess getReflectionAccess() {
+ return ReflectionAccessImpl.THE_ONE;
+ }
+
+
+ /**
+ * Appends the specified class name to the buffer. The class may represent
+ * a simple type, a reference type or an array type.
+ *
+ * @param sb buffer
+ * @param obj the class which name should be appended to the buffer
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendArrayType(StringBuilder sb, Class<?> obj) {
+ if (!obj.isArray()) {
+ sb.append(obj.getName());
+ return;
+ }
+ int dimensions = 1;
+ Class simplified = obj.getComponentType();
+ obj = simplified;
+ while (simplified.isArray()) {
+ obj = simplified;
+ dimensions++;
+ }
+ sb.append(obj.getName());
+ switch (dimensions) {
+ case 1:
+ sb.append(DIMENSION_1);
+ break;
+ case 2:
+ sb.append(DIMENSION_2);
+ break;
+ case 3:
+ sb.append(DIMENSION_3);
+ break;
+ default:
+ for (; dimensions > 0; dimensions--) {
+ sb.append(DIMENSION_1);
+ }
+ }
+ }
+
+ /**
+ * Appends names of the specified array classes to the buffer. The array
+ * elements may represent a simple type, a reference type or an array type.
+ * Output format: java.lang.Object[], java.io.File, void
+ *
+ * @param sb buffer
+ * @param objs array of classes to print the names
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendArrayType(StringBuilder sb, Class[] objs) {
+ if (objs.length > 0) {
+ appendArrayType(sb, objs[0]);
+ for (int i = 1; i < objs.length; i++) {
+ sb.append(',');
+ appendArrayType(sb, objs[i]);
+ }
+ }
+ }
+
+ /**
+ * Appends names of the specified array classes to the buffer. The array
+ * elements may represent a simple type, a reference type or an array type.
+ * Output format: java.lang.Object[], java.io.File, void
+ *
+ * @param sb buffer
+ * @param objs array of classes to print the names
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendArrayGenericType(StringBuilder sb, Type[] objs) {
+ if (objs.length > 0) {
+ appendGenericType(sb, objs[0]);
+ for (int i = 1; i < objs.length; i++) {
+ sb.append(',');
+ appendGenericType(sb, objs[i]);
+ }
+ }
+ }
+
+ /**
+ * Appends the generic type representation to the buffer.
+ *
+ * @param sb buffer
+ * @param obj the generic type which representation should be appended to the buffer
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendGenericType(StringBuilder sb, Type obj) {
+ if (obj instanceof TypeVariable) {
+ sb.append(((TypeVariable)obj).getName());
+ } else if (obj instanceof ParameterizedType) {
+ sb.append(obj.toString());
+ } else if (obj instanceof GenericArrayType) { //XXX: is it a working branch?
+ Type simplified = ((GenericArrayType)obj).getGenericComponentType();
+ appendGenericType(sb, simplified);
+ sb.append("[]");
+ } else if (obj instanceof Class) {
+ Class c = ((Class<?>)obj);
+ if (c.isArray()){
+ String as[] = c.getName().split("\\[");
+ int len = as.length-1;
+ if (as[len].length() > 1){
+ sb.append(as[len].substring(1, as[len].length()-1));
+ } else {
+ char ch = as[len].charAt(0);
+ if (ch == 'I')
+ sb.append("int");
+ else if (ch == 'B')
+ sb.append("byte");
+ else if (ch == 'J')
+ sb.append("long");
+ else if (ch == 'F')
+ sb.append("float");
+ else if (ch == 'D')
+ sb.append("double");
+ else if (ch == 'S')
+ sb.append("short");
+ else if (ch == 'C')
+ sb.append("char");
+ else if (ch == 'Z')
+ sb.append("boolean");
+ else if (ch == 'V') //XXX: is it a working branch?
+ sb.append("void");
+ }
+ for (int i = 0; i < len; i++){
+ sb.append("[]");
+ }
+ } else {
+ sb.append(c.getName());
+ }
+ }
+ }
+
+ /**
+ * Appends names of the specified array classes to the buffer. The array
+ * elements may represent a simple type, a reference type or an array type.
+ * In case if the specified array element represents an array type its
+ * internal will be appended to the buffer.
+ * Output format: [Ljava.lang.Object;, java.io.File, void
+ *
+ * @param sb buffer
+ * @param objs array of classes to print the names
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendSimpleType(StringBuilder sb, Class<?>[] objs) {
+ if (objs.length > 0) {
+ sb.append(objs[0].getName());
+ for (int i = 1; i < objs.length; i++) {
+ sb.append(',');
+ sb.append(objs[i].getName());
+ }
+ }
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/Array.java b/luni-kernel/src/main/java/java/lang/reflect/Array.java
new file mode 100644
index 0000000..d633e69
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/Array.java
@@ -0,0 +1,820 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+/**
+ * This class provides static methods to create and access arrays dynamically.
+ *
+ * @since Android 1.0
+ */
+public final class Array {
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Array(){
+ //do nothing
+ }
+
+ /**
+ * Returns the element of the array at the specified index. This reproduces
+ * the effect of {@code array[index]}. If the array component is a primitive
+ * type, the result is automatically wrapped.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element, possibly wrapped
+ *
+ * @throws NullPointerException
+ * if the array is null
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static Object get(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof Object[])
+ return ((Object[]) array)[index];
+
+ if (array instanceof boolean[])
+ return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE;
+
+ if (array instanceof byte[])
+ return new Byte(((byte[]) array)[index]);
+
+ if (array instanceof char[])
+ return new Character(((char[]) array)[index]);
+
+ if (array instanceof short[])
+ return new Short(((short[]) array)[index]);
+
+ if (array instanceof int[])
+ return new Integer(((int[]) array)[index]);
+
+ if (array instanceof long[])
+ return new Long(((long[]) array)[index]);
+
+ if (array instanceof float[])
+ return new Float(((float[]) array)[index]);
+
+ if (array instanceof double[])
+ return new Double(((double[]) array)[index]);
+
+ if (array == null)
+ throw new NullPointerException();
+
+ throw new IllegalArgumentException("Not an array");
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code boolean}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static boolean getBoolean(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof boolean[]) {
+ return ((boolean[]) array)[index];
+ } else if (array == null) {
+ throw new NullPointerException();
+ } else if (array.getClass().isArray()) {
+ throw new IllegalArgumentException("Wrong array type");
+ } else {
+ throw new IllegalArgumentException("Not an array");
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code byte}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static byte getByte(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof byte[]) {
+ return ((byte[]) array)[index];
+ } else {
+ return getBoolean(array, index) ? (byte)1 : (byte)0;
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code char}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static char getChar(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof char[]) {
+ return ((char[]) array)[index];
+ } else if (array == null) {
+ throw new NullPointerException();
+ } else if (array.getClass().isArray()) {
+ throw new IllegalArgumentException("Wrong array type");
+ } else {
+ throw new IllegalArgumentException("Not an array");
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code double}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static double getDouble(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof double[]) {
+ return ((double[]) array)[index];
+ } else {
+ return getFloat(array, index);
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code float}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static float getFloat(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof float[]) {
+ return ((float[]) array)[index];
+ } else {
+ return getLong(array, index);
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to an
+ * {@code int}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static int getInt(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof int[]) {
+ return ((int[]) array)[index];
+ } else {
+ return getShort(array, index);
+ }
+ }
+
+ /**
+ * Returns the length of the array. This reproduces the effect of {@code
+ * array.length}
+ *
+ * @param array
+ * the array
+ *
+ * @return the length of the array
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array
+ *
+ * @since Android 1.0
+ */
+ public static int getLength(Object array) {
+ if (array instanceof Object[])
+ return ((Object[]) array).length;
+
+ if (array instanceof boolean[])
+ return ((boolean[]) array).length;
+
+ if (array instanceof byte[])
+ return ((byte[]) array).length;
+
+ if (array instanceof char[])
+ return ((char[]) array).length;
+
+ if (array instanceof short[])
+ return ((short[]) array).length;
+
+ if (array instanceof int[])
+ return ((int[]) array).length;
+
+ if (array instanceof long[])
+ return ((long[]) array).length;
+
+ if (array instanceof float[])
+ return ((float[]) array).length;
+
+ if (array instanceof double[])
+ return ((double[]) array).length;
+
+ if (array == null)
+ throw new NullPointerException();
+
+ throw new IllegalArgumentException("Not an array");
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code long}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static long getLong(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof long[]) {
+ return ((long[]) array)[index];
+ } else {
+ return getInt(array, index);
+ }
+ }
+
+ /**
+ * Returns the element of the array at the specified index, converted to a
+ * {@code short}, if possible. This reproduces the effect of {@code
+ * array[index]}
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ *
+ * @return the requested element
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the element at the
+ * index position can not be converted to the return type
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static short getShort(Object array, int index)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof short[])
+ return ((short[]) array)[index];
+
+ return getByte(array, index);
+ }
+
+ /**
+ * Returns a new multidimensional array of the specified component type and
+ * dimensions. This reproduces the effect of {@code new
+ * componentType[d0][d1]...[dn]} for a dimensions array of { d0, d1, ... ,
+ * dn }.
+ *
+ * @param componentType
+ * the component type of the new array
+ * @param dimensions
+ * the dimensions of the new array
+ *
+ * @return the new array
+ *
+ * @throws NullPointerException
+ * if the component type is {@code null}
+ * @throws NegativeArraySizeException
+ * if any of the dimensions are negative
+ * @throws IllegalArgumentException
+ * if the array of dimensions is of size zero, or exceeds the
+ * limit of the number of dimension for an array (currently 255)
+ *
+ * @since Android 1.0
+ */
+ public static Object newInstance(Class<?> componentType, int[] dimensions)
+ throws NegativeArraySizeException, IllegalArgumentException {
+ if (dimensions.length <= 0 || dimensions.length > 255)
+ throw new IllegalArgumentException("Bad number of dimensions");
+
+ if (componentType == Void.TYPE)
+ throw new IllegalArgumentException();
+
+ if (componentType == null)
+ throw new NullPointerException();
+
+ return createMultiArray(componentType, dimensions);
+ }
+
+ /*
+ * Create a multi-dimensional array of objects with the specified type.
+ */
+ native private static Object createMultiArray(Class<?> componentType,
+ int[] dimensions) throws NegativeArraySizeException;
+
+ /**
+ * Returns a new array of the specified component type and length. This
+ * reproduces the effect of {@code new componentType[size]}.
+ *
+ * @param componentType
+ * the component type of the new array
+ * @param size
+ * the length of the new array
+ *
+ * @return the new array
+ *
+ * @throws NullPointerException
+ * if the component type is null
+ * @throws NegativeArraySizeException
+ * if {@code size < 0}
+ *
+ * @since Android 1.0
+ */
+ public static Object newInstance(Class<?> componentType, int size)
+ throws NegativeArraySizeException {
+ if (!componentType.isPrimitive())
+ return createObjectArray(componentType, size);
+
+ if (componentType == Boolean.TYPE)
+ return new boolean[size];
+
+ if (componentType == Byte.TYPE)
+ return new byte[size];
+
+ if (componentType == Character.TYPE)
+ return new char[size];
+
+ if (componentType == Short.TYPE)
+ return new short[size];
+
+ if (componentType == Integer.TYPE)
+ return new int[size];
+
+ if (componentType == Long.TYPE)
+ return new long[size];
+
+ if (componentType == Float.TYPE)
+ return new float[size];
+
+ if (componentType == Double.TYPE)
+ return new double[size];
+
+ if (componentType == Void.TYPE)
+ throw new IllegalArgumentException();
+
+ throw new RuntimeException(); // should be impossible
+ }
+
+ /*
+ * Create a one-dimensional array of objects with the specified type.
+ */
+ native private static Object createObjectArray(Class<?> componentType,
+ int length) throws NegativeArraySizeException;
+
+ /**
+ * Sets the element of the array at the specified index to the value. This
+ * reproduces the effect of {@code array[index] = value}. If the array
+ * component is a primitive type, the value is automatically unwrapped.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void set(Object array, int index, Object value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (!array.getClass().isArray()) {
+ throw new IllegalArgumentException("Not an array type");
+ }
+
+ if (array instanceof Object[]) {
+ if (value != null &&
+ !array.getClass().getComponentType().isInstance(value)) {
+ // incompatible object type for this array
+ throw new IllegalArgumentException("Wrong array type");
+ }
+
+ ((Object[]) array)[index] = value;
+ } else {
+ if (value == null) {
+ throw new IllegalArgumentException("Primitive array can't take null values.");
+ }
+
+ if (value instanceof Boolean)
+ setBoolean(array, index, ((Boolean) value).booleanValue());
+ else if (value instanceof Byte)
+ setByte(array, index, ((Byte) value).byteValue());
+ else if (value instanceof Character)
+ setChar(array, index, ((Character) value).charValue());
+ else if (value instanceof Short)
+ setShort(array, index, ((Short) value).shortValue());
+ else if (value instanceof Integer)
+ setInt(array, index, ((Integer) value).intValue());
+ else if (value instanceof Long)
+ setLong(array, index, ((Long) value).longValue());
+ else if (value instanceof Float)
+ setFloat(array, index, ((Float) value).floatValue());
+ else if (value instanceof Double)
+ setDouble(array, index, ((Double) value).doubleValue());
+ }
+ }
+
+ /**
+ * Sets the element of the array at the specified index to the {@code
+ * boolean} value. This reproduces the effect of {@code array[index] =
+ * value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setBoolean(Object array, int index, boolean value) {
+ if (array instanceof boolean[]) {
+ ((boolean[]) array)[index] = value;
+ } else {
+ setByte(array, index, value ? (byte)1 : (byte)0);
+ }
+ }
+
+ /**
+ * Sets the element of the array at the specified index to the {@code byte}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setByte(Object array, int index, byte value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof byte[]) {
+ ((byte[]) array)[index] = value;
+ } else {
+ setShort(array, index, value);
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code char}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setChar(Object array, int index, char value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof char[]) {
+ ((char[]) array)[index] = value;
+ } else if (array == null) {
+ throw new NullPointerException();
+ } else if (!array.getClass().isArray()) {
+ throw new IllegalArgumentException("Not an array");
+ } else {
+ throw new IllegalArgumentException("Wrong array type");
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code double}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setDouble(Object array, int index, double value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof double[]) {
+ ((double[]) array)[index] = value;
+ } else if (array == null) {
+ throw new NullPointerException();
+ } else if (!array.getClass().isArray()) {
+ throw new IllegalArgumentException("Not an array");
+ } else {
+ throw new IllegalArgumentException("Wrong array type");
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code float}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setFloat(Object array, int index, float value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof float[]) {
+ ((float[]) array)[index] = value;
+ } else {
+ setDouble(array, index, value);
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code int}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setInt(Object array, int index, int value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof int[]) {
+ ((int[]) array)[index] = value;
+ } else {
+ setLong(array, index, value);
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code long}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setLong(Object array, int index, long value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof long[]) {
+ ((long[]) array)[index] = value;
+ } else {
+ setFloat(array, index, value);
+ }
+ }
+
+ /**
+ * Set the element of the array at the specified index to the {@code short}
+ * value. This reproduces the effect of {@code array[index] = value}.
+ *
+ * @param array
+ * the array
+ * @param index
+ * the index
+ * @param value
+ * the new value
+ *
+ * @throws NullPointerException
+ * if the {@code array} is {@code null}
+ * @throws IllegalArgumentException
+ * if the {@code array} is not an array or the value cannot be
+ * converted to the array type by a widening conversion
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code index < 0 || index >= array.length}
+ *
+ * @since Android 1.0
+ */
+ public static void setShort(Object array, int index, short value)
+ throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+ if (array instanceof short[]) {
+ ((short[]) array)[index] = value;
+ } else {
+ setInt(array, index, value);
+ }
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/Constructor.java b/luni-kernel/src/main/java/java/lang/reflect/Constructor.java
new file mode 100644
index 0000000..c6927eb
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/Constructor.java
@@ -0,0 +1,491 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+import dalvik.system.VMStack;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.ListOfTypes;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * This class represents a constructor. Information about the constructor can be
+ * accessed, and the constructor can be invoked dynamically.
+ *
+ * @param <T> the class that declares this constructor
+ *
+ * @since Android 1.0
+ */
+public final class Constructor<T> extends AccessibleObject implements GenericDeclaration,
+ Member {
+
+ Class<T> declaringClass;
+
+ Class<?>[] parameterTypes;
+
+ Class<?>[] exceptionTypes;
+
+ ListOfTypes genericExceptionTypes;
+ ListOfTypes genericParameterTypes;
+ TypeVariable<Constructor<T>>[] formalTypeParameters;
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private synchronized void initGenericTypes() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForConstructor(this, signatureAttribute);
+ formalTypeParameters = parser.formalTypeParameters;
+ genericParameterTypes = parser.parameterTypes;
+ genericExceptionTypes = parser.exceptionTypes;
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ int slot;
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Constructor(){
+ //do nothing
+ }
+
+ /**
+ * Creates an instance of the class. Only called from native code, thus
+ * private.
+ *
+ * @param declaringClass
+ * the class this constructor object belongs to
+ * @param ptypes
+ * the parameter types of the constructor
+ * @param extypes
+ * the exception types of the constructor
+ * @param slot
+ * the slot of the constructor inside the VM class structure
+ */
+ private Constructor (Class<T> declaringClass, Class<?>[] ptypes, Class<?>[] extypes, int slot){
+ this.declaringClass = declaringClass;
+ this.parameterTypes = ptypes;
+ this.exceptionTypes = extypes; // may be null
+ this.slot = slot;
+ }
+
+ @Override /*package*/ String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Get the Signature annotation for this constructor. Returns null if not
+ * found.
+ */
+ native private Object[] getSignatureAnnotation(Class declaringClass,
+ int slot);
+
+ public TypeVariable<Constructor<T>>[] getTypeParameters() {
+ initGenericTypes();
+ return formalTypeParameters.clone();
+ }
+
+ /**
+ * Returns the string representation of the constructor's declaration,
+ * including the type parameters.
+ *
+ * @return the string representation of the constructor's declaration
+ *
+ * @since Android 1.0
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+ initGenericTypes();
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier & ~Modifier.VARARGS)).append(' ');
+ }
+ // append type parameters
+ if (formalTypeParameters != null && formalTypeParameters.length > 0) {
+ sb.append('<');
+ for (int i = 0; i < formalTypeParameters.length; i++) {
+ appendGenericType(sb, formalTypeParameters[i]);
+ if (i < formalTypeParameters.length - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("> ");
+ }
+ // append constructor name
+ appendArrayType(sb, getDeclaringClass());
+ // append parameters
+ sb.append('(');
+ appendArrayGenericType(sb,
+ Types.getClonedTypeArray(genericParameterTypes));
+ sb.append(')');
+ // append exeptions if any
+ Type[] genericEceptionTypeArray =
+ Types.getClonedTypeArray(genericExceptionTypes);
+ if (genericEceptionTypeArray.length > 0) {
+ sb.append(" throws ");
+ appendArrayGenericType(sb, genericEceptionTypeArray);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the generic parameter types as an array of {@code Type}
+ * instances, in declaration order. If this constructor has no generic
+ * parameters, an empty array is returned.
+ *
+ * @return the parameter types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic constructor signature is invalid
+ * @throws TypeNotPresentException
+ * if any parameter type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any parameter type points to a type that cannot be
+ * instantiated for some reason
+ *
+ * @since Android 1.0
+ */
+ public Type[] getGenericParameterTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericParameterTypes);
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Type} instances. If
+ * this constructor has no declared exceptions, an empty array will be
+ * returned.
+ *
+ * @return an array of generic exception types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic constructor signature is invalid
+ * @throws TypeNotPresentException
+ * if any exception type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any exception type points to a type that cannot be
+ * instantiated for some reason
+ * @since Android 1.0
+ */
+ public Type[] getGenericExceptionTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericExceptionTypes);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(declaringClass, slot);
+ }
+ native private Annotation[] getDeclaredAnnotations(Class declaringClass,
+ int slot);
+
+ /**
+ * Returns an array of arrays that represent the annotations of the formal
+ * parameters of this constructor. If there are no parameters on this
+ * constructor, then an empty array is returned. If there are no annotations
+ * set, then an array of empty arrays is returned.
+ *
+ * @return an array of arrays of {@code Annotation} instances
+ *
+ * @since Android 1.0
+ */
+ public Annotation[][] getParameterAnnotations() {
+ Annotation[][] parameterAnnotations
+ = getParameterAnnotations(declaringClass, slot);
+ if (parameterAnnotations.length == 0) {
+ return Method.noAnnotations(parameterTypes.length);
+ }
+ return parameterAnnotations;
+ }
+ native private Annotation[][] getParameterAnnotations(Class declaringClass,
+ int slot);
+
+ /**
+ * Indicates whether or not this constructor takes a variable number of
+ * arguments.
+ *
+ * @return {@code true} if a vararg is declare, otherwise
+ * {@code false}
+ *
+ * @since Android 1.0
+ */
+ public boolean isVarArgs() {
+ int mods = getConstructorModifiers(declaringClass, slot);
+ return (mods & Modifier.VARARGS) != 0;
+ }
+
+ /**
+ * Indicates whether or not this constructor is synthetic (artificially
+ * introduced by the compiler).
+ *
+ * @return {@code true} if this constructor is synthetic, {@code false}
+ * otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isSynthetic() {
+ int mods = getConstructorModifiers(declaringClass, slot);
+ return (mods & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * constructor. To be equal, the specified object must be an instance
+ * of {@code Constructor} with the same declaring class and parameter types
+ * as this constructor.
+ *
+ * @param object
+ * the object to compare
+ *
+ * @return {@code true} if the specified object is equal to this
+ * constructor, {@code false} otherwise
+ *
+ * @see #hashCode
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof Constructor && toString().equals(object.toString());
+ }
+
+ /**
+ * Returns the class that declares this constructor.
+ *
+ * @return the declaring class
+ *
+ * @since Android 1.0
+ */
+ public Class<T> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Class} instances. If
+ * this constructor has no declared exceptions, an empty array will be
+ * returned.
+ *
+ * @return the declared exception classes
+ *
+ * @since Android 1.0
+ */
+ public Class<?>[] getExceptionTypes() {
+ if (exceptionTypes == null)
+ return new Class[0];
+ return exceptionTypes;
+ }
+
+ /**
+ * Returns the modifiers for this constructor. The {@link Modifier} class
+ * should be used to decode the result.
+ *
+ * @return the modifiers for this constructor
+ *
+ * @see Modifier
+ *
+ * @since Android 1.0
+ */
+ public int getModifiers() {
+ return getConstructorModifiers(declaringClass, slot);
+ }
+
+ private native int getConstructorModifiers(Class<T> declaringClass, int slot);
+
+ /**
+ * Returns the name of this constructor.
+ *
+ * @return the name of this constructor
+ *
+ * @since Android 1.0
+ */
+ public String getName() {
+ return declaringClass.getName();
+ }
+
+ /**
+ * Returns an array of the {@code Class} objects associated with the
+ * parameter types of this constructor. If the constructor was declared with
+ * no parameters, an empty array will be returned.
+ *
+ * @return the parameter types
+ *
+ * @since Android 1.0
+ */
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return the constructor's signature
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ for(int i = 0; i < parameterTypes.length; i++) {
+ result.append(getSignature(parameterTypes[i]));
+ }
+ result.append(")V");
+
+ return result.toString();
+ }
+
+ /**
+ * Returns an integer hash code for this constructor. Constructors which are
+ * equal return the same value for this method. The hash code for a
+ * Constructor is the hash code of the name of the declaring class.
+ *
+ * @return the hash code
+ *
+ * @see #equals
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public int hashCode() {
+ return declaringClass.getName().hashCode();
+ }
+
+ /**
+ * Returns a new instance of the declaring class, initialized by dynamically
+ * invoking the constructor represented by this {@code Constructor} object.
+ * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... ,
+ * argN)} This method performs the following:
+ * <ul>
+ * <li>A new instance of the declaring class is created. If the declaring
+ * class cannot be instantiated (i.e. abstract class, an interface, an array
+ * type, or a primitive type) then an InstantiationException is thrown.</li>
+ * <li>If this Constructor object is enforcing access control (see
+ * {@link AccessibleObject}) and this constructor is not accessible from the
+ * current context, an IllegalAccessException is thrown.</li>
+ * <li>If the number of arguments passed and the number of parameters do not
+ * match, an IllegalArgumentException is thrown.</li>
+ * <li>For each argument passed:
+ * <ul>
+ * <li>If the corresponding parameter type is a primitive type, the argument
+ * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
+ * thrown.</li>
+ * <li>If the resulting argument cannot be converted to the parameter type
+ * via a widening conversion, an IllegalArgumentException is thrown.</li>
+ * </ul>
+ * <li>The constructor represented by this {@code Constructor} object is
+ * then invoked. If an exception is thrown during the invocation, it is
+ * caught and wrapped in an InvocationTargetException. This exception is
+ * then thrown. If the invocation completes normally, the newly initialized
+ * object is returned.
+ * </ul>
+ *
+ * @param args
+ * the arguments to the constructor
+ *
+ * @return the new, initialized, object
+ *
+ * @exception InstantiationException
+ * if the class cannot be instantiated
+ * @exception IllegalAccessException
+ * if this constructor is not accessible
+ * @exception IllegalArgumentException
+ * if an incorrect number of arguments are passed, or an
+ * argument could not be converted by a widening conversion
+ * @exception InvocationTargetException
+ * if an exception was thrown by the invoked constructor
+ *
+ * @see AccessibleObject
+ *
+ * @since Android 1.0
+ */
+ public T newInstance(Object... args) throws InstantiationException, IllegalAccessException,
+ IllegalArgumentException, InvocationTargetException {
+ return constructNative (args, declaringClass, parameterTypes, slot, flag);
+ }
+
+ private native T constructNative(Object[] args, Class<T> declaringClass,
+ Class<?>[] parameterTypes, int slot,
+ boolean noAccessCheck) throws InstantiationException, IllegalAccessException,
+ InvocationTargetException;
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * constructor. The format of the string is:
+ *
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>declaring class name
+ * <li>'('
+ * <li>parameter types, separated by ',' (if any)
+ * <li>')'
+ * <li>'throws' plus exception types, separated by ',' (if any)
+ * </ol>
+ *
+ * For example:
+ * {@code public String(byte[],String) throws UnsupportedEncodingException}
+ *
+ * @return a printable representation for this constructor
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+ if (result.length() != 0)
+ result.append(' ');
+ result.append(declaringClass.getName());
+ result.append("(");
+ result.append(toString(parameterTypes));
+ result.append(")");
+ if (exceptionTypes != null && exceptionTypes.length != 0) {
+ result.append(" throws ");
+ result.append(toString(exceptionTypes));
+ }
+
+ return result.toString();
+ }
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/Field.java b/luni-kernel/src/main/java/java/lang/reflect/Field.java
new file mode 100644
index 0000000..0ea16df
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/Field.java
@@ -0,0 +1,939 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+import dalvik.system.VMStack;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.Types;
+import org.apache.harmony.kernel.vm.StringUtils;
+
+/**
+ * This class represents a field. Information about the field can be accessed,
+ * and the field's value can be accessed dynamically.
+ *
+ * @since Android 1.0
+ */
+public final class Field extends AccessibleObject implements Member {
+
+ private Class<?> declaringClass;
+
+ private Class<?> type;
+
+ private Type genericType;
+
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private String name;
+
+ private int slot;
+
+ private static final int TYPE_BOOLEAN = 1;
+
+ private static final int TYPE_BYTE = 2;
+
+ private static final int TYPE_CHAR = 3;
+
+ private static final int TYPE_SHORT = 4;
+
+ private static final int TYPE_INTEGER = 5;
+
+ private static final int TYPE_FLOAT = 6;
+
+ private static final int TYPE_LONG = 7;
+
+ private static final int TYPE_DOUBLE = 8;
+
+ /**
+ * Construct a clone of the given instance.
+ *
+ * @param orig non-null; the original instance to clone
+ */
+ /*package*/ Field(Field orig) {
+ this(orig.declaringClass, orig.type, orig.name, orig.slot);
+
+ // Copy the accessible flag.
+ if (orig.flag) {
+ this.flag = true;
+ }
+ }
+
+ private Field(Class<?> declaringClass, Class<?> type, String name, int slot) {
+ this.declaringClass = declaringClass;
+ this.type = type;
+ this.name = name;
+ this.slot = slot;
+ }
+
+ private synchronized void initGenericType() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForField(this.declaringClass, signatureAttribute);
+ genericType = parser.fieldType;
+ if (genericType == null) {
+ genericType = getType();
+ }
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ /* package */String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Get the Signature annotation for this field. Returns null if not found.
+ */
+ native private Object[] getSignatureAnnotation(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not this field is synthetic.
+ *
+ * @return {@code true} if this field is synthetic, {@code false} otherwise
+ * @since Android 1.0
+ */
+ public boolean isSynthetic() {
+ int flags = getFieldModifiers(declaringClass, slot);
+ return (flags & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns the string representation of this field, including the field's
+ * generic type.
+ *
+ * @return the string representation of this field
+ * @since Android 1.0
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier)).append(' ');
+ }
+ // append generic type
+ appendGenericType(sb, getGenericType());
+ sb.append(' ');
+ // append full field name
+ sb.append(getDeclaringClass().getName()).append('.').append(getName());
+ return sb.toString();
+ }
+
+ /**
+ * Indicates whether or not this field is an enumeration constant.
+ *
+ * @return {@code true} if this field is an enumeration constant, {@code
+ * false} otherwise
+ * @since Android 1.0
+ */
+ public boolean isEnumConstant() {
+ int flags = getFieldModifiers(declaringClass, slot);
+ return (flags & Modifier.ENUM) != 0;
+ }
+
+ /**
+ * Returns the generic type of this field.
+ *
+ * @return the generic type
+ * @throws GenericSignatureFormatError
+ * if the generic field signature is invalid
+ * @throws TypeNotPresentException
+ * if the generic type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if the generic type points to a type that cannot be
+ * instantiated for some reason
+ * @since Android 1.0
+ */
+ public Type getGenericType() {
+ initGenericType();
+ return Types.getType(genericType);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(declaringClass, slot);
+ }
+
+ native private Annotation[] getDeclaredAnnotations(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * field. To be equal, the specified object must be an instance of
+ * {@code Field} with the same declaring class, type and name as this field.
+ *
+ * @param object
+ * the object to compare
+ * @return {@code true} if the specified object is equal to this method,
+ * {@code false} otherwise
+ * @see #hashCode
+ * @since Android 1.0
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof Field && toString().equals(object.toString());
+ }
+
+ /**
+ * Returns the value of the field in the specified object. This reproduces
+ * the effect of {@code object.fieldName}
+ * <p>
+ * If the type of this field is a primitive type, the field value is
+ * automatically wrapped.
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is null, a NullPointerException is thrown. If
+ * the object is not an instance of the declaring class of the method, an
+ * IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ *
+ * @param object
+ * the object to access
+ * @return the field value, possibly wrapped
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public Object get(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getField(object, declaringClass, type, slot, flag);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code
+ * boolean}. This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public boolean getBoolean(Object object) throws IllegalAccessException,
+ IllegalArgumentException {
+ return getZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code byte}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getBField(object, declaringClass, type, slot, flag, TYPE_BYTE);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code char}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getCField(object, declaringClass, type, slot, flag, TYPE_CHAR);
+ }
+
+ /**
+ * Returns the class that declares this field.
+ *
+ * @return the declaring class
+ * @since Android 1.0
+ */
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code
+ * double}. This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code float}
+ * . This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getFField(object, declaringClass, type, slot, flag, TYPE_FLOAT);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as an {@code int}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getIField(object, declaringClass, type, slot, flag, TYPE_INTEGER);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code long}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getJField(object, declaringClass, type, slot, flag, TYPE_LONG);
+ }
+
+ /**
+ * Returns the modifiers for this field. The {@link Modifier} class should
+ * be used to decode the result.
+ *
+ * @return the modifiers for this field
+ * @see Modifier
+ * @since Android 1.0
+ */
+ public int getModifiers() {
+ return getFieldModifiers(declaringClass, slot);
+ }
+
+ private native int getFieldModifiers(Class<?> declaringClass, int slot);
+
+ /**
+ * Returns the name of this field.
+ *
+ * @return the name of this field
+ * @since Android 1.0
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code short}
+ * . This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getSField(object, declaringClass, type, slot, flag, TYPE_SHORT);
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return the constructor's signature.
+ * @since Android 1.0
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ return getSignature(type);
+ }
+
+ /**
+ * Return the {@link Class} associated with the type of this field.
+ *
+ * @return the type of this field
+ * @since Android 1.0
+ */
+ public Class<?> getType() {
+ return type;
+ }
+
+ /**
+ * Returns an integer hash code for this field. Objects which are equal
+ * return the same value for this method.
+ * <p>
+ * The hash code for a Field is the exclusive-or combination of the hash
+ * code of the field's name and the hash code of the name of its declaring
+ * class.
+ *
+ * @return the hash code for this field
+ * @see #equals
+ * @since Android 1.0
+ */
+ @Override
+ public int hashCode() {
+ // BEGIN android-changed
+ return name.hashCode() ^ getDeclaringClass().getName().hashCode();
+ // END android-changed
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the value. This
+ * reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the field type is a primitive type, the value is automatically
+ * unwrapped. If the unwrap fails, an IllegalArgumentException is thrown. If
+ * the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void set(Object object, Object value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setField(object, declaringClass, type, slot, flag, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code
+ * boolean} value. This reproduces the effect of {@code object.fieldName =
+ * value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setBoolean(Object object, boolean value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code byte}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setByte(Object object, byte value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setBField(object, declaringClass, type, slot, flag, TYPE_BYTE, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code char}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setChar(Object object, char value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setCField(object, declaringClass, type, slot, flag, TYPE_CHAR, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code double}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setDouble(Object object, double value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code float}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setFloat(Object object, float value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setFField(object, declaringClass, type, slot, flag, TYPE_FLOAT, value);
+ }
+
+ /**
+ * Set the value of the field in the specified object to the {@code int}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setInt(Object object, int value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setIField(object, declaringClass, type, slot, flag, TYPE_INTEGER, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code long}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setLong(Object object, long value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setJField(object, declaringClass, type, slot, flag, TYPE_LONG, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code short}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ * @since Android 1.0
+ */
+ public void setShort(Object object, short value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setSField(object, declaringClass, type, slot, flag, TYPE_SHORT, value);
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * field.
+ * <p>
+ * The format of the string is:
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>type
+ * <li>declaring class name
+ * <li>'.'
+ * <li>field name
+ * </ol>
+ * <p>
+ * For example: {@code public static java.io.InputStream
+ * java.lang.System.in}
+ *
+ * @return a printable representation for this field
+ * @since Android 1.0
+ */
+ @Override
+ public String toString() {
+ StringBuffer result = new StringBuffer(Modifier.toString(getModifiers()));
+
+ if (result.length() != 0)
+ result.append(' ');
+ result.append(type.getName());
+ result.append(' ');
+ result.append(declaringClass.getName());
+ result.append('.');
+ result.append(name);
+
+ return result.toString();
+ }
+
+ private native Object getField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck) throws IllegalAccessException;
+
+ private native double getDField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native int getIField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native long getJField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native boolean getZField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native float getFField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native char getCField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native short getSField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native byte getBField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no) throws IllegalAccessException;
+
+ private native void setField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, Object value) throws IllegalAccessException;
+
+ private native void setDField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, double v) throws IllegalAccessException;
+
+ private native void setIField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, int i) throws IllegalAccessException;
+
+ private native void setJField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, long j) throws IllegalAccessException;
+
+ private native void setZField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, boolean z) throws IllegalAccessException;
+
+ private native void setFField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, float f) throws IllegalAccessException;
+
+ private native void setCField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, char c) throws IllegalAccessException;
+
+ private native void setSField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, short s) throws IllegalAccessException;
+
+ private native void setBField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, int type_no, byte b) throws IllegalAccessException;
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/Method.java b/luni-kernel/src/main/java/java/lang/reflect/Method.java
new file mode 100644
index 0000000..473726d
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/Method.java
@@ -0,0 +1,595 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+import dalvik.system.VMStack;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.ListOfTypes;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * This class represents a method. Information about the method can be accessed,
+ * and the method can be invoked dynamically.
+ *
+ * @since Android 1.0
+ */
+public final class Method extends AccessibleObject implements GenericDeclaration, Member {
+
+ private int slot;
+
+ private Class<?> declaringClass;
+
+ private String name;
+
+ private Class<?>[] parameterTypes;
+
+ private Class<?>[] exceptionTypes;
+
+ private Class<?> returnType;
+
+ private ListOfTypes genericExceptionTypes;
+ private ListOfTypes genericParameterTypes;
+ private Type genericReturnType;
+ private TypeVariable<Method>[] formalTypeParameters;
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private synchronized void initGenericTypes() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ VMStack.getCallingClassLoader2());
+ parser.parseForMethod(this, signatureAttribute);
+ formalTypeParameters = parser.formalTypeParameters;
+ genericParameterTypes = parser.parameterTypes;
+ genericExceptionTypes = parser.exceptionTypes;
+ genericReturnType = parser.returnType;
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ /**
+ * Construct a clone of the given instance.
+ *
+ * @param orig non-null; the original instance to clone
+ */
+ /*package*/ Method(Method orig) {
+ this(orig.declaringClass, orig.parameterTypes, orig.exceptionTypes,
+ orig.returnType, orig.name, orig.slot);
+
+ // Copy the accessible flag.
+ if (orig.flag) {
+ this.flag = true;
+ }
+ }
+
+ private Method(Class<?> declaring, Class<?>[] paramTypes, Class<?>[] exceptTypes, Class<?> returnType, String name, int slot)
+ {
+ this.declaringClass = declaring;
+ this.name = name;
+ this.slot = slot;
+ this.parameterTypes = paramTypes;
+ this.exceptionTypes = exceptTypes; // may be null
+ this.returnType = returnType;
+ }
+
+ public TypeVariable<Method>[] getTypeParameters() {
+ initGenericTypes();
+ return formalTypeParameters.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override /*package*/ String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Returns the Signature annotation for this method. Returns {@code null} if
+ * not found.
+ */
+ native private Object[] getSignatureAnnotation(Class declaringClass,
+ int slot);
+
+ /**
+ * Returns the string representation of the method's declaration, including
+ * the type parameters.
+ *
+ * @return the string representation of this method
+ *
+ * @since Android 1.0
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+
+ initGenericTypes();
+
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier & ~(Modifier.BRIDGE +
+ Modifier.VARARGS))).append(' ');
+ }
+ // append type parameters
+ if (formalTypeParameters != null && formalTypeParameters.length > 0) {
+ sb.append('<');
+ for (int i = 0; i < formalTypeParameters.length; i++) {
+ appendGenericType(sb, formalTypeParameters[i]);
+ if (i < formalTypeParameters.length - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("> ");
+ }
+ // append return type
+ appendGenericType(sb, Types.getType(genericReturnType));
+ sb.append(' ');
+ // append method name
+ appendArrayType(sb, getDeclaringClass());
+ sb.append("."+getName());
+ // append parameters
+ sb.append('(');
+ appendArrayGenericType(sb,
+ Types.getClonedTypeArray(genericParameterTypes));
+ sb.append(')');
+ // append exceptions if any
+ Type[] genericExceptionTypeArray = Types.getClonedTypeArray(
+ genericExceptionTypes);
+ if (genericExceptionTypeArray.length > 0) {
+ sb.append(" throws ");
+ appendArrayGenericType(sb, genericExceptionTypeArray);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the parameter types as an array of {@code Type} instances, in
+ * declaration order. If this method has no parameters, an empty array is
+ * returned.
+ *
+ * @return the parameter types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if any parameter type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any parameter type points to a type that cannot be
+ * instantiated for some reason
+ *
+ * @since Android 1.0
+ */
+ public Type[] getGenericParameterTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericParameterTypes);
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Type} instances. If
+ * this method has no declared exceptions, an empty array will be returned.
+ *
+ * @return an array of generic exception types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if any exception type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any exception type points to a type that cannot be
+ * instantiated for some reason
+ *
+ * @since Android 1.0
+ */
+ public Type[] getGenericExceptionTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericExceptionTypes);
+ }
+
+ /**
+ * Returns the return type of this method as a {@code Type} instance.
+ *
+ * @return the return type of this method
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if the return type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if the return type points to a type that cannot be
+ * instantiated for some reason
+ *
+ * @since Android 1.0
+ */
+ public Type getGenericReturnType() {
+ initGenericTypes();
+ return Types.getType(genericReturnType);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(declaringClass, slot);
+ }
+ native private Annotation[] getDeclaredAnnotations(Class declaringClass,
+ int slot);
+
+ private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
+
+ /**
+ * Creates an array of empty Annotation arrays.
+ */
+ /*package*/ static Annotation[][] noAnnotations(int size) {
+ Annotation[][] annotations = new Annotation[size][];
+ for (int i = 0; i < size; i++) {
+ annotations[i] = NO_ANNOTATIONS;
+ }
+ return annotations;
+ }
+
+ /**
+ * Returns an array of arrays that represent the annotations of the formal
+ * parameters of this method. If there are no parameters on this method,
+ * then an empty array is returned. If there are no annotations set, then
+ * and array of empty arrays is returned.
+ *
+ * @return an array of arrays of {@code Annotation} instances
+ *
+ * @since Android 1.0
+ */
+ public Annotation[][] getParameterAnnotations() {
+ Annotation[][] parameterAnnotations
+ = getParameterAnnotations(declaringClass, slot);
+ if (parameterAnnotations.length == 0) {
+ return noAnnotations(parameterTypes.length);
+ }
+ return parameterAnnotations;
+ }
+
+ native private Annotation[][] getParameterAnnotations(Class declaringClass,
+ int slot);
+
+ /**
+ * Indicates whether or not this method takes a variable number argument.
+ *
+ * @return {@code true} if a vararg is declared, {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isVarArgs() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.VARARGS) != 0;
+ }
+
+ /**
+ * Indicates whether or not this method is a bridge.
+ *
+ * @return {@code true} if this method is a bridge, {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isBridge() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.BRIDGE) != 0;
+ }
+
+ /**
+ * Indicates whether or not this method is synthetic.
+ *
+ * @return {@code true} if this method is synthetic, {@code false} otherwise
+ *
+ * @since Android 1.0
+ */
+ public boolean isSynthetic() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns the default value for the annotation member represented by this
+ * method.
+ *
+ * @return the default value, or {@code null} if none
+ *
+ * @throws TypeNotPresentException
+ * if this annotation member is of type {@code Class} and no
+ * definition can be found
+ *
+ * @since Android 1.0
+ */
+ public Object getDefaultValue() {
+ return getDefaultValue(declaringClass, slot);
+ }
+ native private Object getDefaultValue(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * method. To be equal, the specified object must be an instance
+ * of {@code Method} with the same declaring class and parameter types
+ * as this method.
+ *
+ * @param object
+ * the object to compare
+ *
+ * @return {@code true} if the specified object is equal to this
+ * method, {@code false} otherwise
+ *
+ * @see #hashCode
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof Method && toString().equals(object.toString());
+ }
+
+ /**
+ * Returns the class that declares this method.
+ *
+ * @return the declaring class
+ *
+ * @since Android 1.0
+ */
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Class} instances. If
+ * this method has no declared exceptions, an empty array is returned.
+ *
+ * @return the declared exception classes
+ *
+ * @since Android 1.0
+ */
+ public Class<?>[] getExceptionTypes() {
+ if (exceptionTypes == null) {
+ return new Class[0];
+ }
+
+ return exceptionTypes;
+ }
+
+ /**
+ * Returns the modifiers for this method. The {@link Modifier} class should
+ * be used to decode the result.
+ *
+ * @return the modifiers for this method
+ *
+ * @see Modifier
+ *
+ * @since Android 1.0
+ */
+ public int getModifiers() {
+ return getMethodModifiers(declaringClass, slot);
+ }
+
+ private native int getMethodModifiers(Class<?> decl_class, int slot);
+
+ /**
+ * Returns the name of the method represented by this {@code Method}
+ * instance.
+ *
+ * @return the name of this method
+ *
+ * @since Android 1.0
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns an array of {@code Class} objects associated with the parameter
+ * types of this method. If the method was declared with no parameters, an
+ * empty array will be returned.
+ *
+ * @return the parameter types
+ *
+ * @since Android 1.0
+ */
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ /**
+ * Returns the {@code Class} associated with the return type of this
+ * method.
+ *
+ * @return the return type
+ *
+ * @since Android 1.0
+ */
+ public Class<?> getReturnType() {
+ return returnType;
+ }
+
+ /**
+ * Returns an integer hash code for this method. Objects which are equal
+ * return the same value for this method. The hash code for this Method is
+ * the hash code of the name of this method.
+ *
+ * @return hash code for this method
+ *
+ * @see #equals
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Returns the result of dynamically invoking this method. This reproduces
+ * the effect of {@code receiver.methodName(arg1, arg2, ... , argN)} This
+ * method performs the following:
+ * <ul>
+ * <li>If this method is static, the receiver argument is ignored.</li>
+ * <li>Otherwise, if the receiver is null, a NullPointerException is thrown.
+ * </li>
+ * <li>If the receiver is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.</li>
+ * <li>If this Method object is enforcing access control (see
+ * AccessibleObject) and this method is not accessible from the current
+ * context, an IllegalAccessException is thrown.</li>
+ * <li>If the number of arguments passed and the number of parameters do not
+ * match, an IllegalArgumentException is thrown.</li>
+ * <li>For each argument passed:
+ * <ul>
+ * <li>If the corresponding parameter type is a primitive type, the argument
+ * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
+ * thrown.</li>
+ * <li>If the resulting argument cannot be converted to the parameter type
+ * via a widening conversion, an IllegalArgumentException is thrown.</li>
+ * </ul>
+ * <li>If this method is static, it is invoked directly. If it is
+ * non-static, this method and the receiver are then used to perform a
+ * standard dynamic method lookup. The resulting method is then invoked.</li>
+ * <li>If an exception is thrown during the invocation it is caught and
+ * wrapped in an InvocationTargetException. This exception is then thrown.</li>
+ * <li>If the invocation completes normally, the return value itself is
+ * returned. If the method is declared to return a primitive type, the
+ * return value is first wrapped. If the return type is void, null is
+ * returned.</li>
+ * </ul>
+ *
+ * @param receiver
+ * the object on which to call this method
+ * @param args
+ * the arguments to the method
+ *
+ * @return the new, initialized, object
+ *
+ * @throws NullPointerException
+ * if the receiver is null for a non-static method
+ * @throws IllegalAccessException
+ * if this method is not accessible
+ * @throws IllegalArgumentException
+ * if an incorrect number of arguments are passed, the receiver
+ * is incompatible with the declaring class, or an argument
+ * could not be converted by a widening conversion
+ * @throws InvocationTargetException
+ * if an exception was thrown by the invoked method
+ *
+ * @see AccessibleObject
+ *
+ * @since Android 1.0
+ */
+ public Object invoke(Object receiver, Object... args)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException {
+ if (args == null) {
+ args = new Object[0];
+ }
+
+ return invokeNative (receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
+ }
+
+ private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass, Class<?>[] parameterTYpes, Class<?> returnType, int slot, boolean noAccessCheck)
+ throws IllegalAccessException,
+ IllegalArgumentException,
+ InvocationTargetException;
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * method. The format of the string is:
+ *
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>return type or 'void'
+ * <li>declaring class name
+ * <li>'('
+ * <li>parameter types, separated by ',' (if any)
+ * <li>')'
+ * <li>'throws' plus exception types, separated by ',' (if any)
+ * </ol>
+ *
+ * For example: {@code public native Object
+ * java.lang.Method.invoke(Object,Object) throws
+ * IllegalAccessException,IllegalArgumentException
+ * ,InvocationTargetException}
+ *
+ * @return a printable representation for this method
+ *
+ * @since Android 1.0
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+ if (result.length() != 0)
+ result.append(' ');
+ result.append(returnType.getName());
+ result.append(' ');
+ result.append(declaringClass.getName());
+ result.append('.');
+ result.append(name);
+ result.append("(");
+ result.append(toString(parameterTypes));
+ result.append(")");
+ if (exceptionTypes != null && exceptionTypes.length != 0) {
+ result.append(" throws ");
+ result.append(toString(exceptionTypes));
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return The constructor's signature.
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ for(int i = 0; i < parameterTypes.length; i++) {
+ result.append(getSignature(parameterTypes[i]));
+ }
+ result.append(')');
+ result.append(getSignature(returnType));
+
+ return result.toString();
+ }
+
+}
diff --git a/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java b/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java
new file mode 100644
index 0000000..15cd798
--- /dev/null
+++ b/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 java.lang.reflect;
+
+import org.apache.harmony.kernel.vm.ReflectionAccess;
+
+/**
+ * Implementation of bridge from {@code java.lang} to
+ * {@code java.lang.reflect}.
+ */
+/*package*/ final class ReflectionAccessImpl implements ReflectionAccess {
+ /** non-null; unique instance of this class */
+ /*package*/ static final ReflectionAccessImpl THE_ONE =
+ new ReflectionAccessImpl();
+
+ /**
+ * This class is not publicly instantiable. Use {@link #THE_ONE}.
+ */
+ private ReflectionAccessImpl() {
+ // This space intentionally left blank.
+ }
+
+ public Method clone(Method method) {
+ return new Method(method);
+ }
+
+ public Field clone(Field field) {
+ return new Field(field);
+ }
+
+ public Method accessibleClone(Method method) {
+ Method result = new Method(method);
+ result.setAccessibleNoCheck(true);
+ return result;
+ }
+
+ public void setAccessibleNoCheck(AccessibleObject ao, boolean accessible) {
+ ao.setAccessibleNoCheck(accessible);
+ }
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java
new file mode 100644
index 0000000..8b321e4
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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.kernel.vm;
+
+import dalvik.system.VMStack;
+
+/**
+ * Bridge into <code>java.lang</code> from other trusted parts of the
+ * core library. Trusted packages either get seeded with an instance
+ * of this class directly or may call {@link #getInstance} on this
+ * class, to allow them to call into what would otherwise be
+ * package-scope functionality in <code>java.lang</code>.
+ */
+public abstract class LangAccess {
+ /** unique instance of this class */
+ private static LangAccess theInstance = null;
+
+ /**
+ * Sets the unique instance of this class. This may only be done once.
+ *
+ * @param instance non-null; the instance
+ */
+ public static void setInstance(LangAccess instance) {
+ if (theInstance != null) {
+ throw new UnsupportedOperationException("already initialized");
+ }
+
+ theInstance = instance;
+ }
+
+ /**
+ * Gets the unique instance of this class. This is only allowed in
+ * very limited situations.
+ */
+ public static LangAccess getInstance() {
+ /*
+ * Only code on the bootclasspath is allowed to get at the
+ * instance.
+ */
+ ClassLoader calling = VMStack.getCallingClassLoader2();
+ ClassLoader current = LangAccess.class.getClassLoader();
+
+ if ((calling != null) && (calling != current)) {
+ throw new SecurityException("LangAccess access denied");
+ }
+
+ if (theInstance == null) {
+ throw new UnsupportedOperationException("not yet initialized");
+ }
+
+ return theInstance;
+ }
+
+ /**
+ * Gets a shared array of the enum constants of a given class in
+ * declaration (ordinal) order. It is not safe to hand out this
+ * array to any user code.
+ *
+ * @param clazz non-null; the class in question
+ * @return null-ok; the class's list of enumerated constants in
+ * declaration order or <code>null</code> if the given class is
+ * not an enumeration
+ */
+ public abstract <T> T[] getEnumValuesInOrder(Class<T> clazz);
+
+ /**
+ * Unparks the given thread. This unblocks the thread it if it was
+ * previously parked, or indicates that the thread is "preemptively
+ * unparked" if it wasn't already parked. The latter means that the
+ * next time the thread is told to park, it will merely clear its
+ * latent park bit and carry on without blocking.
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @param thread non-null; the thread to unpark
+ */
+ public abstract void unpark(Thread thread);
+
+ /**
+ * Parks the current thread for a particular number of nanoseconds, or
+ * indefinitely. If not indefinitely, this method unparks the thread
+ * after the given number of nanoseconds if no other thread unparks it
+ * first. If the thread has been "preemptively unparked," this method
+ * cancels that unparking and returns immediately. This method may
+ * also return spuriously (that is, without the thread being told to
+ * unpark and without the indicated amount of time elapsing).
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @param nanos number of nanoseconds to park for or <code>0</code>
+ * to park indefinitely
+ * @throws IllegalArgumentException thrown if <code>nanos &lt; 0</code>
+ */
+ public abstract void parkFor(long nanos);
+
+ /**
+ * Parks the current thread until the specified system time. This
+ * method attempts to unpark the current thread immediately after
+ * <code>System.currentTimeMillis()</code> reaches the specified
+ * value, if no other thread unparks it first. If the thread has
+ * been "preemptively unparked," this method cancels that
+ * unparking and returns immediately. This method may also return
+ * spuriously (that is, without the thread being told to unpark
+ * and without the indicated amount of time elapsing).
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @param time the time after which the thread should be unparked,
+ * in absolute milliseconds-since-the-epoch
+ */
+ public abstract void parkUntil(long time);
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java
new file mode 100644
index 0000000..1487ba1
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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.kernel.vm;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Bridge from <code>java.lang</code> to <code>java.lang.reflect</code>.
+ * The package <code>java.lang</code> gets seeded with an instance of
+ * this interface, to allow it to call into what would otherwise be
+ * package-scope functionality in <code>java.lang.reflect</code>.
+ */
+public interface ReflectionAccess {
+ /**
+ * Gets a clone of the given method.
+ *
+ * @param method non-null; the method to clone
+ * @return non-null; the clone
+ */
+ public Method clone(Method method);
+
+ /**
+ * Gets a clone of the given field.
+ *
+ * @param field non-null; the field to clone
+ * @return non-null; the clone
+ */
+ public Field clone(Field field);
+
+ /**
+ * Gets a clone of the given method, where the clone has
+ * its "accessible" flag set to <code>true</code>
+ *
+ * @param method non-null; the method to clone
+ * @return non-null; the accessible clone
+ */
+ public Method accessibleClone(Method method);
+
+ /**
+ * Sets the accessible flag on a given {@link AccessibleObject}
+ * without doing any checks.
+ *
+ * @param ao non-null; the instance in question
+ * @param flag the new value for the accessible flag
+ */
+ public void setAccessibleNoCheck(AccessibleObject ao, boolean flag);
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java
new file mode 100644
index 0000000..b392388
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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.kernel.vm;
+
+/**
+ * String utility functions.
+ */
+public final class StringUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private StringUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Combine a list of strings in an <code>Object[]</code> into a single
+ * string.
+ *
+ * @param list non-null; the strings to combine
+ * @return non-null; the combined form
+ */
+ public static String combineStrings(Object[] list) {
+ int listLength = list.length;
+
+ switch (listLength) {
+ case 0: {
+ return "";
+ }
+ case 1: {
+ return (String) list[0];
+ }
+ }
+
+ int strLength = 0;
+
+ for (int i = 0; i < listLength; i++) {
+ strLength += ((String) list[i]).length();
+ }
+
+ StringBuilder sb = new StringBuilder(strLength);
+
+ for (int i = 0; i < listLength; i++) {
+ sb.append(list[i]);
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java
new file mode 100644
index 0000000..8aa7c4b
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java
@@ -0,0 +1,289 @@
+/*
+ * 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.kernel.vm;
+
+/**
+ * This class must be implemented by the vm vendor. Represents the running
+ * virtual machine. All VM specific API are implemented on this class.
+ * <p>
+ * Note that all methods in VM are static. There is no singleton instance which
+ * represents the actively running VM.
+ */
+public final class VM {
+
+ /*
+ * kernelVersion has the format: aabbxxyy where: aa - major version of
+ * kernel. Must equal that stored in jcl. bb - minor version of kernel. Must
+ * be >= that in jcl. xx - major version of jcl. Must equal that stored in
+ * kernel. yy - minor version of jcl. Must be >= that in kernel.
+ */
+ private static final int kernelVersion = 0x01000100;
+
+ /**
+ * This method must be provided by the vm vendor, as it is used by
+ * org.apache.harmony.kernel.vm.MsgHelp.setLocale() to get the bootstrap
+ * ClassLoader. MsgHelp uses the bootstrap ClassLoader to find the resource
+ * bundle of messages packaged with the bootstrap classes. Returns the
+ * ClassLoader of the method (including natives) at the specified depth on
+ * the stack of the calling thread. Frames representing the VM
+ * implementation of java.lang.reflect are not included in the list. This is
+ * not a public method as it can return the bootstrap class loader, which
+ * should not be accessed by non-bootstrap classes. Notes:
+ * <ul>
+ * <li>This method operates on the defining classes of methods on stack.
+ * NOT the classes of receivers.</li>
+ * <li>The item at depth zero is the caller of this method</li>
+ * </ul>
+ *
+ * @param depth the stack depth of the requested ClassLoader
+ * @return the ClassLoader at the specified depth
+ * @see java.lang.ClassLoader#getStackClassLoader
+ */
+ static final ClassLoader getStackClassLoader(int depth) {
+ return null;
+ };
+
+ /**
+ * This method must be provided by the vm vendor, as it is used by other
+ * provided class implementations. For example,
+ * java.io.ObjectInputStream.readObject() and
+ * java.io.ObjectInputStream.resolveProxyClass(). It is also useful for
+ * other classes, such as java.rmi.server.RMIClassLoader. Walk the stack and
+ * answer the most recent non-null and non-bootstrap ClassLoader on the
+ * stack of the calling thread. If no such ClassLoader is found, null is
+ * returned. Notes: 1) This method operates on the defining classes of
+ * methods on stack. NOT the classes of receivers.
+ *
+ * @return the first non-bootstrap ClassLoader on the stack
+ */
+ static public final ClassLoader getNonBootstrapClassLoader() {
+ return null;
+ };
+
+ /**
+ * Initialize the classloader.
+ *
+ * @param loader ClassLoader the ClassLoader instance
+ * @param bootLoader boolean true for the bootstrap class loader
+ */
+ public final static void initializeClassLoader(ClassLoader loader, boolean bootLoader) {
+ return;
+ };
+
+ /**
+ * This method must be provided by the vm vendor.
+ *
+ * Searches an internal table of strings for a string equal to the specified
+ * String. If the string is not in the table, it is added. Returns the
+ * string contained in the table which is equal to the specified String. The
+ * same string object is always answered for strings which are equal.
+ *
+ * @param string the String to intern
+ *
+ * @return the interned string equal to the specified String
+ */
+ public static final String intern(String string) {
+ return null;
+ }
+
+ /**
+ * Native used to find and load a class using the VM
+ *
+ * @return java.lang.Class the class or null.
+ * @param className String the name of the class to search for.
+ * @param classLoader the classloader to do the work
+ */
+ static Class<?> findClassOrNull(String className, ClassLoader classLoader) {
+ return null;
+ }
+
+ /**
+ * This method must be included, as it is used by
+ * ResourceBundle.getBundle(), and other places as well. The reference
+ * implementation of this method uses the getStackClassLoader() method.
+ * Returns the ClassLoader of the method that called the caller. i.e. A.x()
+ * calls B.y() calls callerClassLoader(), A's ClassLoader will be returned.
+ * Returns null for the bootstrap ClassLoader.
+ *
+ * @return a ClassLoader or null for the bootstrap ClassLoader
+ * @throws SecurityException when called from a non-bootstrap Class
+ */
+ public static ClassLoader callerClassLoader() {
+ return null;
+ }
+
+ /**
+ * This method must be provided by the vm vendor, as it is used by
+ * org.apache.harmony.luni.util.MsgHelp.setLocale() to get the bootstrap
+ * ClassLoader. MsgHelp uses the bootstrap ClassLoader to find the resource
+ * bundle of messages packaged with the bootstrap classes. The reference
+ * implementation of this method uses the getStackClassLoader() method.
+ *
+ * Returns the ClassLoader of the method that called the caller. i.e. A.x()
+ * calls B.y() calls callerClassLoader(), A's ClassLoader will be returned.
+ * Returns null for the bootstrap ClassLoader.
+ *
+ * @return a ClassLoader
+ *
+ * @throws SecurityException when called from a non-bootstrap Class
+ */
+ public static ClassLoader bootCallerClassLoader() {
+ return null;
+ }
+
+ /**
+ * Native used to dump a string to the system console for debugging.
+ *
+ * @param str String the String to display
+ */
+ public static void dumpString(String str) {
+ return;
+ }
+
+ /**
+ * Get the classpath entry that was used to load the class that is the arg.
+ * <p>
+ * This method is for internal use only.
+ *
+ * @param targetClass Class the class to set the classpath of.
+ * @see java.lang.Class
+ */
+ static int getCPIndexImpl(Class<?> targetClass) {
+ return 0;
+ }
+
+ /**
+ * Does internal initialization required by VM.
+ *
+ */
+ static void initializeVM() {
+ }
+
+ /**
+ * Registers a new virtual-machine shutdown hook. This is equivalent to the
+ * 1.3 API of the same name.
+ *
+ * @param hook the hook (a Thread) to register
+ */
+ public static void addShutdownHook(Thread hook) {
+ return;
+ }
+
+ /**
+ * De-registers a previously-registered virtual-machine shutdown hook. This
+ * is equivalent to the 1.3 API of the same name.
+ *
+ * @param hook the hook (a Thread) to de-register
+ * @return true if the hook could be de-registered
+ */
+ public static boolean removeShutdownHook(Thread hook) {
+ return false;
+ }
+
+ /**
+ * This method must be provided by the vm vendor. Called to signal that the
+ * org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection
+ * class has been loaded and JarURLConnection.closeCachedFiles() should be
+ * called on VM shutdown.
+ */
+ public static void closeJars() {
+ return;
+ }
+
+ /**
+ * This method must be provided by the vm vendor. Called to signal that the
+ * org.apache.harmony.luni.util.DeleteOnExit class has been loaded and
+ * DeleteOnExit.deleteOnExit() should be called on VM shutdown.
+ */
+ public static void deleteOnExit() {
+ return;
+ }
+
+ // Constants used by getClassPathEntryType to indicate the class path entry
+ // type
+ static final int CPE_TYPE_UNKNOWN = 0;
+
+ static final int CPE_TYPE_DIRECTORY = 1;
+
+ static final int CPE_TYPE_JAR = 2;
+
+ static final int CPE_TYPE_TCP = 3;
+
+ static final int CPE_TYPE_UNUSABLE = 5;
+
+ /**
+ * Return the type of the specified entry on the class path for a
+ * ClassLoader. Valid types are: CPE_TYPE_UNKNOWN CPE_TYPE_DIRECTORY
+ * CPE_TYPE_JAR CPE_TYPE_TCP - this is obsolete CPE_TYPE_UNUSABLE
+ *
+ * @param classLoader the ClassLoader
+ * @param cpIndex the index on the class path
+ *
+ * @return a int which specifies the class path entry type
+ */
+ static final int getClassPathEntryType(Object classLoader, int cpIndex) {
+ return 0;
+ }
+
+ /**
+ * Returns command line arguments passed to the VM. Internally these are
+ * broken into optionString and extraInfo. This only returns the
+ * optionString part.
+ * <p>
+ *
+ * @return a String array containing the optionString part of command line
+ * arguments
+ */
+ public static String[] getVMArgs() {
+ return null;
+ }
+
+ /**
+ * Return the number of entries on the bootclasspath.
+ *
+ * @return an int which is the number of entries on the bootclasspath
+ */
+ static int getClassPathCount() {
+ return 0;
+ }
+
+ /**
+ * Return the specified bootclasspath entry.
+ *
+ * @param index the index of the bootclasspath entry
+ *
+ * @return a byte array containing the bootclasspath entry
+ * specified in the vm options
+ */
+ static byte[] getPathFromClassPath(int index) {
+ return null;
+ }
+
+ /**
+ * This method must be provided by the vm vendor.
+ *
+ * Returns an int containing the version number of the kernel. Used to check for kernel
+ * compatibility.
+ *
+ * @return an int containing the kernel version number
+ */
+ public static int getKernelVersion() {
+ return kernelVersion;
+ }
+
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
new file mode 100644
index 0000000..a898f54
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
@@ -0,0 +1,323 @@
+/*
+ * 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.lang.annotation;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.IncompleteAnnotationException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import static org.apache.harmony.lang.annotation.AnnotationMember.ARRAY;
+import static org.apache.harmony.lang.annotation.AnnotationMember.ERROR;
+
+/**
+ * The annotation implementation based on dynamically generated proxy instances.
+ * It conforms to all requirements stated in public APIs, see in particular
+ * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
+ * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
+ * Namely, annotation instances are immutable and serializable; they provide
+ * conforming access to annotation member values and required implementations of
+ * methods declared in Annotation interface.
+ *
+ * @see android.lang.annotation.AnnotationMember
+ * @see java.lang.annotation.Annotation
+ *
+ * @author Alexey V. Varlamov, Serguei S. Zapreyev
+ * @version $Revision$
+ */
+@SuppressWarnings({"serial"})
+public final class AnnotationFactory implements InvocationHandler, Serializable {
+
+ private static final transient
+ Map<Class<? extends Annotation>, AnnotationMember[]>
+ cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>();
+
+ /**
+ * Reflects specified annotation type and returns an array
+ * of member element definitions with default values.
+ */
+ public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType ) {
+ AnnotationMember[] desc = cache.get(annotationType);
+ if (desc == null) {
+ if (!annotationType.isAnnotation()) {
+ throw new IllegalArgumentException("Type is not annotation: "
+ + annotationType.getName());
+ }
+ Method[] m = annotationType.getDeclaredMethods();
+ desc = new AnnotationMember[m.length];
+ int idx = 0;
+ for(Method element : m) {
+ String name = element.getName();
+ Class<?> type = element.getReturnType();
+ try {
+ desc[idx] = new AnnotationMember(name,
+ element.getDefaultValue(), type, element);
+ } catch (Throwable t) {
+ desc[idx] = new AnnotationMember(name, t, type, element);
+ }
+ idx++;
+ }
+ cache.put(annotationType, desc);
+ }
+ return desc;
+ }
+
+ /**
+ * Provides a new annotation instance.
+ * @param annotationType the annotation type definition
+ * @param elements name-value pairs representing elements of the annotation
+ * @return a new annotation instance
+ */
+ public static Annotation createAnnotation(
+ Class<? extends Annotation> annotationType,
+ AnnotationMember[] elements)
+ {
+ AnnotationFactory antn = new AnnotationFactory(annotationType, elements);
+ return (Annotation)Proxy.newProxyInstance( annotationType.getClassLoader(),
+ new Class[]{annotationType}, antn);
+ }
+
+ private final Class<? extends Annotation> klazz;
+ private AnnotationMember[] elements;
+
+ /**
+ * New instances should not be created directly, use factory method
+ * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
+ * instead.
+ *
+ * @param klzz class defining the annotation type
+ * @param values actual element values
+ */
+ private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) {
+ klazz = klzz;
+ AnnotationMember[] defs = getElementsDescription(klazz);
+ if (values == null) {
+ elements = defs;
+ } else {
+ //merge default and actual values
+ elements = new AnnotationMember[defs.length];
+ next: for (int i = elements.length - 1; i >= 0; i-- ){
+ for (AnnotationMember val : values){
+ if (val.name.equals(defs[i].name)) {
+ elements[i] = val.setDefinition(defs[i]);
+ continue next;
+ }
+ }
+ elements[i] = defs[i];
+ }
+ }
+ }
+
+ /**
+ * Reads the object, obtains actual member definitions for the annotation type,
+ * and merges deserialized values with the new definitions.
+ */
+ private void readObject(ObjectInputStream os) throws IOException,
+ ClassNotFoundException {
+ os.defaultReadObject();
+ // Annotation type members can be changed arbitrarily
+ // So there may be zombi elements from the previous life;
+ // they hardly fit into this new annotation's incarnation,
+ // as we have no defining methods for them.
+ // Reasonably just drop such elements,
+ // but seems better to keep them for compatibility
+ AnnotationMember[] defs = getElementsDescription(klazz);
+ AnnotationMember[] old = elements;
+ List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
+ defs.length + old.length);
+ nextOld: for (AnnotationMember el1 : old) {
+ for (AnnotationMember el2 : defs) {
+ if (el2.name.equals(el1.name)) {
+ continue nextOld;
+ }
+ }
+ merged.add(el1); //phantom element
+ }
+ nextNew: for (AnnotationMember def : defs){
+ for (AnnotationMember val : old){
+ if (val.name.equals(def.name)) {
+ // nothing to do about cached errors (if any)
+ // anyway they remain relevant to values
+ merged.add(val.setDefinition(def));
+ continue nextNew;
+ }
+ }
+ merged.add(def); // brand new element
+ }
+ elements = merged.toArray(new AnnotationMember[merged.size()]);
+ }
+
+ /**
+ * Returns true if the specified object represents the same annotation instance.
+ * That is, if it implements the same annotation type and
+ * returns the same element values.
+ * <br>Note, actual underlying implementation mechanism does not matter - it may
+ * differ completely from this class.
+ * @return true if the passed object is equivalent annotation instance,
+ * false otherwise.
+ * @see android.lang.annotation.AnnotationMember#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!klazz.isInstance(obj)) {
+ return false;
+ }
+ Object handler = null;
+ if (Proxy.isProxyClass(obj.getClass())
+ && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) {
+ AnnotationFactory other = (AnnotationFactory) handler;
+ if (elements.length != other.elements.length) {
+ return false;
+ }
+ next: for (AnnotationMember el1 : elements){
+ for (AnnotationMember el2 : other.elements) {
+ if (el1.equals(el2)) {
+ continue next;
+ }
+ }
+ return false;
+ }
+ return true;
+ } else {
+ // encountered foreign annotation implementaton
+ // so have to obtain element values via invocation
+ // of corresponding methods
+ for (final AnnotationMember el : elements) {
+ if (el.tag == ERROR) {
+ // undefined value is incomparable (transcendent)
+ return false;
+ }
+ try {
+ if (!el.definingMethod.isAccessible()) {
+ AccessController.doPrivileged(new PrivilegedAction<Object>(){
+ public Object run() {
+ try {
+ el.definingMethod.setAccessible(true);
+ } catch (Exception ignore) {}
+ return null;
+ }
+ });
+ }
+ Object otherValue = el.definingMethod.invoke(obj);
+ if (otherValue != null ) {
+ if (el.tag == ARRAY) {
+ if (!el.equalArrayValue(otherValue)) {
+ return false;
+ }
+ } else {
+ if (!el.value.equals(otherValue)) {
+ return false;
+ }
+ }
+ } else if (el.value != AnnotationMember.NO_VALUE) {
+ return false;
+ }
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Returns a hash code composed as a sum of hash codes of member elements,
+ * including elements with default values.
+ * @see android.lang.annotation.AnnotationMember#hashCode()
+ */
+ public int hashCode() {
+ int hash = 0;
+ for (AnnotationMember element : elements) {
+ hash += element.hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * Provides detailed description of this annotation instance,
+ * including all member name-values pairs.
+ * @return string representation of this annotation
+ */
+ public String toString() {
+ String res = "@" + klazz.getName() + "(";
+ for(int i = 0; i < elements.length; i++) {
+ if ( i != 0 ) {
+ res += ", ";
+ }
+ res += elements[i].toString();;
+ }
+ return res + ")";
+ }
+
+ /**
+ * Processes a method invocation request to this annotation instance.
+ * Recognizes the methods declared in the
+ * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
+ * interface, and member-defining methods of the implemented annotation type.
+ * @throws IllegalArgumentException If the specified method is none of the above
+ * @return the invocation result
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ String name = method.getName();
+ Class[] params = method.getParameterTypes();
+ if (params.length == 0) {
+ if ("annotationType".equals(name)) {
+ return klazz;
+ } else if ("toString".equals(name)) {
+ return toString();
+ } else if ("hashCode".equals(name)) {
+ return hashCode();
+ }
+
+ // this must be element value request
+ AnnotationMember element = null;
+ for (AnnotationMember el : elements) {
+ if (name.equals(el.name)) {
+ element = el;
+ break;
+ }
+ }
+ if (element == null || !method.equals(element.definingMethod)) {
+ throw new IllegalArgumentException(method.toString());
+ } else {
+ Object value = element.validateValue();
+ if (value == null) {
+ throw new IncompleteAnnotationException(klazz, name);
+ }
+ return value;
+ }
+ } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){
+ return Boolean.valueOf(equals(args[0]));
+ }
+ throw new IllegalArgumentException(
+ "Invalid method for annotation type: " + method);
+ }
+}
diff --git a/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java
new file mode 100644
index 0000000..f8dea31
--- /dev/null
+++ b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java
@@ -0,0 +1,387 @@
+/*
+ * 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.lang.annotation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.annotation.AnnotationTypeMismatchException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * This class represents member element of an annotation.
+ * It consists of name and value, supplemented with element
+ * definition information (such as declared type of element).
+ * <br>The value may be one of the following types:
+ * <ul>
+ * <li> boxed primitive
+ * <li> Class
+ * <li> enum constant
+ * <li> annotation (nested)
+ * <li> one-dimensional array of the above
+ * <li> Throwable
+ * </ul>
+ * The last type is specific for this implementation; a Throwable value
+ * means that the error occured during parsing or resolution of corresponding
+ * class-data structures and throwing is delayed until the element
+ * is requested for value.
+ *
+ * @see android.lang.annotation.AnnotationFactory
+ *
+ * @author Alexey V. Varlamov, Serguei S. Zapreyev
+ * @version $Revision$
+ */
+@SuppressWarnings({"serial"})
+public class AnnotationMember implements Serializable {
+
+ /**
+ * Tag description of a Throwable value type.
+ */
+ protected static final char ERROR = '!';
+
+ /**
+ * Tag description of an array value type.
+ */
+ protected static final char ARRAY = '[';
+
+ /**
+ * Tag description of all value types except arrays and Throwables.
+ */
+ protected static final char OTHER = '*';
+
+// public static final char INT = 'I';
+// public static final char CHAR = 'C';
+// public static final char DOUBLE = 'D';
+// public static final char FLOAT = 'F';
+// public static final char BYTE = 'B';
+// public static final char LONG = 'J';
+// public static final char SHORT = 'S';
+// public static final char BOOL = 'Z';
+// public static final char CLASS = 'c';
+// public static final char ENUM = 'e';
+// public static final char ANTN = '@';
+
+ private enum DefaultValues {NO_VALUE}
+
+ /**
+ * Singleton representing missing element value.
+ */
+ protected static final Object NO_VALUE = DefaultValues.NO_VALUE;
+
+ protected final String name;
+ protected final Object value; // a primitive value is wrapped to the corresponding wrapper class
+ protected final char tag;
+ // no sense to serialize definition info as it can be changed arbitrarily
+ protected transient Class<?> elementType;
+ protected transient Method definingMethod;
+
+
+ /**
+ * Creates a new element with specified name and value.
+ * Definition info will be provided later when this
+ * element becomes actual annotation member.
+ * @param name element name, must not be null
+ * @param val element value, should be of addmissible type,
+ * as specified in the description of this class
+ *
+ * @see #setDefinition(AnnotationMember)
+ */
+ public AnnotationMember(String name, Object val) {
+ this.name = name;
+ value = val == null ? NO_VALUE : val;
+ if (value instanceof Throwable) {
+ tag = ERROR;
+ } else if (value.getClass().isArray()) {
+ tag = ARRAY;
+ } else {
+ tag = OTHER;
+ }
+ }
+
+ /**
+ * Creates the completely defined element.
+ * @param name element name, must not be null
+ * @param value element value, should be of addmissible type,
+ * as specified in the description of this class
+ * @param m element-defining method, reflected on the annotation type
+ * @param type declared type of this element
+ * (return type of the defining method)
+ */
+ public AnnotationMember(String name, Object val, Class type, Method m) {
+ this(name, val);
+
+ definingMethod = m;
+
+ if (type == int.class) {
+ elementType = Integer.class;
+ } else if (type == boolean.class) {
+ elementType = Boolean.class;
+ } else if (type == char.class) {
+ elementType = Character.class;
+ } else if (type == float.class) {
+ elementType = Float.class;
+ } else if (type == double.class) {
+ elementType = Double.class;
+ } else if (type == long.class) {
+ elementType = Long.class;
+ } else if (type == short.class) {
+ elementType = Short.class;
+ } else if (type == byte.class) {
+ elementType = Byte.class;
+ } else {
+ elementType = type;
+ }
+ }
+
+ /**
+ * Fills in element's definition info and returns this.
+ */
+ protected AnnotationMember setDefinition(AnnotationMember copy) {
+ definingMethod = copy.definingMethod;
+ elementType = copy.elementType;
+ return this;
+ }
+
+ /**
+ * Returns readable description of this annotation value.
+ */
+ public String toString() {
+ if (tag == ARRAY) {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append(name).append("=[");
+ int len = Array.getLength(value);
+ for (int i = 0; i < len; i++) {
+ if (i != 0) sb.append(", ");
+ sb.append(Array.get(value, i));
+ }
+ return sb.append("]").toString();
+ } else {
+ return name+ "=" +value;
+ }
+ }
+
+ /**
+ * Returns true if the specified object represents equal element
+ * (equivalent name-value pair).
+ * <br> A special case is the contained Throwable value; it is considered
+ * transcendent so no other element would be equal.
+ * @return true if passed object is equivalent element representation,
+ * false otherwise
+ * @see #equalArrayValue(Object)
+ * @see java.lang.annotation.Annotation#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ // not a mere optimization,
+ // this is needed for consistency with hashCode()
+ return true;
+ }
+ if (obj instanceof AnnotationMember) {
+ AnnotationMember that = (AnnotationMember)obj;
+ if (name.equals(that.name) && tag == that.tag) {
+ if (tag == ARRAY) {
+ return equalArrayValue(that.value);
+ } else if (tag == ERROR) {
+ // undefined value is incomparable (transcendent)
+ return false;
+ } else {
+ return value.equals(that.value);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the contained value and a passed object are equal arrays,
+ * false otherwise. Appropriate overloaded method of Arrays.equals()
+ * is used for equality testing.
+ * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[])
+ * @return true if the value is array and is equal to specified object,
+ * false otherwise
+ */
+ public boolean equalArrayValue(Object otherValue) {
+ if (value instanceof Object[] && otherValue instanceof Object[]) {
+ return Arrays.equals((Object[])value, (Object[])otherValue);
+ }
+ Class type = value.getClass();
+ if (type != otherValue.getClass()) {
+ return false;
+ }
+ if (type == int[].class) {
+ return Arrays.equals((int[])value, (int[])otherValue);
+ } else if (type == byte[].class) {
+ return Arrays.equals((byte[])value, (byte[])otherValue);
+ } else if (type == short[].class) {
+ return Arrays.equals((short[])value, (short[])otherValue);
+ } else if (type == long[].class) {
+ return Arrays.equals((long[])value, (long[])otherValue);
+ } else if (type == char[].class) {
+ return Arrays.equals((char[])value, (char[])otherValue);
+ } else if (type == boolean[].class) {
+ return Arrays.equals((boolean[])value, (boolean[])otherValue);
+ } else if (type == float[].class) {
+ return Arrays.equals((float[])value, (float[])otherValue);
+ } else if (type == double[].class) {
+ return Arrays.equals((double[])value, (double[])otherValue);
+ }
+ return false;
+ }
+
+ /**
+ * Computes hash code of this element. The formula is as follows:
+ * <code> (name.hashCode() * 127) ^ value.hashCode() </code>
+ * <br>If value is an array, one of overloaded Arrays.hashCode()
+ * methods is used.
+ * @return the hash code
+ * @see java.util.Arrays#hashCode(java.lang.Object[])
+ * @see java.lang.annotation.Annotation#hashCode()
+ */
+ public int hashCode() {
+ int hash = name.hashCode() * 127;
+ if (tag == ARRAY) {
+ Class type = value.getClass();
+ if (type == int[].class) {
+ return hash ^ Arrays.hashCode((int[])value);
+ } else if (type == byte[].class) {
+ return hash ^ Arrays.hashCode((byte[])value);
+ } else if (type == short[].class) {
+ return hash ^ Arrays.hashCode((short[])value);
+ } else if (type == long[].class) {
+ return hash ^ Arrays.hashCode((long[])value);
+ } else if (type == char[].class) {
+ return hash ^ Arrays.hashCode((char[])value);
+ } else if (type == boolean[].class) {
+ return hash ^ Arrays.hashCode((boolean[])value);
+ } else if (type == float[].class) {
+ return hash ^ Arrays.hashCode((float[])value);
+ } else if (type == double[].class) {
+ return hash ^ Arrays.hashCode((double[])value);
+ }
+ return hash ^ Arrays.hashCode((Object[])value);
+ } else {
+ return hash ^ value.hashCode();
+ }
+ }
+
+ /**
+ * Throws contained error (if any) with a renewed stack trace.
+ */
+ public void rethrowError() throws Throwable {
+ if (tag == ERROR) {
+ // need to throw cloned exception for thread safety
+ // besides it is better to provide actual stack trace
+ // rather than recorded during parsing
+
+ // first check for expected types
+ if (value instanceof TypeNotPresentException) {
+ TypeNotPresentException tnpe = (TypeNotPresentException)value;
+ throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause());
+ } else if (value instanceof EnumConstantNotPresentException) {
+ EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value;
+ throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName());
+ } else if (value instanceof ArrayStoreException) {
+ ArrayStoreException ase = (ArrayStoreException)value;
+ throw new ArrayStoreException(ase.getMessage());
+ }
+ // got some other error, have to go with deep cloning
+ // via serialization mechanism
+ Throwable error = (Throwable)value;
+ StackTraceElement[] ste = error.getStackTrace();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(
+ ste == null ? 512 : (ste.length + 1) * 80);
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(error);
+ oos.flush();
+ oos.close();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos
+ .toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ error = (Throwable)ois.readObject();
+ ois.close();
+
+ throw error;
+ }
+ }
+
+ /**
+ * Validates contained value against its member definition
+ * and if ok returns the value.
+ * Otherwise, if the value type mismatches definition
+ * or the value itself describes an error,
+ * throws appropriate exception.
+ * <br> Note, this method may return null if this element was constructed
+ * with such value.
+ *
+ * @see #rethrowError()
+ * @see #copyValue()
+ * @return actual valid value or null if no value
+ */
+ public Object validateValue() throws Throwable {
+ if (tag == ERROR) {
+ rethrowError();
+ }
+ if (value == NO_VALUE) {
+ return null;
+ }
+ if (elementType == value.getClass()
+ || elementType.isInstance(value)) { // nested annotation value
+ return copyValue();
+ } else {
+ throw new AnnotationTypeMismatchException(definingMethod,
+ value.getClass().getName());
+ }
+
+ }
+
+
+ /**
+ * Provides mutation-safe access to contained value. That is, caller is free
+ * to modify the returned value, it will not affect the contained data value.
+ * @return cloned value if it is mutable or the original immutable value
+ */
+ public Object copyValue() throws Throwable
+ {
+ if (tag != ARRAY || Array.getLength(value) == 0) {
+ return value;
+ }
+ Class type = value.getClass();
+ if (type == int[].class) {
+ return ((int[])value).clone();
+ } else if (type == byte[].class) {
+ return ((byte[])value).clone();
+ } else if (type == short[].class) {
+ return ((short[])value).clone();
+ } else if (type == long[].class) {
+ return ((long[])value).clone();
+ } else if (type == char[].class) {
+ return ((char[])value).clone();
+ } else if (type == boolean[].class) {
+ return ((boolean[])value).clone();
+ } else if (type == float[].class) {
+ return ((float[])value).clone();
+ } else if (type == double[].class) {
+ return ((double[])value).clone();
+ }
+ return ((Object[])value).clone();
+ }
+}
diff --git a/luni-kernel/src/main/native/java_lang_ProcessManager.c b/luni-kernel/src/main/native/java_lang_ProcessManager.c
new file mode 100644
index 0000000..ee2fc58
--- /dev/null
+++ b/luni-kernel/src/main/native/java_lang_ProcessManager.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "ProcessManager"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "utils/Log.h"
+#include "AndroidSystemNatives.h"
+
+/** Environment variables. */
+extern char **environ;
+
+static jmethodID onExitMethod = NULL;
+static jfieldID descriptorField = NULL;
+
+#ifdef ANDROID
+// Keeps track of the system properties fd so we don't close it.
+static int androidSystemPropertiesFd = -1;
+#endif
+
+/*
+ * These are constants shared with the higher level code in
+ * ProcessManager.java.
+ */
+#define WAIT_STATUS_UNKNOWN (-1) // unknown child status
+#define WAIT_STATUS_NO_CHILDREN (-2) // no children to wait for
+#define WAIT_STATUS_STRANGE_ERRNO (-3) // observed an undocumented errno
+
+/** Closes a file descriptor. */
+static int closeNow(int fd) {
+ int result;
+ do {
+ result = close(fd);
+ } while (result == -1 && errno == EINTR);
+ return result;
+}
+
+/** Closes a file descriptor. */
+static void java_lang_ProcessManager_close(JNIEnv* env,
+ jclass clazz, jobject javaDescriptor) {
+ int fd = (*env)->GetIntField(env, javaDescriptor, descriptorField);
+ if (closeNow(fd) == -1) {
+ jclass ioException = (*env)->FindClass(env, "java/io/IOException");
+ (*env)->ThrowNew(env, ioException, strerror(errno));
+ }
+}
+
+/**
+ * Kills process with the given ID.
+ */
+static void java_lang_ProcessManager_kill(JNIEnv* env, jclass clazz, jint pid) {
+ int result = kill((pid_t) pid, SIGKILL);
+ if (result == -1) {
+ jniThrowIOException(env, errno);
+ }
+}
+
+/**
+ * Loops indefinitely and calls ProcessManager.onExit() when children exit.
+ */
+static void java_lang_ProcessManager_watchChildren(JNIEnv* env, jobject o) {
+ if (onExitMethod == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "staticInitialize() must run first.");
+ }
+
+ while (1) {
+ int status;
+
+ pid_t pid = wait(&status);
+
+ if (pid >= 0) {
+ // Extract real status.
+ if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ status = WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ status = WSTOPSIG(status);
+ } else {
+ status = WAIT_STATUS_UNKNOWN;
+ }
+ } else {
+ /*
+ * The pid should be -1 already, but force it here just in case
+ * we somehow end up with some other negative value.
+ */
+ pid = -1;
+
+ switch (errno) {
+ case ECHILD: {
+ /*
+ * Expected errno: There are no children to wait()
+ * for. The callback will sleep until it is
+ * informed of another child coming to life.
+ */
+ status = WAIT_STATUS_NO_CHILDREN;
+ break;
+ }
+ case EINTR: {
+ /*
+ * An unblocked signal came in while waiting; just
+ * retry the wait().
+ */
+ continue;
+ }
+ default: {
+ /*
+ * Unexpected errno, so squawk! Note: Per the
+ * Linux docs, there are no errnos defined for
+ * wait() other than the two that are handled
+ * immediately above.
+ */
+ LOGE("Error %d calling wait(): %s", errno,
+ strerror(errno));
+ status = WAIT_STATUS_STRANGE_ERRNO;
+ break;
+ }
+ }
+ }
+
+ (*env)->CallVoidMethod(env, o, onExitMethod, pid, status);
+
+ if ((*env)->ExceptionOccurred(env)) {
+ /*
+ * The callback threw, so break out of the loop and return,
+ * letting the exception percolate up.
+ */
+ break;
+ }
+ }
+}
+
+/** Close all open fds > 2 (i.e. everything but stdin/out/err). */
+static void closeNonStandardFds(int skipFd) {
+ DIR* dir = opendir("/proc/self/fd");
+
+ if (dir == NULL) {
+ // Print message to standard err. The parent process can read this
+ // from Process.getErrorStream().
+ perror("opendir");
+ return;
+ }
+
+ struct dirent* entry;
+ int dirFd = dirfd(dir);
+ while ((entry = readdir(dir)) != NULL) {
+ int fd = atoi(entry->d_name);
+ if (fd > 2 && fd != dirFd && fd != skipFd
+#ifdef ANDROID
+ && fd != androidSystemPropertiesFd
+#endif
+ ) {
+ close(fd);
+ }
+ }
+
+ closedir(dir);
+}
+
+#define PIPE_COUNT (4) // number of pipes used to communicate with child proc
+
+/** Closes all pipes in the given array. */
+static void closePipes(int pipes[], int skipFd) {
+ int i;
+ for (i = 0; i < PIPE_COUNT * 2; i++) {
+ int fd = pipes[i];
+ if (fd == -1) {
+ return;
+ }
+ if (fd != skipFd) {
+ close(pipes[i]);
+ }
+ }
+}
+
+/** Executes a command in a child process. */
+static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,
+ const char* workingDirectory, jobject inDescriptor,
+ jobject outDescriptor, jobject errDescriptor) {
+ int i, result, error;
+
+ // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
+ int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+ for (i = 0; i < PIPE_COUNT; i++) {
+ if (pipe(pipes + i * 2) == -1) {
+ jniThrowIOException(env, errno);
+ closePipes(pipes, -1);
+ return -1;
+ }
+ }
+ int stdinIn = pipes[0];
+ int stdinOut = pipes[1];
+ int stdoutIn = pipes[2];
+ int stdoutOut = pipes[3];
+ int stderrIn = pipes[4];
+ int stderrOut = pipes[5];
+ int statusIn = pipes[6];
+ int statusOut = pipes[7];
+
+ pid_t childPid = fork();
+
+ // If fork() failed...
+ if (childPid == -1) {
+ jniThrowIOException(env, errno);
+ closePipes(pipes, -1);
+ return -1;
+ }
+
+ // If this is the child process...
+ if (childPid == 0) {
+ // Replace stdin, out, and err with pipes.
+ dup2(stdinIn, 0);
+ dup2(stdoutOut, 1);
+ dup2(stderrOut, 2);
+
+ // Close all but statusOut. This saves some work in the next step.
+ closePipes(pipes, statusOut);
+
+ // Make statusOut automatically close if execvp() succeeds.
+ fcntl(statusOut, F_SETFD, FD_CLOEXEC);
+
+ // Close remaining open fds with the exception of statusOut.
+ closeNonStandardFds(statusOut);
+
+ // Switch to working directory.
+ if (workingDirectory != NULL) {
+ if (chdir(workingDirectory) == -1) {
+ goto execFailed;
+ }
+ }
+
+ // Set up environment.
+ if (environment != NULL) {
+ environ = environment;
+ }
+
+ // Execute process. By convention, the first argument in the arg array
+ // should be the command itself. In fact, I get segfaults when this
+ // isn't the case.
+ execvp(commands[0], commands);
+
+ // If we got here, execvp() failed or the working dir was invalid.
+ execFailed:
+ error = errno;
+ write(statusOut, &error, sizeof(int));
+ close(statusOut);
+ exit(error);
+ }
+
+ // This is the parent process.
+
+ // Close child's pipe ends.
+ close(stdinIn);
+ close(stdoutOut);
+ close(stderrOut);
+ close(statusOut);
+
+ // Check status pipe for an error code. If execvp() succeeds, the other
+ // end of the pipe should automatically close, in which case, we'll read
+ // nothing.
+ int count = read(statusIn, &result, sizeof(int));
+ close(statusIn);
+ if (count > 0) {
+ jniThrowIOException(env, result);
+
+ close(stdoutIn);
+ close(stdinOut);
+ close(stderrIn);
+
+ return -1;
+ }
+
+ // Fill in file descriptor wrappers.
+ jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
+ jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
+ jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);
+
+ return childPid;
+}
+
+/** Converts a Java String[] to a 0-terminated char**. */
+static char** convertStrings(JNIEnv* env, jobjectArray javaArray) {
+ if (javaArray == NULL) {
+ return NULL;
+ }
+
+ char** array = NULL;
+ jsize length = (*env)->GetArrayLength(env, javaArray);
+ array = (char**) malloc(sizeof(char*) * (length + 1));
+ array[length] = 0;
+ jsize index;
+ for (index = 0; index < length; index++) {
+ jstring javaEntry = (jstring) (*env)->GetObjectArrayElement(
+ env, javaArray, index);
+ char* entry = (char*) (*env)->GetStringUTFChars(
+ env, javaEntry, NULL);
+ array[index] = entry;
+ }
+
+ return array;
+}
+
+/** Frees a char** which was converted from a Java String[]. */
+static void freeStrings(JNIEnv* env, jobjectArray javaArray, char** array) {
+ if (javaArray == NULL) {
+ return;
+ }
+
+ jsize length = (*env)->GetArrayLength(env, javaArray);
+ jsize index;
+ for (index = 0; index < length; index++) {
+ jstring javaEntry = (jstring) (*env)->GetObjectArrayElement(
+ env, javaArray, index);
+ (*env)->ReleaseStringUTFChars(env, javaEntry, array[index]);
+ }
+
+ free(array);
+}
+
+/**
+ * Converts Java String[] to char** and delegates to executeProcess().
+ */
+static pid_t java_lang_ProcessManager_exec(
+ JNIEnv* env, jclass clazz, jobjectArray javaCommands,
+ jobjectArray javaEnvironment, jstring javaWorkingDirectory,
+ jobject inDescriptor, jobject outDescriptor, jobject errDescriptor) {
+
+ // Copy commands into char*[].
+ char** commands = convertStrings(env, javaCommands);
+
+ // Extract working directory string.
+ const char* workingDirectory = NULL;
+ if (javaWorkingDirectory != NULL) {
+ workingDirectory = (const char*) (*env)->GetStringUTFChars(
+ env, javaWorkingDirectory, NULL);
+ }
+
+ // Convert environment array.
+ char** environment = convertStrings(env, javaEnvironment);
+
+ pid_t result = executeProcess(
+ env, commands, environment, workingDirectory,
+ inDescriptor, outDescriptor, errDescriptor);
+
+ // Temporarily clear exception so we can clean up.
+ jthrowable exception = (*env)->ExceptionOccurred(env);
+ (*env)->ExceptionClear(env);
+
+ freeStrings(env, javaEnvironment, environment);
+
+ // Clean up working directory string.
+ if (javaWorkingDirectory != NULL) {
+ (*env)->ReleaseStringUTFChars(
+ env, javaWorkingDirectory, workingDirectory);
+ }
+
+ freeStrings(env, javaCommands, commands);
+
+ // Re-throw exception if present.
+ if (exception != NULL) {
+ if ((*env)->Throw(env, exception) < 0) {
+ LOGE("Error rethrowing exception!");
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Looks up Java members.
+ */
+static void java_lang_ProcessManager_staticInitialize(JNIEnv* env,
+ jclass clazz) {
+#ifdef ANDROID
+ char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE");
+ if (fdString) {
+ androidSystemPropertiesFd = atoi(fdString);
+ }
+#endif
+
+ onExitMethod = (*env)->GetMethodID(env, clazz, "onExit", "(II)V");
+ if (onExitMethod == NULL) {
+ return;
+ }
+
+ jclass fileDescriptorClass
+ = (*env)->FindClass(env, "java/io/FileDescriptor");
+ if (fileDescriptorClass == NULL) {
+ return;
+ }
+ descriptorField = (*env)->GetFieldID(env, fileDescriptorClass,
+ "descriptor", "I");
+ if (descriptorField == NULL) {
+ return;
+ }
+}
+
+static JNINativeMethod methods[] = {
+ { "kill", "(I)V", (void*) java_lang_ProcessManager_kill },
+ { "watchChildren", "()V", (void*) java_lang_ProcessManager_watchChildren },
+ { "exec", "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;"
+ "Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;"
+ "Ljava/io/FileDescriptor;)I", (void*) java_lang_ProcessManager_exec },
+ { "staticInitialize", "()V",
+ (void*) java_lang_ProcessManager_staticInitialize },
+ { "close", "(Ljava/io/FileDescriptor;)V",
+ (void*) java_lang_ProcessManager_close },
+};
+
+int register_java_lang_ProcessManager(JNIEnv* env) {
+ return jniRegisterNativeMethods(
+ env, "java/lang/ProcessManager", methods, NELEM(methods));
+}
diff --git a/luni-kernel/src/main/native/java_lang_System.c b/luni-kernel/src/main/native/java_lang_System.c
new file mode 100644
index 0000000..1c06a40
--- /dev/null
+++ b/luni-kernel/src/main/native/java_lang_System.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 "JNIHelp.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+ * public static native String getEnvByName(String name)
+ *
+ * (Calling it plain "getenv" might confuse GDB if you try to put a breakpoint
+ * on the libc version.)
+ */
+static jstring java_getEnvByName(JNIEnv* env, jclass clazz, jstring nameStr)
+{
+ jstring valueStr = NULL;
+
+ if (nameStr != NULL) {
+ const char* name;
+ const char* val;
+
+ name = (*env)->GetStringUTFChars(env, nameStr, NULL);
+ val = getenv(name);
+ if (val != NULL)
+ valueStr = (*env)->NewStringUTF(env, val);
+
+ (*env)->ReleaseStringUTFChars(env, nameStr, name);
+ } else {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ }
+
+ return valueStr;
+}
+
+/*
+ * Pointer to complete environment, from Posix.
+ */
+extern char** environ;
+
+/*
+ * public static native String getEnvByIndex()
+ *
+ * (Calling it plain "getenv" might confuse GDB if you try to put a breakpoint
+ * on the libc version.)
+ */
+static jstring java_getEnvByIndex(JNIEnv* env, jclass clazz, jint index)
+{
+ jstring valueStr = NULL;
+
+ char* entry = environ[index];
+ if (entry != NULL) {
+ valueStr = (*env)->NewStringUTF(env, entry);
+ }
+
+ return valueStr;
+}
+
+/*
+ * public static native String setFieldImpl()
+ *
+ * Sets a field via JNI. Used for the standard streams, which are r/o
+ * otherwise.
+ */
+static void java_setFieldImpl(JNIEnv* env, jclass clazz, jstring name, jstring sig, jobject object)
+{
+ const char* fieldName = (*env)->GetStringUTFChars(env, name, NULL);
+ const char* fieldSig = (*env)->GetStringUTFChars(env, sig, NULL);
+
+ jfieldID fieldID = (*env)->GetStaticFieldID(env, clazz, fieldName, fieldSig);
+ (*env)->SetStaticObjectField(env, clazz, fieldID, object);
+
+ (*env)->ReleaseStringUTFChars(env, name, fieldName);
+ (*env)->ReleaseStringUTFChars(env, sig, fieldSig);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "getEnvByName", "(Ljava/lang/String;)Ljava/lang/String;", java_getEnvByName },
+ { "getEnvByIndex", "(I)Ljava/lang/String;", java_getEnvByIndex },
+ { "setFieldImpl", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V", java_setFieldImpl },
+};
+
+int register_java_lang_System(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "java/lang/System",
+ gMethods, NELEM(gMethods));
+}
+
diff --git a/luni-kernel/src/main/native/sub.mk b/luni-kernel/src/main/native/sub.mk
new file mode 100644
index 0000000..cd4ce92
--- /dev/null
+++ b/luni-kernel/src/main/native/sub.mk
@@ -0,0 +1,17 @@
+# This file is included by the top-level libcore Android.mk.
+# It's not a normal makefile, so we don't include CLEAR_VARS
+# or BUILD_*_LIBRARY.
+
+LOCAL_SRC_FILES := \
+ java_lang_ProcessManager.c \
+ java_lang_System.c
+
+LOCAL_C_INCLUDES +=
+
+# Any shared/static libs that are listed here must also
+# be listed in libs/nativehelper/Android.mk.
+# TODO: fix this requirement
+
+LOCAL_SHARED_LIBRARIES +=
+
+LOCAL_STATIC_LIBRARIES +=
diff --git a/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java b/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java
new file mode 100644
index 0000000..3791191
--- /dev/null
+++ b/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed 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 tests.api.org.apache.harmony.kernel.dalvik;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import sun.misc.Unsafe;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+
+/**
+ * Tests for the <code>park()</code> functionality of {@link Unsafe}.
+ */
+@TestTargetClass(Unsafe.class)
+public class ThreadsTest extends TestCase {
+ private static Unsafe UNSAFE = null;
+ private static RuntimeException INITIALIZEFAILED = null;
+
+ static {
+ /*
+ * Set up {@link #UNSAFE}. This subverts the access check to
+ * get the unique Unsafe instance. We can do this because
+ * there's no security manager installed when running the
+ * test.
+ */
+ try {
+ Field field = Unsafe.class.getDeclaredField("THE_ONE");
+ field.setAccessible(true);
+
+ UNSAFE = (Unsafe) field.get(null);
+ } catch (NoSuchFieldException ex) {
+ INITIALIZEFAILED = new RuntimeException(ex);
+ } catch (IllegalAccessException ex) {
+ INITIALIZEFAILED = new RuntimeException(ex);
+ }
+ }
+
+ /** Test the case where the park times out. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkFor_1() {
+ Parker parker = new Parker(false, 500);
+ Thread parkerThread = new Thread(parker);
+ Thread waiterThread =
+ new Thread(new WaitAndUnpark(1000, parkerThread));
+
+ parkerThread.start();
+ waiterThread.start();
+ parker.assertDurationIsInRange(500);
+ }
+
+ /** Test the case where the unpark happens before the timeout. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkFor_2() {
+ Parker parker = new Parker(false, 1000);
+ Thread parkerThread = new Thread(parker);
+ Thread waiterThread =
+ new Thread(new WaitAndUnpark(300, parkerThread));
+
+ parkerThread.start();
+ waiterThread.start();
+ parker.assertDurationIsInRange(300);
+ }
+
+ /** Test the case where the thread is preemptively unparked. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkFor_3() {
+ Parker parker = new Parker(false, 1000);
+ Thread parkerThread = new Thread(parker);
+
+ UNSAFE.unpark(parkerThread);
+ parkerThread.start();
+ parker.assertDurationIsInRange(0);
+ }
+
+ /** Test the case where the park times out. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkUntil_1() {
+ Parker parker = new Parker(true, 500);
+ Thread parkerThread = new Thread(parker);
+ Thread waiterThread =
+ new Thread(new WaitAndUnpark(1000, parkerThread));
+
+ parkerThread.start();
+ waiterThread.start();
+ parker.assertDurationIsInRange(500);
+ }
+
+ /** Test the case where the unpark happens before the timeout. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkUntil_2() {
+ Parker parker = new Parker(true, 1000);
+ Thread parkerThread = new Thread(parker);
+ Thread waiterThread =
+ new Thread(new WaitAndUnpark(300, parkerThread));
+
+ parkerThread.start();
+ waiterThread.start();
+ parker.assertDurationIsInRange(300);
+ }
+
+ /** Test the case where the thread is preemptively unparked. */
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "",
+ method = "unpark",
+ args = {Object.class}
+ )
+ public void test_parkUntil_3() {
+ Parker parker = new Parker(true, 1000);
+ Thread parkerThread = new Thread(parker);
+
+ UNSAFE.unpark(parkerThread);
+ parkerThread.start();
+ parker.assertDurationIsInRange(0);
+ }
+
+ // TODO: Add more tests.
+
+ /**
+ * Helper <code>Runnable</code> for tests, which parks for or until
+ * the indicated value, noting the duration of time actually parked.
+ */
+ private static class Parker implements Runnable {
+ /** whether {@link #amount} is milliseconds to wait in an
+ * absolute fashion (<code>true</code>) or nanoseconds to wait
+ * in a relative fashion (<code>false</code>) */
+ private final boolean absolute;
+
+ /** amount to wait (see above) */
+ private final long amount;
+
+ /** whether the run has completed */
+ private boolean completed;
+
+ /** recorded start time */
+ private long startMillis;
+
+ /** recorded end time */
+ private long endMillis;
+
+ /**
+ * Construct an instance.
+ *
+ * @param absolute whether to use an absolute time or not; in
+ * either case, this constructor takes a duration to park for
+ * @param parkMillis the number of milliseconds to be parked
+ */
+ public Parker(boolean absolute, long parkMillis) {
+ this.absolute = absolute;
+
+ // Multiply by 1000000 because parkFor() takes nanoseconds.
+ this.amount = absolute ? parkMillis : parkMillis * 1000000;
+ }
+
+ public void run() {
+ boolean absolute = this.absolute;
+ long amount = this.amount;
+ long start = System.currentTimeMillis();
+
+ if (absolute) {
+ UNSAFE.park(true, start + amount);
+ } else {
+ UNSAFE.park(false, amount);
+ }
+
+ long end = System.currentTimeMillis();
+
+ synchronized (this) {
+ startMillis = start;
+ endMillis = end;
+ completed = true;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Wait for the test to complete and return the duration.
+ *
+ * @param maxWaitMillis the maximum amount of time to
+ * wait for the test to complete
+ * @return the duration in milliseconds
+ */
+ public long getDurationMillis(long maxWaitMillis) {
+ synchronized (this) {
+ if (! completed) {
+ try {
+ wait(maxWaitMillis);
+ } catch (InterruptedException ex) {
+ // Ignore it.
+ }
+ if (! completed) {
+ Assert.fail("parker hanging");
+ }
+ }
+
+ return endMillis - startMillis;
+ }
+ }
+
+ /**
+ * Asserts that the actual duration is within 5% of the
+ * given expected time.
+ *
+ * @param expectedMillis the expected duration, in milliseconds
+ */
+ public void assertDurationIsInRange(long expectedMillis) {
+ /*
+ * Allow a bit more slop for the maximum on "expected
+ * instantaneous" results.
+ */
+ long minimum = (long) ((double) expectedMillis * 0.95);
+ long maximum =
+ Math.max((long) ((double) expectedMillis * 1.05), 10);
+ long waitMillis = Math.max(expectedMillis * 10, 10);
+ long duration = getDurationMillis(waitMillis);
+
+ if (duration < minimum) {
+ Assert.fail("expected duration: " + expectedMillis +
+ "; actual too short: " + duration);
+ } else if (duration > maximum) {
+ Assert.fail("expected duration: " + expectedMillis +
+ "; actual too long: " + duration);
+ }
+ }
+ }
+
+ /**
+ * Helper <code>Runnable</code> for tests, which waits for the
+ * specified amount of time and then unparks an indicated thread.
+ */
+ private static class WaitAndUnpark implements Runnable {
+ private final long waitMillis;
+ private final Thread thread;
+
+ public WaitAndUnpark(long waitMillis, Thread thread) {
+ this.waitMillis = waitMillis;
+ this.thread = thread;
+ }
+
+ public void run() {
+ try {
+ Thread.sleep(waitMillis);
+ } catch (InterruptedException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ UNSAFE.unpark(thread);
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (INITIALIZEFAILED != null)
+ throw INITIALIZEFAILED;
+ }
+}