Merge "Fix proguard flags."
diff --git a/apps/BugReportSender/Android.mk b/apps/BugReportSender/Android.mk
index b94f802..1c3a5e4 100644
--- a/apps/BugReportSender/Android.mk
+++ b/apps/BugReportSender/Android.mk
@@ -18,6 +18,6 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_MODULE_TAGS := eng
 LOCAL_PACKAGE_NAME := BugReportSender
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 4
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 include $(BUILD_PACKAGE)
diff --git a/apps/BugReportSender/AndroidManifest.xml b/apps/BugReportSender/AndroidManifest.xml
index aee89f1..4d1f97d 100644
--- a/apps/BugReportSender/AndroidManifest.xml
+++ b/apps/BugReportSender/AndroidManifest.xml
@@ -15,16 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.bugreportsender" >
+        package="com.android.bugreportsender"
+        android:versionCode="11" >
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
+    <!-- Minimum Donut, target Froyo -->
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="7" />
+
     <application
             android:icon="@drawable/icon"
-            android:label="Bug Report Sender"
-            android:versionCode="1" >
-
-        <uses-sdk android:minSdkVersion="4" />
+            android:label="Bug Report Sender" >
 
         <activity android:name="BugReportListActivity">
             <intent-filter>
diff --git a/apps/Development/Android.mk b/apps/Development/Android.mk
index fa929b6..1cef548 100644
--- a/apps/Development/Android.mk
+++ b/apps/Development/Android.mk
@@ -3,7 +3,6 @@
 
 LOCAL_MODULE_TAGS := eng
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-common
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files) \
diff --git a/apps/Development/src/com/android/development/PointerLocation.java b/apps/Development/src/com/android/development/PointerLocation.java
index 88828b0..a8b64e3 100644
--- a/apps/Development/src/com/android/development/PointerLocation.java
+++ b/apps/Development/src/com/android/development/PointerLocation.java
@@ -16,7 +16,7 @@
 
 package com.android.development;
 
-import com.android.common.ui.PointerLocationView;
+import com.android.internal.widget.PointerLocationView;
 
 import android.app.Activity;
 import android.os.Bundle;
diff --git a/build/sdk.atree b/build/sdk.atree
index a571949..a2d22a7 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -65,10 +65,11 @@
 sdk/templates/icon_hdpi.png platforms/${PLATFORM_NAME}/templates/icon_hdpi.png
 
 # sdk.git files
-sdk/files/ant.properties platforms/${PLATFORM_NAME}/templates/ant.properties
-sdk/files/android_rules.xml platforms/${PLATFORM_NAME}/templates/android_rules.xml
+sdk/files/ant.properties         platforms/${PLATFORM_NAME}/templates/ant.properties
+sdk/files/android_rules.xml      platforms/${PLATFORM_NAME}/templates/android_rules.xml
+sdk/files/android_lib_rules.xml  platforms/${PLATFORM_NAME}/templates/android_lib_rules.xml
 sdk/files/android_test_rules.xml platforms/${PLATFORM_NAME}/templates/android_test_rules.xml
-sdk/files/devices.xml tools/lib/devices.xml
+sdk/files/devices.xml            tools/lib/devices.xml
 
 # emacs support from sdk.git
 sdk/files/android.el tools/lib/android.el
diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh
index d30c956..6afc185 100755
--- a/build/tools/make_windows_sdk.sh
+++ b/build/tools/make_windows_sdk.sh
@@ -15,14 +15,11 @@
 
 set -e  # Fail this script as soon as a command fails -- fail early, fail fast
 
-# Set to 1 to force removal of old unzipped SDK. Only disable for debugging, as it
-# will make some rm/mv commands to fail.
-FORCE="1" 
 
 PROG_NAME="$0"
-SDK_ZIP="$1"
-DIST_DIR="$2"
-TEMP_DIR="$3"
+SDK_ZIP="$1"; shift
+DIST_DIR="$1"; shift
+TEMP_DIR="$1"; shift
 [ -z "$TEMP_DIR" ] && TEMP_DIR=${TMP:-/tmp}
 
 function die() {
@@ -51,6 +48,18 @@
     [ -f "$SDK_ZIP" ] || usage
     [ -d "$DIST_DIR" ] || usage
 
+
+    # We need mgwz.dll in the SDK when compiling with Cygwin 1.5
+    # Right now we don't support building with Cygwin 1.7 yet, as it lacks this DLL.
+    NEED_MGWZ=1
+    # We can skip this check for debug purposes.
+    echo $*
+    [[ "$1" == "-no-mgwz" ]] && NEED_MGWZ=""
+    CYG_MGWZ_PATH=/cygdrive/c/cygwin/bin/mgwz.dll
+    [[ -n $NEED_MGWZ && ! -f $CYG_MGWZ_PATH ]] && \
+        die "Cygwin is missing $CYG_MGWZ_PATH. Use -no-mgwz to override."
+
+
     # Use the BUILD_ID as SDK_NUMBER if defined, otherwise try to get it from the
     # provided zip filename.
     if [ -f config/build_id.make ]; then
@@ -110,6 +119,9 @@
         zipalign \
         || die "Build failed"
 
+    # Fix permissions. Git/cygwin may not make this +x as needed.
+    chmod +x prebuilt/windows/sdl/bin/sdl-config
+
     # It's worth building the emulator with -j 4 so do it separately
     make -j 4 emulator || die "Build failed"
 }
@@ -123,14 +135,12 @@
     TEMP_SDK_DIR="$TEMP_DIR/$DEST_NAME"
 
     # Unzip current linux/mac SDK and rename using the windows name
-    if [[ -n "$FORCE" || ! -d "$TEMP_SDK_DIR" ]]; then
-        [ -e "$TEMP_SDK_DIR" ] && rm -rfv "$TEMP_SDK_DIR"  # cleanup dest first if exists
-        UNZIPPED=`basename "$SDK_ZIP"`
-        UNZIPPED="$TEMP_DIR/${UNZIPPED/.zip/}"
-        [ -e "$UNZIPPED" ] && rm -rfv "$UNZIPPED"  # cleanup unzip dir (if exists)
-        unzip "$SDK_ZIP" -d "$TEMP_DIR"
-        mv -v "$UNZIPPED" "$TEMP_SDK_DIR"
-    fi
+    [ -e "$TEMP_SDK_DIR" ] && rm -rfv "$TEMP_SDK_DIR"  # cleanup dest first if exists
+    UNZIPPED=`basename "$SDK_ZIP"`
+    UNZIPPED="$TEMP_DIR/${UNZIPPED/.zip/}"
+    [ -e "$UNZIPPED" ] && rm -rfv "$UNZIPPED"  # cleanup unzip dir (if exists)
+    unzip "$SDK_ZIP" -d "$TEMP_DIR"
+    mv -v "$UNZIPPED" "$TEMP_SDK_DIR"
     
     # Assert that the package contains only one platform
     PLATFORMS="$TEMP_SDK_DIR/platforms"
@@ -172,7 +182,7 @@
     # cp -v external/qemu/NOTICE "$TOOLS"/emulator_NOTICE.txt
 
     # We currently need libz from MinGW for aapt
-    cp -v /cygdrive/c/cygwin/bin/mgwz.dll "$TOOLS"/
+    [[ -n $NEED_MGWZ ]] && cp -v $CYG_MGWZ_PATH "$TOOLS"/
 
     # Update a bunch of bat files
     cp -v sdk/files/post_tools_install.bat            "$LIB"/
@@ -215,7 +225,7 @@
     cp -v dalvik/dx/etc/dx.bat "$PLATFORM_TOOLS"/
     mv -v "$TOOLS"/{aapt.exe,aidl.exe,dexdump.exe} "$PLATFORM_TOOLS"/
     # Note: mgwz.dll must be both in SDK/tools for zipalign and in SDK/platform/XYZ/tools/ for aapt
-    cp -v "$TOOLS"/mgwz.dll "$PLATFORM_TOOLS"/
+    [[ -n $NEED_MGWZ ]] && cp -v "$TOOLS"/mgwz.dll "$PLATFORM_TOOLS"/
 
     # Fix EOL chars to make window users happy - fix all files at the top level only
     # as well as all batch files including those in platforms/<name>/tools/
@@ -241,7 +251,7 @@
     echo "Resulting SDK is in $DIST_DIR/$DEST_NAME_ZIP"
 }
 
-check
+check $*
 status
 build
 package
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index 693cbfd..eb2fa76 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -42,7 +42,7 @@
 	<classpathentry kind="src" path="frameworks/base/sax/java"/>
 	<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/test-runner/src"/>
 	<classpathentry kind="src" path="frameworks/base/vpn/java"/>
 	<classpathentry kind="src" path="frameworks/base/wifi/java"/>
 	<classpathentry kind="src" path="frameworks/policies/base/phone"/>
@@ -107,6 +107,6 @@
 	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/google-common_intermediates/javalib.jar"/>
 	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/gsf-client_intermediates/javalib.jar"/>
 	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/mms-common_intermediates/javalib.jar"/>
-	<classpathentry kind="lib" path="packages/apps/Calculator/arity-2.0.2.jar"/>
+	<classpathentry kind="lib" path="packages/apps/Calculator/arity-2.1.2.jar"/>
 	<classpathentry kind="output" path="out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes"/>
 </classpath>
diff --git a/ndk/apps/hello-gl2/project/AndroidManifest.xml b/ndk/apps/hello-gl2/project/AndroidManifest.xml
index 0ef6fb0..5a4d5f2 100644
--- a/ndk/apps/hello-gl2/project/AndroidManifest.xml
+++ b/ndk/apps/hello-gl2/project/AndroidManifest.xml
@@ -32,5 +32,6 @@
             </intent-filter>
         </activity>
     </application>
+    <uses-feature android:glEsVersion="0x00020000"/>
     <uses-sdk android:minSdkVersion="5"/>
 </manifest>
diff --git a/ndk/apps/hello-gl2/project/src/com/android/gl2jni/GL2JNIView.java b/ndk/apps/hello-gl2/project/src/com/android/gl2jni/GL2JNIView.java
index 72b1dfb..060290a 100644
--- a/ndk/apps/hello-gl2/project/src/com/android/gl2jni/GL2JNIView.java
+++ b/ndk/apps/hello-gl2/project/src/com/android/gl2jni/GL2JNIView.java
@@ -33,6 +33,7 @@
 
 
 import android.content.Context;
+import android.graphics.PixelFormat;
 import android.opengl.GLSurfaceView;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -46,16 +47,26 @@
 import javax.microedition.khronos.opengles.GL10;
 
 /**
- * An implementation of SurfaceView that uses the dedicated surface for
- * displaying an OpenGL animation.  This allows the animation to run in a
- * separate thread, without requiring that it be driven by the update mechanism
- * of the view hierarchy.
+ * A simple GLSurfaceView sub-class that demonstrate how to perform
+ * OpenGL ES 2.0 rendering into a GL Surface. Note the following important
+ * details:
  *
- * The application-specific rendering code is delegated to a GLView.Renderer
- * instance.
+ * - The class must use a custom context factory to enable 2.0 rendering.
+ *   See ContextFactory class definition below.
+ *
+ * - The class must use a custom EGLConfigChooser to be able to select
+ *   an EGLConfig that supports 2.0. This is done by providing a config
+ *   specification to eglChooseConfig() that has the attribute
+ *   EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag
+ *   set. See ConfigChooser class definition below.
+ *
+ * - The class must select the surface's format, then choose an EGLConfig
+ *   that matches it exactly (with regards to red/green/blue/alpha channels
+ *   bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
  */
 class GL2JNIView extends GLSurfaceView {
     private static String TAG = "GL2JNIView";
+    private static final boolean DEBUG = false;
 
     public GL2JNIView(Context context) {
         super(context);
@@ -68,10 +79,31 @@
     }
 
     private void init(boolean translucent, int depth, int stencil) {
+
+        /* By default, GLSurfaceView() creates a RGB_565 opaque surface.
+         * If we want a translucent one, we should change the surface's
+         * format here, using PixelFormat.TRANSLUCENT for GL Surfaces
+         * is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
+         */
+        if (translucent) {
+            this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+        }
+
+        /* Setup the context factory for 2.0 rendering.
+         * See ContextFactory class definition below
+         */
         setEGLContextFactory(new ContextFactory());
+
+        /* We need to choose an EGLConfig that matches the format of
+         * our surface exactly. This is going to be done in our
+         * custom config chooser. See ConfigChooser class definition
+         * below.
+         */
         setEGLConfigChooser( translucent ?
-              new ConfigChooser(8,8,8,8, depth, stencil) :
-              new ConfigChooser(5,6,5,0, depth, stencil));
+                             new ConfigChooser(8, 8, 8, 8, depth, stencil) :
+                             new ConfigChooser(5, 6, 5, 0, depth, stencil) );
+
+        /* Set the renderer responsible for frame rendering */
         setRenderer(new Renderer());
     }
 
