Merge change 2768 into donut

* changes:
  Add an API demo that shows the effets of setting Bitmaps as being purgeable.
diff --git a/emulator/qemud/qemud.c b/emulator/qemud/qemud.c
index c578145..92b2a2b 100644
--- a/emulator/qemud/qemud.c
+++ b/emulator/qemud/qemud.c
@@ -849,6 +849,10 @@
 static void
 fdhandler_shutdown( FDHandler*  f )
 {
+    /* prevent later fdhandler_close() to
+     * call the receiver's close.
+     */
+    f->receiver->close = NULL;
 
     if (f->out_first != NULL && !f->closing)
     {
@@ -856,9 +860,6 @@
         f->closing = 1;
         fdhandler_remove(f);
         fdhandler_prepend(f, &f->list->closing);
-
-        /* notify the receiver that we're closing */
-        receiver_close(f->receiver);
         return;
     }
 
diff --git a/emulator/sensors/sensors_qemu.c b/emulator/sensors/sensors_qemu.c
index 85a5af4..0cc636a 100644
--- a/emulator/sensors/sensors_qemu.c
+++ b/emulator/sensors/sensors_qemu.c
@@ -34,6 +34,7 @@
 #include <errno.h>
 #include <string.h>
 #include <cutils/log.h>
+#include <cutils/native_handle.h>
 #include <cutils/sockets.h>
 #include <hardware/sensors.h>
 
@@ -123,16 +124,19 @@
 /* this must return a file descriptor that will be used to read
  * the sensors data (it is passed to data__data_open() below
  */
-static int
+static native_handle_t*
 control__open_data_source(struct sensors_control_device_t *dev)
 {
     SensorControl*  ctl = (void*)dev;
+    native_handle_t* handle;
 
     if (ctl->fd < 0) {
         ctl->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
     }
     D("%s: fd=%d", __FUNCTION__, ctl->fd);
-    return ctl->fd;
+    handle = native_handle_create(1, 0);
+    handle->data[0] = ctl->fd;
+    return handle;
 }
 
 static int
@@ -244,7 +248,7 @@
 }
 
 static int
-data__data_open(struct sensors_data_device_t *dev, int fd)
+data__data_open(struct sensors_data_device_t *dev, native_handle_t* handle)
 {
     SensorData*  data = (void*)dev;
     int i;
@@ -258,7 +262,9 @@
     data->timeStart      = 0;
     data->timeOffset     = 0;
 
-    data->events_fd = dup(fd);
+    data->events_fd = dup(handle->data[0]);
+    native_handle_close(handle);
+    native_handle_delete(handle);
     return 0;
 }
 
diff --git a/emulator/tools/Android.mk b/emulator/tools/Android.mk
new file mode 100644
index 0000000..c9d9613
--- /dev/null
+++ b/emulator/tools/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2009 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.
+
+# this file is used to build emulator-specific program tools
+# that should only run in the emulator.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_PRODUCT),sim)
+
+# The 'qemu-props' program is run from /system/etc/init.goldfish.rc
+# to setup various system properties sent by the emulator program.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE    := qemu-props
+LOCAL_SRC_FILES := qemu-props.c
+LOCAL_SHARED_LIBRARIES := libcutils
+# we don't want this in 'user' builds which don't have
+# emulator-specific binaries.
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
+
+endif # TARGET_PRODUCT != sim
+
diff --git a/emulator/tools/qemu-props.c b/emulator/tools/qemu-props.c
new file mode 100644
index 0000000..09105fc
--- /dev/null
+++ b/emulator/tools/qemu-props.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/* this program is used to read a set of system properties and their values
+ * from the emulator program and set them in the currently-running emulated
+ * system. It does so by connecting to the 'boot-properties' qemud service.
+ *
+ * This program should be run as root and called from
+ * /system/etc/init.goldfish.rc exclusively.
+ */
+
+#define LOG_TAG  "qemu-props"
+
+#define DEBUG  1
+
+#if DEBUG
+#  include <cutils/log.h>
+#  define  DD(...)    LOGI(__VA_ARGS__)
+#else
+#  define  DD(...)    ((void)0)
+#endif
+
+#include <cutils/properties.h>
+#include <unistd.h>
+#include <hardware/qemud.h>
+
+/* Name of the qemud service we want to connect to.
+ */
+#define  QEMUD_SERVICE  "boot-properties"
+
+#define  MAX_TRIES      5
+
+int  main(void)
+{
+    int  qemud_fd, count = 0;
+
+    /* try to connect to the qemud service */
+    {
+        int  tries = MAX_TRIES;
+
+        while (1) {
+            qemud_fd = qemud_channel_open( "boot-properties" );
+            if (qemud_fd >= 0)
+                break;
+
+            if (--tries <= 0) {
+                DD("Could not connect after too many tries. Aborting");
+                return 1;
+            }
+
+            DD("waiting 1s to wait for qemud.");
+            sleep(1);
+        }
+    }
+
+    DD("connected to '%s' qemud service.", QEMUD_SERVICE);
+
+    /* send the 'list' command to the service */
+    if (qemud_channel_send(qemud_fd, "list", -1) < 0) {
+        DD("could not send command to '%s' service", QEMUD_SERVICE);
+        return 1;
+    }
+
+    /* read each system property as a single line from the service,
+     * until exhaustion.
+     */
+    for (;;)
+    {
+#define  BUFF_SIZE   (PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2)
+
+        char* q;
+        char  temp[BUFF_SIZE];
+        int   len = qemud_channel_recv(qemud_fd, temp, sizeof temp - 1);
+
+        if (len < 0 || len > BUFF_SIZE-1)
+            break;
+
+        temp[len] = '\0';  /* zero-terminate string */
+
+        DD("received: %.*s", len, temp);
+
+        /* separate propery name from value */
+        q = strchr(temp, '=');
+        if (q == NULL) {
+            DD("invalid format, ignored.");
+            continue;
+        }
+        *q++ = '\0';
+
+        if (property_set(temp, q) < 0) {
+            DD("could not set property '%s' to '%s'", temp, q);
+        } else {
+            count += 1;
+        }
+    }
+
+    /* finally, close the channel and exit */
+    close(qemud_fd);
+    DD("exiting (%d properties set).", count);
+    return 0;
+}
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index f4d2cee..281349b 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -48,6 +48,7 @@
 	<classpathentry kind="src" path="frameworks/base/services/java"/>
 	<classpathentry kind="src" path="frameworks/base/telephony/java"/>
 	<classpathentry kind="src" path="frameworks/base/test-runner"/>
+	<classpathentry kind="src" path="frameworks/base/tts/java"/>
 	<classpathentry kind="src" path="frameworks/base/wifi/java"/>
 	<classpathentry kind="src" path="frameworks/policies/base/phone"/>
 	<classpathentry kind="src" path="development/samples/ApiDemos/src"/>
@@ -102,6 +103,7 @@
 	<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location/java"/>
 	<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java"/>
 	<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java"/>
+	<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/tts/java"/>
 	<classpathentry kind="src" path="out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/wifi/java"/>
 	<classpathentry kind="src" path="out/target/common/R"/>
 	<classpathentry kind="src" path="external/tagsoup/src"/>
diff --git a/ndk/README.TXT b/ndk/README.TXT
index 56299c6..4c5ff7f 100644
--- a/ndk/README.TXT
+++ b/ndk/README.TXT
@@ -9,6 +9,10 @@
 in docs/OVERVIEW.TXT. Please read this document as it contains crucial
 information for correct usage.
 
+See docs/STABLE-APIS.TXT for the list of frozen binary APIs exposed by
+this NDK, as well as the corresponding system image versions that support
+them.
+
 Before using the NDK, you will need to follow the steps described by
 docs/INSTALL.TXT which lists the NDK pre-requisites and the steps needed
 to set it up properly on your machine.
@@ -17,8 +21,10 @@
 note that the NDK is *not* a good way to write non-JNI native code for the
 Android platform.
 
-The document docs/ROADMAP.TXT gives a tentative roadmap for upcoming
-NDK features and improvements.
+See docs/HOWTO.TXT for a few useful tips and tricks when using the NDK.
+
+See docs/SYSTEM-ISSUES.TXT for a list of important issues related to
+the Android system images that all NDK developers should be aware of.
 
 Finally, discussions related to the Android NDK happen on the public
 "android-ndk" forum located at the following address:
diff --git a/ndk/apps/hello-jni/project/libs/armeabi/.gitignore b/ndk/apps/hello-jni/project/libs/armeabi/.gitignore
new file mode 100644
index 0000000..bd01364
--- /dev/null
+++ b/ndk/apps/hello-jni/project/libs/armeabi/.gitignore
@@ -0,0 +1 @@
+lib*.so
diff --git a/ndk/apps/two-libs/project/libs/armeabi/.gitignore b/ndk/apps/two-libs/project/libs/armeabi/.gitignore
new file mode 100644
index 0000000..bd01364
--- /dev/null
+++ b/ndk/apps/two-libs/project/libs/armeabi/.gitignore
@@ -0,0 +1 @@
+lib*.so
diff --git a/ndk/build/core/build-binary.mk b/ndk/build/core/build-binary.mk
index 6566fec..1446a7d 100644
--- a/ndk/build/core/build-binary.mk
+++ b/ndk/build/core/build-binary.mk
@@ -29,6 +29,10 @@
 # list of generated object files
 LOCAL_OBJECTS :=
 
+# always define ANDROID when building binaries
+#
+LOCAL_CFLAGS := -DANDROID $(LOCAL_CFLAGS)
+
 #
 # Add the default system shared libraries to the build
 #
diff --git a/ndk/build/core/definitions.mk b/ndk/build/core/definitions.mk
index 4135faa..1a425de 100644
--- a/ndk/build/core/definitions.mk
+++ b/ndk/build/core/definitions.mk
@@ -370,7 +370,6 @@
 $$(_OBJ): PRIVATE_CFLAGS   := $$($$(my)CFLAGS) \
                               $$($$(my)_$(LOCAL_ARM_MODE)_$(LOCAL_BUILD_MODE)_CFLAGS) \
                               -I$$(LOCAL_PATH) \
-                              -I$$(OBJS_DIR)   \
                               $$(LOCAL_CFLAGS) \
                               $$(NDK_APP_CPPFLAGS) \
                               $$(NDK_APP_CFLAGS) \
@@ -430,7 +429,6 @@
 $$(_OBJ): PRIVATE_CXXFLAGS := $$($$(my)CXXFLAGS) \
                               $$($$(my)_$(LOCAL_ARM_MODE)_$(LOCAL_BUILD_MODE)_CFLAGS) \
                               -I$$(LOCAL_PATH) \
-                              -I$$(OBJS_DIR)   \
                               $$(LOCAL_CFLAGS) \
                               $$(NDK_APP_CPPFLAGS) \
                               $$(NDK_APP_CXXFLAGS) \
diff --git a/ndk/build/host-setup.sh b/ndk/build/host-setup.sh
index 6926f1b..e94944e 100755
--- a/ndk/build/host-setup.sh
+++ b/ndk/build/host-setup.sh
@@ -54,6 +54,7 @@
 echo "Detecting host toolchain."
 echo ""
 
+force_32bit_binaries
 setup_toolchain
 
 create_config_mk
diff --git a/ndk/build/platforms/android-1.5/arch-arm/usr/include/android/log.h b/ndk/build/platforms/android-1.5/arch-arm/usr/include/android/log.h
new file mode 120000
index 0000000..da91a66
--- /dev/null
+++ b/ndk/build/platforms/android-1.5/arch-arm/usr/include/android/log.h
@@ -0,0 +1 @@
+../../../../common/include/android/log.h
\ No newline at end of file
diff --git a/ndk/build/platforms/android-1.5/arch-arm/usr/lib/liblog.so b/ndk/build/platforms/android-1.5/arch-arm/usr/lib/liblog.so
new file mode 100644
index 0000000..92bf1a7
--- /dev/null
+++ b/ndk/build/platforms/android-1.5/arch-arm/usr/lib/liblog.so
Binary files differ
diff --git a/ndk/build/platforms/android-1.5/common/include/android/log.h b/ndk/build/platforms/android-1.5/common/include/android/log.h
new file mode 100644
index 0000000..0ea4c29
--- /dev/null
+++ b/ndk/build/platforms/android-1.5/common/include/android/log.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef _ANDROID_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit) since
+ *   platform release 1.5
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ *   - a priority
+ *   - a log tag
+ *   - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ *  - Sending log messages eats CPU and slow down your application and the
+ *    system.
+ *
+ *  - The circular log buffer is pretty small (<64KB), sending many messages
+ *    might push off other important log messages from the rest of the system.
+ *
+ *  - In release builds, only send log messages to account for exceptional
+ *    conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+    ANDROID_LOG_UNKNOWN = 0,
+    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
+    ANDROID_LOG_VERBOSE,
+    ANDROID_LOG_DEBUG,
+    ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,
+    ANDROID_LOG_ERROR,
+    ANDROID_LOG_FATAL,
+    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char *tag, const char *text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char *tag,
+                         const char *fmt, va_list ap);
+
+/*
+ * Log an assertion failure and SIGTRAP the process to have a chance
+ * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char *cond, const char *tag,
+			  const char *fmt, ...)    
+#if defined(__GNUC__)
+    __attribute__ ((noreturn))
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/ndk/build/tools/build-toolchain.sh b/ndk/build/tools/build-toolchain.sh
index 75de8f9..42f7276 100755
--- a/ndk/build/tools/build-toolchain.sh
+++ b/ndk/build/tools/build-toolchain.sh
@@ -34,6 +34,7 @@
 OPTION_PLATFORM=
 OPTION_FORCE_32=no
 OPTION_REBUILD=no
+
 VERBOSE=no
 for opt do
   optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
@@ -73,9 +74,7 @@
 done
 
 if [ $OPTION_HELP = "yes" ] ; then
-    echo "Collect files from an Android build tree and assembles a sysroot"
-    echo "suitable for building a standalone toolchain or be used by the"
-    echo "Android NDK."
+    echo "Rebuild the prebuilt binaries for the Android NDK toolchain."
     echo ""
     echo "options:"
     echo ""
@@ -90,27 +89,14 @@
     exit 0
 fi
 
-# some Linux platforms report 64-bit while they have a 32-bit userland
-# so check that the compiler generates 32-bit binaries
-# if it does, call force_32bit_binaries to change the value of $HOST_TAG
-#
-# note that this also changes HOST_CFLAGS and HOST_LDFLAGS, however, there
-# is no way for the configure script in the toolchain to get these properly.
-#
-if [ $HOST_TAG = linux-x86_64 ] ; then
-    setup_toolchain
-    cat >$TMPC <<EOF
-int main(void)
-{
-    return 0;
-}
-EOF
-    compile_exec_run
-    readelf -h $TMPE | grep -q -e "ELF32"
-    if [ $? = 0 ] ; then
-        force_32bit_binaries
-    fi
-fi
+# Force generation of 32-bit binaries on 64-bit systems
+case $HOST_TAG in
+    *-x86_64)
+        HOST_CFLAGS="$HOST_CFLAGS -m32"
+        HOST_LDFLAGS="$HOST_LDFLAGS -m32"
+        force_32bit_binaries  # to modify HOST_TAG and others
+        ;;
+esac
 
 TMPLOG=/tmp/android-toolchain-build-$$.log
 rm -rf $TMPLOG
@@ -305,6 +291,7 @@
             echo "ERROR: Invalid MD5 Sum for $PACKAGE_TARBALL"
             echo "    Expected $PKGSUM"
             echo "    Computed $SUM"
+            echo "You might want to use the --force-download option."
             exit 2
         fi
 
@@ -378,7 +365,8 @@
     echo "Configure: toolchain build"
     mkdir -p $TOOLCHAIN_BUILD &&
     cd $TOOLCHAIN_BUILD &&
-    CFLAGS="$HOST_CFLAGS" LDFLAGS="$HOST_LDFLAGS" run \
+    export CFLAGS="$HOST_CFLAGS" &&
+    export LDFLAGS="$HOST_LDFLAGS" && run \
     $TOOLCHAIN_SRC/configure --target=arm-eabi \
                              --disable-nls \
                              --prefix=$TOOLCHAIN_PREFIX \
@@ -394,9 +382,11 @@
 
 # build the toolchain
 if ! timestamp_check toolchain build ; then
-    echo "Building : toolchain (this can take a long time)."
+    echo "Building : toolchain [this can take a long time]."
     cd $TOOLCHAIN_BUILD &&
-    CFLAGS="$HOST_CFLAGS" LDFLAGS="$HOST_LDFLAGS" run make -j$JOBS
+    export CFLAGS="$HOST_CFLAGS" &&
+    export LDFLAGS="$HOST_LDFLAGS" &&
+    run make -j$JOBS
     if [ $? != 0 ] ; then
         echo "Error while building toolchain. See $TMPLOG"
         exit 1
@@ -414,8 +404,12 @@
         echo "Error while installing toolchain. See $TMPLOG"
         exit 1
     fi
+    # don't forget to copy the GPL and LGPL license files
+    cp -f $TOOLCHAIN_SRC/COPYING $TOOLCHAIN_SRC/COPYING.LIB $TOOLCHAIN_PREFIX
+    # remove some unneeded files
     rm -f $TOOLCHAIN_PREFIX/bin/*-gccbug
     rm -rf $TOOLCHAIN_PREFIX/man $TOOLCHAIN_PREFIX/info
+    # strip binaries to reduce final package size
     strip $TOOLCHAIN_PREFIX/bin/*
     strip $TOOLCHAIN_PREFIX/arm-eabi/bin/*
     strip $TOOLCHAIN_PREFIX/libexec/gcc/*/*/cc1
@@ -431,6 +425,7 @@
     mkdir -p $GDBSERVER_BUILD
     cd $GDBSERVER_BUILD &&
     CFLAGS="-g -O2 -static -mandroid -I$ANDROID_SYSROOT/usr/include" \
+    LDFLAGS= \
     CC="$TOOLCHAIN_PREFIX/bin/arm-eabi-gcc" \
     run $TOOLCHAIN_SRC/gdb-6.6/gdb/gdbserver/configure \
     --host=arm-eabi-linux \
diff --git a/ndk/build/tools/make-release.sh b/ndk/build/tools/make-release.sh
index b4ab459..a599124 100755
--- a/ndk/build/tools/make-release.sh
+++ b/ndk/build/tools/make-release.sh
@@ -21,7 +21,7 @@
 PREBUILT_PREFIX=android-ndk-prebuilt-20090323
 
 # the list of supported host development systems
-PREBUILT_SYSTEMS="linux-x86 linux-x86_64 darwin-x86 windows"
+PREBUILT_SYSTEMS="linux-x86 darwin-x86 windows"
 
 
 OPTION_HELP=no
diff --git a/ndk/docs/ANDROID-MK.TXT b/ndk/docs/ANDROID-MK.TXT
index fb6bc8e..9ba5388 100644
--- a/ndk/docs/ANDROID-MK.TXT
+++ b/ndk/docs/ANDROID-MK.TXT
@@ -353,6 +353,17 @@
     i.e. you should still add them to your application's required modules
     in your Application.mk
 
+LOCAL_LDLIBS
+    The list of additional linker flags to be used when building your
+    module. This is useful to pass the name of specific system libraries
+    with the "-l" prefix. For example, the following will tell the linker
+    to generate a module that links to /system/lib/libz.so at load time:
+
+      LOCAL_LDLIBS := -lz
+
+    See docs/STABLE-APIS.TXT for the list of exposed system libraries you
+    can linked against with this NDK release.
+
 LOCAL_ALLOW_UNDEFINED_SYMBOLS
     By default, any undefined reference encountered when trying to build
     a shared library will result in an "undefined symbol" error. This is a
diff --git a/ndk/docs/CHANGES.TXT b/ndk/docs/CHANGES.TXT
new file mode 100644
index 0000000..c65c821
--- /dev/null
+++ b/ndk/docs/CHANGES.TXT
@@ -0,0 +1,4 @@
+Android NDK ChangeLog:
+
+-------------------------------------------------------------------------------
+android-1.5_r1 released.
diff --git a/ndk/docs/HOWTO.TXT b/ndk/docs/HOWTO.TXT
new file mode 100644
index 0000000..d2fa95f
--- /dev/null
+++ b/ndk/docs/HOWTO.TXT
@@ -0,0 +1,98 @@
+Android NDK How-To:
+===================
+
+A collection of tips and tricks for NDK users
+
+
+How to force the display of build commands:
+-------------------------------------------
+
+Do "make APP=<yourapp> V=1" and actual build commands will be
+displayed. This can be used to verify that things are compiled
+as you expect them to, and check for bugs in the NDK build system.
+
+(The V=1 trick comes from the Linux kernel build system)
+
+
+
+How to force a rebuild of all your sources:
+-------------------------------------------
+
+Use GNU Make's "-B" option, as in:
+
+   make APP=<yourapp> -B
+
+
+How to store your native sources in under your project's path:
+--------------------------------------------------------------
+
+It is often more convenient to store your native sources at a
+different location than $NDK_ROOT/sources. For example, you would
+typically place them under the same directory than your other project
+files, to keep everything under source control, etc...
+
+The NDK build system needs to access the sources from $NDK_ROOT/sources
+and your Application.mk from $NDK_ROOT/apps/<name>. This is essentially
+to keep its implementation sane and simple, and to ensure that certain
+features, like automatic dependency management, work reliably.
+
+You can however use simple symlinks to redirect paths to your project's
+storage location. For example:
+
+   $NDK_ROOT/sources/<foo>  ---->  $PROJECT_PATH/jni/<foo>
+   $NDK_ROOT/apps/<foo>     ---->  $PROJECT_PATH/jni/<foo>
+
+Where $PROJECT_PATH/jni/<foo> contains both an Android.mk and an
+Application.mk. Note that things like $(call my-dir) will always evaluate
+to the NDK paths (i.e. $NDK_ROOT/sources/<foo>) at build time.
+
+Windows users: The NDK is only supported on Cygwin, which implements
+symbolic links through the "ln -s" command, as in:
+
+    ln -s  <target>  <link>
+
+
+How to properly add include directories to your module declaration:
+-------------------------------------------------------------------
+
+If you define several modules, it is common to need to include one
+module's header while compiling another one. For example, consider
+the following example:
+
+  $NDK_ROOT/sources/foo/
+    Android.mk
+    foo.h
+    foo.c
+
+  $NDK_ROOT/sources/bar/
+    Android.mk
+    bar.c
+
+Where the 'bar.c' uses '#include <foo.h>'. You will need to add the
+path to the 'foo' module in bar/Android.mk to build it properly.
+
+One is tempted to use the following:
+
+  LOCAL_CPPFLAGS := -I../foo
+
+However this will not work because all compilation happens from the
+root NDK directory (i.e. $NDK_ROOT), and include files must be relative
+to it. The above line really translates to:
+
+  LOCAL_CPPFLAGS := -I$(NDK_ROOT)/../foo
+
+Which adds a non-existing directory to the C include path. The correct
+line is instead:
+
+  LOCAL_CPPFLAGS := -Isources/foo
+
+Or even better:
+
+  LOCAL_CPPFLAGS := $(LOCAL_PATH)/../foo
+
+Which uses a path relative to $(LOCAL_PATH), in the case where you would
+need to move 'foo' and 'bar' to a deeper level in the 'sources' hierarchy.
+
+
+
+
diff --git a/ndk/docs/STABLE-APIS.TXT b/ndk/docs/STABLE-APIS.TXT
new file mode 100644
index 0000000..743d657
--- /dev/null
+++ b/ndk/docs/STABLE-APIS.TXT
@@ -0,0 +1,113 @@
+Android NDK Stable APIs:
+========================
+
+This is the list of stable APIs/ABIs exposed by the Android NDK.
+
+I. Purpose:
+-----------
+
+Each API corresponds to a set of headers files, and a shared library file
+that contains the corresponding implementation, and which must be linked
+against by your native code.
+
+For example, to use system library "Foo", you would include a header
+like <foo.h> in your code, then tell the build system that your native
+module needs to link to /system/lib/libfoo.so at load-time by adding
+the following line to your Android.mk file:
+
+  LOCAL_LDLIBS := -lfoo
+
+Note that the build system automatically links the C library, the Math
+library and the C++ support library to your native code, there is no
+need to list them in a LOCAL_LDLIBS line.
+
+
+
+II. Android 1.5 Stable Native APIs:
+-----------------------------------
+
+All the APIs listed below are available for developing native code that
+runs on Android 1.5 system images and above.
+
+The C Library:
+--------------
+
+The C library headers, as they are defined on Android 1.5 are available
+through their standard names (<stdlib.h>, <stdio.h>, etc...). If one header
+is not there at build time, it's because its implementation is not available
+on a 1.5 system image.
+
+The build system automatically links your native modules to the C library,
+you don't need to add it to LOCAL_LDLIBS.
+
+Note that the Android C library includes support for pthread (<pthread.h>),
+so "LOCAL_LIBS := -lpthread" is not needed. The same is true for real-time
+extensions (-lrt on typical Linux distributions).
+
+
+** VERY IMPORTANT NOTE: ******************************************************
+*
+*  The kernel-specific headers in <linux/...> and <asm/...> are not considered
+*  stable at this point. Avoid including them directly because some of them
+*  are likely to change in future releases of the platform. This is especially
+*  true for anything related to specific hardware definitions.
+*
+******************************************************************************
+
+
+The Math Library:
+-----------------
+
+<math.h> is available, and the math library is automatically linked to your
+native modules at build time, so there is no need to list "-lm" through
+LOCAL_LDLIBS.
+
+
+
+C++ Library:
+------------
+
+An *extremely* minimal C++ support API is available. For Android 1.5, this is
+currently limited to the following headers:
+
+   <cstddef>
+   <new>
+   <utility>
+   <stl_pair.h>
+
+They may not contain all definitions required by the standard. Notably, support
+for C++ exceptions and RTTI is not available with Android 1.5 system images.
+
+The C++ support library (-lstdc++) is automatically linked to your native
+modules too, so there is no need to list it through LOCAL_LDLIBS
+
+
+
+Android-specific Log Support:
+-----------------------------
+
+<android/log.h> contains various definitions that can be used to send log messages
+to the kernel from your native code. Please have a look at its content in
+(build/platforms/android-1.5/common/include/android/log.h), which contain many
+informative comments on how to use it.
+
+You should be able to write helpful wrapper macros for your own usage to
+access this facility.
+
+If you use it, your native module should link to /system/lib/liblog.so with:
+
+  LOCAL_LDLIBS := -llog
+
+
+
+ZLib Compression Library:
+-------------------------
+
+<zlib.h> and <zconf.h> are available and can be used to use the ZLib compression
+library available on Android 1.5 system images. Documentation for it is available
+on the ZLib page: http://www.zlib.net/manual.html
+
+If you use it, your native module should link to /system/lib/libz.so with:
+
+  LOCAL_LDLIBS := -lz
+
diff --git a/ndk/docs/SYSTEM-ISSUES.TXT b/ndk/docs/SYSTEM-ISSUES.TXT
new file mode 100644
index 0000000..17ee4e8
--- /dev/null
+++ b/ndk/docs/SYSTEM-ISSUES.TXT
@@ -0,0 +1,113 @@
+Android System Image Issues
+===========================
+
+This document contains a list of known issues in existing Android
+system images that NDK developers should be aware of.
+
+I. Android 1.5 System Issues:
+-----------------------------
+
+The following issues correspond to the official Android 1.5
+system images:
+
+
+No standard C++ library support:
+--------------------------------
+
+The Android 1.5 system does not use any C++ standard library, and does
+not provide one to applicative native code. Instead, a very limited set
+of headers are provided (see docs/STABLE-APIS.TXT) which correspond to
+the C++ support code used to build the Android platform.
+
+It is possible to hack existing C++ STL implementations to work on top
+of this, but this is not supported yet. We recommend trying with uSTL
+and STLport at this point if you really need this.
+
+
+No support for C++ exceptions and RTTI:
+---------------------------------------
+
+The Android 1.5 system image lacks several features necessary to reliably
+implement C++ exceptions and RTTI. C++ code that depends on these features
+will simply not link or run appropriately on Android 1.5
+
+
+C Library limitations:
+----------------------
+
+The C library doesn't try to implement every feature under the sun.
+Most notably, pthread cancellation is not supported. A detailed overview
+of the C library and its design is available in docs/system/libc/OVERVIEW.TXT
+
+
+No SysV IPCs in C library:
+--------------------------
+
+Unix System V Inter-Process Communication APIs (e.g. semget()) are
+intentionally not available from the C library, to avoid denial-of-service
+issues. See docs/system/libc/SYSV-IPC.TXT for details.
+
+
+C Library bug: getservbyname() returns port number in incorrect order:
+----------------------------------------------------------------------
+
+The Android 1.5 C library function getservbyname() returns the port number
+corresponding to a given network service in incorrect order. The function
+stores its result in a 'struct servent' structure, and the port number in
+its 's_port' field.
+
+The standard mandates that this value is stored in network order (and thus
+should be converted to host order through ntohs()). However, the 1.5
+implementation is buggy and returns the number.
+
+This bug will be fixed in future releases of the platform, and applications
+should not depend on the wrong behaviour in the future. Avoid using this
+function if possible; if this is not possible, try to use a small wrapper
+like the following one:
+
+static struct servent*
+my_getservbyname(const char*  name, const char*  proto)
+{
+    static int       has_bug = -1;
+    struct servent*  ret;
+
+    if (has_bug < 0) {
+        ret = getservbyname("http",NULL);
+        has_bug = (ret == NULL || ret->s_port == 80);
+    }
+
+    ret = getservbyname(name, proto);
+    if (has_bug)
+        ret->s_port = htons(ret->s_port);
+}
+
+(the returned struct servent is thread-local and can be modified by the
+ caller. It will be over-written on the next call to the function though).
+
+
+Dynamic Linker limitations:
+---------------------------
+
+The Android dynamic linker in 1.5 has many important limitations:
+
+- No support for LD_LIBRARY_PATH, LD_PRELOAD, RTLD_LOCAL and many
+  other options.
+
+- Static C++ constructors in executables are called twice due to a bug
+  in the C library initialization sequence. However, static C++
+  constructors in shared libraries are only called once.
+
+- Static destructors are never called at the moment, either at program
+  exit, or when dlclose() is called.
+
+- dlerror() reporting is very limited and only provides a few generic
+  error messages that make it difficult to know why a dynamic load/link
+  operation failed. Most of the time, the culprit is a missing symbol.
+
+- A bug prevents one application shared library from depending on another
+  one. For example, if you build both libfoo.so and libbar.so for your
+  application, and list libfoo.so as a dependency for libbar.so in
+  bar/Android.mk (with LOCAL_SHARED_LIBRARIES := foo), then loading
+  libbar.so will always fail, even if you have already loaded libfoo.so
+  in your process.
+
diff --git a/ndk/docs/system/jni/jni-tips.html b/ndk/docs/system/jni/jni-tips.html
deleted file mode 100644
index e2c3b85..0000000
--- a/ndk/docs/system/jni/jni-tips.html
+++ /dev/null
@@ -1,512 +0,0 @@
-<html>
-  <head>
-    <title>Android JNI Tips</title>
-    <link rel=stylesheet href="android.css">
-  </head>
-
-  <body>
-    <h1><a name="JNI_Tips"></a>Android JNI Tips</h1>
-<p>
-</p><p>
-</p><ul>
-<li> <a href="#What_s_JNI_">What's JNI?</a>
-</li>
-<li> <a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a>
-
-</li>
-<li> <a href="#jclassID_jmethodID_and_jfieldID">jclassID, jmethodID, and jfieldID</a>
-</li>
-<li> <a href="#local_vs_global_references">Local vs. Global References</a>
-</li>
-<li> <a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a>
-</li>
-<li> <a href="#Arrays">Primitive Arrays</a>
-</li>
-<li> <a href="#RegionCalls">Region Calls</a>
-</li>
-<li> <a href="#Exceptions">Exceptions</a>
-</li>
-
-<li> <a href="#Extended_checking">Extended Checking</a>
-</li>
-<li> <a href="#Native_Libraries">Native Libraries</a>
-</li>
-<li> <a href="#64bit">64-bit Considerations</a>
-</li>
-
-<li> <a href="#Unsupported">Unsupported Features</a>
-</ul>
-<p>
-<noautolink>
-</noautolink></p><p>
-</p><h2><a name="What_s_JNI_"> </a> What's JNI? </h2>
-<p>
-
-JNI is the Java Native Interface.  It defines a way for code written in the
-Java programming language to interact with native
-code, e.g. functions written in C/C++.  It's VM-neutral, has support for loading code from
-dynamic shared libraries, and while cumbersome at times is reasonably efficient.
-</p><p>
-You really should read through the
-<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 1.6</a>
-to get a sense for how JNI works and what features are available.  Some
-aspects of the interface aren't immediately obvious on
-first reading, so you may find the next few sections handy.
-The more detailed <i>JNI Programmer's Guide and Specification</i> can be found
-<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.
-</p><p>
-</p><p>
-</p><h2><a name="JavaVM_and_JNIEnv"> </a> JavaVM and JNIEnv </h2>
-<p>
-JNI defines two key data structures, "JavaVM" and "JNIEnv".  Both of these are essentially
-pointers to pointers to function tables.  (In the C++ version, it's a class whose sole member
-is a pointer to a function table.)  The JavaVM provides the "invocation interface" functions,
-which allow you to create and destroy the VM.  In theory you can have multiple VMs per process,
-but Android's VMs only allow one.
-</p><p>
-The JNIEnv provides most of the JNI functions.  Your native functions all receive a JNIEnv as
-the first argument.
-</p><p>
-
-On some VMs, the JNIEnv is used for thread-local storage.  For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
-If a piece of code has no other way to get its JNIEnv, you should share
-the JavaVM, and use JavaVM-&gt;GetEnv to discover the thread's JNIEnv.
-</p><p>
-The C and C++ declarations of JNIEnv and JavaVM are different.  "jni.h" provides different typedefs
-depending on whether it's included into ".c" or ".cpp".  For this reason it's a bad idea to
-include JNIEnv arguments in header files included by both languages.  (Put another way: if your
-header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in
-that header refers to JNIEnv.)
-</p><p>
-</p><p>
-</p><h2><a name="jclassID_jmethodID_and_jfieldID"> jclassID, jmethodID, and jfieldID </a></h2>
-<p>
-If you want to access an object's field from native code, you would do the following:
-</p><p>
-</p><ul>
-<li> Get the class object reference for the class with <code>FindClass</code>
-</li>
-<li> Get the field ID for the field with <code>GetFieldID</code>
-</li>
-<li> Get the contents of the field with something appropriate, e.g.
-<code>GetIntField</code>
-</li>
-</ul>
-<p>
-Similarly, to call a method, you'd first get a class object reference and then a method ID.  The IDs are often just
-pointers to internal VM data structures.  Looking them up may require several string
-comparisons, but once you have them the actual call to get the field or invoke the method
-is very quick.
-</p><p>
-If performance is important, it's useful to look the values up once and cache the results
-in your native code.  Because we are limiting ourselves to one VM per process, it's reasonable
-to store this data in a static local structure.
-</p><p>
-The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded.  Classes
-are only unloaded if all classes associated with a ClassLoader can be garbage collected,
-which is rare but will not be impossible in our system.  The jclassID
-is a class reference and <strong>must be protected</strong> with a call
-to <code>NewGlobalRef</code> (see the next section).
-</p><p>
-If you would like to cache the IDs when a class is loaded, and automatically re-cache them
-if the class is ever unloaded and reloaded, the correct way to initialize
-the IDs is to add a piece of code that looks like this to the appropriate class:
-</p><p>
-
-</p><pre>    /*
-     * We use a class initializer to allow the native code to cache some
-     * field offsets.
-     */
-
-    /*
-     * A native function that looks up and caches interesting
-     * class/field/method IDs for this class.  Returns false on failure.
-     */
-    native private static boolean nativeClassInit();
- 
-    /*
-     * Invoke the native initializer when the class is loaded.
-     */
-    static {
-        if (!nativeClassInit())
-            throw new RuntimeException("native init failed");
-    }
-</pre>
-<p>
-Create a nativeClassInit method in your C/C++ code that performs the ID lookups.  The code
-will be executed once, when the class is initialized.  If the class is ever unloaded and
-then reloaded, it will be executed again.  (See the implementation of java.io.FileDescriptor
-for an example in our source tree.)
-</p><p>
-</p><p>
-</p><p>
-</p><h2><a name="local_vs_global_references"> Local vs. Global References </a></h2>
-<p>
-Every object that JNI returns is a "local reference".  This means that it's valid for the
-duration of the current native method in the current thread.
-<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
-This applies to all sub-classes of jobject, including jclass and jarray.
-(Dalvik VM will warn you about this when -Xcheck:jni is enabled.)
-</p><p>
-
-If you want to hold on to a reference for a longer period, you must use a "global" reference.
-The <code>NewGlobalRef</code> function takes the local reference as
-an argument and returns a global one:
-
-<p><pre>jobject* localRef = [...];
-jobject* globalRef;
-globalRef = env-&gt;NewGlobalRef(localRef);
-</pre>
-
-The global reference is guaranteed to be valid until you call
-<code>DeleteGlobalRef</code>.
-</p><p>
-All JNI methods accept both local and global references as arguments.
-</p><p>
-Programmers are required to "not excessively allocate" local references.  In practical terms this means
-that if you're creating large numbers of local references, perhaps while running through an array of
-Objects, you should free them manually with
-<code>DeleteLocalRef</code> instead of letting JNI do it for you.  The
-VM is only required to reserve slots for
-16 local references, so if you need more than that you should either delete as you go or use
-<code>EnsureLocalCapacity</code> to reserve more.
-</p><p>
-Note: method and field IDs are just 32-bit identifiers, not object
-references, and should not be passed to <code>NewGlobalRef</code>.  The raw data
-pointers returned by functions like <code>GetStringUTFChars</code>
-and <code>GetByteArrayElements</code> are also not objects.
-</p><p>
-One unusual case deserves separate mention.  If you attach a native
-thread to the VM with AttachCurrentThread, the code you are running will
-never "return" to the VM until the thread detaches from the VM.  Any local
-references you create will have to be deleted manually unless the thread
-is about to exit or detach.
-</p><p>
-</p><p>
-</p><p>
-</p><h2><a name="UTF_8_and_UTF_16_strings"> </a> UTF-8 and UTF-16 Strings </h2>
-<p>
-The Java programming language uses UTF-16.  For convenience, JNI provides methods that work with "modified UTF-8" encoding
-as well.  (Some VMs use the modified UTF-8 internally to store strings; ours do not.)  The
-modified encoding only supports the 8- and 16-bit forms, and stores ASCII NUL values in a 16-bit encoding.
-The nice thing about it is that you can count on having C-style zero-terminated strings,
-suitable for use with standard libc string functions.  The down side is that you cannot pass
-arbitrary UTF-8 data into the VM and expect it to work correctly.
-</p><p>
-It's usually best to operate with UTF-16 strings.  With our current VMs, the
-<code>GetStringChars</code> method
-does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion.  Note that
-<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
-so you need to hang on to the string length as well as
-the string pointer.
-
-</p><p>
-<strong>Don't forget to Release the strings you Get</strong>.  The string functions return <code>jchar*</code> or <code>jbyte*</code>, which
-are pointers to primitive types rather than local references.  They are
-guaranteed valid until Release is called, which means they are not
-released when the native method returns.
-</p><p>
-</p><p>
-
-
-</p><h2><a name="Arrays"> </a> Primitive Arrays </h2>
-<p>
-JNI provides functions for accessing the contents of array objects.
-While arrays of objects must be accessed one entry at a time, arrays of
-primitives can be read and written directly as if they were declared in C.
-</p><p>
-To make the interface as efficient as possible without constraining
-the VM implementation,
-the <code>Get&lt;PrimitiveType&gt;ArrayElements</code> family of calls
-allows the VM to either return a pointer to the actual elements, or
-allocate some memory and make a copy.  Either way, the raw pointer returned
-is guaranteed to be valid until the corresponding <code>Release</code> call
-is issued (which implies that, if the data wasn't copied, the array object
-will be pinned down and can't be relocated as part of compacting the heap).
-<strong>You must Release every array you Get.</strong>  Also, if the Get
-call fails, you must ensure that your code doesn't try to Release a NULL
-pointer later.
-</p><p>
-You can determine whether or not the data was copied by passing in a
-non-NULL pointer for the <code>isCopy</code> argument.  This is rarely
-useful.
-</p><p>
-The <code>Release</code> call takes a <code>mode</code> argument that can
-have one of three values.  The actions performed by the VM depend upon
-whether it returned a pointer to the actual data or a copy of it:
-<ul>
-    <li><code>0</code>
-    <ul>
-        <li>Actual: the array object is un-pinned.
-        <li>Copy: data is copied back.  The buffer with the copy is freed.
-    </ul>
-    <li><code>JNI_COMMIT</code>
-    <ul>
-        <li>Actual: does nothing.
-        <li>Copy: data is copied back.  The buffer with the copy
-        <strong>is not freed</strong>.
-    </ul>
-    <li><code>JNI_ABORT</code>
-    <ul>
-        <li>Actual: the array object is un-pinned.  Earlier
-        writes are <strong>not</strong> aborted.
-        <li>Copy: the buffer with the copy is freed; any changes to it are lost.
-    </ul>
-</ul>
-</p><p>
-One reason for checking the <code>isCopy</code> flag is to know if
-you need to call <code>Release</code> with <code>JNI_COMMIT</code>
-after making changes to an array -- if you're alternating between making
-changes and executing code that uses the contents of the array, you may be
-able to
-skip the no-op commit.  Another possible reason for checking the flag is for
-efficient handling of <code>JNI_ABORT</code>.  For example, you might want
-to get an array, modify it in place, pass pieces to other functions, and
-then discard the changes.  If you know that JNI is making a new copy for
-you, there's no need to create another "editable" copy.  If JNI is passing
-you the original, then you do need to make your own copy.
-</p><p>
-Some have asserted that you can skip the <code>Release</code> call if
-<code>*isCopy</code> is false.  This is not the case.  If no copy buffer was
-allocated, then the original memory must be pinned down and can't be moved by
-the garbage collector.
-</p><p>
-Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
-and you will need to call <code>Release</code> again with a different flag
-eventually.
-</p><p>
-</p><p>
-
-
-</p><h2><a name="RegionCalls"> Region Calls </a></h2>
-
-<p>
-There is an alternative to calls like <code>Get&lt;Type&gt;ArrayElements</code>
-and <code>GetStringChars</code> that may be very helpful when all you want
-to do is copy data in or out.  Consider the following:
-<pre>
-    jbyte* data = env->GetByteArrayElements(array, NULL);
-    if (data != NULL) {
-        memcpy(buffer, data, len);
-        env->ReleaseByteArrayElements(array, data, JNI_ABORT);
-    }
-</pre>
-<p>
-This grabs the array, copies the first <code>len</code> byte
-elements out of it, and then releases the array.  Depending upon the VM
-policies the <code>Get</code> call will either pin or copy the array contents.
-We copy the data (for perhaps a second time), then call Release; in this case
-we use <code>JNI_ABORT</code> so there's no chance of a third copy.
-</p><p>
-We can accomplish the same thing with this:
-<pre>
-    env->GetByteArrayRegion(array, 0, len, buffer);
-</pre>
-</p><p>
-This accomplishes the same thing, with several advantages:
-<ul>
-    <li>Requires one JNI call instead of 3, reducing overhead.
-    <li>Doesn't require pinning or extra data copies.
-    <li>Reduces the risk of programmer error -- no need to match up
-    <code>Get</code> and <code>Release</code> calls.
-</ul>
-</p><p>
-Similarly, you can use the <code>Set&lt;Type&gt;ArrayRegion</code> call
-to copy data into an array, and <code>GetStringRegion</code> or
-<code>GetStringUTFRegion</code> to copy characters out of a
-<code>String</code>.
-
-
-</p><h2><a name="Exceptions"> Exceptions </a></h2>
-<p>
-<strong>You may not call most JNI functions while an exception is pending.</strong>
-Your code is expected to notice the exception (via the function's return value,
-<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
-or clear the exception and handle it.
-</p><p>
-The only JNI functions that you are allowed to call while an exception is
-pending are:
-<font size="-1"><ul>
-    <li>DeleteGlobalRef
-    <li>DeleteLocalRef
-    <li>DeleteWeakGlobalRef
-    <li>ExceptionCheck
-    <li>ExceptionClear
-    <li>ExceptionDescribe
-    <li>ExceptionOccurred
-    <li>MonitorExit
-    <li>PopLocalFrame
-    <li>PushLocalFrame
-    <li>Release<PrimitiveType>ArrayElements
-    <li>ReleasePrimitiveArrayCritical
-    <li>ReleaseStringChars
-    <li>ReleaseStringCritical
-    <li>ReleaseStringUTFChars
-</ul></font>
-</p><p>
-Note that exceptions thrown by interpreted code do not "leap over" native code,
-and C++ exceptions thrown by native code are not handled by Dalvik.
-The JNI <code>Throw</code> and <code>ThrowNew</code> instructions just
-set an exception pointer in the current thread.  Upon returning to the VM from
-native code, the exception will be noted and handled appropriately.
-</p><p>
-Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
-<code>ExceptionOccurred</code>, and clear it with
-<code>ExceptionClear</code>.  As usual,
-discarding exceptions without handling them can lead to problems.
-</p><p>
-There are no built-in functions for manipulating the Throwable object
-itself, so if you want to (say) get the exception string you will need to
-find the Throwable class, look up the method ID for
-<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
-is non-NULL use <code>GetStringUTFChars</code> to get something you can
-hand to printf or a LOG macro.
-
-</p><p>
-</p><p>
-</p><h2><a name="Extended_checking"> Extended Checking </a></h2>
-<p>
-JNI does very little error checking.  Calling <code>SetFieldInt</code>
-on an Object field will succeed, even if the field is marked
-<code>private</code> and <code>final</code>.  The
-goal is to minimize the overhead on the assumption that, if you've written it in native code,
-you probably did it for performance reasons.
-</p><p>
-Some VMs support extended checking with the "<code>-Xcheck:jni</code>" flag.  If the flag is set, the VM
-puts a different table of functions into the JavaVM and JNIEnv pointers.  These functions do
-an extended series of checks before calling the standard implementation.
-
-</p><p>
-Some things that may be verified:
-</p><p>
-</p>
-<ul>
-<li> Check for null pointers where not allowed.
-<li>
-<li> Verify argument type correctness (jclass is a class object,
-jfieldID points to field data, jstring is a java.lang.String).
-</li>
-<li> Field type correctness, e.g. don't store a HashMap in a String field.
-</li>
-<li> Check to see if an exception is pending on calls where pending exceptions are not legal.
-</li>
-<li> Check for calls to inappropriate functions between Critical get/release calls.
-</li>
-<li> Check that JNIEnv structs aren't being shared between threads.
-
-</li>
-<li> Make sure local references aren't used outside their allowed lifespan.
-</li>
-<li> UTF-8 strings contain valid "modified UTF-8" data.
-</li>
-</ul>
-<p>Accessibility of methods and fields (i.e. public vs. private) is not
-checked.
-<p>
-The Dalvik VM supports the <code>-Xcheck:jni</code> flag.  For a
-description of how to enable it for Android apps, see
-<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
-It's currently enabled by default in the Android emulator and on
-"engineering" device builds.
-
-</p><p>
-JNI checks can be modified with the <code>-Xjniopts</code> command-line
-flag.  Currently supported values include:
-</p>
-<blockquote><dl>
-<dt>forcecopy
-<dd>When set, any function that can return a copy of the original data
-(array of primitive values, UTF-16 chars) will always do so.  The buffers
-are over-allocated and surrounded with a guard pattern to help identify
-code writing outside the buffer, and the contents are erased before the
-storage is freed to trip up code that uses the data after calling Release.
-<dt>warnonly
-<dd>By default, JNI "warnings" cause the VM to abort.  With this flag
-it continues on.
-</dl></blockquote>
-
-
-</p><p>
-</p><h2><a name="Native_Libraries"> Native Libraries </a></h2>
-<p>
-You can load native code from shared libraries with the standard
-<code>System.loadLibrary()</code> call.  The
-preferred way to get at your native code is:
-</p><p>
-</p><ul>
-<li> Call <code>System.loadLibrary()</code> from a static class initializer.  (See the earlier example, where one is used to call nativeClassInit().)  The argument is the "undecorated" library name, e.g. to load "libfubar.so" you would pass in "fubar".
-
-</li>
-<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code>
-</li>
-<li>In <code>JNI_OnLoad</code>, register all of your native methods.  You
-should declare
-the methods "static" so the names don't take up space in the symbol table
-on the device.
-</li>
-</ul>
-<p>
-The <code>JNI_OnLoad</code> function should look something like this if
-written in C:
-</p><blockquote><pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
-    JNIEnv* env;
-    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
-        return -1;
-
-    /* get class with (*env)->FindClass */
-    /* register methods with (*env)->RegisterNatives */
-
-    return JNI_VERSION_1_4;
-}
-</pre></blockquote>
-</p><p>
-You can also call <code>System.load()</code> with the full path name of the
-shared library.  For Android apps, you can get the full path to the
-application's private data storage area from the context object.
-</p><p>
-Dalvik does support "discovery" of native methods that are named in a
-specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
-    the JNI spec</a> for details), but this is a less desirable
-approach.  It requires more space in the shared object symbol table,
-loading is slower because it requires string searches through all of the
-loaded shared libraries, and if a method signature is wrong you won't know
-about it until the first time the method is actually used.
-</p><p>
-
-
-</p><h2><a name="64bit"> 64-bit Considerations </a></h2>
-
-<p>
-Android is currently expected to run on 32-bit platforms.  In theory it
-could be built for a 64-bit system, but that is not a goal at this time.
-For the most part this isn't something that you will need to worry about
-when interacting with native code,
-but it becomes significant if you plan to store pointers to native
-structures in integer fields in an object.  To support architectures
-that use 64-bit pointers, <strong>you need to stash your native pointers in a
-<code>long</code> field rather than an <code>int</code></strong>.
-
-
-</p><h2><a name="Unsupported"> Unsupported Features </a></h2>
-<p>All JNI 1.6 features are supported, with the following exceptions:
-<ul>
-    <li><code>DefineClass</code> is not implemented.  Dalvik does not use
-    Java bytecodes or class files, so passing in binary class data
-    doesn't work.  Translation facilities may be added in a future
-    version of the VM.</li>
-    <li><code>NewWeakGlobalRef</code> and <code>DeleteWeakGlobalRef</code>
-    are not implemented.  The
-    VM supports weak references, but not JNI "weak global" references.
-    These will be supported in a future release.</li>
-    <li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
-    functional -- it can't always tell the difference between "local" and
-    "global" references.</li>
-</ul>
-
-</p>
-
-<address>Copyright &copy; 2008 The Android Open Source Project</address>
-
-  </body>
-</html>
diff --git a/ndk/out/.gitignore b/ndk/out/.gitignore
new file mode 100644
index 0000000..ef2875b
--- /dev/null
+++ b/ndk/out/.gitignore
@@ -0,0 +1,2 @@
+# Ignore all generated files here
+*
diff --git a/pdk/docs/guide/build_cookbook.jd b/pdk/docs/guide/build_cookbook.jd
index ad5f8b8..b9d9f7c 100755
--- a/pdk/docs/guide/build_cookbook.jd
+++ b/pdk/docs/guide/build_cookbook.jd
@@ -10,7 +10,6 @@
     <a href="http://wiki.corp.google.com/twiki/bin/view/Main/AndroidBuildCookbook#Building_a_APK_that_should_be_si">Building a APK that should be signed with a specific vendor key</a><br>
     <a href="http://wiki.corp.google.com/twiki/bin/view/Main/AndroidBuildCookbook#Adding_a_prebuilt_APK">Adding a prebuilt APK</a><br>
     <a href="http://wiki.corp.google.com/twiki/bin/view/Main/AndroidBuildCookbook#Adding_a_Static_Java_Library">Adding a Static Java Library</a><br>