@@ -99,15 +131,6 @@
     }
 
     private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
-        private static int EGL_OPENGL_ES2_BIT = 4;
-        private static int[] s_configAttribs2 =
-        {
-            EGL10.EGL_RED_SIZE, 4,
-            EGL10.EGL_GREEN_SIZE, 4,
-            EGL10.EGL_BLUE_SIZE, 4,
-            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-            EGL10.EGL_NONE
-        };
 
         public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
             mRedSize = r;
@@ -118,8 +141,24 @@
             mStencilSize = stencil;
         }
 
+        /* This EGL config specification is used to specify 2.0 rendering.
+         * We use a minimum size of 4 bits for red/green/blue, but will
+         * perform actual matching in chooseConfig() below.
+         */
+        private static int EGL_OPENGL_ES2_BIT = 4;
+        private static int[] s_configAttribs2 =
+        {
+            EGL10.EGL_RED_SIZE, 4,
+            EGL10.EGL_GREEN_SIZE, 4,
+            EGL10.EGL_BLUE_SIZE, 4,
+            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL10.EGL_NONE
+        };
+
         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
 
+            /* Get the number of minimally matching EGL configurations
+             */
             int[] num_config = new int[1];
             egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
 
@@ -128,41 +167,46 @@
             if (numConfigs <= 0) {
                 throw new IllegalArgumentException("No configs match configSpec");
             }
+
+            /* Allocate then read the array of minimally matching EGL configs
+             */
             EGLConfig[] configs = new EGLConfig[numConfigs];
             egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
-            // printConfigs(egl, display, configs);
+
+            if (DEBUG) {
+                 printConfigs(egl, display, configs);
+            }
+            /* Now return the "best" one
+             */
             return chooseConfig(egl, display, configs);
         }
 
         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
                 EGLConfig[] configs) {
-            EGLConfig closestConfig = null;
-            int closestDistance = 1000;
             for(EGLConfig config : configs) {
                 int d = findConfigAttrib(egl, display, config,
                         EGL10.EGL_DEPTH_SIZE, 0);
                 int s = findConfigAttrib(egl, display, config,
                         EGL10.EGL_STENCIL_SIZE, 0);
-                if (d >= mDepthSize && s>= mStencilSize) {
-                    int r = findConfigAttrib(egl, display, config,
-                            EGL10.EGL_RED_SIZE, 0);
-                    int g = findConfigAttrib(egl, display, config,
-                             EGL10.EGL_GREEN_SIZE, 0);
-                    int b = findConfigAttrib(egl, display, config,
-                              EGL10.EGL_BLUE_SIZE, 0);
-                    int a = findConfigAttrib(egl, display, config,
-                            EGL10.EGL_ALPHA_SIZE, 0);
-                    int distance = Math.abs(r - mRedSize)
-                                + Math.abs(g - mGreenSize)
-                                + Math.abs(b - mBlueSize)
-                                + Math.abs(a - mAlphaSize);
-                    if (distance < closestDistance) {
-                        closestDistance = distance;
-                        closestConfig = config;
-                    }
-                }
+
+                // We need at least mDepthSize and mStencilSize bits
+                if (d < mDepthSize || s < mStencilSize)
+                    continue;
+
+                // We want an *exact* match for red/green/blue/alpha
+                int r = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_RED_SIZE, 0);
+                int g = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_GREEN_SIZE, 0);
+                int b = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_BLUE_SIZE, 0);
+                int a = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_ALPHA_SIZE, 0);
+
+                if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
+                    return config;
             }
-            return closestConfig;
+            return null;
         }
 
         private int findConfigAttrib(EGL10 egl, EGLDisplay display,
@@ -293,4 +337,3 @@
         }
     }
 }
-
diff --git a/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.c b/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.c
index bac8659..35367c1 100644
--- a/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.c
+++ b/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
 #include "helloneon-intrinsics.h"
 #include <arm_neon.h>
 
diff --git a/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.h b/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.h
index 1f2ff9a..ad4c8db 100644
--- a/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.h
+++ b/ndk/apps/hello-neon/project/jni/helloneon-intrinsics.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 HELLONEON_INTRINSICS_H
 #define HELLONEON_INTRINSICS_H
 
diff --git a/ndk/apps/hello-neon/project/jni/helloneon.c b/ndk/apps/hello-neon/project/jni/helloneon.c
index 3a2e928..ee19e08 100644
--- a/ndk/apps/hello-neon/project/jni/helloneon.c
+++ b/ndk/apps/hello-neon/project/jni/helloneon.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
 #include <jni.h>
 #include <time.h>
 #include <stdio.h>
diff --git a/ndk/build/tools/build-toolchain.sh b/ndk/build/tools/build-toolchain.sh
index 80bb4e9..51f3fa7 100755
--- a/ndk/build/tools/build-toolchain.sh
+++ b/ndk/build/tools/build-toolchain.sh
@@ -389,14 +389,13 @@
 }
 
 # Unpack a given package in a target location
-# $1: package name
-# $2: target directory
+# $1: package name      (e.g. toolchain)
+# $2: target directory  (e.g. /tmp/foo)
 #
 unpack_package ()
 {
-    WORKSPACE=$ANDROID_NDK_ARCHIVE/$1
-    SRCDIR=$2
     SRCPKG=`var_value PKG_$1`
+    SRCDIR=$2
     if ! timestamp_check $1 unpack; then
         echo "Unpack  : $1 sources"
         echo "          from $SRCPKG"
@@ -413,6 +412,45 @@
             exit 1
         fi
         timestamp_set   $1 unpack
+        timestamp_force $1 patch
+    fi
+}
+
+# Patch a given package at a target location
+# $1: package name      (e.g. toolchain)
+# $2: target directory  (e.g. /tmp/foo)
+# $3: patch directory   (e.g. build/tools/toolchain-patches)
+#
+# The rationale here is that anything named like $3/<subpath>/<foo>.patch
+# will be applied with "patch -p1" under $2/<subpath>
+#
+# Patches are listed and applied in alphanumerical order of their names
+# as returned by 'find'. Consider using numbered prefixes like the patch
+# files generated by "git format-patch" are named.
+#
+patch_package ()
+{
+    SRCPKG=`var_value PKG_$1`
+    SRCDIR=$2
+    if ! timestamp_check $1 patch; then
+        PATCH_FILES=`(cd $3 && find . -name "*.patch") 2> /dev/null`
+        if [ -z "$PATCH_FILES" ] ; then
+            echo "Patch   : none provided"
+            return
+        fi
+        for PATCH in $PATCH_FILES; do
+            echo "Patch   : $1 sources"
+            echo "          from $PATCH"
+            echo "          into $SRCDIR"
+            PATCHDIR=`dirname $PATCH`
+            PATCHNAME=`basename $PATCH`
+            cd $SRCDIR/$PATCHDIR && patch -p1 < $3/$PATCH
+            if [ $? != 0 ] ; then
+                echo "Patch failure !! Please check toolchain package !"
+                exit 1
+            fi
+        done
+        timestamp_set   $1 patch
         timestamp_force $1 configure
     fi
 }
@@ -443,6 +481,7 @@
 fi
 
 unpack_package toolchain $ANDROID_TOOLCHAIN_SRC
+patch_package  toolchain $ANDROID_TOOLCHAIN_SRC $ANDROID_NDK_ROOT/build/tools/toolchain-patches
 
 # remove all info files from the unpacked toolchain sources
 # they create countless little problems during the build
diff --git a/ndk/sources/cpufeatures/cpu-features.c b/ndk/sources/cpufeatures/cpu-features.c
index d165635..c46b884 100644
--- a/ndk/sources/cpufeatures/cpu-features.c
+++ b/ndk/sources/cpufeatures/cpu-features.c
@@ -1,3 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
 #include <sys/system_properties.h>
 #include <machine/cpu-features.h>
 #include <pthread.h>
diff --git a/ndk/sources/cpufeatures/cpu-features.h b/ndk/sources/cpufeatures/cpu-features.h
index d5fa876..e1cafd6 100644
--- a/ndk/sources/cpufeatures/cpu-features.h
+++ b/ndk/sources/cpufeatures/cpu-features.h
@@ -1,3 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
 #ifndef CPU_FEATURES_H
 #define CPU_FEATURES_H
 
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 6615d71..1e3f66b 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -29,6 +29,8 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
     <!-- We will request access to the camera, saying we require a camera
          of some sort but not one with autofocus capability. -->
@@ -651,6 +653,23 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".os.SmsMessagingDemo" android:label="OS/SMS Messaging">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".os.SmsReceivedDialog"
+                  android:theme="@android:style/Theme.Translucent.NoTitleBar"
+                  android:launchMode="singleInstance" />
+
+        <receiver android:name=".os.SmsMessageReceiver" android:enabled="false">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
+            </intent-filter>
+        </receiver>
+
         <!-- ************************************* -->
         <!--     ANIMATION PACKAGE SAMPLES         -->
         <!-- ************************************* -->
diff --git a/samples/ApiDemos/res/layout/sms_demo.xml b/samples/ApiDemos/res/layout/sms_demo.xml
new file mode 100644
index 0000000..1074f52
--- /dev/null
+++ b/samples/ApiDemos/res/layout/sms_demo.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- Demonstrates sending and receiving SMS messages.
+     See corresponding Java code SmsMessagingDemo.java
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="6dip">
+        <TextView
+            android:textColor="#ff0000"
+            android:textStyle="bold"
+            android:text="@string/sms_warning"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+        <CheckBox
+            android:id="@+id/sms_enable_receiver"
+            android:text="@string/sms_enable_receiver"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <TableLayout
+            android:padding="6dip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:stretchColumns="1">
+            <TableRow android:layout_width="match_parent">
+                <TextView android:text="@string/sms_recipient_label" />
+                <EditText android:id="@+id/sms_recipient" />
+            </TableRow>
+            <TableRow>
+                <TextView android:text="@string/sms_content_label" />
+                <EditText android:id="@+id/sms_content" />
+            </TableRow>
+            <TableRow>
+                <Button
+                    android:id="@+id/sms_send_message"
+                    android:text="@string/sms_send_message"
+                    android:layout_column="1" />
+            </TableRow>
+            <TableRow>
+                <TextView
+                    android:id="@+id/sms_status"
+                    android:layout_column="1" />
+            </TableRow>
+        </TableLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index c7bb84e..799d67f 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -879,5 +879,21 @@
     <string name="appwidget_configure_instructions">This text will be shown before the date in our example widget.</string>
     <string name="appwidget_prefix_default">Oh hai</string>
     <string name="appwidget_text_format"><xliff:g id="prefix">%1$s</xliff:g>: <xliff:g id="time">%2$s</xliff:g></string>
+
+    <!-- ============================ -->
+    <!--  SMS Messaging examples strings  -->
+    <!-- ============================ -->
+
+    <string name="sms_warning">
+      WARNING: this demo can send actual text messages (one at a time), so be sure to
+      test with the Android emulator or have a text messaging plan with your carrier.
+    </string>
+    <string name="sms_enable_receiver">Enable SMS broadcast receiver</string>
+    <string name="sms_recipient_label">Recipient #</string>
+    <string name="sms_content_label">Message Body</string>
+    <string name="sms_send_message">Send</string>
+    <string name="sms_speak_string_format">Message from "%1$s": %2$s</string>
+    <string name="reply">Reply</string>
+    <string name="dismiss">Dismiss</string>
 </resources>
 
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
index 776a6fb..07ce0c6 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
@@ -21,8 +21,8 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
-import android.app.DeviceAdminReceiver;
-import android.app.DevicePolicyManager;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -339,7 +339,8 @@
                 }
                 boolean active = mDPM.isAdminActive(mDeviceAdminSample);
                 if (active) {
-                    mDPM.resetPassword(mPassword.getText().toString());
+                    mDPM.resetPassword(mPassword.getText().toString(),
+                            DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
                 }
             }
         };
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java
new file mode 100644
index 0000000..ec80955
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.example.android.apis.os;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.telephony.SmsMessage;
+
+public class SmsMessageReceiver extends BroadcastReceiver {
+    /** Tag string for our debug logs */
+    private static final String TAG = "SmsMessageReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle extras = intent.getExtras();
+        if (extras == null)
+            return;
+
+        Object[] pdus = (Object[]) extras.get("pdus");
+
+        for (int i = 0; i < pdus.length; i++) {
+            SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
+            String fromAddress = message.getOriginatingAddress();
+            String fromDisplayName = fromAddress;
+
+            Uri uri;
+            String[] projection;
+
+            // If targeting Donut or below, use
+            // Contacts.Phones.CONTENT_FILTER_URL and
+            // Contacts.Phones.DISPLAY_NAME
+            uri = Uri.withAppendedPath(
+                    ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                    Uri.encode(fromAddress));
+            projection = new String[] { ContactsContract.PhoneLookup.DISPLAY_NAME };
+
+            // Query the filter URI
+            Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+            if (cursor != null) {
+                if (cursor.moveToFirst())
+                    fromDisplayName = cursor.getString(0);
+
+                cursor.close();
+            }
+
+            // Trigger the main activity to fire up a dialog that shows/reads the received messages
+            Intent di = new Intent();
+            di.setClass(context, SmsReceivedDialog.class);
+            di.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            di.putExtra(SmsReceivedDialog.SMS_FROM_ADDRESS_EXTRA, fromAddress);
+            di.putExtra(SmsReceivedDialog.SMS_FROM_DISPLAY_NAME_EXTRA, fromDisplayName);
+            di.putExtra(SmsReceivedDialog.SMS_MESSAGE_EXTRA, message.getMessageBody().toString());
+            context.startActivity(di);
+
+            // For the purposes of this demo, we'll only handle the first received message.
+            break;
+        }
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java
new file mode 100644
index 0000000..94f4e5a
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 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.example.android.apis.os;
+
+import java.util.List;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import com.example.android.apis.R;
+
+public class SmsMessagingDemo extends Activity {
+    /** Tag string for our debug logs */
+    private static final String TAG = "SmsMessagingDemo";
+
+    public static final String SMS_RECIPIENT_EXTRA = "com.example.android.apis.os.SMS_RECIPIENT";
+
+    public static final String ACTION_SMS_SENT = "com.example.android.apis.os.SMS_SENT_ACTION";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.sms_demo);
+
+        if (getIntent().hasExtra(SMS_RECIPIENT_EXTRA)) {
+            ((TextView) findViewById(R.id.sms_recipient)).setText(getIntent().getExtras()
+                    .getString(SMS_RECIPIENT_EXTRA));
+            ((TextView) findViewById(R.id.sms_content)).requestFocus();
+        }
+
+        // Enable or disable the broadcast receiver depending on the checked
+        // state of the checkbox.
+        CheckBox enableCheckBox = (CheckBox) findViewById(R.id.sms_enable_receiver);
+
+        final PackageManager pm = this.getPackageManager();
+        final ComponentName componentName = new ComponentName("com.example.android.apis",
+                "com.example.android.apis.os.SmsMessageReceiver");
+
+        enableCheckBox.setChecked(pm.getComponentEnabledSetting(componentName) ==
+                                  PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+        enableCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                Log.d(TAG, (isChecked ? "Enabling" : "Disabling") + " SMS receiver");
+
+                pm.setComponentEnabledSetting(componentName,
+                        isChecked ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                        PackageManager.DONT_KILL_APP);
+            }
+        });
+
+        final EditText recipientTextEdit = (EditText) SmsMessagingDemo.this
+                .findViewById(R.id.sms_recipient);
+        final EditText contentTextEdit = (EditText) SmsMessagingDemo.this
+                .findViewById(R.id.sms_content);
+        final TextView statusView = (TextView) SmsMessagingDemo.this.findViewById(R.id.sms_status);
+
+        // Watch for send button clicks and send text messages.
+        Button sendButton = (Button) findViewById(R.id.sms_send_message);
+        sendButton.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                if (TextUtils.isEmpty(recipientTextEdit.getText())) {
+                    Toast.makeText(SmsMessagingDemo.this, "Please enter a message recipient.",
+                            Toast.LENGTH_SHORT).show();
+                    return;
+                }
+
+                if (TextUtils.isEmpty(contentTextEdit.getText())) {
+                    Toast.makeText(SmsMessagingDemo.this, "Please enter a message body.",
+                            Toast.LENGTH_SHORT).show();
+                    return;
+                }
+
+                recipientTextEdit.setEnabled(false);
+                contentTextEdit.setEnabled(false);
+
+                SmsManager sms = SmsManager.getDefault();
+
+                List<String> messages = sms.divideMessage(contentTextEdit.getText().toString());
+
+                String recipient = recipientTextEdit.getText().toString();
+                for (String message : messages) {
+                    sms.sendTextMessage(recipient, null, message, PendingIntent.getBroadcast(
+                            SmsMessagingDemo.this, 0, new Intent(ACTION_SMS_SENT), 0), null);
+                }
+            }
+        });
+
+        // Register broadcast receivers for SMS sent and delivered intents
+        registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String message = null;
+                boolean error = true;
+                switch (getResultCode()) {
+                case Activity.RESULT_OK:
+                    message = "Message sent!";
+                    error = false;
+                    break;
+                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+                    message = "Error.";
+                    break;
+                case SmsManager.RESULT_ERROR_NO_SERVICE:
+                    message = "Error: No service.";
+                    break;
+                case SmsManager.RESULT_ERROR_NULL_PDU:
+                    message = "Error: Null PDU.";
+                    break;
+                case SmsManager.RESULT_ERROR_RADIO_OFF:
+                    message = "Error: Radio off.";
+                    break;
+                }
+
+                recipientTextEdit.setEnabled(true);
+                contentTextEdit.setEnabled(true);
+                contentTextEdit.setText("");
+
+                statusView.setText(message);
+                statusView.setTextColor(error ? Color.RED : Color.GREEN);
+            }
+        }, new IntentFilter(ACTION_SMS_SENT));
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java
new file mode 100644
index 0000000..0df93a7
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 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.example.android.apis.os;
+
+import java.util.Locale;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.util.Log;
+
+import com.example.android.apis.R;
+
+public class SmsReceivedDialog extends Activity implements OnInitListener {
+    private static final String TAG = "SmsReceivedDialog";
+
+    private static final int DIALOG_SHOW_MESSAGE = 1;
+
+    public static final String SMS_FROM_ADDRESS_EXTRA = "com.example.android.apis.os.SMS_FROM_ADDRESS";
+    public static final String SMS_FROM_DISPLAY_NAME_EXTRA = "com.example.android.apis.os.SMS_FROM_DISPLAY_NAME";
+    public static final String SMS_MESSAGE_EXTRA = "com.example.android.apis.os.SMS_MESSAGE";
+
+    private TextToSpeech mTts;
+
+    private String mFromDisplayName;
+    private String mFromAddress;
+    private String mMessage;
+    private String mFullBodyString;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFromAddress = getIntent().getExtras().getString(SMS_FROM_ADDRESS_EXTRA);
+        mFromDisplayName = getIntent().getExtras().getString(SMS_FROM_DISPLAY_NAME_EXTRA);
+        mMessage = getIntent().getExtras().getString(SMS_MESSAGE_EXTRA);
+
+        mFullBodyString = String.format(
+                getResources().getString(R.string.sms_speak_string_format),
+                mFromDisplayName,
+                mMessage);
+
+        showDialog(DIALOG_SHOW_MESSAGE);
+        mTts = new TextToSpeech(this, this);
+    }
+
+    public void onInit(int status) {
+        if (status == TextToSpeech.SUCCESS) {
+            int result = mTts.setLanguage(Locale.US);
+            if (result == TextToSpeech.LANG_MISSING_DATA
+                    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+                Log.e(TAG, "TTS language is not available.");
+            } else {
+                mTts.speak(mFullBodyString, TextToSpeech.QUEUE_ADD, null);
+            }
+        } else {
+            // Initialization failed.
+            Log.e(TAG, "Could not initialize TTS.");
+        }
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+        case DIALOG_SHOW_MESSAGE:
+            return new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_email)
+                    .setTitle("Message Received")
+                    .setMessage(mFullBodyString)
+                    .setPositiveButton(R.string.reply, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            // Begin creating the reply with the SmsMessagingDemo activity
+                            Intent i = new Intent();
+                            i.setClass(SmsReceivedDialog.this, SmsMessagingDemo.class);
+                            i.putExtra(SmsMessagingDemo.SMS_RECIPIENT_EXTRA, mFromAddress);
+                            startActivity(i);
+
+                            dialog.dismiss();
+                            finish();
+                        }
+                    })
+                    .setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            dialog.dismiss();
+                            finish();
+                        }
+                    })
+                    .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                        public void onCancel(DialogInterface dialog) {
+                            finish();
+                        }
+                    }).create();
+        }
+        return null;
+    }
+}
diff --git a/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
index 578fe6d..6eed39e 100644
--- a/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
+++ b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
@@ -185,11 +185,18 @@
             gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request draw events", inst());
             break;
         case kLifecycle_ANPEventType:
-            if (evt->data.lifecycle.action == kOnLoad_ANPLifecycleAction) {
-                gLogI.log(kDebug_ANPLogType, " ------ %p the plugin received an onLoad event", inst());
-                return 1;
+            switch (evt->data.lifecycle.action)  {
+                case kOnLoad_ANPLifecycleAction:
+                    gLogI.log(kDebug_ANPLogType, " ------ %p onLoad", inst());
+                    return 1;
+                case kOnScreen_ANPLifecycleAction:
+                    gLogI.log(kDebug_ANPLogType, " ------ %p onScreen", inst());
+                    return 1;
+                case kOffScreen_ANPLifecycleAction:
+                    gLogI.log(kDebug_ANPLogType, " ------ %p offScreen", inst());
+                    return 1;
             }
-            break;
+            break; // end kLifecycle_ANPEventType
         case kTouch_ANPEventType:
             if (kDown_ANPTouchAction == evt->data.touch.action)
                 return kHandleLongPress_ANPTouchResult | kHandleDoubleTap_ANPTouchResult;
diff --git a/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp b/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp
index e478897..1ec86c2 100644
--- a/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp
+++ b/samples/BrowserPlugin/jni/paint/PaintPlugin.cpp
@@ -37,6 +37,7 @@
 extern ANPSurfaceInterfaceV0    gSurfaceI;
 extern ANPSystemInterfaceV0     gSystemI;
 extern ANPTypefaceInterfaceV0   gTypefaceI;
+extern ANPWindowInterfaceV0     gWindowI;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -49,6 +50,7 @@
     memset(&m_drawingSurface, 0, sizeof(m_drawingSurface));
     memset(&m_inputToggle,  0, sizeof(m_inputToggle));
     memset(&m_colorToggle, 0, sizeof(m_colorToggle));
+    memset(&m_fullScreenToggle, 0, sizeof(m_fullScreenToggle));
     memset(&m_clearSurface,  0, sizeof(m_clearSurface));
 
     // initialize the drawing surface
@@ -170,7 +172,7 @@
                       m_inputToggle.top - fontMetrics.fTop, m_paintSurface);
 
     // draw the color selector button
-    m_colorToggle.left = (W/2) - (buttonWidth/2);
+    m_colorToggle.left = (W/3) - (buttonWidth/2);
     m_colorToggle.top = H - buttonHeight - 5;
     m_colorToggle.right = m_colorToggle.left + buttonWidth;
     m_colorToggle.bottom = m_colorToggle.top + buttonHeight;
@@ -179,6 +181,17 @@
     gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5,
                       m_colorToggle.top - fontMetrics.fTop, m_paintSurface);
 
+    // draw the full-screen toggle button
+    m_fullScreenToggle.left = ((W*2)/3) - (buttonWidth/2);
+    m_fullScreenToggle.top = H - buttonHeight - 5;
+    m_fullScreenToggle.right = m_fullScreenToggle.left + buttonWidth;
+    m_fullScreenToggle.bottom = m_fullScreenToggle.top + buttonHeight;
+    gCanvasI.drawRect(canvas, &m_fullScreenToggle, m_paintButton);
+    const char* fullScreenText = "Full";
+    gCanvasI.drawText(canvas, fullScreenText, strlen(fullScreenText),
+                      m_fullScreenToggle.left + 5,
+                      m_fullScreenToggle.top - fontMetrics.fTop, m_paintSurface);
+
     // draw the clear canvas button
     m_clearSurface.left = W - buttonWidth - 5;
     m_clearSurface.top = H - buttonHeight - 5;
@@ -306,6 +319,8 @@
                     toggleInputMethod();
                 else if (rect == &m_colorToggle)
                     togglePaintColor();