-    <a href="http://wiki.corp.google.com/twiki/bin/view/Main/AndroidBuildCookbook#LOCAL_MODULE_TAGS">Using LOCAL_MODULE_TAGS</a><br>
   </div>
   
 <p>The Android Build Cookbook offers code snippets to help you quickly implement some common build tasks. For additional instruction, please see the other build documents in this section.</p>  
@@ -107,12 +106,443 @@
   # Build a static jar file.
   include $(BUILD_STATIC_JAVA_LIBRARY)
 </pre>
-<h2><a name="Random_other_build_tidbits" id="Random_other_build_tidbits"></a>Random other build tidbits</h2>
-<h3><a name="LOCAL_MODULE_TAGS" id="LOCAL_MODULE_TAGS"></a>LOCAL_MODULE_TAGS</h3>
-<p>This variable controls what build flavors the package gets included in. For example:</p>
-<ul type="disc">
-  <li>user - means include this in user/userdebug builds</li>
-  <li>eng - means include this in eng builds</li>
-  <li>tests - means the target is a testing target and makes it available for tests</li>
-  <li>optional - don't include this</li>
+<h2><a name="Android_mk_variables" id="Android_mk_variables"></a>Android.mk Variables</h2>
+
+<p>These are the variables that you'll commonly see in Android.mk files, listed
+alphabetically. First, a note on the variable naming: </p> 
+
+<ul> 
+    <li><b>LOCAL_</b> - These variables are set per-module.  They are cleared
+    by the <code>include $(CLEAR_VARS)</code> line, so you can rely on them
+    being empty after including that file.  Most of the variables you'll use
+    in most modules are LOCAL_ variables.</li> 
+    <li><b>PRIVATE_</b> - These variables are make-target-specific variables.  That
+    means they're only usable within the commands for that module.  It also
+    means that they're unlikely to change behind your back from modules that
+    are included after yours.  This 
+    <a href="http://www.gnu.org/software/make/manual/make.html#Target_002dspecific">link to the make documentation</a> 
+    describes more about target-specific variables.
+    </li> 
+    <li><b>HOST_</b> and <b>TARGET_</b> - These contain the directories
+    and definitions that are specific to either the host or the target builds.
+    Do not set variables that start with HOST_ or TARGET_ in your makefiles.
+    </li> 
+    <li><b>BUILD_</b> and <b>CLEAR_VARS</b> - These contain the names of
+    well-defined template makefiles to include.  Some examples are CLEAR_VARS
+    and BUILD_HOST_PACKAGE.</li> 
+    <li>Any other name is fair-game for you to use in your Android.mk.  However,
+    remember that this is a non-recursive build system, so it is possible that
+    your variable will be changed by another Android.mk included later, and be
+    different when the commands for your rule / module are executed.</li> 
 </ul>
+
+<table border=1 cellpadding=2 cellspacing=0>
+ <tbody><tr>
+  <th scope="col">Parameter</th>
+  <th scope="col">Description</th>
+ </tr>
+<tr>
+<td valign="top">LOCAL_AAPT_FLAGS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ACP_UNAVAILABLE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ADDITIONAL_JAVA_DIR</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_AIDL_INCLUDES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ALLOW_UNDEFINED_SYMBOLS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ARM_MODE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ASFLAGS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ASSET_DIR</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_ASSET_FILES</td>
+<td valign="top">In Android.mk files that <code>include $(BUILD_PACKAGE)</code> set this
+to the set of files you want built into your app.  Usually:</p> 
+<p><code>LOCAL_ASSET_FILES += $(call find-subdir-assets)</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_BUILT_MODULE_STEM</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_C_INCLUDES</td>
+<td valign="top"><p>Additional directories to instruct the C/C++ compilers to look for header
+files in.  These paths are rooted at the top of the tree.  Use
+<code>LOCAL_PATH</code> if you have subdirectories of your own that you
+want in the include paths.  For example:</p> 
+<p><code> 
+LOCAL_C_INCLUDES += extlibs/zlib-1.2.3<br/> 
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+</code></p> 
+<p>You should not add subdirectories of include to
+<code>LOCAL_C_INCLUDES</code>, instead you should reference those files
+in the <code>#include</code> statement with their subdirectories.  For
+example:</p> 
+<p><code>#include &lt;utils/KeyedVector.h&gt;</code><br/> 
+not <code><s>#include &lt;KeyedVector.h&gt;</s></code></p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CC</td>
+<td valign="top">If you want to use a different C compiler for this module, set LOCAL_CC
+to the path to the compiler.  If LOCAL_CC is blank, the appropriate default
+compiler is used.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CERTIFICATE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CFLAGS</td>
+<td valign="top">If you have additional flags to pass into the C or C++ compiler, add
+them here.  For example:</p> 
+<p><code>LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CLASSPATH</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_COMPRESS_MODULE_SYMBOLS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_COPY_HEADERS</td>
+<td valign="top"><p>The set of files to copy to the install include tree.  You must also
+supply <code>LOCAL_COPY_HEADERS_TO</code>.</p> 
+<p>This is going away because copying headers messes up the error messages, and
+may lead to people editing those headers instead of the correct ones.  It also
+makes it easier to do bad layering in the system, which we want to avoid.  We
+also aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any
+headers.</p></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_COPY_HEADERS_TO</td>
+<td valign="top"><p>The directory within "include" to copy the headers listed in
+<code>LOCAL_COPY_HEADERS</code> to.</p> 
+<p>This is going away because copying headers messes up the error messages, and
+may lead to people editing those headers instead of the correct ones.  It also
+makes it easier to do bad layering in the system, which we want to avoid.  We
+also aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any
+headers.</p></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CPP_EXTENSION</td>
+<td valign="top">If your C++ files end in something other than "<code>.cpp</code>",
+you can specify the custom extension here.  For example:
+<p><code>LOCAL_CPP_EXTENSION := .cc</code></p> 
+Note that all C++ files for a given module must have the same
+extension; it is not currently possible to mix different extensions.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CPPFLAGS</td>
+<td valign="top">If you have additional flags to pass into <i>only</i> the C++ compiler, add
+them here.  For example:</p> 
+<p><code>LOCAL_CPPFLAGS += -ffriend-injection</code></p> 
+<code>LOCAL_CPPFLAGS</code> is guaranteed to be after <code>LOCAL_CFLAGS</code> 
+on the compile line, so you can use it to override flags listed in
+<code>LOCAL_CFLAGS</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_CXX</td>
+<td valign="top">If you want to use a different C++ compiler for this module, set LOCAL_CXX
+to the path to the compiler.  If LOCAL_CXX is blank, the appropriate default
+compiler is used.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_DX_FLAGS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_EXPORT_PACKAGE_RESOURCES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_FORCE_STATIC_EXECUTABLE</td>
+<td valign="top"><p>If your executable should be linked statically, set 
+<code>LOCAL_FORCE_STATIC_EXECUTABLE:=true</code>.  There is a very short
+list of libraries that we have in static form (currently only libc).  This is
+really only used for executables in /sbin on the root filesystem.</p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_GENERATED_SOURCES</td>
+<td valign="top"><p>Files that you add to <code>LOCAL_GENERATED_SOURCES</code> will be
+automatically generated and then linked in when your module is built.
+See the <a href="#custom-tools">Custom Tools</a> template makefile for an
+example.</p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_INSTRUMENTATION_FOR</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_INSTRUMENTATION_FOR_PACKAGE_NAME</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_INTERMEDIATE_SOURCES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_INTERMEDIATE_TARGETS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_IS_HOST_MODULE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JAR_MANIFEST</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JARJAR_RULES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JAVA_LIBRARIES</td>
+<td valign="top"><p>When linking Java apps and libraries, <code>LOCAL_JAVA_LIBRARIES</code> 
+specifies which sets of java classes to include.  Currently there are
+two of these: <code>core</code> and <code>framework</code>.
+In most cases, it will look like this:</p> 
+<p><code>LOCAL_JAVA_LIBRARIES := core framework</code></p> 
+<p>Note that setting <code>LOCAL_JAVA_LIBRARIES</code> is not necessary
+(and is not allowed) when building an APK with
+"<code>include $(BUILD_PACKAGE)</code>".  The appropriate libraries
+will be included automatically.</p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JAVA_RESOURCE_DIRS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JAVA_RESOURCE_FILES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_JNI_SHARED_LIBRARIES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_LDFLAGS</td>
+<td valign="top"><p>You can pass additional flags to the linker by setting
+<code>LOCAL_LDFLAGS</code>.  Keep in mind that the order of parameters is
+very important to ld, so test whatever you do on all platforms.</p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_LDLIBS</td>
+<td valign="top"><p><code>LOCAL_LDLIBS</code> allows you to specify additional libraries
+that are not part of the build for your executable or library.  Specify
+the libraries you want in -lxxx format; they're passed directly to the 
+link line.  However, keep in mind that there will be no dependency generated
+for these libraries.  It's most useful in simulator builds where you want
+to use a library preinstalled on the host.  The linker (ld) is a particularly
+fussy beast, so it's sometimes necessary to pass other flags here if you're
+doing something sneaky. Some examples:</p> 
+<p><code>LOCAL_LDLIBS += -lcurses -lpthread<br/> 
+LOCAL_LDLIBS += -Wl,-z,origin
+</code></p> </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_MODULE</td>
+<td valign="top"><code>LOCAL_MODULE</code> is the name of what's supposed to be generated
+from your Android.mk.  For exmample, for libkjs, the <code>LOCAL_MODULE</code> 
+is "libkjs" (the build system adds the appropriate suffix -- .so .dylib .dll).
+For app modules, use <code>LOCAL_PACKAGE_NAME</code> instead of 
+<code>LOCAL_MODULE</code>. </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_MODULE_PATH</td>
+<td valign="top">Instructs the build system to put the module somewhere other than what's
+normal for its type.  If you override this, make sure you also set
+<code>LOCAL_UNSTRIPPED_PATH</code> if it's an executable or a shared library
+so the unstripped binary has somewhere to go.  An error will occur if you forget
+to.</p> 
+<p>See <a href="#moving-modules">Putting modules elsewhere</a> for more.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_MODULE_STEM</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_MODULE_TAGS</td>
+<td valign="top"><p>Set <code>LOCAL_MODULE_TAGS</code> to any number of whitespace-separated
+tags.  <p>This variable controls what build flavors the package gets included in. For example:</p>
+<ul type="disc">
+  <li><code>user</code>: include this in user/userdebug builds</li>
+  <li><code>eng</code>: include this in eng builds</li>
+  <li><code>tests</code>: the target is a testing target and makes it available for tests</li>
+  <li><code>optional</code>: don't include this</li>
+</ul></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_NO_DEFAULT_COMPILER_FLAGS</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_NO_EMMA_COMPILE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_NO_EMMA_INSTRUMENT</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_NO_STANDARD_LIBRARIES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_OVERRIDES_PACKAGES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PACKAGE_NAME</td>
+<td valign="top"><code>LOCAL_PACKAGE_NAME</code> is the name of an app.  For example,
+Dialer, Contacts, etc. </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_POST_PROCESS_COMMAND</td>
+<td valign="top"><p>For host executables, you can specify a command to run on the module
+after it's been linked.  You might have to go through some contortions
+to get variables right because of early or late variable evaluation:</p> 
+<p><code>module := $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)<br/> 
+LOCAL_POST_PROCESS_COMMAND := /Developer/Tools/Rez -d __DARWIN__ -t APPL\<br/> 
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-d __WXMAC__ -o $(module) Carbon.r
+</code></p> 
+ </td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PREBUILT_EXECUTABLES</td>
+<td valign="top">When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to
+executables that you want copied.  They're located automatically into the
+right bin directory.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PREBUILT_JAVA_LIBRARIES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PREBUILT_LIBS</td>
+<td valign="top">When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to
+libraries that you want copied.  They're located automatically into the
+right lib directory.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PREBUILT_OBJ_FILES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_PRELINK_MODULE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_REQUIRED_MODULES</td>
+<td valign="top"><p>Set <code>LOCAL_REQUIRED_MODULES</code> to any number of whitespace-separated
+module names, like "libblah" or "Email".  If this module is installed, all
+of the modules that it requires will be installed as well.  This can be
+used to, e.g., ensure that necessary shared libraries or providers are
+installed when a given app is installed.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_RESOURCE_DIR</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_SDK_VERSION</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_SHARED_LIBRARIES</td>
+<td valign="top">These are the libraries you directly link against.  You don't need to
+pass transitively included libraries.  Specify the name without the suffix:</p> 
+<p><code>LOCAL_SHARED_LIBRARIES := \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libutils \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libui \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libaudio \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libexpat \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libsgl
+</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_SRC_FILES</td>
+<td valign="top">The build system looks at <code>LOCAL_SRC_FILES</code> to know what source
+files to compile -- .cpp .c .y .l .java.  For lex and yacc files, it knows
+how to correctly do the intermediate .h and .c/.cpp files automatically.  If
+the files are in a subdirectory of the one containing the Android.mk, prefix
+them with the directory name:</p> 
+<p><code>LOCAL_SRC_FILES := \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;file1.cpp \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;dir/file2.cpp
+</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_STATIC_JAVA_LIBRARIES</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_STATIC_LIBRARIES</td>
+<td valign="top">These are the static libraries that you want to include in your module.
+Mostly, we use shared libraries, but there are a couple of places, like
+executables in sbin and host executables where we use static libraries instead.
+<p><code>LOCAL_STATIC_LIBRARIES := \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libutils \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libtinyxml
+</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_UNINSTALLABLE_MODULE</td>
+<td valign="top"></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_UNSTRIPPED_PATH</td>
+<td valign="top">Instructs the build system to put the unstripped version of the module
+somewhere other than what's normal for its type.  Usually, you override this
+because you overrode <code>LOCAL_MODULE_PATH</code> for an executable or a
+shared library.  If you overrode <code>LOCAL_MODULE_PATH</code>, but not 
+<code>LOCAL_UNSTRIPPED_PATH</code>, an error will occur.</p> 
+<p>See <a href="#moving-modules">Putting modules elsewhere</a> for more.</td>
+</tr>
+<tr>
+<td valign="top">LOCAL_WHOLE_STATIC_LIBRARIES</td>
+<td valign="top">These are the static libraries that you want to include in your module without allowing
+the linker to remove dead code from them. This is mostly useful if you want to add a static library
+to a shared library and have the static library's content exposed from the shared library.
+<p><code>LOCAL_WHOLE_STATIC_LIBRARIES := \<br/> 
+	&nbsp;&nbsp;&nbsp;&nbsp;libsqlite3_android<br/> 
+</code></td>
+</tr>
+<tr>
+<td valign="top">LOCAL_YACCFLAGS</td>
+<td valign="top">Any flags to pass to invocations of yacc for your module.  A known limitation
+here is that the flags will be the same for all invocations of YACC for your
+module.  This can be fixed.  If you ever need it to be, just ask.</p> 
+<p><code>LOCAL_YACCFLAGS := -p kjsyy</code></td>
+</tr>
+<tr>
+<td valign="top">OVERRIDE_BUILT_MODULE_PATH</td>
+<td valign="top"></td>
+</tr>
+
+</table>
\ No newline at end of file
diff --git a/pdk/docs/guide/build_new_device.jd b/pdk/docs/guide/build_new_device.jd
index e320ff3..d914ebe 100755
--- a/pdk/docs/guide/build_new_device.jd
+++ b/pdk/docs/guide/build_new_device.jd
@@ -126,3 +126,191 @@
   </ul>
 </ul>
 </p>
+
+<a name="androidBuildSystemProductDefFiles"></a><h2>Product Definition Files</h2>
+
+<p>Product-specific variables are defined in product definition files. A product definition file can inherit from other product definition files, thus reducing the need to copy and simplifying maintenance.</p>
+<p>Variables maintained in a product definition files include:</p>
+<p>
+<table border=1 cellpadding=2 cellspacing=0>
+ <tbody><tr>
+  <th scope="col">Parameter</th>
+  <th scope="col">Description</th>
+  <th scope="col">Example</th>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_NAME</td>
+   <td valign="top">End-user-visible name for the overall product. Appears in the "About the phone" info.</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_MODEL</td>
+   <td valign="top">End-user-visible name for the end product</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_LOCALES</td>
+   <td valign="top">A space-separated list of two-letter language code, two-letter country code pairs that describe several settings for the user, such as the UI language and time, date and currency formatting. The first locale listed in PRODUCT_LOCALES is is used if the locale has never been set before.</td>
+   <td valign="top"><code>en_GB de_DE es_ES fr_CA</code></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_PACKAGES</td>
+   <td valign="top">Lists the APKs to install.</td>
+   <td valign="top"><code>Calendar Contacts</code></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_DEVICE</td>
+   <td valign="top">Name of the industrial design</td>
+   <td valign="top"><code>dream</code></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_MANUFACTURER</td>
+   <td valign="top">Name of the manufacturer</td>
+   <td valign="top"><code>acme</code></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_BRAND</td>
+   <td valign="top">The brand (e.g., carrier) the software is customized for, if any</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_PROPERTY_OVERRIDES</td>
+   <td valign="top">List of property assignments in the format "key=value"</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_COPY_FILES</td>
+   <td valign="top">List of words like <code>source_path:destination_path</code>. The file at the source path should be copied to the destination path when building this product. The rules for the copy steps are defined in config/Makefile</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_OTA_PUBLIC_KEYS</td>
+   <td valign="top">List of OTA public keys for the product</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_POLICY</td>
+   <td valign="top">Indicate which policy this product should use</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_PACKAGE_OVERLAYS</td>
+   <td valign="top">Indicate whether to use default resources or add any product specific overlays</td>
+   <td valign="top"><code>vendor/acme/overlay</code></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_CONTRIBUTORS_FILE</td>
+   <td valign="top">HTML file containing the contributors to the project.</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_TAGS</td>
+   <td valign="top">list of space-separated words for a given product</td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_SDK_ADDON_NAME</td>
+   <td valign="top"></td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_SDK_ADDON_COPY_FILES</td>
+   <td valign="top"></td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_SDK_ADDON_COPY_MODULES</td>
+   <td valign="top"></td>
+   <td valign="top"></td>
+ </tr>
+ <tr>
+   <td valign="top">PRODUCT_SDK_ADDON_DOC_MODULE</td>
+   <td valign="top"></td>
+   <td valign="top"></td>
+ </tr>
+</table>
+
+</P>
+<p>The snippet below illustrates a typical product definition file.</p>
+<pre class="prettyprint">
+$(call inherit-product, build/target/product/generic.mk)
+
+#Overrides
+PRODUCT_NAME := MyDevice
+PRODUCT_MANUFACTURER := acme
+PRODUCT_BRAND := acme_us
+PRODUCT_LOCALES := en_GB es_ES fr_FR
+PRODUCT_PACKAGE_OVERLAYS := vendor/acme/overlay
+
+</pre>
+
+<a name="androidBuildVariants"></a><h2>Build Variants</h2>
+
+<p> 
+When building for a particular product, it's often useful to have minor
+variations on what is ultimately the final release build.  These are the
+currently-defined build variants:
+</p> 
+ 
+<table border=1> 
+<tr> 
+    <td> 
+        <code>eng<code> 
+    </td> 
+    <td> 
+        This is the default flavor. A plain "<code>make</code>" is the
+        same as "<code>make eng</code>".  <code>droid</code> is an alias
+        for <code>eng</code>.
+        <ul> 
+        <li>Installs modules tagged with: <code>eng</code>, <code>debug</code>,
+            <code>user</code>, and/or <code>development</code>.
+        <li>Installs non-APK modules that have no tags specified.
+        <li>Installs APKs according to the product definition files, in
+            addition to tagged APKs.
+        <li><code>ro.secure=0</code> 
+        <li><code>ro.debuggable=1</code> 
+        <li><code>ro.kernel.android.checkjni=1</code> 
+        <li><code>adb</code> is enabled by default.
+    </td> 
+</tr> 
+<tr> 
+    <td> 
+        <code>user<code> 
+    </td> 
+    <td> 
+        "<code>make user</code>"
+        <p> 
+        This is the flavor intended to be the final release bits.
+        <ul> 
+        <li>Installs modules tagged with <code>user</code>.
+        <li>Installs non-APK modules that have no tags specified.
+        <li>Installs APKs according to the product definition files; tags
+            are ignored for APK modules.
+        <li><code>ro.secure=1</code> 
+        <li><code>ro.debuggable=0</code> 
+        <li><code>adb</code> is disabled by default.
+    </td> 
+</tr> 
+<tr> 
+    <td> 
+        <code>userdebug<code> 
+    </td> 
+    <td> 
+        "<code>make userdebug</code>"
+        <p> 
+        The same as <code>user</code>, except:
+        <ul> 
+        <li>Also installs modules tagged with <code>debug</code>.
+        <li><code>ro.debuggable=1</code> 
+        <li><code>adb</code> is enabled by default.
+    </td> 
+</tr> 
+</table> 
+ 
+<p> 
+If you build one flavor and then want to build another, you should run
+"<code>make installclean</code>" between the two makes to guarantee that
+you don't pick up files installed by the previous flavor.  "<code>make
+clean</code>" will also suffice, but it takes a lot longer.
+</p> 
+ 
\ No newline at end of file
diff --git a/pdk/docs/guide/build_system.jd b/pdk/docs/guide/build_system.jd
index 158a0a7..36936aa 100755
--- a/pdk/docs/guide/build_system.jd
+++ b/pdk/docs/guide/build_system.jd
@@ -10,7 +10,6 @@
 
 <a href="#androidBuildSystemOverview">Understanding the makefile</a><br/>
 <a href="#androidBuildSystemLayers">Layers</a><br/>
-<a href="#androidBuildSystemProductDefFiles">Product Definition Files</a><br/></div>
 <a href="#androidSourceSetupBuildingCodeBase">Building the Android Platform</a><br/><div style="padding-left:40px">
 
 <a href="#androidSourceSetupBuildingDeviceCodeBase">Device Code</a><br/>
@@ -31,8 +30,6 @@
 
 <a name="androidBuildSystemUnderstanding"></a><h2>Understanding Android's Build System</h2>
 
-
-
 <a name="androidBuildSystemOverview"></a><h3>Understanding the makefile</h3>
 
 <p>A makefile defines how to build a particular application. Makefiles typically include all of the following elements:</p>
@@ -95,38 +92,6 @@
   </tr>
 </table>
 
-
-<a name="androidBuildSystemProductDefFiles"></a><h3>Product Definition Files</h3>
-
-<p>Product-specific variables are defined in product definition files. A product definition file can inherit from other product definition files, thus reducing the need to copy and simplifying maintenance.</p>
-<p>Variables maintained in a product definition files include:</p>
-<p><ul>
-<li><code>PRODUCT_DEVICE</code></LI>
-<LI><code>LOCALES</code></LI>
-<LI><code>BRANDING_PARTNER</code></LI>
-<LI><code>PROPERTY_OVERRIDES</code></LI>
-</UL>
-</P>
-<p>The snippet below illustrates a typical product definition file.</p>
-<PRE class="prettyprint">
-//device/target/product/core.mk
-PRODUCT_PACKAGES := Home SettingsProvider ...
-//device/target/product/generic.mk
-PRODUCT_PACKAGES := Calendar Camera SyncProvider ...
-$(call inherit-product, target/product/core.mk)
-PRODUCT_NAME := generic
-//device/partner/google/products/core.mk
-PRODUCT_PACKAGES := Maps GoogleAppsProvider ...
-$(call inherit-product, target/product/core.mk)
-//device/partner/google/products/generic.mk
-PRODUCT_PACKAGES := Gmail GmailProvider ...
-$(call inherit-product, partner/google/products/core.mk)
-$(call inherit-product, target/product/generic.mk)
-PRODUCT_NAME := google_generic
-
-</pre>
-
-
 <a name="androidSourceSetupBuildingCodeBase"></a><h2>Building the Android Platform</h2>
 
 <p>This section describes how to build the default version of Android. Once you are comfortable with a generic build, then you can begin to modify Android for your own target device.</p>
diff --git a/pdk/docs/guide/dalvik.jd b/pdk/docs/guide/dalvik.jd
new file mode 100755
index 0000000..30c92ef
--- /dev/null
+++ b/pdk/docs/guide/dalvik.jd
@@ -0,0 +1,351 @@
+page.title=Dalvik
+pdk.version=1.0
+@jd:body
+ 
+<a name="toc"/> 
+<div style="padding:10px"> 
+<a href="#androidDalvikIntroduction">Introduction</a><br/> 
+<a href="#dalvikCoreLibraries">Core Libraries</a><br/>
+<a href="#dalvikJNICallBridge">JNI Call Bridge</a><br/>
+<a href="#dalvikInterpreter">Interpreter</a><br/></div></font>
+ 
+<a name="androidDalvikIntroduction"></a><h2>Introduction</h2> 
+ 
+<p> 
+The Dalvik virtual machine is intended to run on a variety of platforms.
+The baseline system is expected to be a variant of UNIX (Linux, BSD, Mac
+OS X) running the GNU C compiler.  Little-endian CPUs have been exercised
+the most heavily, but big-endian systems are explicitly supported.
+</p><p> 
+There are two general categories of work: porting to a Linux system
+with a previously unseen CPU architecture, and porting to a different
+operating system.  This document covers the former.
+</p>
+ 
+ 
+<a name="dalvikCoreLibraries"></a><h2>Core Libraries</h2> 
+ 
+<p> 
+The native code in the core libraries (chiefly <code>dalvik/libcore</code>,
+but also <code>dalvik/vm/native</code>) is written in C/C++ and is expected
+to work without modification in a Linux environment.  Much of the code
+comes directly from the Apache Harmony project.
+</p><p> 
+The core libraries pull in code from many other projects, including
+OpenSSL, zlib, and ICU.  These will also need to be ported before the VM
+can be used.
+</p> 
+ 
+ 
+<a name="dalvikJNICallBridge"></a><h2>JNI Call Bridge</h2> 
+ 
+<p> 
+Most of the Dalvik VM runtime is written in portable C.  The one
+non-portable component of the runtime is the JNI call bridge.  Simply put,
+this converts an array of integers into function arguments of various
+types, and calls a function.  This must be done according to the C calling
+conventions for the platform.  The task could be as simple as pushing all
+of the arguments onto the stack, or involve complex rules for register
+assignment and stack alignment.
+</p><p> 
+To ease porting to new platforms, the <a href="http://sourceware.org/libffi/"> 
+open-source FFI library</a> (Foreign Function Interface) is used when a
+custom bridge is unavailable.  FFI is not as fast as a native implementation,
+and the optional performance improvements it does offer are not used, so
+writing a replacement is a good first step.
+</p><p> 
+The code lives in <code>dalvik/vm/arch/*</code>, with the FFI-based version
+in the "generic" directory.  There are two source files for each architecture.
+One defines the call bridge itself:
+</p><p><blockquote> 
+<code>void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+int argc, const u4* argv, const char* signature, void* func,
+JValue* pReturn)</code> 
+</blockquote></p><p> 
+This will invoke a C/C++ function declared:
+</p><p><blockquote> 
+    <code>return_type func(JNIEnv* pEnv, Object* this [, <i>args</i>])<br></code> 
+</blockquote>or (for a "static" method):<blockquote> 
+    <code>return_type func(JNIEnv* pEnv, ClassObject* clazz [, <i>args</i>])</code> 
+</blockquote></p><p> 
+The role of <code>dvmPlatformInvoke</code> is to convert the values in
+<code>argv</code> into C-style calling conventions, call the method, and
+then place the return type into <code>pReturn</code> (a union that holds
+all of the basic JNI types).  The code may use the method signature
+(a DEX "shorty" signature, with one character for the return type and one
+per argument) to determine how to handle the values.
+</p><p> 
+The other source file involved here defines a 32-bit "hint".  The hint
+is computed when the method's class is loaded, and passed in as the
+"argInfo" argument.  The hint can be used to avoid scanning the ASCII
+method signature for things like the return value, total argument size,
+or inter-argument 64-bit alignment restrictions.
+</p> 
+ 
+<a name="dalvikInterpreter"></a><h2>Interpreter</h2> 
+ 
+<p> 
+The Dalvik runtime includes two interpreters, labeled "portable" and "fast".
+The portable interpreter is largely contained within a single C function,
+and should compile on any system that supports gcc.  (If you don't have gcc,
+you may need to disable the "threaded" execution model, which relies on
+gcc's "goto table" implementation; look for the THREADED_INTERP define.)
+</p><p> 
+The fast interpreter uses hand-coded assembly fragments.  If none are
+available for the current architecture, the build system will create an
+interpreter out of C "stubs".  The resulting "all stubs" interpreter is
+quite a bit slower than the portable interpreter, making "fast" something
+of a misnomer.
+</p><p> 
+The fast interpreter is enabled by default.  On platforms without native
+support, you may want to switch to the portable interpreter.  This can
+be controlled with the <code>dalvik.vm.execution-mode</code> system
+property.  For example, if you:
+</p><p><blockquote> 
+<code>adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop"</code> 
+</blockquote></p><p> 
+and reboot, the Android app framework will start the VM with the portable
+interpreter enabled.
+</p> 
+ 
+ 
+<h3>Mterp Interpreter Structure</h3> 
+ 
+<p> 
+There may be significant performance advantages to rewriting the
+interpreter core in assembly language, using architecture-specific
+optimizations.  In Dalvik this can be done one instruction at a time.
+</p><p> 
+The simplest way to implement an interpreter is to have a large "switch"
+statement.  After each instruction is handled, the interpreter returns to
+the top of the loop, fetches the next instruction, and jumps to the
+appropriate label.
+</p><p> 
+An improvement on this is called "threaded" execution.  The instruction
+fetch and dispatch are included at the end of every instruction handler.
+This makes the interpreter a little larger overall, but you get to avoid
+the (potentially expensive) branch back to the top of the switch statement.
+</p><p> 
+Dalvik mterp goes one step further, using a computed goto instead of a goto
+table.  Instead of looking up the address in a table, which requires an
+extra memory fetch on every instruction, mterp multiplies the opcode number
+by a fixed value.  By default, each handler is allowed 64 bytes of space.
+</p><p> 
+Not all handlers fit in 64 bytes.  Those that don't can have subroutines
+or simply continue on to additional code outside the basic space.  Some of
+this is handled automatically by Dalvik, but there's no portable way to detect
+overflow of a 64-byte handler until the VM starts executing.
+</p><p> 
+The choice of 64 bytes is somewhat arbitrary, but has worked out well for
+ARM and x86.
+</p><p> 
+In the course of development it's useful to have C and assembly
+implementations of each handler, and be able to flip back and forth
+between them when hunting problems down.  In mterp this is relatively
+straightforward.  You can always see the files being fed to the compiler
+and assembler for your platform by looking in the
+<code>dalvik/vm/mterp/out</code> directory.
+</p><p> 
+The interpreter sources live in <code>dalvik/vm/mterp</code>.  If you
+haven't yet, you should read <code>dalvik/vm/mterp/README.txt</code> now.
+</p> 
+ 
+ 
+<h3>Getting Started With Mterp</h3> 
+ 
+</p><p> 
+Getting started:
+<ol> 
+<li>Decide on the name of your architecture.  For the sake of discussion,
+let's call it <code>myarch</code>.
+<li>Make a copy of <code>dalvik/vm/mterp/config-allstubs</code> to
+<code>dalvik/vm/mterp/config-myarch</code>.
+<li>Create a <code>dalvik/vm/mterp/myarch</code> directory to hold your
+source files.
+<li>Add <code>myarch</code> to the list in
+<code>dalvik/vm/mterp/rebuild.sh</code>.
+<li>Make sure <code>dalvik/vm/Android.mk</code> will find the files for
+your architecture.  If <code>$(TARGET_ARCH)</code> is configured this
+will happen automatically.
+</ol> 
+</p><p> 
+You now have the basic framework in place.  Whenever you make a change, you
+need to perform two steps: regenerate the mterp output, and build the
+core VM library.  (It's two steps because we didn't want the build system
+to require Python 2.5.  Which, incidentally, you need to have.)
+<ol> 
+<li>In the <code>dalvik/vm/mterp</code> directory, regenerate the contents
+of the files in <code>dalvik/vm/mterp/out</code> by executing
+<code>./rebuild.sh</code>.  Note there are two files, one in C and one
+in assembly.
+<li>In the <code>dalvik</code> directory, regenerate the
+<code>libdvm.so</code> library with <code>mm</code>.  You can also use
+<code>make libdvm</code> from the top of the tree.
+</ol> 
+</p><p> 
+This will leave you with an updated libdvm.so, which can be pushed out to
+a device with <code>adb sync</code> or <code>adb push</code>.  If you're
+using the emulator, you need to add <code>make snod</code> (System image,
+NO Dependency check) to rebuild the system image file.  You should not
+need to do a top-level "make" and rebuild the dependent binaries.
+</p><p> 
+At this point you have an "all stubs" interpreter.  You can see how it
+works by examining <code>dalvik/vm/mterp/cstubs/entry.c</code>.  The
+code runs in a loop, pulling out the next opcode, and invoking the
+handler through a function pointer.  Each handler takes a "glue" argument
+that contains all of the useful state.
+</p><p> 
+Your goal is to replace the entry method, exit method, and each individual
+instruction with custom implementations.  The first thing you need to do
+is create an entry function that calls the handler for the first instruction.
+After that, the instructions chain together, so you don't need a loop.
+(Look at the ARM or x86 implementation to see how they work.)
+</p><p> 
+Once you have that, you need something to jump to.  You can't branch
+directly to the C stub because it's expecting to be called with a "glue"
+argument and then return.  We need a C stub "wrapper" that does the
+setup and jumps directly to the next handler.  We write this in assembly
+and then add it to the config file definition.
+</p><p> 
+To see how this works, create a file called
+<code>dalvik/vm/mterp/myarch/stub.S</code> that contains one line:
+<pre> 
+/* stub for ${opcode} */
+</pre> 
+Then, in <code>dalvik/vm/mterp/config-myarch</code>, add this below the
+<code>handler-size</code> directive:
+<pre> 
+# source for the instruction table stub
+asm-stub myarch/stub.S
+</pre> 
+</p><p> 
+Regenerate the sources with <code>./rebuild.sh</code>, and take a look
+inside <code>dalvik/vm/mterp/out/InterpAsm-myarch.S</code>.  You should
+see 256 copies of the stub function in a single large block after the
+<code>dvmAsmInstructionStart</code> label.  The <code>stub.S</code> 
+code will be used anywhere you don't provide an assembly implementation.
+</p><p> 
+Note that each block begins with a <code>.balign 64</code> directive.
+This is what pads each handler out to 64 bytes.  Note also that the
+<code>${opcode}</code> text changed into an opcode name, which should
+be used to call the C implementation (<code>dvmMterp_${opcode}</code>).
+</p><p> 
+The actual contents of <code>stub.S</code> are up to you to define.
+See <code>entry.S</code> and <code>stub.S</code> in the <code>armv5te</code> 
+or <code>x86</code> directories for working examples.
+</p><p> 
+If you're working on a variation of an existing architecture, you may be
+able to use most of the existing code and just provide replacements for
+a few instructions.  Look at the <code>armv4t</code> implementation as
+an example.
+</p> 
+ 
+ 
+<h3>Replacing Stubs</h3> 
+ 
+<p> 
+There are roughly 230 Dalvik opcodes, including some that are inserted by
+<a href="dexopt.html">dexopt</a> and aren't described in the
+<a href="dalvik-bytecode.html">Dalvik bytecode</a> documentation.  Each
+one must perform the appropriate actions, fetch the next opcode, and
+branch to the next handler.  The actions performed by the assembly version
+must exactly match those performed by the C version (in
+<code>dalvik/vm/mterp/c/OP_*</code>).
+</p><p> 
+It is possible to customize the set of "optimized" instructions for your
+platform.  This is possible because optimized DEX files are not expected
+to work on multiple devices.  Adding, removing, or redefining instructions
+is beyond the scope of this document, and for simplicity it's best to stick
+with the basic set defined by the portable interpreter.
+</p><p> 
+Once you have written a handler that looks like it should work, add
+it to the config file.  For example, suppose we have a working version
+of <code>OP_NOP</code>.  For demonstration purposes, fake it for now by
+putting this into <code>dalvik/vm/mterp/myarch/OP_NOP.S</code>:
+<pre> 
+/* This is my NOP handler */
+</pre> 
+</p><p> 
+Then, in the <code>op-start</code> section of <code>config-myarch</code>, add:
+<pre> 
+    op OP_NOP myarch
+</pre> 
+</p><p> 
+This tells the generation script to use the assembly version from the
+<code>myarch</code> directory instead of the C version from the <code>c</code> 
+directory.
+</p><p> 
+Execute <code>./rebuild.sh</code>.  Look at <code>InterpAsm-myarch.S</code> 
+and <code>InterpC-myarch.c</code> in the <code>out</code> directory.  You
+will see that the <code>OP_NOP</code> stub wrapper has been replaced with our
+new code in the assembly file, and the C stub implementation is no longer
+included.
+</p><p> 
+As you implement instructions, the C version and corresponding stub wrapper
+will disappear from the output files.  Eventually you will have a 100%
+assembly interpreter.
+</p> 
+ 
+ 
+<h3>Interpreter Switching</h3> 
+ 
+<p> 
+The Dalvik VM actually includes a third interpreter implementation: the debug
+interpreter.  This is a variation of the portable interpreter that includes
+support for debugging and profiling.
+</p><p> 
+When a debugger attaches, or a profiling feature is enabled, the VM
+will switch interpreters at a convenient point.  This is done at the
+same time as the GC safe point check: on a backward branch, a method
+return, or an exception throw.  Similarly, when the debugger detaches
+or profiling is discontinued, execution transfers back to the "fast" or
+"portable" interpreter.
+</p><p> 
+Your entry function needs to test the "entryPoint" value in the "glue"
+pointer to determine where execution should begin.  Your exit function
+will need to return a boolean that indicates whether the interpreter is
+exiting (because we reached the "bottom" of a thread stack) or wants to
+switch to the other implementation.
+</p><p> 
+See the <code>entry.S</code> file in <code>x86</code> or <code>armv5te</code> 
+for examples.
+</p> 
+ 
+ 
+<h3>Testing</h3> 
+ 
+<p> 
+A number of VM tests can be found in <code>dalvik/tests</code>.  The most
+useful during interpreter development is <code>003-omnibus-opcodes</code>,
+which tests many different instructions.
+</p><p> 
+The basic invocation is:
+<pre> 
+$ cd dalvik/tests
+$ ./run-test 003
+</pre> 
+</p><p> 
+This will run test 003 on an attached device or emulator.  You can run
+the test against your desktop VM by specifying <code>--reference</code> 
+if you suspect the test may be faulty.  You can also use
+<code>--portable</code> and <code>--fast</code> to explictly specify
+one Dalvik interpreter or the other.
+</p><p> 
+Some instructions are replaced by <code>dexopt</code>, notably when
+"quickening" field accesses and method invocations.  To ensure
+that you are testing the basic form of the instruction, add the
+<code>--no-optimize</code> option.
+</p><p> 
+There is no in-built instruction tracing mechanism.  If you want
+to know for sure that your implementation of an opcode handler
+is being used, the easiest approach is to insert a "printf"
+call.  For an example, look at <code>common_squeak</code> in
+<code>dalvik/vm/mterp/armv5te/footer.S</code>.
+</p><p> 
+At some point you need to ensure that debuggers and profiling work with
+your interpreter.  The easiest way to do this is to simply connect a
+debugger or toggle profiling.  (A future test suite may include some
+tests for this.)
+</p> 
+
+
diff --git a/pdk/docs/guide/pdk_toc.cs b/pdk/docs/guide/pdk_toc.cs
index 282e5a2..1ab43b6 100644
--- a/pdk/docs/guide/pdk_toc.cs
+++ b/pdk/docs/guide/pdk_toc.cs
@@ -11,25 +11,44 @@
      <li><a href="<?cs var:toroot ?>guide/system_requirements.html">Device Requirements</a></li>           
      <li><a href="<?cs var:toroot ?>guide/build_system.html">Build System</a></li>
      <li><a href="<?cs var:toroot ?>guide/build_new_device.html">Building New Device</a></li>
-     <li><a href="<?cs var:toroot ?>guide/bring_up.html">Bring up</a></li>
-     <li><a href="<?cs var:toroot ?>guide/keymaps_keyboard_input.html">Keymaps and Keyboard</a></li>
-     <li><a href="<?cs var:toroot ?>guide/display_drivers.html">Display Drivers</a></li>
      <li><a href="<?cs var:toroot ?>guide/build_cookbook.html">Build Cookbook</a></li>
+     <li><a href="<?cs var:toroot ?>guide/bring_up.html">Bring up</a></li>
   </ul>
 </li>
 
+<li> <h2>Customization</h2>
+  <ul>
+    <li><a href="<?cs var:toroot ?>guide/customization.html">Customization</a></li>
+  </ul>
+</li>
 
 <li> <h2>System</h2>
 
 <ul>
   <li class="toggle-list">
+    <div><a href="javascript:nothing()">Connectivity</a></div>
+    <ul> 
+      <li><a href="<?cs var:toroot ?>guide/bluetooth.html">Bluetooth</a></li>
+      <li><a href="<?cs var:toroot ?>guide/gps.html">GPS</a></li>
+      <li><a href="<?cs var:toroot ?>guide/wifi.html">Wi-Fi</a></li>
+    </ul>
+  </li>
+ 
+  <li><a href="<?cs var:toroot ?>guide/display_drivers.html">Display Drivers</a></li>
+  <li class="toggle-list"> 
+    <div><a href="javascript:nothing()">Input Devices</a></div>
+    <ul>
+      <li><a href="<?cs var:toroot ?>guide/keymaps_keyboard_input.html">Keymaps and Keyboard</a></li>
+    </ul>
+  </li>
+  <li><a href="<?cs var:toroot ?>guide/lights.html">LED</a></li>
+  <li class="toggle-list">
     <div><a href="javascript:nothing()">Multimedia</a></div>
     <ul>
       <li><a href="<?cs var:toroot ?>guide/audio.html">Audio</a></li>
-      <li><a href="<?cs var:toroot ?>guide/camera.html">Camera</a></li>
+      <li><a href="<?cs var:toroot ?>guide/camera.html">Camera/Video</a></li>
     </ul>
   </li>
-
   <li class="toggle-list">
     <div><a href="<?cs var:toroot ?>guide/power_management.html">Power Management</a></div>
     <ul>
@@ -37,13 +56,12 @@
       <li><a href="<?cs var:toroot ?>guide/early_suspend.html">Early Suspend</a></li>
     </ul>
   </li>
-
+  <li><a href="<?cs var:toroot ?>guide/sensors.html">Sensors</a></li>
   <li class="toggle-list">
-    <div><a href="javascript:nothing()">Networking</a></div>
+    <div><a href="javascript:nothing()">Telephony</a></div>
     <ul>
-      <li><a href="<?cs var:toroot ?>guide/wifi.html">Wi-Fi</a></li>
-      <li><a href="<?cs var:toroot ?>guide/gps.html">GPS</a></li>
-      <li><a href="<?cs var:toroot ?>guide/bluetooth.html">Bluetooth</a></li>
+      <li><a href="<?cs var:toroot ?>guide/telephony.html">Radio Interface Layer</a></li>
+      <li><a href="<?cs var:toroot ?>guide/stk.html">SIM Toolkit Application (STK)</a></li>
     </ul>
   </li>
 
@@ -57,13 +75,11 @@
 </ul>
 </li>
 
-<li> <h2>Telephony</h2>
+<li> <h2>Dalvik Virtual Machine</h2>
   <ul>
-    <li><a href="<?cs var:toroot ?>guide/telephony.html">Radio Interface Layer</a></li>
-    <li><a href="<?cs var:toroot ?>guide/stk.html">SIM Toolkit Application (STK)</a></li>
+    <li><a href="<?cs var:toroot ?>guide/dalvik.html">Porting Dalvik</a></li>
   </ul>
 </li>
-
 <li> <h2>Testing and Debugging</h2>
   <ul>
     <li><a href="<?cs var:toroot ?>guide/instrumentation_testing.html">Instrumentation Testing</a></li>
@@ -72,11 +88,7 @@
   </ul>
 </li>
 
-<li> <h2>Customization</h2>
-  <ul>
-    <li><a href="<?cs var:toroot ?>guide/customization.html">Customization</a></li>
-  </ul>
-</li>
+
 
 </ul>
 
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar4.java b/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar4.java
index ee396b3..86188f2 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar4.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar4.java
@@ -26,7 +26,7 @@
 
 
 /**
- * Demonstrates how to use an indetermiate progress indicator in the window's title bar.
+ * Demonstrates how to use an indeterminate progress indicator in the window's title bar.
  */
 public class ProgressBar4 extends Activity {
     private boolean mToggleIndeterminate = false;
diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py
index dd8d6f4..b1e3757 100755
--- a/testrunner/adb_interface.py
+++ b/testrunner/adb_interface.py
@@ -157,8 +157,9 @@
     separated into its package and runner components.
     """
     instrumentation_path = "%s/%s" % (package_name, runner_name)
-    return self.StartInstrumentation(self, instrumentation_path, timeout_time,
-                                     no_window_animation, instrumentation_args)
+    return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time,
+                                     no_window_animation=no_window_animation,
+                                     instrumentation_args=instrumentation_args)
 
   def StartInstrumentation(
       self, instrumentation_path, timeout_time=60*10, no_window_animation=False,
@@ -203,7 +204,7 @@
         instrumentation_path, no_window_animation=no_window_animation,
         profile=profile, raw_mode=True,
         instrumentation_args=instrumentation_args)
-
+    logger.Log(command_string)
     (test_results, inst_finished_bundle) = (
         am_instrument_parser.ParseAmInstrumentOutput(
             self.SendShellCommand(command_string, timeout_time=timeout_time,
@@ -217,7 +218,7 @@
       short_msg_result = "no error message"
       if "shortMsg" in inst_finished_bundle:
         short_msg_result = inst_finished_bundle["shortMsg"]
-        logger.Log(short_msg_result)
+        logger.Log("Error! Test run failed: %s" % short_msg_result)
       raise errors.InstrumentationError(short_msg_result)
 
     if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
diff --git a/testrunner/coverage.py b/testrunner/coverage.py
index 39a2ceb..c80eea0 100755
--- a/testrunner/coverage.py
+++ b/testrunner/coverage.py
@@ -44,16 +44,15 @@
     # path to EMMA host jar, relative to Android build root
   _EMMA_JAR = os.path.join(_EMMA_BUILD_PATH, "lib", "emma.jar")
   _TEST_COVERAGE_EXT = "ec"
-  # default device-side path to code coverage results file
-  _DEVICE_COVERAGE_PATH = "/sdcard/coverage.ec"
   # root path of generated coverage report files, relative to Android build root
   _COVERAGE_REPORT_PATH = os.path.join("out", "emma")
+  _TARGET_DEF_FILE = "coverage_targets.xml"
   _CORE_TARGET_PATH = os.path.join("development", "testrunner",
-                                   "coverage_targets.xml")
+                                   _TARGET_DEF_FILE)
   # vendor glob file path patterns to tests, relative to android
   # build root
   _VENDOR_TARGET_PATH = os.path.join("vendor", "*", "tests", "testinfo",
-                                     "coverage_targets.xml")
+                                     _TARGET_DEF_FILE)
 
   # path to root of target build intermediates
   _TARGET_INTERMEDIATES_BASE_PATH = os.path.join("out", "target", "common",
@@ -93,7 +92,7 @@
       return False
 
   def ExtractReport(self, test_suite,
-                    device_coverage_path=_DEVICE_COVERAGE_PATH,
+                    device_coverage_path,
                     output_path=None):
     """Extract runtime coverage data and generate code coverage report.
 
@@ -122,8 +121,14 @@
       report_path = os.path.join(output_path,
                                  test_suite.GetName())
       target = self._targets_manifest.GetTarget(test_suite.GetTargetName())
-      return self._GenerateReport(report_path, coverage_local_path, [target],
-                                  do_src=True)
+      if target is None:
+        msg = ["Error: test %s references undefined target %s."
+                % (test_suite.GetName(), test_suite.GetTargetName())]
+        msg.append(" Ensure target is defined in %s" % self._TARGET_DEF_FILE)
+        logger.Log("".join(msg))
+      else:
+        return self._GenerateReport(report_path, coverage_local_path, [target],
+                                    do_src=True)
     return None
 
   def _GenerateReport(self, report_path, coverage_file_path, targets,
diff --git a/testrunner/errors.py b/testrunner/errors.py
index e240899..c04fd01 100755
--- a/testrunner/errors.py
+++ b/testrunner/errors.py
@@ -34,7 +34,7 @@
   """Generic exception that indicates a fatal error has occurred and program
   execution should be aborted."""
 
-  def __init__(self, msg="AbortError"):
+  def __init__(self, msg=""):
     self.msg = msg
 
 
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 05d29ec..e66b8de 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -112,7 +112,13 @@
                       default=False, action="store_true",
                       help="Run all tests defined as part of the continuous "
                       "test set")
-
+    parser.add_option("--timeout", dest="timeout",
+                      default=300, help="Set a timeout limit (in sec) for "
+                      "running native tests on a device (default: 300 secs)")
+    parser.add_option("--cts", dest="cts_tests",
+                      default=False, action="store_true",
+                      help="Run all tests defined as part of the "
+                      "compatibility test suite")
     group = optparse.OptionGroup(
         parser, "Targets", "Use these options to direct tests to a specific "
         "Android target")
@@ -126,8 +132,11 @@
 
     self._options, self._test_args = parser.parse_args()
 
-    if (not self._options.only_list_tests and not self._options.all_tests
-        and not self._options.continuous_tests and len(self._test_args) < 1):
+    if (not self._options.only_list_tests
+        and not self._options.all_tests
+        and not self._options.continuous_tests
+        and not self._options.cts_tests
+        and len(self._test_args) < 1):
       parser.print_help()
       logger.SilentLog("at least one test name must be specified")
       raise errors.AbortError
@@ -226,8 +235,10 @@
     """Get a list of TestSuite objects to run, based on command line args."""
     if self._options.all_tests:
       return self._known_tests.GetTests()
-    if self._options.continuous_tests:
+    elif self._options.continuous_tests:
       return self._known_tests.GetContinuousTests()
+    elif self._options.cts_tests:
+      return self._known_tests.GetCtsTests()
     tests = []
     for name in self._test_args:
       test = self._known_tests.GetTest(name)
@@ -275,16 +286,53 @@
           raw_mode=self._options.raw_mode,
           instrumentation_args=instrumentation_args)
       logger.Log(adb_cmd)
+    elif self._options.coverage:
+      # need to parse test output to determine path to coverage file
+      logger.Log("Running in coverage mode, suppressing test output")
+      try:
+        (test_results, status_map) = self._adb.StartInstrumentationForPackage(
+          package_name=test_suite.GetPackageName(),
+          runner_name=test_suite.GetRunnerName(),
+          timeout_time=60*60,
+          instrumentation_args=instrumentation_args)
+      except errors.InstrumentationError, errors.DeviceUnresponsiveError:
+        return
+      self._PrintTestResults(test_results)
+      device_coverage_path = status_map.get("coverageFilePath", None)
+      if device_coverage_path is None:
+        logger.Log("Error: could not find coverage data on device")
+        return
+      coverage_file = self._coverage_gen.ExtractReport(test_suite, device_coverage_path)
+      if coverage_file is not None:
+        logger.Log("Coverage report generated at %s" % coverage_file)
     else:
       self._adb.StartInstrumentationNoResults(
           package_name=test_suite.GetPackageName(),
           runner_name=test_suite.GetRunnerName(),
           raw_mode=self._options.raw_mode,
           instrumentation_args=instrumentation_args)