+                else if (rect == &m_fullScreenToggle)
+                    gWindowI.requestFullScreen(inst());
                 else if (rect == &m_clearSurface)
                     drawCleanPlugin();
             }
@@ -316,6 +331,12 @@
             switch (evt->data.other[0]) {
                 case kSurfaceCreated_CustomEvent:
                     gLogI.log(kDebug_ANPLogType, " ---- customEvent: surfaceCreated");
+                    /* The second draw call is added to cover up a problem in this
+                       plugin and is not a recommended usage pattern. This plugin
+                       does not correctly make partial updates to the double
+                       buffered surface and this second call hides that problem.
+                     */
+                    drawCleanPlugin();
                     drawCleanPlugin();
                     break;
                 case kSurfaceChanged_CustomEvent: {
@@ -358,6 +379,8 @@
         return &m_inputToggle;
     else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
         return &m_colorToggle;
+    else if (fx > m_fullScreenToggle.left && fx < m_fullScreenToggle.right && fy > m_fullScreenToggle.top && fy < m_fullScreenToggle.bottom)
+        return &m_fullScreenToggle;
     else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
         return &m_clearSurface;
     else
diff --git a/samples/BrowserPlugin/jni/paint/PaintPlugin.h b/samples/BrowserPlugin/jni/paint/PaintPlugin.h
index 4867a70..035e51b 100644
--- a/samples/BrowserPlugin/jni/paint/PaintPlugin.h
+++ b/samples/BrowserPlugin/jni/paint/PaintPlugin.h
@@ -58,6 +58,7 @@
     ANPRectF    m_drawingSurface;
     ANPRectF    m_inputToggle;
     ANPRectF    m_colorToggle;
+    ANPRectF    m_fullScreenToggle;
     ANPRectF    m_clearSurface;
 
     ANPPaint*   m_paintSurface;
diff --git a/testrunner/coverage_targets.xml b/testrunner/coverage_targets.xml
index 869c905..09da0c8 100644
--- a/testrunner/coverage_targets.xml
+++ b/testrunner/coverage_targets.xml
@@ -52,9 +52,7 @@
         <src path="wifi/java" />
     </coverage_target>
     <coverage_target name="android.test.runner"
-        build_path="frameworks/base/test-runner" type="JAVA_LIBRARIES">
-        <src path="." />
-    </coverage_target>
+        build_path="frameworks/base/test-runner" type="JAVA_LIBRARIES" />
    
    <!-- apps -->
     <coverage_target name="ApiDemos" build_path="development/samples/ApiDemos"
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 495b6a7..d53312f 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -120,6 +120,12 @@
                       help="Restrict test to a specific java package")
     parser.add_option("-z", "--size", dest="test_size",
                       help="Restrict test to a specific test size")
+    parser.add_option("--annotation", dest="test_annotation",
+                      help="Include only those tests tagged with a specific"
+                      " annotation")
+    parser.add_option("--not-annotation", dest="test_not_annotation",
+                      help="Exclude any tests tagged with a specific"
+                      " annotation")
     parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
                       metavar="FILE", default=user_test_default,
                       help="Alternate source of user test definitions")
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 9b3b7ca..381190c 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -80,9 +80,9 @@
     continuous="true" />
 
 <test name="frameworks-testrunner"
-    build_path="frameworks/base/test-runner/tests"
+    build_path="frameworks/base/test-runner"
     package="com.android.frameworks.testrunner.tests"
-    coverage_target="framework"
+    coverage_target="android.test.runner"
     continuous="true" />
 
 <test name="frameworks-vpn"
diff --git a/testrunner/test_defs/instrumentation_test.py b/testrunner/test_defs/instrumentation_test.py
index c932a0b..f0a8656 100644
--- a/testrunner/test_defs/instrumentation_test.py
+++ b/testrunner/test_defs/instrumentation_test.py
@@ -131,6 +131,10 @@
       instrumentation_args["suiteAssignment"] = "true"
     if options.coverage:
       instrumentation_args["coverage"] = "true"
+    if options.test_annotation:
+      instrumentation_args["annotation"] = options.test_annotation
+    if options.test_not_annotation:
+      instrumentation_args["notAnnotation"] = options.test_not_annotation
     if options.preview:
       adb_cmd = adb.PreviewInstrumentationCommand(
           package_name=self.GetPackageName(),
diff --git a/tools/apkcheck/Android.mk b/tools/apkcheck/Android.mk
new file mode 100644
index 0000000..c388939
--- /dev/null
+++ b/tools/apkcheck/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2010 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script file's timestamp is at least as new as the
+# .jar file it wraps.
+
+# the execution script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := apkcheck
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/apkcheck$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/apkcheck | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+		src \
+	))
+
+include $(subdirs)
diff --git a/tools/apkcheck/README.txt b/tools/apkcheck/README.txt
new file mode 100644
index 0000000..70dcd9a
--- /dev/null
+++ b/tools/apkcheck/README.txt
@@ -0,0 +1,179 @@
+Android APK Checker
+
+This compares the set of classes, fields, and methods used by an Android
+application against the published API.  It identifies and reports the
+use of any unpublished members or methods.
+
+The public API description files live in the source tree, in
+frameworks/base/api/.  The tip-of-tree version is in "current.xml",
+and each officially released API has a numbered file (e.g. "6.xml").
+They're generated from the sources, and can take into acount javadoc
+annotations like "@hide" in comments.
+
+The dependency set for an APK can be generated with "dexdeps".  It finds
+all classes, fields, and methods that are referenced by classes.dex but not
+defined locally.  The tool can't easily tell anything about a dependency
+beyond the name (e.g. whether a class is a static or non-static inner
+class), so while the output from dexdeps is similar in structure to the
+API XML file, it has much less detail.
+
+
+==== Usage ====
+
+% apkcheck [options] public-api.xml apk1.xml ...
+
+Provide the public API data file of choice, and one or more XML files
+generated by dexdeps.  The time required to parse and manipulate the
+public API XML file is generally much larger than the time required to
+analyze the APK, so if you have a large set of APKs it's best to run them
+through in large batches.
+
+Options:
+
+  --help
+    Show options summary.
+
+  --uses-library=<lib.xml>
+    Load additional public API list.  This is intended for APKs that
+    use "uses-library" directives to pull in external libraries.  Since
+    the external libraries are not part of the public API, their use
+    would otherwise be flagged as illegal by apkcheck.
+
+  --ignore-package=<package-name>
+    Ignore errors generated by references to the named package (e.g.
+    "com.google.android.maps").  Warnings will be generated instead.
+    Useful for ignoring references to shared library content when
+    XML API data is not available.
+
+  --[no-]warn
+    Enable or disable warning messages.  These are disabled by default.
+
+  --[no-]error
+    Enable or disable error messages.  These are enabled by default.  If
+    you disable both warnings and errors you will only see a summary.
+
+In some cases involving generic signatures it may not be possible
+to accurately reconstruct the public API.  Some popular cases have
+been hard-coded into the program.  They can be included by specifying
+"--uses-library=BUILTIN".
+
+Example use:
+
+% dexdeps out/target/product/sapphire/system/app/Gmail.apk > Gmail.apk.xml
+% apkcheck --uses-library=BUILTIN frameworks/base/api/current.xml Gmail.apk.xml
+Gmail.apk.xml: summary: 0 errors, 15 warnings
+
+
+==== Limitations ====
+
+The API XML files have some ambiguous entries and are missing important
+pieces.  A summary of the issues follows.
+
+(1) Class names are not in binary form
+
+Example:
+
+ type="android.os.Parcelable.Creator"
+
+This could be a Creator class in the package android.os.Parcelable,
+or Parcelable.Creator in the package android.os.  We can guess based on
+capitalization, but that's unreliable.
+
+The API XML does specify each package in a <package> tag, so we should have
+the full set of packages available.  From this we can remove one element
+at a time from the right until we match a known package.  This will work
+unless "android.os" and "android.os.Parcelable" are both valid packages.
+
+
+(2) Public enums are not enumerated
+
+Enumeration classes are included, and always have two methods ("valueOf"
+and "values").  What isn't included are entries for the fields representing
+the enumeration values.  This makes it look like an APK is referring
+to non-public fields in the class.
+
+If apkcheck sees a reference to an unknown field, and the field's defining
+class appears to be an Enum (the superclass is java.lang.Enum), we emit
+a warning instead of an error.
+
+
+(3) Public annotation methods are not listed
+
+Annotation classes have trivial entries that show only the class name
+and "implements java.lang.annotation.Annotation".  It is not possible
+to verify that a method call on an annotation is valid.
+
+If apkcheck sees a method call to an unknown method, and the class appears
+to be an annotation (extends Object, implements Annotation, defines no
+fields or methods), we emit a warning instead of an error.
+
+
+(4) Covariant return types
+
+Suppose a class defines a method "public Foo gimmeFoo()".  Any subclass
+that overrides that method must also return Foo, so it would seem that
+there's no need to emit a method entry for gimmeFoo() in the subclasses.
+
+However, it's possible to override gimmeFoo with "public MegaFoo
+gimmeFoo()" so long as MegaFoo is an instance of Foo.  In that case it
+is necessary to emit a new method entry, but the public API XML generator
+does not.
+
+If apkcheck can't find an exact match for a method reference, but can
+find a method that matches on everything but the return type, it will
+emit a warning instead of an error.  (We could be more thorough and try
+to verify that the return types are related, but that's more trouble than
+it's worth.)
+
+
+(5) Generic signatures
+
+When generic signatures are used, the public API file will contain
+entries like these:
+
+ <parameter name="key" type="K">
+ <parameter name="others" type="E...">
+ <parameter name="map" type="java.util.Map&lt;? extends K, ? extends V&gt;">
+
+The generic types are generally indistinguishable from classes in the
+default package (i.e. that have no package name).  In most cases they're
+a single letter, so apkcheck includes a kluge that converts single-letter
+class names to java.lang.Object.
+
+This often works, but falls apart in a few cases.  For example:
+
+ public <T extends Parcelable> T getParcelableExtra(String name) {
+     return mExtras == null ? null : mExtras.<T>getParcelable(name);
+ }
+
+This is emitted as:
+
+ <method name="getParcelableExtra" return="T">
+
+which gets converted to java.lang.Object.  Unfortunately the APK wants
+a method with a more specific return type (android.os.Parcelable), so
+the lookup fails.
+
+There is no way to recover the actual type, because the generic signature
+details are not present in the XML.  This particular case will be handled
+as a covariant return type.  When the generic type is in the parameter
+list, though, this isn't handled so easily.
+
+These cases are relatively few, so they were handled by baking the
+signatures into the code (--uses-library=BUILTIN).  (At some point it
+may be worthwhile to try a little harder here.)
+
+
+(6) Use of opaque non-public types
+
+Some classes are not meant for public consumption, but are still referred
+to by application code.  For example, an opaque type might be passed to
+the app as a cookie.
+
+Another example is the Dalvik annotation classes, like
+dalvik.annotation.InnerClass.  These are emitted by "dx", and referenced
+from the DEX file, but not intended to be used by application code.
+
+If an APK refers to a non-public class, but doesn't access any fields
+or methods, a warning is emitted instead of an error.
+
diff --git a/tools/apkcheck/etc/apkcheck b/tools/apkcheck/etc/apkcheck
new file mode 100644
index 0000000..78af93a
--- /dev/null
+++ b/tools/apkcheck/etc/apkcheck
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+exec java $javaOpts -jar $libdir/apkcheck.jar "$@"
diff --git a/tools/apkcheck/etc/manifest.txt b/tools/apkcheck/etc/manifest.txt
new file mode 100644
index 0000000..aa4fef2
--- /dev/null
+++ b/tools/apkcheck/etc/manifest.txt
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.android.apkcheck.ApkCheck
diff --git a/tools/apkcheck/src/Android.mk b/tools/apkcheck/src/Android.mk
new file mode 100644
index 0000000..abc813a
--- /dev/null
+++ b/tools/apkcheck/src/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2010 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# apkcheck java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+
+LOCAL_MODULE:= apkcheck
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/ApiDescrHandler.java b/tools/apkcheck/src/com/android/apkcheck/ApiDescrHandler.java
new file mode 100644
index 0000000..427a20f
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/ApiDescrHandler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;
+import java.io.*;
+
+
+/**
+ * Provides implementation for SAX parser.
+ */
+class ApiDescrHandler extends DefaultHandler {
+    /*
+     * Uber-container.
+     */
+    private ApiList mApiList;
+
+    /*
+     * Temporary objects, used as containers while we accumulate the
+     * innards.
+     */
+    private PackageInfo mCurrentPackage = null;
+    private ClassInfo mCurrentClass = null;
+    private MethodInfo mCurrentMethod = null;
+
+    /**
+     * Constructs an ApiDescrHandler.
+     *
+     * @param fileName Source file name, used for debugging.
+     */
+    public ApiDescrHandler(ApiList apiList) {
+        mApiList = apiList;
+    }
+
+    /**
+     * Returns the ApiList in its current state.  Generally only
+     * makes sense to call here after parsing is completed.
+     */
+    public ApiList getApiList() {
+        return mApiList;
+    }
+
+    /**
+     * Processes start tags.  If the file is malformed we will likely
+     * NPE, but this is captured by the caller.
+     *
+     * We currently assume that packages and classes only appear once,
+     * so all classes associated with a package are wrapped in a singular
+     * instance of &lt;package&gt;.  We may want to remove this assumption
+     * by attempting to find an existing package/class with the same name.
+     */
+    @Override
+    public void startElement(String uri, String localName, String qName,
+            Attributes attributes) {
+
+        if (qName.equals("package")) {
+            /* top-most element */
+            mCurrentPackage = mApiList.getOrCreatePackage(
+                    attributes.getValue("name"));
+        } else if (qName.equals("class") || qName.equals("interface")) {
+            /* get class, gather fields/methods and interfaces */
+            mCurrentClass = mCurrentPackage.getOrCreateClass(
+                    attributes.getValue("name"),
+                    attributes.getValue("extends"),
+                    attributes.getValue("static"));
+        } else if (qName.equals("implements")) {
+            /* add name of interface to current class */
+            mCurrentClass.addInterface(attributes.getValue("name"));
+        } else if (qName.equals("method")) {
+            /* hold object while we gather parameters */
+            mCurrentMethod = new MethodInfo(attributes.getValue("name"),
+                attributes.getValue("return"));
+        } else if (qName.equals("constructor")) {
+            /* like "method", but has no name or return type */
+            mCurrentMethod = new MethodInfo("<init>", "void");
+
+            /*
+             * If this is a non-static inner class, we want to add the
+             * "hidden" outer class parameter as the first parameter.
+             * We can tell if it's an inner class because the class name
+             * will include a '$' (it has been normalized already).
+             */
+            String staticClass = mCurrentClass.getStatic();
+            if (staticClass == null) {
+                /*
+                 * We're parsing an APK file, which means we can't know
+                 * if the class we're referencing is static or not.  We
+                 * also already have the "secret" first parameter
+                 * represented in the method parameter list, so we don't
+                 * need to insert it here.
+                 */
+            } else if ("false".equals(staticClass)) {
+                String className = mCurrentClass.getName();
+                int dollarIndex = className.indexOf('$');
+                if (dollarIndex >= 0) {
+                    String outerClass = className.substring(0, dollarIndex);
+                    //System.out.println("--- inserting " +
+                    //    mCurrentPackage.getName() + "." + outerClass +
+                    //    " into constructor for " + className);
+                    mCurrentMethod.addParameter(mCurrentPackage.getName() +
+                        "." + outerClass);
+                }
+            }
+        } else if (qName.equals("field")) {
+            /* add to current class */
+            FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
+                    attributes.getValue("type"));
+            mCurrentClass.addField(fInfo);
+        } else if (qName.equals("parameter")) {
+            /* add to current method */
+            mCurrentMethod.addParameter(attributes.getValue("type"));
+        }
+    }
+
+    /**
+     * Processes end tags.  Generally these add the under-construction
+     * item to the appropriate container.
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName) {
+        if (qName.equals("method") || qName.equals("constructor")) {
+            /* add method to class */
+            mCurrentClass.addMethod(mCurrentMethod);
+            mCurrentMethod = null;
+        } else if (qName.equals("class") || qName.equals("interface")) {
+            mCurrentClass = null;
+        } else if (qName.equals("package")) {
+            mCurrentPackage = null;
+        }
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/ApiList.java b/tools/apkcheck/src/com/android/apkcheck/ApiList.java
new file mode 100644
index 0000000..9eb7001
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/ApiList.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Holds a list of API members, including classes, fields, and methods.
+ */
+public class ApiList {
+    private HashMap<String,PackageInfo> mPackageList;
+    private String mDebugString;
+    private int mWarnings, mErrors;
+
+    /**
+     * Constructs an ApiList.
+     *
+     * @param debugString Identification string useful for debugging.
+     */
+    public ApiList(String debugString) {
+        mPackageList = new HashMap<String,PackageInfo>();
+        mDebugString = debugString;
+    }
+
+    /**
+     * Returns the source filename.  Useful for debug messages only.
+     */
+    public String getDebugString() {
+        return mDebugString;
+    }
+
+    /**
+     * Increment the number of warnings associated with this API list.
+     */
+    public void incrWarnings() {
+        mWarnings++;
+    }
+
+    /**
+     * Increment the errors of warnings associated with this API list.
+     */
+    public void incrErrors() {
+        mErrors++;
+    }
+
+    /**
+     * Returns the number of warnings associated with this API list.
+     */
+    public int getWarningCount() {
+        return mWarnings;
+    }
+
+    /**
+     * Returns the number of errors associated with this API list.
+     */
+    public int getErrorCount() {
+        return mErrors;
+    }
+
+    /**
+     * Retrieves the named package.
+     *
+     * @return the package, or null if no match was found
+     */
+    public PackageInfo getPackage(String name) {
+        return mPackageList.get(name);
+    }
+
+    /**
+     * Retrieves the named package, creating it if it doesn't already
+     * exist.
+     */
+    public PackageInfo getOrCreatePackage(String name) {
+        PackageInfo pkgInfo = mPackageList.get(name);
+        if (pkgInfo == null) {
+            pkgInfo = new PackageInfo(name);
+            mPackageList.put(name, pkgInfo);
+        }
+        return pkgInfo;
+    }
+
+    /**
+     * Returns an iterator for the set of known packages.
+     */
+    public Iterator<PackageInfo> getPackageIterator() {
+        return mPackageList.values().iterator();
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/ApkCheck.java b/tools/apkcheck/src/com/android/apkcheck/ApkCheck.java
new file mode 100644
index 0000000..65d8ca0
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/ApkCheck.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+
+
+/**
+ * Checks an APK's dependencies against the published API specification.
+ *
+ * We need to read two XML files (spec and APK) and perform some operations
+ * on the elements.  The file formats are similar but not identical, so
+ * we distill it down to common elements.
+ *
+ * We may also want to read some additional API lists representing
+ * libraries that would be included with a "uses-library" directive.
+ *
+ * For performance we want to allow processing of multiple APKs so
+ * we don't have to re-parse the spec file each time.
+ */
+public class ApkCheck {
+    /* keep track of current APK file name, for error messages */
+    private static ApiList sCurrentApk;
+
+    /* show warnings? */
+    private static boolean sShowWarnings = false;
+    /* show errors? */
+    private static boolean sShowErrors = true;
+
+    /* names of packages we're allowed to ignore */
+    private static HashSet<String> sIgnorablePackages = new HashSet<String>();
+
+
+    /**
+     * Program entry point.
+     */
+    public static void main(String[] args) {
+        ApiList apiDescr = new ApiList("public-api");
+
+        if (args.length < 2) {
+            usage();
+            return;
+        }
+
+        /* process args */
+        int idx;
+        for (idx = 0; idx < args.length; idx++) {
+            if (args[idx].equals("--help")) {
+                usage();
+                return;
+            } else if (args[idx].startsWith("--uses-library=")) {
+                String libName = args[idx].substring(args[idx].indexOf('=')+1);
+                if ("BUILTIN".equals(libName)) {
+                    Reader reader = Builtin.getReader();
+                    if (!parseXml(apiDescr, reader, "BUILTIN"))
+                        return;
+                } else {
+                    if (!parseApiDescr(apiDescr, libName))
+                        return;
+                }
+            } else if (args[idx].startsWith("--ignore-package=")) {
+                String pkgName = args[idx].substring(args[idx].indexOf('=')+1);
+                sIgnorablePackages.add(pkgName);
+            } else if (args[idx].equals("--warn")) {
+                sShowWarnings = true;
+            } else if (args[idx].equals("--no-warn")) {
+                sShowWarnings = false;
+            } else if (args[idx].equals("--error")) {
+                sShowErrors = true;
+            } else if (args[idx].equals("--no-error")) {
+                sShowErrors = false;
+
+            } else if (args[idx].startsWith("--")) {
+                if (args[idx].equals("--")) {
+                    // remainder are filenames, even if they start with "--"
+                    idx++;
+                    break;
+                } else {
+                    // unknown option specified
+                    System.err.println("ERROR: unknown option " +
+                        args[idx] + " (use \"--help\" for usage info)");
+                    return;
+                }
+            } else {
+                break;
+            }
+        }
+        if (idx > args.length - 2) {
+            usage();
+            return;
+        }
+
+        /* parse base API description */
+        if (!parseApiDescr(apiDescr, args[idx++]))
+            return;
+
+        /* "flatten" superclasses and interfaces */
+        sCurrentApk = apiDescr;
+        flattenInherited(apiDescr);
+
+        /* walk through list of libs we want to scan */
+        for ( ; idx < args.length; idx++) {
+            ApiList apkDescr = new ApiList(args[idx]);
+            sCurrentApk = apkDescr;
+            boolean success = parseApiDescr(apkDescr, args[idx]);
+            if (!success) {
+                if (idx < args.length-1)
+                    System.err.println("Skipping...");
+                continue;
+            }
+
+            check(apiDescr, apkDescr);
+            System.out.println(args[idx] + ": summary: " +
+                apkDescr.getErrorCount() + " errors, " +
+                apkDescr.getWarningCount() + " warnings\n");
+        }
+    }
+
+    /**
+     * Prints usage statement.
+     */
+    static void usage() {
+        System.err.println("Android APK checker v1.0");
+        System.err.println("Copyright (C) 2010 The Android Open Source Project\n");
+        System.err.println("Usage: apkcheck [options] public-api.xml apk1.xml ...\n");
+        System.err.println("Options:");
+        System.err.println("  --help                  show this message");
+        System.err.println("  --uses-library=lib.xml  load additional public API list");
+        System.err.println("  --ignore-package=pkg    don't show errors for references to this package");
+        System.err.println("  --[no-]warn             enable or disable display of warnings");
+        System.err.println("  --[no-]error            enable or disable display of errors");
+    }
+
+    /**
+     * Opens the file and passes it to parseXml.
+     *
+     * TODO: allow '-' as an alias for stdin?
+     */
+    static boolean parseApiDescr(ApiList apiList, String fileName) {
+        boolean result = false;
+
+        try {
+            FileReader fileReader = new FileReader(fileName);
+            result = parseXml(apiList, fileReader, fileName);
+            fileReader.close();
+        } catch (IOException ioe) {
+            System.err.println("Error opening " + fileName);
+        }
+        return result;
+    }
+
+    /**
+     * Parses an XML file holding an API description.
+     *
+     * @param fileReader Data source.
+     * @param apiList Container to add stuff to.
+     * @param fileName Input file name, only used for debug messages.
+     */
+    static boolean parseXml(ApiList apiList, Reader reader,
+            String fileName) {
+        //System.out.println("--- parsing " + fileName);
+        try {
+            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+            ApiDescrHandler handler = new ApiDescrHandler(apiList);
+            xmlReader.setContentHandler(handler);
+            xmlReader.setErrorHandler(handler);
+            xmlReader.parse(new InputSource(reader));
+
+            //System.out.println("--- parsing complete");
+            //dumpApi(apiList);
+            return true;
+        } catch (SAXParseException ex) {
+            System.err.println("Error parsing " + fileName + " line " +
+                ex.getLineNumber() + ": " + ex.getMessage());
+        } catch (Exception ex) {
+            System.err.println("Error while reading " + fileName + ": " +
+                ex.getMessage());
+            ex.printStackTrace();
+        }
+
+        // failed
+        return false;
+    }
+
+    /**
+     * Expands lists of fields and methods to recursively include superclass
+     * and interface entries.
+     *
+     * The API description files have entries for every method a class
+     * declares, even if it's present in the superclass (e.g. toString()).
+     * Removal of one of these methods doesn't constitute an API change,
+     * though, so if we don't find a method in a class we need to hunt
+     * through its superclasses.
+     *
+     * We can walk up the hierarchy while analyzing the target APK,
+     * or we can "flatten" the methods declared by the superclasses and
+     * interfaces before we begin the analysis.  Expanding up front can be
+     * beneficial if we're analyzing lots of APKs in one go, but detrimental
+     * to startup time if we just want to look at one small APK.
+     *
+     * It also means filling the field/method hash tables with lots of
+     * entries that never get used, possibly worsening the hash table
+     * hit rate.
+     *
+     * We only need to do this for the public API list.  The dexdeps output
+     * doesn't have this sort of information anyway.
+     */
+    static void flattenInherited(ApiList pubList) {
+        Iterator<PackageInfo> pkgIter = pubList.getPackageIterator();
+        while (pkgIter.hasNext()) {
+            PackageInfo pubPkgInfo = pkgIter.next();
+
+            Iterator<ClassInfo> classIter = pubPkgInfo.getClassIterator();
+            while (classIter.hasNext()) {
+                ClassInfo pubClassInfo = classIter.next();
+
+                pubClassInfo.flattenClass(pubList);
+            }
+        }
+    }
+
+    /**
+     * Checks the APK against the public API.
+     *
+     * Run through and find the mismatches.
+     *
+     * @return true if all is well
+     */
+    static boolean check(ApiList pubList, ApiList apkDescr) {
+
+        Iterator<PackageInfo> pkgIter = apkDescr.getPackageIterator();
+        while (pkgIter.hasNext()) {
+            PackageInfo apkPkgInfo = pkgIter.next();
+            PackageInfo pubPkgInfo = pubList.getPackage(apkPkgInfo.getName());
+            boolean badPackage = false;
+
+            if (pubPkgInfo == null) {
+                // "illegal package" not a tremendously useful message
+                //apkError("Illegal package ref: " + apkPkgInfo.getName());
+                badPackage = true;
+            }
+
+            Iterator<ClassInfo> classIter = apkPkgInfo.getClassIterator();
+            while (classIter.hasNext()) {
+                ClassInfo apkClassInfo = classIter.next();
+
+                if (badPackage) {
+                    /*
+                     * The package is not present in the public API file,
+                     * but simply saying "bad package" isn't all that
+                     * useful, so we emit the names of each of the classes.
+                     */
+                    if (isIgnorable(apkPkgInfo)) {
+                        apkWarning("Ignoring class ref: " +
+                            apkPkgInfo.getName() + "." + apkClassInfo.getName());
+                    } else {
+                        apkError("Illegal class ref: " +
+                            apkPkgInfo.getName() + "." + apkClassInfo.getName());
+                    }
+                } else {
+                    checkClass(pubPkgInfo, apkClassInfo);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks the class against the public API.  We check the class
+     * itself and then any fields and methods.
+     */
+    static boolean checkClass(PackageInfo pubPkgInfo, ClassInfo classInfo) {
+
+        ClassInfo pubClassInfo = pubPkgInfo.getClass(classInfo.getName());
+
+        if (pubClassInfo == null) {
+            if (isIgnorable(pubPkgInfo)) {
+                apkWarning("Ignoring class ref: " +
+                    pubPkgInfo.getName() + "." + classInfo.getName());
+            } else if (classInfo.hasNoFieldMethod()) {
+                apkWarning("Hidden class referenced: " +
+                    pubPkgInfo.getName() + "." + classInfo.getName());
+            } else {
+                apkError("Illegal class ref: " +
+                    pubPkgInfo.getName() + "." + classInfo.getName());
+                // could list specific fields/methods used
+            }
+            return false;
+        }
+
+        /*
+         * Check the contents of classInfo against pubClassInfo.
+         */
+        Iterator<FieldInfo> fieldIter = classInfo.getFieldIterator();
+        while (fieldIter.hasNext()) {
+            FieldInfo apkFieldInfo = fieldIter.next();
+            String nameAndType = apkFieldInfo.getNameAndType();
+            FieldInfo pubFieldInfo = pubClassInfo.getField(nameAndType);
+            if (pubFieldInfo == null) {
+                if (pubClassInfo.isEnum()) {
+                    apkWarning("Enum field ref: " + pubPkgInfo.getName() +
+                        "." + classInfo.getName() + "." + nameAndType);
+                } else {
+                    apkError("Illegal field ref: " + pubPkgInfo.getName() +
+                        "." + classInfo.getName() + "." + nameAndType);
+                }
+            }
+        }
+
+        Iterator<MethodInfo> methodIter = classInfo.getMethodIterator();
+        while (methodIter.hasNext()) {
+            MethodInfo apkMethodInfo = methodIter.next();
+            String nameAndDescr = apkMethodInfo.getNameAndDescriptor();
+            MethodInfo pubMethodInfo = pubClassInfo.getMethod(nameAndDescr);
+            if (pubMethodInfo == null) {
+                pubMethodInfo = pubClassInfo.getMethodIgnoringReturn(nameAndDescr);
+                if (pubMethodInfo == null) {
+                    if (pubClassInfo.isAnnotation()) {
+                        apkWarning("Annotation method ref: " +
+                            pubPkgInfo.getName() + "." + classInfo.getName() +
+                            "." + nameAndDescr);
+                    } else {
+                        apkError("Illegal method ref: " + pubPkgInfo.getName() +
+                            "." + classInfo.getName() + "." + nameAndDescr);
+                    }
+                } else {
+                    apkWarning("Possibly covariant method ref: " +
+                        pubPkgInfo.getName() + "." + classInfo.getName() +
+                        "." + nameAndDescr);
+                }
+            }
+        }
+
+
+        return true;
+    }
+
+    /**
+     * Returns true if the package is in the "ignored" list.
+     */
+    static boolean isIgnorable(PackageInfo pkgInfo) {
+        return sIgnorablePackages.contains(pkgInfo.getName());
+    }
+
+    /**
+     * Prints a warning message about an APK problem.
+     */
+    public static void apkWarning(String msg) {
+        if (sShowWarnings) {
+            System.out.println("(warn) " + sCurrentApk.getDebugString() +
+                ": " + msg);
+        }
+        sCurrentApk.incrWarnings();
+    }
+
+    /**
+     * Prints an error message about an APK problem.
+     */
+    public static void apkError(String msg) {
+        if (sShowErrors) {
+            System.out.println(sCurrentApk.getDebugString() + ": " + msg);
+        }
+        sCurrentApk.incrErrors();
+    }
+
+    /**
+     * Recursively dumps the contents of the API.  Sort order is not
+     * specified.
+     */
+    private static void dumpApi(ApiList apiList) {
+        Iterator<PackageInfo> iter = apiList.getPackageIterator();
+        while (iter.hasNext()) {
+            PackageInfo pkgInfo = iter.next();
+            dumpPackage(pkgInfo);
+        }
+    }
+
+    private static void dumpPackage(PackageInfo pkgInfo) {
+        Iterator<ClassInfo> iter = pkgInfo.getClassIterator();
+        System.out.println("PACKAGE " + pkgInfo.getName());
+        while (iter.hasNext()) {
+            ClassInfo classInfo = iter.next();
+            dumpClass(classInfo);
+        }
+    }
+
+    private static void dumpClass(ClassInfo classInfo) {
+        System.out.println(" CLASS " + classInfo.getName());
+        Iterator<FieldInfo> fieldIter = classInfo.getFieldIterator();
+        while (fieldIter.hasNext()) {
+            FieldInfo fieldInfo = fieldIter.next();
+            dumpField(fieldInfo);
+        }
+        Iterator<MethodInfo> methIter = classInfo.getMethodIterator();
+        while (methIter.hasNext()) {
+            MethodInfo methInfo = methIter.next();
+            dumpMethod(methInfo);
+        }
+    }
+
+    private static void dumpMethod(MethodInfo methInfo) {
+        System.out.println("  METHOD " + methInfo.getNameAndDescriptor());
+    }
+
+    private static void dumpField(FieldInfo fieldInfo) {
+        System.out.println("  FIELD " + fieldInfo.getNameAndType());
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/Builtin.java b/tools/apkcheck/src/com/android/apkcheck/Builtin.java
new file mode 100644
index 0000000..8e3b196
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/Builtin.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.io.StringReader;
+
+/**
+ * Class containing "built-in" API description entries.
+ *
+ * There are some bugs in the API description file that we can't work around
+ * (notably some ambiguity with generic types).  The easiest way to cope
+ * is to supply the correct definitions in an add-on file.  Rather than
+ * cart around an extra file, we bake them in here.
+ */
+public class Builtin {
+    private Builtin() {}
+
+    private static final String BUILTIN =
+        "<api>\n" +
+        " <package name=\"java.util\">\n" +
+        "  <class name=\"EnumSet\"\n" +
+        "   extends=\"java.util.AbstractSet\">\n" +
+        "   <method name=\"of\" return=\"java.util.EnumSet\">\n" +
+        "    <parameter name=\"e\" type=\"java.lang.Enum\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"of\" return=\"java.util.EnumSet\">\n" +
+        "    <parameter name=\"e1\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e2\" type=\"java.lang.Enum\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"of\" return=\"java.util.EnumSet\">\n" +
+        "    <parameter name=\"e1\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e2\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e3\" type=\"java.lang.Enum\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"of\" return=\"java.util.EnumSet\">\n" +
+        "    <parameter name=\"e1\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e2\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e3\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e4\" type=\"java.lang.Enum\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"of\" return=\"java.util.EnumSet\">\n" +
+        "    <parameter name=\"e1\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e2\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e3\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e4\" type=\"java.lang.Enum\"/>\n" +
+        "    <parameter name=\"e5\" type=\"java.lang.Enum\"/>\n" +
+        "   </method>\n" +
+        "  </class>\n" +
+
+        " </package>\n" +
+        " <package name=\"android.os\">\n" +
+
+        "  <class name=\"RemoteCallbackList\"\n" +
+        "   extends=\"java.lang.Object\">\n" +
+        "   <method name=\"register\" return=\"boolean\">\n" +
+        "    <parameter name=\"callback\" type=\"android.os.IInterface\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"unregister\" return=\"boolean\">\n" +
+        "    <parameter name=\"callback\" type=\"android.os.IInterface\"/>\n" +
+        "   </method>\n" +
+        "  </class>\n" +
+
+        "  <class name=\"AsyncTask\"\n" +
+        "   extends=\"java.lang.Object\">\n" +
+        "   <method name=\"onPostExecute\" return=\"void\">\n" +
+        "    <parameter name=\"result\" type=\"java.lang.Object\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"onProgressUpdate\" return=\"void\">\n" +
+        "    <parameter name=\"values\" type=\"java.lang.Object[]\"/>\n" +
+        "   </method>\n" +
+        "   <method name=\"execute\" return=\"android.os.AsyncTask\">\n" +
+        "    <parameter name=\"params\" type=\"java.lang.Object[]\"/>\n" +
+        "   </method>\n" +
+        "  </class>\n" +
+
+        " </package>\n" +
+        " <package name=\"android.widget\">\n" +
+
+        "  <class name=\"AutoCompleteTextView\"\n" +
+        "   extends=\"android.widget.EditText\">\n" +
+        "   <method name=\"setAdapter\" return=\"void\">\n" +
+        "    <parameter name=\"adapter\" type=\"android.widget.ListAdapter\"/>\n" +
+        "   </method>\n" +
+        "  </class>\n" +
+
+        " </package>\n" +
+        "</api>\n"
+        ;
+
+    /**
+     * Returns the built-in definition "file".
+     */
+    public static StringReader getReader() {
+        return new StringReader(BUILTIN);
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/ClassInfo.java b/tools/apkcheck/src/com/android/apkcheck/ClassInfo.java
new file mode 100644
index 0000000..be2c1b1
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/ClassInfo.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Container representing a class or interface with fields and methods.
+ */
+public class ClassInfo {
+    private String mName;
+    // methods are hashed on name:descriptor
+    private HashMap<String,MethodInfo> mMethodList;
+    // fields are hashed on name:type
+    private HashMap<String,FieldInfo> mFieldList;
+
+    private String mSuperclassName;
+
+    // is this a static inner class?
+    private String mIsStatic;
+
+    // holds the name of the superclass and all declared interfaces
+    private ArrayList<String> mSuperNames;
+
+    // is this an enumerated type?
+    private boolean mIsEnum;
+    // is this an annotation type?
+    private boolean mIsAnnotation;
+
+    private boolean mFlattening = false;
+    private boolean mFlattened = false;
+
+    /**
+     * Constructs a new ClassInfo with the provided class name.
+     *
+     * @param className Binary class name without the package name,
+     *      e.g. "AlertDialog$Builder".
+     * @param superclassName Fully-qualified binary or non-binary superclass
+     *      name (e.g. "java.lang.Enum").
+     * @param isStatic Class static attribute, may be "true", "false", or null.
+     */
+    public ClassInfo(String className, String superclassName, String isStatic) {
+        mName = className;
+        mMethodList = new HashMap<String,MethodInfo>();
+        mFieldList = new HashMap<String,FieldInfo>();
+        mSuperNames = new ArrayList<String>();
+        mIsStatic = isStatic;
+
+        /*
+         * Record the superclass name, and add it to the interface list
+         * since we'll need to do the same "flattening" work on it.
+         *
+         * Interfaces and java.lang.Object have a null value.
+         */
+        if (superclassName != null) {
+            mSuperclassName = superclassName;
+            mSuperNames.add(superclassName);
+        }
+    }
+
+    /**
+     * Returns the name of the class.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the name of the superclass.
+     */
+    public String getSuperclassName() {
+        return mSuperclassName;
+    }
+
+    /**
+     * Returns the "static" attribute.
+     *
+     * This is actually tri-state:
+     *   "true" means it is static
+     *   "false" means it's not static
+     *   null means it's unknown
+     *
+     * The "unknown" state is associated with the APK input, while the
+     * known states are from the public API definition.
+     *
+     * This relates to the handling of the "secret" first parameter to
+     * constructors of non-static inner classes.
+     */
+    public String getStatic() {
+        return mIsStatic;
+    }
+
+    /**
+     * Returns whether or not this class is an enumerated type.
+     */
+    public boolean isEnum() {
+        assert mFlattened;
+        return mIsEnum;
+    }
+
+    /**
+     * Returns whether or not this class is an annotation type.
+     */
+    public boolean isAnnotation() {
+        assert mFlattened;
+        return mIsAnnotation;
+    }
+
+    /**
+     * Adds a field to the list.
+     */
+    public void addField(FieldInfo fieldInfo) {
+        mFieldList.put(fieldInfo.getNameAndType(), fieldInfo);
+    }
+
+    /**
+     * Retrives a field from the list.
+     *
+     * @param nameAndType fieldName:type
+     */
+    public FieldInfo getField(String nameAndType) {
+        return mFieldList.get(nameAndType);
+    }
+
+    /**
+     * Returns an iterator over all known fields.
+     */
+    public Iterator<FieldInfo> getFieldIterator() {
+        return mFieldList.values().iterator();
+    }
+
+    /**
+     * Adds a method to the list.
+     */
+    public void addMethod(MethodInfo methInfo) {
+        mMethodList.put(methInfo.getNameAndDescriptor(), methInfo);
+    }
+
+    /**
+     * Returns an iterator over all known methods.
+     */
+    public Iterator<MethodInfo> getMethodIterator() {
+        return mMethodList.values().iterator();
+    }
+
+    /**
+     * Retrieves a method from the list.
+     *
+     * @param nameAndDescr methodName:descriptor
+     */
+    public MethodInfo getMethod(String nameAndDescr) {
+        return mMethodList.get(nameAndDescr);
+    }
+
+    /**
+     * Retrieves a method from the list, matching on the part of the key
+     * before the return type.
+     *
+     * The API file doesn't include an entry for a method that overrides
+     * a method in the superclass.  Ordinarily this is a good thing, but
+     * if the override uses a covariant return type then the reference
+     * to it in the APK won't match.
+     *
+     * @param nameAndDescr methodName:descriptor
+     */
+    public MethodInfo getMethodIgnoringReturn(String nameAndDescr) {
+        String shortKey = nameAndDescr.substring(0, nameAndDescr.indexOf(')')+1);
+
+        Iterator<MethodInfo> iter = getMethodIterator();
+        while (iter.hasNext()) {
+            MethodInfo methInfo = iter.next();
+            String nad = methInfo.getNameAndDescriptor();
+            if (nad.startsWith(shortKey))
+                return methInfo;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if the method and field lists are empty.
+     */
+    public boolean hasNoFieldMethod() {
+        return mMethodList.size() == 0 && mFieldList.size() == 0;
+    }
+
+    /**
+     * Adds an interface to the list of classes implemented by this class.
+     */
+    public void addInterface(String interfaceName) {
+        mSuperNames.add(interfaceName);
+    }
+
+    /**
+     * Flattens a class.  This involves copying all methods and fields
+     * declared by the superclass and interfaces (and, recursively, their
+     * superclasses and interfaces) into the local structure.
+     *
+     * The public API file must be fully parsed before calling here.
+     *
+     * This also detects if we're an Enum or Annotation.
+     */
+    public void flattenClass(ApiList apiList) {
+        if (mFlattened)
+            return;
+
+        /*
+         * Recursive class definitions aren't allowed in Java code, but
+         * there could be one in the API definition file.
+         */
+        if (mFlattening) {
+            throw new RuntimeException("Recursive invoke; current class is "
+                + mName);
+        }
+        mFlattening = true;
+
+        /*
+         * Normalize the ambiguous types.  This requires regenerating the
+         * field and method lists, because the signature is used as the
+         * hash table key.
+         */
+        normalizeTypes(apiList);
+
+        /*
+         * Figure out if this class is an enumerated type.
+         */
+        mIsEnum = "java.lang.Enum".equals(mSuperclassName);
+
+        /*
+         * Figure out if this class is an annotation type.  We expect it
+         * to extend Object, implement java.lang.annotation.Annotation,
+         * and declare no fields or methods.  (If the API XML file is
+         * fixed, it will declare methods; but at that point having special
+         * handling for annotations will be unnecessary.)
+         */
+        if ("java.lang.Object".equals(mSuperclassName) &&
+            mSuperNames.contains("java.lang.annotation.Annotation") &&
+            hasNoFieldMethod())
+        {
+            mIsAnnotation = true;
+        }
+
+        /*
+         * Flatten our superclass and interfaces.
+         */
+        for (int i = 0; i < mSuperNames.size(); i++) {
+            /*
+             * The contents of mSuperNames are in an ambiguous form.
+             * Normalize it to binary form before working with it.
+             */
+            String interfaceName = TypeUtils.ambiguousToBinaryName(mSuperNames.get(i),
+                    apiList);
+            ClassInfo classInfo = lookupClass(interfaceName, apiList);
+            if (classInfo == null) {
+                ApkCheck.apkWarning("Class " + interfaceName +
+                    " not found (super of " + mName + ")");
+                continue;
+            }
+
+            /* flatten it */
+            classInfo.flattenClass(apiList);
+
+            /* copy everything from it in here */
+            mergeFrom(classInfo);
+        }
+
+        mFlattened = true;
+    }
+
+    /**
+     * Normalizes the type names used in field and method descriptors.
+     *
+     * We call the field/method normalization function, which updates how
+     * it thinks of itself (and may be called multiple times from different
+     * classes).  We then have to re-add it to the hash map because the
+     * key may have changed.  (We're using an iterator, so we create a
+     * new hashmap and replace the old.)
+     */
+    private void normalizeTypes(ApiList apiList) {
+        Iterator<String> keyIter;
+
+        HashMap<String,FieldInfo> tmpFieldList = new HashMap<String,FieldInfo>();
+        keyIter = mFieldList.keySet().iterator();
+        while (keyIter.hasNext()) {
+            String key = keyIter.next();
+            FieldInfo fieldInfo = mFieldList.get(key);
+            fieldInfo.normalizeType(apiList);
+            tmpFieldList.put(fieldInfo.getNameAndType(), fieldInfo);
+        }
+        mFieldList = tmpFieldList;
+
+        HashMap<String,MethodInfo> tmpMethodList = new HashMap<String,MethodInfo>();
+        keyIter = mMethodList.keySet().iterator();
+        while (keyIter.hasNext()) {
+            String key = keyIter.next();
+            MethodInfo methodInfo = mMethodList.get(key);
+            methodInfo.normalizeTypes(apiList);
+            tmpMethodList.put(methodInfo.getNameAndDescriptor(), methodInfo);
+        }
+        mMethodList = tmpMethodList;
+    }
+
+    /**
+     * Merges the fields and methods from "otherClass" into this class.
+     *
+     * Redundant entries will be merged.  We don't specify who the winner
+     * will be.
+     */
+    private void mergeFrom(ClassInfo otherClass) {
+        /*System.out.println("merging into " + getName() + ": fields=" +
+            mFieldList.size() + "/" + otherClass.mFieldList.size() +
+            ", methods=" +
+            mMethodList.size() + "/" + otherClass.mMethodList.size());*/
+
+        mFieldList.putAll(otherClass.mFieldList);
+        mMethodList.putAll(otherClass.mMethodList);
+
+        /*System.out.println("  now fields=" + mFieldList.size() +
+            ", methods=" + mMethodList.size());*/
+    }
+
+
+    /**
+     * Finds the named class in the ApiList.
+     *
+     * @param className Fully-qualified dot notation (e.g. "java.lang.String")
+     * @param apiList The hierarchy to search in.
+     * @return The class or null if not found.
+     */
+    private static ClassInfo lookupClass(String fullname, ApiList apiList) {
+        String packageName = TypeUtils.packageNameOnly(fullname);
+        String className = TypeUtils.classNameOnly(fullname);
+
+        PackageInfo pkg = apiList.getPackage(packageName);
+        if (pkg == null)
+            return null;
+        return pkg.getClass(className);
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/FieldInfo.java b/tools/apkcheck/src/com/android/apkcheck/FieldInfo.java
new file mode 100644
index 0000000..4ab0665
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/FieldInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+/**
+ * Container representing a method with parameters.
+ */
+public class FieldInfo {
+    private String mName;
+    private String mType;
+    private String mNameAndType;
+    private boolean mTypeNormalized;
+
+    /**
+     * Constructs a FieldInfo.
+     *
+     * @param name Field name.
+     * @param type Fully-qualified binary or non-binary type name.
+     */
+    public FieldInfo(String name, String type) {
+        mName = name;
+        mType = type;
+    }
+
+    /**
+     * Returns the combined name and type.  This value is used as a hash
+     * table key.
+     */
+    public String getNameAndType() {
+        if (mNameAndType == null)
+            mNameAndType = mName + ":" + TypeUtils.typeToDescriptor(mType);
+        return mNameAndType;
+    }
+
+    /**
+     * Normalize the type used in fields.
+     */
+    public void normalizeType(ApiList apiList) {
+        if (!mTypeNormalized) {
+            String type = TypeUtils.ambiguousToBinaryName(mType, apiList);
+            if (!type.equals(mType)) {
+                /* name changed, force regen on name+type */
+                mType = type;
+                mNameAndType = null;
+            }
+            mTypeNormalized = true;
+        }
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/MethodInfo.java b/tools/apkcheck/src/com/android/apkcheck/MethodInfo.java
new file mode 100644
index 0000000..79e4cd5
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/MethodInfo.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Container representing a method with parameters.
+ */
+public class MethodInfo {
+    private String mName;
+    private String mReturn;
+    private String mNameAndDescriptor;
+    private ArrayList<String> mParameters;
+    private boolean mParametersNormalized;
+
+    /**
+     * Constructs MethodInfo.  Tuck the method return type away for
+     * later construction of the signature.
+     */
+    public MethodInfo(String name, String returnType) {
+        mName = name;
+        mReturn = returnType;
+        mParameters = new ArrayList<String>();
+    }
+
+    /**
+     * Returns the method signature.  This is generated when needed.
+     */
+    public String getNameAndDescriptor() {
+        if (mNameAndDescriptor == null) {
+            StringBuilder newSig = new StringBuilder(mName);
+            newSig.append(":(");
+            for (int i = 0; i < mParameters.size(); i++) {
+                String humanType = mParameters.get(i);
+                String sigType = TypeUtils.typeToDescriptor(humanType);
+                newSig.append(sigType);
+            }
+            newSig.append(")");
+            newSig.append(TypeUtils.typeToDescriptor(mReturn));
+            mNameAndDescriptor = newSig.toString();
+        }
+        return mNameAndDescriptor;
+    }
+
+    /**
+     * Adds a parameter to the method.  The "type" is a primitive or
+     * object type, formatted in human-centric form.  For now we just
+     * store it.
+     */
+    public void addParameter(String type) {
+        mParameters.add(type);
+        if (mNameAndDescriptor != null) {
+            System.err.println("WARNING: late add of params to method");
+            mNameAndDescriptor = null;      // force regen
+        }
+    }
+
+    /**
+     * Normalizes the types in parameter lists to unambiguous binary form.
+     *
+     * The public API file must be fully parsed before calling here,
+     * because we need the full set of package names.
+     */
+    public void normalizeTypes(ApiList apiList) {
+        if (!mParametersNormalized) {
+            mReturn = TypeUtils.ambiguousToBinaryName(mReturn, apiList);
+
+            for (int i = 0; i < mParameters.size(); i++) {
+                String fixed = TypeUtils.ambiguousToBinaryName(mParameters.get(i),
+                        apiList);
+                mParameters.set(i, fixed);
+            }
+
+            mNameAndDescriptor = null;      // force regen
+            mParametersNormalized = true;
+        }
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/PackageInfo.java b/tools/apkcheck/src/com/android/apkcheck/PackageInfo.java
new file mode 100644
index 0000000..533f917
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/PackageInfo.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Container representing a package of classes and interfaces.
+ */
+public class PackageInfo {
+    private String mName;
+    private HashMap<String,ClassInfo> mClassList;
+
+    public PackageInfo(String name) {
+        mName = name;
+        mClassList = new HashMap<String,ClassInfo>();
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Retrieves the named class.
+     *
+     * @return the package, or null if no match was found
+     */
+    public ClassInfo getClass(String name) {
+        return mClassList.get(name);
+    }
+
+    /**
+     * Retrieves the named class, creating it if it doesn't already
+     * exist.
+     *
+     * @param className Binary or non-binary class name without the
+     *      package name, e.g. "AlertDialog.Builder".
+     * @param superclassName Fully-qualified binary or non-binary superclass
+     *      name (e.g. "java.lang.Enum").
+     * @param isStatic Class static attribute, may be "true", "false", or null.
+     */
+    public ClassInfo getOrCreateClass(String className, String superclassName,
+            String isStatic) {
+        String fixedName = TypeUtils.simpleClassNameToBinary(className);
+        ClassInfo classInfo = mClassList.get(fixedName);
+        if (classInfo == null) {
+            //System.out.println("--- creating entry for class " + fixedName +
+            //    " (super=" + superclassName + ")");
+            classInfo = new ClassInfo(fixedName, superclassName, isStatic);
+            mClassList.put(fixedName, classInfo);
+        } else {
+            //System.out.println("--- returning existing class " + name);
+        }
+        return classInfo;
+    }
+
+    /**
+     * Returns an iterator for the set of classes in this package.
+     */
+    public Iterator<ClassInfo> getClassIterator() {
+        return mClassList.values().iterator();
+    }
+}
+
diff --git a/tools/apkcheck/src/com/android/apkcheck/TypeUtils.java b/tools/apkcheck/src/com/android/apkcheck/TypeUtils.java
new file mode 100644
index 0000000..6cfe031
--- /dev/null
+++ b/tools/apkcheck/src/com/android/apkcheck/TypeUtils.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2010 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.apkcheck;
+
+import java.util.HashMap;
+
+public class TypeUtils {
+    private void TypeUtils() {}
+
+    /*
+     * Conversions for the primitive types, as well as a few things
+     * that show up a lot so we can avoid the string manipulation.
+     */
+    private static final HashMap<String,String> sQuickConvert;
+    static {
+        sQuickConvert = new HashMap<String,String>();
+
+        sQuickConvert.put("boolean", "Z");
+        sQuickConvert.put("byte", "B");
+        sQuickConvert.put("char", "C");
+        sQuickConvert.put("short", "S");
+        sQuickConvert.put("int", "I");
+        sQuickConvert.put("float", "F");
+        sQuickConvert.put("long", "J");
+        sQuickConvert.put("double", "D");
+        sQuickConvert.put("void", "V");
+        sQuickConvert.put("java.lang.Object", "Ljava/lang/Object;");
+        sQuickConvert.put("java.lang.String", "Ljava/lang/String;");
+        sQuickConvert.put("java.util.ArrayList", "Ljava/util/ArrayList;");
+        sQuickConvert.put("java.util.HashMap", "Ljava/util/HashMap;");
+    };
+
+    /*
+     * Convert a human-centric type into something suitable for a method
+     * signature.  Examples:
+     *
+     *   int --> I
+     *   float[] --> [F
+     *   java.lang.String --> Ljava/lang/String;
+     */
+    public static String typeToDescriptor(String type) {
+        String quick = sQuickConvert.get(type);
+        if (quick != null)
+            return quick;
+
+        int arrayDepth = 0;
+        int firstPosn = -1;
+        int posn = -1;
+        while ((posn = type.indexOf('[', posn+1)) != -1) {
+            if (firstPosn == -1)
+                firstPosn = posn;
+            arrayDepth++;
+        }
+
+        /* if we found an array, strip the brackets off */
+        if (firstPosn != -1)
+            type = type.substring(0, firstPosn);
+
+        StringBuilder builder = new StringBuilder();
+        while (arrayDepth-- > 0)
+            builder.append("[");
+
+        /* retry quick convert */
+        quick = sQuickConvert.get(type);
+        if (quick != null) {
+            builder.append(quick);
+        } else {
+            builder.append("L");
+            builder.append(type.replace('.', '/'));
+            builder.append(";");
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Converts a "simple" class name into a "binary" class name.  For
+     * example:
+     *
+     *   SharedPreferences.Editor --&gt; SharedPreferences$Editor
+     *
+     * Do not use this on fully-qualified class names.
+     */
+    public static String simpleClassNameToBinary(String className) {
+        return className.replace('.', '$');
+    }
+
+    /**
+     * Returns the class name portion of a fully-qualified binary class name.
+     */
+    static String classNameOnly(String typeName) {
+        int start = typeName.lastIndexOf(".");
+        if (start < 0) {
+            return typeName;
+        } else {
+            return typeName.substring(start+1);
+        }
+    }
+
+    /**
+     * Returns the package portion of a fully-qualified binary class name.
+     */
+    static String packageNameOnly(String typeName) {
+        int end = typeName.lastIndexOf(".");
+        if (end < 0) {
+            /* lives in default package */
+            return "";
+        } else {
+            return typeName.substring(0, end);
+        }
+    }
+
+
+    /**
+     * Normalizes a full class name to binary form.
+     *
+     * For example, "android.view.View.OnClickListener" could be in
+     * the "android.view" package or the "android.view.View" package.
+     * Checking capitalization is unreliable.  We do have a full list
+     * of package names from the file though, so there's an excellent
+     * chance that we can identify the package that way.  (Of course, we
+     * can only do this after we have finished parsing the file.)
+     *
+     * If the name has two or more dots, we need to compare successively
+     * shorter strings until we find a match in the package list.
+     *
+     * Do not call this on previously-returned output, as that may
+     * confuse the code that deals with generic signatures.
+     */
+    public static String ambiguousToBinaryName(String typeName, ApiList apiList) {
+        /*
+         * In some cases this can be a generic signature:
+         *   <parameter name="collection" type="java.util.Collection&lt;? extends E&gt;">
+         *   <parameter name="interfaces" type="java.lang.Class&lt;?&gt;[]">
+         *   <parameter name="object" type="E">
+         *
+         * If we see a '<', strip out everything from it to the '>'.  That
+         * does pretty much the right thing, though we have to deal with
+         * nested stuff like "<? extends Map<String>>".
+         *
+         * Handling the third item is ugly.  If the string is a single
+         * character, change it to java.lang.Object.  This is generally
+         * insufficient and also ambiguous with respect to classes in the
+         * default package, but we don't have much choice here, and it gets
+         * us through the standard collection classes.  Note this is risky
+         * if somebody tries to normalize a string twice, since we could be
+         * "promoting" a primitive type.
+         */
+        typeName = stripAngleBrackets(typeName);
+        if (typeName.length() == 1) {
+            //System.out.println("converting X to Object: " + typeName);
+            typeName = "java.lang.Object";
+        } else if (typeName.length() == 3 &&
+                   typeName.substring(1, 3).equals("[]")) {
+            //System.out.println("converting X[] to Object[]: " + typeName);
+            typeName = "java.lang.Object[]";
+        } else if (typeName.length() == 4 &&
+                   typeName.substring(1, 4).equals("...")) {
+            //System.out.println("converting X... to Object[]: " + typeName);
+            typeName = "java.lang.Object[]";
+        }
+
+        /*
+         * Catch-all for varargs, which come in different varieties:
+         *  java.lang.Object...
+         *  java.lang.Class...
+         *  java.lang.CharSequence...
+         *  int...
+         *  Progress...
+         *
+         * The latter is a generic type that we didn't catch above because
+         * it's not using a single-character descriptor.
+         *
+         * The method reference for "java.lang.Class..." will be looking
+         * for java.lang.Class[], not java.lang.Object[], so we don't want
+         * to do a blanket conversion.  Similarly, "int..." turns into int[].
+         *
+         * There's not much we can do with "Progress...", unless we want
+         * to write off the default package and filter out primitive types.
+         * Probably easier to fix it up elsewhere.
+         */
+        int ellipsisIndex = typeName.indexOf("...");
+        if (ellipsisIndex >= 0) {
+            String newTypeName = typeName.substring(0, ellipsisIndex) + "[]";
+            //System.out.println("vararg " + typeName + " --> " + newTypeName);
+            typeName = newTypeName;
+        }
+
+        /*
+         * It's possible the code that generates API definition files
+         * has been fixed.  If we see a '$', just return the original.
+         */
+        if (typeName.indexOf('$') >= 0)
+            return typeName;
+
+        int lastDot = typeName.lastIndexOf('.');
+        if (lastDot < 0)
+            return typeName;
+
+        /*
+         * What we have looks like some variation of these:
+         *   package.Class
+         *   Class.InnerClass
+         *   long.package.name.Class
+         *   long.package.name.Class.InnerClass
+         *
+         * We cut it off at the last '.' and test to see if it's a known
+         * package name.  If not, keep moving left until we run out of dots.
+         */
+        int nextDot = lastDot;
+        while (nextDot >= 0) {
+            String testName = typeName.substring(0, nextDot);
+            if (apiList.getPackage(testName) != null) {
+                break;
+            }
+
+            nextDot = typeName.lastIndexOf('.', nextDot-1);
+        }
+
+        if (nextDot < 0) {
+            /* no package name found, convert all dots */
+            System.out.println("+++ no pkg name found on " + typeName + typeName.length());
+            typeName = typeName.replace('.', '$');
+        } else if (nextDot == lastDot) {
+            /* class name is last element; original string is fine */
+        } else {
+            /* in the middle; zap the dots in the inner class name */
+            String oldClassName = typeName;
+            typeName = typeName.substring(0, nextDot+1) +
+                typeName.substring(nextDot+1).replace('.', '$');
+            //System.out.println("+++ " + oldClassName + " --> " + typeName);
+        }
+
+        return typeName;
+    }
+
+    /**
+     * Strips out everything between '<' and '>'.  This will handle
+     * nested brackets, but we're not expecting to see multiple instances
+     * in series (i.e. "&lt;foo&lt;bar&gt;&gt;" is expected, but
+     * "&lt;foo&gt;STUFF&lt;bar&gt; is not).
+     *
+     * @return the stripped string
+     */
+    private static String stripAngleBrackets(String str) {
+        /*
+         * Since we only expect to see one "run", we can just find the
+         * first '<' and the last '>'.  Ideally we'd verify that they're
+         * not mismatched, but we're assuming the input file is generally
+         * correct.
+         */
+        int ltIndex = str.indexOf('<');
+        if (ltIndex < 0)
+            return str;
+
+        int gtIndex = str.lastIndexOf('>');
+        if (gtIndex < 0) {
+            System.err.println("ERROR: found '<' without '>': " + str);
+            return str;     // not much we can do
+        }
+
+        return str.substring(0, ltIndex) + str.substring(gtIndex+1);
+    }
+}
+
diff --git a/tools/findunused/findunusedstrings b/tools/findunused/findunusedstrings
index 7057527..9615abc 100755
--- a/tools/findunused/findunusedstrings
+++ b/tools/findunused/findunusedstrings
@@ -28,7 +28,8 @@
     if [ -d $app/res ]
     then
         pushd $app > /dev/null
-        for i in $(grep -Rs "\(string\|plurals\) name=" res | sed 's/.*<\(string\|plurals\) name="//'|sed 's/".*$//'|sort -u)
+        # Two sed's were needed because the | operator is not supported on the mac
+        for i in $(grep -Rs "\(string\|plurals\) name=" res | sed 's/.*string name=\"//' | sed 's/.*plurals name=\"//'|sed 's/".*$//'|sort -u)
         do
             echo $i $(grep -Rws R.plurals.$i\\\|R.string.$i\\\|@string/$i .|wc -l)
         done | grep ' 0$' | {