-      if self._options.coverage and test_suite.GetTargetName() is not None:
-        coverage_file = self._coverage_gen.ExtractReport(test_suite)
-        if coverage_file is not None:
-          logger.Log("Coverage report generated at %s" % coverage_file)
+
+  def _PrintTestResults(self, test_results):
+    """Prints a summary of test result data to stdout.
+
+    Args:
+      test_results: a list of am_instrument_parser.TestResult
+    """
+    total_count = 0
+    error_count = 0
+    fail_count = 0
+    for test_result in test_results:
+      if test_result.GetStatusCode() == -1: # error
+        logger.Log("Error in %s: %s" % (test_result.GetTestName(),
+                                        test_result.GetFailureReason()))
+        error_count+=1
+      elif test_result.GetStatusCode() == -2: # failure
+        logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
+                                          test_result.GetFailureReason()))
+        fail_count+=1
+      total_count+=1
+    logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
+               (total_count, fail_count, error_count))
 
   def _CollectTestSources(self, test_list, dirname, files):
     """For each directory, find tests source file and add them to the list.
@@ -384,7 +432,8 @@
 
       # Single quotes are needed to prevent the shell splitting it.
       output = self._adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
-                                          full_path)
+                                          full_path,
+                                          int(self._options.timeout))
       success = output.endswith("exit code:0")
       logger.Log("%s... %s" % (f, success and "ok" or "failed"))
       # Print the captured output when the test failed.
diff --git a/testrunner/test_defs.py b/testrunner/test_defs.py
index 2cdcfa8..0542a05 100644
--- a/testrunner/test_defs.py
+++ b/testrunner/test_defs.py
@@ -144,6 +144,14 @@
         con_tests.append(test)
     return con_tests
 
+  def GetCtsTests(self):
+    """Return list of cts tests."""
+    cts_tests = []
+    for test in self.GetTests():
+      if test.IsCts():
+        cts_tests.append(test)
+    return cts_tests
+
   def GetTest(self, name):
     return self._testname_map.get(name, None)
   
@@ -157,6 +165,7 @@
   _TARGET_ATTR = "coverage_target"
   _BUILD_ATTR = "build_path"
   _CONTINUOUS_ATTR = "continuous"
+  _CTS_ATTR = "cts"
   _DESCRIPTION_ATTR = "description"
   _EXTRA_MAKE_ARGS_ATTR = "extra_make_args"
 
@@ -199,6 +208,11 @@
       self._continuous = suite_element.getAttribute(self._CONTINUOUS_ATTR)
     else:
       self._continuous = False
+    if suite_element.hasAttribute(self._CTS_ATTR):
+      self._cts = suite_element.getAttribute(self._CTS_ATTR)
+    else:
+      self._cts = False
+
     if suite_element.hasAttribute(self._DESCRIPTION_ATTR):
       self._description = suite_element.getAttribute(self._DESCRIPTION_ATTR)
     else:
@@ -236,6 +250,10 @@
     """Returns true if test is flagged as being part of the continuous tests"""  
     return self._continuous
 
+  def IsCts(self):
+    """Returns true if test is part of the compatibility test suite"""
+    return self._cts
+
   def IsNative(self):
     """Returns true if test is a native one."""
     return self._native
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 34366ab..e1d9cc5 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -42,6 +42,8 @@
   continuous: Optional boolean. Default is false. Set to true if tests are known
     to be reliable, and should be included in a continuous test system. false if
     they are under development.
+  cts: Optional boolean. Default is false. Set to true if test is included in
+    compatibility test suite.
 
   description: Optional string. Default is empty. Short description (typically
      less than 60 characters) about this test.
@@ -56,7 +58,7 @@
 =============
   The <test-native> element has the following attributes
 
-  name build_path [continuous description]
+  name build_path [continuous description extra_make_args]
 
   Where:
   name: Self-descriptive name used to uniquely identify the test
@@ -83,7 +85,9 @@
     done
 -->
 
-<test-definitions version="1">
+<test-definitions xmlns="http://schemas.android.com/testrunner/test_defs/1.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://schemas.android.com/testrunner/test_defs/1.0 test_defs.xsd">
 
 <!-- system-wide tests -->
 <test name="framework"
@@ -164,6 +168,162 @@
     coverage_target="framework"
     continuous="true" />
 
+<!--  cts tests -->
+
+<test name="cts-permission"
+    build_path="cts/tests"
+    package="com.android.cts.permission"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    continuous="true"
+    cts="true" />
+
+<test name="cts-process"
+    build_path="cts/tests"
+    package="com.android.cts.process"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-api-signature"
+    build_path="cts/tests"
+    package="android.tests.sigtest"
+    runner=".InstrumentationRunner"
+    cts="true" />
+
+<test name="cts-api-signature-func"
+    build_path="cts/tests"
+    package="android.tests.sigtest.tests"
+    cts="true" />
+
+<test name="cts-apidemos"
+    build_path="cts/tests"
+    package="android.apidemos.cts"
+    coverage_target="ApiDemos"
+    cts="true" />
+
+<test name="cts-app"
+    build_path="cts/tests"
+    package="com.android.cts.app"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-content"
+    build_path="cts/tests"
+    package="com.android.cts.content"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-database"
+    build_path="cts/tests"
+    package="com.android.cts.database"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-graphics"
+    build_path="cts/tests"
+    package="com.android.cts.graphics"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-hardware"
+    build_path="cts/tests"
+    package="com.android.cts.hardware"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-location"
+    build_path="cts/tests"
+    package="com.android.cts.location"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-net"
+    build_path="cts/tests"
+    package="com.android.cts.net"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-os"
+    build_path="cts/tests"
+    package="com.android.cts.os"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-perf1"
+    build_path="cts/tests"
+    package="com.android.cts.performance"
+    runner="android.test.InstrumentationCtsTestRunner"
+    cts="true" />
+
+<test name="cts-perf2"
+    build_path="cts/tests"
+    package="com.android.cts.performance2"
+    runner="android.test.InstrumentationCtsTestRunner"
+    cts="true" />
+
+<test name="cts-perf3"
+    build_path="cts/tests"
+    package="com.android.cts.performance3"
+    runner="android.test.InstrumentationCtsTestRunner"
+    cts="true" />
+
+<test name="cts-perf4"
+    build_path="cts/tests"
+    package="com.android.cts.performance4"
+    runner="android.test.InstrumentationCtsTestRunner"
+    cts="true" />
+
+<test name="cts-perf5"
+    build_path="cts/tests"
+    package="com.android.cts.performance5"
+    runner="android.test.InstrumentationCtsTestRunner"
+    cts="true" />
+
+<test name="cts-provider"
+    build_path="cts/tests"
+    package="com.android.cts.provider"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-text"
+    build_path="cts/tests"
+    package="com.android.cts.text"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-util"
+    build_path="cts/tests"
+    package="com.android.cts.util"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-view"
+    build_path="cts/tests"
+    package="com.android.cts.view"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<test name="cts-widget"
+    build_path="cts/tests"
+    package="com.android.cts.widget"
+    runner="android.test.InstrumentationCtsTestRunner"
+    coverage_target="framework"
+    cts="true" />
+
+<!--  end of cts tests -->
+
 <!--  selected app tests -->
 <test name="browser"
     build_path="packages/apps/Browser"
@@ -190,13 +350,13 @@
     continuous="true" />
 
 <test name="camerastress"
-    build_path="packages/apps/Camera/tests"
+    build_path="packages/apps/Camera"
     package="com.android.camera.tests"
     class="com.android.camera.StressTests"
     coverage_target="Camera" />
 
 <test name="camera"
-    build_path="packages/apps/Camera/tests"
+    build_path="packages/apps/Camera"
     package="com.android.camera.tests"
     class="com.android.camera.UnitTests"
     continuous="true"
@@ -226,6 +386,26 @@
     coverage_target="framework"
     continuous="true" />
 
+<test name="mediaapitest"
+    build_path="frameworks/base/media/tests/MediaFrameworkTest"
+    package="com.android.mediaframeworktest"
+    class="com.android.mediaframeworktest.functional.MediaPlayerApiTest"
+    runner=".MediaFrameworkTestRunner"
+    coverage_target="framework" />
+
+<test name="mediarecordertest"
+    build_path="frameworks/base/media/tests/MediaFrameworkTest"
+    package="com.android.mediaframeworktest"
+    class="com.android.mediaframeworktest.functional.MediaRecorderTest"
+    runner=".MediaFrameworkTestRunner"
+    coverage_target="framework" />
+
+<test name="mediastresstest"
+    build_path="frameworks/base/media/tests/MediaFrameworkTest"
+    package="com.android.mediaframeworktest"
+    runner=".MediaRecorderStressTestRunner"
+    coverage_target="framework" />
+
 <test name="mediaunit"
     build_path="frameworks/base/media/tests/MediaFrameworkTest"
     package="com.android.mediaframeworktest"
diff --git a/testrunner/test_defs.xsd b/testrunner/test_defs.xsd
new file mode 100644
index 0000000..f964779
--- /dev/null
+++ b/testrunner/test_defs.xsd
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://schemas.android.com/testrunner/test_defs/1.0"
+        xmlns="http://schemas.android.com/testrunner/test_defs/1.0"
+        elementFormDefault="qualified">
+
+    <xs:element name="test-definitions">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:choice minOccurs="0" maxOccurs="unbounded">
+                    <xs:element name="test" type="javaTestType"/>
+                    <xs:element name="test-native" type="nativeTestType"/>
+                </xs:choice>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="javaTestType">
+        <xs:attribute name="name" type="xs:string" use="required"/>
+        <xs:attribute name="package" type="xs:string" use="required"/>
+        <xs:attribute name="build_path" type="xs:string" use="optional"/>
+        <xs:attribute name="class" type="xs:string" use="optional"/>
+        <xs:attribute name="runner" type="xs:string" use="optional"
+                      default="android.test.InstrumentationTestRunner"/>
+        <xs:attribute name="coverage_target" type="xs:string" use="optional"/>
+        <xs:attribute name="continuous" type="xs:boolean" use="optional" default="false"/>
+        <xs:attribute name="cts" type="xs:boolean" use="optional" default="false"/>
+    </xs:complexType>
+
+    <xs:complexType name="nativeTestType">
+        <xs:attribute name="name" type="xs:string" use="required"/>
+        <xs:attribute name="build_path" type="xs:string" use="required"/>
+        <xs:attribute name="extra_make_args" type="xs:string" use="optional"/>
+        <xs:attribute name="description" type="xs:string" use="optional"/>
+        <xs:attribute name="continuous" type="xs:boolean" use="optional" default="false"/>
+    </xs:complexType>
+</xs:schema>
diff --git a/tools/ddms/app/etc/ddms b/tools/ddms/app/etc/ddms
index b4470b5..c63930b 100755
--- a/tools/ddms/app/etc/ddms
+++ b/tools/ddms/app/etc/ddms
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 # Copyright 2005-2007, The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -67,12 +67,28 @@
     os_opts="-XstartOnFirstThread"
     #because Java 1.6 is 64 bits only and SWT doesn't support this, we force the usage of java 1.5
     java_cmd="/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Commands/java"
-elif [[ `uname -s` = 'Linux' && `uname -m` = 'x86_64' ]]; then
-#   Use 32-bit Java on x86_64 Linux.
-    java_cmd="/usr/lib/jvm/ia32-java-6-sun/jre/bin/java"
-    if [ ! -x $java_cmd ]; then
-        echo `basename "$prog"`": missing 32-bit JVM. Please install ia32-sun-java6-bin."
-        exit 1
+elif [[ `uname -s` = 'Linux' ]]; then
+    # We need a 32-bit Java on Linux, because our JNI libraries are 32-bit.
+    java_cmd=`which java`
+
+    if [ -x "$java_cmd" ]; then
+        if [[ ! `file -L "$java_cmd"` =~ "ELF 32-bit LSB executable" ]]; then
+            java_cmd=""
+        fi
+    fi
+
+    if [ ! -x "$java_cmd" ]; then
+        # The default JVM is not suitable.
+        # See if we can find a particular known-good JVM
+        java_cmd="/usr/lib/jvm/ia32-java-6-sun/jre/bin/java"
+        if [ ! -x "$java_cmd" ]; then
+            PREFIX=`basename "$prog"`
+            echo "$PREFIX: The default Java VM is not an ELF 32-bit LSB executable."
+            echo "$PREFIX: Please do one of the following:"
+            echo "$PREFIX: 1) Arrange for the default Java VM to be an ELF 32-bit LSB executable."
+            echo "$PREFIX: 2) Install the ia32-sun-java6-bin package."
+            exit 1
+        fi
     fi
 else
     os_opts=
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 09ecc63..751d33d 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -47,6 +47,8 @@
  org.eclipse.core.expressions
 Eclipse-LazyStart: true
 Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.actions;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.build;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.editors;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
@@ -69,13 +71,36 @@
  com.android.ide.eclipse.adt.internal.editors.xml;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.launch;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.launch.junit;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.launch.junit.runtime;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.preferences;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.project;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.properties;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.refactorings.extractstring;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.resources;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.resources.configurations;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.resources.manager;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.resources.manager.files;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.sdk;x-friends:="com.android.ide.eclipse.tests",
  com.android.ide.eclipse.adt.internal.ui;x-friends:="com.android.ide.eclipse.tests",
- com.android.ide.eclipse.adt.internal.wizards.newproject;x-friends:="com.android.ide.eclipse.tests"
+ com.android.ide.eclipse.adt.internal.wizards.actions;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.wizards.avdmanager;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.wizards.export;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.internal.wizards.newxmlfile;x-friends:="com.android.ide.eclipse.tests",
+ com.android.jarutils;x-friends:="com.android.ide.eclipse.tests",
+ com.android.layoutlib.api;x-friends:="com.android.ide.eclipse.tests",
+ com.android.layoutlib.utils;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ninepatch;x-friends:="com.android.ide.eclipse.tests",
+ com.android.prefs;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdklib;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdklib.internal.avd;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdklib.internal.project;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdklib.internal.repository;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdklib.repository;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdkstats;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdkuilib;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdkuilib.internal.repository;x-friends:="com.android.ide.eclipse.tests",
+ com.android.sdkuilib.repository;x-friends:="com.android.ide.eclipse.tests"
 
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index a5b107b..dc2c612 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -455,23 +455,23 @@
    <extension
          point="org.eclipse.wst.sse.ui.editorConfiguration">
       <sourceViewerConfiguration
-            class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig"
+            class="com.android.ide.eclipse.adt.internal.editors.manifest.ManifestSourceViewerConfig"
             target="com.android.ide.eclipse.editors.manifest.ManifestEditor">
       </sourceViewerConfiguration>
       <sourceViewerConfiguration
-            class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig"
+            class="com.android.ide.eclipse.adt.internal.editors.resources.ResourcesSourceViewerConfig"
             target="com.android.ide.eclipse.editors.resources.ResourcesEditor">
       </sourceViewerConfiguration>
       <sourceViewerConfiguration
-            class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig"
+            class="com.android.ide.eclipse.adt.internal.editors.layout.LayoutSourceViewerConfig"
             target="com.android.ide.eclipse.editors.layout.LayoutEditor">
       </sourceViewerConfiguration>
       <sourceViewerConfiguration
-            class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig"
+            class="com.android.ide.eclipse.adt.internal.editors.menu.MenuSourceViewerConfig"
             target="com.android.ide.eclipse.editors.menu.MenuEditor">
       </sourceViewerConfiguration>
       <sourceViewerConfiguration
-            class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig"
+            class="com.android.ide.eclipse.adt.internal.editors.xml.XmlSourceViewerConfig"
             target="com.android.ide.eclipse.editors.xml.XmlEditor">
       </sourceViewerConfiguration>
    </extension>
@@ -530,9 +530,9 @@
             id="adt.actionSet.refactorings"
             label="Android Refactorings"
             visible="true">
- 
+
          <!-- This duplicates the Refactoring Menu definition from the jdt.ui plugin.xml,
-              which allows us to insert our contribution even if the JDT is not loaded. 
+              which allows us to insert our contribution even if the JDT is not loaded.
               We overload the definition with our new group.-->
          <menu
                label="Refactor"
@@ -550,12 +550,12 @@
             <separator name="scriptGroup"/>
          </menu>
 
-         <menu 
-               label="Android" 
-               path="org.eclipse.jdt.ui.refactoring.menu/androidGroup" 
-               id="com.android.ide.eclipse.adt.refactoring.menu"> 
-              <separator name="android"/> 
-         </menu> 
+         <menu
+               label="Android"
+               path="org.eclipse.jdt.ui.refactoring.menu/androidGroup"
+               id="com.android.ide.eclipse.adt.refactoring.menu">
+              <separator name="android"/>
+         </menu>
          <action
                class="com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringAction"
                definitionId="com.android.ide.eclipse.adt.refactoring.extract.string"
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
index ddfce4d..4464daa 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
@@ -90,7 +90,7 @@
 
     /** Name of the android sources directory */
     public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$
-    
+
     /** Resource java class  filename, i.e. "R.java" */
     public final static String FN_RESOURCE_CLASS = "R.java"; //$NON-NLS-1$
     /** Resource class file  filename, i.e. "R.class" */
@@ -104,15 +104,11 @@
     /** Temporary packaged resources file name for a specific set of configuration */
     public final static String FN_RESOURCES_S_AP_ = "resources-%s.ap_"; //$NON-NLS-1$
     public final static Pattern PATTERN_RESOURCES_S_AP_ =
-        Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE);
+        Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
 
-    public final static String FN_ADB =
-        (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
-            "adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
+    public final static String FN_ADB = SdkConstants.FN_ADB;
 
-    public final static String FN_EMULATOR =
-        (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
-            "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
+    public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
 
     public final static String FN_TRACEVIEW =
         (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
@@ -128,8 +124,8 @@
     public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
 
     /** Leaf of the javaDoc folder. Does not start with a separator. */
-    public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" +
-            SdkConstants.FD_DOCS_REFERENCE; //$NON-NLS-1$
+    public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" + //$NON-NLS-1$
+            SdkConstants.FD_DOCS_REFERENCE;
 
     /** Path of the samples directory relative to the sdk folder.
      *  This is an OS path, ending with a separator.
@@ -159,10 +155,10 @@
 
     /** aidl marker error. */
     public final static String MARKER_AIDL = COMMON_PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$
-    
+
     /** android marker error */
     public final static String MARKER_ANDROID = COMMON_PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$
-    
+
     /** Name for the "type" marker attribute */
     public final static String MARKER_ATTR_TYPE = "android.type"; //$NON-NLS-1$
     /** Name for the "class" marker attribute */
@@ -176,9 +172,9 @@
     /** provider value for marker attribute "type" */
     public final static String MARKER_ATTR_TYPE_PROVIDER = "provider"; //$NON-NLS-1$
 
-    public final static String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$ 
-    public final static String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$ 
-    public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$ 
+    public final static String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$
+    public final static String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$
+    public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$
     public final static String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$
     public final static String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$
     public final static String CLASS_INSTRUMENTATION_RUNNER =
@@ -202,7 +198,7 @@
         "android.preference." + CLASS_NAME_PREFERENCE_SCREEN; //$NON-NLS-1$
     public final static String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$
     public final static String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$
-    
+
     public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$
 
     /**
@@ -219,6 +215,6 @@
 
     /** The base URL where to find the Android class & manifest documentation */
     public static final String CODESITE_BASE_URL = "http://code.google.com/android";  //$NON-NLS-1$
-    
+
     public static final String LIBRARY_TEST_RUNNER = "android.test.runner"; // $NON-NLS-1$
 }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
index 92ddd84..725c6a8 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.editors;
 
+import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
@@ -30,6 +31,7 @@
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.sdklib.SdkConstants;
 
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.ITextViewer;
@@ -74,7 +76,7 @@
 
     /** Regexp to detect an element tag name */
     private static Pattern sFirstElementWord = Pattern.compile("^[a-zA-Z0-9_:]+"); //$NON-NLS-1$
-    
+
     /** Regexp to detect whitespace */
     private static Pattern sWhitespace = Pattern.compile("\\s+"); //$NON-NLS-1$
 
@@ -86,11 +88,11 @@
     private ElementDescriptor mRootDescriptor;
 
     private final int mDescriptorId;
-    
+
     private AndroidEditor mEditor;
 
     /**
-     * Constructor for AndroidContentAssist 
+     * Constructor for AndroidContentAssist
      * @param descriptorId An id for {@link AndroidTargetData#getDescriptorProvider(int)}.
      *      The Id can be one of {@link AndroidTargetData#DESCRIPTOR_MANIFEST},
      *      {@link AndroidTargetData#DESCRIPTOR_LAYOUT},
@@ -110,17 +112,22 @@
      * @param viewer the viewer whose document is used to compute the proposals
      * @param offset an offset within the document for which completions should be computed
      * @return an array of completion proposals or <code>null</code> if no proposals are possible
-     * 
+     *
      * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
      */
     public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
-      
+
         if (mEditor == null) {
             mEditor = getAndroidEditor(viewer);
+            if (mEditor == null) {
+                // This should not happen. Duck and forget.
+                AdtPlugin.log(IStatus.ERROR, "Editor not found during completion");
+                return null;
+            }
         }
 
         UiElementNode rootUiNode = mEditor.getUiRootNode();
-        
+
         Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor
                                     or String or null */
         String parent = "";      //$NON-NLS-1$
@@ -136,7 +143,12 @@
         // check to see if we can find a UiElementNode matching this XML node
         UiElementNode currentUiNode =
             rootUiNode == null ? null : rootUiNode.findXmlNode(currentNode);
-        
+
+        if (currentNode == null) {
+            // Should not happen (an XML doc always has at least a doc node). Just give up.
+            return null;
+        }
+
         if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
             parent = currentNode.getNodeName();
 
@@ -154,7 +166,7 @@
                     // We're editing attributes in an element node (either the attributes' names
                     // or their values).
                     choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
-                    
+
                     if (info.correctedPrefix != null) {
                         wordPrefix = info.correctedPrefix;
                     }
@@ -180,7 +192,7 @@
                 needTag = '<';
             }
         }
-        
+
         // get the selection length
         int selectionLength = 0;
         ISelection selection = viewer.getSelectionProvider().getSelection();
@@ -196,7 +208,7 @@
     /**
      * Returns the namespace prefix matching the Android Resource URI.
      * If no such declaration is found, returns the default "android" prefix.
-     *  
+     *
      * @param node The current node. Must not be null.
      * @param nsUri The namespace URI of which the prefix is to be found,
      *              e.g. {@link SdkConstants#NS_RESOURCES}
@@ -210,9 +222,9 @@
         if (XmlnsAttributeDescriptor.XMLNS_URI.equals(nsUri)) {
             return "xmlns"; //$NON-NLS-1$
         }
-        
+
         HashSet<String> visited = new HashSet<String>();
-        
+
         String prefix = null;
         for (; prefix == null &&
                     node != null &&
@@ -230,7 +242,7 @@
                 }
             }
         }
-        
+
         // Use a sensible default prefix if we can't find one.
         // We need to make sure the prefix is not one that was declared in the scope
         // visited above.
@@ -251,7 +263,7 @@
      * <p/>
      * Example: <manifest><applic*cursor* => returns the list of all elements that
      * can be found under <manifest>, of which <application> is one of the choices.
-     * 
+     *
      * @return an ElementDescriptor[] or null if no valid element was found.
      */
     private Object[] getChoicesForElement(String parent, Node current_node) {
@@ -287,8 +299,8 @@
      *   lenient as suitable for attribute values.
      * - AttribInfo.needTag will be non-zero if we find that the attribute completion proposal
      *   must be double-quoted.
-     * @param currentUiNode 
-     * 
+     * @param currentUiNode
+     *
      * @return an AttributeDescriptor[] if the user is editing an attribute name.
      *         a String[] if the user is editing an attribute value with some known values,
      *         or null if nothing is known about the context.
@@ -309,7 +321,7 @@
             } else {
                 attrInfo.needTag = '"';
             }
-            
+
             if (currentUiNode != null) {
                 // look for an UI attribute matching the current attribute name
                 String attrName = attrInfo.name;
@@ -329,7 +341,7 @@
 
                 if (currAttrNode != null) {
                     choices = currAttrNode.getPossibleValues(value);
-                    
+
                     if (currAttrNode instanceof UiFlagAttributeNode) {
                         // A "flag" can consist of several values separated by "or" (|).
                         // If the correct prefix contains such a pipe character, we change
@@ -345,7 +357,7 @@
 
             if (choices == null) {
                 // fallback on the older descriptor-only based lookup.
-                
+
                 // in order to properly handle the special case of the name attribute in
                 // the action tag, we need the grandparent of the action node, to know
                 // what type of actions we need.
@@ -358,7 +370,7 @@
                         greatGrandParentName = greatGrandParent.getLocalName();
                     }
                 }
-                
+
                 AndroidTargetData data = mEditor.getTargetData();
                 if (data != null) {
                     choices = data.getAttributeValues(parent, attrInfo.name, greatGrandParentName);
@@ -382,7 +394,7 @@
      * This means the user is editing outside of any XML element or attribute.
      * Simply return the list of XML elements that can be present there, based on the
      * parent of the current node.
-     * 
+     *
      * @return An ElementDescriptor[] or null.
      */
     private Object[] getChoicesForTextNode(Node currentNode) {
@@ -413,7 +425,7 @@
      * - AttributeDescriptor: a possible attribute descriptor which XML name should be completed.
      * - String: string values to display as-is to the user. Typically those are possible
      *           values for a given attribute.
-     * 
+     *
      * @return The ICompletionProposal[] to display to the user.
      */
     private ICompletionProposal[] computeProposals(int offset, Node currentNode,
@@ -421,7 +433,7 @@
             boolean is_attribute, int selectionLength) {
         ArrayList<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
         HashMap<String, String> nsUriMap = new HashMap<String, String>();
-        
+
         for (Object choice : choices) {
             String keyword = null;
             String nsPrefix = null;
@@ -441,7 +453,7 @@
                 if (choice instanceof TextAttributeDescriptor) {
                     tooltip = ((TextAttributeDescriptor) choice).getTooltip();
                 }
-                
+
                 // Get the namespace URI for the attribute. Note that some attributes
                 // do not have a namespace and thus return null here.
                 String nsUri = ((AttributeDescriptor)choice).getNamespaceUri();
@@ -455,13 +467,13 @@
                 if (nsPrefix != null) {
                     nsPrefix += ":"; //$NON-NLS-1$
                 }
-                
+
             } else if (choice instanceof String) {
                 keyword = (String) choice;
             } else {
                 continue; // discard unknown choice
             }
-            
+
             String nsKeyword = nsPrefix == null ? keyword : (nsPrefix + keyword);
 
             if (keyword.startsWith(wordPrefix) ||
@@ -499,7 +511,7 @@
                 proposals.add(proposal);
             }
         }
-        
+
         return proposals.toArray(new ICompletionProposal[proposals.size()]);
     }
 
@@ -510,7 +522,7 @@
      * <p/>
      * Elements can have children if the descriptor has children element descriptors
      * or if one of the attributes is a TextValueDescriptor.
-     * 
+     *
      * @param descriptor An ElementDescriptor or an AttributeDescriptor
      * @return True if the descriptor is an ElementDescriptor that can have children or a text value
      */
@@ -548,7 +560,7 @@
     /**
      * Returns the characters which when entered by the user should
      * automatically trigger the presentation of possible completions.
-     * 
+     *
      * In our case, we auto-activate on opening tags and attributes namespace.
      *
      * @return the auto activation characters for completion proposal or <code>null</code>
@@ -569,7 +581,7 @@
     public String getErrorMessage() {
         return null;
     }
-    
+
     /**
      * Heuristically extracts the prefix used for determining template relevance
      * from the viewer's document. The default implementation returns the String from
@@ -578,7 +590,7 @@
      *
      * The part were we access the docment was extracted from
      * org.eclipse.jface.text.templatesTemplateCompletionProcessor and adapted to our needs.
-     * 
+     *
      * @param viewer the viewer
      * @param offset offset into document
      * @return the prefix to consider
@@ -611,7 +623,7 @@
             return ""; //$NON-NLS-1$
         }
     }
-    
+
     /**
      * Extracts the character at the given offset.
      * Returns 0 if the offset is invalid.
@@ -676,7 +688,7 @@
                 // text will contain the full string of the current element,
                 // i.e. whatever is after the "<" to the current cursor
                 String text = document.get(offset, n - offset);
-                
+
                 // Normalize whitespace to single spaces
                 text = sWhitespace.matcher(text).replaceAll(" "); //$NON-NLS-1$
 
@@ -684,13 +696,13 @@
                 // any whitespace. If there's nothing left, no attribute has been defined yet.
                 // Be sure to keep any whitespace after the initial word if any, as it matters.
                 text = sFirstElementWord.matcher(text).replaceFirst("");  //$NON-NLS-1$
-                
+
                 // There MUST be space after the element name. If not, the cursor is still
                 // defining the element name.
                 if (!text.startsWith(" ")) { //$NON-NLS-1$
                     return null;
                 }
-                
+
                 // Remove full attributes:
                 // Syntax:
                 //    name = "..." quoted string with all but < and "
@@ -709,7 +721,7 @@
                 //   merged with the previous one.
                 // - string with an = sign, optionally followed by a quote (' or "): the user is
                 //   writing the value of the attribute.
-                int pos_equal = text.indexOf('='); 
+                int pos_equal = text.indexOf('=');
                 if (pos_equal == -1) {
                     info.isInValue = false;
                     info.name = text.trim();
@@ -749,7 +761,7 @@
 
         return node;
     }
-    
+
     /**
      * Computes (if needed) and returns the root descriptor.
      */
@@ -758,17 +770,17 @@
             AndroidTargetData data = mEditor.getTargetData();
             if (data != null) {
                 IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
-                
+
                 if (descriptorProvider != null) {
                     mRootDescriptor = new ElementDescriptor("",
                             descriptorProvider.getRootElementDescriptors());
                 }
             }
         }
-        
+
         return mRootDescriptor;
     }
-    
+
     /**
      * Returns the active {@link AndroidEditor} matching this source viewer.
      */
@@ -789,7 +801,7 @@
 
         return null;
     }
-    
-    
+
+
 
 }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/Messages.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/Messages.java
index 9b2803c..1474102 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/Messages.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/Messages.java
@@ -4,7 +4,7 @@
 import org.eclipse.osgi.util.NLS;
 
 public class Messages extends NLS {
-    private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.preferences.messages"; //$NON-NLS-1$
+    private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.internal.preferences.messages"; //$NON-NLS-1$
 
     public static String AndroidPreferencePage_ERROR_Reserved_Char;
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java
index 72ff51d..3deea23 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java
@@ -75,28 +75,28 @@
 
     private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$
     private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$
-    
+
     public final static int INVALID_MIN_SDK = -1;
-    
+
     /**
      * Instrumentation info obtained from manifest
      */
     public static class Instrumentation {
         private final String mName;
         private final String mTargetPackage;
-        
+
         Instrumentation(String name, String targetPackage) {
             mName = name;
             mTargetPackage = targetPackage;
         }
-        
+
         /**
          * Returns the fully qualified instrumentation class name
          */
         public String getName() {
             return mName;
         }
-        
+
         /**
          * Returns the Android app package that is the target of this instrumentation
          */
@@ -104,7 +104,7 @@
             return mTargetPackage;
         }
     }
-    
+
     /**
      * Activity info obtained from the manifest.
      */
@@ -114,32 +114,32 @@
         private boolean mHasAction = false;
         private boolean mHasMainAction = false;
         private boolean mHasLauncherCategory = false;
-        
+
         public Activity(String name, boolean exported) {
             mName = name;
             mIsExported = exported;
         }
-        
+
         public String getName() {
             return mName;
         }
-        
+
         public boolean isExported() {
             return mIsExported;
         }
-        
+
         public boolean hasAction() {
             return mHasAction;
         }
-        
+
         public boolean isHomeActivity() {
             return mHasMainAction && mHasLauncherCategory;
         }
-        
+
         void setHasAction(boolean hasAction) {
             mHasAction = hasAction;
         }
-        
+
         /** If the activity doesn't yet have a filter set for the launcher, this resets both
          * flags. This is to handle multiple intent-filters where one could have the valid
          * action, and another one of the valid category.
@@ -149,16 +149,16 @@
                 mHasMainAction = mHasLauncherCategory = false;
             }
         }
-        
+
         void setHasMainAction(boolean hasMainAction) {
             mHasMainAction = hasMainAction;
         }
-        
+
         void setHasLauncherCategory(boolean hasLauncherCategory) {
             mHasLauncherCategory = hasLauncherCategory;
         }
     }
-    
+
     /**
      * XML error & data handler used when parsing the AndroidManifest.xml file.
      * <p/>
@@ -166,9 +166,9 @@
      * to collect data from the manifest.
      */
     private static class ManifestHandler extends XmlErrorHandler {
-        
+
         //--- data read from the parsing
-        
+
         /** Application package */
         private String mPackage;
         /** List of all activities */
@@ -196,10 +196,10 @@
         private int mValidLevel = 0;
         private Activity mCurrentActivity = null;
         private Locator mLocator;
-        
+
         /**
          * Creates a new {@link ManifestHandler}, which is also an {@link XmlErrorHandler}.
-         *  
+         *
          * @param manifestFile The manifest file being parsed. Can be null.
          * @param errorListener An optional error listener.
          * @param gatherData True if data should be gathered.
@@ -221,24 +221,24 @@
         String getPackage() {
             return mPackage;
         }
-        
-        /** 
+
+        /**
          * Returns the list of activities found in the manifest.
          * @return An array of fully qualified class names, or empty if no activity were found.
          */
         Activity[] getActivities() {
             return mActivities.toArray(new Activity[mActivities.size()]);
         }
-        
+
         /**
          * Returns the name of one activity found in the manifest, that is configured to show
-         * up in the HOME screen.  
-         * @return the fully qualified name of a HOME activity or null if none were found. 
+         * up in the HOME screen.
+         * @return the fully qualified name of a HOME activity or null if none were found.
          */
         Activity getLauncherActivity() {
             return mLauncherActivity;
         }
-        
+
         /**
          * Returns the list of process names declared by the manifest.
          */
@@ -246,42 +246,42 @@
             if (mProcesses != null) {
                 return mProcesses.toArray(new String[mProcesses.size()]);
             }
-            
+
             return new String[0];
         }
-        
+
         /**
          * Returns the <code>debuggable</code> attribute value or null if it is not set.
          */
         Boolean getDebuggable() {
             return mDebuggable;
         }
-        
+
         /**
          * Returns the <code>minSdkVersion</code> attribute, or
-         * {@link AndroidManifestParser#INVALID_MIN_SDK} if it's not set. 
+         * {@link AndroidManifestParser#INVALID_MIN_SDK} if it's not set.
          */
         int getApiLevelRequirement() {
             return mApiLevelRequirement;
         }
-        
-        /** 
+
+        /**
          * Returns the list of instrumentations found in the manifest.
-         * @return An array of {@link Instrumentation}, or empty if no instrumentations were 
+         * @return An array of {@link Instrumentation}, or empty if no instrumentations were
          * found.
          */
         Instrumentation[] getInstrumentations() {
             return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]);
         }
-        
-        /** 
+
+        /**
          * Returns the list of libraries in use found in the manifest.
          * @return An array of library names, or empty if no libraries were found.
          */
         String[] getUsesLibraries() {
             return mLibraries.toArray(new String[mLibraries.size()]);
         }
-        
+
         /* (non-Javadoc)
          * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
          */
@@ -290,7 +290,7 @@
             mLocator = locator;
             super.setDocumentLocator(locator);
         }
-        
+
         /* (non-Javadoc)
          * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
          * java.lang.String, org.xml.sax.Attributes)
@@ -322,18 +322,18 @@
                                 if (value != null) {
                                     addProcessName(value);
                                 }
-                                
+
                                 value = getAttributeValue(attributes, ATTRIBUTE_DEBUGGABLE,
                                         true /* hasNamespace*/);
                                 if (value != null) {
                                     mDebuggable = Boolean.parseBoolean(value);
                                 }
-                                
+
                                 mValidLevel++;
                             } else if (NODE_USES_SDK.equals(localName)) {
                                 value = getAttributeValue(attributes, ATTRIBUTE_MIN_SDK_VERSION,
                                         true /* hasNamespace */);
-                                
+
                                 if (value != null) {
                                     try {
                                         mApiLevelRequirement = Integer.parseInt(value);
@@ -343,7 +343,7 @@
                                 }
                             } else if (NODE_INSTRUMENTATION.equals(localName)) {
                                 processInstrumentationNode(attributes);
-                            }    
+                            }
                             break;
                         case LEVEL_ACTIVITY:
                             if (NODE_ACTIVITY.equals(localName)) {
@@ -364,7 +364,7 @@
                                 if (value != null) {
                                     mLibraries.add(value);
                                 }
-                            }    
+                            }
                             break;
                         case LEVEL_INTENT_FILTER:
                             // only process this level if we are in an activity
@@ -391,7 +391,7 @@
                                         mCurrentActivity.setHasLauncherCategory(true);
                                     }
                                 }
-                                
+
                                 // no need to increase mValidLevel as we don't process anything
                                 // below this level.
                             }
@@ -415,13 +415,13 @@
                 if (mGatherData == false) {
                     return;
                 }
-    
+
                 // decrement the levels.
                 if (mValidLevel == mCurrentLevel) {
                     mValidLevel--;
                 }
                 mCurrentLevel--;
-                
+
                 // if we're at a valid level
                 // process the end of the element
                 if (mValidLevel == mCurrentLevel) {
@@ -442,13 +442,13 @@
                         default:
                             break;
                     }
-    
+
                 }
             } finally {
                 super.endElement(uri, localName, name);
             }
         }
-        
+
         /* (non-Javadoc)
          * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
          */
@@ -478,7 +478,7 @@
                 super.warning(e);
             }
         }
-        
+
         /**
          * Processes the activity node.
          * @param attributes the attributes for the activity node.
@@ -489,14 +489,14 @@
                     true /* hasNamespace */);
             if (activityName != null) {
                 activityName = combinePackageAndClassName(mPackage, activityName);
-                
+
                 // get the exported flag.
                 String exportedStr = getAttributeValue(attributes, ATTRIBUTE_EXPORTED, true);
                 boolean exported = exportedStr == null ||
                         exportedStr.toLowerCase().equals("true"); // $NON-NLS-1$
                 mCurrentActivity = new Activity(activityName, exported);
                 mActivities.add(mCurrentActivity);
-                
+
                 if (mMarkErrors) {
                     checkClass(activityName, AndroidConstants.CLASS_ACTIVITY,
                             true /* testVisibility */);
@@ -506,7 +506,7 @@
                 // so we don't have to do anything
                 mCurrentActivity = null;
             }
-            
+
             String processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
                     true /* hasNamespace */);
             if (processName != null) {
@@ -526,19 +526,19 @@
                     true /* hasNamespace */);
             if (serviceName != null) {
                 serviceName = combinePackageAndClassName(mPackage, serviceName);
-                
+
                 if (mMarkErrors) {
                     checkClass(serviceName, superClassName, false /* testVisibility */);
                 }
             }
-            
+
             String processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
                     true /* hasNamespace */);
             if (processName != null) {
                 addProcessName(processName);
             }
         }
-        
+
         /**
          * Processes the instrumentation nodes.
          * @param attributes the attributes for the activity node.
@@ -563,7 +563,7 @@
         /**
          * Checks that a class is valid and can be used in the Android Manifest.
          * <p/>
-         * Errors are put as {@link IMarker} on the manifest file. 
+         * Errors are put as {@link IMarker} on the manifest file.
          * @param className the fully qualified name of the class to test.
          * @param superClassName the fully qualified name of the class it is supposed to extend.
          * @param testVisibility if <code>true</code>, the method will check the visibility of
@@ -579,12 +579,12 @@
             if (result != BaseProjectHelper.TEST_CLASS_OK) {
                 // get the line number
                 int line = mLocator.getLineNumber();
-                
+
                 // mark the file
                 IMarker marker = BaseProjectHelper.addMarker(getFile(),
                         AndroidConstants.MARKER_ANDROID,
                         result, line, IMarker.SEVERITY_ERROR);
-                
+
                 // add custom attributes to be used by the manifest editor.
                 if (marker != null) {
                     try {
@@ -594,7 +594,7 @@
                     } catch (CoreException e) {
                     }
                 }
-            }           
+            }
         }
 
         /**
@@ -616,21 +616,21 @@
                     return attributes.getValue(i);
                 }
             }
-            
+
             return null;
         }
-        
+
         private void addProcessName(String processName) {
             if (mProcesses == null) {
                 mProcesses = new TreeSet<String>();
             }
-            
+
             mProcesses.add(processName);
         }
     }
 
     private static SAXParserFactory sParserFactory;
-    
+
     private final String mJavaPackage;
     private final Activity[] mActivities;
     private final Activity mLauncherActivity;
@@ -644,14 +644,14 @@
         sParserFactory = SAXParserFactory.newInstance();
         sParserFactory.setNamespaceAware(true);
     }
-    
+
     /**
      * Parses the Android Manifest, and returns an object containing the result of the parsing.
      * <p/>
      * This method is useful to parse a specific {@link IFile} in a Java project.
      * <p/>
      * If you only want to gather data, consider {@link #parseForData(IFile)} instead.
-     * 
+     *
      * @param javaProject The java project.
      * @param manifestFile the {@link IFile} representing the manifest file.
      * @param errorListener
@@ -670,41 +670,42 @@
                 boolean markErrors)
             throws CoreException {
         try {
-            SAXParser parser = sParserFactory.newSAXParser();
+            if (manifestFile != null) {
+                SAXParser parser = sParserFactory.newSAXParser();
 
-            ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
-                    errorListener, gatherData, javaProject, markErrors);
-            parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
-            
-            // get the result from the handler
-            
-            return new AndroidManifestParser(manifestHandler.getPackage(),
-                    manifestHandler.getActivities(),
-                    manifestHandler.getLauncherActivity(),
-                    manifestHandler.getProcesses(),
-                    manifestHandler.getDebuggable(),
-                    manifestHandler.getApiLevelRequirement(),
-                    manifestHandler.getInstrumentations(),
-                    manifestHandler.getUsesLibraries());
+                ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
+                        errorListener, gatherData, javaProject, markErrors);
+                parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
+
+                // get the result from the handler
+                return new AndroidManifestParser(manifestHandler.getPackage(),
+                        manifestHandler.getActivities(),
+                        manifestHandler.getLauncherActivity(),
+                        manifestHandler.getProcesses(),
+                        manifestHandler.getDebuggable(),
+                        manifestHandler.getApiLevelRequirement(),
+                        manifestHandler.getInstrumentations(),
+                        manifestHandler.getUsesLibraries());
+            }
         } catch (ParserConfigurationException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Bad parser configuration for %s: %s",
                     manifestFile.getFullPath(),
                     e.getMessage());
         } catch (SAXException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Parser exception for %s: %s",
                     manifestFile.getFullPath(),
                     e.getMessage());
         } catch (IOException e) {
             // Don't log a console error when failing to read a non-existing file
             if (!(e instanceof FileNotFoundException)) {
-                AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+                AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                         "I/O error for %s: %s",
                         manifestFile.getFullPath(),
                         e.getMessage());
             }
-        } 
+        }
 
         return null;
     }
@@ -716,7 +717,7 @@
      * parsing a file that is not part of an Eclipse Java project.
      * <p/>
      * It assumes errors cannot be marked on the file and that data gathering is enabled.
-     * 
+     *
      * @param manifestFile the manifest file to parse.
      * @return an {@link AndroidManifestParser} or null if the parsing failed.
      * @throws CoreException
@@ -733,11 +734,11 @@
                     null, //javaProject
                     false //markErrors
                     );
-            
+
             parser.parse(new InputSource(new FileReader(manifestFile)), manifestHandler);
-            
+
             // get the result from the handler
-            
+
             return new AndroidManifestParser(manifestHandler.getPackage(),
                     manifestHandler.getActivities(),
                     manifestHandler.getLauncherActivity(),
@@ -747,25 +748,25 @@
                     manifestHandler.getInstrumentations(),
                     manifestHandler.getUsesLibraries());
         } catch (ParserConfigurationException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Bad parser configuration for %s: %s",
                     manifestFile.getAbsolutePath(),
                     e.getMessage());
         } catch (SAXException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Parser exception for %s: %s",
                     manifestFile.getAbsolutePath(),
                     e.getMessage());
         } catch (IOException e) {
             // Don't log a console error when failing to read a non-existing file
             if (!(e instanceof FileNotFoundException)) {
-                AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+                AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                         "I/O error for %s: %s",
                         manifestFile.getAbsolutePath(),
                         e.getMessage());
             }
         }
-        
+
         return null;
     }
 
@@ -788,9 +789,9 @@
                 boolean gatherData,
                 boolean markErrors)
             throws CoreException {
-        
+
         IFile manifestFile = getManifest(javaProject.getProject());
-        
+
         try {
             SAXParser parser = sParserFactory.newSAXParser();
 
@@ -799,25 +800,25 @@
                         errorListener, gatherData, javaProject, markErrors);
 
                 parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
-                
+
                 // get the result from the handler
                 return new AndroidManifestParser(manifestHandler.getPackage(),
                         manifestHandler.getActivities(), manifestHandler.getLauncherActivity(),
                         manifestHandler.getProcesses(), manifestHandler.getDebuggable(),
-                        manifestHandler.getApiLevelRequirement(), 
+                        manifestHandler.getApiLevelRequirement(),
                         manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries());
             }
         } catch (ParserConfigurationException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Bad parser configuration for %s", manifestFile.getFullPath());
         } catch (SAXException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "Parser exception for %s", manifestFile.getFullPath());
         } catch (IOException e) {
-            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), 
+            AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
                     "I/O error for %s", manifestFile.getFullPath());
-        } 
-        
+        }
+
         return null;
     }
 
@@ -849,7 +850,7 @@
 
     /**
      * Parses the manifest file, and collects data.
-     * 
+     *
      * @param osManifestFilePath The OS path of the manifest file to parse.
      * @return an {@link AndroidManifestParser} or null if the parsing failed.
      */
@@ -871,7 +872,7 @@
         return mJavaPackage;
     }
 
-    /** 
+    /**
      * Returns the list of activities found in the manifest.
      * @return An array of {@link Activity}, or empty if no activity were found.
      */
@@ -881,35 +882,35 @@
 
     /**
      * Returns the name of one activity found in the manifest, that is configured to show
-     * up in the HOME screen.  
-     * @return The {@link Activity} representing a HOME activity or null if none were found. 
+     * up in the HOME screen.
+     * @return The {@link Activity} representing a HOME activity or null if none were found.
      */
     public Activity getLauncherActivity() {
         return mLauncherActivity;
     }
-    
+
     /**
      * Returns the list of process names declared by the manifest.
      */
     public String[] getProcesses() {
         return mProcesses;
     }
-    
+
     /**
      * Returns the debuggable attribute value or <code>null</code> if it is not set.
      */
     public Boolean getDebuggable() {
         return mDebuggable;
     }
-    
+
     /**
      * Returns the <code>minSdkVersion</code> attribute, or {@link #INVALID_MIN_SDK}
-     * if it's not set. 
+     * if it's not set.
      */
     public int getApiLevelRequirement() {
         return mApiLevelRequirement;
     }
-    
+
     /**
      * Returns the list of instrumentations found in the manifest.
      * @return An array of {@link Instrumentation}, or empty if no instrumentations were found.
@@ -917,7 +918,7 @@
     public Instrumentation[] getInstrumentations() {
         return mInstrumentations;
     }
-    
+
     /**
      * Returns the list of libraries in use found in the manifest.
      * @return An array of library names, or empty if no uses-library declarations were found.
@@ -926,7 +927,7 @@
         return mLibraries;
     }
 
-    
+
     /**
      * Private constructor to enforce using
      * {@link #parse(IJavaProject, XmlErrorListener, boolean, boolean)},
@@ -977,7 +978,7 @@
      * Combines a java package, with a class value from the manifest to make a fully qualified
      * class name
      * @param javaPackage the java package from the manifest.
-     * @param className the class name from the manifest. 
+     * @param className the class name from the manifest.
      * @return the fully qualified class name.
      */
     public static String combinePackageAndClassName(String javaPackage, String className) {
@@ -1010,8 +1011,8 @@
      * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project
      * package base name (e.g. com.foo), returns the relative activity name that would be used
      * the "name" attribute of an "activity" element.
-     *    
-     * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" 
+     *
+     * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass"
      * @param packageName The project base package name, e.g. "com.foo"
      * @return The relative activity name if it can be computed or the original fullActivityName.
      */
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
index 93c27b7..df716b2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java
@@ -26,6 +26,7 @@
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceDelta;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
@@ -42,7 +43,7 @@
 public final class CompiledResourcesMonitor implements IFileListener, IProjectListener {
 
     private final static CompiledResourcesMonitor sThis = new CompiledResourcesMonitor();
-    
+
     /**
      * Sets up the monitoring system.
      * @param monitor The main Resource Monitor.
@@ -62,12 +63,12 @@
     /* (non-Javadoc)
      * Sent when a file changed : if the file is the R class, then it is parsed again to update
      * the internal data.
-     * 
+     *
      * @param file The file that changed.
      * @param markerDeltas The marker deltas for the file.
      * @param kind The change kind. This is equivalent to
      * {@link IResourceDelta#accept(IResourceDeltaVisitor)}
-     * 
+     *
      * @see IFileListener#fileChanged
      */
     public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
@@ -111,7 +112,7 @@
             // pass
         }
     }
-    
+
     private void loadAndParseRClass(IProject project) {
         try {
             // first check there's a ProjectResources to store the content
@@ -129,13 +130,13 @@
                     return;
                 }
 
-                // create a temporary class loader to load it. 
+                // create a temporary class loader to load it.
                 ProjectClassLoader loader = new ProjectClassLoader(null /* parentClassLoader */,
                         project);
-                
+
                 try {
                     Class<?> clazz = loader.loadClass(className);
-                    
+
                     if (clazz != null) {
                         // create the maps to store the result of the parsing
                         Map<String, Map<String, Integer>> resourceValueMap =
@@ -144,7 +145,7 @@
                             new HashMap<Integer, String[]>();
                         Map<IntArrayWrapper, String> styleableValueToNameMap =
                             new HashMap<IntArrayWrapper, String>();
-                        
+
                         // parse the class
                         if (parseClass(clazz, genericValueToNameMap, styleableValueToNameMap,
                                 resourceValueMap)) {
@@ -180,7 +181,7 @@
 
                 Map<String, Integer> fullMap = new HashMap<String, Integer>();
                 resourceValueMap.put(resType, fullMap);
-                
+
                 for (Field f : inner.getDeclaredFields()) {
                     // only process static final fields.
                     int modifiers = f.getModifiers();
@@ -191,7 +192,7 @@
                             styleableValueToNameMap.put(new IntArrayWrapper((int[]) f.get(null)),
                                     f.getName());
                         } else if (type == int.class) {
-                            Integer value = (Integer) f.get(null); 
+                            Integer value = (Integer) f.get(null);
                             genericValueToNameMap.put(value, new String[] { f.getName(), resType });
                             fullMap.put(f.getName(), value);
                         } else {
@@ -210,16 +211,18 @@
 
     /**
      * Returns the class name of the R class, based on the project's manifest's package.
-     * 
+     *
      * @return A class name (e.g. "my.app.R") or null if there's no valid package in the manifest.
      */
     private String getRClassName(IProject project) {
         try {
             IFile manifestFile = AndroidManifestParser.getManifest(project);
-            AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile);
-            if (data != null) {
-                String javaPackage = data.getPackage();
-                return javaPackage + ".R"; //$NON-NLS-1$
+            if (manifestFile != null && manifestFile.isSynchronized(IResource.DEPTH_ZERO)) {
+                AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile);
+                if (data != null) {
+                    String javaPackage = data.getPackage();
+                    return javaPackage + ".R"; //$NON-NLS-1$
+                }
             }
         } catch (CoreException e) {
             // This will typically happen either because the manifest file is not present
@@ -232,5 +235,5 @@
         }
         return null;
     }
-    
+
 }
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index 236327c..1f5daac 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -205,7 +205,7 @@
         <echo>Uninstalling ${application-package} from the default emulator...</echo>
         <exec executable="${adb}" failonerror="true">
             <arg value="uninstall" />
-            <arg path="${application-package}" />
+            <arg value="${application-package}" />
         </exec>
     </target>
     
diff --git a/tools/sdkmanager/app/src/Android.mk b/tools/sdkmanager/app/src/Android.mk
index b508076..6346349 100644
--- a/tools/sdkmanager/app/src/Android.mk
+++ b/tools/sdkmanager/app/src/Android.mk
@@ -9,7 +9,11 @@
 LOCAL_JAVA_LIBRARIES := \
 	androidprefs \
 	sdklib \
-	sdkuilib
+	sdkuilib \
+	swt \
+	org.eclipse.jface_3.2.0.I20060605-1400 \
+	org.eclipse.equinox.common_3.2.0.v20060603 \
+	org.eclipse.core.commands_3.2.0.I20060605-1400
 LOCAL_MODULE := sdkmanager
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 8c9ad26..26af498 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -29,13 +29,13 @@
 import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
 import com.android.sdklib.internal.project.ProjectCreator;
 import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
+import com.android.sdkmanager.internal.repository.AboutPage;
+import com.android.sdkmanager.internal.repository.SettingsPage;
 import com.android.sdkuilib.repository.UpdaterWindow;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -53,9 +53,6 @@
     private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
     private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
 
-    /** Preference file containing the usb ids for adb */
-    private final static String ADB_INI = "adb_usb.ini";
-
     /** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */
     private String mOsSdkFolder;
     /** Logger object. Use this to print normal output, warnings or errors. */
@@ -243,8 +240,11 @@
     private void showMainWindow() {
         try {
             UpdaterWindow window = new UpdaterWindow(
+                    mSdkLog,
                     mOsSdkFolder,
                     false /*userCanChangeSdkRoot*/);
+            window.registerPage("Settings", SettingsPage.class);
+            window.registerPage("About", AboutPage.class);
             window.open();
         } catch (Exception e) {
             e.printStackTrace();
@@ -758,26 +758,8 @@
      * Updates adb with the USB devices declared in the SDK add-ons.
      */
     private void updateAdb() {
-        FileWriter writer = null;
         try {
-            // get the android prefs location to know where to write the file.
-            File adbIni = new File(AndroidLocation.getFolder(), ADB_INI);
-            writer = new FileWriter(adbIni);
-
-            // first, put all the vendor id in an HashSet to remove duplicate.
-            HashSet<Integer> set = new HashSet<Integer>();
-            IAndroidTarget[] targets = mSdkManager.getTargets();
-            for (IAndroidTarget target : targets) {
-                if (target.getUsbVendorId() != IAndroidTarget.NO_USB_ID) {
-                    set.add(target.getUsbVendorId());
-                }
-            }
-
-            // now write the Id in a text file, one per line.
-            for (Integer i : set) {
-                writer.write(i.toString());
-                writer.write("\n");
-            }
+            mSdkManager.updateAdb();
 
             mSdkLog.printf(
                     "adb has been updated. You must restart adb with the following commands\n" +
@@ -787,14 +769,6 @@
             errorAndExit(e.getMessage());
         } catch (IOException e) {
             errorAndExit(e.getMessage());
-        } finally {
-            if (writer != null) {
-                try {
-                    writer.close();
-                } catch (IOException e) {
-                    // ignore
-                }
-            }
         }
     }
 
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
new file mode 100755
index 0000000..49aad29
--- /dev/null
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
@@ -0,0 +1,72 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkmanager.internal.repository;

+

+

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Label;

+

+/*

+ * TODO list

+ * - Change version to be a constant pulled from somewhere.

+ */

+

+public class AboutPage extends Composite {

+

+    private Label mLabel;

+

+    /**

+     * Create the composite.

+     * @param parent The parent of the composite.

+     */

+    public AboutPage(Composite parent) {

+        super(parent, SWT.BORDER);

+

+        createContents(this);

+

+        postCreate();  //$hide$

+    }

+

+    private void createContents(Composite parent) {

+        parent.setLayout(new GridLayout(1, false));

+

+        mLabel = new Label(parent, SWT.NONE);

+        mLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 1));

+        mLabel.setText("Android SDK Updater.\n\nVersion 0.1.\n\nCopyright (C) 2009 The Android Open Source Project.");

+    }

+

+    @Override

+    protected void checkSubclass() {

+        // Disable the check that prevents subclassing of SWT components

+    }

+

+    // -- Start of internal part ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    /**

+     * Called by the constructor right after {@link #createContents(Composite)}.

+     */

+    private void postCreate() {

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java
new file mode 100755
index 0000000..df72cf9
--- /dev/null
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkmanager.internal.repository;

+

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Button;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Group;

+import org.eclipse.swt.widgets.Label;

+import org.eclipse.swt.widgets.Text;

+

+/*

+ * TODO list

+ * - The window should probably set a callback to be notified when settings are changed.

+ * - Actually use the settings.

+ */

+

+public class SettingsPage extends Composite {

+

+    private Group mProxySettingsGroup;

+    private Group mPlaceholderGroup;

+    private Button mApplyButton;

+    private Label mSomeMoreSettings;

+    private Label mProxyServerLabel;

+    private Label mProxyPortLabel;

+    private Text mProxyServerText;

+    private Text mProxyPortText;

+

+    /**

+     * Create the composite.

+     * @param parent The parent of the composite.

+     */

+    public SettingsPage(Composite parent) {

+        super(parent, SWT.BORDER);

+

+        createContents(this);

+

+        mProxySettingsGroup = new Group(this, SWT.NONE);

+        mProxySettingsGroup.setText("Proxy Settings");

+        mProxySettingsGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

+        mProxySettingsGroup.setLayout(new GridLayout(2, false));

+

+        mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE);

+        mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mProxyServerLabel.setText("HTTP Proxy Server");

+

+        mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER);

+        mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

+

+        mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE);

+        mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mProxyPortLabel.setText("HTTP Proxy Port");

+

+        mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER);

+        mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

+

+        mPlaceholderGroup = new Group(this, SWT.NONE);

+        mPlaceholderGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

+        mPlaceholderGroup.setText("Placeholder");

+        mPlaceholderGroup.setLayout(new GridLayout(1, false));

+

+        mSomeMoreSettings = new Label(mPlaceholderGroup, SWT.NONE);

+        mSomeMoreSettings.setText("Some more settings here");

+

+        mApplyButton = new Button(this, SWT.NONE);

+        mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mApplyButton.setText("Apply");

+

+        postCreate();  //$hide$

+    }

+

+    private void createContents(Composite parent) {

+        parent.setLayout(new GridLayout(1, false));

+    }

+

+    @Override

+    protected void checkSubclass() {

+        // Disable the check that prevents subclassing of SWT components

+    }

+

+    // -- Start of internal part ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    /**

+     * Called by the constructor right after {@link #createContents(Composite)}.

+     */

+    private void postCreate() {

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
index 75b1d65..d988340 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
@@ -24,57 +24,57 @@
 public interface IAndroidTarget extends Comparable<IAndroidTarget> {
 
     /** OS Path to the "android.jar" file. */
-    public static int ANDROID_JAR         = 1;
+    public final static int ANDROID_JAR         = 1;
     /** OS Path to the "framework.aidl" file. */
-    public static int ANDROID_AIDL        = 2;
+    public final static int ANDROID_AIDL        = 2;
     /** OS Path to "images" folder which contains the emulator system images. */
-    public static int IMAGES              = 3;
+    public final static int IMAGES              = 3;
     /** OS Path to the "samples" folder which contains sample projects. */
-    public static int SAMPLES             = 4;
+    public final static int SAMPLES             = 4;
     /** OS Path to the "skins" folder which contains the emulator skins. */
-    public static int SKINS               = 5;
+    public final static int SKINS               = 5;
     /** OS Path to the "templates" folder which contains the templates for new projects. */
-    public static int TEMPLATES           = 6;
+    public final static int TEMPLATES           = 6;
     /** OS Path to the "data" folder which contains data & libraries for the SDK tools. */
-    public static int DATA                = 7;
+    public final static int DATA                = 7;
     /** OS Path to the "attrs.xml" file. */
-    public static int ATTRIBUTES          = 8;
+    public final static int ATTRIBUTES          = 8;
     /** OS Path to the "attrs_manifest.xml" file. */
-    public static int MANIFEST_ATTRIBUTES = 9;
+    public final static int MANIFEST_ATTRIBUTES = 9;
     /** OS Path to the "data/layoutlib.jar" library. */
-    public static int LAYOUT_LIB          = 10;
+    public final static int LAYOUT_LIB          = 10;
     /** OS Path to the "data/res" folder. */
-    public static int RESOURCES           = 11;
+    public final static int RESOURCES           = 11;
     /** OS Path to the "data/fonts" folder. */
-    public static int FONTS               = 12;
+    public final static int FONTS               = 12;
     /** OS Path to the "data/widgets.txt" file. */
-    public static int WIDGETS             = 13;
+    public final static int WIDGETS             = 13;
     /** OS Path to the "data/activity_actions.txt" file. */
-    public static int ACTIONS_ACTIVITY    = 14;
+    public final static int ACTIONS_ACTIVITY    = 14;
     /** OS Path to the "data/broadcast_actions.txt" file. */
-    public static int ACTIONS_BROADCAST   = 15;
+    public final static int ACTIONS_BROADCAST   = 15;
     /** OS Path to the "data/service_actions.txt" file. */
-    public static int ACTIONS_SERVICE     = 16;
+    public final static int ACTIONS_SERVICE     = 16;
     /** OS Path to the "data/categories.txt" file. */
-    public static int CATEGORIES          = 17;
+    public final static int CATEGORIES          = 17;
     /** OS Path to the "sources" folder. */
-    public static int SOURCES             = 18;
+    public final static int SOURCES             = 18;
     /** OS Path to the target specific docs */
-    public static int DOCS                = 19;
+    public final static int DOCS                = 19;
     /** OS Path to the target's version of the aapt tool. */
-    public static int AAPT                = 20;
+    public final static int AAPT                = 20;
     /** OS Path to the target's version of the aidl tool. */
-    public static int AIDL                = 21;
+    public final static int AIDL                = 21;
     /** OS Path to the target's version of the dx too. */
-    public static int DX                  = 22;
+    public final static int DX                  = 22;
     /** OS Path to the target's version of the dx.jar file. */
-    public static int DX_JAR              = 23;
+    public final static int DX_JAR              = 23;
 
     /**
      * Return value for {@link #getUsbVendorId()} meaning no USB vendor IDs are defined by the
      * Android target.
      */
-    public static int NO_USB_ID = 0;
+    public final static int NO_USB_ID = 0;
 
     public interface IOptionalLibrary {
         String getName();
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 557f354..32b9a2e 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -82,18 +82,26 @@
     /** dex.jar file */
     public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$
 
-    /** dx executable */
+    /** dx executable (with extension for the current OS)  */
     public final static String FN_DX = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
             "dx.bat" : "dx"; //$NON-NLS-1$ //$NON-NLS-2$
 
-    /** aapt executable */
+    /** aapt executable (with extension for the current OS)  */
     public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
             "aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$
 
-    /** aidl executable */
+    /** aidl executable (with extension for the current OS)  */
     public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
             "aidl.exe" : "aidl"; //$NON-NLS-1$ //$NON-NLS-2$
 
+    /** adb executable (with extension for the current OS)  */
+    public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+            "adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
+
+    /** emulator executable (with extension for the current OS) */
+    public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+            "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
+
     /* Folder Names for Android Projects . */
 
     /** Resources folder name, i.e. "res". */
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 5827aa1..ebafa77 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -16,15 +16,20 @@
 
 package com.android.sdklib;
 
+import com.android.prefs.AndroidLocation;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -68,6 +73,14 @@
         SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + SdkConstants.FN_DX_JAR,
     };
 
+    /** Preference file containing the usb ids for adb */
+    private final static String ADB_INI_FILE = "adb_usb.ini";
+       //0--------90--------90--------90--------90--------90--------90--------90--------9
+    private final static String ADB_INI_HEADER =
+        "# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.\n" +
+        "# USE 'android update adb' TO GENERATE.\n" +
+        "# 1 USB VENDOR ID PER LINE.\n";
+
     /** the location of the SDK */
     private final String mSdkLocation;
     private IAndroidTarget[] mTargets;
@@ -132,6 +145,38 @@
         return null;
     }
 
+    /**
+     * Updates adb with the USB devices declared in the SDK add-ons.
+     * @throws AndroidLocationException
+     * @throws IOException
+     */
+    public void updateAdb() throws AndroidLocationException, IOException {
+        FileWriter writer = null;
+        try {
+            // get the android prefs location to know where to write the file.
+            File adbIni = new File(AndroidLocation.getFolder(), ADB_INI_FILE);
+            writer = new FileWriter(adbIni);
+
+            // first, put all the vendor id in an HashSet to remove duplicate.
+            HashSet<Integer> set = new HashSet<Integer>();
+            IAndroidTarget[] targets = getTargets();
+            for (IAndroidTarget target : targets) {
+                if (target.getUsbVendorId() != IAndroidTarget.NO_USB_ID) {
+                    set.add(target.getUsbVendorId());
+                }
+            }
+
+            // write file header.
+            writer.write(ADB_INI_HEADER);
+
+            // now write the Id in a text file, one per line.
+            for (Integer i : set) {
+                writer.write(String.format("0x%04x\n", i));
+            }
+        } finally {
+            writer.close();
+        }
+    }
 
     private SdkManager(String sdkLocation) {
         mSdkLocation = sdkLocation;
@@ -375,13 +420,6 @@
 
                 // get the default skin, or take it from the base platform if needed.
                 String defaultSkin = propertyMap.get(ADDON_DEFAULT_SKIN);
-
-                // get the USB ID (if available)
-                int usbVendorId = convertId(propertyMap.get(ADDON_USB_VENDOR));
-                if (usbVendorId != IAndroidTarget.NO_USB_ID) {
-                    target.setUsbVendorId(usbVendorId);
-                }
-
                 if (defaultSkin == null) {
                     if (skins.length == 1) {
                         defaultSkin = skins[1];
@@ -390,6 +428,12 @@
                     }
                 }
 
+                // get the USB ID (if available)
+                int usbVendorId = convertId(propertyMap.get(ADDON_USB_VENDOR));
+                if (usbVendorId != IAndroidTarget.NO_USB_ID) {
+                    target.setUsbVendorId(usbVendorId);
+                }
+
                 target.setSkins(skins, defaultSkin);
 
                 return target;
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
new file mode 100755
index 0000000..bd76a4c
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -0,0 +1,201 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.IAndroidTarget;

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.SdkManager;

+import com.android.sdklib.IAndroidTarget.IOptionalLibrary;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+import java.util.ArrayList;

+

+/**

+ * Represents an add-on XML node in an SDK repository.

+ */

+public class AddonPackage extends Package {

+

+    private final String mVendor;

+    private final String mName;

+    private final int    mApiLevel;

+

+    /** An add-on library. */

+    public static class Lib {

+        private final String mName;

+        private final String mDescription;

+

+        public Lib(String name, String description) {

+            mName = name;

+            mDescription = description;

+        }

+

+        public String getName() {

+            return mName;

+        }

+

+        public String getDescription() {

+            return mDescription;

+        }

+    }

+

+    private final Lib[] mLibs;

+

+    /**

+     * Creates a new add-on package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    AddonPackage(RepoSource source, Node packageNode) {

+        super(source, packageNode);

+        mVendor   = getXmlString(packageNode, SdkRepository.NODE_VENDOR);

+        mName     = getXmlString(packageNode, SdkRepository.NODE_NAME);

+        mApiLevel = getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);

+

+        mLibs = parseLibs(getFirstChild(packageNode, SdkRepository.NODE_LIBS));

+    }

+

+    /**

+     * Creates a new platform package based on an actual {@link IAndroidTarget} (with

+     * {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.

+     * This is used to list local SDK folders.

+     */

+    AddonPackage(IAndroidTarget target) {

+        super(  null,                       //source

+                0,                          //revision

+                target.getDescription(),  //description

+                null,                       //descUrl

+                Os.getCurrentOs(),          //archiveOs

+                Arch.getCurrentArch(),      //archiveArch

+                "",                         //archiveUrl   //$NON-NLS-1$

+                0,                          //archiveSize

+                null                        //archiveChecksum

+                );

+

+        mApiLevel = target.getApiVersionNumber();

+        mName     = target.getName();

+        mVendor   = target.getVendor();

+

+        IOptionalLibrary[] optLibs = target.getOptionalLibraries();

+        if (optLibs == null || optLibs.length == 0) {

+            mLibs = new Lib[0];

+        } else {

+            mLibs = new Lib[optLibs.length];

+            for (int i = 0; i < optLibs.length; i++) {

+                mLibs[i] = new Lib(optLibs[i].getName(), optLibs[i].getDescription());

+            }

+        }

+    }

+

+    /**

+     * Parses a <libs> element.

+     */

+    private Lib[] parseLibs(Node libsNode) {

+        ArrayList<Lib> libs = new ArrayList<Lib>();

+

+        if (libsNode != null) {

+            for(Node child = libsNode.getFirstChild();

+                child != null;

+                child = child.getNextSibling()) {

+

+                if (child.getNodeType() == Node.ELEMENT_NODE &&

+                        SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&

+                        SdkRepository.NODE_LIB.equals(child.getLocalName())) {

+                    libs.add(parseLib(child));

+                }

+            }

+        }

+

+        return libs.toArray(new Lib[libs.size()]);

+    }

+

+    /**

+     * Parses a <lib> element from a <libs> container.

+     */

+    private Lib parseLib(Node libNode) {

+        return new Lib(getXmlString(libNode, SdkRepository.NODE_NAME),

+                       getXmlString(libNode, SdkRepository.NODE_DESCRIPTION));

+    }

+

+    /** Returns the vendor, a string, for add-on packages. */

+    public String getVendor() {

+        return mVendor;

+    }

+

+    /** Returns the name, a string, for add-on packages or for libraries. */

+    public String getName() {

+        return mName;

+    }

+

+    /** Returns the api-level, an int > 0, for platform, add-on and doc packages. */

+    public int getApiLevel() {

+        return mApiLevel;

+    }

+

+    /** Returns the libs defined in this add-on. Can be an empty array but not null. */

+    public Lib[] getLibs() {

+        return mLibs;

+    }

+

+    /** Returns a short description for an {@link IDescription}. */

+    @Override

+    public String getShortDescription() {

+        return String.format("%1$s by %2$s for Android API %3$d",

+                getName(),

+                getVendor(),

+                getApiLevel());

+    }

+

+    /** Returns a long description for an {@link IDescription}. */

+    @Override

+    public String getLongDescription() {

+        return String.format("%1$s.\n%2$s",

+                getShortDescription(),

+                super.getLongDescription());

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * An add-on package is typically installed in SDK/add-ons/"addon-name"-"api-level".

+     * The name needs to be sanitized to be acceptable as a directory name.

+     * However if we can find a different directory under SDK/add-ons that already

+     * has this add-ons installed, we'll use that one.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    @Override

+    public File getInstallFolder(String osSdkRoot) {

+        File addons = new File(osSdkRoot, SdkConstants.FD_ADDONS);

+

+        String name = String.format("%s-%d", getName(), getApiLevel()); // $NON-NLS-1$

+

+        name = name.replaceAll("[^a-zA-Z0-9_-]+", "_");                 // $NON-NLS-1$

+        name = name.replaceAll("_+", "_");                              // $NON-NLS-1$

+

+        File folder = new File(addons, name);

+

+        // TODO find similar existing addon in addons folder

+        return folder;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
new file mode 100755
index 0000000..9686cbd
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
@@ -0,0 +1,273 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import java.security.MessageDigest;

+import java.security.NoSuchAlgorithmException;

+

+

+/**

+ * A {@link Archive} is the base class for "something" that can be downloaded from

+ * the SDK repository -- subclasses include {@link PlatformPackage}, {@link AddonPackage},

+ * {@link DocPackage} and {@link ToolPackage}.

+ * <p/>

+ * A package has some attributes (revision, description) and a list of archives

+ * which represent the downloadable bits.

+ * <p/>

+ * Packages are contained in offered by a {@link RepoSource} (a download site).

+ */

+public class Archive implements IDescription {

+

+    /** The checksum type. */

+    public enum ChecksumType {

+        /** A SHA1 checksum, represented as a 40-hex string. */

+        SHA1("SHA-1");  //$NON-NLS-1$

+

+        private final String mAlgorithmName;

+

+        /**

+         * Constructs a {@link ChecksumType} with the algorigth name

+         * suitable for {@link MessageDigest#getInstance(String)}.

+         * <p/>

+         * These names are officially documented at

+         * http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#MessageDigest

+         */

+        private ChecksumType(String algorithmName) {

+            mAlgorithmName = algorithmName;

+        }

+

+        /**

+         * Returns a new {@link MessageDigest} instance for this checksum type.

+         * @throws NoSuchAlgorithmException if this algorithm is not available.

+         */

+        public MessageDigest getMessageDigest() throws NoSuchAlgorithmException {

+            return MessageDigest.getInstance(mAlgorithmName);

+        }

+    }

+

+    /** The OS that this archive can be downloaded on. */

+    public enum Os {

+        ANY("Any"),

+        LINUX("Linux"),

+        MACOSX("MacOS X"),

+        WINDOWS("Windows");

+

+        private final String mUiName;

+

+        private Os(String uiName) {

+            mUiName = uiName;

+        }

+

+        @Override

+        public String toString() {

+            return mUiName;

+        }

+

+        /**

+         * Returns the current OS as one of the {@link Os} enum values or null.

+         */

+        public static Os getCurrentOs() {

+            String os = System.getProperty("os.name");          //$NON-NLS-1$

+            if (os.startsWith("Mac OS")) {                      //$NON-NLS-1$

+                return Os.MACOSX;

+

+            } else if (os.startsWith("Windows")) {              //$NON-NLS-1$

+                return Os.WINDOWS;

+

+            } else if (os.startsWith("Linux")) {                //$NON-NLS-1$

+                return Os.LINUX;

+            }

+

+            return null;

+        }

+    }

+

+    /** The Architecture that this archive can be downloaded on. */

+    public enum Arch {

+        ANY("Any"),

+        PPC("PowerPC"),

+        X86("x86"),

+        X86_64("x86_64");

+

+        private final String mUiName;

+

+        private Arch(String uiName) {

+            mUiName = uiName;

+        }

+

+        @Override

+        public String toString() {

+            return mUiName;

+        }

+

+        /**

+         * Returns the current architecture as one of the {@link Arch} enum values or null.

+         */

+        public static Arch getCurrentArch() {

+            // Values listed from http://lopica.sourceforge.net/os.html

+            String arch = System.getProperty("os.arch");

+

+            if (arch.equalsIgnoreCase("x86_64") || arch.equalsIgnoreCase("amd64")) {

+                return Arch.X86_64;

+

+            } else if (arch.equalsIgnoreCase("x86")

+                    || arch.equalsIgnoreCase("i386")

+                    || arch.equalsIgnoreCase("i686")) {

+                return Arch.X86;

+

+            } else if (arch.equalsIgnoreCase("ppc") || arch.equalsIgnoreCase("PowerPC")) {

+                return Arch.PPC;

+            }

+

+            return null;

+        }

+    }

+

+    private final Os     mOs;

+    private final Arch   mArch;

+    private final String mUrl;

+    private final long   mSize;

+    private final String mChecksum;

+    private final ChecksumType mChecksumType = ChecksumType.SHA1;

+    private final Package mPackage;

+

+    /**

+     * Creates a new archive.

+     */

+    Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum) {

+        mPackage = pkg;

+        mOs = os;

+        mArch = arch;

+        mUrl = url;

+        mSize = size;

+        mChecksum = checksum;

+    }

+

+    /**

+     * Returns the package that created and owns this archive.

+     * It should generally not be null.

+     */

+    public Package getParentPackage() {

+        return mPackage;

+    }

+

+    /**

+     * Returns the archive size, an int > 0.

+     * Size will be 0 if this a local installed folder of unknown size.

+     */

+    public long getSize() {

+        return mSize;

+    }

+

+    /**

+     * Returns the SHA1 archive checksum, as a 40-char hex.

+     * Can be empty but not null for local installed folders.

+     */

+    public String getChecksum() {

+        return mChecksum;

+    }

+

+    /**

+     * Returns the checksum type, always {@link ChecksumType#SHA1} right now.

+     */

+    public ChecksumType getChecksumType() {

+        return mChecksumType;

+    }

+

+    /**

+     * Returns the download archive URL, either absolute or relative to the repository xml.

+     * For a local installed folder, an URL is frabricated from the folder path.

+     */

+    public String getUrl() {

+        return mUrl;

+    }

+

+    /**

+     * Returns the archive {@link Os} enum.

+     * Can be null for a local installed folder on an unknown OS.

+     */

+    public Os getOs() {

+        return mOs;

+    }

+

+    /**

+     * Returns the archive {@link Arch} enum.

+     * Can be null for a local installed folder on an unknown architecture.

+     */

+    public Arch getArch() {

+        return mArch;

+    }

+

+    /**

+     * Generates a short description for this archive.

+     */

+    public String getShortDescription() {

+        String os;

+        if (mOs == null) {

+            os = "unknown OS";

+        } else if (mOs == Os.ANY) {

+            os = "any OS";

+        } else {

+            os = mOs.toString();

+        }

+

+        String arch = "";

+        if (mArch != null && mArch != Arch.ANY) {

+            arch = mArch.toString();

+        }

+

+        return String.format("Archive for %1$s %2$s", os, arch);

+    }

+

+    /**

+     * Generates a longer description for this archive.

+     */

+    public String getLongDescription() {

+        return String.format("%1$s\nSize: %2$d MiB\nSHA1: %3$s",

+                getShortDescription(),

+                Math.round(getSize() / (1024*1024)),

+                getChecksum());

+    }

+

+    /**

+     * Returns true if this archive can be installed on the current platform.

+     */

+    public boolean isCompatible() {

+        // Check OS

+        Os os = getOs();

+

+        if (os != Os.ANY) {

+            Os os2 = Os.getCurrentOs();

+            if (os2 != os) {

+                return false;

+            }

+        }

+

+        // Check Arch

+        Arch arch = getArch();

+

+        if (arch != Arch.ANY) {

+            Arch arch2 = Arch.getCurrentArch();

+            if (arch2 != arch) {

+                return false;

+            }

+        }

+

+        return true;

+    }

+}

+

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
new file mode 100755
index 0000000..8f07255
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
@@ -0,0 +1,108 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+

+/**

+ * Represents a doc XML node in an SDK repository.

+ */

+public class DocPackage extends Package {

+

+    private final int mApiLevel;

+

+    /**

+     * Creates a new doc package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    DocPackage(RepoSource source, Node packageNode) {

+        super(source, packageNode);

+        mApiLevel = getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0);

+    }

+

+    /**

+     * Manually create a new package with one archive and the given attributes.

+     * This is used to create packages from local directories.

+     */

+    DocPackage(RepoSource source,

+            int apiLevel,

+            int revision,

+            String description,

+            String descUrl,

+            Os archiveOs,

+            Arch archiveArch,

+            String archiveUrl,

+            long archiveSize,

+            String archiveChecksum) {

+        super(source,

+                revision,

+                description,

+                descUrl,

+                archiveOs,

+                archiveArch,

+                archiveUrl,

+                archiveSize,

+                archiveChecksum);

+        mApiLevel = apiLevel;

+    }

+

+    /** Returns the api-level, an int > 0, for platform, add-on and doc packages.

+     *  Can be 0 if this is a local package of unknown api-level. */

+    public int getApiLevel() {

+        return mApiLevel;

+    }

+

+    /** Returns a short description for an {@link IDescription}. */

+    @Override

+    public String getShortDescription() {

+        if (mApiLevel != 0) {

+            return String.format("Documentation for Android SDK, API %1$d", mApiLevel);

+        } else {

+            return String.format("Documentation for Android SDK");

+        }

+    }

+

+    /** Returns a long description for an {@link IDescription}. */

+    @Override

+    public String getLongDescription() {

+        return String.format("%1$s.\n%2$s",

+                getShortDescription(),

+                super.getLongDescription());

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * A "doc" package should always be located in SDK/docs.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    @Override

+    public File getInstallFolder(String osSdkRoot) {

+        return new File(osSdkRoot, SdkConstants.FD_DOCS);

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IDescription.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IDescription.java
new file mode 100755
index 0000000..7af92e2
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IDescription.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+/**

+ * Interface for elements that can provide a description of themselves.

+ */

+public interface IDescription {

+

+    /**

+     * Returns a description of the given element. Cannot be null.

+     * <p/>

+     * A description is a multi-line of text, typically much more

+     * elaborate than what {@link #toString()} would provide.

+     */

+    public abstract String getShortDescription();

+

+    /**

+     * Returns a description of the given element. Cannot be null.

+     * <p/>

+     * A description is a multi-line of text, typically much more

+     * elaborate than what {@link #toString()} would provide.

+     */

+    public abstract String getLongDescription();

+

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITask.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITask.java
new file mode 100755
index 0000000..9178460
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITask.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+

+/**

+ * A task that executes and can update a monitor to display its status.

+ * The task will generally be run in a separate thread.

+ */

+public interface ITask {

+    public abstract void run(ITaskMonitor monitor);

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java
new file mode 100755
index 0000000..540825c
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskFactory.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+/**

+ * A factory that can start and run new {@link ITask}s.

+ */

+public interface ITaskFactory {

+

+    public abstract void start(String title, ITask task);

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
new file mode 100755
index 0000000..05c982d
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+

+/**

+ * A monitor interface for a {@link ITask}.

+ * <p/>

+ * Depending on the task factory that created the task, there might not be any UI

+ * or it might not implement all the methods, in which case calling them would be

+ * a no-op but is guaranteed not to crash.

+ * <p/>

+ * If the task runs in a non-UI worker thread, the task factory implementation

+ * will take care of the update the UI in the correct thread. The task itself

+ * must not have to deal with it.

+ */

+public interface ITaskMonitor {

+

+    /**

+     * Sets the description in the current task dialog.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void setDescription(String descriptionFormat, Object...args);

+

+    /**

+     * Sets the result text in the current task dialog.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void setResult(String resultFormat, Object...args);

+

+    /**

+     * Sets the max value of the progress bar.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void setProgressMax(int max);

+

+    /**

+     * Increments the current value of the progress bar.

+     * This method can be invoked from a non-UI thread.

+     */

+    public void incProgress(int delta);

+

+    /**

+     * Returns true if the user requested to cancel the operation.

+     * It is up to the task thread to pool this and exit as soon

+     * as possible.

+     */

+    public boolean cancelRequested();

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
new file mode 100755
index 0000000..f150510
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -0,0 +1,415 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.IAndroidTarget;

+import com.android.sdklib.ISdkLog;

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.SdkManager;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Node;

+import org.xml.sax.InputSource;

+import org.xml.sax.SAXException;

+

+import java.io.BufferedReader;

+import java.io.File;

+import java.io.FileReader;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.StringReader;

+import java.net.MalformedURLException;

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.Set;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+import javax.xml.XMLConstants;

+import javax.xml.parsers.DocumentBuilder;

+import javax.xml.parsers.DocumentBuilderFactory;

+import javax.xml.parsers.ParserConfigurationException;

+import javax.xml.transform.stream.StreamSource;

+import javax.xml.validation.Schema;

+import javax.xml.validation.SchemaFactory;

+import javax.xml.validation.Validator;

+

+/**

+ * Scans a local SDK to find which packages are currently installed.

+ */

+public class LocalSdkParser {

+

+    private static final String SOURCE_XML = "source.xml";  //$NON-NLS-1$ // TODO move to global constants

+    private Package[] mPackages;

+

+    public LocalSdkParser() {

+        // TODO Auto-generated constructor stub

+    }

+

+    /**

+     * Returns the packages found by the last call to {@link #parseSdk(String)}.

+     */

+    public Package[] getPackages() {

+        return mPackages;

+    }

+

+    /**

+     * Clear the internal packages list. After this call, {@link #getPackages()} will return

+     * null till {@link #parseSdk(String)} is called.

+     */

+    public void clearPackages() {

+        mPackages = null;

+    }

+

+    /**

+     * Scan the give SDK to find all the packages already installed at this location.

+     * <p/>

+     * Store the packages internally. You can use {@link #getPackages()} to retrieve them

+     * at any time later.

+     *

+     * @param osSdkRoot The path to the SDK folder.

+     * @return The packages found. Can be retrieved later using {@link #getPackages()}.

+     */

+    public Package[] parseSdk(String osSdkRoot) {

+        ArrayList<Package> packages = new ArrayList<Package>();

+

+        Package pkg = scanDoc(new File(osSdkRoot, SdkConstants.FD_DOCS));

+        if (pkg != null) {

+            packages.add(pkg);

+        }

+

+        pkg = scanTools(new File(osSdkRoot, SdkConstants.FD_TOOLS));

+        if (pkg != null) {

+            packages.add(pkg);

+        }

+

+        // for platforms and add-ons, rely on the SdkManager parser

+        SdkManager sdkman = SdkManager.createManager(osSdkRoot, new ISdkLog() {

+            // A dummy sdk logger that doesn't log anything.

+            public void error(Throwable t, String errorFormat, Object... args) {

+                // pass

+            }

+            public void printf(String msgFormat, Object... args) {

+                // pass

+            }

+            public void warning(String warningFormat, Object... args) {

+                // pass

+            }

+        });

+

+        for(IAndroidTarget target : sdkman.getTargets()) {

+            pkg = null;

+

+            if (target.isPlatform()) {

+                pkg = parseXml(new File(target.getLocation(), SOURCE_XML),

+                               SdkRepository.NODE_PLATFORM);

+                if (pkg == null) {

+                    pkg = new PlatformPackage(target);

+                }

+

+            } else {

+                pkg = parseXml(new File(target.getLocation(), SOURCE_XML),

+                                        SdkRepository.NODE_ADD_ON);

+

+                if (pkg == null) {

+                    pkg = new AddonPackage(target);

+                }

+            }

+

+            if (pkg != null) {

+                packages.add(pkg);

+            }

+        }

+

+        mPackages = packages.toArray(new Package[packages.size()]);

+        return mPackages;

+    }

+

+    /**

+     * Try to find a tools package at the given location.

+     * Returns null if not found.

+     */

+    private Package scanTools(File toolFolder) {

+        // Can we find a source.xml?

+        Package pkg = parseXml(new File(toolFolder, SOURCE_XML), SdkRepository.NODE_TOOL);

+

+        // We're not going to check that all tools are present. At the very least

+        // we should expect to find adb, android and an emulator adapted to the current OS.

+        Set<String> names = new HashSet<String>();

+        for (File file : toolFolder.listFiles()) {

+            names.add(file.getName());

+        }

+        if (!names.contains(SdkConstants.FN_ADB) ||

+                !names.contains(SdkConstants.androidCmdName()) ||

+                !names.contains(SdkConstants.FN_EMULATOR)) {

+            return null;

+        }

+

+        // if we don't have the package info, make one up

+        if (pkg == null) {

+            pkg = new ToolPackage(

+                    null,                       //source

+                    0,                          //revision

+                    "Tools",                    //description

+                    null,                       //descUrl

+                    Os.getCurrentOs(),          //archiveOs

+                    Arch.getCurrentArch(),      //archiveArch

+                    "",                         //archiveUrl   //$NON-NLS-1$

+                    0,                          //archiveSize

+                    null                        //archiveChecksum

+                    );

+        }

+

+        return pkg;

+    }

+

+    /**

+     * Try to find a docs package at the given location.

+     * Returns null if not found.

+     */

+    private Package scanDoc(File docFolder) {

+        // Can we find a source.xml?

+        Package pkg = parseXml(new File(docFolder, SOURCE_XML), SdkRepository.NODE_DOC);

+

+        // To start with, a doc folder should have an "index.html" to be acceptable.

+        String html = readFile(new File(docFolder, "index.html"));

+        if (html != null) {

+            // Try to find something that looks like this line:

+            //   <a href="./sdk/1.5_r1/index.html">

+            // We should find one or more of these and we want the highest version

+            // and release numbers. Note that unfortunately that doesn't give us

+            // the api-level we care about for the doc package.

+

+            String found = null;

+            Pattern re = Pattern.compile(

+                    "<a\\s+href=\"./sdk/(\\d\\.\\d_r\\d)/index.html\">",

+                    Pattern.DOTALL);

+            Matcher m = re.matcher(html);

+            while(m.find()) {

+                String v = m.group(1);

+                if (found == null || v.compareTo(found) == 1) {

+                    found = v;

+                }

+            }

+

+            if (found == null) {

+                // That doesn't look like a doc folder.

+                return null;

+            }

+

+            // We found the line, so it seems like an SDK doc.

+            // Create a pkg if we don't have one yet.

+

+            if (pkg == null) {

+                String url = null;

+                try {

+                    url = docFolder.toURI().toURL().toString();

+                } catch (MalformedURLException e) {

+                    // ignore

+                }

+

+                pkg = new DocPackage(

+                        null,                       //source

+                        0,                          //apiLevel

+                        0,                          //revision

+                        String.format("Documentation for %1$s", found),     //description

+                        null,                       //descUrl

+                        Os.getCurrentOs(),          //archiveOs

+                        Arch.getCurrentArch(),      //archiveArch

+                        url,                        //archiveUrl

+                        0,                          //archiveSize

+                        null                        //archiveChecksum

+                        );

+            }

+        }

+

+        return pkg;

+    }

+

+    /**

+     * Parses the given XML file for the specific element filter.

+     * The element must one of the package type local names: doc, tool, platform or addon.

+     * Returns null if no such package was found.

+     */

+    private Package parseXml(File sourceXmlFile, String elementFilter) {

+

+        String xml = readFile(sourceXmlFile);

+        if (xml != null) {

+            if (validateXml(xml)) {

+                return parsePackages(xml, elementFilter);

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Parses the given XML to find the specific element filter.

+     * The element must one of the package type local names: doc, tool, platform or addon.

+     * Returns null if no such package was found.

+     */

+    private Package parsePackages(String xml, String elementFilter) {

+

+        try {

+            Document doc = getDocument(xml);

+

+            Node root = getFirstChild(doc, SdkRepository.NODE_SDK_REPOSITORY);

+            if (root != null) {

+

+                for (Node child = root.getFirstChild();

+                     child != null;

+                     child = child.getNextSibling()) {

+                    if (child.getNodeType() == Node.ELEMENT_NODE &&

+                            SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&

+                            elementFilter.equals(child.getLocalName())) {

+                        String name = child.getLocalName();

+                        Package p = null;

+

+                        try {

+                            if (SdkRepository.NODE_ADD_ON.equals(name)) {

+                                return new AddonPackage(null /*source*/, child);

+

+                            } else if (SdkRepository.NODE_PLATFORM.equals(name)) {

+                                return new PlatformPackage(null /*source*/, child);

+

+                            } else if (SdkRepository.NODE_DOC.equals(name)) {

+                                return new DocPackage(null /*source*/, child);

+

+                            } else if (SdkRepository.NODE_TOOL.equals(name)) {

+                                return new ToolPackage(null /*source*/, child);

+                            }

+                        } catch (Exception e) {

+                            // Ignore invalid packages

+                        }

+                    }

+                }

+            }

+

+        } catch (Exception e) {

+            // ignore

+        }

+

+        return null;

+    }

+

+    /**

+     * Reads a file as a string.

+     * Returns null if the file could not be read.

+     */

+    private String readFile(File sourceXmlFile) {

+        FileReader fr = null;

+        try {

+            fr = new FileReader(sourceXmlFile);

+            BufferedReader br = new BufferedReader(fr);

+            StringBuilder dest = new StringBuilder();

+            char[] buf = new char[65536];

+            int n;

+            while ((n = br.read(buf)) > 0) {

+                if (n > 0) {

+                    dest.append(buf, 0, n);

+                }

+            }

+            return dest.toString();

+

+        } catch (IOException e) {

+            // ignore

+

+        } finally {

+            if (fr != null) {

+                try {

+                    fr.close();

+                } catch (IOException e) {

+                    // ignore

+                }

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Validates this XML against the SDK Repository schema.

+     * Returns true if the XML was correctly validated.

+     */

+    private boolean validateXml(String xml) {

+

+        try {

+            Validator validator = getValidator();

+            validator.validate(new StreamSource(new StringReader(xml)));

+            return true;

+

+        } catch (SAXException e) {

+            // ignore

+

+        } catch (IOException e) {

+            // ignore

+        }

+

+        return false;

+    }

+

+    /**

+     * Helper method that returns a validator for our XSD

+     */

+    private Validator getValidator() throws SAXException {

+        InputStream xsdStream = SdkRepository.getXsdStream();

+        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

+

+        // This may throw a SAX Exception if the schema itself is not a valid XSD

+        Schema schema = factory.newSchema(new StreamSource(xsdStream));

+

+        Validator validator = schema.newValidator();

+

+        return validator;

+    }

+

+    /**

+     * Returns the first child element with the given XML local name.

+     * If xmlLocalName is null, returns the very first child element.

+     */

+    private Node getFirstChild(Node node, String xmlLocalName) {

+

+        for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {

+            if (child.getNodeType() == Node.ELEMENT_NODE &&

+                    SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {

+                if (xmlLocalName == null || child.getLocalName().equals(xmlLocalName)) {

+                    return child;

+                }

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Takes an XML document as a string as parameter and returns a DOM for it.

+     */

+    private Document getDocument(String xml)

+            throws ParserConfigurationException, SAXException, IOException {

+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

+        factory.setIgnoringComments(true);

+        factory.setNamespaceAware(true);

+

+        DocumentBuilder builder = factory.newDocumentBuilder();

+        Document doc = builder.parse(new InputSource(new StringReader(xml)));

+

+        return doc;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
new file mode 100755
index 0000000..55ecaef
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
@@ -0,0 +1,278 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+import java.util.ArrayList;

+

+/**

+ * A {@link Package} is the base class for "something" that can be downloaded from

+ * the SDK repository -- subclasses include {@link PlatformPackage}, {@link AddonPackage},

+ * {@link DocPackage} and {@link ToolPackage}.

+ * <p/>

+ * A package has some attributes (revision, description) and a list of archives

+ * which represent the downloadable bits.

+ * <p/>

+ * Packages are contained by a {@link RepoSource} (a download site).

+ * <p/>

+ * Derived classes must implement the {@link IDescription} methods.

+ */

+public abstract class Package implements IDescription {

+

+    private final int mRevision;

+    private final String mDescription;

+    private final String mDescUrl;

+    private final Archive[] mArchives;

+    private final RepoSource mSource;

+

+    /**

+     * Creates a new package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    Package(RepoSource source, Node packageNode) {

+        mSource = source;

+        mRevision    = getXmlInt   (packageNode, SdkRepository.NODE_REVISION, 0);

+        mDescription = getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);

+        mDescUrl     = getXmlString(packageNode, SdkRepository.NODE_DESC_URL);

+

+        mArchives = parseArchives(getFirstChild(packageNode, SdkRepository.NODE_ARCHIVES));

+    }

+

+    /**

+     * Manually create a new package with one archive and the given attributes.

+     * This is used to create packages from local directories.

+     */

+    public Package(RepoSource source,

+            int revision,

+            String description,

+            String descUrl,

+            Os archiveOs,

+            Arch archiveArch,

+            String archiveUrl,

+            long archiveSize,

+            String archiveChecksum) {

+        mSource = source;

+        mRevision = revision;

+        mDescription = description;

+        mDescUrl = descUrl;

+        mArchives = new Archive[1];

+        mArchives[0] = new Archive(this,

+                archiveOs,

+                archiveArch,

+                archiveUrl,

+                archiveSize,

+                archiveChecksum);

+    }

+

+    /**

+     * Parses an XML node to process the <archives> element.

+     */

+    private Archive[] parseArchives(Node archivesNode) {

+        ArrayList<Archive> archives = new ArrayList<Archive>();

+

+        if (archivesNode != null) {

+            for(Node child = archivesNode.getFirstChild();

+                child != null;

+                child = child.getNextSibling()) {

+

+                if (child.getNodeType() == Node.ELEMENT_NODE &&

+                        SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&

+                        SdkRepository.NODE_ARCHIVE.equals(child.getLocalName())) {

+                    archives.add(parseArchive(child));

+                }

+            }

+        }

+

+        return archives.toArray(new Archive[archives.size()]);

+    }

+

+    /**

+     * Parses one <archive> element from an <archives> container.

+     */

+    private Archive parseArchive(Node archiveNode) {

+        Archive a = new Archive(

+                    this,

+                    (Os)   getEnumAttribute(archiveNode, SdkRepository.ATTR_OS,

+                            Os.values(), null),

+                    (Arch) getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH,

+                            Arch.values(), Arch.ANY),

+                    getXmlString(archiveNode, SdkRepository.NODE_URL),

+                    getXmlLong(archiveNode, SdkRepository.NODE_SIZE, 0),

+                    getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM)

+                );

+

+        return a;

+    }

+

+    /**

+     * Returns the source that created (and owns) this package. Can be null.

+     */

+    public RepoSource getParentSource() {

+        return mSource;

+    }

+

+    /**

+     * Returns the revision, an int > 0, for all packages (platform, add-on, tool, doc).

+     * Can be 0 if this is a local package of unknown revision.

+     */

+    public int getRevision() {

+        return mRevision;

+    }

+

+    /**

+     * Returns the optional description for all packages (platform, add-on, tool, doc) or

+     * for a lib. Can be empty but not null.

+     */

+    public String getDescription() {

+        return mDescription;

+    }

+

+    /**

+     * Returns the optional description URL for all packages (platform, add-on, tool, doc).

+     * Can be empty but not null.

+     */

+    public String getDescUrl() {

+        return mDescUrl;

+    }

+

+    /**

+     * Returns the archives defined in this package.

+     * Can be an empty array but not null.

+     */

+    public Archive[] getArchives() {

+        return mArchives;

+    }

+

+    /**

+     * Returns a short description for an {@link IDescription}.

+     * Can be empty but not null.

+     */

+    public abstract String getShortDescription();

+

+    /**

+     * Returns a long description for an {@link IDescription}.

+     * Can be empty but not null.

+     */

+    public String getLongDescription() {

+        return String.format("%1$s\nRevision %2$d", getDescription(), getRevision());

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * Some types of packages install in a fix location, for example docs and tools.

+     * In this case the returned folder may already exist with a different archive installed

+     * at the desired location.

+     * For other packages types, such as add-on or platform, the folder name is only partially

+     * relevant to determine the content and thus a real check will be done to provide an

+     * existing or new folder depending on the current content of the SDK.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    public abstract File getInstallFolder(String osSdkRoot);

+

+    //---

+

+    /**

+     * Returns the first child element with the given XML local name.

+     * If xmlLocalName is null, returns the very first child element.

+     */

+    protected static Node getFirstChild(Node node, String xmlLocalName) {

+

+        for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {

+            if (child.getNodeType() == Node.ELEMENT_NODE &&

+                    SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {

+                if (xmlLocalName == null || xmlLocalName.equals(child.getLocalName())) {

+                    return child;

+                }

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Retrieves the value of that XML element as a string.

+     * Returns an empty string when the element is missing.

+     */

+    protected static String getXmlString(Node node, String xmlLocalName) {

+        Node child = getFirstChild(node, xmlLocalName);

+

+        return child == null ? "" : child.getTextContent();  //$NON-NLS-1$

+    }

+

+    /**

+     * Retrieves the value of that XML element as an integer.

+     * Returns the default value when the element is missing or is not an integer.

+     */

+    protected static int getXmlInt(Node node, String xmlLocalName, int defaultValue) {

+        String s = getXmlString(node, xmlLocalName);

+        try {

+            return Integer.parseInt(s);

+        } catch (NumberFormatException e) {

+            return defaultValue;

+        }

+    }

+

+    /**

+     * Retrieves the value of that XML element as a long.

+     * Returns the default value when the element is missing or is not an integer.

+     */

+    protected static long getXmlLong(Node node, String xmlLocalName, long defaultValue) {

+        String s = getXmlString(node, xmlLocalName);

+        try {

+            return Long.parseLong(s);

+        } catch (NumberFormatException e) {

+            return defaultValue;

+        }

+    }

+

+    /**

+     * Retrieve an attribute which value must match one of the given enums using a

+     * case-insensitive name match.

+     *

+     * Returns defaultValue if the attribute does not exist or its value does not match

+     * the given enum values.

+     */

+    private Object getEnumAttribute(

+            Node archiveNode,

+            String attrName,

+            Object[] values,

+            Object defaultValue) {

+

+        Node attr = archiveNode.getAttributes().getNamedItem(attrName);

+        if (attr != null) {

+            String found = attr.getNodeValue();

+            for (Object value : values) {

+                if (value.toString().equalsIgnoreCase(found)) {

+                    return value;

+                }

+            }

+        }

+

+        return defaultValue;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
new file mode 100755
index 0000000..0d51c58
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
@@ -0,0 +1,114 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.IAndroidTarget;

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.SdkManager;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+

+/**

+ * Represents a platform XML node in an SDK repository.

+ */

+public class PlatformPackage extends Package {

+

+    private final String mVersion;

+    private final int mApiLevel;

+

+    /**

+     * Creates a new platform package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    PlatformPackage(RepoSource source, Node packageNode) {

+        super(source, packageNode);

+        mVersion  = getXmlString(packageNode, SdkRepository.NODE_VERSION);

+        mApiLevel = getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);

+    }

+

+    /**

+     * Creates a new platform package based on an actual {@link IAndroidTarget} (with

+     * must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.

+     * This is used to list local SDK folders.

+     */

+    PlatformPackage(IAndroidTarget target) {

+        super(  null,                       //source

+                0,                          //revision

+                target.getDescription(),  //description

+                null,                       //descUrl

+                Os.getCurrentOs(),          //archiveOs

+                Arch.getCurrentArch(),      //archiveArch

+                "",                         //archiveUrl   //$NON-NLS-1$

+                0,                          //archiveSize

+                null                        //archiveChecksum

+                );

+

+        mApiLevel = target.getApiVersionNumber();

+        mVersion  = target.getApiVersionName();

+    }

+

+    /** Returns the version, a string, for platform packages. */

+    public String getVersion() {

+        return mVersion;

+    }

+

+    /** Returns the api-level, an int > 0, for platform, add-on and doc packages. */

+    public int getApiLevel() {

+        return mApiLevel;

+    }

+

+    /** Returns a short description for an {@link IDescription}. */

+    @Override

+    public String getShortDescription() {

+        return String.format("SDK Platform Android %1$s, API %2$d",

+                getVersion(),

+                getApiLevel());

+    }

+

+    /** Returns a long description for an {@link IDescription}. */

+    @Override

+    public String getLongDescription() {

+        return String.format("%1$s.\n%2$s",

+                getShortDescription(),

+                super.getLongDescription());

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * A platform package is typically installed in SDK/platforms/android-"version".

+     * However if we can find a different directory under SDK/platform that already

+     * has this platform version installed, we'll use that one.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    @Override

+    public File getInstallFolder(String osSdkRoot) {

+        File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);

+        File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$

+        // TODO find similar existing platform in platforms folder

+        return folder;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSource.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
similarity index 62%
rename from tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSource.java
rename to tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
index d2f65de..1fd880f 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSource.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
@@ -1,11 +1,11 @@
 /*

  * Copyright (C) 2009 The Android Open Source Project

  *

- * Licensed under the Eclipse Public License, Version 1.0 (the "License");

+ * 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.eclipse.org/org/documents/epl-v10.php

+ *      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,

@@ -14,12 +14,10 @@
  * limitations under the License.

  */

 

-package com.android.sdkuilib.repository;

+package com.android.sdklib.internal.repository;

 

 import com.android.sdklib.repository.SdkRepository;

-import com.android.sdkuilib.repository.ProgressTask.ThreadTask;

 

-import org.eclipse.swt.widgets.Shell;

 import org.w3c.dom.Document;

 import org.w3c.dom.Node;

 import org.xml.sax.InputSource;

@@ -43,14 +41,16 @@
 import javax.xml.validation.Validator;

 

 /**

- *

+ * An sdk-repository source, i.e. a download site.

+ * It may be a full repository or an add-on only repository.

+ * A repository describes one or {@link Package}s available for download.

  */

-class RepoSource {

+public class RepoSource implements IDescription {

 

     private final String mUrl;

     private final boolean mAddonOnly;

 

-    private ArrayList<String> mPackages;

+    private Package[] mPackages;

     private String mDescription;

 

     /**

@@ -59,11 +59,7 @@
     public RepoSource(String url, boolean addonOnly) {

         mUrl = url;

         mAddonOnly = addonOnly;

-    }

-

-    @Override

-    public String toString() {

-        return mUrl;

+        setDefaultDescription();

     }

 

     /** Returns the URL of the source repository. */

@@ -72,32 +68,47 @@
     }

 

     /**

-     * Returns the list of known packages. This is null when the source hasn't been loaded yet.

+     * Returns the list of known packages found by the last call to {@link #load(ITaskFactory)}.

+     * This is null when the source hasn't been loaded yet.

      */

-    public ArrayList<String> getPackages() {

+    public Package[] getPackages() {

         return mPackages;

     }

 

-    public String getDescription() {

+    /**

+     * Clear the internal packages list. After this call, {@link #getPackages()} will return

+     * null till {@link #load(ITaskFactory)} is called.

+     */

+    public void clearPackages() {

+        mPackages = null;

+    }

+

+    public String getShortDescription() {

+        return mUrl;

+    }

+

+    public String getLongDescription() {

         return mDescription == null ? "" : mDescription;  //$NON-NLS-1$

     }

 

     /**

      * Tries to fetch the repository index for the given URL.

      */

-    public void load(Shell shell) {

+    public void load(ITaskFactory taskFactory) {

 

-        ProgressTask.start(shell, "Init SDK Updater", new ThreadTask() {

-            public void PerformTask(ITaskMonitor monitor) {

+        taskFactory.start("Init SDK Updater", new ITask() {

+            public void run(ITaskMonitor monitor) {

                 monitor.setProgressMax(4);

 

-                monitor.setDescription(String.format("Fetching %1$s", mUrl));

+                setDefaultDescription();

+

+                monitor.setDescription("Fetching %1$s", mUrl);

                 monitor.incProgress(1);

 

                 String xml = fetchUrl(mUrl, monitor);

 

                 if (xml == null) {

-                    mDescription = String.format("Failed to fetch URL %1$s", mUrl);

+                    mDescription += String.format("\nFailed to fetch URL %1$s", mUrl);

                     return;

                 }

 

@@ -105,13 +116,20 @@
                 monitor.incProgress(1);

 

                 if (!validateXml(xml, monitor)) {

-                    mDescription = String.format("Failed to validate XML at %1$s", mUrl);

+                    mDescription += String.format("\nFailed to validate XML at %1$s", mUrl);

                     return;

                 }

 

                 monitor.setDescription("Parse XML");

                 monitor.incProgress(1);

                 parsePackages(xml, monitor);

+                if (mPackages.length == 0) {

+                    mDescription += "\nNo packages found.";

+                } else if (mPackages.length == 1) {

+                    mDescription += "\nOne package found.";

+                } else {

+                    mDescription += String.format("\n%1$d packages found.", mPackages.length);

+                }

 

                 // done

                 monitor.incProgress(1);

@@ -119,7 +137,18 @@
         });

     }

 

-    /*

+    private void setDefaultDescription() {

+        if (mAddonOnly) {

+            mDescription = String.format("Add-on Source: %1$s", mUrl);

+        } else {

+            mDescription = String.format("SDK Source: %1$s", mUrl);

+        }

+    }

+

+    /**

+     * Fetches the document at the given URL and returns it as a string.

+     * Returns null if anything wrong happens and write errors to the monitor.

+     *

      * References:

      * Java URL Connection: http://java.sun.com/docs/books/tutorial/networking/urls/readingWriting.html

      * Java URL Reader: http://java.sun.com/docs/books/tutorial/networking/urls/readingURL.html

@@ -169,6 +198,10 @@
         return null;

     }

 

+    /**

+     * Validates this XML against the SDK Repository schema.

+     * Returns true if the XML was correctly validated.

+     */

     private boolean validateXml(String xml, ITaskMonitor monitor) {

 

         try {

@@ -186,7 +219,9 @@
         return false;

     }

 

-    /** Helper method that returns a validator for our XSD */

+    /**

+     * Helper method that returns a validator for our XSD

+     */

     private Validator getValidator() throws SAXException {

         InputStream xsdStream = SdkRepository.getXsdStream();

         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

@@ -200,6 +235,10 @@
     }

 

 

+    /**

+     * Parse all packages defined in the SDK Repository XML and creates

+     * a new mPackages array with them.

+     */

     private boolean parsePackages(String xml, ITaskMonitor monitor) {

 

         try {

@@ -208,32 +247,42 @@
             Node root = getFirstChild(doc, SdkRepository.NODE_SDK_REPOSITORY);

             if (root != null) {

 

-                mPackages = new ArrayList<String>();

+                ArrayList<Package> packages = new ArrayList<Package>();

 

                 for (Node child = root.getFirstChild();

                      child != null;

                      child = child.getNextSibling()) {

                     if (child.getNodeType() == Node.ELEMENT_NODE &&

-                            child.getNamespaceURI().equals(SdkRepository.NS_SDK_REPOSITORY)) {

+                            SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {

                         String name = child.getLocalName();

-                        if (SdkRepository.NODE_ADD_ON.equals(name)) {

-                            parseAddon(child, mPackages, monitor);

+                        Package p = null;

 

-                        } else if (!mAddonOnly) {

-                            if (SdkRepository.NODE_PLATFORM.equals(name)) {

-                                parsePlatform(child, mPackages, monitor);

+                        try {

+                            if (SdkRepository.NODE_ADD_ON.equals(name)) {

+                                p = new AddonPackage(this, child);

 

-                            } else if (SdkRepository.NODE_DOC.equals(name)) {

-                                parseDoc(child, mPackages, monitor);

-

-                            } else if (SdkRepository.NODE_TOOL.equals(name)) {

-                                parseTool(child, mPackages, monitor);

-

+                            } else if (!mAddonOnly) {

+                                if (SdkRepository.NODE_PLATFORM.equals(name)) {

+                                    p = new PlatformPackage(this, child);

+                                } else if (SdkRepository.NODE_DOC.equals(name)) {

+                                    p = new DocPackage(this, child);

+                                } else if (SdkRepository.NODE_TOOL.equals(name)) {

+                                    p = new ToolPackage(this, child);

+                                }

                             }

+

+                            if (p != null) {

+                                packages.add(p);

+                                monitor.setDescription("Found %1$s", p.getShortDescription());

+                            }

+                        } catch (Exception e) {

+                            // Ignore invalid packages

                         }

                     }

                 }

 

+                mPackages = packages.toArray(new Package[packages.size()]);

+

                 return true;

             }

 

@@ -250,11 +299,15 @@
         return false;

     }

 

+    /**

+     * Returns the first child element with the given XML local name.

+     * If xmlLocalName is null, returns the very first child element.

+     */

     private Node getFirstChild(Node node, String xmlLocalName) {

 

         for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {

             if (child.getNodeType() == Node.ELEMENT_NODE &&

-                    child.getNamespaceURI().equals(SdkRepository.NS_SDK_REPOSITORY)) {

+                    SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {

                 if (xmlLocalName == null || child.getLocalName().equals(xmlLocalName)) {

                     return child;

                 }

@@ -264,6 +317,9 @@
         return null;

     }

 

+    /**

+     * Takes an XML document as a string as parameter and returns a DOM for it.

+     */

     private Document getDocument(String xml)

             throws ParserConfigurationException, SAXException, IOException {

         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

@@ -275,42 +331,4 @@
 

         return doc;

     }

-

-    private void parseAddon(Node addon, ArrayList<String> packages, ITaskMonitor monitor) {

-        // TODO Auto-generated method stub

-        String s = String.format("addon %1$s by %2$s, api %3$s, rev %4$s",

-                getFirstChild(addon, SdkRepository.NODE_NAME).getTextContent(),

-                getFirstChild(addon, SdkRepository.NODE_VENDOR).getTextContent(),

-                getFirstChild(addon, SdkRepository.NODE_API_LEVEL).getTextContent(),

-                getFirstChild(addon, SdkRepository.NODE_REVISION).getTextContent()

-                );

-        packages.add(s);

-    }

-

-    private void parsePlatform(Node platform, ArrayList<String> packages, ITaskMonitor monitor) {

-        // TODO Auto-generated method stub

-        String s = String.format("platform %1$s, api %2$s, rev %3$s",

-                getFirstChild(platform, SdkRepository.NODE_VERSION).getTextContent(),

-                getFirstChild(platform, SdkRepository.NODE_API_LEVEL).getTextContent(),

-                getFirstChild(platform, SdkRepository.NODE_REVISION).getTextContent()

-                );

-        packages.add(s);

-    }

-

-    private void parseDoc(Node doc, ArrayList<String> packages, ITaskMonitor monitor) {

-        // TODO Auto-generated method stub

-        String s = String.format("doc for api %1$s, rev %2$s",

-                getFirstChild(doc, SdkRepository.NODE_API_LEVEL).getTextContent(),

-                getFirstChild(doc, SdkRepository.NODE_REVISION).getTextContent()

-                );

-        packages.add(s);

-    }

-

-    private void parseTool(Node tool, ArrayList<String> packages, ITaskMonitor monitor) {

-        // TODO Auto-generated method stub

-        String s = String.format("tool, rev %1$s",

-                getFirstChild(tool, SdkRepository.NODE_REVISION).getTextContent()

-                );

-        packages.add(s);

-    }

 }

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSources.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSources.java
new file mode 100755
index 0000000..0a70953
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSources.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import java.util.ArrayList;

+

+/**

+ * A list of sdk-repository sources.

+ */

+public class RepoSources {

+

+    private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();

+    private ITaskFactory mTaskFactory;

+

+    public RepoSources() {

+    }

+

+    public void setTaskFactory(ITaskFactory taskFactory) {

+        mTaskFactory = taskFactory;

+    }

+

+    public ITaskFactory getTaskFactory() {

+        return mTaskFactory;

+    }

+

+    public void add(RepoSource source) {

+        mSources.add(source);

+    }

+

+    public ArrayList<RepoSource> getSources() {

+        return mSources;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
new file mode 100755
index 0000000..71e35c4
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
@@ -0,0 +1,92 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdklib.internal.repository;

+

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.internal.repository.Archive.Arch;

+import com.android.sdklib.internal.repository.Archive.Os;

+

+import org.w3c.dom.Node;

+

+import java.io.File;

+

+/**

+ * Represents a tool XML node in an SDK repository.

+ */

+public class ToolPackage extends Package {

+

+    /**

+     * Creates a new tool package from the attributes and elements of the given XML node.

+     * <p/>

+     * This constructor should throw an exception if the package cannot be created.

+     */

+    ToolPackage(RepoSource source, Node packageNode) {

+        super(source, packageNode);

+    }

+

+    /**

+     * Manually create a new package with one archive and the given attributes.

+     * This is used to create packages from local directories.

+     */

+    ToolPackage(RepoSource source,

+            int revision,

+            String description,

+            String descUrl,

+            Os archiveOs,

+            Arch archiveArch,

+            String archiveUrl,

+            long archiveSize,

+            String archiveChecksum) {

+        super(source,

+                revision,

+                description,

+                descUrl,

+                archiveOs,

+                archiveArch,

+                archiveUrl,

+                archiveSize,

+                archiveChecksum);

+    }

+

+    /** Returns a short description for an {@link IDescription}. */

+    @Override

+    public String getShortDescription() {

+        return String.format("Android SDK Tools, revision %1$d", getRevision());

+    }

+

+    /** Returns a long description for an {@link IDescription}. */

+    @Override

+    public String getLongDescription() {

+        return String.format("Android SDK Tools, revision %1$d.\n%2$s",

+                getRevision(),

+                super.getLongDescription());

+    }

+

+    /**

+     * Computes a potential installation folder if an archive of this package were

+     * to be installed right away in the given SDK root.

+     * <p/>

+     * A "tool" package should always be located in SDK/tools.

+     *

+     * @param osSdkRoot The OS path of the SDK root folder.

+     * @return A new {@link File} corresponding to the directory to use to install this package.

+     */

+    @Override

+    public File getInstallFolder(String osSdkRoot) {

+        return new File(osSdkRoot, SdkConstants.FD_TOOLS);

+    }

+}

diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
index 72cc425..673e43f 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
@@ -16,27 +16,74 @@
 

 package com.android.sdklib.repository;

 

+

 import java.io.InputStream;

 

 /**

- * Constants for the sdk-repository XML Schema

+ * Public constants for the sdk-repository XML Schema.

  */

 public class SdkRepository {

 

+    /** The URL of the official Google sdk-repository site. */

+    public static final String URL_GOOGLE_SDK_REPO_SITE =

+        "https://dl.google.com/android/eclipse/repository/index.xml";           //$NON-NLS-1$

+

+    /** The XML namespace of the sdk-repository XML. */

     public static final String NS_SDK_REPOSITORY =

         "http://schemas.android.com/sdk/android/repository/1";                  //$NON-NLS-1$

 

-    public static final String NODE_VERSION = "version";                        //$NON-NLS-1$

-    public static final String NODE_REVISION = "revision";                      //$NON-NLS-1$

-    public static final String NODE_API_LEVEL = "api-level";                    //$NON-NLS-1$

-    public static final String NODE_VENDOR = "vendor";                          //$NON-NLS-1$

-    public static final String NODE_NAME = "name";                              //$NON-NLS-1$

-    public static final String NODE_TOOL = "tool";                              //$NON-NLS-1$

-    public static final String NODE_DOC = "doc";                                //$NON-NLS-1$

-    public static final String NODE_PLATFORM = "platform";                      //$NON-NLS-1$

-    public static final String NODE_ADD_ON = "add-on";                          //$NON-NLS-1$

+    /** The root sdk-repository element */

     public static final String NODE_SDK_REPOSITORY = "sdk-repository";          //$NON-NLS-1$

 

+    /** A platform package. */

+    public static final String NODE_PLATFORM = "platform";                      //$NON-NLS-1$

+    /** An add-on package. */

+    public static final String NODE_ADD_ON   = "add-on";                        //$NON-NLS-1$

+    /** A tool package. */

+    public static final String NODE_TOOL     = "tool";                          //$NON-NLS-1$

+    /** A doc package. */

+    public static final String NODE_DOC      = "doc";                           //$NON-NLS-1$

+

+    /** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */

+    public static final String NODE_REVISION    = "revision";                   //$NON-NLS-1$

+    /** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */

+    public static final String NODE_DESCRIPTION = "description";                //$NON-NLS-1$

+    /** The optional description URL for all packages (platform, add-on, tool, doc). */

+    public static final String NODE_DESC_URL    = "desc-url";                   //$NON-NLS-1$

+

+    /** The version, a string, for platform packages. */

+    public static final String NODE_VERSION   = "version";                      //$NON-NLS-1$

+    /** The api-level, an int > 0, for platform, add-on and doc packages. */

+    public static final String NODE_API_LEVEL = "api-level";                    //$NON-NLS-1$

+    /** The vendor, a string, for add-on packages. */

+    public static final String NODE_VENDOR    = "vendor";                       //$NON-NLS-1$

+    /** The name, a string, for add-on packages or for libraries. */

+    public static final String NODE_NAME      = "name";                         //$NON-NLS-1$

+

+    /** The libs container, optional for an add-on. */

+    public static final String NODE_LIBS      = "libs";                         //$NON-NLS-1$

+    /** A lib element in a libs container. */

+    public static final String NODE_LIB       = "lib";                          //$NON-NLS-1$

+

+    /** The archives container, for all packages. */

+    public static final String NODE_ARCHIVES = "archives";                      //$NON-NLS-1$

+    /** An archive element, for the archives container. */

+    public static final String NODE_ARCHIVE  = "archive";                       //$NON-NLS-1$

+

+    /** An archive size, an int > 0. */

+    public static final String NODE_SIZE     = "size";                          //$NON-NLS-1$

+    /** A sha1 archive checksum, as a 40-char hex. */

+    public static final String NODE_CHECKSUM = "checksum";                      //$NON-NLS-1$

+    /** A download archive URL, either absolute or relative to the repository xml. */

+    public static final String NODE_URL      = "url";                           //$NON-NLS-1$

+

+    /** An archive checksum type, mandatory. */

+    public static final String ATTR_TYPE = "type";                              //$NON-NLS-1$

+    /** An archive OS attribute, mandatory. */

+    public static final String ATTR_OS   = "os";                                //$NON-NLS-1$

+    /** An optional archive Architecture attribute. */

+    public static final String ATTR_ARCH = "arch";                              //$NON-NLS-1$

+

     public static InputStream getXsdStream() {

         return SdkRepository.class.getResourceAsStream("sdk-repository.xsd");   //$NON-NLS-1$

     }

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
index 44c7724..0166556 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
@@ -1,11 +1,11 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
  *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ *      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,
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
index b23c865..b90bd61 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ *      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,
@@ -46,17 +46,17 @@
  * selection.
  */
 public class SdkTargetSelector {
-    
+
     private IAndroidTarget[] mTargets;
     private final boolean mAllowSelection;
     private SelectionListener mSelectionListener;
     private Table mTable;
     private Label mDescription;
     private Composite mInnerGroup;
-    
+
     /**
      * Creates a new SDK Target Selector.
-     * 
+     *
      * @param parent The parent composite where the selector will be added.
      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
      *                Targets can be null or an empty array, in which case the table is disabled.
@@ -67,7 +67,7 @@
 
     /**
      * Creates a new SDK Target Selector.
-     * 
+     *
      * @param parent The parent composite where the selector will be added.
      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
      *                Targets can be null or an empty array, in which case the table is disabled.
@@ -79,7 +79,7 @@
         mInnerGroup.setLayout(new GridLayout());
         mInnerGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
         mInnerGroup.setFont(parent.getFont());
-        
+
         mAllowSelection = allowSelection;
         int style = SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION;
         if (allowSelection) {
@@ -137,7 +137,7 @@
 
     /**
      * Changes the targets of the SDK Target Selector.
-     * 
+     *
      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
      */
     public void setTargets(IAndroidTarget[] targets) {
@@ -154,19 +154,19 @@
      * The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
      * <p/>
      * It is recommended that the caller uses the {@link #getSelected()} method instead.
-     * 
+     *
      * @param selectionListener The new listener or null to remove it.
      */
     public void setSelectionListener(SelectionListener selectionListener) {
         mSelectionListener = selectionListener;
     }
-    
+
     /**
      * Sets the current target selection.
      * <p/>
      * If the selection is actually changed, this will invoke the selection listener
      * (if any) with a null event.
-     * 
+     *
      * @param target the target to be selection
      * @return true if the target could be selected, false otherwise.
      */
@@ -174,7 +174,7 @@
         if (!mAllowSelection) {
             return false;
         }
-        
+
         boolean found = false;
         boolean modified = false;
 
@@ -192,17 +192,17 @@
                 }
             }
         }
-        
+
         if (modified && mSelectionListener != null) {
             mSelectionListener.widgetSelected(null);
         }
-        
+
         return found;
     }
 
     /**
      * Returns the selected item.
-     * 
+     *
      * @return The selected item or null.
      */
     public IAndroidTarget getSelected() {
@@ -234,7 +234,7 @@
             @Override
             public void controlResized(ControlEvent e) {
                 Rectangle r = table.getClientArea();
-                column0.setWidth(r.width * 30 / 100); // 30%  
+                column0.setWidth(r.width * 30 / 100); // 30%
                 column1.setWidth(r.width * 45 / 100); // 45%
                 column2.setWidth(r.width * 15 / 100); // 15%
                 column3.setWidth(r.width * 10 / 100); // 10%
@@ -267,7 +267,7 @@
                     mSelectionListener.widgetDefaultSelected(e);
                 }
             }
-            
+
             public void widgetSelected(SelectionEvent e) {
                 if (e.item instanceof TableItem) {
                     TableItem i = (TableItem) e.item;
@@ -315,7 +315,7 @@
         }
 
         table.removeAll();
-        
+
         if (mTargets != null && mTargets.length > 0) {
             table.setEnabled(true);
             for (IAndroidTarget target : mTargets) {
@@ -350,36 +350,36 @@
         }
 
         /*
-         * Reference: 
+         * Reference:
          * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
          */
-        
+
         final Listener listener = new Listener() {
             public void handleEvent(Event event) {
-                
+
                 switch(event.type) {
                 case SWT.KeyDown:
                 case SWT.MouseExit:
                 case SWT.MouseDown:
                     return;
-                    
+
                 case SWT.MouseHover:
                     updateDescription(table.getItem(new Point(event.x, event.y)));
                     break;
-                    
+
                 case SWT.Selection:
                     if (event.item instanceof TableItem) {
                         updateDescription((TableItem) event.item);
                     }
                     break;
-                    
+
                 default:
                     return;
                 }
 
             }
         };
-        
+
         table.addListener(SWT.Dispose, listener);
         table.addListener(SWT.KeyDown, listener);
         table.addListener(SWT.MouseMove, listener);
@@ -414,5 +414,5 @@
             enableControl(c2, enabled);
         }
     }
-    
+
 }
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
new file mode 100755
index 0000000..3f32ddc
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
@@ -0,0 +1,251 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.IDescription;

+import com.android.sdklib.internal.repository.ITask;

+import com.android.sdklib.internal.repository.ITaskMonitor;

+

+import org.eclipse.jface.viewers.ISelection;

+import org.eclipse.jface.viewers.IStructuredSelection;

+import org.eclipse.jface.viewers.TableViewer;

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.events.ControlAdapter;

+import org.eclipse.swt.events.ControlEvent;

+import org.eclipse.swt.events.SelectionAdapter;

+import org.eclipse.swt.events.SelectionEvent;

+import org.eclipse.swt.graphics.Rectangle;

+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Button;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Group;

+import org.eclipse.swt.widgets.Label;

+import org.eclipse.swt.widgets.Table;

+import org.eclipse.swt.widgets.TableColumn;

+import org.eclipse.swt.widgets.Text;

+

+/*

+ * TODO list

+ * - select => update desc, enable update + delete, enable home page if url

+ * - home page callback

+ * - update callback

+ * - delete callback

+ * - refresh callback

+ */

+

+public class LocalPackagesPage extends Composite {

+    private UpdaterData mUpdaterData;

+

+    private Label mSdkLocLabel;

+    private Text mSdkLocText;

+    private Button mSdkLocBrowse;

+    private TableViewer mTableViewerPackages;

+    private Table mTablePackages;

+    private TableColumn mColumnPackages;

+    private Group mDescriptionContainer;

+    private Composite mContainerButtons;

+    private Button mUpdateButton;

+    private Label mPlaceholder1;

+    private Button mDeleteButton;

+    private Label mPlaceholder2;

+    private Button mHomePageButton;

+    private Label mDescriptionLabel;

+

+    /**

+     * Create the composite.

+     * @param parent The parent of the composite.

+     * @param updaterData An instance of {@link UpdaterData}. If null, a local

+     *        one will be allocated just to help with the SWT Designer.

+     */

+    public LocalPackagesPage(Composite parent, UpdaterData updaterData) {

+        super(parent, SWT.BORDER);

+

+        mUpdaterData = updaterData != null ? updaterData : new UpdaterData();

+

+        createContents(this);

+        postCreate();  //$hide$

+    }

+

+    private void createContents(Composite parent) {

+        parent.setLayout(new GridLayout(3, false));

+

+        createSdkLocation(parent);

+

+        mTableViewerPackages = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);

+        mTablePackages = mTableViewerPackages.getTable();

+        mTablePackages.setHeaderVisible(true);

+        mTablePackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));

+        mTablePackages.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onTreeSelected(); //$hide$

+            }

+        });

+

+        mColumnPackages = new TableColumn(mTablePackages, SWT.NONE);

+        mColumnPackages.setWidth(377);

+        mColumnPackages.setText("Installed Packages");

+

+        mDescriptionContainer = new Group(parent, SWT.NONE);

+        mDescriptionContainer.setLayout(new GridLayout(1, false));

+        mDescriptionContainer.setText("Description");

+        mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));

+

+        mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);

+        mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1));

+        mDescriptionLabel.setText("Line1\nLine2\nLine3");

+

+        mContainerButtons = new Composite(parent, SWT.NONE);

+        mContainerButtons.setLayout(new GridLayout(5, false));

+        mContainerButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));

+

+        mUpdateButton = new Button(mContainerButtons, SWT.NONE);

+        mUpdateButton.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onUpdateInstalledPackage();  //$hide$ (hide from SWT designer)

+            }

+        });

+        mUpdateButton.setText("Update...");

+

+        mPlaceholder1 = new Label(mContainerButtons, SWT.NONE);

+        mPlaceholder1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));

+

+        mDeleteButton = new Button(mContainerButtons, SWT.NONE);

+        mDeleteButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));

+        mDeleteButton.setText("Delete...");

+

+        mPlaceholder2 = new Label(mContainerButtons, SWT.NONE);

+        mPlaceholder2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));

+

+        mHomePageButton = new Button(mContainerButtons, SWT.NONE);

+        mHomePageButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mHomePageButton.setText("Home Page...");

+    }

+

+    private void createSdkLocation(Composite parent) {

+        mSdkLocLabel = new Label(parent, SWT.NONE);

+        mSdkLocLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

+        mSdkLocLabel.setText("SDK Location:");

+

+        // If the sdk path is not user-customizable, do not create

+        // the browse button and use horizSpan=2 on the text field.

+

+        mSdkLocText = new Text(parent, SWT.BORDER);

+        mSdkLocText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

+

+        if (mUpdaterData.canUserChangeSdkRoot()) {

+            mSdkLocBrowse = new Button(parent, SWT.NONE);

+            mSdkLocBrowse.setText("Browse...");

+        } else {

+            mSdkLocText.setEditable(false);

+            ((GridData)mSdkLocText.getLayoutData()).horizontalSpan++;

+        }

+

+        if (mUpdaterData.getOsSdkRoot() != null) {

+            mSdkLocText.setText(mUpdaterData.getOsSdkRoot());

+        }

+    }

+

+    @Override

+    protected void checkSubclass() {

+        // Disable the check that prevents subclassing of SWT components

+    }

+

+    // -- Start of internal part ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    /**

+     * Must be called once to set the adapter input for the package table viewer.

+     */

+    public void setInput(LocalSdkAdapter localSdkAdapter) {

+        mTableViewerPackages.setLabelProvider(  localSdkAdapter.getLabelProvider());

+        mTableViewerPackages.setContentProvider(localSdkAdapter.getContentProvider());

+        mTableViewerPackages.setInput(localSdkAdapter);

+        onTreeSelected();

+    }

+

+    /**

+     * Called by the constructor right after {@link #createContents(Composite)}.

+     */

+    private void postCreate() {

+        adjustColumnsWidth();

+    }

+

+    /**

+     * Adds a listener to adjust the columns width when the parent is resized.

+     * <p/>

+     * If we need something more fancy, we might want to use this:

+     * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co

+     */

+    private void adjustColumnsWidth() {

+        // Add a listener to resize the column to the full width of the table

+        mTablePackages.addControlListener(new ControlAdapter() {

+            @Override

+            public void controlResized(ControlEvent e) {

+                Rectangle r = mTablePackages.getClientArea();

+                mColumnPackages.setWidth(r.width);

+            }

+        });

+    }

+

+    /**

+     * Called when an item in the package table viewer is selected.

+     * If the items is an {@link IDescription} (as it should), this will display its long

+     * description in the description area. Otherwise when the item is not of the expected

+     * type or there is no selection, it empties the description area.

+     */

+    private void onTreeSelected() {

+        ISelection sel = mTableViewerPackages.getSelection();

+        if (sel instanceof IStructuredSelection) {

+            Object elem = ((IStructuredSelection) sel).getFirstElement();

+            if (elem instanceof IDescription) {

+                mDescriptionLabel.setText(((IDescription) elem).getLongDescription());

+                mDescriptionContainer.layout(true);

+                return;

+            }

+        }

+        mDescriptionLabel.setText("");  //$NON-NLS1-$

+    }

+

+    private void onUpdateInstalledPackage() {

+        // TODO just a test, needs to be removed later.

+        ProgressTask.start(getShell(), "Test", new ITask() {

+            public void run(ITaskMonitor monitor) {

+                monitor.setDescription("Test");

+                monitor.setProgressMax(100);

+                int n = 0;

+                int d = 1;

+                while(!monitor.cancelRequested()) {

+                    monitor.incProgress(d);

+                    n += d;

+                    if (n == 0 || n == 100) d = -d;

+                    try {

+                        Thread.sleep(5);

+                    } catch (InterruptedException e) {

+                        // ignore

+                    }

+                }

+            }

+        });

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java
new file mode 100755
index 0000000..330be18
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java
@@ -0,0 +1,116 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.IDescription;

+import com.android.sdklib.internal.repository.LocalSdkParser;

+import com.android.sdklib.internal.repository.Package;

+import com.android.sdklib.internal.repository.RepoSource;

+

+import org.eclipse.jface.viewers.IContentProvider;

+import org.eclipse.jface.viewers.ILabelProvider;

+import org.eclipse.jface.viewers.IStructuredContentProvider;

+import org.eclipse.jface.viewers.LabelProvider;

+import org.eclipse.jface.viewers.Viewer;

+import org.eclipse.swt.graphics.Image;

+

+/**

+ * Table adapters to use the local SDK list.

+ */

+class LocalSdkAdapter  {

+

+    private final LocalSdkParser mLocalSdkParser;

+    private String mOsSdkRoot;

+

+    public LocalSdkAdapter(LocalSdkParser localSdkParser) {

+        mLocalSdkParser = localSdkParser;

+    }

+

+    public void setSdkRoot(String osSdkRoot) {

+        mOsSdkRoot = osSdkRoot;

+        mLocalSdkParser.clearPackages();

+    }

+

+    public ILabelProvider getLabelProvider() {

+        return new ViewerLabelProvider();

+    }

+

+

+    public IContentProvider getContentProvider() {

+        return new TableContentProvider();

+    }

+

+    // ------------

+

+    public static class ViewerLabelProvider extends LabelProvider {

+        /** Returns null by default */

+        @Override

+        public Image getImage(Object element) {

+            return super.getImage(element);

+        }

+

+        /** Returns the toString of the element. */

+        @Override

+        public String getText(Object element) {

+            if (element instanceof IDescription) {

+                return ((IDescription) element).getShortDescription();

+            }

+            return super.getText(element);

+        }

+    }

+

+    // ------------

+

+    private static class TableContentProvider implements IStructuredContentProvider {

+

+        // Called when the viewer is disposed

+        public void dispose() {

+            // pass

+        }

+

+        // Called when the input is set or changed on the provider

+        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

+            // pass

+        }

+

+        /**

+         * Called to collect the root elements for the given input.

+         * The input here is a {@link LocalSdkAdapter} object, this returns an array

+         * of {@link RepoSource}.

+         */

+        public Object[] getElements(Object inputElement) {

+            if (inputElement instanceof LocalSdkAdapter) {

+                LocalSdkAdapter adapter = (LocalSdkAdapter) inputElement;

+                LocalSdkParser parser = adapter.mLocalSdkParser;

+

+                Package[] packages = parser.getPackages();

+

+                if (packages == null) {

+                    // load on demand the first time

+                    packages = parser.parseSdk(adapter.mOsSdkRoot);

+                }

+

+                if (packages != null) {

+                    return packages;

+                }

+            }

+

+            return new Object[0];

+        }

+    }

+

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ProgressTask.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
similarity index 76%
rename from tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ProgressTask.java
rename to tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
index ee768dd..7667355 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ProgressTask.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
@@ -1,11 +1,11 @@
 /*

  * Copyright (C) 2009 The Android Open Source Project

  *

- * Licensed under the Eclipse Public License, Version 1.0 (the "License");

+ * 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.eclipse.org/org/documents/epl-v10.php

+ *      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,

@@ -14,7 +14,10 @@
  * limitations under the License.

  */

 

-package com.android.sdkuilib.repository;

+package com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.ITask;

+import com.android.sdklib.internal.repository.ITaskMonitor;

 

 import org.eclipse.swt.SWT;

 import org.eclipse.swt.events.SelectionAdapter;

@@ -30,6 +33,13 @@
 import org.eclipse.swt.widgets.Shell;

 import org.eclipse.swt.widgets.Text;

 

+/*

+ * TODO:

+ * - trap window.close and treat it as a cancel request

+ * - on cancel as been clicked *and* the task finished,, change it to a "close" button

+ */

+

+

 class ProgressTask extends Dialog

     implements ITaskMonitor             //$hide$ (hide from SWT designer)

     {

@@ -72,6 +82,9 @@
                 display.sleep();

             }

         }

+

+        mCancelRequested = true;

+

         if (!mDialogShell.isDisposed()) {

             mDialogShell.close();

         }

@@ -119,11 +132,7 @@
     // Hide everything down-below from SWT designer

     //$hide>>$

 

-    public interface ThreadTask {

-        public abstract void PerformTask(ITaskMonitor monitor);

-    }

-

-    private ThreadTask mTask;

+    private ITask mTask;

 

     /**

      * Creates a new {@link ProgressTask} with the given title.

@@ -131,7 +140,7 @@
      *

      * This blocks till the thread ends.

      */

-    public static ProgressTask start(Shell parent, String title, ThreadTask task) {

+    public static ProgressTask start(Shell parent, String title, ITask task) {

         ProgressTask t = new ProgressTask(parent);

         t.setText(title);

         t.setTask(task);

@@ -143,11 +152,11 @@
      * Sets the description in the current task dialog.

      * This method can be invoke from a non-UI thread.

      */

-    public void setDescription(final String description) {

+    public void setDescription(final String descriptionFormat, final Object...args) {

         mDialogShell.getDisplay().asyncExec(new Runnable() {

             public void run() {

                 if (!mLabel.isDisposed()) {

-                    mLabel.setText(description);

+                    mLabel.setText(String.format(descriptionFormat, args));

                 }

             }

         });

@@ -157,16 +166,18 @@
      * Sets the description in the current task dialog.

      * This method can be invoke from a non-UI thread.

      */

-    public void setResult(final String result) {

+    public void setResult(final String resultFormat, final Object...args) {

         mAutomaticallyCloseOnTaskCompletion = false;

-        mDialogShell.getDisplay().asyncExec(new Runnable() {

-            public void run() {

-                if (!mResultText.isDisposed()) {

-                    mResultText.setVisible(true);

-                    mResultText.setText(result);

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().asyncExec(new Runnable() {

+                public void run() {

+                    if (!mResultText.isDisposed()) {

+                        mResultText.setVisible(true);

+                        mResultText.setText(String.format(resultFormat, args));

+                    }

                 }

-            }

-        });

+            });

+        }

     }

 

     /**

@@ -176,13 +187,15 @@
      * @see ProgressBar#setMaximum(int)

      */

     public void setProgressMax(final int max) {

-        mDialogShell.getDisplay().asyncExec(new Runnable() {

-            public void run() {

-                if (!mProgressBar.isDisposed()) {

-                    mProgressBar.setMaximum(max);

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().asyncExec(new Runnable() {

+                public void run() {

+                    if (!mProgressBar.isDisposed()) {

+                        mProgressBar.setMaximum(max);

+                    }

                 }

-            }

-        });

+            });

+        }

     }

 

     /**

@@ -191,13 +204,15 @@
      * This method can be invoked from a non-UI thread.

      */

     public void incProgress(final int delta) {

-        mDialogShell.getDisplay().asyncExec(new Runnable() {

-            public void run() {

-                if (!mProgressBar.isDisposed()) {

-                    mProgressBar.setSelection(mProgressBar.getSelection() + delta);

+        if (!mDialogShell.isDisposed()) {

+            mDialogShell.getDisplay().asyncExec(new Runnable() {

+                public void run() {

+                    if (!mProgressBar.isDisposed()) {

+                        mProgressBar.setSelection(mProgressBar.getSelection() + delta);

+                    }

                 }

-            }

-        });

+            });

+        }

     }

 

     /**

@@ -209,12 +224,12 @@
     }

 

     /** Sets the task that will execute in a separate thread. */

-    private void setTask(ThreadTask task) {

+    private void setTask(ITask task) {

         mTask = task;

     }

 

     /**

-     * Starts the task from {@link #setTask(ThreadTask)} in a separate thread.

+     * Starts the task from {@link #setTask(ITask)} in a separate thread.

      * When the task completes, set {@link #mCloseRequested} to end the dialog loop.

      */

     private void startTask() {

@@ -222,7 +237,7 @@
             new Thread(getText()) {

                 @Override

                 public void run() {

-                    mTask.PerformTask(ProgressTask.this);

+                    mTask.run(ProgressTask.this);

                     if (mAutomaticallyCloseOnTaskCompletion) {

                         mCloseRequested = true;

                     }

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java
new file mode 100755
index 0000000..ceb701e
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTaskFactory.java
@@ -0,0 +1,39 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.ITask;

+import com.android.sdklib.internal.repository.ITaskFactory;

+

+import org.eclipse.swt.widgets.Shell;

+

+/**

+ * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog

+ * for each new task.

+ */

+public class ProgressTaskFactory implements ITaskFactory {

+

+    private final Shell mShell;

+

+    public ProgressTaskFactory(Shell shell) {

+        mShell = shell;

+    }

+

+    public void start(String title, ITask task) {

+        ProgressTask.start(mShell, title, task);

+    }

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
new file mode 100755
index 0000000..1fe5ca4
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
@@ -0,0 +1,242 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+

+import com.android.sdklib.internal.repository.Archive;

+import com.android.sdklib.internal.repository.IDescription;

+

+import org.eclipse.jface.viewers.CheckStateChangedEvent;

+import org.eclipse.jface.viewers.CheckboxTreeViewer;

+import org.eclipse.jface.viewers.DoubleClickEvent;

+import org.eclipse.jface.viewers.ICheckStateListener;

+import org.eclipse.jface.viewers.IDoubleClickListener;

+import org.eclipse.jface.viewers.ISelection;

+import org.eclipse.jface.viewers.ITreeSelection;

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.events.ControlAdapter;

+import org.eclipse.swt.events.ControlEvent;

+import org.eclipse.swt.events.SelectionAdapter;

+import org.eclipse.swt.events.SelectionEvent;

+import org.eclipse.swt.graphics.Rectangle;

+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Button;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Group;

+import org.eclipse.swt.widgets.Label;

+import org.eclipse.swt.widgets.Tree;

+import org.eclipse.swt.widgets.TreeColumn;

+

+import java.util.ArrayList;

+

+/*

+ * TODO list

+ * - check source => toggle packages: all, none

+ * - check package => set source check to tri-state

+ * - check callback => install enable if has selection

+ * - select tree item: delete site enable if add-on source

+ * - select tree item: refresh enable if source

+ * - load add-on sites from pref

+ * - delete site callback, update pref

+ * - refresh callback

+ * - install selected callback

+ */

+

+public class RemotePackagesPage extends Composite {

+

+    private final UpdaterWindowImpl mUpdaterWindow;

+    private final UpdaterData mUpdaterData;

+

+    private CheckboxTreeViewer mTreeViewerSources;

+    private Tree mTreeSources;

+    private TreeColumn mColumnSource;

+    private Group mDescriptionContainer;

+    private Button mAddSiteButton;

+    private Button mRemoveSiteButton;

+    private Label mPlaceholder3;

+    private Button mRefreshButton;

+    private Button mInstallSelectedButton;

+    private Label mDescriptionLabel;

+

+

+    /**

+     * Create the composite.

+     * @param parent The parent of the composite.

+     * @param updaterData An instance of {@link UpdaterData}. If null, a local

+     *        one will be allocated just to help with the SWT Designer.

+     * @param updaterWindow The parent window.

+     */

+    RemotePackagesPage(Composite parent,

+            UpdaterData updaterData,

+            UpdaterWindowImpl updaterWindow) {

+        super(parent, SWT.BORDER);

+        mUpdaterWindow = updaterWindow;

+

+        mUpdaterData = updaterData != null ? updaterData : new UpdaterData();

+

+        createContents(this);

+        postCreate();  //$hide$

+    }

+

+    private void createContents(Composite parent) {

+        parent.setLayout(new GridLayout(5, false));

+

+        mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);

+        mTreeViewerSources.addDoubleClickListener(new IDoubleClickListener() {

+            public void doubleClick(DoubleClickEvent event) {

+                onTreeDoubleClick(event); //$hide$

+            }

+        });

+        mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {

+            public void checkStateChanged(CheckStateChangedEvent event) {

+                onTreeCheckStateChanged(event); //$hide$

+            }

+        });

+        mTreeSources = mTreeViewerSources.getTree();

+        mTreeSources.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onTreeSelected(); //$hide$

+            }

+        });

+        mTreeSources.setHeaderVisible(true);

+        mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));

+

+        mColumnSource = new TreeColumn(mTreeSources, SWT.NONE);

+        mColumnSource.setWidth(289);

+        mColumnSource.setText("Sources, Packages and Archives");

+

+        mDescriptionContainer = new Group(parent, SWT.NONE);

+        mDescriptionContainer.setLayout(new GridLayout(1, false));

+        mDescriptionContainer.setText("Description");

+        mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1));

+

+        mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);

+        mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

+        mDescriptionLabel.setText("Line1\nLine2\nLine3");

+

+        mAddSiteButton = new Button(parent, SWT.NONE);

+        mAddSiteButton.setText("Add Site...");

+

+        mRemoveSiteButton = new Button(parent, SWT.NONE);

+        mRemoveSiteButton.setText("Delete Site...");

+

+        mPlaceholder3 = new Label(parent, SWT.NONE);

+        mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));

+

+        mRefreshButton = new Button(parent, SWT.NONE);

+        mRefreshButton.setText("Refresh");

+

+        mInstallSelectedButton = new Button(parent, SWT.NONE);

+        mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onInstallSelectedArchives();  //$hide$

+            }

+        });

+        mInstallSelectedButton.setText("Install Selected");

+    }

+

+    @Override

+    protected void checkSubclass() {

+        // Disable the check that prevents subclassing of SWT components

+    }

+

+    // -- Start of internal part ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    /**

+     * Must be called once to set the adapter input for the sources tree viewer.

+     */

+    public void setInput(RepoSourcesAdapter sources) {

+        mTreeViewerSources.setContentProvider(sources.getContentProvider());

+        mTreeViewerSources.setLabelProvider(  sources.getLabelProvider());

+        mTreeViewerSources.setInput(sources);

+        onTreeSelected();

+    }

+

+    /**

+     * Called by the constructor right after {@link #createContents(Composite)}.

+     */

+    private void postCreate() {

+        adjustColumnsWidth();

+    }

+

+    /**

+     * Adds a listener to adjust the columns width when the parent is resized.

+     * <p/>

+     * If we need something more fancy, we might want to use this:

+     * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co

+     */

+    private void adjustColumnsWidth() {

+        // Add a listener to resize the column to the full width of the table

+        mTreeSources.addControlListener(new ControlAdapter() {

+            @Override

+            public void controlResized(ControlEvent e) {

+                Rectangle r = mTreeSources.getClientArea();

+                mColumnSource.setWidth(r.width);

+            }

+        });

+    }

+

+    /**

+     * Called when an item in the package table viewer is selected.

+     * If the items is an {@link IDescription} (as it should), this will display its long

+     * description in the description area. Otherwise when the item is not of the expected

+     * type or there is no selection, it empties the description area.

+     */

+    private void onTreeSelected() {

+        ISelection sel = mTreeViewerSources.getSelection();

+        if (sel instanceof ITreeSelection) {

+            Object elem = ((ITreeSelection) sel).getFirstElement();

+            if (elem instanceof IDescription) {

+                mDescriptionLabel.setText(((IDescription) elem).getLongDescription());

+                mDescriptionContainer.layout(true);

+                return;

+            }

+        }

+        mDescriptionLabel.setText("");  //$NON-NLS1-$

+    }

+

+    private void onTreeCheckStateChanged(CheckStateChangedEvent event) {

+        boolean b = event.getChecked();

+        Object elem = event.getElement(); // Will be Archive or Package or RepoSource

+        Object src = event.getSource();

+        // TODO

+    }

+

+    private void onTreeDoubleClick(DoubleClickEvent event) {

+        // TODO

+    }

+

+    private void onInstallSelectedArchives() {

+

+        ArrayList<Archive> archives = new ArrayList<Archive>();

+        for (Object element : mTreeViewerSources.getCheckedElements()) {

+            if (element instanceof Archive) {

+                archives.add((Archive) element);

+            }

+        }

+

+        mUpdaterWindow.installArchives(archives);

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
new file mode 100755
index 0000000..3f020d1
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
@@ -0,0 +1,157 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.internal.repository.Archive;

+import com.android.sdklib.internal.repository.IDescription;

+import com.android.sdklib.internal.repository.Package;

+import com.android.sdklib.internal.repository.RepoSource;

+import com.android.sdklib.internal.repository.RepoSources;

+

+import org.eclipse.jface.viewers.IContentProvider;

+import org.eclipse.jface.viewers.ILabelProvider;

+import org.eclipse.jface.viewers.ITreeContentProvider;

+import org.eclipse.jface.viewers.LabelProvider;

+import org.eclipse.jface.viewers.Viewer;

+import org.eclipse.swt.graphics.Image;

+

+/**

+ * A list of sdk-repository sources.

+ *

+ * This implementation is UI dependent.

+ */

+class RepoSourcesAdapter {

+

+    private final RepoSources mRepoSources;

+

+    public RepoSourcesAdapter(RepoSources repoSources) {

+        mRepoSources = repoSources;

+    }

+

+    public ILabelProvider getLabelProvider() {

+        return new ViewerLabelProvider();

+    }

+

+

+    public IContentProvider getContentProvider() {

+        return new TreeContentProvider();

+    }

+

+    // ------------

+

+    public static class ViewerLabelProvider extends LabelProvider {

+        /** Returns null by default */

+        @Override

+        public Image getImage(Object element) {

+            return super.getImage(element);

+        }

+

+        /** Returns the toString of the element. */

+        @Override

+        public String getText(Object element) {

+            if (element instanceof IDescription) {

+                return ((IDescription) element).getShortDescription();

+            }

+            return super.getText(element);

+        }

+    }

+

+    // ------------

+

+    private static class TreeContentProvider implements ITreeContentProvider {

+

+        private RepoSourcesAdapter mInput;

+

+        // Called when the viewer is disposed

+        public void dispose() {

+            // pass

+        }

+

+        // Called when the input is set or changed on the provider

+        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

+            assert newInput == null || newInput instanceof RepoSourcesAdapter;

+            mInput = (RepoSourcesAdapter) newInput;

+            // pass

+        }

+

+        /**

+         * Called to collect the root elements for the given input.

+         * The input here is a {@link RepoSourcesAdapter} object, this returns an array

+         * of {@link RepoSource}.

+         */

+        public Object[] getElements(Object inputElement) {

+            return getChildren(inputElement);

+        }

+

+        /**

+         * Get the children of the given parent. This is requested on-demand as

+         * nodes are expanded.

+         *

+         * For a {@link RepoSourcesAdapter} object, returns an array of {@link RepoSource}s.

+         * For a {@link RepoSource}, returns an array of {@link Package}s.

+         * For a {@link Package}, returns an array of {@link Archive}s.

+         */

+        public Object[] getChildren(Object parentElement) {

+            if (parentElement instanceof RepoSourcesAdapter) {

+                return ((RepoSourcesAdapter) parentElement).mRepoSources.getSources().toArray();

+

+            } else if (parentElement instanceof RepoSource) {

+                RepoSource source = (RepoSource) parentElement;

+                Package[] packages = source.getPackages();

+

+                if (packages == null) {

+                    source.load(mInput.mRepoSources.getTaskFactory());

+                    packages = source.getPackages();

+                }

+                if (packages != null) {

+                    return packages;

+                }

+

+            } else if (parentElement instanceof Package) {

+                return ((Package) parentElement).getArchives();

+            }

+

+            return new Object[0];

+        }

+

+        /**

+         * Returns the parent of a given element.

+         * The input {@link RepoSourcesAdapter} is the parent of all {@link RepoSource} elements.

+         */

+        public Object getParent(Object element) {

+

+            if (element instanceof RepoSource) {

+                return mInput;

+

+            } else if (element instanceof Package) {

+                return ((Package) element).getParentSource();

+            }

+            return null;

+        }

+

+        /**

+         * Returns true if a given element has children, which is used to display a

+         * "+/expand" box next to the tree node.

+         * All {@link RepoSource} and {@link Package} are expandable, whether they actually

+         * have any children or not.

+         */

+        public boolean hasChildren(Object element) {

+            return element instanceof RepoSource || element instanceof Package;

+        }

+    }

+

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
new file mode 100755
index 0000000..47ce3ac
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
@@ -0,0 +1,76 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+import com.android.sdklib.ISdkLog;

+import com.android.sdklib.internal.repository.LocalSdkParser;

+import com.android.sdklib.internal.repository.RepoSources;

+

+/**

+ * Data shared between {@link UpdaterWindowImpl} and its pages.

+ */

+class UpdaterData {

+    private ISdkLog mSdkLog;

+    private String mOsSdkRoot;

+    private boolean mUserCanChangeSdkRoot;

+

+    private final LocalSdkParser mLocalSdkParser = new LocalSdkParser();

+    private final RepoSources mSources = new RepoSources();

+

+    private final LocalSdkAdapter mLocalSdkAdapter = new LocalSdkAdapter(mLocalSdkParser);

+    private final RepoSourcesAdapter mSourcesAdapter = new RepoSourcesAdapter(mSources);

+

+    public void setOsSdkRoot(String osSdkRoot) {

+        mOsSdkRoot = osSdkRoot;

+    }

+

+    public String getOsSdkRoot() {

+        return mOsSdkRoot;

+    }

+

+    public void setUserCanChangeSdkRoot(boolean userCanChangeSdkRoot) {

+        mUserCanChangeSdkRoot = userCanChangeSdkRoot;

+    }

+

+    public boolean canUserChangeSdkRoot() {

+        return mUserCanChangeSdkRoot;

+    }

+

+    public RepoSources getSources() {

+        return mSources;

+    }

+

+    public RepoSourcesAdapter getSourcesAdapter() {

+        return mSourcesAdapter;

+    }

+

+    public LocalSdkParser getLocalSdkParser() {

+        return mLocalSdkParser;

+    }

+

+    public LocalSdkAdapter getLocalSdkAdapter() {

+        return mLocalSdkAdapter;

+    }

+

+    public void setSdkLog(ISdkLog sdkLog) {

+        mSdkLog = sdkLog;

+    }

+

+    public ISdkLog getSdkLog() {

+        return mSdkLog;

+    }

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
new file mode 100755
index 0000000..8ab37bb
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
@@ -0,0 +1,533 @@
+/*

+ * Copyright (C) 2009 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 com.android.sdkuilib.internal.repository;

+

+

+import com.android.sdklib.ISdkLog;

+import com.android.sdklib.internal.repository.Archive;

+import com.android.sdklib.internal.repository.ITask;

+import com.android.sdklib.internal.repository.ITaskMonitor;

+import com.android.sdklib.internal.repository.Package;

+import com.android.sdklib.internal.repository.RepoSource;

+import com.android.sdklib.repository.SdkRepository;

+

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.SWTException;

+import org.eclipse.swt.custom.SashForm;

+import org.eclipse.swt.custom.StackLayout;

+import org.eclipse.swt.events.DisposeEvent;

+import org.eclipse.swt.events.DisposeListener;

+import org.eclipse.swt.events.SelectionAdapter;

+import org.eclipse.swt.events.SelectionEvent;

+import org.eclipse.swt.graphics.Image;

+import org.eclipse.swt.graphics.ImageData;

+import org.eclipse.swt.graphics.Point;

+import org.eclipse.swt.layout.FillLayout;

+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Display;

+import org.eclipse.swt.widgets.List;

+import org.eclipse.swt.widgets.Shell;

+

+import java.io.File;

+import java.io.FileOutputStream;

+import java.io.IOException;

+import java.io.InputStream;

+import java.lang.reflect.Constructor;

+import java.net.URL;

+import java.security.MessageDigest;

+import java.util.ArrayList;

+import java.util.Collection;

+

+/**

+ * This is the private implementation of the UpdateWindow.

+ */

+public class UpdaterWindowImpl {

+

+    private static final int NUM_FETCH_URL_MONITOR_INC = 10;

+

+    /** Internal data shared between the window and its pages. */

+    private final UpdaterData mUpdaterData = new UpdaterData();

+    /** The array of pages instances. Only one is visible at a time. */

+    private ArrayList<Composite> mPages = new ArrayList<Composite>();

+    /** Indicates a page change is due to an internal request. Prevents callbacks from looping. */

+    private boolean mInternalPageChange;

+    /** A list of extra pages to instantiate. Each entry is an object array with 2 elements:

+     *  the string title and the Composite class to instantiate to create the page. */

+    private ArrayList<Object[]> mExtraPages;

+    /** A factory to create progress task dialogs. */

+    private ProgressTaskFactory mTaskFactory;

+

+

+    // --- UI members ---

+

+    protected Shell mAndroidSdkUpdater;

+    private SashForm mSashForm;

+    private List mPageList;

+    private Composite mPagesRootComposite;

+    private LocalPackagesPage mLocalPackagePage;

+    private RemotePackagesPage mRemotePackagesPage;

+    private StackLayout mStackLayout;

+    private Image mIconImage;

+

+    public UpdaterWindowImpl(ISdkLog sdkLog, String osSdkRoot, boolean userCanChangeSdkRoot) {

+        mUpdaterData.setSdkLog(sdkLog);

+        mUpdaterData.setOsSdkRoot(osSdkRoot);

+        mUpdaterData.setUserCanChangeSdkRoot(userCanChangeSdkRoot);

+    }

+

+    /**

+     * Open the window.

+     * @wbp.parser.entryPoint

+     */

+    public void open() {

+        Display display = Display.getDefault();

+        createContents();

+        mAndroidSdkUpdater.open();

+        mAndroidSdkUpdater.layout();

+

+        firstInit();    //$hide$ (hide from SWT designer)

+

+        while (!mAndroidSdkUpdater.isDisposed()) {

+            if (!display.readAndDispatch()) {

+                display.sleep();

+            }

+        }

+    }

+

+    /**

+     * Create contents of the window.

+     */

+    protected void createContents() {

+        mAndroidSdkUpdater = new Shell();

+        setWindowImage(mAndroidSdkUpdater);

+        mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {

+            public void widgetDisposed(DisposeEvent e) {

+                onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)

+            }

+        });

+

+        mAndroidSdkUpdater.setLayout(new FillLayout(SWT.HORIZONTAL));

+        mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));

+        mAndroidSdkUpdater.setSize(745, 433);

+        mAndroidSdkUpdater.setText("Android SDK Updater");

+

+        mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);

+

+        mPageList = new List(mSashForm, SWT.BORDER);

+        mPageList.addSelectionListener(new SelectionAdapter() {

+            @Override

+            public void widgetSelected(SelectionEvent e) {

+                onPageListSelected();    //$hide$ (hide from SWT designer)

+            }

+        });

+

+        mPagesRootComposite = new Composite(mSashForm, SWT.NONE);

+        mStackLayout = new StackLayout();

+        mPagesRootComposite.setLayout(mStackLayout);

+

+        mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);

+        mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData, this);

+        mSashForm.setWeights(new int[] {150, 576});

+    }

+

+

+    // -- Start of internal part ----------

+    // Hide everything down-below from SWT designer

+    //$hide>>$

+

+    // --- UI Callbacks -----------

+

+

+    /**

+     * Registers an extra page for the updater window.

+     * <p/>

+     * Pages must derive from {@link Composite} and implement a constructor that takes

+     * a single parent {@link Composite} argument.

+     * <p/>

+     * All pages must be registered before the call to {@link #open()}.

+     *

+     * @param title The title of the page.

+     * @param pageClass The {@link Composite}-derived class that will implement the page.

+     */

+    public void registerExtraPage(String title, Class<? extends Composite> pageClass) {

+        if (mExtraPages == null) {

+            mExtraPages = new ArrayList<Object[]>();

+        }

+        mExtraPages.add(new Object[]{ title, pageClass });

+    }

+

+    /**

+     * Helper to return the SWT shell.

+     */

+    private Shell getShell() {

+        return mAndroidSdkUpdater;

+    }

+

+    /**

+     * Callback called when the window shell is disposed.

+     */

+    private void onAndroidSdkUpdaterDispose() {

+        if (mIconImage != null) {

+            mIconImage.dispose();

+            mIconImage = null;

+        }

+    }

+

+    /**

+     * Creates the icon of the window shell.

+     * The icon is disposed by {@link #onAndroidSdkUpdaterDispose()}.

+     */

+    private void setWindowImage(Shell androidSdkUpdater) {

+        InputStream stream = getClass().getResourceAsStream("android_icon_16.png");  //$NON-NLS-1$

+        if (stream != null) {

+            try {

+                ImageData imgData = new ImageData(stream);

+                mIconImage = new Image(mAndroidSdkUpdater.getDisplay(),

+                        imgData,

+                        imgData.getTransparencyMask());

+                mAndroidSdkUpdater.setImage(mIconImage);

+            } catch (SWTException e) {

+                mUpdaterData.getSdkLog().error(e, "Failed to set window icon");  //$NON-NLS-1$

+            } catch (IllegalArgumentException e) {

+                mUpdaterData.getSdkLog().error(e, "Failed to set window icon");  //$NON-NLS-1$

+            }

+        }

+    }

+

+    /**

+     * Once the UI has been created, initializes the content.

+     * This creates the pages, selects the first one, setup sources and scan for local folders.

+     */

+    private void firstInit() {

+        mTaskFactory = new ProgressTaskFactory(getShell());

+

+        addPage(mLocalPackagePage, "Installed Packages");

+        addPage(mRemotePackagesPage, "Available Packages");

+        addExtraPages();

+

+        displayPage(0);

+        mPageList.setSelection(0);

+

+        // TODO read and apply settings

+        // TODO read add-on sources from some file

+        setupSources();

+        scanLocalSdkFolders();

+    }

+

+    // --- page switching ---

+

+    /**

+     * Adds an instance of a page to the page list.

+     * <p/>

+     * Each page is a {@link Composite}. The title of the page is stored in the

+     * {@link Composite#getData()} field.

+     */

+    private void addPage(Composite page, String title) {

+        page.setData(title);

+        mPages.add(page);

+        mPageList.add(title);

+    }

+

+    /**

+     * Adds all extra pages. For each page, instantiates an instance of the {@link Composite}

+     * using the constructor that takes a single {@link Composite} argument and then adds it

+     * to the page list.

+     */

+    @SuppressWarnings("unchecked")

+    private void addExtraPages() {

+        for (Object[] extraPage : mExtraPages) {

+            String title = (String) extraPage[0];

+            Class<? extends Composite> clazz = (Class<? extends Composite>) extraPage[1];

+

+            // We want the constructor that takes a single Composite as parameter

+            Constructor<? extends Composite> cons;

+            try {

+                cons = clazz.getConstructor(new Class<?>[] { Composite.class });

+                Composite instance = cons.newInstance(new Object[] { mPagesRootComposite });

+                addPage(instance, title);

+

+            } catch (NoSuchMethodException e) {

+                // There is no such constructor.

+                mUpdaterData.getSdkLog().error(e,

+                        "Failed to add extra page %1$s. Constructor args must be (Composite parent).",  //$NON-NLS-1$

+                        clazz.getSimpleName());

+

+            } catch (Exception e) {

+                // Log this instead of crashing the whole app.

+                mUpdaterData.getSdkLog().error(e,

+                        "Failed to add extra page %1$s.",  //$NON-NLS-1$

+                        clazz.getSimpleName());

+            }

+        }

+    }

+

+    /**

+     * Callback invoked when an item is selected in the page list.

+     * If this is not an internal page change, displays the given page.

+     */

+    private void onPageListSelected() {

+        if (mInternalPageChange == false) {

+            int index = mPageList.getSelectionIndex();

+            if (index >= 0) {

+                displayPage(index);

+            }

+        }

+    }

+

+    /**

+     * Displays the page at the given index.

+     *

+     * @param index An index between 0 and {@link #mPages}'s length - 1.

+     */

+    private void displayPage(int index) {

+        Composite page = mPages.get(index);

+        if (page != null) {

+            mStackLayout.topControl = page;

+            mPagesRootComposite.layout(true);

+

+            if (!mInternalPageChange) {

+                mInternalPageChange = true;

+                mPageList.setSelection(index);

+                mInternalPageChange = false;

+            }

+        }

+    }

+

+    /**

+     * Used to initialize the sources.

+     */

+    private void setupSources() {

+        mUpdaterData.getSources().setTaskFactory(mTaskFactory);

+

+        mUpdaterData.getSources().add(

+                new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /* addonOnly */));

+

+        String url = System.getenv("TEMP_SDK_URL"); // TODO STOPSHIP temporary remove before shipping

+        if (url != null) {

+            mUpdaterData.getSources().add(new RepoSource(url, false /* addonOnly */));

+        }

+

+        mRemotePackagesPage.setInput(mUpdaterData.getSourcesAdapter());

+    }

+

+    /**

+     * Used to scan the local SDK folders the first time.

+     */

+    private void scanLocalSdkFolders() {

+        mUpdaterData.getLocalSdkAdapter().setSdkRoot(mUpdaterData.getOsSdkRoot());

+

+        mLocalPackagePage.setInput(mUpdaterData.getLocalSdkAdapter());

+    }

+

+    /**

+     * Install the list of given {@link Archive}s.

+     * @param archives The archives to install. Incompatible ones will be skipped.

+     */

+    public void installArchives(final Collection<Archive> archives) {

+        // TODO move most parts to SdkLib, maybe as part of Archive, making archives self-installing.

+        mTaskFactory.start("Installing Archives", new ITask() {

+            public void run(ITaskMonitor monitor) {

+

+                monitor.setProgressMax(archives.size() * (NUM_FETCH_URL_MONITOR_INC + 3));

+                monitor.setDescription("Preparing to install archives");

+

+                int num_installed = 0;

+                for (Archive archive : archives) {

+

+                    if (!archive.isCompatible()) {

+                        monitor.setResult("Skipping incompatible archive: %1$s",

+                                archive.getShortDescription());

+                        monitor.incProgress(3);

+                        continue;

+                    }

+

+                    File archiveFile = null;

+                    try {

+                        archiveFile = downloadArchive(archive, monitor);

+                        monitor.incProgress(1);

+                        if (archiveFile != null) {

+                            if (installArchive(archive, archiveFile, monitor)) {

+                                num_installed++;

+                            }

+                        }

+                        monitor.incProgress(1);

+                    } finally {

+                        if (archiveFile != null) {

+                            if (!archiveFile.delete()) {

+                                archiveFile.deleteOnExit();

+                            }

+                        }

+                    }

+                }

+

+                if (num_installed == 0) {

+                    monitor.setResult("Nothing was installed.");

+                }

+            }

+        });

+    }

+

+    /**

+     * Downloads an archive and returns the temp file with it.

+     * Caller is responsible with deleting the temp file when done.

+     */

+    private File downloadArchive(Archive archive, ITaskMonitor monitor) {

+

+        try {

+            File tmpFile = File.createTempFile("sdkupload", "bin"); //$NON-NLS-1$ //$NON-NLS-2$

+

+            monitor.setDescription("Downloading %1$s", archive.getShortDescription());

+

+            String link = archive.getUrl();

+            if (!link.startsWith("http://")                          //$NON-NLS-1$

+                    && !link.startsWith("https://")                  //$NON-NLS-1$

+                    && !link.startsWith("ftp://")) {                 //$NON-NLS-1$

+                // Make the URL absolute by prepending the source

+                Package pkg = archive.getParentPackage();

+                RepoSource src = pkg.getParentSource();

+                if (src == null) {

+                    monitor.setResult("Internal error: no source for archive %1$s",

+                            archive.getShortDescription());

+                    return null;

+                }

+

+                String base = src.getUrl();

+                if (!base.endsWith("/") && !link.startsWith("/")) {  //$NON-NLS-1$ //$NON-NLS-2$

+                    base += "/";                                     //$NON-NLS-1$

+                }

+

+                link = base + link;

+            }

+

+            fetchUrl(tmpFile, archive, link, monitor);

+

+        } catch (IOException e) {

+            monitor.setResult(e.getMessage());

+        }

+        return null;

+    }

+

+    /**

+     * Actually performs the download.

+     * Also computes the SHA1 of the file on the fly.

+     * <p/>

+     * Success is defined as downloading as many bytes as was expected and having the same

+     * SHA1 as expected. Returns true on success or false if any of those checks fail.

+     * <p/>

+     * Increments the monitor by {@link #NUM_FETCH_URL_MONITOR_INC} (which is 10).

+     */

+    private boolean fetchUrl(File tmpFile, Archive archive, String urlString, ITaskMonitor monitor) {

+        URL url;

+

+        FileOutputStream os = null;

+        InputStream is = null;

+        try {

+            url = new URL(urlString);

+            is = url.openStream();

+            os = new FileOutputStream(tmpFile);

+

+            MessageDigest digester = archive.getChecksumType().getMessageDigest();

+

+            byte[] buf = new byte[65536];

+            int n;

+

+            long total = 0;

+            long size = archive.getSize();

+            long inc = size / NUM_FETCH_URL_MONITOR_INC;

+            long next_inc = inc;

+

+            while ((n = is.read(buf)) >= 0) {

+                if (n > 0) {

+                    os.write(buf, 0, n);

+                    digester.update(buf, 0, n);

+                }

+

+                total += n;

+                if (total >= next_inc) {

+                    monitor.incProgress(1);

+                    next_inc += inc;

+                }

+

+                if (monitor.cancelRequested()) {

+                    monitor.setResult("Download aborted by user at %1$d bytes.", total);

+                    return false;

+                }

+

+            }

+

+            if (total != size) {

+                monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",

+                        size, total);

+                return false;

+            }

+

+            // Create an hex string from the digest

+            byte[] digest = digester.digest();

+            n = digest.length;

+            String hex = "0123456789abcdef";                     //$NON-NLS-1$

+            char[] hexDigest = new char[n * 2];

+            for (int i = 0; i < n; i++) {

+                byte b = digest[i];

+                hexDigest[i*2 + 0] = hex.charAt(b >>> 4);

+                hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);

+            }

+

+            String expected = archive.getChecksum();

+            String actual   = new String(hexDigest);

+            if (!actual.equalsIgnoreCase(expected)) {

+                monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",

+                        expected, actual);

+                return false;

+            }

+

+            return true;

+

+        } catch (Exception e) {

+            monitor.setResult(e.getMessage());

+

+        } finally {

+            if (os != null) {

+                try {

+                    os.close();

+                } catch (IOException e) {

+                    // pass

+                }

+            }

+

+            if (is != null) {

+                try {

+                    is.close();

+                } catch (IOException e) {

+                    // pass

+                }

+            }

+        }

+

+        return false;

+    }

+

+    private boolean installArchive(Archive archive, File archiveFile, ITaskMonitor monitor) {

+        monitor.setDescription("Installing %1$s", archive.getShortDescription());

+

+        File destFolder = archive.getParentPackage().getInstallFolder(mUpdaterData.getOsSdkRoot());

+

+        return false;

+    }

+

+    // End of hiding from SWT Designer

+    //$hide<<$

+}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/android_icon_16.png b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/android_icon_16.png
similarity index 100%
rename from tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/android_icon_16.png
rename to tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/android_icon_16.png
Binary files differ
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvailablePackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvailablePackagesPage.java
deleted file mode 100755
index 7c4565a..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/AvailablePackagesPage.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.android.sdkuilib.repository;

-

-import com.android.sdkuilib.repository.UpdaterWindow.UpdaterData;

-

-import org.eclipse.jface.viewers.CheckboxTreeViewer;

-import org.eclipse.swt.SWT;

-import org.eclipse.swt.layout.GridData;

-import org.eclipse.swt.layout.GridLayout;

-import org.eclipse.swt.widgets.Button;

-import org.eclipse.swt.widgets.Composite;

-import org.eclipse.swt.widgets.Group;

-import org.eclipse.swt.widgets.Label;

-import org.eclipse.swt.widgets.Tree;

-import org.eclipse.swt.widgets.TreeColumn;

-

-class AvailablePackagesPage extends Composite {

-

-    private final UpdaterData mUpdaterData;

-

-    private CheckboxTreeViewer mTreeViewAvailPkg;

-    private Tree mTreeAvailPkg;

-    private TreeColumn mColumnAvailSummary;

-    private TreeColumn mColumnAvailApiLevel;

-    private TreeColumn mColumnAvailRevision;

-    private TreeColumn mColumnAvailOs;

-    private TreeColumn mColumnAvailInstalled;

-    private Group mAvailDescription;

-    private Button mAvailAddSite;

-    private Button mAvailRemoveSite;

-    private Label mPlaceholder3;

-    private Button mAvailRefresh;

-    private Button mAvailInstallSelected;

-

-

-    /**

-     * Create the composite.

-     * @param parent The parent of the composite.

-     * @param updaterData An instance of {@link UpdaterWindow.UpdaterData}. If null, a local

-     *        one will be allocated just to help with the SWT Designer.

-     */

-    public AvailablePackagesPage(Composite parent, UpdaterData updaterData) {

-        super(parent, SWT.BORDER);

-

-        mUpdaterData = updaterData != null ? updaterData : new UpdaterWindow.UpdaterData();

-

-        createContents(this);

-    }

-

-    private void createContents(Composite parent) {

-        parent.setLayout(new GridLayout(5, false));

-

-        mTreeViewAvailPkg = new CheckboxTreeViewer(parent, SWT.BORDER);

-        mTreeViewAvailPkg.setContentProvider(mUpdaterData.getSources().getContentProvider());

-        mTreeViewAvailPkg.setLabelProvider(mUpdaterData.getSources().getLabelProvider());

-        mTreeAvailPkg = mTreeViewAvailPkg.getTree();

-        mTreeAvailPkg.setHeaderVisible(true);

-        mTreeAvailPkg.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));

-

-        mColumnAvailSummary = new TreeColumn(mTreeAvailPkg, SWT.NONE);

-        mColumnAvailSummary.setWidth(289);

-        mColumnAvailSummary.setText("Summary");

-

-        mColumnAvailApiLevel = new TreeColumn(mTreeAvailPkg, SWT.NONE);

-        mColumnAvailApiLevel.setWidth(66);

-        mColumnAvailApiLevel.setText("API Level");

-

-        mColumnAvailRevision = new TreeColumn(mTreeAvailPkg, SWT.NONE);

-        mColumnAvailRevision.setWidth(63);

-        mColumnAvailRevision.setText("Revision");

-

-        mColumnAvailOs = new TreeColumn(mTreeAvailPkg, SWT.NONE);

-        mColumnAvailOs.setWidth(100);

-        mColumnAvailOs.setText("OS/Arch");

-

-        mColumnAvailInstalled = new TreeColumn(mTreeAvailPkg, SWT.NONE);

-        mColumnAvailInstalled.setWidth(59);

-        mColumnAvailInstalled.setText("Installed");

-

-        mAvailDescription = new Group(parent, SWT.NONE);

-        mAvailDescription.setText("Description");

-        mAvailDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 5, 1));

-

-        mAvailAddSite = new Button(parent, SWT.NONE);

-        mAvailAddSite.setText("Add Site...");

-

-        mAvailRemoveSite = new Button(parent, SWT.NONE);

-        mAvailRemoveSite.setText("Delete Site...");

-

-        mPlaceholder3 = new Label(parent, SWT.NONE);

-        mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));

-

-        mAvailRefresh = new Button(parent, SWT.NONE);

-        mAvailRefresh.setText("Refresh");

-

-        mAvailInstallSelected = new Button(parent, SWT.NONE);

-        mAvailInstallSelected.setText("Install Selected");

-    }

-

-    @Override

-    protected void checkSubclass() {

-        // Disable the check that prevents subclassing of SWT components

-    }

-

-    // -- Start of internal part ----------

-    // Hide everything down-below from SWT designer

-    //$hide>>$

-

-    public void setInput(RepoSources sources) {

-        mTreeViewAvailPkg.setInput(sources);

-    }

-

-

-    // End of hiding from SWT Designer

-    //$hide<<$

-

-}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ITaskMonitor.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ITaskMonitor.java
deleted file mode 100755
index 72375ab..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/ITaskMonitor.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*

- * Copyright (C) 2009 The Android Open Source Project

- *

- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php

- *

- * 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 com.android.sdkuilib.repository;

-

-import org.eclipse.swt.widgets.ProgressBar;

-

-/**

- * A monitor interface for a {@link ProgressTask}

- */

-interface ITaskMonitor {

-

-    /**

-     * Sets the description in the current task dialog.

-     * This method can be invoke from a non-UI thread.

-     */

-    public void setDescription(String description);

-

-    /**

-     * Sets the result text in the current task dialog.

-     * This method can be invoked from a non-UI thread.

-     */

-    public void setResult(String result);

-

-    /**

-     * Sets the max value of the progress bar.

-     * This method can be invoke from a non-UI thread.

-     *

-     * @see ProgressBar#setMaximum(int)

-     */

-    public void setProgressMax(int max);

-

-    /**

-     * Increments the current value of the progress bar.

-     *

-     * This method can be invoked from a non-UI thread.

-     */

-    public void incProgress(int delta);

-

-    /**

-     * Returns true if the "Cancel" button was selected.

-     * It is up to the task thread to pool this and exit.

-     */

-    public boolean cancelRequested();

-

-

-}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/InstalledPackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/InstalledPackagesPage.java
deleted file mode 100755
index 95d180d..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/InstalledPackagesPage.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package com.android.sdkuilib.repository;

-

-import com.android.sdkuilib.repository.ProgressTask.ThreadTask;

-import com.android.sdkuilib.repository.UpdaterWindow.UpdaterData;

-

-import org.eclipse.jface.viewers.TableViewer;

-import org.eclipse.swt.SWT;

-import org.eclipse.swt.events.SelectionAdapter;

-import org.eclipse.swt.events.SelectionEvent;

-import org.eclipse.swt.layout.GridData;

-import org.eclipse.swt.layout.GridLayout;

-import org.eclipse.swt.widgets.Button;

-import org.eclipse.swt.widgets.Composite;

-import org.eclipse.swt.widgets.Group;

-import org.eclipse.swt.widgets.Label;

-import org.eclipse.swt.widgets.Table;

-import org.eclipse.swt.widgets.TableColumn;

-import org.eclipse.swt.widgets.Text;

-

-public class InstalledPackagesPage extends Composite {

-    private UpdaterData mUpdaterData;

-

-    private Label mSdkLocLabel;

-    private Text mSdkLocText;

-    private Button mSdkLocBrowse;

-    private Label mInstalledPkgLabel;

-    private TableViewer mTableViewerInstPkg;

-    private Table mTableInstPkg;

-    private TableColumn mColumnInstSummary;

-    private TableColumn mColumnInstApiLevel;

-    private TableColumn mColumnInstRevision;

-    private Group mInstDescription;

-    private Composite mInstButtons;

-    private Button mInstUpdate;

-    private Label mPlaceholder1;

-    private Button mInstDelete;

-    private Label mPlaceholder2;

-    private Button mInstHomePage;

-

-    /**

-     * Create the composite.

-     * @param parent The parent of the composite.

-     * @param updaterData An instance of {@link UpdaterWindow.UpdaterData}. If null, a local

-     *        one will be allocated just to help with the SWT Designer.

-     */

-    public InstalledPackagesPage(Composite parent, UpdaterData updaterData) {

-        super(parent, SWT.BORDER);

-

-        mUpdaterData = updaterData != null ? updaterData : new UpdaterWindow.UpdaterData();

-

-        createContents(this);

-    }

-

-    private void createContents(Composite parent) {

-        parent.setLayout(new GridLayout(3, false));

-

-        createSdkLocation(parent);

-

-        mInstalledPkgLabel = new Label(parent, SWT.NONE);

-        mInstalledPkgLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));

-        mInstalledPkgLabel.setText("Installed Packages:");

-

-        mTableViewerInstPkg = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);

-        mTableInstPkg = mTableViewerInstPkg.getTable();

-        mTableInstPkg.setHeaderVisible(true);

-        mTableInstPkg.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));

-

-        mColumnInstSummary = new TableColumn(mTableInstPkg, SWT.NONE);

-        mColumnInstSummary.setWidth(377);

-        mColumnInstSummary.setText("Summary");

-

-        mColumnInstApiLevel = new TableColumn(mTableInstPkg, SWT.NONE);

-        mColumnInstApiLevel.setWidth(100);

-        mColumnInstApiLevel.setText("API Level");

-

-        mColumnInstRevision = new TableColumn(mTableInstPkg, SWT.NONE);

-        mColumnInstRevision.setWidth(100);

-        mColumnInstRevision.setText("Revision");

-

-        mInstDescription = new Group(parent, SWT.NONE);

-        mInstDescription.setText("Description");

-        mInstDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));

-

-        mInstButtons = new Composite(parent, SWT.NONE);

-        mInstButtons.setLayout(new GridLayout(5, false));

-        mInstButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));

-

-        mInstUpdate = new Button(mInstButtons, SWT.NONE);

-        mInstUpdate.addSelectionListener(new SelectionAdapter() {

-            @Override

-            public void widgetSelected(SelectionEvent e) {

-                onUpdateInstalledPackage();  //$hide$ (hide from SWT designer)

-            }

-        });

-        mInstUpdate.setText("Update...");

-

-        mPlaceholder1 = new Label(mInstButtons, SWT.NONE);

-        mPlaceholder1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));

-

-        mInstDelete = new Button(mInstButtons, SWT.NONE);

-        mInstDelete.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));

-        mInstDelete.setText("Delete...");

-

-        mPlaceholder2 = new Label(mInstButtons, SWT.NONE);

-        mPlaceholder2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));

-

-        mInstHomePage = new Button(mInstButtons, SWT.NONE);

-        mInstHomePage.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

-        mInstHomePage.setText("Home Page...");

-    }

-

-    private void createSdkLocation(Composite parent) {

-        mSdkLocLabel = new Label(parent, SWT.NONE);

-        mSdkLocLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));

-        mSdkLocLabel.setText("SDK Location:");

-

-        // If the sdk path is not user-customizable, do not create

-        // the browse button and use horizSpan=2 on the text field.

-

-        mSdkLocText = new Text(parent, SWT.BORDER);

-        mSdkLocText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

-

-        if (mUpdaterData.canUserChangeSdkRoot()) {

-            mSdkLocBrowse = new Button(parent, SWT.NONE);

-            mSdkLocBrowse.setText("Browse...");

-        } else {

-            mSdkLocText.setEditable(false);

-            ((GridData)mSdkLocText.getLayoutData()).horizontalSpan++;

-        }

-

-        if (mUpdaterData.getOsSdkRoot() != null) {

-            mSdkLocText.setText(mUpdaterData.getOsSdkRoot());

-        }

-    }

-

-    @Override

-    protected void checkSubclass() {

-        // Disable the check that prevents subclassing of SWT components

-    }

-

-    // -- Start of internal part ----------

-    // Hide everything down-below from SWT designer

-    //$hide>>$

-

-    protected void onUpdateInstalledPackage() {

-        ProgressTask.start(getShell(), "Test", new ThreadTask() {

-            public void PerformTask(ITaskMonitor monitor) {

-                monitor.setDescription("Test");

-                monitor.setProgressMax(100);

-                int n = 0;

-                int d = 1;

-                while(!monitor.cancelRequested()) {

-                    monitor.incProgress(d);

-                    n += d;

-                    if (n == 0 || n == 100) d = -d;

-                    try {

-                        Thread.sleep(5);

-                    } catch (InterruptedException e) {

-                    }

-                }

-            }

-        });

-    }

-

-

-    // End of hiding from SWT Designer

-    //$hide<<$

-}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSources.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSources.java
deleted file mode 100755
index c06e16d..0000000
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/RepoSources.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*

- * Copyright (C) 2009 The Android Open Source Project

- *

- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php

- *

- * 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 com.android.sdkuilib.repository;

-

-import org.eclipse.jface.viewers.IContentProvider;

-import org.eclipse.jface.viewers.ILabelProvider;

-import org.eclipse.jface.viewers.ITreeContentProvider;

-import org.eclipse.jface.viewers.LabelProvider;

-import org.eclipse.jface.viewers.Viewer;

-import org.eclipse.swt.graphics.Image;

-import org.eclipse.swt.widgets.Shell;

-

-import java.util.ArrayList;

-

-/**

- *

- */

-class RepoSources {

-

-    private Shell mShell;

-    private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();

-

-    public RepoSources() {

-    }

-

-    public void setShell(Shell shell) {

-        mShell = shell;

-    }

-

-    public void add(RepoSource source) {

-        mSources.add(source);

-    }

-

-    public ILabelProvider getLabelProvider() {

-        return new ViewerLabelProvider();

-    }

-

-

-    public IContentProvider getContentProvider() {

-        return new TreeContentProvider();

-    }

-

-    // ------------

-

-    public class ViewerLabelProvider extends LabelProvider {

-        /** Returns null by default */

-        @Override

-        public Image getImage(Object element) {

-            return super.getImage(element);

-        }

-

-        /** Returns the toString of the element. */

-        @Override

-        public String getText(Object element) {

-            return super.getText(element);

-        }

-    }

-

-    // ------------

-

-    private class TreeContentProvider implements ITreeContentProvider {

-

-        private Object mInput;

-

-        // Called when the viewer is disposed

-        public void dispose() {

-            // pass

-        }

-

-        // Called when the input is set or changed on the provider

-        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

-            mInput = newInput;

-            // pass

-        }

-

-        /**

-         * Called to collect the root elements for the given input.

-         * The input here is a {@link RepoSources} object, this returns an array

-         * of {@link RepoSource}.

-         */

-        public Object[] getElements(Object inputElement) {

-            return getChildren(inputElement);

-        }

-

-        /**

-         * Get the children of the given parent. This is requested on-demand as

-         * nodes are expanded.

-         *

-         * For a {@link RepoSources} object, returns an array of {@link RepoSource}.

-         * For a {@link RepoSource}, returns an array of packages.

-         */

-        public Object[] getChildren(Object parentElement) {

-            if (parentElement instanceof RepoSources) {

-                return ((RepoSources) parentElement).mSources.toArray();

-

-            } else if (parentElement instanceof RepoSource) {

-                RepoSource source = (RepoSource) parentElement;

-                ArrayList<String> pkgs = source.getPackages();

-

-                if (pkgs == null) {

-                    source.load(mShell);

-                    pkgs = source.getPackages();

-                }

-                if (pkgs != null) {

-                    return pkgs.toArray();

-                }

-            }

-

-            return new Object[0];

-        }

-

-        /**

-         * Returns the parent of a given element.

-         * The input {@link RepoSources} is the parent of all {@link RepoSource} elements.

-         */

-        public Object getParent(Object element) {

-            if (element instanceof RepoSource) {

-                return mInput;

-            }

-            return null;

-        }

-

-        /**

-         * Returns true if a given element has children, which is used to display a

-         * "+/expand" box next to the tree node.

-         * All {@link RepoSource} are expandable, whether they actually have any childre or not.

-         */

-        public boolean hasChildren(Object element) {

-            return element instanceof RepoSource;

-        }

-    }

-

-}

diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
index a9f5cdd..8ca3324 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
@@ -1,11 +1,11 @@
 /*

  * Copyright (C) 2009 The Android Open Source Project

  *

- * Licensed under the Eclipse Public License, Version 1.0 (the "License");

+ * 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.eclipse.org/org/documents/epl-v10.php

+ *      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,

@@ -16,230 +16,50 @@
 

 package com.android.sdkuilib.repository;

 

-import org.eclipse.swt.SWT;

-import org.eclipse.swt.SWTException;

-import org.eclipse.swt.custom.SashForm;

-import org.eclipse.swt.custom.StackLayout;

-import org.eclipse.swt.events.DisposeEvent;

-import org.eclipse.swt.events.DisposeListener;

-import org.eclipse.swt.events.SelectionAdapter;

-import org.eclipse.swt.events.SelectionEvent;

-import org.eclipse.swt.graphics.Image;

-import org.eclipse.swt.graphics.ImageData;

-import org.eclipse.swt.graphics.Point;

-import org.eclipse.swt.layout.FillLayout;

+import com.android.sdklib.ISdkLog;

+import com.android.sdkuilib.internal.repository.UpdaterWindowImpl;

+

 import org.eclipse.swt.widgets.Composite;

-import org.eclipse.swt.widgets.Display;

-import org.eclipse.swt.widgets.List;

-import org.eclipse.swt.widgets.Shell;

 

-import java.io.InputStream;

-import java.util.ArrayList;

-

+/**

+ * Opens an SDK Updater Window.

+ *

+ * This is the public interface for using the window.

+ */

 public class UpdaterWindow {

 

-    static class UpdaterData {

-        private String mOsSdkRoot;

-        private boolean mUserCanChangeSdkRoot;

-        private RepoSources mSources = new RepoSources();

+    private UpdaterWindowImpl mWindow;

 

-        public void setOsSdkRoot(String osSdkRoot) {

-            mOsSdkRoot = osSdkRoot;

-        }

-

-        public String getOsSdkRoot() {

-            return mOsSdkRoot;

-        }

-

-        public void setUserCanChangeSdkRoot(boolean userCanChangeSdkRoot) {

-            mUserCanChangeSdkRoot = userCanChangeSdkRoot;

-        }

-

-        public boolean canUserChangeSdkRoot() {

-            return mUserCanChangeSdkRoot;

-        }

-

-        public void setSources(RepoSources sources) {

-            mSources = sources;

-        }

-

-        public RepoSources getSources() {

-            return mSources;

-        }

-    }

-

-    private final UpdaterData mUpdaterData = new UpdaterData();

-    private ArrayList<Composite> mPages = new ArrayList<Composite>();

-    private boolean mInternalPageChange;

-

-    // --- UI members ---

-

-    protected Shell mAndroidSdkUpdater;

-    private SashForm mSashForm;

-    private List mPageList;

-    private Composite mPagesRootComposite;

-    private InstalledPackagesPage mInstalledPackagePage;

-    private AvailablePackagesPage mAvailablePackagesPage;

-    private StackLayout mStackLayout;

-    private Image mIconImage;

-

-    public UpdaterWindow(String osSdkRoot, boolean userCanChangeSdkRoot) {

-        mUpdaterData.setOsSdkRoot(osSdkRoot);

-        mUpdaterData.setUserCanChangeSdkRoot(userCanChangeSdkRoot);

+    /**

+     * Creates a new window. Caller must call open(), which will block.

+     * @param sdkLog

+     * @param osSdkRoot The OS path to the SDK root.

+     * @param userCanChangeSdkRoot If true, the window lets the user change the SDK path

+     *                             being browsed.

+     */

+    public UpdaterWindow(ISdkLog sdkLog, String osSdkRoot, boolean userCanChangeSdkRoot) {

+        mWindow = new UpdaterWindowImpl(sdkLog, osSdkRoot, userCanChangeSdkRoot);

     }

 

     /**

-     * Open the window.

-     * @wbp.parser.entryPoint

+     * Registers an extra page for the updater window.

+     * <p/>

+     * Pages must derive from {@link Composite} and implement a constructor that takes

+     * a single parent {@link Composite} argument.

+     * <p/>

+     * All pages must be registered before the call to {@link #open()}.

+     *

+     * @param title The title of the page.

+     * @param pageClass The {@link Composite}-derived class that will implement the page.

+     */

+    public void registerPage(String title, Class<? extends Composite> pageClass) {

+        mWindow.registerExtraPage(title, pageClass);

+    }

+

+    /**

+     * Opens the window.

      */

     public void open() {

-        Display display = Display.getDefault();

-        createContents();

-        mAndroidSdkUpdater.open();

-        mAndroidSdkUpdater.layout();

-

-        firstInit();    //$hide$ (hide from SWT designer)

-

-        while (!mAndroidSdkUpdater.isDisposed()) {

-            if (!display.readAndDispatch()) {

-                display.sleep();

-            }

-        }

+        mWindow.open();

     }

-

-    /**

-     * Create contents of the window.

-     */

-    protected void createContents() {

-        mAndroidSdkUpdater = new Shell();

-        setWindowImage(mAndroidSdkUpdater);

-        mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {

-            public void widgetDisposed(DisposeEvent e) {

-                onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)

-            }

-        });

-

-        mAndroidSdkUpdater.setLayout(new FillLayout(SWT.HORIZONTAL));

-        mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));

-        mAndroidSdkUpdater.setSize(745, 433);

-        mAndroidSdkUpdater.setText("Android SDK Updater");

-

-        mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);

-

-        mPageList = new List(mSashForm, SWT.BORDER);

-        mPageList.addSelectionListener(new SelectionAdapter() {

-            @Override

-            public void widgetSelected(SelectionEvent e) {

-                onPageListSelected(e);    //$hide$ (hide from SWT designer)

-            }

-        });

-

-        mPagesRootComposite = new Composite(mSashForm, SWT.NONE);

-        mStackLayout = new StackLayout();

-        mPagesRootComposite.setLayout(mStackLayout);

-

-        mInstalledPackagePage = new InstalledPackagesPage(mPagesRootComposite, mUpdaterData);

-        mAvailablePackagesPage = new AvailablePackagesPage(mPagesRootComposite, mUpdaterData);

-        mSashForm.setWeights(new int[] {150, 576});

-    }

-

-

-    // -- Start of internal part ----------

-    // Hide everything down-below from SWT designer

-    //$hide>>$

-

-    // --- UI Callbacks -----------

-

-    private void onAndroidSdkUpdaterDispose() {

-        if (mIconImage != null) {

-            mIconImage.dispose();

-            mIconImage = null;

-        }

-    }

-

-    private void setWindowImage(Shell androidSdkUpdater) {

-        InputStream stream = getClass().getResourceAsStream("android_icon_16.png");  //$NON-NLS-1$

-        if (stream != null) {

-            try {

-                ImageData imgData = new ImageData(stream);

-                mIconImage = new Image(mAndroidSdkUpdater.getDisplay(),

-                        imgData,

-                        imgData.getTransparencyMask());

-                mAndroidSdkUpdater.setImage(mIconImage);

-            } catch (SWTException e) {

-                // ignore

-            } catch (IllegalArgumentException e) {

-                // ignore

-            }

-        }

-    }

-

-    private Shell getShell() {

-        return mAndroidSdkUpdater;

-    }

-

-    /**

-     * Once the UI has been created, initialize the content

-     */

-    private void firstInit() {

-        addPage(mInstalledPackagePage, "Installed Packages");

-        addPage(mAvailablePackagesPage, "Available Packages");

-        displayPage(0);

-        mPageList.setSelection(0);

-

-        setupSources();

-        scanLocalSdkFolders();

-    }

-

-    // --- page switching ---

-

-    private void addPage(Composite page, String title) {

-        page.setData(title);

-        mPages.add(page);

-        mPageList.add(title);

-    }

-

-    private void onPageListSelected(SelectionEvent e) {

-        if (mInternalPageChange == false) {

-            int index = mPageList.getSelectionIndex();

-            if (index >= 0) {

-                displayPage(index);

-            }

-        }

-    }

-

-    private void displayPage(int index) {

-        Composite page = mPages.get(index);

-        if (page != null) {

-            mStackLayout.topControl = page;

-            mPagesRootComposite.layout(true);

-

-            if (!mInternalPageChange) {

-                mInternalPageChange = true;

-                mPageList.setSelection(index);

-                mInternalPageChange = false;

-            }

-        }

-    }

-

-    private void setupSources() {

-        mUpdaterData.getSources().setShell(getShell());

-

-        mUpdaterData.getSources().add(new RepoSource(

-                "https://dl.google.com/android/eclipse/repository/index.xml",          //$NON-NLS-1$

-                false /* addonOnly */));

-        mUpdaterData.getSources().add(new RepoSource(

-                "http://www.corp.google.com/~raphael/android/sdk-repo/repository.xml", //$NON-NLS-1$

-                false /* addonOnly */));

-

-        mAvailablePackagesPage.setInput(mUpdaterData.getSources());

-    }

-

-    private void scanLocalSdkFolders() {

-        // TODO Auto-generated method stub

-

-    }

-

-    // End of hiding from SWT Designer

-    //$hide<<$

 }