Merge "Improve ContractsContract.RawContacts documentation"
diff --git a/api/current.xml b/api/current.xml
index 7acbccb..1142088 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -14690,6 +14690,25 @@
 <parameter name="value" type="java.lang.String">
 </parameter>
 </method>
+<method name="testHasFeatures"
+ return="android.accounts.AccountManagerFuture&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
 <method name="updateCredentials"
  return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
  abstract="false"
@@ -23739,6 +23758,19 @@
 </implements>
 <implements name="android.content.DialogInterface.OnDismissListener">
 </implements>
+<method name="getSearchableInfo"
+ return="android.app.SearchableInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="componentName" type="android.content.ComponentName">
+</parameter>
+</method>
 <method name="getSearchablesInGlobalSearch"
  return="java.util.List&lt;android.app.SearchableInfo&gt;"
  abstract="false"
@@ -23895,6 +23927,28 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_SELECT_QUERY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;select_query&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_ACTION_GLOBAL_SEARCH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.search.action.GLOBAL_SEARCH&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="INTENT_ACTION_SEARCHABLES_CHANGED"
  type="java.lang.String"
  transient="false"
@@ -51681,17 +51735,6 @@
 <exception name="SQLException" type="android.database.SQLException">
 </exception>
 </method>
-<method name="resetCompiledSqlCache"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="setLocale"
  return="void"
  abstract="false"
@@ -51718,19 +51761,6 @@
 <parameter name="lockingEnabled" type="boolean">
 </parameter>
 </method>
-<method name="setMaxSqlCacheSize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cacheSize" type="int">
-</parameter>
-</method>
 <method name="setMaximumSize"
  return="long"
  abstract="false"
@@ -83861,6 +83891,50 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_MOBILE_DUN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_MOBILE_HIPRI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_MOBILE_MMS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_MOBILE_SUPL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TYPE_WIFI"
  type="int"
  transient="false"
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 27891ec..431a556 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -15,15 +15,4 @@
 
 include $(BUILD_EXECUTABLE)
 
-COMMANDS = dumpcrash
-SYMLINKS := $(addprefix $(TARGET_OUT_EXECUTABLES)/,$(COMMANDS))
-$(SYMLINKS): DUMPSTATE_BINARY := dumpstate
-$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
-	@echo "Symlink: $@ -> $(DUMPSTATE_BINARY)"
-	@mkdir -p $(dir $@)
-	@rm -rf $@
-	$(hide) ln -sf $(DUMPSTATE_BINARY) $@
-
-ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
-
 endif
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index a2b5d8d..9300915 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -14,361 +14,254 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
 
-#include <cutils/sockets.h>
+#include <cutils/properties.h>
+
 #include "private/android_filesystem_config.h"
 
-
 #define LOG_TAG "dumpstate"
 #include <utils/Log.h>
 
 #include "dumpstate.h"
 
-static char* const gzip_args[] = { "gzip", "-6", 0 };
-static int start_pattern[] = { 150, 0 };
-static int end_pattern[] = { 75, 50, 75, 50, 75, 0 };
-
-static struct tm now;
-
-static void dump_kernel_log(const char *path, const char *title) ;
+/* read before root is shed */
+static char cmdline_buf[16384] = "(unknown)";
+static const char *dump_traces_path = NULL;
 
 /* dumps the current system state to stdout */
-static void dumpstate(int full) {
-    if (full) {
-        PRINT("========================================================");
-        PRINT("== dumpstate");
-        PRINT("========================================================");
-        PRINT("------ MEMORY INFO ------");
-        DUMP("/proc/meminfo");
-        PRINT("------ CPU INFO ------");
-        EXEC7("top", "-n", "1", "-d", "1", "-m", "30", "-t");
-        PRINT("------ PROCRANK ------");
-        EXEC_XBIN("procrank");
-        PRINT("------ VIRTUAL MEMORY STATS ------");
-        DUMP("/proc/vmstat");
-        PRINT("------ VMALLOC INFO ------");
-        DUMP("/proc/vmallocinfo");
-        PRINT("------ SLAB INFO ------");
-        DUMP("/proc/slabinfo");
-        PRINT("------ ZONEINFO ------");
-        DUMP("/proc/zoneinfo");
-        PRINT("------ SYSTEM LOG ------");
-        EXEC4("logcat", "-v", "time", "-d", "*:v");
-        PRINT("------ VM TRACES ------");
-        DUMP("/data/anr/traces.txt");
-        PRINT("------ EVENT LOG TAGS ------");
-        DUMP("/etc/event-log-tags");
-        PRINT("------ EVENT LOG ------");
-        EXEC6("logcat", "-b", "events", "-v", "time", "-d", "*:v");
-        PRINT("------ RADIO LOG ------");
-        EXEC6("logcat", "-b", "radio", "-v", "time", "-d", "*:v");
-        PRINT("------ NETWORK STATE ------");
-        PRINT("Interfaces:");
-        EXEC("netcfg");
-        PRINT("");
-        PRINT("Routes:");
-        DUMP("/proc/net/route");
+static void dumpstate() {
+    time_t now = time(NULL);
+    char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
+    char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
+    char network[PROPERTY_VALUE_MAX], date[80];
+
+    property_get("ro.build.display.id", build, "(unknown)");
+    property_get("ro.build.fingerprint", fingerprint, "(unknown)");
+    property_get("ro.baseband", radio, "(unknown)");
+    property_get("ro.bootloader", bootloader, "(unknown)");
+    property_get("gsm.operator.alpha", network, "(unknown)");
+    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
+
+    printf("========================================================\n");
+    printf("== dumpstate: %s\n", date);
+    printf("========================================================\n");
+
+    printf("\n");
+    printf("Build: %s\n", build);
+    printf("Bootloader: %s\n", bootloader);
+    printf("Radio: %s\n", radio);
+    printf("Network: %s\n", network);
+
+    printf("Kernel: ");
+    dump_file(NULL, "/proc/version");
+    printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+    printf("\n");
+
+    dump_file("MEMORY INFO", "/proc/meminfo");
+    run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-t", NULL);
+    run_command("PROCRANK", 20, "procrank", NULL);
+    dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
+    dump_file("VMALLOC INFO", "/proc/vmallocinfo");
+    dump_file("SLAB INFO", "/proc/slabinfo");
+    dump_file("ZONEINFO", "/proc/zoneinfo");
+
+    run_command("SYSTEM LOG", 20, "logcat", "-v", "time", "-d", "*:v", NULL);
+
+    /* show the traces we collected in main(), if that was done */
+    if (dump_traces_path != NULL) {
+        dump_file("VM TRACES JUST NOW", dump_traces_path);
+    }
+
+    /* only show ANR traces if they're less than 15 minutes old */
+    struct stat st;
+    char anr_traces_path[PATH_MAX];
+    property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
+    if (anr_traces_path[0] && !stat(anr_traces_path, &st) && time(NULL) - st.st_mtime < 15 * 60) {
+        dump_file("VM TRACES AT LAST ANR", anr_traces_path);
+    }
+
+    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "time", "-d", "*:v", NULL);
+    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "time", "-d", "*:v", NULL);
+
+    run_command("NETWORK INTERFACES", 10, "netcfg", NULL);
+    dump_file("NETWORK ROUTES", "/proc/net/route");
+
 #ifdef FWDUMP_bcm4329
-        PRINT("Dump wlan FW log");
-        EXEC_XBIN6("su", "root","dhdutil","-i","eth0","upload","/data/local/tmp/wlan_crash.dump");
+    run_command("DUMP WIFI FIRMWARE LOG", 60,
+            "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
 #endif
-        PRINT("------ SYSTEM PROPERTIES ------");
-        print_properties();
-        PRINT("------ KERNEL LOG ------");
-        EXEC("dmesg");
-        PRINT("------ KERNEL WAKELOCKS ------");
-        DUMP("/proc/wakelocks");
-        PRINT("------ KERNEL CPUFREQ ------");
-        DUMP("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-        PRINT("");
-        PRINT("------ PROCESSES ------");
-        EXEC1("ps", "-P");
-        PRINT("------ PROCESSES AND THREADS ------");
-        EXEC3("ps", "-t", "-p", "-P");
-        PRINT("------ LIBRANK ------");
-        EXEC_XBIN("librank");
-        PRINT("------ BINDER FAILED TRANSACTION LOG ------");
-        DUMP("/proc/binder/failed_transaction_log");
-        PRINT("");
-        PRINT("------ BINDER TRANSACTION LOG ------");
-        DUMP("/proc/binder/transaction_log");
-        PRINT("");
-        PRINT("------ BINDER TRANSACTIONS ------");
-        DUMP("/proc/binder/transactions");
-        PRINT("");
-        PRINT("------ BINDER STATS ------");
-        DUMP("/proc/binder/stats");
-        PRINT("");
-        PRINT("------ BINDER PROCESS STATE: $i ------");
-        DUMP_FILES("/proc/binder/proc");
-        PRINT("------ FILESYSTEMS ------");
-        EXEC("df");
-        PRINT("------ PACKAGE SETTINGS ------");
-        DUMP("/data/system/packages.xml");
-        PRINT("------ PACKAGE UID ERRORS ------");
-        DUMP("/data/system/uiderrors.txt");
 
-        dump_kernel_log("/proc/last_kmsg", "LAST KMSG");
+    print_properties();
 
-        PRINT("------ LAST RADIO LOG ------");
-        EXEC1("parse_radio_log", "/proc/last_radio_log");
+    run_command("KERNEL LOG", 20, "dmesg", NULL);
 
-        dump_kernel_log("/data/dontpanic/apanic_console",
-                        "PANIC CONSOLE");
-        dump_kernel_log("/data/dontpanic/apanic_threads",
-                        "PANIC THREADS");
+    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
+    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
 
-        PRINT("------ BACKLIGHTS ------");
-        DUMP_PROMPT("LCD brightness=", "/sys/class/leds/lcd-backlight/brightness");
-        DUMP_PROMPT("Button brightness=", "/sys/class/leds/button-backlight/brightness");
-        DUMP_PROMPT("Keyboard brightness=", "/sys/class/leds/keyboard-backlight/brightness");
-        DUMP_PROMPT("ALS mode=", "/sys/class/leds/lcd-backlight/als");
-        DUMP_PROMPT("LCD driver registers:\n", "/sys/class/leds/lcd-backlight/registers");
-    }
-    PRINT("========================================================");
-    PRINT("== build.prop");
-    PRINT("========================================================");
+    run_command("PROCESSES", 10, "ps", "-P", NULL);
+    run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
+    run_command("LIBRANK", 10, "librank", NULL);
 
-    /* the crash server parses key-value pairs between the VERSION INFO and
-     * END lines so we can aggregate crash reports based on this data.
-     */
-    PRINT("------ VERSION INFO ------");
-    print_date("currenttime=", &now);
-    DUMP_PROMPT("kernel.version=", "/proc/version");
-    DUMP_PROMPT("kernel.cmdline=", "/proc/cmdline");
-    DUMP("/system/build.prop");
-    PROPERTY("gsm.version.ril-impl");
-    PROPERTY("gsm.version.baseband");
-    PROPERTY("gsm.imei");
-    PROPERTY("gsm.sim.operator.numeric");
-    PROPERTY("gsm.operator.alpha");
-    PRINT("------ END ------");
+    dump_file("BINDER FAILED TRANSACTION LOG", "/proc/binder/failed_transaction_log");
+    dump_file("BINDER TRANSACTION LOG", "/proc/binder/transaction_log");
+    dump_file("BINDER TRANSACTIONS", "/proc/binder/transactions");
+    dump_file("BINDER STATS", "/proc/binder/stats");
+    run_command("BINDER PROCESS STATE", 10, "sh", "-c", "cat /proc/binder/proc/*");
 
-    if (full) {
-        PRINT("========================================================");
-        PRINT("== dumpsys");
-        PRINT("========================================================");
-        /* the full dumpsys is starting to take a long time, so we need
-           to increase its timeout.  we really need to do the timeouts in
-           dumpsys itself... */
-        EXEC_TIMEOUT("dumpsys", 60);
-    }
+    run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
+
+    dump_file("PACKAGE SETTINGS", "/data/system/packages.xml");
+    dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
+
+    dump_file("LAST KMSG", "/proc/last_kmsg");
+    run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+    dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
+    dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
+
+    printf("----- BACKLIGHTS -----\n");
+    printf("LCD brightness=");
+    dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
+    printf("Button brightness=");
+    dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
+    printf("Keyboard brightness=");
+    dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
+    printf("ALS mode=");
+    dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
+    printf("LCD driver registers:\n");
+    dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
+    printf("\n");
+
+    printf("========================================================\n");
+    printf("== Android Framework Services\n");
+    printf("========================================================\n");
+
+    /* the full dumpsys is starting to take a long time, so we need
+       to increase its timeout.  we really need to do the timeouts in
+       dumpsys itself... */
+    run_command("DUMPSYS", 60, "dumpsys", NULL);
 }
 
-/* used to check the file name passed via argv[0] */
-static int check_command_name(const char* name, const char* test) {
-    int name_length, test_length;
 
-    if (!strcmp(name, test))
-        return 1;
-
-    name_length = strlen(name);
-    test_length = strlen(test);
-
-    if (name_length > test_length + 2) {
-        name += (name_length - test_length);
-        if (name[-1] != '/')
-            return 0;
-        if (!strcmp(name, test))
-            return 1;
-    }
-
-    return 0;
+static void usage() {
+    fprintf(stderr, "usage: dumpstate [-d] [-o file] [-s] [-z]\n"
+            "  -d: append date to filename (requires -o)\n"
+            "  -o: write to file (instead of stdout)\n"
+            "  -s: write output to control socket (for init)\n"
+            "  -z: gzip output (requires -o)\n");
 }
 
 int main(int argc, char *argv[]) {
-    int dumpcrash = check_command_name(argv[0], "dumpcrash");
-    int add_date = 0;
-    char* outfile = 0;
-    int vibrate = 0;
-    int compress = 0;
-    int socket = 0;
-    int c, fd, vibrate_fd, fds[2];
-    char path[PATH_MAX];
-    pid_t   pid;
-    gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
+    int do_add_date = 0;
+    int do_compress = 0;
+    char* use_outfile = 0;
+    int use_socket = 0;
 
     LOGI("begin\n");
 
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
-    protect_from_oom_killer();
+    FILE *oom_adj = fopen("/proc/self/oom_adj", "w");
+    if (oom_adj) {
+        fputs("-17", oom_adj);
+        fclose(oom_adj);
+    }
 
-    get_time(&now);
+    /* very first thing, collect VM traces from Dalvik (needs root) */
+    dump_traces_path = dump_vm_traces();
 
-    do {
-        c = getopt(argc, argv, "do:svz");
-        if (c == EOF)
-            break;
+    int c;
+    while ((c = getopt(argc, argv, "dho:svz")) != -1) {
         switch (c) {
-            case 'd':
-                add_date = 1;
-                break;
-            case 'o':
-                outfile = optarg;
-                break;
-            case 'v':
-                vibrate = 1;
-                break;
-            case 'z':
-                compress = 1;
-                break;
-            case 's':
-                socket = 1;
-                break;
-            case '?':
-            fprintf(stderr, "%s: invalid option -%c\n",
-                argv[0], optopt);
+            case 'd': do_add_date = 1;       break;
+            case 'o': use_outfile = optarg;  break;
+            case 's': use_socket = 1;        break;
+            case 'v': break;  // compatibility no-op
+            case 'z': do_compress = 6;       break;
+            case '?': printf("\n");
+            case 'h':
+                usage();
                 exit(1);
         }
-    } while (1);
+    }
 
-    /* open vibrator before switching user */
-    if (vibrate) {
-        vibrate_fd = open("/sys/class/timed_output/vibrator/enable", O_WRONLY);
-        if (vibrate_fd > 0)
-            fcntl(vibrate_fd, F_SETFD, FD_CLOEXEC);
-    } else
-        vibrate_fd = -1;
+    /* open the vibrator before dropping root */
+    FILE *vibrator = fopen("/sys/class/timed_output/vibrator/enable", "w");
+    if (vibrator) fcntl(fileno(vibrator), F_SETFD, FD_CLOEXEC);
+
+    /* read /proc/cmdline before dropping root */
+    FILE *cmdline = fopen("/proc/cmdline", "r");
+    if (cmdline != NULL) {
+        fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
+        fclose(cmdline);
+    }
 
     /* switch to non-root user and group */
+    gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
     setgroups(sizeof(groups)/sizeof(groups[0]), groups);
     setuid(AID_SHELL);
 
-    /* make it safe to use both printf and STDOUT_FILENO */ 
-    setvbuf(stdout, 0, _IONBF, 0);
+    char path[PATH_MAX], tmp_path[PATH_MAX];
+    pid_t gzip_pid = -1;
 
-    if (socket) {
-        struct sockaddr addr;
-        socklen_t alen;
-
-        int s = android_get_control_socket("dumpstate");
-        if (s < 0) {
-            fprintf(stderr, "could not open dumpstate socket\n");
-            exit(1);
+    if (use_socket) {
+        redirect_to_socket(stdout, "dumpstate");
+    } else if (use_outfile) {
+        strlcpy(path, use_outfile, sizeof(path));
+        if (do_add_date) {
+            char date[80];
+            time_t now = time(NULL);
+            strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now));
+            strlcat(path, date, sizeof(path));
         }
-        if (listen(s, 4) < 0) {
-            fprintf(stderr, "could not listen on dumpstate socket\n");
-            exit(1);
-        }
-
-        alen = sizeof(addr);
-        fd = accept(s, &addr, &alen);
-        if (fd < 0) {
-            fprintf(stderr, "could not accept dumpstate socket\n");
-            exit(1);
-        }
-
-        /* redirect stdout to the socket */
-        dup2(fd, STDOUT_FILENO);
-        close(fd);
-    } else if (outfile) {
-        if (strlen(outfile) > sizeof(path) - 100)
-            exit(1);
-
-        strcpy(path, outfile);
-        if (add_date) {
-            char date[260];
-            strftime(date, sizeof(date),
-                "-%Y-%m-%d-%H-%M-%S",
-                &now);
-            strcat(path, date);
-        }
-        if (compress)
-            strcat(path, ".gz");
-        else
-            strcat(path, ".txt");
-
-        /* ensure that all directories in the path exist */ 
-        create_directories(path);
-        fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-        if (fd < 0)
-            return fd;
-
-        if (compress) {
-            pipe(fds);
-
-            /* redirect our stdout to the pipe */
-            dup2(fds[1], STDOUT_FILENO);
-            close(fds[1]);
-
-            if ((pid = fork()) < 0)
-            {
-                fprintf(stderr, "fork error\n");
-                exit(1);
-            }
-
-            if (pid) {
-                /* parent case */
-
-                /* close our copy of the input to gzip */
-                close(fds[0]);
-                /* close our copy of the output file */
-                close(fd);
-            } else {
-                /* child case */
-
-               /* redirect our input pipe to stdin */
-                dup2(fds[0], STDIN_FILENO);
-                close(fds[0]);
-
-                /* redirect stdout to the output file */
-                dup2(fd, STDOUT_FILENO);
-                close(fd);
-
-                /* run gzip to postprocess our output */
-                execv("/system/bin/gzip", gzip_args);
-                fprintf(stderr, "execv returned\n");
-            }
-        } else {
-            /* redirect stdout to the output file */
-            dup2(fd, STDOUT_FILENO);
-            close(fd);
-        }
-    }
-    /* else everything will print to stdout */
-
-    if (vibrate) {
-        vibrate_pattern(vibrate_fd, start_pattern);
-    }
-    dumpstate(!dumpcrash);
-    if (vibrate) {
-        vibrate_pattern(vibrate_fd, end_pattern);
-        close(vibrate_fd);
+        strlcat(path, ".txt", sizeof(path));
+        if (do_compress) strlcat(path, ".gz", sizeof(path));
+        strlcpy(tmp_path, path, sizeof(tmp_path));
+        strlcat(tmp_path, ".tmp", sizeof(tmp_path));
+        gzip_pid = redirect_to_file(stdout, tmp_path, do_compress);
     }
 
-    /* so gzip will terminate */
-    close(STDOUT_FILENO);
+    /* bzzzzzz */
+    if (vibrator) {
+        fputs("150", vibrator);
+        fflush(vibrator);
+    }
+
+    dumpstate();
+
+    /* bzzz bzzz bzzz */
+    if (vibrator) {
+        int i;
+        for (i = 0; i < 3; i++) {
+            fputs("75\n", vibrator);
+            fflush(vibrator);
+            usleep((75 + 50) * 1000);
+        }
+        fclose(vibrator);
+    }
+
+    /* wait for gzip to finish, otherwise it might get killed when we exit */
+    if (gzip_pid > 0) {
+        fclose(stdout);
+        waitpid(gzip_pid, NULL, 0);
+    }
+
+    /* rename the (now complete) .tmp file to its final location */
+    if (use_outfile && rename(tmp_path, path)) {
+        fprintf(stderr, "rename(%s, %s): %s\n", tmp_path, path, strerror(errno));
+    }
 
     LOGI("done\n");
 
     return 0;
 }
-
-static void dump_kernel_log(const char *path, const char *title) 
-
-{
-    printf("------ KERNEL %s LOG ------\n", title);
-    if (access(path, R_OK) < 0)
-        printf("%s: %s\n", path, strerror(errno));
-    else {
-        struct stat sbuf;
-
-        if (stat(path, &sbuf) < 0)
-            printf("%s: stat failed (%s)\n", path, strerror(errno));
-        else
-            printf("Harvested %s", ctime(&sbuf.st_mtime));
-    
-        DUMP(path);
-    }
-}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index ed1f005..6d48a85 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -18,157 +18,24 @@
 #define _DUMPSTATE_H_
 
 #include <time.h>
-
-// Commands time out after 60 seconds
-#define TIMEOUT     60
-
-#define PRINT(s) printf("%s\n", s)
-
-#define DUMP(file) dump_file(file)
-
-#define DUMP_FILES(path) dump_files(path)
-
-#define DUMP_PROMPT(prompt, file)   \
-{                                   \
-    printf(prompt);                 \
-    dump_file(file);                \
-}
-
-#define EXEC(cmd)               \
-{                               \
-    static struct Command c = { \
-        "/system/bin/" cmd,     \
-        { cmd, 0 }              \
-    };                          \
-    run_command(&c, TIMEOUT);   \
-}
-
-#define EXEC_TIMEOUT(cmd, tmout)\
-{                               \
-    static struct Command c = { \
-        "/system/bin/" cmd,     \
-        { cmd, 0 }              \
-    };                          \
-    run_command(&c, tmout);     \
-}
-
-#define EXEC_XBIN(cmd)          \
-{                               \
-    static struct Command c = { \
-        "/system/xbin/" cmd,    \
-        { cmd, 0 }              \
-    };                          \
-    run_command(&c, TIMEOUT);   \
-}
-
-#define EXEC1(cmd, a1)          \
-{                               \
-    static struct Command c = { \
-        "/system/bin/" cmd,     \
-        { cmd, a1, 0 }          \
-    };                          \
-    run_command(&c, TIMEOUT);   \
-}
-
-#define EXEC2(cmd, a1, a2)      \
-{                               \
-    static struct Command c = { \
-        "/system/bin/" cmd,     \
-        { cmd, a1, a2, 0 }      \
-    };                          \
-    run_command(&c, TIMEOUT);   \
-}
-
-#define EXEC3(cmd, a1, a2, a3)      \
-{                                   \
-    static struct Command c = {     \
-        "/system/bin/" cmd,         \
-        { cmd, a1, a2, a3, 0 }      \
-    };                              \
-    run_command(&c, TIMEOUT);       \
-}
-
-#define EXEC4(cmd, a1, a2, a3, a4)  \
-{                                   \
-    static struct Command c = {     \
-        "/system/bin/" cmd,         \
-        { cmd, a1, a2, a3, a4, 0 }  \
-    };                              \
-    run_command(&c, TIMEOUT);       \
-}
-
-#define EXEC6(cmd, a1, a2, a3, a4, a5, a6)  \
-{                                           \
-    static struct Command c = {             \
-        "/system/bin/" cmd,                 \
-        { cmd, a1, a2, a3, a4, a5, a6, 0 }  \
-    };                                      \
-    run_command(&c, TIMEOUT);               \
-}
-
-#define EXEC7(cmd, a1, a2, a3, a4, a5, a6, a7)  \
-{                                               \
-    static struct Command c = {                 \
-        "/system/bin/" cmd,                     \
-        { cmd, a1, a2, a3, a4, a5, a6, a7, 0 }  \
-    };                                          \
-    run_command(&c, TIMEOUT);                   \
-}
-
-#define EXEC8(cmd, a1, a2, a3, a4, a5, a6, a7, a8)  \
-{                                                   \
-    static struct Command c = {                     \
-        "/system/bin/" cmd,                         \
-        { cmd, a1, a2, a3, a4, a5, a6, a7, a8, 0 }  \
-    };                                              \
-    run_command(&c, TIMEOUT);                       \
-}
-
-#define EXEC_XBIN6(cmd, a1, a2, a3, a4, a5, a6)  \
-{                                           \
-    static struct Command c = {             \
-        "/system/xbin/" cmd,                \
-        { cmd, a1, a2, a3, a4, a5, a6, 0 }  \
-    };                                      \
-    run_command(&c, TIMEOUT);               \
-}
-
-#define PROPERTY(name) print_property(name)
-
-struct Command {
-    const char* path;
-    char* const args[];
-};
-typedef struct Command Command;
+#include <unistd.h>
 
 /* prints the contents of a file */
-int dump_file(const char* path);
+int dump_file(const char *title, const char* path);
 
-/* prints the contents of all files in a directory */
-void dump_files(const char* path);
-
-/* forks a command and waits for it to finish */
-int run_command(struct Command* cmd, int timeout);
-
-/* reads the current time into tm */
-void get_time(struct tm *tm);
-
-/* prints the date in tm */
-void print_date(const char* prompt, struct tm *tm);
-
-/* prints the name and value of a system property */
-int print_property(const char* name);
+/* forks a command and waits for it to finish -- terminate args with NULL */
+int run_command(const char *title, int timeout_seconds, const char *command, ...);
 
 /* prints all the system properties */
 void print_properties();
 
-/* creates directories as needed for the given path */
-void create_directories(char *path);
+/* redirect output to a service control socket */
+void redirect_to_socket(FILE *redirect, const char *service);
 
-/* runs the vibrator using the given pattern */
-void vibrate_pattern(int fd, int* pattern);
+/* redirect output to a file, optionally gzipping; returns gzip pid */
+pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level);
 
-/* prevents the OOM killer from killing us */
-void protect_from_oom_killer();
+/* dump Dalvik stack traces, return the trace file location (NULL if none) */
+const char *dump_vm_traces();
 
 #endif /* _DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 60d845f..fda618c 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -14,217 +14,337 @@
  * limitations under the License.
  */
 
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <time.h>
-#include <unistd.h>
+#include <sys/inotify.h>
 #include <sys/stat.h>
-#include <dirent.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <signal.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
 
 #include <cutils/properties.h>
-#include <sys/system_properties.h>
+#include <cutils/sockets.h>
 
 #include "dumpstate.h"
 
 
 /* prints the contents of a file */
-int dump_file(const char* path) {
-    char    buffer[32768];
-    int fd, amount_read;
-    int ret = 0;
+int dump_file(const char *title, const char* path) {
+    char buffer[32768];
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        int err = errno;
+        if (title) printf("----- %s (%s) -----\n", title, path);
+        printf("*** %s: %s\n", path, strerror(err));
+        if (title) printf("\n");
+        return -1;
+    }
 
-    fd = open(path, O_RDONLY);
-    if (fd < 0)
-        return fd;
+    if (title) printf("----- %s (%s", title, path);
 
-    do {
-        ret = read(fd, buffer, sizeof(buffer));
-        if (ret > 0)
-            ret = write(STDOUT_FILENO, buffer, ret);
-    } while (ret > 0);
+    if (title) {
+        struct stat st;
+        if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
+            char stamp[80];
+            time_t mtime = st.st_mtime;
+            strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
+            printf(": %s", stamp);
+        }
+        printf(") -----\n");
+    }
 
-    buffer[0] = '\n';
-    write(STDOUT_FILENO, buffer, 1);
+    int newline = 0;
+    for (;;) {
+        int ret = read(fd, buffer, sizeof(buffer));
+        if (ret > 0) {
+            newline = (buffer[ret - 1] == '\n');
+            ret = fwrite(buffer, ret, 1, stdout);
+        }
+        if (ret <= 0) break;
+    }
 
     close(fd);
-    return ret;
-}
-
-/* prints the contents of all files in a directory */
-void dump_files(const char* path) {
-    DIR* dir;
-    struct dirent* entry;
-    char buffer[PATH_MAX];
-
-    dir = opendir(path);
-    if (!dir) {
-        fprintf(stderr, "could not open directory %s\n", path);
-        return;
-    }
-
-    while ((entry = readdir(dir))) {
-        if (entry->d_type == DT_REG) {
-            snprintf(buffer, sizeof(buffer), "%s/%s", path, entry->d_name);
-            dump_file(path);
-            printf("\n");
-        }
-    }
-
-    closedir(dir);
-}
-
-/* prints the name and value of a system property */
-int print_property(const char* name) {
-    char    value[PROP_VALUE_MAX];
-
-    __system_property_get(name, value);
-    printf("%s=%s\n", name, value);
+    if (!newline) printf("\n");
+    if (title) printf("\n");
     return 0;
 }
 
-static pid_t alarm_pid = 0;
-static int timed_out = 0;
-static void sig_alarm(int sig)
-{
-    if (alarm_pid) {
-        kill(alarm_pid, SIGKILL);
-        timed_out = 1;
-        alarm_pid = 0;
-    }
-}
-
 /* forks a command and waits for it to finish */
-int run_command(struct Command* cmd, int timeout) {
-    struct sigaction sa;
-    pid_t pid;
-    int status;
+int run_command(const char *title, int timeout_seconds, const char *command, ...) {
+    fflush(stdout);
+    clock_t start = clock();
+    pid_t pid = fork();
 
-    pid = fork();
     /* handle error case */
-    if (pid < 0)
+    if (pid < 0) {
+        printf("*** fork: %s\n", strerror(errno));
         return pid;
+    }
 
     /* handle child case */
     if (pid == 0) {
-        int ret = execv(cmd->path, cmd->args);
-        if (ret)
-            fprintf(stderr, "execv %s returned %d\n", cmd->path, ret);
-        exit(ret);
+        const char *args[1024] = {command};
+        size_t arg;
+
+        va_list ap;
+        va_start(ap, command);
+        if (title) printf("----- %s (%s", title, command);
+        for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
+            args[arg] = va_arg(ap, const char *);
+            if (args[arg] == NULL) break;
+            if (title) printf(" %s", args[arg]);
+        }
+        if (title) printf(") -----\n");
+        fflush(stdout);
+
+        execvp(command, (char**) args);
+        printf("*** exec(%s): %s\n", command, strerror(errno));
+        _exit(-1);
     }
 
     /* handle parent case */
-    timed_out = 0;
-    if (timeout) {
-        memset(&sa, 0, sizeof(sa));
-        sa.sa_flags = SA_RESETHAND;
-        sa.sa_handler = sig_alarm;
-        sigaction(SIGALRM, &sa, NULL);
+    for (;;) {
+        int status;
+        pid_t p = waitpid(pid, &status, WNOHANG);
+        float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
+        if (p == pid) {
+            if (WIFSIGNALED(status)) {
+                printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
+            } else if (WEXITSTATUS(status) > 0) {
+                printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
+            }
+            if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed);
+            return status;
+        }
 
-        /* set an alarm so we don't hang forever */
-        alarm_pid = pid;
-        alarm(timeout);
+        if (timeout_seconds && elapsed > timeout_seconds) {
+            printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid);
+            kill(pid, SIGTERM);
+            return -1;
+        }
+
+        usleep(100000);  // poll every 0.1 sec
     }
-
-    waitpid(pid, &status, 0);
-
-    if (timed_out)
-        printf("ERROR: command %s timed out\n", cmd->path);
-
-    return status;
 }
 
-/* reads the current time into tm */
-void get_time(struct tm *tm) {
-    time_t t;
+size_t num_props = 0;
+static char* props[2000];
 
-    tzset();
-    time(&t);
-    localtime_r(&t, tm);
+static void print_prop(const char *key, const char *name, void *user) {
+    (void) user;
+    if (num_props < sizeof(props) / sizeof(props[0])) {
+        char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
+        snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
+        props[num_props++] = strdup(buf);
+    }
 }
 
-/* prints the date in tm */
-void print_date(const char* prompt, struct tm *tm) {
-    char strbuf[260];
-
-    strftime(strbuf, sizeof(strbuf),
-             "%a %b %e %H:%M:%S %Z %Y",
-             tm);
-    printf("%s%s\n", prompt, strbuf);
-}
-
-
-static void print_prop(const char *key, const char *name, 
-                     void *user __attribute__((unused)))
-{
-    printf("[%s]: [%s]\n", key, name);
+static int compare_prop(const void *a, const void *b) {
+    return strcmp(*(char * const *) a, *(char * const *) b);
 }
 
 /* prints all the system properties */
 void print_properties() {
+    size_t i;
+    num_props = 0;
     property_list(print_prop, NULL);
+    qsort(&props, num_props, sizeof(props[0]), compare_prop);
+
+    printf("----- SYSTEM PROPERTIES -----\n");
+    for (i = 0; i < num_props; ++i) {
+        fputs(props[i], stdout);
+        free(props[i]);
+    }
+    printf("\n");
 }
 
-/* creates directories as needed for the given path */
-void create_directories(char *path)
-{
+/* redirect output to a service control socket */
+void redirect_to_socket(FILE *redirect, const char *service) {
+    int s = android_get_control_socket(service);
+    if (s < 0) {
+        fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
+        exit(1);
+    }
+    if (listen(s, 4) < 0) {
+        fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
+        exit(1);
+    }
+
+    struct sockaddr addr;
+    socklen_t alen = sizeof(addr);
+    int fd = accept(s, &addr, &alen);
+    if (fd < 0) {
+        fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
+        exit(1);
+    }
+
+    fflush(redirect);
+    dup2(fd, fileno(redirect));
+    close(fd);
+}
+
+/* redirect output to a file, optionally gzipping; returns gzip pid (or -1) */
+pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
     char *chp = path;
 
     /* skip initial slash */
     if (chp[0] == '/')
         chp++;
 
+    /* create leading directories, if necessary */
     while (chp && chp[0]) {
         chp = strchr(chp, '/');
         if (chp) {
             *chp = 0;
-            mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-            *chp = '/';
-            chp++;
+            mkdir(path, 0775);  /* drwxrwxr-x */
+            *chp++ = '/';
         }
     }
-}
 
-/* runs the vibrator using the given pattern */
-void vibrate_pattern(int fd, int* pattern)
-{
-    struct timespec tm;
-    char    buffer[10];
-
-    while (*pattern) {
-        /* read vibrate on time */
-        int on_time = *pattern++;
-        snprintf(buffer, sizeof(buffer), "%d", on_time);
-        write(fd, buffer, strlen(buffer));
-
-        /* read vibrate off time */
-        int delay = *pattern++;
-        if (delay) {
-            delay += on_time;
-
-            tm.tv_sec = delay / 1000;
-            tm.tv_nsec = (delay % 1000) * 1000000;
-            nanosleep(&tm, NULL);
-        } else
-            break;
+    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (fd < 0) {
+        fprintf(stderr, "%s: %s\n", path, strerror(errno));
+        exit(1);
     }
-}
 
-/* prevents the OOM killer from killing us */
-void protect_from_oom_killer()
-{
-    int fd;
+    pid_t gzip_pid = -1;
+    if (gzip_level > 0) {
+        int fds[2];
+        if (pipe(fds)) {
+            fprintf(stderr, "pipe: %s\n", strerror(errno));
+            exit(1);
+        }
 
-    fd = open("/proc/self/oom_adj", O_WRONLY);
-    if (fd >= 0) {
-        // -17 should make us immune to OOM
-        const char* text = "-17";
-        write(fd, text, strlen(text));
+        fflush(redirect);
+        fflush(stdout);
+
+        gzip_pid = fork();
+        if (gzip_pid < 0) {
+            fprintf(stderr, "fork: %s\n", strerror(errno));
+            exit(1);
+        }
+
+        if (gzip_pid == 0) {
+            dup2(fds[0], STDIN_FILENO);
+            dup2(fd, STDOUT_FILENO);
+
+            close(fd);
+            close(fds[0]);
+            close(fds[1]);
+
+            char level[10];
+            snprintf(level, sizeof(level), "-%d", gzip_level);
+            execlp("gzip", "gzip", level, NULL);
+            fprintf(stderr, "exec(gzip): %s\n", strerror(errno));
+            _exit(-1);
+        }
+
         close(fd);
+        close(fds[0]);
+        fd = fds[1];
     }
+
+    dup2(fd, fileno(redirect));
+    close(fd);
+    return gzip_pid;
+}
+
+/* dump Dalvik stack traces, return the trace file location (NULL if none) */
+const char *dump_vm_traces() {
+    char traces_path[PROPERTY_VALUE_MAX] = "";
+    property_get("dalvik.vm.stack-trace-file", traces_path, "");
+    if (!traces_path[0]) return NULL;
+
+    /* move the old traces.txt (if any) out of the way temporarily */
+    char anr_traces_path[PATH_MAX];
+    strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
+    strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
+    rename(traces_path, anr_traces_path);
+
+    /* create a new, empty traces.txt file to receive stack dumps */
+    int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);  /* -rw-rw-rw- */
+    if (fd < 0) {
+        fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
+        return NULL;
+    }
+    close(fd);
+
+    /* walk /proc and kill -QUIT all Dalvik processes */
+    DIR *proc = opendir("/proc");
+    if (proc == NULL) {
+        fprintf(stderr, "/proc: %s\n", strerror(errno));
+        return NULL;
+    }
+
+    /* use inotify to find when processes are done dumping */
+    int ifd = inotify_init();
+    if (ifd < 0) {
+        fprintf(stderr, "inotify_init: %s\n", strerror(errno));
+        return NULL;
+    }
+
+    int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+    if (wfd < 0) {
+        fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+        return NULL;
+    }
+
+    struct dirent *d;
+    while ((d = readdir(proc))) {
+        int pid = atoi(d->d_name);
+        if (pid <= 0) continue;
+
+        /* identify Dalvik: /proc/(pid)/exe = /system/bin/app_process */
+        char path[PATH_MAX], data[PATH_MAX];
+        snprintf(path, sizeof(path), "/proc/%d/exe", pid);
+        size_t len = readlink(path, data, sizeof(data) - 1);
+        if (len <= 0 || memcmp(data, "/system/bin/app_process", 23)) continue;
+
+        /* skip zygote -- it won't dump its stack anyway */
+        snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+        int fd = open(path, O_RDONLY);
+        len = read(fd, data, sizeof(data) - 1);
+        close(fd);
+        if (len <= 0 || !memcmp(data, "zygote", 6)) continue;
+
+        if (kill(pid, SIGQUIT)) {
+            fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
+            continue;
+        }
+
+        /* wait for the writable-close notification from inotify */
+        struct pollfd pfd = { ifd, POLLIN, 0 };
+        int ret = poll(&pfd, 1, 200);  /* 200 msec timeout */
+        if (ret < 0) {
+            fprintf(stderr, "poll: %s\n", strerror(errno));
+        } else if (ret == 0) {
+            fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
+        } else {
+            struct inotify_event ie;
+            read(ifd, &ie, sizeof(ie));
+        }
+    }
+
+    close(ifd);
+
+    static char dump_traces_path[PATH_MAX];
+    strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
+    strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
+    if (rename(traces_path, dump_traces_path)) {
+        fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+        return NULL;
+    }
+
+    /* replace the saved [ANR] traces.txt file */
+    rename(anr_traces_path, traces_path);
+    return dump_traces_path;
 }
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dcae0c7..79bda74 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -218,14 +218,20 @@
 static int is_valid_apk_path(const char *path)
 {
     int len = strlen(APK_DIR_PREFIX);
+int nosubdircheck = 0;
     if (strncmp(path, APK_DIR_PREFIX, len)) {
         len = strlen(PROTECTED_DIR_PREFIX);
         if (strncmp(path, PROTECTED_DIR_PREFIX, len)) {
-            LOGE("invalid apk path '%s' (bad prefix)\n", path);
-            return 0;
+            len = strlen(SDCARD_DIR_PREFIX);
+            if (strncmp(path, SDCARD_DIR_PREFIX, len)) {
+                LOGE("invalid apk path '%s' (bad prefix)\n", path);
+                return 0;
+            } else {
+                nosubdircheck = 1;
+            }
         }
     }
-    if (strchr(path + len, '/')) {
+    if ((nosubdircheck != 1) && strchr(path + len, '/')) {
         LOGE("invalid apk path '%s' (subdir?)\n", path);
         return 0;
     }
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 1679d14..35a173e 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -68,6 +68,7 @@
 /* other handy constants */
 
 #define PROTECTED_DIR_PREFIX  "/data/app-private/"
+#define SDCARD_DIR_PREFIX  "/asec/"
 
 #define DALVIK_CACHE_PREFIX   "/data/dalvik-cache/"
 #define DALVIK_CACHE_POSTFIX  "/classes.dex"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 79eb310b..4953f5d 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -594,6 +594,8 @@
                 }
             } else if (opt.equals("-t")) {
                 installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+            } else if (opt.equals("-s")) {
+                installFlags |= PackageManager.INSTALL_ON_SDCARD;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 showUsage();
@@ -822,7 +824,7 @@
         System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
         System.err.println("       pm list features");
         System.err.println("       pm path PACKAGE");
-        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] PATH");
+        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
@@ -854,6 +856,7 @@
         System.err.println("  -r: reinstall an exisiting app, keeping its data.");
         System.err.println("  -t: allow test .apks to be installed.");
         System.err.println("  -i: specify the installer package name.");
+        System.err.println("  -s: install package on sdcard.");
         System.err.println("");
         System.err.println("The uninstall command removes a package from the system. Options:");
         System.err.println("  -k: keep the data and cache directories around.");
diff --git a/common/java/com/android/common/DNParser.java b/common/java/com/android/common/DNParser.java
new file mode 100644
index 0000000..32d57c0
--- /dev/null
+++ b/common/java/com/android/common/DNParser.java
@@ -0,0 +1,447 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.common;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A simple distinguished name(DN) parser.
+ *
+ * <p>This class is based on org.apache.harmony.security.x509.DNParser.  It's customized to remove
+ * external references which are unnecessary for our requirements.
+ *
+ * <p>This class is only meant for extracting a string value from a DN.  e.g. it doesn't support
+ * values in the hex-string style.
+ *
+ * <p>This class is used by {@link DomainNameValidator} only.  However, in order to make this
+ * class visible from unit tests, it's made public.
+ */
+public final class DNParser {
+    private static final String TAG = "DNParser";
+
+    /** DN to be parsed. */
+    private final String dn;
+
+    // length of distinguished name string
+    private final int length;
+
+    private int pos, beg, end;
+
+    // tmp vars to store positions of the currently parsed item
+    private int cur;
+
+    // distinguished name chars
+    private char[] chars;
+
+    /**
+     * Exception message thrown when we failed to parse DN, which shouldn't happen because we
+     * only handle DNs that {@link X500Principal#getName} returns, which shouldn't be malformed.
+     */
+    private static final String ERROR_PARSE_ERROR = "Failed to parse DN";
+
+    /**
+     * Constructor.
+     *
+     * @param principal - {@link X500Principal} to be parsed
+     */
+    public DNParser(X500Principal principal) {
+        this.dn = principal.getName(X500Principal.RFC2253);
+        this.length = dn.length();
+    }
+
+    // gets next attribute type: (ALPHA 1*keychar) / oid
+    private String nextAT() throws IOException {
+
+        // skip preceding space chars, they can present after
+        // comma or semicolon (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+        if (pos == length) {
+            return null; // reached the end of DN
+        }
+
+        // mark the beginning of attribute type
+        beg = pos;
+
+        // attribute type chars
+        pos++;
+        for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) {
+            // we don't follow exact BNF syntax here:
+            // accept any char except space and '='
+        }
+        if (pos >= length) {
+            // unexpected end of DN
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        // mark the end of attribute type
+        end = pos;
+
+        // skip trailing space chars between attribute type and '='
+        // (compatibility with RFC 1779)
+        if (chars[pos] == ' ') {
+            for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {
+            }
+
+            if (chars[pos] != '=' || pos == length) {
+                // unexpected end of DN
+                throw new IOException(ERROR_PARSE_ERROR);
+            }
+        }
+
+        pos++; //skip '=' char
+
+        // skip space chars between '=' and attribute value
+        // (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+
+        // in case of oid attribute type skip its prefix: "oid." or "OID."
+        // (compatibility with RFC 1779)
+        if ((end - beg > 4) && (chars[beg + 3] == '.')
+                && (chars[beg] == 'O' || chars[beg] == 'o')
+                && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
+                && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
+            beg += 4;
+        }
+
+        return new String(chars, beg, end - beg);
+    }
+
+    // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION
+    private String quotedAV() throws IOException {
+
+        pos++;
+        beg = pos;
+        end = beg;
+        while (true) {
+
+            if (pos == length) {
+                // unexpected end of DN
+                throw new IOException(ERROR_PARSE_ERROR);
+            }
+
+            if (chars[pos] == '"') {
+                // enclosing quotation was found
+                pos++;
+                break;
+            } else if (chars[pos] == '\\') {
+                chars[end] = getEscaped();
+            } else {
+                // shift char: required for string with escaped chars
+                chars[end] = chars[pos];
+            }
+            pos++;
+            end++;
+        }
+
+        // skip trailing space chars before comma or semicolon.
+        // (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+
+        return new String(chars, beg, end - beg);
+    }
+
+    // gets hex string attribute value: "#" hexstring
+    private String hexAV() throws IOException {
+
+        if (pos + 4 >= length) {
+            // encoded byte array  must be not less then 4 c
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        beg = pos; // store '#' position
+        pos++;
+        while (true) {
+
+            // check for end of attribute value
+            // looks for space and component separators
+            if (pos == length || chars[pos] == '+' || chars[pos] == ','
+                    || chars[pos] == ';') {
+                end = pos;
+                break;
+            }
+
+            if (chars[pos] == ' ') {
+                end = pos;
+                pos++;
+                // skip trailing space chars before comma or semicolon.
+                // (compatibility with RFC 1779)
+                for (; pos < length && chars[pos] == ' '; pos++) {
+                }
+                break;
+            } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
+                chars[pos] += 32; //to low case
+            }
+
+            pos++;
+        }
+
+        // verify length of hex string
+        // encoded byte array  must be not less then 4 and must be even number
+        int hexLen = end - beg; // skip first '#' char
+        if (hexLen < 5 || (hexLen & 1) == 0) {
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        // get byte encoding from string representation
+        byte[] encoded = new byte[hexLen / 2];
+        for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
+            encoded[i] = (byte) getByte(p);
+        }
+
+        return new String(chars, beg, hexLen);
+    }
+
+    // gets string attribute value: *( stringchar / pair )
+    private String escapedAV() throws IOException {
+
+        beg = pos;
+        end = pos;
+        while (true) {
+
+            if (pos >= length) {
+                // the end of DN has been found
+                return new String(chars, beg, end - beg);
+            }
+
+            switch (chars[pos]) {
+            case '+':
+            case ',':
+            case ';':
+                // separator char has beed found
+                return new String(chars, beg, end - beg);
+            case '\\':
+                // escaped char
+                chars[end++] = getEscaped();
+                pos++;
+                break;
+            case ' ':
+                // need to figure out whether space defines
+                // the end of attribute value or not
+                cur = end;
+
+                pos++;
+                chars[end++] = ' ';
+
+                for (; pos < length && chars[pos] == ' '; pos++) {
+                    chars[end++] = ' ';
+                }
+                if (pos == length || chars[pos] == ',' || chars[pos] == '+'
+                        || chars[pos] == ';') {
+                    // separator char or the end of DN has beed found
+                    return new String(chars, beg, cur - beg);
+                }
+                break;
+            default:
+                chars[end++] = chars[pos];
+                pos++;
+            }
+        }
+    }
+
+    // returns escaped char
+    private char getEscaped() throws IOException {
+
+        pos++;
+        if (pos == length) {
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        switch (chars[pos]) {
+        case '"':
+        case '\\':
+        case ',':
+        case '=':
+        case '+':
+        case '<':
+        case '>':
+        case '#':
+        case ';':
+        case ' ':
+        case '*':
+        case '%':
+        case '_':
+            //FIXME: escaping is allowed only for leading or trailing space char
+            return chars[pos];
+        default:
+            // RFC doesn't explicitly say that escaped hex pair is
+            // interpreted as UTF-8 char. It only contains an example of such DN.
+            return getUTF8();
+        }
+    }
+
+    // decodes UTF-8 char
+    // see http://www.unicode.org for UTF-8 bit distribution table
+    private char getUTF8() throws IOException {
+
+        int res = getByte(pos);
+        pos++; //FIXME tmp
+
+        if (res < 128) { // one byte: 0-7F
+            return (char) res;
+        } else if (res >= 192 && res <= 247) {
+
+            int count;
+            if (res <= 223) { // two bytes: C0-DF
+                count = 1;
+                res = res & 0x1F;
+            } else if (res <= 239) { // three bytes: E0-EF
+                count = 2;
+                res = res & 0x0F;
+            } else { // four bytes: F0-F7
+                count = 3;
+                res = res & 0x07;
+            }
+
+            int b;
+            for (int i = 0; i < count; i++) {
+                pos++;
+                if (pos == length || chars[pos] != '\\') {
+                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+                }
+                pos++;
+
+                b = getByte(pos);
+                pos++; //FIXME tmp
+                if ((b & 0xC0) != 0x80) {
+                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+                }
+
+                res = (res << 6) + (b & 0x3F);
+            }
+            return (char) res;
+        } else {
+            return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+        }
+    }
+
+    // Returns byte representation of a char pair
+    // The char pair is composed of DN char in
+    // specified 'position' and the next char
+    // According to BNF syntax:
+    // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
+    //                    / "a" / "b" / "c" / "d" / "e" / "f"
+    private int getByte(int position) throws IOException {
+
+        if ((position + 1) >= length) {
+            // to avoid ArrayIndexOutOfBoundsException
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        int b1, b2;
+
+        b1 = chars[position];
+        if (b1 >= '0' && b1 <= '9') {
+            b1 = b1 - '0';
+        } else if (b1 >= 'a' && b1 <= 'f') {
+            b1 = b1 - 87; // 87 = 'a' - 10
+        } else if (b1 >= 'A' && b1 <= 'F') {
+            b1 = b1 - 55; // 55 = 'A' - 10
+        } else {
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        b2 = chars[position + 1];
+        if (b2 >= '0' && b2 <= '9') {
+            b2 = b2 - '0';
+        } else if (b2 >= 'a' && b2 <= 'f') {
+            b2 = b2 - 87; // 87 = 'a' - 10
+        } else if (b2 >= 'A' && b2 <= 'F') {
+            b2 = b2 - 55; // 55 = 'A' - 10
+        } else {
+            throw new IOException(ERROR_PARSE_ERROR);
+        }
+
+        return (b1 << 4) + b2;
+    }
+
+    /**
+     * Parses the DN and returns the attribute value for an attribute type.
+     *
+     * @param attributeType attribute type to look for (e.g. "ca")
+     * @return value of the attribute that first found, or null if none found
+     */
+    public String find(String attributeType) {
+        try {
+            // Initialize internal state.
+            pos = 0;
+            beg = 0;
+            end = 0;
+            cur = 0;
+            chars = dn.toCharArray();
+
+            String attType = nextAT();
+            if (attType == null) {
+                return null;
+            }
+            while (true) {
+                String attValue = "";
+
+                if (pos == length) {
+                    return null;
+                }
+
+                switch (chars[pos]) {
+                case '"':
+                    attValue = quotedAV();
+                    break;
+                case '#':
+                    attValue = hexAV();
+                    break;
+                case '+':
+                case ',':
+                case ';': // compatibility with RFC 1779: semicolon can separate RDNs
+                    //empty attribute value
+                    break;
+                default:
+                    attValue = escapedAV();
+                }
+
+                if (attributeType.equalsIgnoreCase(attType)) {
+                    return attValue;
+                }
+
+                if (pos >= length) {
+                    return null;
+                }
+
+                if (chars[pos] == ',' || chars[pos] == ';') {
+                } else if (chars[pos] != '+') {
+                    throw new IOException(ERROR_PARSE_ERROR);
+                }
+
+                pos++;
+                attType = nextAT();
+                if (attType == null) {
+                    throw new IOException(ERROR_PARSE_ERROR);
+                }
+            }
+        } catch (IOException e) {
+            // Parse error shouldn't happen, because we only handle DNs that
+            // X500Principal.getName() returns, which shouldn't be malformed.
+            Log.e(TAG, "Failed to parse DN: " + dn);
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/net/http/DomainNameChecker.java b/common/java/com/android/common/DomainNameValidator.java
similarity index 85%
rename from core/java/android/net/http/DomainNameChecker.java
rename to common/java/com/android/common/DomainNameValidator.java
index 3e01d2c..ad44a7d 100644
--- a/core/java/android/net/http/DomainNameChecker.java
+++ b/common/java/com/android/common/DomainNameValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -13,29 +13,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.common;
 
-package android.net.http;
-
-import org.bouncycastle.asn1.x509.X509Name;
+import android.util.Config;
+import android.util.Log;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.security.cert.X509Certificate;
 import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
-import java.util.Vector;
 
-/**
- * Implements basic domain-name validation as specified by RFC2818.
- * 
- * {@hide}
- */
-public class DomainNameChecker {
+import javax.security.auth.x500.X500Principal;
+
+public class DomainNameValidator {
+    private final static String TAG = "DomainNameValidator";
+
+    private static final boolean DEBUG = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
     private static Pattern QUICK_IP_PATTERN;
     static {
         try {
@@ -84,8 +84,8 @@
                   errorMessage = "unknown host exception";
                 }
 
-                if (HttpLog.LOGV) {
-                    HttpLog.v("DomainNameChecker.isIpAddress(): " + errorMessage);
+                if (LOG_ENABLED) {
+                    Log.v(TAG, "DomainNameValidator.isIpAddress(): " + errorMessage);
                 }
 
                 rval = false;
@@ -102,8 +102,8 @@
      * @return True iff if there is a domain match as specified by RFC2818
      */
     private static boolean matchIpAddress(X509Certificate certificate, String thisDomain) {
-        if (HttpLog.LOGV) {
-            HttpLog.v("DomainNameChecker.matchIpAddress(): this domain: " + thisDomain);
+        if (LOG_ENABLED) {
+            Log.v(TAG, "DomainNameValidator.matchIpAddress(): this domain: " + thisDomain);
         }
 
         try {
@@ -118,8 +118,8 @@
                             if (altNameType.intValue() == ALT_IPA_NAME) {
                                 String altName = (String)(altNameEntry.get(1));
                                 if (altName != null) {
-                                    if (HttpLog.LOGV) {
-                                        HttpLog.v("alternative IP: " + altName);
+                                    if (LOG_ENABLED) {
+                                        Log.v(TAG, "alternative IP: " + altName);
                                     }
                                     if (thisDomain.equalsIgnoreCase(altName)) {
                                         return true;
@@ -171,26 +171,26 @@
             // spec (a valid DNS name must start with a letter); there is no
             // good way around this, and in order to be compatible we proceed
             // to check the common name (ie, ignore alternative names)
-            if (HttpLog.LOGV) {
+            if (LOG_ENABLED) {
                 String errorMessage = e.getMessage();
                 if (errorMessage == null) {
                     errorMessage = "failed to parse certificate";
                 }
 
-                if (HttpLog.LOGV) {
-                    HttpLog.v("DomainNameChecker.matchDns(): " + errorMessage);
-                }
+                Log.v(TAG, "DomainNameValidator.matchDns(): " + errorMessage);
             }
         }
 
         if (!hasDns) {
-            X509Name xName = new X509Name(certificate.getSubjectDN().getName());
-            Vector val = xName.getValues();
-            Vector oid = xName.getOIDs();
-            for (int i = 0; i < oid.size(); i++) {
-                if (oid.elementAt(i).equals(X509Name.CN)) {
-                    return matchDns(thisDomain, (String)(val.elementAt(i)));
-                }
+            final String cn = new DNParser(certificate.getSubjectX500Principal())
+                    .find("cn");
+            if (LOG_ENABLED) {
+                Log.v(TAG, "Validating subject: DN:"
+                        + certificate.getSubjectX500Principal().getName(X500Principal.CANONICAL)
+                        + "  CN:" + cn);
+            }
+            if (cn != null) {
+                return matchDns(thisDomain, cn);
             }
         }
 
@@ -202,9 +202,10 @@
      * @param thatDomain The domain name from the certificate
      * @return True iff thisDomain matches thatDomain as specified by RFC2818
      */
-    private static boolean matchDns(String thisDomain, String thatDomain) {
-        if (HttpLog.LOGV) {
-            HttpLog.v("DomainNameChecker.matchDns():" +
+    // not private for testing
+    public static boolean matchDns(String thisDomain, String thatDomain) {
+        if (LOG_ENABLED) {
+            Log.v(TAG, "DomainNameValidator.matchDns():" +
                       " this domain: " + thisDomain +
                       " that domain: " + thatDomain);
         }
diff --git a/common/java/com/android/common/Patterns.java b/common/java/com/android/common/Patterns.java
index 2eab3e1..24a18c0 100644
--- a/common/java/com/android/common/Patterns.java
+++ b/common/java/com/android/common/Patterns.java
@@ -122,7 +122,7 @@
 
     public static final Pattern EMAIL_ADDRESS
         = Pattern.compile(
-            "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" +
+            "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" +
             "\\@" +
             "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
             "(" +
diff --git a/common/tests/src/com/android/common/DNParserTest.java b/common/tests/src/com/android/common/DNParserTest.java
new file mode 100644
index 0000000..34b140a
--- /dev/null
+++ b/common/tests/src/com/android/common/DNParserTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.common;
+
+import javax.security.auth.x500.X500Principal;
+
+import junit.framework.TestCase;
+
+public class DNParserTest extends TestCase {
+    public void testFind() {
+        checkFind("", "cn", null);
+        checkFind("ou=xxx", "cn", null);
+        checkFind("ou=xxx,cn=xxx", "cn", "xxx");
+        checkFind("ou=xxx+cn=yyy,cn=zzz+cn=abc", "cn", "yyy");
+        checkFind("2.5.4.3=a,ou=xxx", "cn", "a"); // OID
+        checkFind("cn=a,cn=b", "cn", "a");
+        checkFind("ou=Cc,ou=Bb,ou=Aa", "ou", "Cc");
+        checkFind("cn=imap.gmail.com", "cn", "imap.gmail.com");
+
+        // Quoted string (see http://www.ietf.org/rfc/rfc2253.txt)
+        checkFind("o=\"\\\" a ,=<>#;\"", "o", "\" a ,=<>#;");
+        checkFind("o=abc\\,def", "o", "abc,def");
+
+        // UTF-8 (example in rfc 2253)
+        checkFind("cn=Lu\\C4\\8Di\\C4\\87", "cn", "\u004c\u0075\u010d\u0069\u0107");
+
+        // whitespaces
+        checkFind("ou=a, o=  a  b  ,cn=x", "o", "a  b");
+        checkFind("o=\"  a  b  \" ,cn=x", "o", "  a  b  ");
+    }
+
+    private void checkFind(String dn, String attrType, String expected) {
+        String actual = new DNParser(new X500Principal(dn)).find(attrType);
+        assertEquals("dn:" + dn + "  attr:" + attrType, expected, actual);
+    }
+}
diff --git a/common/tests/src/com/android/common/DomainNameValidatorTest.java b/common/tests/src/com/android/common/DomainNameValidatorTest.java
new file mode 100644
index 0000000..4fdd4cdc
--- /dev/null
+++ b/common/tests/src/com/android/common/DomainNameValidatorTest.java
@@ -0,0 +1,336 @@
+/*
+ * 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.common;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import junit.framework.TestCase;
+
+public class DomainNameValidatorTest extends TestCase {
+    private static final int ALT_UNKNOWN = 0;
+    private static final int ALT_DNS_NAME = 2;
+    private static final int ALT_IPA_NAME = 7;
+
+    /**
+     * Tests {@link DomainNameValidator#match}
+     */
+    public void testMatch() {
+        // TODO Use actual X509Certificate objects, instead of StubX509Certificate.
+        // Comment in DomainNameValidator suggests X509Certificate fails to parse a certificate
+        // if subject alternative names contain a domain name that begins with '*'.
+        // This test won't cover this kind of errors.
+
+        checkMatch("11", new StubX509Certificate("cn=imap.g.com"), "imap.g.com", true);
+        checkMatch("12", new StubX509Certificate("cn=imap2.g.com"), "imap.g.com", false);
+        checkMatch("13", new StubX509Certificate("cn=sub.imap.g.com"), "imap.g.com", false);
+
+        // If a subjectAltName extension of type dNSName is present, that MUST
+        // be used as the identity
+        checkMatch("21", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")
+                , "imap.g.com", false);
+        checkMatch("22", new StubX509Certificate("cn=imap.g.com") // This cn should be ignored
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")
+                , "imap.g.com", false);
+        checkMatch("23", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                , "imap.g.com", true);
+
+        // With wildcards
+        checkMatch("24", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")
+                , "imap.g.com", true);
+
+
+        // host name is ip address
+        checkMatch("31", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
+                , "1.2.3.4", true);
+        checkMatch("32", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
+                , "1.2.3.5", false);
+        checkMatch("32", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")
+                , "192.168.100.1", true);
+
+        // Has unknown subject alternative names
+        checkMatch("41", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN,  "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
+                , "imap.g.com", true);
+
+        checkMatch("42", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
+                , "2.33.44.55", true);
+
+        checkMatch("43", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
+                , "g.com", false);
+
+        checkMatch("44", new StubX509Certificate("")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
+                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
+                , "2.33.44.1", false);
+    }
+
+    private void checkMatch(String message, X509Certificate certificate, String thisDomain,
+            boolean expected) {
+        Boolean actual = DomainNameValidator.match(certificate, thisDomain);
+        assertEquals(message, (Object) expected, (Object) actual);
+    }
+
+    /**
+     * Tests {@link DomainNameValidator#matchDns}
+     */
+    public void testMatchDns() {
+        checkMatchDns("11", "a.b.c.d", "a.b.c.d", true);
+        checkMatchDns("12", "a.b.c.d", "*.b.c.d", true);
+        checkMatchDns("13", "b.c.d", "*.b.c.d", true);
+        checkMatchDns("14", "b.c.d", "b*.c.d", true);
+
+        checkMatchDns("15", "a.b.c.d", "*.*.c.d", false);
+        checkMatchDns("16", "a.b.c.d", "*.c.d", false);
+
+        checkMatchDns("21", "imap.google.com", "imap.google.com", true);
+        checkMatchDns("22", "imap2.google.com", "imap.google.com", false);
+        checkMatchDns("23", "imap.google.com", "*.google.com", true);
+        checkMatchDns("24", "imap2.google.com", "*.google.com", true);
+        checkMatchDns("25", "imap.google.com", "*.googl.com", false);
+        checkMatchDns("26", "imap2.google2.com", "*.google3.com", false);
+        checkMatchDns("27", "imap.google.com", "ima*.google.com", true);
+        checkMatchDns("28", "imap.google.com", "imap*.google.com", true);
+        checkMatchDns("29", "imap.google.com", "*.imap.google.com", true);
+
+        checkMatchDns("41", "imap.google.com", "a*.google.com", false);
+        checkMatchDns("42", "imap.google.com", "ix*.google.com", false);
+
+        checkMatchDns("51", "imap.google.com", "iMap.Google.Com", true);
+    }
+
+    private void checkMatchDns(String message, String thisDomain, String thatDomain,
+            boolean expected) {
+        boolean actual = DomainNameValidator.matchDns(thisDomain, thatDomain);
+        assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Minimal {@link X509Certificate} implementation for {@link DomainNameValidator}.
+     */
+    private static class StubX509Certificate extends X509Certificate {
+        private final X500Principal subjectX500Principal;
+        private Collection<List<?>> subjectAlternativeNames;
+
+        public StubX509Certificate(String subjectDn) {
+            subjectX500Principal = new X500Principal(subjectDn);
+            subjectAlternativeNames = null;
+        }
+
+        public StubX509Certificate addSubjectAlternativeName(int type, String name) {
+            if (subjectAlternativeNames == null) {
+                subjectAlternativeNames = new ArrayList<List<?>>();
+            }
+            LinkedList<Object> entry = new LinkedList<Object>();
+            entry.add(type);
+            entry.add(name);
+            subjectAlternativeNames.add(entry);
+            return this;
+        }
+
+        @Override
+        public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
+            return subjectAlternativeNames;
+        }
+
+        @Override
+        public X500Principal getSubjectX500Principal() {
+            return subjectX500Principal;
+        }
+
+        @Override
+        public void checkValidity() throws CertificateExpiredException,
+                CertificateNotYetValidException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public void checkValidity(Date date) throws CertificateExpiredException,
+                CertificateNotYetValidException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public int getBasicConstraints() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public Principal getIssuerDN() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public boolean[] getIssuerUniqueID() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public boolean[] getKeyUsage() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public Date getNotAfter() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public Date getNotBefore() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public BigInteger getSerialNumber() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public String getSigAlgName() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public String getSigAlgOID() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public byte[] getSigAlgParams() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public byte[] getSignature() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public Principal getSubjectDN() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public boolean[] getSubjectUniqueID() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public byte[] getTBSCertificate() throws CertificateEncodingException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public int getVersion() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public PublicKey getPublicKey() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public String toString() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+                InvalidKeyException, NoSuchProviderException, SignatureException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        @Override
+        public void verify(PublicKey key, String sigProvider) throws CertificateException,
+                NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+                SignatureException {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        public Set<String> getCriticalExtensionOIDs() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        public byte[] getExtensionValue(String oid) {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        public Set<String> getNonCriticalExtensionOIDs() {
+            throw new RuntimeException("Method not implemented");
+        }
+
+        public boolean hasUnsupportedCriticalExtension() {
+            throw new RuntimeException("Method not implemented");
+        }
+    }
+}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 9765496..3bbfce8 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -230,6 +230,47 @@
     }
 
     /**
+     * Tests that the given account has the specified features. If this account does not exist
+     * then this call returns false.
+     * <p>
+     * This call returns immediately but runs asynchronously and the result is accessed via the
+     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
+     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
+     * method asynchronously then they will generally pass in a callback object that will get
+     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
+     * they will generally pass null for the callback and instead call
+     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
+     * which will then block until the request completes.
+     * <p>
+     * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
+     *
+     * @param account The {@link Account} to test
+     * @param features the features for which to test
+     * @param callback A callback to invoke when the request completes. If null then
+     * no callback is invoked.
+     * @param handler The {@link Handler} to use to invoke the callback. If null then the
+     * main thread's {@link Handler} is used.
+     * @return an {@link AccountManagerFuture} that represents the future result of the call.
+     * The future result is a {@link Boolean} that is true if the account exists and has the
+     * specified features.
+     */
+    public AccountManagerFuture<Boolean> testHasFeatures(final Account account,
+            final String[] features,
+            AccountManagerCallback<Boolean> callback, Handler handler) {
+        return new Future2Task<Boolean>(handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.testHasFeatures(mResponse, account, features);
+            }
+            public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+                if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
+                    throw new AuthenticatorException("no result in response");
+                }
+                return bundle.getBoolean(KEY_BOOLEAN_RESULT);
+            }
+        }.start();
+    }
+
+    /**
      * Add an account to the AccountManager's set of known accounts. 
      * <p>
      * Requires that the caller has permission
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index d5a9b02..f5166c2 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -449,6 +449,64 @@
         return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
     }
 
+    public void testHasFeatures(IAccountManagerResponse response,
+            Account account, String[] features) {
+        checkReadAccountsPermission();
+        long identityToken = clearCallingIdentity();
+        try {
+            new TestFeaturesSession(response, account, features).bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private class TestFeaturesSession extends Session {
+        private final String[] mFeatures;
+        private final Account mAccount;
+
+        public TestFeaturesSession(IAccountManagerResponse response,
+                Account account, String[] features) {
+            super(response, account.type, false /* expectActivityLaunch */);
+            mFeatures = features;
+            mAccount = account;
+        }
+
+        public void run() throws RemoteException {
+            try {
+                mAuthenticator.hasFeatures(this, mAccount, mFeatures);
+            } catch (RemoteException e) {
+                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
+            }
+        }
+
+        public void onResult(Bundle result) {
+            IAccountManagerResponse response = getResponseAndClose();
+            if (response != null) {
+                try {
+                    if (result == null) {
+                        onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
+                        return;
+                    }
+                    final Bundle newResult = new Bundle();
+                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
+                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
+                    response.onResult(newResult);
+                } catch (RemoteException e) {
+                    // if the caller is dead then there is no one to care about remote exceptions
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "failure while notifying response", e);
+                    }
+                }
+            }
+        }
+
+        protected String toDebugString(long now) {
+            return super.toDebugString(now) + ", testHasFeatures"
+                    + ", " + mAccount
+                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
+        }
+    }
+    
     public void removeAccount(IAccountManagerResponse response, Account account) {
         checkManageAccountsPermission();
         long identityToken = clearCallingIdentity();
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 0e318c0..cbd26ee 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -31,6 +31,8 @@
     String getUserData(in Account account, String key);
     AuthenticatorDescription[] getAuthenticatorTypes();
     Account[] getAccounts(String accountType);
+    void testHasFeatures(in IAccountManagerResponse response, in Account account,
+        in String[] features);
     void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
     boolean addAccount(in Account account, String password, in Bundle extras);
     void removeAccount(in IAccountManagerResponse response, in Account account);
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 5961ef5..981145b 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1332,20 +1332,11 @@
     public final static String EXTRA_DATA_KEY = "intent_extra_data_key";
 
     /**
-     * String extra data key for {@link Intent#ACTION_GLOBAL_SEARCH} intents. Contains the initial
-     * query to show in the global search activity.
-     *
-     * @hide Pending API council approval
+     * Boolean extra data key for {@link #INTENT_ACTION_GLOBAL_SEARCH} intents. If {@code true},
+     * the initial query should be selected when the global search activity is started, so
+     * that the user can easily replace it with another query.
      */
-    public final static String INITIAL_QUERY = "initial_query";
-
-    /**
-     * Boolean extra data key for {@link Intent#ACTION_GLOBAL_SEARCH} intents. If {@code true},
-     * the initial query should be selected.
-     *
-     * @hide Pending API council approval
-     */
-    public final static String SELECT_INITIAL_QUERY = "select_initial_query";
+    public final static String EXTRA_SELECT_QUERY = "select_query";
 
     /**
      * Defines the constants used in the communication between {@link android.app.SearchDialog} and
@@ -1643,10 +1634,12 @@
             = "android.search.action.CHANGE_SEARCH_SOURCE";
 
     /**
-     * Intent action for finding the global search activity.
+     * Intent action for starting the global search activity.
      * The global search provider should handle this intent.
-     * 
-     * @hide Pending API council approval.
+     *
+     * Supported extra data keys: {@link #QUERY},
+     * {@link #EXTRA_SELECT_QUERY},
+     * {@link #APP_DATA}.
      */
     public final static String INTENT_ACTION_GLOBAL_SEARCH 
             = "android.search.action.GLOBAL_SEARCH";
@@ -1816,17 +1809,17 @@
             Log.w(TAG, "No global search activity found.");
             return;
         }
-        Intent intent = new Intent(Intent.ACTION_GLOBAL_SEARCH);
+        Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
         intent.setComponent(globalSearchActivity);
         // TODO: Always pass name of calling package as an extra?
         if (appSearchData != null) {
             intent.putExtra(APP_DATA, appSearchData);
         }
         if (!TextUtils.isEmpty(initialQuery)) {
-            intent.putExtra(INITIAL_QUERY, initialQuery);
+            intent.putExtra(QUERY, initialQuery);
         }
         if (selectInitialQuery) {
-            intent.putExtra(SELECT_INITIAL_QUERY, selectInitialQuery);
+            intent.putExtra(EXTRA_SELECT_QUERY, selectInitialQuery);
         }
         try {
             if (DBG) Log.d(TAG, "Starting global search: " + intent.toUri(0));
@@ -1847,7 +1840,7 @@
      * we have settled on the right mechanism for finding the global search activity.
      */
     private ComponentName getGlobalSearchActivity() {
-        Intent intent = new Intent(Intent.ACTION_GLOBAL_SEARCH);
+        Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
                 pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
@@ -2030,8 +2023,23 @@
     }
 
     /**
-     * Gets information about a searchable activity. This method is static so that it can
-     * be used from non-Activity contexts.
+     * Gets information about a searchable activity.
+     *
+     * @param componentName The activity to get searchable information for.
+     * @return Searchable information, or <code>null</code> if the activity does not
+     *         exist, or is not searchable.
+     */
+    public SearchableInfo getSearchableInfo(ComponentName componentName) {
+        try {
+            return mService.getSearchableInfo(componentName, false);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getSearchableInfo() failed: " + ex);
+            return null;
+        }
+    }
+
+    /**
+     * Gets information about a searchable activity.
      *
      * @param componentName The activity to get searchable information for.
      * @param globalSearch If <code>false</code>, return information about the given activity.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bf37b62..bbd359b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1102,16 +1102,6 @@
     public static final String ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS";
 
     /**
-     * Activity Action: Start the global search activity.
-     * <p>Input: Nothing.
-     * <p>Output: Nothing.
-     *
-     * @hide Pending API council approval
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_GLOBAL_SEARCH = "android.intent.action.GLOBAL_SEARCH";
-
-    /**
      * Activity Action: The user pressed the "Report" button in the crash/ANR dialog.
      * This intent is delivered to the package which installed the application, usually
      * the Market.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 3dea286..3e8a55f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -218,6 +218,22 @@
     public static final int FLAG_NEVER_ENCRYPT = 1<<17;
 
     /**
+     * Value for {@link #flags}: Set to true if the application has been
+     * installed using the forward lock option.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_FORWARD_LOCK = 1<<18;
+
+    /**
+     * Value for {@link #flags}: Set to true if the application is
+     * currently installed on the sdcard.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_ON_SDCARD = 1<<19;
+
+    /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
@@ -226,7 +242,7 @@
      * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
      * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
-     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}.
+     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}
      */
     public int flags = 0;
     
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fc6538f..54db5e0 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -296,4 +296,13 @@
      * in the special development "no pre-dexopt" mode.
      */
     boolean performDexOpt(String packageName);
+
+    /**
+     * Update status of external media on the package manager to scan and
+     * install packages installed on the external media. Like say the
+     * MountService uses this to call into the package manager to update
+     * status of sdcard.
+     */
+    void updateExternalMediaStatus(boolean mounted);
+
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 53a966d..bc59c94 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -252,6 +252,13 @@
     public static final int INSTALL_ALLOW_TEST = 0x00000004;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that this
+     * package has to be installed on the sdcard.
+     * @hide
+     */
+    public static final int INSTALL_ON_SDCARD = 0x00000008;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
@@ -411,6 +418,15 @@
      */
     public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
 
+    // ------ Errors related to sdcard
+    /**
+     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+     * a secure container mount point couldn't be accessed on external media.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
+
     /**
      * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
      * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ad99f54..8a5df32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -327,16 +327,18 @@
         return null;
     }
 
-    public final static int PARSE_IS_SYSTEM = 0x0001;
-    public final static int PARSE_CHATTY = 0x0002;
-    public final static int PARSE_MUST_BE_APK = 0x0004;
-    public final static int PARSE_IGNORE_PROCESSES = 0x0008;
+    public final static int PARSE_IS_SYSTEM = 1<<0;
+    public final static int PARSE_CHATTY = 1<<1;
+    public final static int PARSE_MUST_BE_APK = 1<<2;
+    public final static int PARSE_IGNORE_PROCESSES = 1<<3;
+    public final static int PARSE_FORWARD_LOCK = 1<<4;
+    public final static int PARSE_ON_SDCARD = 1<<5;
 
     public int getParseError() {
         return mParseError;
     }
 
-    public Package parsePackage(File sourceFile, String destFileName,
+    public Package parsePackage(File sourceFile, String destCodePath,
             DisplayMetrics metrics, int flags) {
         mParseError = PackageManager.INSTALL_SUCCEEDED;
 
@@ -413,8 +415,11 @@
         parser.close();
         assmgr.close();
 
-        pkg.applicationInfo.sourceDir = destFileName;
-        pkg.applicationInfo.publicSourceDir = destFileName;
+        // Set code and resource paths
+        pkg.mPath = destCodePath;
+        pkg.mScanPath = mArchiveSourcePath;
+        //pkg.applicationInfo.sourceDir = destCodePath;
+        //pkg.applicationInfo.publicSourceDir = destRes;
         pkg.mSignatures = null;
 
         return pkg;
@@ -1369,6 +1374,14 @@
             }
         }
 
+        if ((flags & PARSE_FORWARD_LOCK) != 0) {
+            ai.flags |= ApplicationInfo.FLAG_FORWARD_LOCK;
+        }
+
+        if ((flags & PARSE_ON_SDCARD) != 0) {
+            ai.flags |= ApplicationInfo.FLAG_ON_SDCARD;
+        }
+
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
                 false)) {
@@ -2530,10 +2543,6 @@
         // preferred up order.
         public int mPreferredOrder = 0;
 
-        // For use by package manager service to keep track of which apps
-        // have been installed with forward locking.
-        public boolean mForwardLocked;
-        
         // For use by the package manager to keep track of the path to the
         // file an app came from.
         public String mScanPath;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index f310586..b59030d 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -243,9 +243,12 @@
      * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
      * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
      */
-    private Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
-    private int mMaxSqlCacheSize = 0; // no caching by default
-    private static final int MAX_SQL_CACHE_SIZE = 1000;
+    /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
+    /**
+     * @hide
+     */
+    public static final int MAX_SQL_CACHE_SIZE = 250;
+    private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
 
     /** maintain stats about number of cache hits and misses */
     private int mNumCacheHits;
@@ -828,6 +831,15 @@
     }
 
     private void closeClosable() {
+        /* deallocate all compiled sql statement objects from mCompiledQueries cache.
+         * this should be done before de-referencing all {@link SQLiteClosable} objects
+         * from this database object because calling
+         * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
+         * to be closed. sqlite doesn't let a database close if there are
+         * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
+         */
+        deallocCachedSqlStatements();
+
         Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
         while (iter.hasNext()) {
             Map.Entry<SQLiteClosable, Object> entry = iter.next();
@@ -836,13 +848,6 @@
                 program.onAllReferencesReleasedFromContainer();
             }
         }
-
-        // finalize all compiled sql statement objects in compiledQueries cache
-        synchronized (mCompiledQueries) {
-            for (SQLiteCompiledSql compiledStatement : mCompiledQueries.values()) {
-                compiledStatement.releaseSqlStatement();
-            }
-        }
     }
 
     /**
@@ -1781,111 +1786,7 @@
         return mPath;
     }
 
-    /**
-     * set the max size of the compiled sql cache for this database after purging the cache.
-     * (size of the cache = number of compiled-sql-statements stored in the cache)
-     *
-     * synchronized because we don't want t threads to change cache size at the same time.
-     * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
-     */
-    public void setMaxSqlCacheSize(int cacheSize) {
-        synchronized(mCompiledQueries) {
-            resetCompiledSqlCache();
-            mMaxSqlCacheSize = (cacheSize > MAX_SQL_CACHE_SIZE) ? MAX_SQL_CACHE_SIZE
-                    : (cacheSize < 0) ? 0 : cacheSize;
-        }
-    }
 
-    /**
-     * remove everything from the compiled sql cache
-     */
-    public void resetCompiledSqlCache() {
-        synchronized(mCompiledQueries) {
-            mCompiledQueries.clear();
-        }
-    }
-
-    /**
-     * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
-     * cache of compiledQueries attached to 'this'.
-     *
-     * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
-     * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
-     * mapping is NOT replaced with the new mapping).
-     *
-     * @return true if the given obj is added to cache. false otherwise.
-     */
-    /* package */ boolean addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
-        if (mMaxSqlCacheSize == 0) {
-            // for this database, there is no cache of compiled sql.
-            if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
-            }
-            return false;
-        }
-
-        SQLiteCompiledSql compiledSql = null;
-        synchronized(mCompiledQueries) {
-            // don't insert the new mapping if a mapping already exists
-            compiledSql = mCompiledQueries.get(sql);
-            if (compiledSql != null) {
-                return false;
-            }
-            // add this <sql, compiledStatement> to the cache
-            if (mCompiledQueries.size() == mMaxSqlCacheSize) {
-                /* reached max cachesize. before adding new entry, remove an entry from the
-                 * cache. we don't want to wipe out the entire cache because of this:
-                 * GCing {@link SQLiteCompiledSql} requires call to sqlite3_finalize
-                 * JNI method. If entire cache is wiped out, it could be cause a big GC activity
-                 * just because a (rogue) process is using the cache incorrectly.
-                 */
-                Set<String> keySet = mCompiledQueries.keySet();
-                for (String s : keySet) {
-                    mCompiledQueries.remove(s);
-                    break;
-                }
-            }
-            compiledSql = new SQLiteCompiledSql(this, sql);
-            mCompiledQueries.put(sql, compiledSql);
-        }
-        if (SQLiteDebug.DEBUG_SQL_CACHE) {
-            Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" +
-                    sql);
-        }
-        return true;
-    }
-
-    /**
-     * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
-     * returns null, if not found in the cache.
-     */
-    /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
-        SQLiteCompiledSql compiledStatement = null;
-        boolean cacheHit;
-        synchronized(mCompiledQueries) {
-            if (mMaxSqlCacheSize == 0) {
-                // for this database, there is no cache of compiled sql.
-                if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                    Log.v(TAG, "|cache NOT found|" + getPath());
-                }
-                return null;
-            }
-            cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
-        }
-        if (cacheHit) {
-            mNumCacheHits++;
-        } else {
-            mNumCacheMisses++;
-        }
-
-        if (SQLiteDebug.DEBUG_SQL_CACHE) {
-            Log.v(TAG, "|cache_stats|" +
-                    getPath() + "|" + mCompiledQueries.size() +
-                    "|" + mNumCacheHits + "|" + mNumCacheMisses +
-                    "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
-        }
-        return compiledStatement;
-    }
 
     /* package */ void logTimeStat(String sql, long beginNanos) {
         // Sample fast queries in proportion to the time taken.
@@ -1934,6 +1835,167 @@
         }
     }
 
+    /*
+     * ============================================================================
+     *
+     *       The following methods deal with compiled-sql cache
+     * ============================================================================
+     */
+    /**
+     * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
+     * cache of compiledQueries attached to 'this'.
+     *
+     * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
+     * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
+     * mapping is NOT replaced with the new mapping).
+     */
+    /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
+        if (mMaxSqlCacheSize == 0) {
+            // for this database, there is no cache of compiled sql.
+            if (SQLiteDebug.DEBUG_SQL_CACHE) {
+                Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
+            }
+            return;
+        }
+
+        SQLiteCompiledSql compiledSql = null;
+        synchronized(mCompiledQueries) {
+            // don't insert the new mapping if a mapping already exists
+            compiledSql = mCompiledQueries.get(sql);
+            if (compiledSql != null) {
+                return;
+            }
+            // add this <sql, compiledStatement> to the cache
+            if (mCompiledQueries.size() == mMaxSqlCacheSize) {
+                /* reached max cachesize. before adding new entry, remove an entry from the
+                 * cache. we don't want to wipe out the entire cache because of this:
+                 * GCing {@link SQLiteCompiledSql} requires call to sqlite3_finalize
+                 * JNI method. If entire cache is wiped out, it could cause a big GC activity
+                 * just because a (rogue) process is using the cache incorrectly.
+                 */
+                Log.wtf(TAG, "Too many sql statements in database cache. Make sure your sql " +
+                        "statements are using prepared-sql-statement syntax with '?' for" +
+                        "bindargs, instead of using actual values");
+                Set<String> keySet = mCompiledQueries.keySet();
+                for (String s : keySet) {
+                    mCompiledQueries.remove(s);
+                    break;
+                }
+            }
+            mCompiledQueries.put(sql, compiledStatement);
+        }
+        if (SQLiteDebug.DEBUG_SQL_CACHE) {
+            Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" +
+                    sql);
+        }
+        return;
+    }
+
+
+    private void deallocCachedSqlStatements() {
+        synchronized (mCompiledQueries) {
+            for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
+                compiledSql.releaseSqlStatement();
+            }
+            mCompiledQueries.clear();
+        }
+    }
+
+    /**
+     * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
+     * returns null, if not found in the cache.
+     */
+    /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
+        SQLiteCompiledSql compiledStatement = null;
+        boolean cacheHit;
+        synchronized(mCompiledQueries) {
+            if (mMaxSqlCacheSize == 0) {
+                // for this database, there is no cache of compiled sql.
+                if (SQLiteDebug.DEBUG_SQL_CACHE) {
+                    Log.v(TAG, "|cache NOT found|" + getPath());
+                }
+                return null;
+            }
+            cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
+        }
+        if (cacheHit) {
+            mNumCacheHits++;
+        } else {
+            mNumCacheMisses++;
+        }
+
+        if (SQLiteDebug.DEBUG_SQL_CACHE) {
+            Log.v(TAG, "|cache_stats|" +
+                    getPath() + "|" + mCompiledQueries.size() +
+                    "|" + mNumCacheHits + "|" + mNumCacheMisses +
+                    "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
+        }
+        return compiledStatement;
+    }
+
+    /**
+     * returns true if the given sql is cached in compiled-sql cache.
+     * @hide
+     */
+    public boolean isInCompiledSqlCache(String sql) {
+        synchronized(mCompiledQueries) {
+            return mCompiledQueries.containsKey(sql);
+        }
+    }
+
+    /**
+     * purges the given sql from the compiled-sql cache.
+     * @hide
+     */
+    public void purgeFromCompiledSqlCache(String sql) {
+        synchronized(mCompiledQueries) {
+            mCompiledQueries.remove(sql);
+        }
+    }
+
+    /**
+     * remove everything from the compiled sql cache
+     * @hide
+     */
+    public void resetCompiledSqlCache() {
+        synchronized(mCompiledQueries) {
+            mCompiledQueries.clear();
+        }
+    }
+
+    /**
+     * return the current maxCacheSqlCacheSize
+     * @hide
+     */
+    public synchronized int getMaxSqlCacheSize() {
+        return mMaxSqlCacheSize;
+    }
+
+    /**
+     * set the max size of the compiled sql cache for this database after purging the cache.
+     * (size of the cache = number of compiled-sql-statements stored in the cache).
+     *
+     * max cache size can ONLY be increased from its current size (default = 0).
+     * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
+     * then IllegalStateException is thrown
+     *
+     * synchronized because we don't want t threads to change cache size at the same time.
+     * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
+     * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
+     * < the value set with previous setMaxSqlCacheSize() call.
+     *
+     * @hide
+     */
+    public synchronized void setMaxSqlCacheSize(int cacheSize) {
+        if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+            throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+        } else if (cacheSize < mMaxSqlCacheSize) {
+            throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+                    "set with previous setMaxSqlCacheSize() call.");
+        }
+        mMaxSqlCacheSize = cacheSize;
+    }
+
     /**
      * Native call to open the database.
      *
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index edc15cb..00b0a86 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -37,15 +37,13 @@
     protected int nHandle = 0;
 
     /**
-     * the compiledSql object for the given sql statement.
+     * the SQLiteCompiledSql object for the given sql statement.
      */
-    private SQLiteCompiledSql compiledSql;
-    private boolean myCompiledSqlIsInCache;
+    private SQLiteCompiledSql mCompiledSql;
 
     /**
-     * compiledSql statement id is populated with the corresponding object from the above
-     * member compiledSql.
-     * this member is used by the native_bind_* methods
+     * SQLiteCompiledSql statement id is populated with the corresponding object from the above
+     * member. This member is used by the native_bind_* methods
      */
     protected int nStatement = 0;
 
@@ -60,47 +58,50 @@
         db.addSQLiteClosable(this);
         this.nHandle = db.mNativeHandle;
 
-        compiledSql = db.getCompiledStatementForSql(sql);
-        if (compiledSql == null) {
+        mCompiledSql = db.getCompiledStatementForSql(sql);
+        if (mCompiledSql == null) {
             // create a new compiled-sql obj
-            compiledSql = new SQLiteCompiledSql(db, sql);
+            mCompiledSql = new SQLiteCompiledSql(db, sql);
 
             // add it to the cache of compiled-sqls
-            myCompiledSqlIsInCache = db.addToCompiledQueries(sql, compiledSql);
-        } else {
-            myCompiledSqlIsInCache = true;
+            db.addToCompiledQueries(sql, mCompiledSql);
         }
-        nStatement = compiledSql.nStatement;
+        nStatement = mCompiledSql.nStatement;
     }
 
     @Override
     protected void onAllReferencesReleased() {
-        // release the compiled sql statement used by me if it is NOT in cache
-        if (!myCompiledSqlIsInCache && compiledSql != null) {
-            compiledSql.releaseSqlStatement();
-            compiledSql = null; // so that GC doesn't call finalize() on it
-        }
+        releaseCompiledSqlIfInCache();
         mDatabase.releaseReference();
         mDatabase.removeSQLiteClosable(this);
     }
 
     @Override
     protected void onAllReferencesReleasedFromContainer() {
-        // release the compiled sql statement used by me if it is NOT in cache
-      if (!myCompiledSqlIsInCache && compiledSql != null) {
-            compiledSql.releaseSqlStatement();
-            compiledSql = null; // so that GC doesn't call finalize() on it
-        }
+        releaseCompiledSqlIfInCache();
         mDatabase.releaseReference();
     }
 
+    private void releaseCompiledSqlIfInCache() {
+        if (mCompiledSql == null) {
+            return;
+        }
+        synchronized(mDatabase.mCompiledQueries) {
+            if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+                mCompiledSql.releaseSqlStatement();
+                mCompiledSql = null; // so that GC doesn't call finalize() on it
+                nStatement = 0;
+            }
+        }
+    }
+
     /**
      * Returns a unique identifier for this program.
      *
      * @return a unique identifier for this program
      */
     public final int getUniqueId() {
-        return compiledSql.nStatement;
+        return nStatement;
     }
 
     /* package */ String getSqlString() {
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index f1dcd89..d6d4899 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -27,7 +27,6 @@
 import static android.gesture.GestureConstants.*;
 
 final class GestureUtilities {
-    private static final int TEMPORAL_SAMPLING_RATE = 16;
 
     private GestureUtilities() {
     }
@@ -51,29 +50,29 @@
         final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive
         float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension];
         Arrays.fill(sample, 0);
-
+  
         RectF rect = gesture.getBoundingBox();
         float sx = targetPatchSize / rect.width();
         float sy = targetPatchSize / rect.height();
         float scale = sx < sy ? sx : sy;
-
+  
         float preDx = -rect.centerX();
         float preDy = -rect.centerY();
         float postDx = targetPatchSize / 2;
         float postDy = targetPatchSize / 2;
-
+  
         final ArrayList<GestureStroke> strokes = gesture.getStrokes();
         final int count = strokes.size();
-
+  
         int size;
         float xpos;
         float ypos;
-
+  
         for (int index = 0; index < count; index++) {
             final GestureStroke stroke = strokes.get(index);
             float[] strokepoints = stroke.points;
             size = strokepoints.length;
-
+  
             final float[] pts = new float[size];
              
             for (int i = 0; i < size; i += 2) {
@@ -118,7 +117,7 @@
                             xpos++;
                         }
                     }
-
+  
                     // evaluating vertically
                     if (segmentEndY > segmentStartY) {
                         ypos = (float) Math.ceil(segmentStartY);
@@ -143,11 +142,11 @@
                 segmentEndY = segmentStartY;
             }
         }
-
-
+  
+  
         return sample;
     }
-
+  
     private static void plot(float x, float y, float[] sample, int sampleSize) {
         x = x < 0 ? 0 : x;
         y = y < 0 ? 0 : y;
@@ -286,8 +285,8 @@
      * @param points
      * @return the covariance matrix
      */
-    private static double[][] computeCoVariance(float[] points) {
-        double[][] array = new double[2][2];
+    private static float[][] computeCoVariance(float[] points) {
+        float[][] array = new float[2][2];
         array[0][0] = 0;
         array[0][1] = 0;
         array[1][0] = 0;
@@ -321,17 +320,17 @@
         return sum;
     }
 
-    static double computeStraightness(float[] points) {
+    static float computeStraightness(float[] points) {
         float totalLen = computeTotalLength(points);
         float dx = points[2] - points[0];
         float dy = points[3] - points[1];
-        return Math.sqrt(dx * dx + dy * dy) / totalLen;
+        return (float) Math.sqrt(dx * dx + dy * dy) / totalLen;
     }
 
-    static double computeStraightness(float[] points, float totalLen) {
+    static float computeStraightness(float[] points, float totalLen) {
         float dx = points[2] - points[0];
         float dy = points[3] - points[1];
-        return Math.sqrt(dx * dx + dy * dy) / totalLen;
+        return (float) Math.sqrt(dx * dx + dy * dy) / totalLen;
     }
 
     /**
@@ -341,8 +340,8 @@
      * @param vector2
      * @return the distance
      */
-    static double squaredEuclideanDistance(float[] vector1, float[] vector2) {
-        double squaredDistance = 0;
+    static float squaredEuclideanDistance(float[] vector1, float[] vector2) {
+        float squaredDistance = 0;
         int size = vector1.length;
         for (int i = 0; i < size; i++) {
             float difference = vector1[i] - vector2[i];
@@ -358,13 +357,13 @@
      * @param vector2
      * @return the distance between 0 and Math.PI
      */
-    static double cosineDistance(float[] vector1, float[] vector2) {
+    static float cosineDistance(float[] vector1, float[] vector2) {
         float sum = 0;
         int len = vector1.length;
         for (int i = 0; i < len; i++) {
             sum += vector1[i] * vector2[i];
         }
-        return Math.acos(sum);
+        return (float) Math.acos(sum);
     }
     
     /**
@@ -375,46 +374,58 @@
      * @param numOrientations the maximum number of orientation allowed
      * @return the distance between the two instances (between 0 and Math.PI)
      */
-    static double minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
+    static float minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
         final int len = vector1.length;
-        double a = 0;
-        double b = 0;
+        float a = 0;
+        float b = 0;
         for (int i = 0; i < len; i += 2) {
             a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1];
             b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i];
         }
         if (a != 0) {
-            final double tan = b/a;
+            final float tan = b/a;
             final double angle = Math.atan(tan);
             if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) {
-                return Math.acos(a);
+                return (float) Math.acos(a);
             } else {
                 final double cosine = Math.cos(angle);
                 final double sine = cosine * tan; 
-                return Math.acos(a * cosine + b * sine);
+                return (float) Math.acos(a * cosine + b * sine);
             }
         } else {
-            return Math.PI / 2;
+            return (float) Math.PI / 2;
         }
     }
 
 
-    static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> pts) {
-        GestureStroke stroke = new GestureStroke(pts);
-        float[] points = temporalSampling(stroke, TEMPORAL_SAMPLING_RATE);
-        return computeOrientedBoundingBox(points);
-    }
-
-    static OrientedBoundingBox computeOrientedBoundingBox(float[] points) {
+    static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> originalPoints) {
+        final int count = originalPoints.size();
+        float[] points = new float[count * 2];
+        for (int i = 0; i < count; i++) {
+            GesturePoint point = originalPoints.get(i);
+            int index = i * 2;
+            points[index] = point.x;
+            points[index + 1] = point.y;
+        }
         float[] meanVector = computeCentroid(points);
         return computeOrientedBoundingBox(points, meanVector);
     }
 
-    static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
+    static OrientedBoundingBox computeOrientedBoundingBox(float[] originalPoints) {
+        int size = originalPoints.length;
+        float[] points = new float[size];
+        for (int i = 0; i < size; i++) {
+            points[i] = originalPoints[i];
+        }
+        float[] meanVector = computeCentroid(points);
+        return computeOrientedBoundingBox(points, meanVector);
+    }
+
+    private static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
         translate(points, -centroid[0], -centroid[1]);
 
-        double[][] array = computeCoVariance(points);
-        double[] targetVector = computeOrientation(array);
+        float[][] array = computeCoVariance(points);
+        float[] targetVector = computeOrientation(array);
 
         float angle;
         if (targetVector[0] == 0 && targetVector[1] == 0) {
@@ -448,25 +459,25 @@
         return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny);
     }
 
-    private static double[] computeOrientation(double[][] covarianceMatrix) {
-        double[] targetVector = new double[2];
+    private static float[] computeOrientation(float[][] covarianceMatrix) {
+        float[] targetVector = new float[2];
         if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) {
             targetVector[0] = 1;
             targetVector[1] = 0;
         }
 
-        double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1];
-        double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1]
+        float a = -covarianceMatrix[0][0] - covarianceMatrix[1][1];
+        float b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1]
                 * covarianceMatrix[1][0];
-        double value = a / 2;
-        double rightside = Math.sqrt(Math.pow(value, 2) - b);
-        double lambda1 = -value + rightside;
-        double lambda2 = -value - rightside;
+        float value = a / 2;
+        float rightside = (float) Math.sqrt(Math.pow(value, 2) - b);
+        float lambda1 = -value + rightside;
+        float lambda2 = -value - rightside;
         if (lambda1 == lambda2) {
             targetVector[0] = 0;
             targetVector[1] = 0;
         } else {
-            double lambda = lambda1 > lambda2 ? lambda1 : lambda2;
+            float lambda = lambda1 > lambda2 ? lambda1 : lambda2;
             targetVector[0] = 1;
             targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1];
         }
@@ -474,13 +485,13 @@
     }
     
     
-    static float[] rotate(float[] points, double angle) {
-        double cos = Math.cos(angle);
-        double sin = Math.sin(angle);
+    static float[] rotate(float[] points, float angle) {
+        float cos = (float) Math.cos(angle);
+        float sin = (float) Math.sin(angle);
         int size = points.length;
         for (int i = 0; i < size; i += 2) {
-            float x = (float) (points[i] * cos - points[i + 1] * sin);
-            float y = (float) (points[i] * sin + points[i + 1] * cos);
+            float x = points[i] * cos - points[i + 1] * sin;
+            float y = points[i] * sin + points[i + 1] * cos;
             points[i] = x;
             points[i + 1] = y;
         }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b315932..5d5bd9c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -841,7 +841,14 @@
      */
     public boolean onEvaluateFullscreenMode() {
         Configuration config = getResources().getConfiguration();
-        return config.orientation == Configuration.ORIENTATION_LANDSCAPE;
+        if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
+            return false;
+        }
+        if (mInputEditorInfo != null
+                && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
+            return false;
+        }
+        return true;
     }
     
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a127df0..30799ec 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -129,37 +129,33 @@
     public static final int TYPE_WIFI        = 1;
     /**
      * An MMS-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applications needing to talk to the carrier's Multimedia Messaging
      * Service servers.  It may coexist with default data connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_MMS  = 2;
     /**
      * A SUPL-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applications needing to talk to the carrier's Secure User Plane
      * Location servers for help locating the device.  It may coexist with
      * default data connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_SUPL = 3;
     /**
      * A DUN-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applicaitons performing a Dial Up Networking bridge so that
      * the carrier is aware of DUN traffic.  It may coexist with default data
      * connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_DUN  = 4;
     /**
      * A High Priority Mobile data connection.  This connection is typically
-     * the same as {@link #TYPEMOBILE} but the routing setup is different.
+     * the same as {@link #TYPE_MOBILE} but the routing setup is different.
      * Only requesting processes will have access to the Mobile DNS servers
      * and only IP's explicitly requested via {@link #requestRouteToHost}
-     * will route over this interface.
-     *{@hide}
+     * will route over this interface if a default route exists.
      */
     public static final int TYPE_MOBILE_HIPRI = 5;
     /** {@hide} */
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index a97b9e5..e40f1b8 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -16,11 +16,12 @@
 
 package android.net;
 
-import android.net.http.DomainNameChecker;
 import android.os.SystemProperties;
 import android.util.Config;
 import android.util.Log;
 
+import com.android.common.DomainNameValidator;
+
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -200,7 +201,7 @@
 
         X509Certificate lastChainCert = (X509Certificate) certs[0];
 
-        if (!DomainNameChecker.match(lastChainCert, destHost)) {
+        if (!DomainNameValidator.match(lastChainCert, destHost)) {
             if (Config.LOGD) {
                 Log.d(LOG_TAG,"validateSocket(): domain name check failed");
             }
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index ed6b4c2..da6af9d 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -16,6 +16,8 @@
 
 package android.net.http;
 
+import com.android.common.DomainNameValidator;
+
 import org.apache.harmony.xnet.provider.jsse.SSLParameters;
 
 import java.io.IOException;
@@ -112,7 +114,7 @@
             closeSocketThrowException(
                 sslSocket, "certificate for this site is null");
         } else {
-            if (!DomainNameChecker.match(currCertificate, domain)) {
+            if (!DomainNameValidator.match(currCertificate, domain)) {
                 String errorMessage = "certificate not for this host: " + domain;
 
                 if (HttpLog.LOGV) {
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index f052689..e73569a 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -42,17 +42,17 @@
     /**
      * Mount external storage at given mount point.
      */
-    void mountMedia(String mountPoint);
+    void mountVolume(String mountPoint);
 
     /**
      * Safely unmount external storage at given mount point.
      */
-    void unmountMedia(String mountPoint);
+    void unmountVolume(String mountPoint);
 
     /**
      * Format external storage given a mount point.
      */
-    void formatMedia(String mountPoint);
+    void formatVolume(String mountPoint);
 
     /**
      * Returns true if media notification sounds are enabled.
@@ -104,8 +104,7 @@
     String[] getSecureContainerList();
 
     /**
-     * Shuts down the MountService and gracefully unmounts
-     * all external media.
+     * Shuts down the MountService and gracefully unmounts all external media.
      */
     void shutdown();
 }
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
index 3679e47..bc76180 100644
--- a/core/java/android/os/Power.java
+++ b/core/java/android/os/Power.java
@@ -17,6 +17,8 @@
 package android.os;
 
 import java.io.IOException;
+import android.os.ServiceManager;
+import android.os.IMountService;
 
 /**
  * Class that provides access to some of the power management functions.
@@ -97,5 +99,19 @@
      * @throws IOException if reboot fails for some reason (eg, lack of
      *         permission)
      */
-    public static native void reboot(String reason) throws IOException;
+    public static void reboot(String reason) throws IOException
+    {
+        IMountService mSvc = IMountService.Stub.asInterface(
+                ServiceManager.getService("mount"));
+
+        if (mSvc != null) {
+            try {
+                mSvc.shutdown();
+            } catch (Exception e) {
+            }
+        }
+        rebootNative(reason);
+    }
+
+    private static native void rebootNative(String reason) throws IOException ;
 }
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index e7c19cf..c2928cb 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -110,11 +110,11 @@
     private long mTimeHandleBase64;
 
     public VCardParser_V21() {
-        this(VCardConfig.PARSE_TYPE_UNKNOWN);
+        this(null);
     }
 
     public VCardParser_V21(VCardSourceDetector detector) {
-        this(detector.getEstimatedType());
+        this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN);
     }
 
     public VCardParser_V21(int parseType) {
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 7f947e9..509aac5 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -583,14 +583,14 @@
          * {@link #ACCOUNT_TYPE} identifies a specific account.
          * <P>Type: TEXT</P>
          */
-        public static final String ACCOUNT_NAME = "account_name";
+        public static final String ACCOUNT_NAME = "_sync_account";
 
         /**
          * The type of account to which this row belongs, which when paired with
          * {@link #ACCOUNT_NAME} identifies a specific account.
          * <P>Type: TEXT</P>
          */
-        public static final String ACCOUNT_TYPE = "account_type";
+        public static final String ACCOUNT_TYPE = "_sync_account_type";
 
         public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
             return new EntityIteratorImpl(cursor, resolver);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 790fe5c..a93cee7 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -510,4 +510,493 @@
      * This download doesn't show in the UI or in the notifications.
      */
     public static final int VISIBILITY_HIDDEN = 2;
+
+    /**
+     * Implementation details
+     */
+    public static final class Impl implements BaseColumns {
+        private Impl() {}
+
+        /**
+         * The permission to access the download manager
+         */
+        public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
+
+        /**
+         * The permission to access the download manager's advanced functions
+         */
+        public static final String PERMISSION_ACCESS_ADVANCED =
+                "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
+
+        /**
+         * The permission to directly access the download manager's cache directory
+         */
+        public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
+
+        /**
+         * The permission to send broadcasts on download completion
+         */
+        public static final String PERMISSION_SEND_INTENTS =
+                "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
+
+        /**
+         * The content:// URI for the data table in the provider
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://downloads/download");
+
+        /**
+         * Broadcast Action: this is sent by the download manager to the app
+         * that had initiated a download when that download completes. The
+         * download's content: uri is specified in the intent's data.
+         */
+        public static final String ACTION_DOWNLOAD_COMPLETED =
+                "android.intent.action.DOWNLOAD_COMPLETED";
+
+        /**
+         * Broadcast Action: this is sent by the download manager to the app
+         * that had initiated a download when the user selects the notification
+         * associated with that download. The download's content: uri is specified
+         * in the intent's data if the click is associated with a single download,
+         * or Downloads.CONTENT_URI if the notification is associated with
+         * multiple downloads.
+         * Note: this is not currently sent for downloads that have completed
+         * successfully.
+         */
+        public static final String ACTION_NOTIFICATION_CLICKED =
+                "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
+
+        /**
+         * The name of the column containing the URI of the data being downloaded.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read</P>
+         */
+        public static final String COLUMN_URI = "uri";
+
+        /**
+         * The name of the column containing application-specific data.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read/Write</P>
+         */
+        public static final String COLUMN_APP_DATA = "entity";
+
+        /**
+         * The name of the column containing the flags that indicates whether
+         * the initiating application is capable of verifying the integrity of
+         * the downloaded file. When this flag is set, the download manager
+         * performs downloads and reports success even in some situations where
+         * it can't guarantee that the download has completed (e.g. when doing
+         * a byte-range request without an ETag, or when it can't determine
+         * whether a download fully completed).
+         * <P>Type: BOOLEAN</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_NO_INTEGRITY = "no_integrity";
+
+        /**
+         * The name of the column containing the filename that the initiating
+         * application recommends. When possible, the download manager will attempt
+         * to use this filename, or a variation, as the actual name for the file.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_FILE_NAME_HINT = "hint";
+
+        /**
+         * The name of the column containing the filename where the downloaded data
+         * was actually stored.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String _DATA = "_data";
+
+        /**
+         * The name of the column containing the MIME type of the downloaded data.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read</P>
+         */
+        public static final String COLUMN_MIME_TYPE = "mimetype";
+
+        /**
+         * The name of the column containing the flag that controls the destination
+         * of the download. See the DESTINATION_* constants for a list of legal values.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_DESTINATION = "destination";
+
+        /**
+         * The name of the column containing the flags that controls whether the
+         * download is displayed by the UI. See the VISIBILITY_* constants for
+         * a list of legal values.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Init/Read/Write</P>
+         */
+        public static final String COLUMN_VISIBILITY = "visibility";
+
+        /**
+         * The name of the column containing the current control state  of the download.
+         * Applications can write to this to control (pause/resume) the download.
+         * the CONTROL_* constants for a list of legal values.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String COLUMN_CONTROL = "control";
+
+        /**
+         * The name of the column containing the current status of the download.
+         * Applications can read this to follow the progress of each download. See
+         * the STATUS_* constants for a list of legal values.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String COLUMN_STATUS = "status";
+
+        /**
+         * The name of the column containing the date at which some interesting
+         * status changed in the download. Stored as a System.currentTimeMillis()
+         * value.
+         * <P>Type: BIGINT</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String COLUMN_LAST_MODIFICATION = "lastmod";
+
+        /**
+         * The name of the column containing the package name of the application
+         * that initiating the download. The download manager will send
+         * notifications to a component in this package when the download completes.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read</P>
+         */
+        public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
+
+        /**
+         * The name of the column containing the component name of the class that
+         * will receive notifications associated with the download. The
+         * package/class combination is passed to
+         * Intent.setClassName(String,String).
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read</P>
+         */
+        public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
+
+        /**
+         * If extras are specified when requesting a download they will be provided in the intent that
+         * is sent to the specified class and package when a download has finished.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
+
+        /**
+         * The name of the column contain the values of the cookie to be used for
+         * the download. This is used directly as the value for the Cookie: HTTP
+         * header that gets sent with the request.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_COOKIE_DATA = "cookiedata";
+
+        /**
+         * The name of the column containing the user agent that the initiating
+         * application wants the download manager to use for this download.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_USER_AGENT = "useragent";
+
+        /**
+         * The name of the column containing the referer (sic) that the initiating
+         * application wants the download manager to use for this download.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_REFERER = "referer";
+
+        /**
+         * The name of the column containing the total size of the file being
+         * downloaded.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String COLUMN_TOTAL_BYTES = "total_bytes";
+
+        /**
+         * The name of the column containing the size of the part of the file that
+         * has been downloaded so far.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Read</P>
+         */
+        public static final String COLUMN_CURRENT_BYTES = "current_bytes";
+
+        /**
+         * The name of the column where the initiating application can provide the
+         * UID of another application that is allowed to access this download. If
+         * multiple applications share the same UID, all those applications will be
+         * allowed to access this download. This column can be updated after the
+         * download is initiated. This requires the permission
+         * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
+         * <P>Type: INTEGER</P>
+         * <P>Owner can Init</P>
+         */
+        public static final String COLUMN_OTHER_UID = "otheruid";
+
+        /**
+         * The name of the column where the initiating application can provided the
+         * title of this download. The title will be displayed ito the user in the
+         * list of downloads.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read/Write</P>
+         */
+        public static final String COLUMN_TITLE = "title";
+
+        /**
+         * The name of the column where the initiating application can provide the
+         * description of this download. The description will be displayed to the
+         * user in the list of downloads.
+         * <P>Type: TEXT</P>
+         * <P>Owner can Init/Read/Write</P>
+         */
+        public static final String COLUMN_DESCRIPTION = "description";
+
+        /*
+         * Lists the destinations that an application can specify for a download.
+         */
+
+        /**
+         * This download will be saved to the external storage. This is the
+         * default behavior, and should be used for any file that the user
+         * can freely access, copy, delete. Even with that destination,
+         * unencrypted DRM files are saved in secure internal storage.
+         * Downloads to the external destination only write files for which
+         * there is a registered handler. The resulting files are accessible
+         * by filename to all applications.
+         */
+        public static final int DESTINATION_EXTERNAL = 0;
+
+        /**
+         * This download will be saved to the download manager's private
+         * partition. This is the behavior used by applications that want to
+         * download private files that are used and deleted soon after they
+         * get downloaded. All file types are allowed, and only the initiating
+         * application can access the file (indirectly through a content
+         * provider). This requires the
+         * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
+         */
+        public static final int DESTINATION_CACHE_PARTITION = 1;
+
+        /**
+         * This download will be saved to the download manager's private
+         * partition and will be purged as necessary to make space. This is
+         * for private files (similar to CACHE_PARTITION) that aren't deleted
+         * immediately after they are used, and are kept around by the download
+         * manager as long as space is available.
+         */
+        public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
+
+        /**
+         * This download will be saved to the download manager's private
+         * partition, as with DESTINATION_CACHE_PARTITION, but the download
+         * will not proceed if the user is on a roaming data connection.
+         */
+        public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
+
+        /**
+         * This download is allowed to run.
+         */
+        public static final int CONTROL_RUN = 0;
+
+        /**
+         * This download must pause at the first opportunity.
+         */
+        public static final int CONTROL_PAUSED = 1;
+
+        /*
+         * Lists the states that the download manager can set on a download
+         * to notify applications of the download progress.
+         * The codes follow the HTTP families:<br>
+         * 1xx: informational<br>
+         * 2xx: success<br>
+         * 3xx: redirects (not used by the download manager)<br>
+         * 4xx: client errors<br>
+         * 5xx: server errors
+         */
+
+        /**
+         * Returns whether the status is informational (i.e. 1xx).
+         */
+        public static boolean isStatusInformational(int status) {
+            return (status >= 100 && status < 200);
+        }
+
+        /**
+         * Returns whether the download is suspended. (i.e. whether the download
+         * won't complete without some action from outside the download
+         * manager).
+         */
+        public static boolean isStatusSuspended(int status) {
+            return (status == STATUS_PENDING_PAUSED || status == STATUS_RUNNING_PAUSED);
+        }
+
+        /**
+         * Returns whether the status is a success (i.e. 2xx).
+         */
+        public static boolean isStatusSuccess(int status) {
+            return (status >= 200 && status < 300);
+        }
+
+        /**
+         * Returns whether the status is an error (i.e. 4xx or 5xx).
+         */
+        public static boolean isStatusError(int status) {
+            return (status >= 400 && status < 600);
+        }
+
+        /**
+         * Returns whether the status is a client error (i.e. 4xx).
+         */
+        public static boolean isStatusClientError(int status) {
+            return (status >= 400 && status < 500);
+        }
+
+        /**
+         * Returns whether the status is a server error (i.e. 5xx).
+         */
+        public static boolean isStatusServerError(int status) {
+            return (status >= 500 && status < 600);
+        }
+
+        /**
+         * Returns whether the download has completed (either with success or
+         * error).
+         */
+        public static boolean isStatusCompleted(int status) {
+            return (status >= 200 && status < 300) || (status >= 400 && status < 600);
+        }
+
+        /**
+         * This download hasn't stated yet
+         */
+        public static final int STATUS_PENDING = 190;
+
+        /**
+         * This download hasn't stated yet and is paused
+         */
+        public static final int STATUS_PENDING_PAUSED = 191;
+
+        /**
+         * This download has started
+         */
+        public static final int STATUS_RUNNING = 192;
+
+        /**
+         * This download has started and is paused
+         */
+        public static final int STATUS_RUNNING_PAUSED = 193;
+
+        /**
+         * This download has successfully completed.
+         * Warning: there might be other status values that indicate success
+         * in the future.
+         * Use isSucccess() to capture the entire category.
+         */
+        public static final int STATUS_SUCCESS = 200;
+
+        /**
+         * This request couldn't be parsed. This is also used when processing
+         * requests with unknown/unsupported URI schemes.
+         */
+        public static final int STATUS_BAD_REQUEST = 400;
+
+        /**
+         * This download can't be performed because the content type cannot be
+         * handled.
+         */
+        public static final int STATUS_NOT_ACCEPTABLE = 406;
+
+        /**
+         * This download cannot be performed because the length cannot be
+         * determined accurately. This is the code for the HTTP error "Length
+         * Required", which is typically used when making requests that require
+         * a content length but don't have one, and it is also used in the
+         * client when a response is received whose length cannot be determined
+         * accurately (therefore making it impossible to know when a download
+         * completes).
+         */
+        public static final int STATUS_LENGTH_REQUIRED = 411;
+
+        /**
+         * This download was interrupted and cannot be resumed.
+         * This is the code for the HTTP error "Precondition Failed", and it is
+         * also used in situations where the client doesn't have an ETag at all.
+         */
+        public static final int STATUS_PRECONDITION_FAILED = 412;
+
+        /**
+         * This download was canceled
+         */
+        public static final int STATUS_CANCELED = 490;
+
+        /**
+         * This download has completed with an error.
+         * Warning: there will be other status values that indicate errors in
+         * the future. Use isStatusError() to capture the entire category.
+         */
+        public static final int STATUS_UNKNOWN_ERROR = 491;
+
+        /**
+         * This download couldn't be completed because of a storage issue.
+         * Typically, that's because the filesystem is missing or full.
+         */
+        public static final int STATUS_FILE_ERROR = 492;
+
+        /**
+         * This download couldn't be completed because of an HTTP
+         * redirect response that the download manager couldn't
+         * handle.
+         */
+        public static final int STATUS_UNHANDLED_REDIRECT = 493;
+
+        /**
+         * This download couldn't be completed because of an
+         * unspecified unhandled HTTP code.
+         */
+        public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
+
+        /**
+         * This download couldn't be completed because of an
+         * error receiving or processing data at the HTTP level.
+         */
+        public static final int STATUS_HTTP_DATA_ERROR = 495;
+
+        /**
+         * This download couldn't be completed because of an
+         * HttpException while setting up the request.
+         */
+        public static final int STATUS_HTTP_EXCEPTION = 496;
+
+        /**
+         * This download couldn't be completed because there were
+         * too many redirects.
+         */
+        public static final int STATUS_TOO_MANY_REDIRECTS = 497;
+
+        /**
+         * This download is visible but only shows in the notifications
+         * while it's in progress.
+         */
+        public static final int VISIBILITY_VISIBLE = 0;
+
+        /**
+         * This download is visible and shows in the notifications while
+         * in progress and after completion.
+         */
+        public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+
+        /**
+         * This download doesn't show in the UI or in the notifications.
+         */
+        public static final int VISIBILITY_HIDDEN = 2;
+
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7db9fdc..c0aee4c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2094,13 +2094,6 @@
         public static final String LOGGING_ID = "logging_id";
 
         /**
-         * The Logging ID (a unique 64-bit value) as a hex string.
-         * Used as a pseudonymous identifier for logging.
-         * @hide
-         */
-        public static final String LOGGING_ID2 = "logging_id2";
-
-        /**
          * User preference for which network(s) should be used. Only the
          * connectivity service should touch this.
          */
@@ -2318,32 +2311,6 @@
         public static final String BACKGROUND_DATA = "background_data";
 
         /**
-         * The time in msec, when the LAST_KMSG file was send to the checkin server.
-         * We will only send the LAST_KMSG file if it was modified after this time.
-         *
-         * @hide
-         */
-        public static final String CHECKIN_SEND_LAST_KMSG_TIME = "checkin_kmsg_time";
-
-        /**
-         * The time in msec, when the apanic_console file was send to the checkin server.
-         * We will only send the apanic_console file if it was modified after this time.
-         *
-         * @hide
-         */
-        public static final String CHECKIN_SEND_APANIC_CONSOLE_TIME =
-            "checkin_apanic_console_time";
-
-        /**
-         * The time in msec, when the apanic_thread file was send to the checkin server.
-         * We will only send the apanic_thread file if it was modified after this time.
-         *
-         * @hide
-         */
-        public static final String CHECKIN_SEND_APANIC_THREAD_TIME =
-            "checkin_apanic_thread_time";
-
-        /**
          * The CDMA roaming mode 0 = Home Networks, CDMA default
          *                       1 = Roaming on Affiliated networks
          *                       2 = Roaming on any networks
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4860cbd..747ae30 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1638,6 +1638,13 @@
              */
             public static final String LAST_TRY = "last_try";
         }
+
+        public static final class WordsTable {
+            public static final String ID = "_id";
+            public static final String SOURCE_ROW_ID = "source_id";
+            public static final String TABLE_ID = "table_to_use";
+            public static final String INDEXED_TEXT = "index_text";
+        }
     }
 
     public static final class Carriers implements BaseColumns {
diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java
index 9045c09..e2d1596 100644
--- a/core/java/android/text/LoginFilter.java
+++ b/core/java/android/text/LoginFilter.java
@@ -158,11 +158,11 @@
 
     /**
      * This filter rejects characters in the user name that are not compatible with Google login.
-     * It is slightly less restrictive than the above filter in that it allows [a-zA-Z0-9._-]. 
+     * It is slightly less restrictive than the above filter in that it allows [a-zA-Z0-9._-+]. 
      * 
      */
     public static class UsernameFilterGeneric extends LoginFilter {
-        private static final String mAllowed = "@_-."; // Additional characters
+        private static final String mAllowed = "@_-+."; // Additional characters
         
         public UsernameFilterGeneric() {
             super(false);
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
new file mode 100644
index 0000000..196716a
--- /dev/null
+++ b/core/java/android/view/TransformGestureDetector.java
@@ -0,0 +1,316 @@
+/*
+ * 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 android.view;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.GestureDetector.SimpleOnGestureListener;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
+ * will notify users when a particular gesture event has occurred. This class
+ * should only be used with {@link MotionEvent}s reported via touch.
+ * 
+ * To use this class:
+ * <ul>
+ *  <li>Create an instance of the {@code TransformGestureDetector} for your
+ *      {@link View}
+ *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ *          callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class TransformGestureDetector {
+    /**
+     * The listener for receiving notifications when gestures occur.
+     * If you want to listen for all the different gestures then implement
+     * this interface. If you only want to listen for a subset it might
+     * be easier to extend {@link SimpleOnGestureListener}.
+     * 
+     * An application will receive events in the following order:
+     * One onTransformBegin()
+     * Zero or more onTransform()
+     * One onTransformEnd() or onTransformFling()
+     */
+    public interface OnTransformGestureListener {
+        /**
+         * Responds to transformation events for a gesture in progress.
+         * Reported by pointer motion.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransform(TransformGestureDetector detector);
+        
+        /**
+         * Responds to the beginning of a transformation gesture. Reported by
+         * new pointers going down.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformBegin(TransformGestureDetector detector);
+ 
+        /**
+         * Responds to the end of a transformation gesture. Reported by existing
+         * pointers going up. If the end of a gesture would result in a fling,
+         * onTransformFling is called instead.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformEnd(TransformGestureDetector detector);
+
+        /**
+         * Responds to the end of a transformation gesture that begins a fling.
+         * Reported by existing pointers going up. If the end of a gesture 
+         * would not result in a fling, onTransformEnd is called instead.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformFling(TransformGestureDetector detector);
+    }
+    
+    private static final boolean DEBUG = false;
+    
+    private static final int INITIAL_EVENT_IGNORES = 2;
+    
+    private Context mContext;
+    private float mTouchSizeScale;
+    private OnTransformGestureListener mListener;
+    private int mVelocityTimeUnits;
+    private MotionEvent mInitialEvent;
+    
+    private MotionEvent mPrevEvent;
+    private MotionEvent mCurrEvent;
+    private VelocityTracker mVelocityTracker;
+
+    private float mCenterX;
+    private float mCenterY;
+    private float mTransX;
+    private float mTransY;
+    private float mPrevFingerDiffX;
+    private float mPrevFingerDiffY;
+    private float mCurrFingerDiffX;
+    private float mCurrFingerDiffY;
+    private float mRotateDegrees;
+    private float mCurrLen;
+    private float mPrevLen;
+    private float mScaleFactor;
+    
+    // Units in pixels. Current value is pulled out of thin air for debugging only.
+    private float mPointerJumpLimit = 30;
+    
+    private int mEventIgnoreCount;
+    
+   public TransformGestureDetector(Context context, OnTransformGestureListener listener,
+            int velocityTimeUnits) {
+        mContext = context;
+        mListener = listener;
+        mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
+        mVelocityTimeUnits = velocityTimeUnits;
+        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
+    }
+    
+    public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
+        this(context, listener, 1000);
+    }
+    
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        boolean handled = true;
+
+        if (mInitialEvent == null) {
+            // No transform gesture in progress
+            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+                    event.getPointerCount() >= 2) {
+                // We have a new multi-finger gesture
+                mInitialEvent = MotionEvent.obtain(event);
+                mPrevEvent = MotionEvent.obtain(event);
+                mVelocityTracker = VelocityTracker.obtain();
+                handled = mListener.onTransformBegin(this);
+            }
+        } else {
+            // Transform gesture in progress - attempt to handle it
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_1_UP:
+                case MotionEvent.ACTION_POINTER_2_UP:
+                    // Gesture ended
+                    handled = mListener.onTransformEnd(this);
+
+                    reset();
+                    break;
+                    
+                case MotionEvent.ACTION_CANCEL:
+                    handled = mListener.onTransformEnd(this);
+                    
+                    reset();
+                    break;
+                    
+                case MotionEvent.ACTION_MOVE:
+                    setContext(event);
+
+                    // Our first few events can be crazy from some touchscreens - drop them.
+                    if (mEventIgnoreCount == 0) {
+                        mVelocityTracker.addMovement(event);
+                        handled = mListener.onTransform(this);
+                    } else {
+                        mEventIgnoreCount--;
+                    }
+                    
+                    mPrevEvent.recycle();
+                    mPrevEvent = MotionEvent.obtain(event);
+                    break;
+            }
+        }
+        return handled;
+    }
+    
+    private void setContext(MotionEvent curr) {
+        mCurrEvent = MotionEvent.obtain(curr);
+
+        mRotateDegrees = -1;
+        mCurrLen = -1;
+        mPrevLen = -1;
+        mScaleFactor = -1;
+
+        final MotionEvent prev = mPrevEvent;
+        
+        float px0 = prev.getX(0);
+        float py0 = prev.getY(0);
+        float px1 = prev.getX(1);
+        float py1 = prev.getY(1);
+        float cx0 = curr.getX(0);
+        float cy0 = curr.getY(0);
+        float cx1 = curr.getX(1);
+        float cy1 = curr.getY(1);
+
+        // Some touchscreens do weird things with pointer values where points are
+        // too close along one axis. Try to detect this here and smooth things out.
+        // The main indicator is that we get the X or Y value from the other pointer.
+        final float dx0 = cx0 - px0;
+        final float dy0 = cy0 - py0;
+        final float dx1 = cx1 - px1;
+        final float dy1 = cy1 - py1;
+
+        if (cx0 == cx1) {
+            if (Math.abs(dx0) > mPointerJumpLimit) {
+                 cx0 = px0;
+            } else if (Math.abs(dx1) > mPointerJumpLimit) {
+                cx1 = px1;
+            }
+        } else if (cy0 == cy1) {
+            if (Math.abs(dy0) > mPointerJumpLimit) {
+                cy0 = py0;
+            } else if (Math.abs(dy1) > mPointerJumpLimit) {
+                cy1 = py1;
+            }
+        }
+        
+        final float pvx = px1 - px0;
+        final float pvy = py1 - py0;
+        final float cvx = cx1 - cx0;
+        final float cvy = cy1 - cy0;
+        mPrevFingerDiffX = pvx;
+        mPrevFingerDiffY = pvy;
+        mCurrFingerDiffX = cvx;
+        mCurrFingerDiffY = cvy;
+
+        final float pmidx = px0 + pvx * 0.5f;
+        final float pmidy = py0 + pvy * 0.5f;
+        final float cmidx = cx0 + cvx * 0.5f;
+        final float cmidy = cy0 + cvy * 0.5f;
+
+        mCenterX = cmidx;
+        mCenterY = cmidy;
+        mTransX = cmidx - pmidx;
+        mTransY = cmidy - pmidy;
+    }
+    
+    private void reset() {
+        if (mInitialEvent != null) {
+            mInitialEvent.recycle();
+            mInitialEvent = null;
+        }
+        if (mPrevEvent != null) {
+            mPrevEvent.recycle();
+            mPrevEvent = null;
+        }
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+            mCurrEvent = null;
+        }
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
+    }
+    
+    public float getCenterX() {
+        return mCenterX;
+    }
+
+    public float getCenterY() {
+        return mCenterY;
+    }
+
+    public float getTranslateX() {
+        return mTransX;
+    }
+
+    public float getTranslateY() {
+        return mTransY;
+    }
+
+    public float getCurrentSpan() {
+        if (mCurrLen == -1) {
+            final float cvx = mCurrFingerDiffX;
+            final float cvy = mCurrFingerDiffY;
+            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+        }
+        return mCurrLen;
+    }
+
+    public float getPreviousSpan() {
+        if (mPrevLen == -1) {
+            final float pvx = mPrevFingerDiffX;
+            final float pvy = mPrevFingerDiffY;
+            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+        }
+        return mPrevLen;
+    }
+
+    public float getScaleFactor() {
+        if (mScaleFactor == -1) {
+            mScaleFactor = getCurrentSpan() / getPreviousSpan();
+        }
+        return mScaleFactor;
+    }
+
+    public float getRotation() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 5d89c46..9581080 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -55,12 +55,12 @@
                 }
             }, 2));
 
-    final float mPastX[] = new float[NUM_PAST];
-    final float mPastY[] = new float[NUM_PAST];
-    final long mPastTime[] = new long[NUM_PAST];
+    final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
+    final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
+    final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
 
-    float mYVelocity;
-    float mXVelocity;
+    float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
+    float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
 
     private VelocityTracker mNext;
 
@@ -105,7 +105,9 @@
      * Reset the velocity tracker back to its initial state.
      */
     public void clear() {
-        mPastTime[0] = 0;
+        for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) {
+            mPastTime[i][0] = 0;
+        }
     }
     
     /**
@@ -120,18 +122,21 @@
     public void addMovement(MotionEvent ev) {
         long time = ev.getEventTime();
         final int N = ev.getHistorySize();
-        for (int i=0; i<N; i++) {
-            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
-                    ev.getHistoricalEventTime(i));
+        final int pointerCount = ev.getPointerCount();
+        for (int p = 0; p < pointerCount; p++) {
+            for (int i=0; i<N; i++) {
+                addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i),
+                        ev.getHistoricalEventTime(i));
+            }
+            addPoint(p, ev.getX(p), ev.getY(p), time);
         }
-        addPoint(ev.getX(), ev.getY(), time);
     }
 
-    private void addPoint(float x, float y, long time) {
+    private void addPoint(int pos, float x, float y, long time) {
         int drop = -1;
         int i;
         if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
-        final long[] pastTime = mPastTime;
+        final long[] pastTime = mPastTime[pos];
         for (i=0; i<NUM_PAST; i++) {
             if (pastTime[i] == 0) {
                 break;
@@ -146,8 +151,8 @@
             drop = 0;
         }
         if (drop == i) drop--;
-        final float[] pastX = mPastX;
-        final float[] pastY = mPastY;
+        final float[] pastX = mPastX[pos];
+        final float[] pastY = mPastY[pos];
         if (drop >= 0) {
             if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
             final int start = drop+1;
@@ -190,44 +195,48 @@
      * must be positive.
      */
     public void computeCurrentVelocity(int units, float maxVelocity) {
-        final float[] pastX = mPastX;
-        final float[] pastY = mPastY;
-        final long[] pastTime = mPastTime;
-        
-        // Kind-of stupid.
-        final float oldestX = pastX[0];
-        final float oldestY = pastY[0];
-        final long oldestTime = pastTime[0];
-        float accumX = 0;
-        float accumY = 0;
-        int N=0;
-        while (N < NUM_PAST) {
-            if (pastTime[N] == 0) {
-                break;
+        for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) {
+            final float[] pastX = mPastX[pos];
+            final float[] pastY = mPastY[pos];
+            final long[] pastTime = mPastTime[pos];
+
+            // Kind-of stupid.
+            final float oldestX = pastX[0];
+            final float oldestY = pastY[0];
+            final long oldestTime = pastTime[0];
+            float accumX = 0;
+            float accumY = 0;
+            int N=0;
+            while (N < NUM_PAST) {
+                if (pastTime[N] == 0) {
+                    break;
+                }
+                N++;
             }
-            N++;
+            // Skip the last received event, since it is probably pretty noisy.
+            if (N > 3) N--;
+
+            for (int i=1; i < N; i++) {
+                final int dur = (int)(pastTime[i] - oldestTime);
+                if (dur == 0) continue;
+                float dist = pastX[i] - oldestX;
+                float vel = (dist/dur) * units;   // pixels/frame.
+                if (accumX == 0) accumX = vel;
+                else accumX = (accumX + vel) * .5f;
+
+                dist = pastY[i] - oldestY;
+                vel = (dist/dur) * units;   // pixels/frame.
+                if (accumY == 0) accumY = vel;
+                else accumY = (accumY + vel) * .5f;
+            }
+            mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
+                    : Math.min(accumX, maxVelocity);
+            mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
+                    : Math.min(accumY, maxVelocity);
+
+            if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
+                    + mXVelocity + " N=" + N);
         }
-        // Skip the last received event, since it is probably pretty noisy.
-        if (N > 3) N--;
-        
-        for (int i=1; i < N; i++) {
-            final int dur = (int)(pastTime[i] - oldestTime);
-            if (dur == 0) continue;
-            float dist = pastX[i] - oldestX;
-            float vel = (dist/dur) * units;   // pixels/frame.
-            if (accumX == 0) accumX = vel;
-            else accumX = (accumX + vel) * .5f;
-            
-            dist = pastY[i] - oldestY;
-            vel = (dist/dur) * units;   // pixels/frame.
-            if (accumY == 0) accumY = vel;
-            else accumY = (accumY + vel) * .5f;
-        }
-        mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity);
-        mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity);
-        
-        if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
-                + mXVelocity + " N=" + N);
     }
     
     /**
@@ -237,7 +246,7 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity() {
-        return mXVelocity;
+        return mXVelocity[0];
     }
     
     /**
@@ -247,6 +256,32 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity() {
-        return mYVelocity;
+        return mYVelocity[0];
+    }
+    
+    /**
+     * Retrieve the last computed X velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @param pos Which pointer's velocity to return.
+     * @return The previously computed X velocity.
+     * 
+     * @hide Pending API approval
+     */
+    public float getXVelocity(int pos) {
+        return mXVelocity[pos];
+    }
+    
+    /**
+     * Retrieve the last computed Y velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @param pos Which pointer's velocity to return.
+     * @return The previously computed Y velocity.
+     * 
+     * @hide Pending API approval
+     */
+    public float getYVelocity(int pos) {
+        return mYVelocity[pos];
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index df4cab0..a3d3521 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6177,7 +6177,10 @@
      * @see #getDrawingCache()
      */
     public void setDrawingCacheBackgroundColor(int color) {
-        mDrawingCacheBackgroundColor = color;
+        if (color != mDrawingCacheBackgroundColor) {
+            mDrawingCacheBackgroundColor = color;
+            mPrivateFlags &= ~DRAWING_CACHE_VALID;
+        }
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c718bac..3da18d6 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -111,7 +111,15 @@
      * flag for you on multi-line text views.
      */
     public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000;
-    
+
+    /**
+     * Flag of {@link #imeOptions}: used to request that the IME never go
+     * into fullscreen mode.  Applications need to be aware that the flag is not
+     * a guarantee, and not all IMEs will respect it.
+     * @hide
+     */
+    public static final int IME_FLAG_NO_FULLSCREEN = 0x80000000;
+
     /**
      * Generic unspecified type for {@link #imeOptions}.
      */
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index ecbc7e7..429b335 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -48,6 +48,8 @@
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
 
 /**
  * <p>Proxy for HTML5 video views.
@@ -71,6 +73,9 @@
     private static final int ENDED             = 201;
     private static final int POSTER_FETCHED    = 202;
 
+    // Timer thread -> UI thread
+    private static final int TIMEUPDATE = 300;
+
     // The C++ MediaPlayerPrivateAndroid object.
     int mNativePointer;
     // The handler for WebCore thread messages;
@@ -95,6 +100,22 @@
         private static View mProgressView;
         // The container for the progress view and video view
         private static FrameLayout mLayout;
+        // The timer for timeupate events.
+        // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
+        private static Timer mTimer;
+        private static final class TimeupdateTask extends TimerTask {
+            private HTML5VideoViewProxy mProxy;
+
+            public TimeupdateTask(HTML5VideoViewProxy proxy) {
+                mProxy = proxy;
+            }
+
+            public void run() {
+                mProxy.onTimeupdate();
+            }
+        }
+        // The spec says the timer should fire every 250 ms or less.
+        private static final int TIMEUPDATE_PERIOD = 250;  // ms
 
         private static final WebChromeClient.CustomViewCallback mCallback =
             new WebChromeClient.CustomViewCallback() {
@@ -104,6 +125,8 @@
                     // which happens when the video view is detached from its parent
                     // view. This happens in the WebChromeClient before this method
                     // is invoked.
+                    mTimer.cancel();
+                    mTimer = null;
                     mCurrentProxy.playbackEnded();
                     mCurrentProxy = null;
                     mLayout.removeView(mVideoView);
@@ -154,10 +177,23 @@
                 mProgressView.setVisibility(View.VISIBLE);
             }
             mLayout.setVisibility(View.VISIBLE);
+            mTimer = new Timer();
             mVideoView.start();
             client.onShowCustomView(mLayout, mCallback);
         }
 
+        public static boolean isPlaying(HTML5VideoViewProxy proxy) {
+            return (mCurrentProxy == proxy && mVideoView != null && mVideoView.isPlaying());
+        }
+
+        public static int getCurrentPosition() {
+            int currentPosMs = 0;
+            if (mVideoView != null) {
+                currentPosMs = mVideoView.getCurrentPosition();
+            }
+            return currentPosMs;
+        }
+
         public static void seek(int time, HTML5VideoViewProxy proxy) {
             if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) {
                 mVideoView.seekTo(time);
@@ -167,10 +203,13 @@
         public static void pause(HTML5VideoViewProxy proxy) {
             if (mCurrentProxy == proxy && mVideoView != null) {
                 mVideoView.pause();
+                mTimer.purge();
             }
         }
 
         public static void onPrepared() {
+            mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
+
             if (mProgressView == null || mLayout == null) {
                 return;
             }
@@ -211,7 +250,11 @@
         sendMessage(obtainMessage(ENDED));
     }
 
-    // Handler for the messages from WebCore thread to the UI thread.
+    public void onTimeupdate() {
+        sendMessage(obtainMessage(TIMEUPDATE));
+    }
+
+    // Handler for the messages from WebCore or Timer thread to the UI thread.
     @Override
     public void handleMessage(Message msg) {
         // This executes on the UI thread.
@@ -249,6 +292,12 @@
                 }
                 break;
             }
+            case TIMEUPDATE: {
+                if (VideoPlayer.isPlaying(this)) {
+                    sendTimeupdate();
+                }
+                break;
+            }
         }
     }
 
@@ -418,6 +467,9 @@
                         Bitmap poster = (Bitmap) msg.obj;
                         nativeOnPosterFetched(poster, mNativePointer);
                         break;
+                    case TIMEUPDATE:
+                        nativeOnTimeupdate(msg.arg1, mNativePointer);
+                        break;
                 }
             }
         };
@@ -434,6 +486,12 @@
         mWebCoreHandler.sendMessage(msg);
     }
 
+    private void sendTimeupdate() {
+        Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
+        msg.arg1 = VideoPlayer.getCurrentPosition();
+        mWebCoreHandler.sendMessage(msg);
+    }
+
     public Context getContext() {
         return mWebView.getContext();
     }
@@ -514,4 +572,5 @@
     private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
     private native void nativeOnEnded(int nativePointer);
     private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
+    private native void nativeOnTimeupdate(int position, int nativePointer);
 }
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 84a8a3c..a9d6ff6 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -124,6 +124,11 @@
         return null;
     }
 
+    // Static method called by jni.
+    private static String mimeTypeFromExtension(String extension) {
+        return getSingleton().getMimeTypeFromExtension(extension);
+    }
+
     /**
      * Return true if the given extension has a registered MIME type.
      * @param extension A file extension without the leading '.'
@@ -344,6 +349,7 @@
             sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl");
             sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl");
             sMimeTypeMap.loadEntry("application/x-shar", "shar");
+            sMimeTypeMap.loadEntry("application/x-shockwave-flash", "swf");
             sMimeTypeMap.loadEntry("application/x-stuffit", "sit");
             sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio");
             sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc");
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index b6891b1..6d0be43 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -807,19 +807,21 @@
         int maxLength = -1;
         int inputType = EditorInfo.TYPE_CLASS_TEXT
                 | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
+        int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+                | EditorInfo.IME_FLAG_NO_FULLSCREEN;
         switch (type) {
             case 1: // TEXT_AREA
                 single = false;
                 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
                         | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
                         | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
-                setImeOptions(EditorInfo.IME_ACTION_NONE);
+                imeOptions |= EditorInfo.IME_ACTION_NONE;
                 break;
             case 2: // PASSWORD
                 inPassword = true;
                 break;
             case 3: // SEARCH
-                setImeOptions(EditorInfo.IME_ACTION_SEARCH);
+                imeOptions |= EditorInfo.IME_ACTION_SEARCH;
                 break;
             case 4: // EMAIL
                 // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents EMAIL_ADDRESS
@@ -858,14 +860,14 @@
                 switch (action) {
                     // Keep in sync with CachedRoot::ImeAction
                     case 0: // NEXT
-                        setImeOptions(EditorInfo.IME_ACTION_NEXT);
+                        imeOptions |= EditorInfo.IME_ACTION_NEXT;
                         break;
                     case 1: // GO
-                        setImeOptions(EditorInfo.IME_ACTION_GO);
+                        imeOptions |= EditorInfo.IME_ACTION_GO;
                         break;
                     case -1: // FAILURE
                     case 2: // DONE
-                        setImeOptions(EditorInfo.IME_ACTION_DONE);
+                        imeOptions |= EditorInfo.IME_ACTION_DONE;
                         break;
                 }
             }
@@ -874,6 +876,7 @@
         setMaxLength(maxLength);
         setHorizontallyScrolling(single);
         setInputType(inputType);
+        setImeOptions(imeOptions);
         setInPassword(inPassword);
         AutoCompleteAdapter adapter = null;
         setAdapterCustom(adapter);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b10d786..09ed931 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2997,8 +2997,11 @@
 
     private void drawLayers(Canvas canvas) {
         if (mRootLayer != 0) {
-            float scrollY = Math.max(mScrollY - getTitleHeight(), 0);
+            int scrollY = computeVerticalScrollOffset();
+            int viewHeight = getHeight() - getVisibleTitleHeight();
+
             nativeDrawLayers(mRootLayer, mScrollX, scrollY,
+                             getWidth(), viewHeight,
                              mActualScale, canvas);
         }
     }
@@ -3892,7 +3895,9 @@
             public void run() {
                 // we always force, in case our height changed, in which case we
                 // still want to send the notification over to webkit
-                setNewZoomScale(mActualScale, true);
+                if (mWebViewCore != null) {
+                    setNewZoomScale(mActualScale, true);
+                }
             }
         });
     }
@@ -5127,12 +5132,12 @@
             mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
                     motionUpData);
         } else {
-            doMotionUp(contentX, contentY, false);
+            doMotionUp(contentX, contentY);
         }
     }
 
-    private void doMotionUp(int contentX, int contentY, boolean useNavCache) {
-        if (nativeMotionUp(contentX, contentY, useNavCache ? mNavSlop : 0)) {
+    private void doMotionUp(int contentX, int contentY) {
+        if (nativeMotionUp(contentX, contentY, mNavSlop)) {
             if (mLogEvent) {
                 Checkin.updateStats(mContext.getContentResolver(),
                         Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
@@ -5761,7 +5766,7 @@
                     break;
 
                 case DO_MOTION_UP:
-                    doMotionUp(msg.arg1, msg.arg2, (Boolean) msg.obj);
+                    doMotionUp(msg.arg1, msg.arg2);
                     break;
 
                 case SHOW_FULLSCREEN:
@@ -6352,7 +6357,8 @@
     private native boolean  nativeLayersHaveAnimations(int layer);
     private native void     nativeUpdateLayers(int layer, int updates);
     private native void     nativeDrawLayers(int layer,
-                                             float scrollX, float scrollY,
+                                             int scrollX, int scrollY,
+                                             int width, int height,
                                              float scale, Canvas canvas);
     private native void     nativeDrawMatches(Canvas canvas);
     private native void     nativeDrawSelectionPointer(Canvas content,
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 463f14e..949b318 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1316,13 +1316,14 @@
 
                         case VALID_NODE_BOUNDS: {
                             MotionUpData motionUpData = (MotionUpData) msg.obj;
-                            boolean result = nativeValidNodeAndBounds(
+                            if (!nativeValidNodeAndBounds(
                                     motionUpData.mFrame, motionUpData.mNode,
-                                    motionUpData.mBounds);
+                                    motionUpData.mBounds)) {
+                                nativeUpdateFrameCache();
+                            }
                             Message message = mWebView.mPrivateHandler
                                     .obtainMessage(WebView.DO_MOTION_UP,
-                                    motionUpData.mX, motionUpData.mY,
-                                    new Boolean(result));
+                                    motionUpData.mX, motionUpData.mY);
                             mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
                                     message);
                             break;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e241c77..b795080 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1567,7 +1567,12 @@
 
         if (!hasWindowFocus) {
             setChildrenDrawingCacheEnabled(false);
-            removeCallbacks(mFlingRunnable);
+            if (mFlingRunnable != null) {
+                removeCallbacks(mFlingRunnable);
+                // let the fling runnable report it's new state which
+                // should be idle
+                mFlingRunnable.endFling();
+            }
             // Always hide the type filter
             dismissPopup();
 
@@ -3245,7 +3250,14 @@
      * @param color The background color
      */
     public void setCacheColorHint(int color) {
-        mCacheColorHint = color;
+        if (color != mCacheColorHint) {
+            mCacheColorHint = color;
+            int count = getChildCount();
+            for (int i = 0; i < count; i++) {
+                getChildAt(i).setDrawingCacheBackgroundColor(color);
+            }
+            mRecycler.setCacheColorHint(color);
+        }
     }
 
     /**
@@ -3665,5 +3677,38 @@
                 }
             }
         }
+
+        /**
+         * Updates the cache color hint of all known views.
+         *
+         * @param color The new cache color hint.
+         */
+        void setCacheColorHint(int color) {
+            if (mViewTypeCount == 1) {
+                final ArrayList<View> scrap = mCurrentScrap;
+                final int scrapCount = scrap.size();
+                for (int i = 0; i < scrapCount; i++) {
+                    scrap.get(i).setDrawingCacheBackgroundColor(color);
+                }
+            } else {
+                final int typeCount = mViewTypeCount;
+                for (int i = 0; i < typeCount; i++) {
+                    final ArrayList<View> scrap = mScrapViews[i];
+                    final int scrapCount = scrap.size();
+                    for (int j = 0; j < scrapCount; j++) {
+                        scrap.get(i).setDrawingCacheBackgroundColor(color);
+                    }
+                }
+            }
+            // Just in case this is called during a layout pass
+            final View[] activeViews = mActiveViews;
+            final int count = activeViews.length;
+            for (int i = 0; i < count; ++i) {
+                final View victim = activeViews[i];
+                if (victim != null) {
+                    victim.setDrawingCacheBackgroundColor(color);
+                }
+            }
+        }
     }
 }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index fad994b..1aa1df3 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -291,6 +291,8 @@
         }        
     }
 
+    // TODO: we need to find another way to implement RelativeLayout
+    // This implementation cannot handle every case
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mDirtyHierarchy) {
@@ -438,6 +440,10 @@
                         final int[] rules = params.getRules();
                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                             centerHorizontal(child, params, width);
+                        } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
+                            final int childWidth = child.getMeasuredWidth();
+                            params.mLeft = width - mPaddingRight - childWidth;
+                            params.mRight = params.mLeft + childWidth;
                         }
                     }
                 }
@@ -464,6 +470,10 @@
                         final int[] rules = params.getRules();
                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                             centerVertical(child, params, height);
+                        } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
+                            final int childHeight = child.getMeasuredHeight();
+                            params.mTop = height - mPaddingBottom - childHeight;
+                            params.mBottom = params.mTop + childHeight;
                         }
                     }
                 }
@@ -673,7 +683,7 @@
                 params.mRight = params.mLeft + child.getMeasuredWidth();
             }
         }
-        return false;
+        return rules[ALIGN_PARENT_RIGHT] != 0;
     }
 
     private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
@@ -702,7 +712,7 @@
                 params.mBottom = params.mTop + child.getMeasuredHeight();
             }
         }
-        return false;
+        return rules[ALIGN_PARENT_BOTTOM] != 0;
     }
 
     private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) {
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 000f6c4..2b07ae6 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -102,7 +102,7 @@
                 .getService("mount"));
             if (mountService != null) {
                 try {
-                    mountService.formatMedia(Environment.getExternalStorageDirectory().toString());
+                    mountService.formatVolume(Environment.getExternalStorageDirectory().toString());
                 } catch (RemoteException e) {
                 }
             }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 58056bd..3c5a753 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -38,8 +38,9 @@
 public class LockPatternUtils {
 
     private static final String TAG = "LockPatternUtils";
-    
+
     private static final String LOCK_PATTERN_FILE = "/system/gesture.key";
+    private static final String LOCK_PASSWORD_FILE = "/system/password.key";
 
     /**
      * The maximum number of incorrect attempts before the user is prevented
@@ -70,20 +71,32 @@
     public static final int MIN_LOCK_PATTERN_SIZE = 4;
 
     /**
+     * Type of password being stored.
+     * pattern = pattern screen
+     * pin = digit-only password
+     * password = alphanumeric password
+     */
+    public static final int MODE_PATTERN = 0;
+    public static final int MODE_PIN = 1;
+    public static final int MODE_PASSWORD = 2;
+
+    /**
      * The minimum number of dots the user must include in a wrong pattern
      * attempt for it to be counted against the counts that affect
      * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
      */
-    public static final int MIN_PATTERN_REGISTER_FAIL = 3;    
+    public static final int MIN_PATTERN_REGISTER_FAIL = 3;
 
     private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
     private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
-    private final static String PATTERN_EVER_CHOSEN = "lockscreen.patterneverchosen";
+    private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
+    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
 
     private final ContentResolver mContentResolver;
 
     private static String sLockPatternFilename;
-    
+    private static String sLockPasswordFilename;
+
     /**
      * @param contentResolver Used to look up and save settings.
      */
@@ -91,16 +104,19 @@
         mContentResolver = contentResolver;
         // Initialize the location of gesture lock file
         if (sLockPatternFilename == null) {
-            sLockPatternFilename = android.os.Environment.getDataDirectory() 
+            sLockPatternFilename = android.os.Environment.getDataDirectory()
                     .getAbsolutePath() + LOCK_PATTERN_FILE;
+            sLockPasswordFilename = android.os.Environment.getDataDirectory()
+                    .getAbsolutePath() + LOCK_PASSWORD_FILE;
         }
+
     }
 
     /**
      * Check to see if a pattern matches the saved pattern.  If no pattern exists,
      * always returns true.
      * @param pattern The pattern to check.
-     * @return Whether the pattern matchees the stored one.
+     * @return Whether the pattern matches the stored one.
      */
     public boolean checkPattern(List<LockPatternView.Cell> pattern) {
         try {
@@ -122,13 +138,40 @@
     }
 
     /**
-     * Check to see if the user has stored a lock pattern.
-     * @return Whether a saved pattern exists.
+     * Check to see if a password matches the saved password.  If no password exists,
+     * always returns true.
+     * @param password The password to check.
+     * @return Whether the password matches the stored one.
      */
-    public boolean savedPatternExists() {
+    public boolean checkPassword(String password) {
+        try {
+            // Read all the bytes from the file
+            RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
+            final byte[] stored = new byte[(int) raf.length()];
+            int got = raf.read(stored, 0, stored.length);
+            raf.close();
+            if (got <= 0) {
+                return true;
+            }
+            // Compare the hash from the file with the entered password's hash
+            return Arrays.equals(stored, LockPatternUtils.passwordToHash(password));
+        } catch (FileNotFoundException fnfe) {
+            return true;
+        } catch (IOException ioe) {
+            return true;
+        }
+    }
+
+    /**
+     * Checks to see if the given file exists and contains any data. Returns true if it does,
+     * false otherwise.
+     * @param filename
+     * @return true if file exists and is non-empty.
+     */
+    private boolean nonEmptyFileExists(String filename) {
         try {
             // Check if we can read a byte from the file
-            RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
+            RandomAccessFile raf = new RandomAccessFile(filename, "r");
             byte first = raf.readByte();
             raf.close();
             return true;
@@ -140,13 +183,29 @@
     }
 
     /**
+     * Check to see if the user has stored a lock pattern.
+     * @return Whether a saved pattern exists.
+     */
+    public boolean savedPatternExists() {
+        return nonEmptyFileExists(sLockPatternFilename);
+    }
+
+    /**
+     * Check to see if the user has stored a lock pattern.
+     * @return Whether a saved pattern exists.
+     */
+    public boolean savedPasswordExists() {
+        return nonEmptyFileExists(sLockPasswordFilename);
+    }
+
+    /**
      * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
      * currently cleared.
      *
      * @return True if the user has ever chosen a pattern.
      */
     public boolean isPatternEverChosen() {
-        return getBoolean(PATTERN_EVER_CHOSEN);
+        return getBoolean(PATTERN_EVER_CHOSEN_KEY);
     }
 
     /**
@@ -166,7 +225,8 @@
                 raf.write(hash, 0, hash.length);
             }
             raf.close();
-            setBoolean(PATTERN_EVER_CHOSEN, true);
+            setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
+            setLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
         } catch (FileNotFoundException fnfe) {
             // Cant do much, unless we want to fail over to using the settings provider
             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
@@ -177,6 +237,38 @@
     }
 
     /**
+     * Save a lock password.
+     * @param password The password to save
+     */
+    public void saveLockPassword(String password) {
+        // Compute the hash
+        boolean numericHint = password != null ? TextUtils.isDigitsOnly(password) : false;
+        final byte[] hash  = LockPatternUtils.passwordToHash(password);
+        try {
+            // Write the hash to file
+            RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
+            // Truncate the file if pattern is null, to clear the lock
+            if (password == null) {
+                raf.setLength(0);
+            } else {
+                raf.write(hash, 0, hash.length);
+            }
+            raf.close();
+            setLong(PASSWORD_TYPE_KEY, numericHint ? MODE_PIN : MODE_PASSWORD);
+        } catch (FileNotFoundException fnfe) {
+            // Cant do much, unless we want to fail over to using the settings provider
+            Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
+        } catch (IOException ioe) {
+            // Cant do much
+            Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
+        }
+    }
+
+    public int getPasswordMode() {
+        return (int) getLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
+    }
+
+    /**
      * Deserialize a pattern.
      * @param string The pattern serialized with {@link #patternToString}
      * @return The pattern.
@@ -210,7 +302,7 @@
         }
         return new String(res);
     }
-    
+
     /*
      * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
      * at least a second level of protection. First level is that the file
@@ -218,11 +310,11 @@
      * @param pattern the gesture pattern.
      * @return the hash of the pattern in a byte array.
      */
-    static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
+    private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
         if (pattern == null) {
             return null;
         }
-        
+
         final int patternSize = pattern.size();
         byte[] res = new byte[patternSize];
         for (int i = 0; i < patternSize; i++) {
@@ -238,11 +330,55 @@
         }
     }
 
+    /*
+     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+     * Not the most secure, but it is at least a second level of protection. First level is that
+     * the file is in a location only readable by the system process.
+     * @param password the gesture pattern.
+     * @return the hash of the pattern in a byte array.
+     */
+     public static byte[] passwordToHash(String password) {
+        if (password == null) {
+            return null;
+        }
+        String algo = null;
+        byte[] hashed = null;
+        try {
+            long salt = 0x2374868151054924L; // TODO: make this unique to device
+            byte[] saltedPassword = (password + Long.toString(salt)).getBytes();
+            byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
+            byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
+            hashed = (toHex(sha1) + toHex(md5)).getBytes();
+        } catch (NoSuchAlgorithmException e) {
+            Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
+        }
+        return hashed;
+    }
+
+    private static String toHex(byte[] ary) {
+        final String hex = "0123456789ABCDEF";
+        String ret = "";
+        for (int i = 0; i < ary.length; i++) {
+            ret += hex.charAt((ary[i] >> 4) & 0xf);
+            ret += hex.charAt(ary[i] & 0xf);
+        }
+        return ret;
+    }
+
+    /**
+     * @return Whether the lock password is enabled.
+     */
+    public boolean isLockPasswordEnabled() {
+        long mode = getLong(PASSWORD_TYPE_KEY, 0);
+        return savedPasswordExists() && (mode == MODE_PASSWORD || mode == MODE_PIN);
+    }
+
     /**
      * @return Whether the lock pattern is enabled.
      */
     public boolean isLockPatternEnabled() {
-        return getBoolean(Settings.System.LOCK_PATTERN_ENABLED);
+        return getBoolean(Settings.System.LOCK_PATTERN_ENABLED)
+                && getLong(PASSWORD_TYPE_KEY, MODE_PATTERN) == MODE_PATTERN;
     }
 
     /**
@@ -361,5 +497,10 @@
         android.provider.Settings.System.putLong(mContentResolver, systemSettingKey, value);
     }
 
-
+    public boolean isSecure() {
+        long mode = getPasswordMode();
+        boolean secure = mode == MODE_PATTERN && isLockPatternEnabled() && savedPatternExists()
+            || (mode == MODE_PIN || mode == MODE_PASSWORD) && savedPasswordExists();
+        return secure;
+    }
 }
diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp
index df5edba..a46c2dd 100644
--- a/core/jni/android_os_Power.cpp
+++ b/core/jni/android_os_Power.cpp
@@ -105,7 +105,7 @@
     { "setLastUserActivityTimeout", "(J)I", (void*)setLastUserActivityTimeout },
     { "setScreenState", "(Z)I", (void*)setScreenState },
     { "shutdown", "()V", (void*)android_os_Power_shutdown },
-    { "reboot", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot },
+    { "rebootNative", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot },
 };
 
 int register_android_os_Power(JNIEnv *env)
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
new file mode 100644
index 0000000..5bc034b
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="@android:color/background_dark">
+
+    <!-- displays dots as user enters pin -->
+    <LinearLayout android:id="@+id/pinDisplayGroup"
+        android:orientation="horizontal"
+        android:layout_centerInParent="true"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:addStatesFromChildren="true"
+        android:gravity="center_vertical"
+        android:baselineAligned="false"
+        android:paddingRight="0dip"
+        android:layout_marginRight="30dip"
+        android:layout_marginLeft="30dip"
+        android:layout_marginTop="6dip"
+        android:background="@android:drawable/edit_text">
+
+        <EditText android:id="@+id/pinDisplay"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="fill_parent"
+            android:maxLines="1"
+            android:background="@null"
+            android:textSize="32sp"
+            android:inputType="textPassword"
+        />
+
+        <ImageButton android:id="@+id/backspace"
+             android:src="@android:drawable/ic_input_delete"
+             android:layout_width="wrap_content"
+             android:layout_height="fill_parent"
+             android:layout_marginTop="2dip"
+             android:layout_marginRight="2dip"
+             android:layout_marginBottom="2dip"
+             android:gravity="center"
+        />
+
+    </LinearLayout>
+
+    <!-- header text ('Enter Pin Code') -->
+    <TextView android:id="@+id/headerText"
+        android:layout_above="@id/pinDisplayGroup"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="30dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+    />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_alignParentBottom="true"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="8dip"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip">
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:textSize="18sp"
+            />
+
+        <Button android:id="@+id/emergencyCall"
+            android:text="@android:string/lockscreen_emergency_call"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+        />
+    </LinearLayout>
+
+    <!-- Not currently visible on this screen -->
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_marginTop="6dip"
+        android:layout_alignParentRight="true"
+        android:layout_marginRight="8dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:visibility="gone"
+    />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
new file mode 100644
index 0000000..a7814af
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+    android:background="#70000000"
+    android:gravity="center_horizontal">
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content">
+
+            <com.android.internal.widget.DigitalClock android:id="@+id/time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="6dip"
+                android:layout_marginLeft="6dip"
+                android:layout_alignParentTop="true"
+                android:layout_alignParentLeft="true">
+
+                <TextView android:id="@+id/timeDisplay"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="bottom"
+                    android:singleLine="true"
+                    android:ellipsize="none"
+                    android:textSize="56sp"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:shadowColor="#C0000000"
+                    android:shadowDx="0"
+                    android:shadowDy="0"
+                    android:shadowRadius="3.0"
+                    android:layout_marginBottom="6dip"
+                />
+
+                <TextView android:id="@+id/am_pm"
+                    android:layout_width="wrap_content"
+                    android:layout_height="fill_parent"
+                    android:gravity="bottom"
+                    android:singleLine="true"
+                    android:ellipsize="none"
+                    android:textSize="18sp"
+                    android:layout_marginLeft="4dip"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:shadowColor="#C0000000"
+                    android:shadowDx="0"
+                    android:shadowDy="0"
+                    android:shadowRadius="3.0"
+                />
+
+            </com.android.internal.widget.DigitalClock>
+
+            <TextView android:id="@+id/carrier"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_marginTop="6dip"
+                android:layout_alignParentRight="true"
+                android:layout_marginRight="8dip"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+            />
+
+        </RelativeLayout>
+
+        <!-- password entry -->
+        <LinearLayout
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginRight="6dip"
+            android:layout_marginLeft="6dip"
+            android:gravity="center_vertical"
+            android:hint="@android:string/keyguard_password_enter_password_code"
+            android:background="@android:drawable/edit_text">
+
+            <!-- displays dots as user enters pin -->
+            <TextView android:id="@+id/pinDisplay"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:maxLines="1"
+                android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                android:textStyle="bold"
+                android:inputType="textPassword"
+            />
+
+            <ImageButton android:id="@+id/backspace"
+                android:src="@android:drawable/ic_input_delete"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="-3dip"
+                android:layout_marginBottom="-3dip"
+            />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <include
+        android:id="@+id/keyPad"
+        layout="@android:layout/twelve_key_entry"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/topDisplayGroup"
+        android:layout_marginTop="10dip"
+    />
+
+    <!-- spacer below keypad -->
+    <View
+        android:id="@+id/spacerBottom"
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="6dip"
+        android:layout_above="@id/emergencyCall"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+
+    <!-- The emergency button should take the rest of the space and be centered vertically -->
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@android:drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@android:string/lockscreen_emergency_call"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_rotary_unlock.xml b/core/res/res/layout/keyguard_screen_rotary_unlock.xml
deleted file mode 100644
index 8a7553c..0000000
--- a/core/res/res/layout/keyguard_screen_rotary_unlock.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the general lock screen which shows information about the
-  state of the device, as well as instructions on how to get past it
-  depending on the state of the device.  It is the same for landscape
-  and portrait.-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent"
-             android:id="@+id/root"
-                 >
-
-<RelativeLayout
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="#70000000"
-        >
-
-    <TextView
-        android:id="@+id/carrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="20dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        />
-
-    <TextView
-        android:id="@+id/time"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/carrier"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="25dip"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="55sp"
-        />
-
-    <TextView
-        android:id="@+id/date"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/time"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="-12dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        />
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="1dip"
-        android:layout_marginTop="10dip"
-        android:layout_below="@id/date"
-        android:background="@android:drawable/divider_horizontal_dark"
-            />
-
-    <TextView
-        android:id="@+id/status1"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/divider"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="6dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:drawablePadding="4dip"
-        />
-
-    <TextView
-        android:id="@+id/status2"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/status1"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="6dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:drawablePadding="4dip"
-        />
-
-    <TextView
-        android:id="@+id/screenLocked"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/status2"
-        android:layout_centerHorizontal="true"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:gravity="center"
-        android:layout_marginTop="12dip"
-        />
-
-    <!-- By having the rotary selector hang from the top, we get a layout more
-         robust for different screen sizes.  On hvga, the widget should be flush with the bottom.-->
-    <com.android.internal.widget.RotarySelector
-        android:id="@+id/rotary"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_centerHorizontal="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginTop="286dip"
-        />
-
-    <!-- emergency call button shown when sim is missing or PUKd -->
-    <Button
-        android:id="@+id/emergencyCallButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/screenLocked"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="24dip"
-        android:drawableLeft="@drawable/ic_emergency"
-        android:drawablePadding="8dip"
-       />
-
-</RelativeLayout>
-
-</FrameLayout>
diff --git a/core/res/res/layout/keyguard_screen_rotary_unlock_land.xml b/core/res/res/layout/keyguard_screen_rotary_unlock_land.xml
deleted file mode 100644
index 898aea6..0000000
--- a/core/res/res/layout/keyguard_screen_rotary_unlock_land.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the general lock screen which shows information about the
-  state of the device, as well as instructions on how to get past it
-  depending on the state of the device.-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             xmlns:rotaryunlock="http://schemas.android.com/apk/res/com.android.rotaryunlock"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent"
-             android:id="@+id/root"
-                 >
-<LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="#70000000"
-        >
-
-    <!-- left side -->
-    <RelativeLayout
-            android:orientation="vertical"
-            android:layout_width="0dip"
-            android:layout_height="match_parent"
-            android:layout_weight="1.0"
-            android:gravity="center_horizontal"
-            >
-
-        <TextView
-            android:id="@+id/carrier"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="20dip"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="?android:attr/textColorSecondary"
-            />
-
-        <TextView
-            android:id="@+id/time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/carrier"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="25dip"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textSize="55sp"
-            />
-
-        <TextView
-            android:id="@+id/date"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/time"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="-12dip"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            />
-
-        <View
-            android:id="@+id/divider"
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:layout_marginTop="10dip"
-            android:layout_below="@id/date"
-            android:background="@android:drawable/divider_horizontal_dark"
-                />
-
-        <TextView
-            android:id="@+id/status1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/divider"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="6dip"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="?android:attr/textColorSecondary"
-            android:drawablePadding="4dip"
-            />
-
-        <TextView
-            android:id="@+id/status2"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/status1"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="6dip"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="?android:attr/textColorSecondary"
-            android:drawablePadding="4dip"
-            />
-
-        <TextView
-            android:id="@+id/screenLocked"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/status2"
-            android:layout_centerHorizontal="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="?android:attr/textColorSecondary"
-            android:gravity="center"
-            android:layout_marginTop="12dip"
-            />
-        <!-- emergency call button shown when sim is missing or PUKd -->
-        <Button
-            android:id="@+id/emergencyCallButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/screenLocked"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="24dip"
-            android:drawableLeft="@drawable/ic_emergency"
-            android:drawablePadding="8dip"
-           />
-    </RelativeLayout>
-
-
-    <!-- right side -->
-    <com.android.internal.widget.RotarySelector
-        android:id="@+id/rotary"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        />
-
-
-</LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
index dad994f..1f7f8f7 100644
--- a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
@@ -68,170 +68,14 @@
 
     </LinearLayout>
 
-    <!-- Keypad section -->
-    <LinearLayout android:id="@+id/keyPad"
-        android:layout_width="match_parent"
+    <include
+        android:id="@+id/keyPad"
+        layout="@android:layout/twelve_key_entry"
+        android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_below="@id/topDisplayGroup"
         android:layout_marginTop="10dip"
-        android:orientation="vertical">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="64dip"
-            android:layout_marginLeft="2dip"
-            android:layout_marginRight="2dip"
-            android:orientation="horizontal">
-
-            <Button android:id="@+id/one"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/two"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/three"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="64dip"
-            android:layout_marginLeft="2dip"
-            android:layout_marginRight="2dip"
-            android:orientation="horizontal">
-
-            <Button android:id="@+id/four"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/five"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/six"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="64dip"
-            android:layout_marginLeft="2dip"
-            android:layout_marginRight="2dip"
-            android:orientation="horizontal">
-
-            <Button android:id="@+id/seven"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/eight"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/nine"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="64dip"
-            android:layout_marginLeft="2dip"
-            android:layout_marginRight="2dip"
-            android:orientation="horizontal">
-
-            <Button android:id="@+id/ok"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textStyle="bold"
-                android:text="@android:string/ok"
-            />
-
-            <Button android:id="@+id/zero"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold"
-            />
-
-            <Button android:id="@+id/cancel"
-                android:layout_width="0sp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:layout_marginLeft="2dip"
-                android:layout_marginRight="2dip"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textStyle="bold"
-                android:text="@android:string/cancel"
-            />
-        </LinearLayout>
-
-    <!-- end keypad -->
-    </LinearLayout>
+    />
 
     <!-- spacer below keypad -->
     <View
diff --git a/core/res/res/layout/twelve_key_entry.xml b/core/res/res/layout/twelve_key_entry.xml
new file mode 100644
index 0000000..46301cd
--- /dev/null
+++ b/core/res/res/layout/twelve_key_entry.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is not a standalone element it can be included into apps that need 12-key input -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="64dip"
+        android:layout_marginLeft="2dip"
+        android:layout_marginRight="2dip"
+        android:orientation="horizontal">
+
+        <Button android:id="@+id/one"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/two"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/three"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="64dip"
+        android:layout_marginLeft="2dip"
+        android:layout_marginRight="2dip"
+        android:orientation="horizontal">
+
+        <Button android:id="@+id/four"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/five"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/six"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="64dip"
+        android:layout_marginLeft="2dip"
+        android:layout_marginRight="2dip"
+        android:orientation="horizontal">
+
+        <Button android:id="@+id/seven"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/eight"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/nine"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="64dip"
+        android:layout_marginLeft="2dip"
+        android:layout_marginRight="2dip"
+        android:orientation="horizontal">
+
+        <Button android:id="@+id/ok"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textStyle="bold"
+            android:text="@android:string/ok"
+        />
+
+        <Button android:id="@+id/zero"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textStyle="bold"
+        />
+
+        <Button android:id="@+id/cancel"
+            android:layout_width="0sp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:layout_marginLeft="2dip"
+            android:layout_marginRight="2dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textStyle="bold"
+            android:text="@android:string/cancel"
+        />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 66f0e82..53bb586 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -131,6 +131,7 @@
          the first component from this list which is found to be installed is set as the
          preferred activity. -->
     <string-array name="default_web_search_providers">
+        <item>com.android.quicksearchbox/com.android.googlesearch.GoogleSearch</item>
         <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
         <item>com.android.googlesearch/.GoogleSearch</item>
         <item>com.android.websearch/.Search.1</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 047115c..539db83 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1325,10 +1325,14 @@
     <!-- Attbution of a contact status update, when the time of update is known -->
     <string name="contact_status_update_attribution_with_date"><xliff:g id="date" example="3 hours ago">%1$s</xliff:g> via <xliff:g id="source" example="Google Talk">%2$s</xliff:g></string>
 
-    <!-- Instructions telling the user to enter their pin to unlock the keyguard.
+    <!-- Instructions telling the user to enter their SIM PIN to unlock the keyguard.
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_pin_code">Enter PIN code</string>
 
+    <!-- Instructions telling the user to enter their PIN password to unlock the keyguard.
+         Displayed in one line in a large font.  -->
+    <string name="keyguard_password_enter_password_code">Enter password to unlock</string>
+
     <!-- Instructions telling the user that they entered the wrong pin while trying
          to unlock the keyguard.  Displayed in one line in a large font.  -->
     <string name="keyguard_password_wrong_pin_code">Incorrect PIN code!</string>
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 115dd62..7d100eb 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -39,6 +39,10 @@
         mType = t;
     }
 
+    public Type getType() {
+        return mType;
+    }
+
     public void uploadToTexture(int baseMipLevel) {
         mRS.validate();
         mRS.validateSurface();
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 9d70cb2..1614ec5 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -111,12 +111,13 @@
             mOutputs[mOutputCount++] = e;
         }
 
-        public void addConstant(Type t) throws IllegalStateException {
+        public int addConstant(Type t) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mConstantCount >= MAX_CONSTANT) {
                 throw new IllegalArgumentException("Max input count exceeded.");
             }
-            mConstants[mConstantCount++] = t;
+            mConstants[mConstantCount] = t;
+            return mConstantCount++;
         }
 
         public void setTextureCount(int count) throws IllegalArgumentException {
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index cfc205c..113c452 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -53,6 +53,7 @@
     METADATA_KEY_VIDEO_HEIGHT    = 19,
     METADATA_KEY_VIDEO_WIDTH     = 20,
     METADATA_KEY_WRITER          = 21,
+    METADATA_KEY_MIMETYPE        = 22,
     // Add more here...
 };
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index b843cd9..f88666a 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,8 @@
 
 class DataSource : public RefBase {
 public:
+    static sp<DataSource> CreateFromURI(const char *uri);
+
     DataSource() {}
 
     virtual status_t initCheck() const = 0;
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index d56d4b3..4bc996e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -43,6 +43,10 @@
     virtual sp<MetaData> getTrackMetaData(
             size_t index, uint32_t flags = 0) = 0;
 
+    // Return container specific meta-data. The default implementation
+    // returns an empty metadata object.
+    virtual sp<MetaData> getMetaData();
+
 protected:
     MediaExtractor() {}
     virtual ~MediaExtractor() {}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index c6ac6c2..2bc2734 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -48,6 +48,18 @@
     kKeyBufferID          = 'bfID',
     kKeyMaxInputSize      = 'inpS',
     kKeyThumbnailTime     = 'thbT',  // int64_t (usecs)
+
+    kKeyAlbum             = 'albu',  // cstring
+    kKeyArtist            = 'arti',  // cstring
+    kKeyComposer          = 'comp',  // cstring
+    kKeyGenre             = 'genr',  // cstring
+    kKeyTitle             = 'titl',  // cstring
+    kKeyYear              = 'year',  // cstring
+    kKeyAlbumArt          = 'albA',  // compressed image data
+    kKeyAlbumArtMIME      = 'alAM',  // cstring
+    kKeyAuthor            = 'auth',  // cstring
+    kKeyCDTrackNumber     = 'cdtr',  // cstring
+    kKeyDate              = 'date',  // cstring
 };
 
 enum {
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index b7639be..656a3c3 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -95,7 +95,9 @@
 
 Program::~Program()
 {
-    bindAllocation(NULL);
+    for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
+        bindAllocation(NULL, ct);
+    }
 
     delete[] mInputElements;
     delete[] mOutputElements;
@@ -106,15 +108,15 @@
 }
 
 
-void Program::bindAllocation(Allocation *alloc)
+void Program::bindAllocation(Allocation *alloc, uint32_t slot)
 {
-    if (mConstants.get() == alloc) {
+    if (mConstants[slot].get() == alloc) {
         return;
     }
-    if (mConstants.get()) {
-        mConstants.get()->removeProgramToDirty(this);
+    if (mConstants[slot].get()) {
+        mConstants[slot].get()->removeProgramToDirty(this);
     }
-    mConstants.set(alloc);
+    mConstants[slot].set(alloc);
     if (alloc) {
         alloc->addProgramToDirty(this);
     }
@@ -239,7 +241,7 @@
 void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants)
 {
     Program *p = static_cast<Program *>(vp);
-    p->bindAllocation(static_cast<Allocation *>(constants));
+    p->bindAllocation(static_cast<Allocation *>(constants), slot);
 }
 
 void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a)
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index 4bb7802..a34e89f 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -39,7 +39,7 @@
                        const uint32_t * params, uint32_t paramLength);
     virtual ~Program();
 
-    void bindAllocation(Allocation *);
+    void bindAllocation(Allocation *, uint32_t slot);
     virtual void createShader();
 
     bool isUserProgram() const {return mUserShader.size() > 0;}
@@ -69,7 +69,7 @@
     uint32_t mOutputCount;
     uint32_t mConstantCount;
 
-    ObjectBaseRef<Allocation> mConstants;
+    ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
 
     mutable bool mDirty;
     String8 mShader;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 8e59bc2..cf6d7fc 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -69,7 +69,7 @@
     }
     state->mLast.set(this);
 
-    const float *f = static_cast<const float *>(mConstants->getPtr());
+    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
 
     glMatrixMode(GL_TEXTURE);
     if (mTextureMatrixEnable) {
@@ -116,16 +116,36 @@
 {
     mShader.setTo("");
 
-    for (uint32_t ct=0; ct < mUniformCount; ct++) {
-        mShader.append("uniform mat4 ");
-        mShader.append(mUniformNames[ct]);
-        mShader.append(";\n");
-    }
-
     mShader.append("varying vec4 varColor;\n");
     mShader.append("varying vec4 varTex0;\n");
 
     if (mUserShader.length() > 1) {
+        mShader.append("uniform mat4 ");
+        mShader.append(mUniformNames[0]);
+        mShader.append(";\n");
+
+        for (uint32_t ct=0; ct < mConstantCount; ct++) {
+            const Element *e = mConstantTypes[ct]->getElement();
+            for (uint32_t field=0; field < e->getFieldCount(); field++) {
+                const Element *f = e->getField(field);
+
+                // Cannot be complex
+                rsAssert(!f->getFieldCount());
+                switch(f->getComponent().getVectorSize()) {
+                case 1: mShader.append("uniform float UNI_"); break;
+                case 2: mShader.append("uniform vec2 UNI_"); break;
+                case 3: mShader.append("uniform vec3 UNI_"); break;
+                case 4: mShader.append("uniform vec4 UNI_"); break;
+                default:
+                    rsAssert(0);
+                }
+
+                mShader.append(e->getFieldName(field));
+                mShader.append(";\n");
+            }
+        }
+
+
         for (uint32_t ct=0; ct < mInputCount; ct++) {
             const Element *e = mInputElements[ct].get();
             for (uint32_t field=0; field < e->getFieldCount(); field++) {
@@ -148,6 +168,12 @@
         }
         mShader.append(mUserShader);
     } else {
+        for (uint32_t ct=0; ct < mUniformCount; ct++) {
+            mShader.append("uniform mat4 ");
+            mShader.append(mUniformNames[ct]);
+            mShader.append(";\n");
+        }
+
         for (uint32_t ct=VertexArray::POSITION; ct < mAttribCount; ct++) {
             mShader.append("attribute vec4 ");
             mShader.append(mAttribNames[ct]);
@@ -155,12 +181,12 @@
         }
 
         mShader.append("void main() {\n");
-        mShader.append("  gl_Position = uni_MVP * ATTRIB_Position;\n");
+        mShader.append("  gl_Position = UNI_MVP * ATTRIB_Position;\n");
         mShader.append("  gl_PointSize = ATTRIB_PointSize.x;\n");
 
         mShader.append("  varColor = ATTRIB_Color;\n");
         if (mTextureMatrixEnable) {
-            mShader.append("  varTex0 = uni_TexMatrix * ATTRIB_Texture;\n");
+            mShader.append("  varTex0 = UNI_TexMatrix * ATTRIB_Texture;\n");
         } else {
             mShader.append("  varTex0 = ATTRIB_Texture;\n");
         }
@@ -180,7 +206,7 @@
 
     glVertexAttrib4f(1, state->color[0], state->color[1], state->color[2], state->color[3]);
 
-    const float *f = static_cast<const float *>(mConstants->getPtr());
+    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
 
     Matrix mvp;
     mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
@@ -194,6 +220,54 @@
                            &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
     }
 
+    uint32_t uidx = 1;
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        Allocation *alloc = mConstants[ct+1].get();
+        if (!alloc) {
+            continue;
+        }
+
+        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
+        const Element *e = mConstantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            uint32_t offset = e->getFieldOffsetBytes(field);
+            int32_t slot = sc->vtxUniformSlot(uidx);
+
+            const float *fd = reinterpret_cast<const float *>(&data[offset]);
+
+            //LOGE("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i", slot, offset, ct, field, uidx);
+            if (slot >= 0) {
+                switch(f->getComponent().getVectorSize()) {
+                case 1:
+                    //LOGE("Uniform 1 = %f", fd[0]);
+                    glUniform1fv(slot, 1, fd);
+                    break;
+                case 2:
+                    //LOGE("Uniform 2 = %f %f", fd[0], fd[1]);
+                    glUniform2fv(slot, 1, fd);
+                    break;
+                case 3:
+                    //LOGE("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
+                    glUniform3fv(slot, 1, fd);
+                    break;
+                case 4:
+                    //LOGE("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
+                    glUniform4fv(slot, 1, fd);
+                    break;
+                default:
+                    rsAssert(0);
+                }
+            }
+            uidx ++;
+        }
+    }
+
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        uint32_t glSlot = sc->vtxUniformSlot(ct + 1);
+
+    }
+
     state->mLast.set(this);
     rsc->checkError("ProgramVertex::setupGL2");
 }
@@ -208,46 +282,46 @@
 
 void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     Matrix mvp;
     mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
                      (Matrix *)&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
     mvp.vectorMultiply(v4out, v3in);
 }
 
-void ProgramVertex::initAddUserAttrib(const Element *e)
+void ProgramVertex::initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix)
 {
     rsAssert(e->getFieldCount());
     for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
         const Element *ce = e->getField(ct);
         if (ce->getFieldCount()) {
-            initAddUserAttrib(ce);
+            initAddUserElement(ce, names, count, prefix);
         } else {
-            String8 tmp("ATTRIB_");
+            String8 tmp(prefix);
             tmp.append(e->getFieldName(ct));
-            mAttribNames[mAttribCount].setTo(tmp.string());
-            mAttribCount++;
+            names[*count].setTo(tmp.string());
+            (*count)++;
         }
     }
 }
@@ -257,7 +331,13 @@
     if (mUserShader.size() > 0) {
         mAttribCount = 0;
         for (uint32_t ct=0; ct < mInputCount; ct++) {
-            initAddUserAttrib(mInputElements[ct].get());
+            initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_");
+        }
+
+        mUniformCount = 1;
+        mUniformNames[0].setTo("UNI_MVP");
+        for (uint32_t ct=0; ct < mConstantCount; ct++) {
+            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
         }
     } else {
         mAttribCount = 5;
@@ -266,11 +346,11 @@
         mAttribNames[2].setTo("ATTRIB_Normal");
         mAttribNames[3].setTo("ATTRIB_PointSize");
         mAttribNames[4].setTo("ATTRIB_Texture");
-    }
 
-    mUniformCount = 2;
-    mUniformNames[0].setTo("uni_MVP");
-    mUniformNames[1].setTo("uni_TexMatrix");
+        mUniformCount = 2;
+        mUniformNames[0].setTo("UNI_MVP");
+        mUniformNames[1].setTo("UNI_TexMatrix");
+    }
 
     createShader();
 }
@@ -299,7 +379,7 @@
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
     pv->init(rsc);
-    pv->bindAllocation(alloc);
+    pv->bindAllocation(alloc, 0);
 
     color[0] = 1.f;
     color[1] = 1.f;
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index dcb988c..28554cc 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -61,7 +61,7 @@
     bool mTextureMatrixEnable;
 
 private:
-    void initAddUserAttrib(const Element *e);
+    void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix);
 };
 
 
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 8a0ab71..3ba9cee 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -973,6 +973,13 @@
     rsi_AllocationUploadToBufferObject(rsc, va);
 }
 
+static void SC_syncToGL(RsAllocation va)
+{
+    GET_TLS();
+    Allocation *a = static_cast<Allocation *>(va);
+
+}
+
 static void SC_ClearColor(float r, float g, float b, float a)
 {
     //LOGE("c %f %f %f %f", r, g, b, a);
@@ -1321,6 +1328,9 @@
     { "uploadToBufferObject", (void *)&SC_uploadToBufferObject,
         "void", "(int)" },
 
+    { "syncToGL", (void *)&SC_syncToGL,
+        "void", "(int)" },
+
     { "colorFloatRGBAtoUNorm8", (void *)&SC_colorFloatRGBAtoUNorm8,
         "int", "(float, float, float, float)" },
     { "colorFloatRGBto565", (void *)&SC_colorFloatRGBAto565,
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 0d9863de..8ac2487 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -132,7 +132,7 @@
                 LOGV("vtx U, %s = %d\n", vtx->getUniformName(ct).string(), e->mVtxUniformSlots[ct]);
             }
         }
-        for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
+        for (uint32_t ct=0; ct < frag->getUniformCount(); ct++) {
             e->mFragUniformSlots[ct] = glGetUniformLocation(pgm, frag->getUniformName(ct));
             if (rsc->props.mLogShaders) {
                 LOGV("frag U, %s = %d\n", frag->getUniformName(ct).string(), e->mFragUniformSlots[ct]);
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cecf4f8..04f8b5d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -255,5 +255,6 @@
     public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
     public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
     public static final int METADATA_KEY_WRITER          = 21;
+    public static final int METADATA_KEY_MIMETYPE        = 22;
     // Add more here...
 }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index af57a4c..6563caa 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1258,22 +1258,24 @@
 
 static const int NUMVIZBUF = 32;
 static const int VIZBUFFRAMES = 1024;
-static const int TOTALBUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC;
 
 static bool gotMem = false;
+static sp<MemoryHeapBase> heap; 
 static sp<MemoryBase> mem[NUMVIZBUF];
-static uint64_t timeStamp[NUMVIZBUF];
+static uint64_t endTime;
 static uint64_t lastReadTime;
 static uint64_t lastWriteTime;
 static int writeIdx = 0;
 
 static void allocVizBufs() {
     if (!gotMem) {
+        heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper");
         for (int i=0;i<NUMVIZBUF;i++) {
-            sp<MemoryHeapBase> heap = new MemoryHeapBase(VIZBUFFRAMES*2, 0, "snooper");
-            mem[i] = new MemoryBase(heap, 0, heap->getSize());
-            timeStamp[i] = 0;
+            mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2);
         }
+        endTime = 0;
         gotMem = true;
     }
 }
@@ -1290,68 +1292,48 @@
 
     allocVizBufs();
 
-    lastReadTime = uptimeMillis() + 100; // account for renderer delay (we shouldn't be doing this here)
+    lastReadTime = uptimeMillis();
 
     // if there is no recent buffer (yet), just return empty handed
     if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
-        //LOGI("@@@@    no audio data to look at yet");
+        //LOGI("@@@@    no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime);
         return NULL;
     }
 
-    char buf[200];
-
-    int closestIdx = -1;
-    uint32_t closestTime = 0x7ffffff;
-
-    for (int i = 0; i < NUMVIZBUF; i++) {
-        uint64_t tsi = timeStamp[i];
-        uint64_t diff = tsi > lastReadTime ? tsi - lastReadTime : lastReadTime - tsi;
-        if (diff < closestTime) {
-            closestIdx = i;
-            closestTime = diff;
-        }
+    int timedelta = endTime - lastReadTime;
+    if (timedelta < 0) timedelta = 0;
+    int framedelta = timedelta * 44100 / 1000;
+    int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1;
+    while (headIdx < 0) {
+        headIdx += NUMVIZBUF;
     }
-
-
-    if (closestIdx >= 0) {
-        //LOGI("@@@ return buffer %d, %d/%d", closestIdx, uint32_t(lastReadTime), uint32_t(timeStamp[closestIdx]));
-        return mem[closestIdx];
-    }
-
-    // we won't get here, since we either bailed out early, or got a buffer
-    LOGD("Didn't expect to be here");
-    return NULL;
+    return mem[headIdx];
 }
 
-static void storeVizBuf(const void *data, int len, uint64_t time) {
-    // Copy the data in to the visualizer buffer
-    // Assume a 16 bit stereo source for now.
-    short *viz = (short*)mem[writeIdx]->pointer();
-    short *src = (short*)data;
-    for (int i = 0; i < VIZBUFFRAMES; i++) {
-        // Degrade quality by mixing to mono and clearing the lowest 3 bits.
-        // This should still be good enough for a visualization
-        *viz++ = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
-        src += 2;
-    }
-    timeStamp[writeIdx++] = time;
-    if (writeIdx >= NUMVIZBUF) {
-        writeIdx = 0;
-    }
-}
-
+// Append the data to the vizualization buffer
 static void makeVizBuffers(const char *data, int len, uint64_t time) {
 
     allocVizBufs();
 
     uint64_t startTime = time;
     const int frameSize = 4; // 16 bit stereo sample is 4 bytes
-    while (len >= VIZBUFFRAMES * frameSize) {
-        storeVizBuf(data, len, time);
-        data += VIZBUFFRAMES * frameSize;
-        len -= VIZBUFFRAMES * frameSize;
-        time += 1000 * VIZBUFFRAMES / 44100;
+    int offset = writeIdx;
+    int maxoff = heap->getSize() / 2; // in shorts
+    short *base = (short*)heap->getBase();
+    short *src = (short*)data;
+    while (len > 0) {
+        
+        // Degrade quality by mixing to mono and clearing the lowest 3 bits.
+        // This should still be good enough for a visualization
+        base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
+        src += 2;
+        len -= frameSize;
+        if (offset >= maxoff) {
+            offset = 0;
+        }
     }
+    writeIdx = offset;
+    endTime = time + (len / frameSize) / 44;
     //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
 }
 
@@ -1509,30 +1491,35 @@
     }
 }
 
+void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) {
+    // Only make visualization buffers if anyone recently requested visualization data
+    uint64_t now = uptimeMillis();
+    if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
+        // Based on the current play counter, the number of frames written and
+        // the current real time we can calculate the approximate real start
+        // time of the buffer we're about to write.
+        uint32_t pos;
+        mTrack->getPosition(&pos);
+
+        // we're writing ahead by this many frames:
+        int ahead = mNumFramesWritten - pos;
+        //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
+        // which is this many milliseconds, assuming 44100 Hz:
+        ahead /= 44;
+
+        makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
+        lastWriteTime = now;
+    }
+}
+
+
 ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
 {
     LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
 
     //LOGV("write(%p, %u)", buffer, size);
     if (mTrack) {
-        // Only make visualization buffers if anyone recently requested visualization data
-        uint64_t now = uptimeMillis();
-        if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
-            // Based on the current play counter, the number of frames written and
-            // the current real time we can calculate the approximate real start
-            // time of the buffer we're about to write.
-            uint32_t pos;
-            mTrack->getPosition(&pos);
-
-            // we're writing ahead by this many frames:
-            int ahead = mNumFramesWritten - pos;
-            //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
-            // which is this many milliseconds, assuming 44100 Hz:
-            ahead /= 44;
-
-            makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
-            lastWriteTime = now;
-        }
+        snoopWrite(buffer, size);
         ssize_t ret = mTrack->write(buffer, size);
         mNumFramesWritten += ret / 4; // assume 16 bit stereo
         return ret;
@@ -1580,6 +1567,7 @@
 // static
 void MediaPlayerService::AudioOutput::CallbackWrapper(
         int event, void *cookie, void *info) {
+    //LOGV("callbackwrapper");
     if (event != AudioTrack::EVENT_MORE_DATA) {
         return;
     }
@@ -1589,6 +1577,7 @@
 
     (*me->mCallback)(
             me, buffer->raw, buffer->size, me->mCallbackCookie);
+    me->snoopWrite(buffer->raw, buffer->size);
 }
 
 #undef LOG_TAG
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 1c90cf9..d1206b4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -113,6 +113,7 @@
 
         public: // visualization hack support
         uint32_t                mNumFramesWritten;
+        void                    snoopWrite(const void*, size_t);
     };
 
     class AudioCache : public MediaPlayerBase::AudioSink
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index bdd7550..3193d5e 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -128,6 +128,18 @@
 AMRExtractor::~AMRExtractor() {
 }
 
+sp<MetaData> AMRExtractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mInitCheck != OK) {
+        return meta;
+    }
+
+    meta->setCString(kKeyMIMEType, mIsWide ? "audio/amr-wb" : "audio/amr");
+
+    return meta;
+}
+
 size_t AMRExtractor::countTracks() {
     return mInitCheck == OK ? 1 : 0;
 }
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 30b4506..e36e78c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -44,14 +44,17 @@
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
         $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
-        $(TOP)/external/opencore/android
+        $(TOP)/external/opencore/android \
+        $(TOP)/external/tremor/Tremor
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder         \
         libmedia          \
         libutils          \
         libcutils         \
-        libui
+        libui             \
+        libsonivox        \
+        libvorbisidec
 
 ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
 
@@ -62,7 +65,8 @@
         libstagefright_amrwbdec \
         libstagefright_avcdec \
         libstagefright_m4vh263dec \
-        libstagefright_mp3dec
+        libstagefright_mp3dec \
+        libstagefright_id3
 
 LOCAL_SHARED_LIBRARIES += \
         libstagefright_amrnb_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 4280683..14842c0 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -133,13 +133,14 @@
 
     if (mAudioSink.get() != NULL) {
         mAudioSink->stop();
+        mAudioSink->close();
     } else {
         mAudioTrack->stop();
 
         delete mAudioTrack;
         mAudioTrack = NULL;
     }
-    
+
     // Make sure to release any buffer we hold onto so that the
     // source is able to stop().
     if (mInputBuffer != NULL) {
@@ -150,7 +151,7 @@
     }
 
     mSource->stop();
-    
+
     mNumFramesPlayed = 0;
     mPositionTimeMediaUs = -1;
     mPositionTimeRealUs = -1;
@@ -259,7 +260,7 @@
 
         mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
                                 mInputBuffer->range_length() - copy);
-                    
+
         size_done += copy;
         size_remaining -= copy;
     }
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 2a6dbc4..741e5e00 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,7 +19,10 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 
+#include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <utils/String8.h>
 
@@ -91,4 +94,24 @@
     RegisterSniffer(SniffWAV);
 }
 
+// static
+sp<DataSource> DataSource::CreateFromURI(const char *uri) {
+    sp<DataSource> source;
+    if (!strncasecmp("file://", uri, 7)) {
+        source = new FileSource(uri + 7);
+    } else if (!strncasecmp("http://", uri, 7)) {
+        source = new HTTPDataSource(uri);
+        source = new CachingDataSource(source, 64 * 1024, 10);
+    } else {
+        // Assume it's a filename.
+        source = new FileSource(uri);
+    }
+
+    if (source == NULL || source->initCheck() != OK) {
+        return NULL;
+    }
+
+    return source;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 37c2450..b6f1af2 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -58,7 +58,10 @@
     }
 
     int err = fseeko(mFile, offset + mOffset, SEEK_SET);
-    CHECK(err != -1);
+    if (err < 0) {
+        LOGE("seek to %lld failed", offset + mOffset);
+        return UNKNOWN_ERROR;
+    }
 
     return fread(data, 1, size, mFile);
 }
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index b8e76fd..accd94d 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -20,6 +20,8 @@
 
 #include "include/MP3Extractor.h"
 
+#include "include/ID3.h"
+
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -667,7 +669,7 @@
         }
 
         // Lost sync.
-        LOGW("lost sync!\n");
+        LOGV("lost sync!\n");
 
         off_t pos = mCurrentPos;
         if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
@@ -706,6 +708,69 @@
     return OK;
 }
 
+sp<MetaData> MP3Extractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mFirstFramePos < 0) {
+        return meta;
+    }
+
+    meta->setCString(kKeyMIMEType, "audio/mpeg");
+
+    ID3 id3(mDataSource);
+
+    if (!id3.isValid()) {
+        return meta;
+    }
+
+    struct Map {
+        int key;
+        const char *tag1;
+        const char *tag2;
+    };
+    static const Map kMap[] = {
+        { kKeyAlbum, "TALB", "TAL" },
+        { kKeyArtist, "TPE1", "TP1" },
+        { kKeyComposer, "TCOM", "TCM" },
+        { kKeyGenre, "TCON", "TCO" },
+        { kKeyTitle, "TIT2", "TT2" },
+        { kKeyYear, "TYE", "TYER" },
+        { kKeyAuthor, "TXT", "TEXT" },
+        { kKeyCDTrackNumber, "TRK", "TRCK" },
+    };
+    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+    for (size_t i = 0; i < kNumMapEntries; ++i) {
+        ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
+        if (it->done()) {
+            delete it;
+            it = new ID3::Iterator(id3, kMap[i].tag2);
+        }
+
+        if (it->done()) {
+            delete it;
+            continue;
+        }
+
+        String8 s;
+        it->getString(&s);
+        delete it;
+
+        meta->setCString(kMap[i].key, s);
+    }
+
+    size_t dataSize;
+    String8 mime;
+    const void *data = id3.getAlbumArt(&dataSize, &mime);
+
+    if (data) {
+        meta->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+        meta->setCString(kKeyAlbumArtMIME, mime.string());
+    }
+
+    return meta;
+}
+
 bool SniffMP3(
         const sp<DataSource> &source, String8 *mimeType, float *confidence) {
     off_t pos = 0;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 143e8ee..07a5a82 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -66,6 +66,8 @@
     uint32_t mCurrentSampleIndex;
 
     bool mIsAVC;
+    size_t mNALLengthSize;
+
     bool mStarted;
 
     MediaBufferGroup *mGroup;
@@ -76,6 +78,8 @@
 
     uint8_t *mSrcBuffer;
 
+    size_t parseNALSize(const uint8_t *data) const;
+
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
 };
@@ -148,6 +152,7 @@
 MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
     : mDataSource(source),
       mHaveMetadata(false),
+      mHasVideo(false),
       mFirstTrack(NULL),
       mLastTrack(NULL) {
 }
@@ -163,6 +168,23 @@
     mFirstTrack = mLastTrack = NULL;
 }
 
+sp<MetaData> MPEG4Extractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return meta;
+    }
+
+    if (mHasVideo) {
+        meta->setCString(kKeyMIMEType, "video/mp4");
+    } else {
+        meta->setCString(kKeyMIMEType, "audio/mp4");
+    }
+
+    return meta;
+}
+
 size_t MPEG4Extractor::countTracks() {
     status_t err;
     if ((err = readMetaData()) != OK) {
@@ -231,7 +253,7 @@
     status_t err;
     while ((err = parseChunk(&offset, 0)) == OK) {
     }
-    
+
     if (mHaveMetadata) {
         return OK;
     }
@@ -557,6 +579,8 @@
         case FOURCC('s', '2', '6', '3'):
         case FOURCC('a', 'v', 'c', '1'):
         {
+            mHasVideo = true;
+
             if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
                 return ERROR_MALFORMED;
             }
@@ -770,6 +794,7 @@
       mSampleTable(sampleTable),
       mCurrentSampleIndex(0),
       mIsAVC(false),
+      mNALLengthSize(0),
       mStarted(false),
       mGroup(NULL),
       mBuffer(NULL),
@@ -780,6 +805,21 @@
     CHECK(success);
 
     mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    if (mIsAVC) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        CHECK(format->findData(kKeyAVCC, &type, &data, &size));
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+
+        // The number of bytes used to encode the length of a NAL unit.
+        mNALLengthSize = 1 + (ptr[4] & 3);
+    }
 }
 
 MPEG4Source::~MPEG4Source() {
@@ -837,6 +877,25 @@
     return mFormat;
 }
 
+size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
+    switch (mNALLengthSize) {
+        case 1:
+            return *data;
+        case 2:
+            return U16_AT(data);
+        case 3:
+            return ((size_t)data[0] << 16) | U16_AT(&data[1]);
+        case 4:
+            return U32_AT(data);
+    }
+
+    // This cannot happen, mNALLengthSize springs to life by adding 1 to
+    // a 2-bit integer.
+    CHECK(!"Should not be here.");
+
+    return 0;
+}
+
 status_t MPEG4Source::read(
         MediaBuffer **out, const ReadOptions *options) {
     CHECK(mStarted);
@@ -919,21 +978,27 @@
         // Each NAL unit is split up into its constituent fragments and
         // each one of them returned in its own buffer.
 
-        CHECK(mBuffer->range_length() >= 2);
+        CHECK(mBuffer->range_length() >= mNALLengthSize);
 
         const uint8_t *src =
             (const uint8_t *)mBuffer->data() + mBuffer->range_offset();
 
-        size_t nal_size = U16_AT(src);
+        size_t nal_size = parseNALSize(src);
+        if (mBuffer->range_length() < mNALLengthSize + nal_size) {
+            LOGE("incomplete NAL unit.");
 
-        CHECK(mBuffer->range_length() >= 2 + nal_size);
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_MALFORMED;
+        }
 
         MediaBuffer *clone = mBuffer->clone();
-        clone->set_range(mBuffer->range_offset() + 2, nal_size);
+        clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
 
         mBuffer->set_range(
-                mBuffer->range_offset() + 2 + nal_size,
-                mBuffer->range_length() - 2 - nal_size);
+                mBuffer->range_offset() + mNALLengthSize + nal_size,
+                mBuffer->range_length() - mNALLengthSize - nal_size);
 
         if (mBuffer->range_length() == 0) {
             mBuffer->release();
@@ -960,12 +1025,18 @@
         uint8_t *dstData = (uint8_t *)mBuffer->data();
         size_t srcOffset = 0;
         size_t dstOffset = 0;
+
         while (srcOffset < size) {
-            CHECK(srcOffset + 1 < size);
-            size_t nalLength =
-                (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
-            CHECK(srcOffset + 1 + nalLength < size);
-            srcOffset += 2;
+            CHECK(srcOffset + mNALLengthSize <= size);
+            size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+            srcOffset += mNALLengthSize;
+
+            if (srcOffset + nalLength > size) {
+                mBuffer->release();
+                mBuffer = NULL;
+
+                return ERROR_MALFORMED;
+            }
 
             if (nalLength == 0) {
                 continue;
@@ -981,6 +1052,7 @@
             srcOffset += nalLength;
             dstOffset += nalLength;
         }
+        CHECK_EQ(srcOffset, size);
 
         mBuffer->set_range(0, dstOffset);
         mBuffer->meta_data()->clear();
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9d3deb7..e46f00e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -23,16 +23,18 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 
-#include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
 #include <utils/String8.h>
 
 namespace android {
 
+sp<MetaData> MediaExtractor::getMetaData() {
+    return new MetaData;
+}
+
 // static
 sp<MediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
@@ -40,7 +42,7 @@
     if (mime == NULL) {
         float confidence;
         if (!source->sniff(&tmp, &confidence)) {
-            LOGE("FAILED to autodetect media content.");
+            LOGV("FAILED to autodetect media content.");
 
             return NULL;
         }
@@ -68,16 +70,7 @@
 // static
 sp<MediaExtractor> MediaExtractor::CreateFromURI(
         const char *uri, const char *mime) {
-    sp<DataSource> source;
-    if (!strncasecmp("file://", uri, 7)) {
-        source = new FileSource(uri + 7);
-    } else if (!strncasecmp("http://", uri, 7)) {
-        source = new HTTPDataSource(uri);
-        source = new CachingDataSource(source, 64 * 1024, 10);
-    } else {
-        // Assume it's a filename.
-        source = new FileSource(uri);
-    }
+    sp<DataSource> source = DataSource::CreateFromURI(uri);
 
     if (source == NULL || source->initCheck() != OK) {
         return NULL;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index d8bd25d..c4d3b5d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1786,6 +1786,21 @@
 void OMXCodec::fillOutputBuffers() {
     CHECK_EQ(mState, EXECUTING);
 
+    // This is a workaround for some decoders not properly reporting
+    // end-of-output-stream. If we own all input buffers and also own
+    // all output buffers and we already signalled end-of-input-stream,
+    // the end-of-output-stream is implied.
+    if (mSignalledEOS
+            && countBuffersWeOwn(mPortBuffers[kPortIndexInput])
+                == mPortBuffers[kPortIndexInput].size()
+            && countBuffersWeOwn(mPortBuffers[kPortIndexOutput])
+                == mPortBuffers[kPortIndexOutput].size()) {
+        mNoMoreOutputData = true;
+        mBufferFilled.signal();
+
+        return;
+    }
+
     Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
     for (size_t i = 0; i < buffers->size(); ++i) {
         fillOutputBuffer(&buffers->editItemAt(i));
@@ -1833,6 +1848,8 @@
 
         mNoMoreOutputData = false;
 
+        CODEC_LOGV("calling emptyBuffer with codec specific data");
+
         status_t err = mOMX->emptyBuffer(
                 mNode, info->mBuffer, 0, size,
                 OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 4aec0e9..2de96d4 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -291,7 +291,7 @@
 
 status_t SampleTable::getChunkForSample(
         uint32_t sample_index,
-        uint32_t *chunk_index, 
+        uint32_t *chunk_index,
         uint32_t *chunk_relative_sample_index,
         uint32_t *desc_index) {
     *chunk_index = 0;
@@ -469,7 +469,7 @@
     for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
         size_t sample_size;
         status_t err = getSampleSize(i, &sample_size);
-        
+
         if (err != OK) {
             return err;
         }
@@ -502,7 +502,7 @@
 
             return OK;
         }
-        
+
         *time += delta * n;
         cur_sample += n;
     }
@@ -510,6 +510,10 @@
     return ERROR_OUT_OF_RANGE;
 }
 
+uint32_t abs_difference(uint32_t time1, uint32_t time2) {
+    return time1 > time2 ? time1 - time2 : time2 - time1;
+}
+
 status_t SampleTable::findClosestSample(
         uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
     Mutex::Autolock autoLock(mLock);
@@ -523,7 +527,16 @@
         if (req_time < time + n * delta) {
             int j = (req_time - time) / delta;
 
-            *sample_index = cur_sample + j;
+            uint32_t time1 = time + j * delta;
+            uint32_t time2 = time1 + delta;
+
+            if (i+1 == mTimeToSampleCount
+                    || (abs_difference(req_time, time1)
+                        < abs_difference(req_time, time2))) {
+                *sample_index = cur_sample + j;
+            } else {
+                *sample_index = cur_sample + j + 1;
+            }
 
             if (flags & kSyncSample_Flag) {
                 return findClosestSyncSample(*sample_index, sample_index);
@@ -554,8 +567,9 @@
     uint32_t right = mNumSyncSamples;
     while (left < right) {
         uint32_t mid = (left + right) / 2;
+
         if (mDataSource->readAt(
-                    mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
+                    mSyncSampleOffset + 8 + mid * 4, &x, 4) != 4) {
             return ERROR_IO;
         }
 
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 9b41929..3451383 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -14,10 +14,21 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightMediaScanner"
+#include <utils/Log.h>
+
 #include <media/stagefright/StagefrightMediaScanner.h>
 
 #include "include/StagefrightMetadataRetriever.h"
 
+// Sonivox includes
+#include <libsonivox/eas.h>
+
+// Ogg Vorbis includes
+#include "ivorbiscodec.h"
+#include "ivorbisfile.h"
+
 namespace android {
 
 StagefrightMediaScanner::StagefrightMediaScanner()
@@ -26,15 +37,154 @@
 
 StagefrightMediaScanner::~StagefrightMediaScanner() {}
 
+static bool FileHasAcceptableExtension(const char *extension) {
+    static const char *kValidExtensions[] = {
+        ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
+        ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
+        ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota"
+    };
+    static const size_t kNumValidExtensions =
+        sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
+
+    for (size_t i = 0; i < kNumValidExtensions; ++i) {
+        if (!strcasecmp(extension, kValidExtensions[i])) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static status_t HandleMIDI(
+        const char *filename, MediaScannerClient *client) {
+    // get the library configuration and do sanity check
+    const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
+    if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
+        LOGE("EAS library/header mismatch\n");
+        return UNKNOWN_ERROR;
+    }
+    EAS_I32 temp;
+
+    // spin up a new EAS engine
+    EAS_DATA_HANDLE easData = NULL;
+    EAS_HANDLE easHandle = NULL;
+    EAS_RESULT result = EAS_Init(&easData);
+    if (result == EAS_SUCCESS) {
+        EAS_FILE file;
+        file.path = filename;
+        file.fd = 0;
+        file.offset = 0;
+        file.length = 0;
+        result = EAS_OpenFile(easData, &file, &easHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_Prepare(easData, easHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_ParseMetaData(easData, easHandle, &temp);
+    }
+    if (easHandle) {
+        EAS_CloseFile(easData, easHandle);
+    }
+    if (easData) {
+        EAS_Shutdown(easData);
+    }
+
+    if (result != EAS_SUCCESS) {
+        return UNKNOWN_ERROR;
+    }
+
+    char buffer[20];
+    sprintf(buffer, "%ld", temp);
+    if (!client->addStringTag("duration", buffer)) return UNKNOWN_ERROR;
+
+    return OK;
+}
+
+static status_t HandleOGG(
+        const char *filename, MediaScannerClient *client) {
+    int duration;
+
+    FILE *file = fopen(filename,"r");
+    if (!file)
+        return UNKNOWN_ERROR;
+
+    OggVorbis_File vf;
+    if (ov_open(file, &vf, NULL, 0) < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    char **ptr=ov_comment(&vf,-1)->user_comments;
+    while(*ptr){
+        char *val = strstr(*ptr, "=");
+        if (val) {
+            int keylen = val++ - *ptr;
+            char key[keylen + 1];
+            strncpy(key, *ptr, keylen);
+            key[keylen] = 0;
+            if (!client->addStringTag(key, val)) goto failure;
+        }
+        ++ptr;
+    }
+
+    // Duration
+    duration = ov_time_total(&vf, -1);
+    if (duration > 0) {
+        char buffer[20];
+        sprintf(buffer, "%d", duration);
+        if (!client->addStringTag("duration", buffer)) goto failure;
+    }
+
+    ov_clear(&vf); // this also closes the FILE
+    return OK;
+
+failure:
+    ov_clear(&vf); // this also closes the FILE
+    return UNKNOWN_ERROR;
+}
+
 status_t StagefrightMediaScanner::processFile(
         const char *path, const char *mimeType,
         MediaScannerClient &client) {
     client.setLocale(locale());
     client.beginFile();
 
+    const char *extension = strrchr(path, '.');
+
+    if (!extension) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (!FileHasAcceptableExtension(extension)) {
+        client.endFile();
+
+        return UNKNOWN_ERROR;
+    }
+
+    if (!strcasecmp(extension, ".mid")
+            || !strcasecmp(extension, ".smf")
+            || !strcasecmp(extension, ".imy")
+            || !strcasecmp(extension, ".midi")
+            || !strcasecmp(extension, ".xmf")
+            || !strcasecmp(extension, ".rtttl")
+            || !strcasecmp(extension, ".rtx")
+            || !strcasecmp(extension, ".ota")) {
+        return HandleMIDI(path, &client);
+    }
+
+    if (!strcasecmp(extension, ".ogg")) {
+        return HandleOGG(path, &client);
+    }
+
     if (mRetriever->setDataSource(path) == OK
             && mRetriever->setMode(
                 METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {
+        const char *value;
+        if ((value = mRetriever->extractMetadata(
+                        METADATA_KEY_MIMETYPE)) != NULL) {
+            client.setMimeType(value);
+        }
+
         struct KeyMap {
             const char *tag;
             int key;
@@ -66,12 +216,27 @@
 }
 
 char *StagefrightMediaScanner::extractAlbumArt(int fd) {
-    if (mRetriever->setDataSource(fd, 0, 0) == OK
+    off_t size = lseek(fd, 0, SEEK_END);
+    if (size < 0) {
+        return NULL;
+    }
+    lseek(fd, 0, SEEK_SET);
+
+    if (mRetriever->setDataSource(fd, 0, size) == OK
             && mRetriever->setMode(
                 METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) {
         MediaAlbumArt *art = mRetriever->extractAlbumArt();
 
-        // TODO: figure out what format the result should be in.
+        if (art != NULL) {
+            char *data = (char *)malloc(art->mSize + 4);
+            *(int32_t *)data = art->mSize;
+            memcpy(&data[4], art->mData, art->mSize);
+
+            delete art;
+            art = NULL;
+
+            return data;
+        }
     }
 
     return NULL;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 128e776..020887c 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -1,19 +1,18 @@
 /*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "StagefrightMetadataRetriever"
@@ -33,7 +32,9 @@
 
 namespace android {
 
-StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
+StagefrightMetadataRetriever::StagefrightMetadataRetriever()
+    : mParsedMetaData(false),
+      mAlbumArt(NULL) {
     LOGV("StagefrightMetadataRetriever()");
 
     DataSource::RegisterDefaultSniffers();
@@ -42,27 +43,168 @@
 
 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
     LOGV("~StagefrightMetadataRetriever()");
+
+    delete mAlbumArt;
+    mAlbumArt = NULL;
+
     mClient.disconnect();
 }
 
 status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
     LOGV("setDataSource(%s)", uri);
 
-    mExtractor = MediaExtractor::CreateFromURI(uri);
+    mParsedMetaData = false;
+    mMetaData.clear();
+    delete mAlbumArt;
+    mAlbumArt = NULL;
 
-    return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR;
-}
+    mSource = DataSource::CreateFromURI(uri);
 
-status_t StagefrightMetadataRetriever::setDataSource(
-        int fd, int64_t offset, int64_t length) {
-    LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+    if (mSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
 
-    mExtractor = MediaExtractor::Create(
-            new FileSource(fd, offset, length));
+    mExtractor = MediaExtractor::Create(mSource);
+
+    if (mExtractor == NULL) {
+        mSource.clear();
+
+        return UNKNOWN_ERROR;
+    }
 
     return OK;
 }
 
+// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
+status_t StagefrightMetadataRetriever::setDataSource(
+        int fd, int64_t offset, int64_t length) {
+    fd = dup(fd);
+
+    LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+    mParsedMetaData = false;
+    mMetaData.clear();
+    delete mAlbumArt;
+    mAlbumArt = NULL;
+
+    mSource = new FileSource(fd, offset, length);
+
+    status_t err;
+    if ((err = mSource->initCheck()) != OK) {
+        mSource.clear();
+
+        return err;
+    }
+
+    mExtractor = MediaExtractor::Create(mSource);
+
+    if (mExtractor == NULL) {
+        mSource.clear();
+
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+static VideoFrame *extractVideoFrameWithCodecFlags(
+        OMXClient *client,
+        const sp<MetaData> &trackMeta,
+        const sp<MediaSource> &source, uint32_t flags) {
+    sp<MediaSource> decoder =
+        OMXCodec::Create(
+                client->interface(), source->getFormat(), false, source,
+                NULL, flags);
+
+    if (decoder.get() == NULL) {
+        LOGV("unable to instantiate video decoder.");
+
+        return NULL;
+    }
+
+    decoder->start();
+
+    // Read one output buffer, ignore format change notifications
+    // and spurious empty buffers.
+
+    MediaSource::ReadOptions options;
+    int64_t thumbNailTime;
+    if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+        options.setSeekTo(thumbNailTime);
+    } else {
+        thumbNailTime = -1;
+    }
+
+    MediaBuffer *buffer = NULL;
+    status_t err;
+    do {
+        if (buffer != NULL) {
+            buffer->release();
+            buffer = NULL;
+        }
+        err = decoder->read(&buffer, &options);
+        options.clearSeekTo();
+    } while (err == INFO_FORMAT_CHANGED
+             || (buffer != NULL && buffer->range_length() == 0));
+
+    if (err != OK) {
+        CHECK_EQ(buffer, NULL);
+
+        LOGV("decoding frame failed.");
+        decoder->stop();
+
+        return NULL;
+    }
+
+    LOGV("successfully decoded video frame.");
+
+    int64_t timeUs;
+    CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+    if (thumbNailTime >= 0) {
+        if (timeUs != thumbNailTime) {
+            const char *mime;
+            CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
+
+            LOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
+                 thumbNailTime, timeUs, mime);
+        }
+    }
+
+    sp<MetaData> meta = decoder->getFormat();
+
+    int32_t width, height;
+    CHECK(meta->findInt32(kKeyWidth, &width));
+    CHECK(meta->findInt32(kKeyHeight, &height));
+
+    VideoFrame *frame = new VideoFrame;
+    frame->mWidth = width;
+    frame->mHeight = height;
+    frame->mDisplayWidth = width;
+    frame->mDisplayHeight = height;
+    frame->mSize = width * height * 2;
+    frame->mData = new uint8_t[frame->mSize];
+
+    int32_t srcFormat;
+    CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
+
+    ColorConverter converter(
+            (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
+    CHECK(converter.isValid());
+
+    converter.convert(
+            width, height,
+            (const uint8_t *)buffer->data() + buffer->range_offset(),
+            0,
+            frame->mData, width * 2);
+
+    buffer->release();
+    buffer = NULL;
+
+    decoder->stop();
+
+    return frame;
+}
+
 VideoFrame *StagefrightMetadataRetriever::captureFrame() {
     LOGV("captureFrame");
 
@@ -99,99 +241,125 @@
         return NULL;
     }
 
-    sp<MetaData> meta = source->getFormat();
+    VideoFrame *frame =
+        extractVideoFrameWithCodecFlags(
+                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs);
 
-    sp<MediaSource> decoder =
-        OMXCodec::Create(
-                mClient.interface(), meta, false, source,
-                NULL, OMXCodec::kPreferSoftwareCodecs);
+    if (frame == NULL) {
+        LOGV("Software decoder failed to extract thumbnail, "
+             "trying hardware decoder.");
 
-    if (decoder.get() == NULL) {
-        LOGV("unable to instantiate video decoder.");
-
-        return NULL;
+        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0);
     }
 
-    decoder->start();
-
-    // Read one output buffer, ignore format change notifications
-    // and spurious empty buffers.
-
-    MediaSource::ReadOptions options;
-    int64_t thumbNailTime;
-    if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
-        options.setSeekTo(thumbNailTime);
-    }
-
-    MediaBuffer *buffer = NULL;
-    status_t err;
-    do {
-        if (buffer != NULL) {
-            buffer->release();
-            buffer = NULL;
-        }
-        err = decoder->read(&buffer, &options);
-        options.clearSeekTo();
-    } while (err == INFO_FORMAT_CHANGED
-             || (buffer != NULL && buffer->range_length() == 0));
-
-    if (err != OK) {
-        CHECK_EQ(buffer, NULL);
-
-        LOGV("decoding frame failed.");
-        decoder->stop();
-
-        return NULL;
-    }
-
-    LOGV("successfully decoded video frame.");
-
-    meta = decoder->getFormat();
-
-    int32_t width, height;
-    CHECK(meta->findInt32(kKeyWidth, &width));
-    CHECK(meta->findInt32(kKeyHeight, &height));
-
-    VideoFrame *frame = new VideoFrame;
-    frame->mWidth = width;
-    frame->mHeight = height;
-    frame->mDisplayWidth = width;
-    frame->mDisplayHeight = height;
-    frame->mSize = width * height * 2;
-    frame->mData = new uint8_t[frame->mSize];
-
-    int32_t srcFormat;
-    CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
-
-    ColorConverter converter(
-            (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
-    CHECK(converter.isValid());
-
-    converter.convert(
-            width, height,
-            (const uint8_t *)buffer->data() + buffer->range_offset(),
-            0,
-            frame->mData, width * 2);
-
-    buffer->release();
-    buffer = NULL;
-
-    decoder->stop();
-
     return frame;
 }
 
 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
     LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
 
+    if (mExtractor == NULL) {
+        return NULL;
+    }
+
+    if (!mParsedMetaData) {
+        parseMetaData();
+
+        mParsedMetaData = true;
+    }
+
+    if (mAlbumArt) {
+        return new MediaAlbumArt(*mAlbumArt);
+    }
+
     return NULL;
 }
 
 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
-    LOGV("extractMetadata %d (extractor: %s)",
-         keyCode, mExtractor.get() != NULL ? "YES" : "NO");
+    LOGV("extractMetadata %d", keyCode);
 
-    return NULL;
+    if (mExtractor == NULL) {
+        return NULL;
+    }
+
+    if (!mParsedMetaData) {
+        parseMetaData();
+
+        mParsedMetaData = true;
+    }
+
+    ssize_t index = mMetaData.indexOfKey(keyCode);
+
+    if (index < 0) {
+        return NULL;
+    }
+
+    return strdup(mMetaData.valueAt(index).string());
 }
 
+void StagefrightMetadataRetriever::parseMetaData() {
+    sp<MetaData> meta = mExtractor->getMetaData();
+
+    struct Map {
+        int from;
+        int to;
+    };
+    static const Map kMap[] = {
+        { kKeyMIMEType, METADATA_KEY_MIMETYPE },
+        { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER },
+        { kKeyAlbum, METADATA_KEY_ALBUM },
+        { kKeyArtist, METADATA_KEY_ARTIST },
+        { kKeyAuthor, METADATA_KEY_AUTHOR },
+        { kKeyComposer, METADATA_KEY_COMPOSER },
+        { kKeyDate, METADATA_KEY_DATE },
+        { kKeyGenre, METADATA_KEY_GENRE },
+        { kKeyTitle, METADATA_KEY_TITLE },
+        { kKeyYear, METADATA_KEY_YEAR },
+    };
+    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
+
+    for (size_t i = 0; i < kNumMapEntries; ++i) {
+        const char *value;
+        if (meta->findCString(kMap[i].from, &value)) {
+            mMetaData.add(kMap[i].to, String8(value));
+        }
+    }
+
+    const void *data;
+    uint32_t type;
+    size_t dataSize;
+    if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)) {
+        mAlbumArt = new MediaAlbumArt;
+        mAlbumArt->mSize = dataSize;
+        mAlbumArt->mData = new uint8_t[dataSize];
+        memcpy(mAlbumArt->mData, data, dataSize);
+    }
+
+    size_t numTracks = mExtractor->countTracks();
+
+    char tmp[32];
+    sprintf(tmp, "%d", numTracks);
+
+    mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
+
+    // The overall duration is the duration of the longest track.
+    int64_t maxDurationUs = 0;
+    for (size_t i = 0; i < numTracks; ++i) {
+        sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
+
+        int64_t durationUs;
+        if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
+            if (durationUs > maxDurationUs) {
+                maxDurationUs = durationUs;
+            }
+        }
+    }
+
+    // The duration value is a string representing the duration in ms.
+    sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
+
+    mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
+}
+
+
 }  // namespace android
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 542c764..959a767 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -82,6 +82,18 @@
 WAVExtractor::~WAVExtractor() {
 }
 
+sp<MetaData> WAVExtractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mInitCheck != OK) {
+        return meta;
+    }
+
+    meta->setCString(kKeyMIMEType, "audio/x-wav");
+
+    return meta;
+}
+
 size_t WAVExtractor::countTracks() {
     return mInitCheck == OK ? 1 : 0;
 }
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 229e933..d874224 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -383,7 +383,7 @@
 
                 return OK;
             } else {
-                LOGE("failed to decode frame (res = %d)", res);
+                LOGV("failed to decode frame (res = %d)", res);
                 return UNKNOWN_ERROR;
             }
         }
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index 2f919c2..f1f7194 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -160,7 +160,15 @@
     mConfig->outputFrameSize = buffer->size() / sizeof(int16_t);
     mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data());
 
-    CHECK_EQ(pvmp3_framedecoder(mConfig, mDecoderBuf), NO_DECODING_ERROR);
+    if (pvmp3_framedecoder(mConfig, mDecoderBuf) != NO_DECODING_ERROR) {
+        buffer->release();
+        buffer = NULL;
+
+        mInputBuffer->release();
+        mInputBuffer = NULL;
+
+        return UNKNOWN_ERROR;
+    }
 
     buffer->set_range(
             0, mConfig->outputFrameSize * sizeof(int16_t));
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
new file mode 100644
index 0000000..3c47e2e
--- /dev/null
+++ b/media/libstagefright/id3/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	ID3.cpp
+
+LOCAL_MODULE := libstagefright_id3
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	testid3.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright libutils libbinder
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_id3
+
+LOCAL_MODULE := testid3
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
new file mode 100644
index 0000000..2b3ef1a
--- /dev/null
+++ b/media/libstagefright/id3/ID3.cpp
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ID3"
+#include <utils/Log.h>
+
+#include "../include/ID3.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+ID3::ID3(const sp<DataSource> &source)
+    : mIsValid(false),
+      mData(NULL),
+      mSize(0),
+      mFirstFrameOffset(0),
+      mVersion(ID3_UNKNOWN) {
+    mIsValid = parse(source);
+}
+
+ID3::~ID3() {
+    if (mData) {
+        free(mData);
+        mData = NULL;
+    }
+}
+
+bool ID3::isValid() const {
+    return mIsValid;
+}
+
+ID3::Version ID3::version() const {
+    return mVersion;
+}
+
+bool ID3::parse(const sp<DataSource> &source) {
+    struct id3_header {
+        char id[3];
+        uint8_t version_major;
+        uint8_t version_minor;
+        uint8_t flags;
+        uint8_t enc_size[4];
+    };
+
+    id3_header header;
+    if (source->readAt(
+                0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
+        return false;
+    }
+
+    if (memcmp(header.id, "ID3", 3)) {
+        return false;
+    }
+
+    if (header.version_major == 0xff || header.version_minor == 0xff) {
+        return false;
+    }
+
+    if (header.version_major == 2) {
+        if (header.flags & 0x3f) {
+            // We only support the 2 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+
+        if (header.flags & 0x40) {
+            // No compression scheme has been decided yet, ignore the
+            // tag if compression is indicated.
+
+            return false;
+        }
+    } else if (header.version_major == 3) {
+        if (header.flags & 0x1f) {
+            // We only support the 3 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    size_t size = 0;
+    for (int32_t i = 0; i < 4; ++i) {
+        if (header.enc_size[i] & 0x80) {
+            return false;
+        }
+
+        size = (size << 7) | header.enc_size[i];
+    }
+
+    mData = (uint8_t *)malloc(size);
+
+    if (mData == NULL) {
+        return false;
+    }
+
+    mSize = size;
+
+    if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        return false;
+    }
+
+    if (header.flags & 0x80) {
+        LOGI("removing unsynchronization");
+        removeUnsynchronization();
+    }
+
+    mFirstFrameOffset = 0;
+    if (header.version_major == 3 && (header.flags & 0x40)) {
+        // Version 2.3 has an optional extended header.
+
+        if (mSize < 4) {
+            return false;
+        }
+
+        size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
+
+        if (extendedHeaderSize > mSize) {
+            return false;
+        }
+
+        mFirstFrameOffset = extendedHeaderSize;
+
+        uint16_t extendedFlags = 0;
+        if (extendedHeaderSize >= 6) {
+            extendedFlags = U16_AT(&mData[4]);
+
+            if (extendedHeaderSize >= 10) {
+                size_t paddingSize = U32_AT(&mData[6]);
+
+                if (mFirstFrameOffset + paddingSize > mSize) {
+                    return false;
+                }
+
+                mSize -= paddingSize;
+            }
+
+            if (extendedFlags & 0x8000) {
+                LOGI("have crc");
+            }
+        }
+    }
+
+    if (header.version_major == 2) {
+        mVersion = ID3_V2_2;
+    } else {
+        CHECK_EQ(header.version_major, 3);
+        mVersion = ID3_V2_3;
+    }
+
+    return true;
+}
+
+void ID3::removeUnsynchronization() {
+    for (size_t i = 0; i + 1 < mSize; ++i) {
+        if (mData[i] == 0xff && mData[i + 1] == 0x00) {
+            memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
+            --mSize;
+        }
+    }
+}
+
+ID3::Iterator::Iterator(const ID3 &parent, const char *id)
+    : mParent(parent),
+      mID(NULL),
+      mOffset(mParent.mFirstFrameOffset),
+      mFrameData(NULL),
+      mFrameSize(0) {
+    if (id) {
+        mID = strdup(id);
+    }
+
+    findFrame();
+}
+
+ID3::Iterator::~Iterator() {
+    if (mID) {
+        free(mID);
+        mID = NULL;
+    }
+}
+
+bool ID3::Iterator::done() const {
+    return mFrameData == NULL;
+}
+
+void ID3::Iterator::next() {
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    mOffset += mFrameSize;
+
+    findFrame();
+}
+
+void ID3::Iterator::getID(String8 *id) const {
+    id->setTo("");
+
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    if (mParent.mVersion == ID3_V2_2) {
+        id->setTo((const char *)&mParent.mData[mOffset], 3);
+    } else {
+        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+        id->setTo((const char *)&mParent.mData[mOffset], 4);
+    }
+}
+
+static void convertISO8859ToString8(
+        const uint8_t *data, size_t size,
+        String8 *s) {
+    size_t utf8len = 0;
+    for (size_t i = 0; i < size; ++i) {
+        if (data[i] < 0x80) {
+            ++utf8len;
+        } else {
+            utf8len += 2;
+        }
+    }
+
+    if (utf8len == size) {
+        // Only ASCII characters present.
+
+        s->setTo((const char *)data, size);
+        return;
+    }
+
+    char *tmp = new char[utf8len];
+    char *ptr = tmp;
+    for (size_t i = 0; i < size; ++i) {
+        if (data[i] < 0x80) {
+            *ptr++ = data[i];
+        } else if (data[i] < 0xc0) {
+            *ptr++ = 0xc2;
+            *ptr++ = data[i];
+        } else {
+            *ptr++ = 0xc3;
+            *ptr++ = data[i] - 64;
+        }
+    }
+
+    s->setTo(tmp, utf8len);
+
+    delete[] tmp;
+    tmp = NULL;
+}
+
+void ID3::Iterator::getString(String8 *id) const {
+    id->setTo("");
+
+    if (mFrameData == NULL) {
+        return;
+    }
+
+    size_t n = mFrameSize - getHeaderLength() - 1;
+
+    if (*mFrameData == 0x00) {
+        // ISO 8859-1
+        convertISO8859ToString8(mFrameData + 1, n, id);
+    } else {
+        // UCS-2
+        id->setTo((const char16_t *)(mFrameData + 1), n);
+    }
+}
+
+const uint8_t *ID3::Iterator::getData(size_t *length) const {
+    *length = 0;
+
+    if (mFrameData == NULL) {
+        return NULL;
+    }
+
+    *length = mFrameSize - getHeaderLength();
+
+    return mFrameData;
+}
+
+size_t ID3::Iterator::getHeaderLength() const {
+    if (mParent.mVersion == ID3_V2_2) {
+        return 6;
+    } else {
+        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+        return 10;
+    }
+}
+
+void ID3::Iterator::findFrame() {
+    for (;;) {
+        mFrameData = NULL;
+        mFrameSize = 0;
+
+        if (mParent.mVersion == ID3_V2_2) {
+            if (mOffset + 6 > mParent.mSize) {
+                return;
+            }
+
+            if (!memcmp(&mParent.mData[mOffset], "\0\0\0", 3)) {
+                return;
+            }
+
+            mFrameSize =
+                (mParent.mData[mOffset + 3] << 16)
+                | (mParent.mData[mOffset + 4] << 8)
+                | mParent.mData[mOffset + 5];
+
+            mFrameSize += 6;
+
+            if (mOffset + mFrameSize > mParent.mSize) {
+                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+                     mOffset, mFrameSize, mParent.mSize - mOffset - 6);
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset + 6];
+
+            if (!mID) {
+                break;
+            }
+
+            char id[4];
+            memcpy(id, &mParent.mData[mOffset], 3);
+            id[3] = '\0';
+
+            if (!strcmp(id, mID)) {
+                break;
+            }
+        } else {
+            CHECK_EQ(mParent.mVersion, ID3_V2_3);
+
+            if (mOffset + 10 > mParent.mSize) {
+                return;
+            }
+
+            if (!memcmp(&mParent.mData[mOffset], "\0\0\0\0", 4)) {
+                return;
+            }
+
+            mFrameSize = 10 + U32_AT(&mParent.mData[mOffset + 4]);
+
+            if (mOffset + mFrameSize > mParent.mSize) {
+                LOGV("partial frame at offset %d (size = %d, bytes-remaining = %d)",
+                     mOffset, mFrameSize, mParent.mSize - mOffset - 10);
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset + 10];
+
+            if (!mID) {
+                break;
+            }
+
+            char id[5];
+            memcpy(id, &mParent.mData[mOffset], 4);
+            id[4] = '\0';
+
+            if (!strcmp(id, mID)) {
+                break;
+            }
+        }
+
+        mOffset += mFrameSize;
+    }
+}
+
+static size_t StringSize(const uint8_t *start, uint8_t encoding) {
+    if (encoding== 0x00) {
+        // ISO 8859-1
+        return strlen((const char *)start) + 1;
+    }
+
+    // UCS-2
+    size_t n = 0;
+    while (start[n] != '\0' || start[n + 1] != '\0') {
+        n += 2;
+    }
+
+    return n;
+}
+
+const void *
+ID3::getAlbumArt(size_t *length, String8 *mime) const {
+    *length = 0;
+    mime->setTo("");
+
+    Iterator it(*this, mVersion == ID3_V2_3 ? "APIC" : "PIC");
+
+    while (!it.done()) {
+        size_t size;
+        const uint8_t *data = it.getData(&size);
+
+        if (mVersion == ID3_V2_3) {
+            uint8_t encoding = data[0];
+            mime->setTo((const char *)&data[1]);
+            size_t mimeLen = strlen((const char *)&data[1]) + 1;
+
+            uint8_t picType = data[1 + mimeLen];
+#if 0
+            if (picType != 0x03) {
+                // Front Cover Art
+                it.next();
+                continue;
+            }
+#endif
+
+            size_t descLen = StringSize(&data[2 + mimeLen], encoding);
+
+            *length = size - 2 - mimeLen - descLen;
+
+            return &data[2 + mimeLen + descLen];
+        } else {
+            uint8_t encoding = data[0];
+
+            if (!memcmp(&data[1], "PNG", 3)) {
+                mime->setTo("image/png");
+            } else if (!memcmp(&data[1], "JPG", 3)) {
+                mime->setTo("image/jpeg");
+            } else if (!memcmp(&data[1], "-->", 3)) {
+                mime->setTo("text/plain");
+            } else {
+                return NULL;
+            }
+
+#if 0
+            uint8_t picType = data[4];
+            if (picType != 0x03) {
+                // Front Cover Art
+                it.next();
+                continue;
+            }
+#endif
+
+            size_t descLen = StringSize(&data[5], encoding);
+
+            *length = size - 5 - descLen;
+
+            return &data[5 + descLen];
+        }
+    }
+
+    return NULL;
+}
+
+
+}  // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
new file mode 100644
index 0000000..305b065
--- /dev/null
+++ b/media/libstagefright/id3/testid3.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "../include/ID3.h"
+
+#include <ctype.h>
+#include <dirent.h>
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaDebug.h>
+
+#define MAXPATHLEN 256
+
+using namespace android;
+
+static void hexdump(const void *_data, size_t size) {
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        printf("0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                printf(" ");
+            }
+
+            if (offset + i < size) {
+                printf("%02x ", data[offset + i]);
+            } else {
+                printf("   ");
+            }
+        }
+
+        printf(" ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                printf("%c", data[offset + i]);
+            } else {
+                printf(".");
+            }
+        }
+
+        printf("\n");
+
+        offset += 16;
+    }
+}
+
+void scanFile(const char *path) {
+    sp<FileSource> file = new FileSource(path);
+    CHECK_EQ(file->initCheck(), OK);
+
+    ID3 tag(file);
+    if (!tag.isValid()) {
+        printf("FAIL %s\n", path);
+    } else {
+        printf("SUCCESS %s\n", path);
+
+        ID3::Iterator it(tag, NULL);
+        while (!it.done()) {
+            String8 id;
+            it.getID(&id);
+
+            CHECK(id.length() > 0);
+            if (id[0] == 'T') {
+                String8 text;
+                it.getString(&text);
+
+                printf("  found text frame '%s': %s\n", id.string(), text.string());
+            } else {
+                printf("  found frame '%s'.\n", id.string());
+            }
+
+            it.next();
+        }
+
+        size_t dataSize;
+        String8 mime;
+        const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+        if (data) {
+            printf("found album art: size=%d mime='%s'\n", dataSize,
+                   mime.string());
+
+            hexdump(data, dataSize > 128 ? 128 : dataSize);
+        }
+    }
+}
+
+void scan(const char *path) {
+    DIR *dir = opendir(path);
+
+    if (dir == NULL) {
+        return;
+    }
+
+    rewinddir(dir);
+
+    struct dirent *ent;
+    while ((ent = readdir(dir)) != NULL) {
+        if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)) {
+            continue;
+        }
+
+        char newPath[MAXPATHLEN];
+        strcpy(newPath, path);
+        strcat(newPath, "/");
+        strcat(newPath, ent->d_name);
+
+        if (ent->d_type == DT_DIR) {
+            scan(newPath);
+        } else if (ent->d_type == DT_REG) {
+            size_t len = strlen(ent->d_name);
+
+            if (len >= 4
+                && !strcasecmp(ent->d_name + len - 4, ".mp3")) {
+                scanFile(newPath);
+            }
+        }
+    }
+
+    closedir(dir);
+    dir = NULL;
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    for (int i = 1; i < argc; ++i) {
+        scan(argv[i]);
+    }
+
+    return 0;
+}
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index 1972a1c..db49fe4 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -32,6 +32,8 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
+    virtual sp<MetaData> getMetaData();
+
 protected:
     virtual ~AMRExtractor();
 
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
new file mode 100644
index 0000000..79931ac
--- /dev/null
+++ b/media/libstagefright/include/ID3.h
@@ -0,0 +1,87 @@
+/*
+ * 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 ID3_H_
+
+#define ID3_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct DataSource;
+struct String8;
+
+struct ID3 {
+    enum Version {
+        ID3_UNKNOWN,
+        ID3_V2_2,
+        ID3_V2_3
+    };
+
+    ID3(const sp<DataSource> &source);
+    ~ID3();
+
+    bool isValid() const;
+
+    Version version() const;
+
+    const void *getAlbumArt(size_t *length, String8 *mime) const;
+
+    struct Iterator {
+        Iterator(const ID3 &parent, const char *id);
+        ~Iterator();
+
+        bool done() const;
+        void getID(String8 *id) const;
+        void getString(String8 *s) const;
+        const uint8_t *getData(size_t *length) const;
+        void next();
+
+    private:
+        const ID3 &mParent;
+        char *mID;
+        size_t mOffset;
+
+        const uint8_t *mFrameData;
+        size_t mFrameSize;
+
+        void findFrame();
+
+        size_t getHeaderLength() const;
+
+        Iterator(const Iterator &);
+        Iterator &operator=(const Iterator &);
+    };
+
+private:
+    bool mIsValid;
+    uint8_t *mData;
+    size_t mSize;
+    size_t mFirstFrameOffset;
+    Version mVersion;
+
+    bool parse(const sp<DataSource> &source);
+    void removeUnsynchronization();
+
+    ID3(const ID3 &);
+    ID3 &operator=(const ID3 &);
+};
+
+}  // namespace android
+
+#endif  // ID3_H_
+
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index b5a6b3c..3ce6df3 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -34,6 +34,8 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
+    virtual sp<MetaData> getMetaData();
+
 protected:
     virtual ~MP3Extractor();
 
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index ce4736d..0e360e8 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -31,9 +31,11 @@
     // Extractor assumes ownership of "source".
     MPEG4Extractor(const sp<DataSource> &source);
 
-    size_t countTracks();
-    sp<MediaSource> getTrack(size_t index);
-    sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+    virtual sp<MetaData> getMetaData();
 
 protected:
     virtual ~MPEG4Extractor();
@@ -49,6 +51,7 @@
 
     sp<DataSource> mDataSource;
     bool mHaveMetadata;
+    bool mHasVideo;
 
     Track *mFirstTrack, *mLastTrack;
 
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 16127d7..b80387f 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -1,19 +1,18 @@
 /*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * 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 STAGEFRIGHT_METADATA_RETRIEVER_H_
 
@@ -22,9 +21,11 @@
 #include <media/MediaMetadataRetrieverInterface.h>
 
 #include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
 
 namespace android {
 
+struct DataSource;
 class MediaExtractor;
 
 struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
@@ -40,8 +41,15 @@
 
 private:
     OMXClient mClient;
+    sp<DataSource> mSource;
     sp<MediaExtractor> mExtractor;
 
+    bool mParsedMetaData;
+    KeyedVector<int, String8> mMetaData;
+    MediaAlbumArt *mAlbumArt;
+
+    void parseMetaData();
+
     StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
 
     StagefrightMetadataRetriever &operator=(
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 10b9700..8545efc 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -34,6 +34,8 @@
     virtual sp<MediaSource> getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
+    virtual sp<MetaData> getMetaData();
+
 protected:
     virtual ~WAVExtractor();
 
diff --git a/preloaded-classes b/preloaded-classes
index ddcecc9..ec4d74c 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -225,7 +225,6 @@
 android.net.Uri$PathSegments
 android.net.Uri$StringUri
 android.net.WebAddress
-android.net.http.DomainNameChecker
 android.net.http.CertificateChainValidator
 android.net.http.EventHandler
 android.net.http.HttpsConnection
diff --git a/services/java/com/android/server/INativeDaemonConnectorCallbacks.java b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java
new file mode 100644
index 0000000..6fbf713
--- /dev/null
+++ b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+interface INativeDaemonConnectorCallbacks {
+
+    void onDaemonConnected();
+    boolean onEvent(int code, String raw, String[] cooked);
+}
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
deleted file mode 100644
index 9443ff8..0000000
--- a/services/java/com/android/server/MountListener.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
-import android.os.Environment;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Config;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.lang.IllegalStateException;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.ListIterator;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Vold Connection class
- */
-final class MountListener implements Runnable {
-    private static final String TAG = "MountListener";
-    private static final String VOLD_SOCKET = "vold";
-    private static final int    RESPONSE_QUEUE_SIZE = 10;
-
-    private MountService          mService;
-    private BlockingQueue<String> mResponseQueue;
-    private OutputStream          mOutputStream;
-
-    class ResponseCode {
-        public static final int ActionInitiated                = 100;
-        public static final int VolumeListResult               = 110;
-        public static final int AsecListResult                 = 111;
-
-        public static final int CommandOkay                    = 200;
-        public static final int ShareAvailabilityResult        = 210;
-        public static final int AsecPathResult                 = 211;
-
-        public static final int UnsolicitedInformational       = 600;
-        public static final int VolumeStateChange              = 605;
-        public static final int VolumeMountFailedBlank         = 610;
-        public static final int VolumeMountFailedDamaged       = 611;
-        public static final int VolumeMountFailedNoMedia       = 612;
-        public static final int ShareAvailabilityChange        = 620;
-        public static final int VolumeDiskInserted             = 630;
-        public static final int VolumeDiskRemoved              = 631;
-        public static final int VolumeBadRemoval               = 632;
-    }
-
-    MountListener(MountService service) {
-        mService = service;
-        mResponseQueue = new LinkedBlockingQueue<String>(RESPONSE_QUEUE_SIZE);
-    }
-
-    public void run() {
-        // Vold does not run in the simulator, so fake out a mounted event to trigger the Media Scanner
-        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-            mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false);
-            return;
-        }
-
-        try {
-            while (true) {
-                listenToSocket();
-            }
-        } catch (Throwable t) {
-            Log.e(TAG, "Fatal error " + t + " in MountListener thread!");
-        }
-    }
-
-    private void listenToSocket() {
-       LocalSocket socket = null;
-
-        try {
-            socket = new LocalSocket();
-            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, 
-                    LocalSocketAddress.Namespace.RESERVED);
-
-            socket.connect(address);
-            mService.onVoldConnected();
-
-            InputStream inputStream = socket.getInputStream();
-            mOutputStream = socket.getOutputStream();
-
-            byte[] buffer = new byte[4096];
-
-            while (true) {
-                int count = inputStream.read(buffer);
-                if (count < 0) break;
-
-                int start = 0;
-                for (int i = 0; i < count; i++) {
-                    if (buffer[i] == 0) {
-                        String event = new String(buffer, start, i - start);
-//                        Log.d(TAG, "Got packet {" + event + "}");
-
-                        String[] tokens = event.split(" ");
-                        try {
-                            int code = Integer.parseInt(tokens[0]);
-
-                            if (code >= ResponseCode.UnsolicitedInformational) {
-                                try {
-                                    handleUnsolicitedEvent(code, event, tokens);
-                                } catch (Exception ex) {
-                                    Log.e(TAG, String.format(
-                                            "Error handling '%s'", event), ex);
-                                }
-                            } else {
-                                try {
-                                    mResponseQueue.put(event);
-                                } catch (InterruptedException ex) {
-                                    Log.e(TAG, "InterruptedException");
-                                }
-                            }
-                        } catch (NumberFormatException nfe) {
-                            Log.w(TAG,
-                                  "Unknown msg from Vold '" + event + "'");
-                        }
-                        start = i + 1;
-                    }                   
-                }
-            }                
-        } catch (IOException ex) {
-            Log.e(TAG, "IOException in listenToSocket");
-        }
-        
-        synchronized (this) {
-            if (mOutputStream != null) {
-                try {
-                    mOutputStream.close();
-                } catch (IOException e) {
-                    Log.w(TAG, "IOException closing output stream");
-                }
-                
-                mOutputStream = null;
-            }
-        }
-        
-        try {
-            if (socket != null) {
-                socket.close();
-            }
-        } catch (IOException ex) {
-            Log.w(TAG, "IOException closing socket");
-        }
-       
-        Log.e(TAG, "Failed to connect to Vold", new IllegalStateException());
-        SystemClock.sleep(5000);
-    }
-
-    private void handleUnsolicitedEvent(int code, String raw,
-                                        String[] cooked) throws IllegalStateException {
-//        Log.d(TAG, "unsolicited {" + raw + "}");
-        if (code == ResponseCode.VolumeStateChange) {
-            // FMT: NNN Volume <label> <mountpoint> state changed from <old_#> (<old_str>) to <new_#> (<new_str>)
-            mService.notifyVolumeStateChange(cooked[2], cooked[3],
-                                             Integer.parseInt(cooked[7]),
-                                             Integer.parseInt(cooked[10]));
-        } else if (code == ResponseCode.VolumeMountFailedBlank) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
-            mService.notifyMediaNoFs(cooked[3]);
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
-        } else if (code == ResponseCode.VolumeMountFailedNoMedia) {
-            mService.notifyMediaRemoved(cooked[3]);
-        } else if (code == ResponseCode.VolumeMountFailedDamaged) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
-            mService.notifyMediaUnmountable(cooked[3]);
-        } else if (code == ResponseCode.ShareAvailabilityChange) {
-            // FMT: NNN Share method <method> now <available|unavailable>
-            boolean avail = false;
-            if (cooked[5].equals("available")) {
-                avail = true;
-            }
-            mService.notifyShareAvailabilityChange(cooked[3], avail);
-        } else if (code == ResponseCode.VolumeDiskInserted) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            mService.notifyMediaInserted(cooked[3]);
-        } else if (code == ResponseCode.VolumeDiskRemoved) {
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            mService.notifyMediaRemoved(cooked[3]);
-        } else if (code == ResponseCode.VolumeBadRemoval) {
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            mService.notifyMediaBadRemoval(cooked[3]);
-        } else {
-            Log.w(TAG, "Unhandled event {" + raw + "}");
-        }
-    }
-    
-
-    private void sendCommand(String command) {
-        sendCommand(command, null);
-    }
-
-    /**
-     * Sends a command to Vold with a single argument
-     *
-     * @param command  The command to send to the mount service daemon
-     * @param argument The argument to send with the command (or null)
-     */
-    private void sendCommand(String command, String argument) {
-        synchronized (this) {
-            // Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
-            if (mOutputStream == null) {
-                Log.e(TAG, "No connection to Vold", new IllegalStateException());
-            } else {
-                StringBuilder builder = new StringBuilder(command);
-                if (argument != null) {
-                    builder.append(argument);
-                }
-                builder.append('\0');
-
-                try {
-                    mOutputStream.write(builder.toString().getBytes());
-                } catch (IOException ex) {
-                    Log.e(TAG, "IOException in sendCommand", ex);
-                }
-            }
-        }
-    }
-
-    private synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
-        sendCommand(cmd);
-
-        ArrayList<String> response = new ArrayList<String>();
-        boolean complete = false;
-        int code = -1;
-
-        while (!complete) {
-            try {
-                String line = mResponseQueue.take();
-//                Log.d(TAG, "Removed off queue -> " + line);
-                String[] tokens = line.split(" ");
-                code = Integer.parseInt(tokens[0]);
-
-                if ((code >= 200) && (code < 600))
-                    complete = true;
-                response.add(line);
-            } catch (InterruptedException ex) {
-                Log.e(TAG, "InterruptedException");
-            }
-        }
-
-        if (code >= 400 && code < 600) {
-            throw new IllegalStateException(String.format(
-                                               "Command %s failed with code %d",
-                                                cmd, code));
-        }
-        return response;
-    }
-
-    boolean getShareAvailable(String method) throws IllegalStateException  {
-        ArrayList<String> rsp = doCommand("share_available " + method);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.ShareAvailabilityResult) {
-                if (tok[2].equals("available"))
-                    return true;
-                return false;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    void setShareMethodEnabled(String mountPoint, String method,
-                               boolean enable) throws IllegalStateException {
-        doCommand((enable ? "" : "un") + "share " + mountPoint + " " + method);
-    }
-
-    /**
-     * Mount media at given mount point.
-     */
-    public void mountVolume(String label) throws IllegalStateException {
-        doCommand("mount " + label);
-    }
-
-    /**
-     * Unmount media at given mount point.
-     */
-    public void unmountVolume(String label) throws IllegalStateException {
-        doCommand("unmount " + label);
-    }
-
-    /**
-     * Format media at given mount point.
-     */
-    public void formatVolume(String label) throws IllegalStateException {
-        doCommand("format " + label);
-    }
-
-    public String createAsec(String id, int sizeMb, String fstype, String key,
-                           int ownerUid) throws IllegalStateException {
-        String cmd = String.format("create_asec %s %d %s %s %d",
-                                   id, sizeMb, fstype, key, ownerUid);
-        doCommand(cmd);
-        return getAsecPath(id);
-    }
-
-    public void finalizeAsec(String id) throws IllegalStateException {
-        doCommand("finalize_asec " + id);
-    }
-
-    public void destroyAsec(String id) throws IllegalStateException {
-        doCommand("destroy_asec " + id);
-    }
-
-    public String mountAsec(String id, String key, int ownerUid) throws IllegalStateException {
-        String cmd = String.format("mount_asec %s %s %d",
-                                   id, key, ownerUid);
-        doCommand(cmd);
-        return getAsecPath(id);
-    }
-
-    public String getAsecPath(String id) throws IllegalStateException {
-        ArrayList<String> rsp = doCommand("asec_path " + id);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.AsecPathResult) {
-                return tok[1];
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    public String[] listAsec() throws IllegalStateException {
-        ArrayList<String> rsp = doCommand("list_asec");
-
-        String[] rdata = new String[rsp.size()];
-        int idx = 0;
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.AsecListResult) {
-                rdata[idx++] = tok[1];
-            } else if (code == ResponseCode.CommandOkay) {
-                return rdata;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index cd17bd2..c8a6915 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.text.TextUtils;
 import android.util.Log;
+import java.util.ArrayList;
 
 import android.provider.Settings;
 import android.content.ContentResolver;
@@ -46,7 +47,8 @@
  * MountService implements an to the mount service daemon
  * @hide
  */
-class MountService extends IMountService.Stub {
+class MountService extends IMountService.Stub
+        implements INativeDaemonConnectorCallbacks {
     
     private static final String TAG = "MountService";
 
@@ -63,15 +65,33 @@
         public static final int SharedMnt  = 8;
     }
 
+    class VoldResponseCode {
+        public static final int VolumeListResult               = 110;
+        public static final int AsecListResult                 = 111;
+
+        public static final int ShareAvailabilityResult        = 210;
+        public static final int AsecPathResult                 = 211;
+
+        public static final int VolumeStateChange              = 605;
+        public static final int VolumeMountFailedBlank         = 610;
+        public static final int VolumeMountFailedDamaged       = 611;
+        public static final int VolumeMountFailedNoMedia       = 612;
+        public static final int ShareAvailabilityChange        = 620;
+        public static final int VolumeDiskInserted             = 630;
+        public static final int VolumeDiskRemoved              = 631;
+        public static final int VolumeBadRemoval               = 632;
+    }
+
+
     /**
      * Binder context for this service
      */
     private Context mContext;
     
     /**
-     * listener object for communicating with the mount service daemon
+     * connectorr object for communicating with vold
      */
-    private MountListener mListener;
+    private NativeDaemonConnector mConnector;
 
     /**
      * The notification that is shown when a USB mass storage host
@@ -119,12 +139,12 @@
         mContext = context;
 
         // Register a BOOT_COMPLETED handler so that we can start
-        // MountListener. We defer the startup so that we don't
+        // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
         mContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
-        mListener =  new MountListener(this);       
+        mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
         mShowSafeUnmountNotificationWhenUnmounted = false;
 
         mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
@@ -202,7 +222,18 @@
             String action = intent.getAction();
 
             if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                Thread thread = new Thread(mListener, MountListener.class.getName());
+                /*
+                 * Vold does not run in the simulator, so fake out a mounted
+                 * event to trigger MediaScanner
+                 */
+                if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+                    notifyMediaMounted(
+                            Environment.getExternalStorageDirectory().getPath(), false);
+                    return;
+                }
+
+                Thread thread = new Thread(
+                        mConnector, NativeDaemonConnector.class.getName());
                 thread.start();
             }
         }
@@ -215,7 +246,7 @@
             throw new SecurityException("Requires SHUTDOWN permission");
         }
 
-        Log.i(TAG, "Shutting down");
+        Log.d(TAG, "Shutting down");
         String state = Environment.getExternalStorageState();
 
         if (state.equals(Environment.MEDIA_SHARED)) {
@@ -258,7 +289,21 @@
              */
             try {
                 String m = Environment.getExternalStorageDirectory().toString();
-                unmountMedia(m);
+                unmountVolume(m);
+
+                int retries = 12;
+                while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException iex) {
+                        Log.e(TAG, "Interrupted while waiting for media", iex);
+                        break;
+                    }
+                    state = Environment.getExternalStorageState();
+                }
+                if (retries == 0) {
+                    Log.e(TAG, "Timed out waiting for media to unmount");
+                }
             } catch (Exception e) {
                 Log.e(TAG, "external storage unmount failed", e);
             }
@@ -288,17 +333,14 @@
             String vs = getVolumeState(vp);
 
             if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-                mListener.unmountVolume(vp);
+                unmountVolume(vp);
                 updateUsbMassStorageNotification(true, false);
             }
 
-            mListener.setShareMethodEnabled(Environment
-                                            .getExternalStorageDirectory()
-                                            .getPath(),
-                                            "ums", enable);
+            setShareMethodEnabled(vp, "ums", enable);
             mUmsEnabled = enable;
             if (!enable) {
-                mountMedia(vp);
+                mountVolume(vp);
                 if (mPromptUms) {
                     updateUsbMassStorageNotification(false, false);
                 } else {
@@ -338,19 +380,19 @@
     /**
      * Attempt to mount external media
      */
-    public void mountMedia(String mountPath) throws IllegalStateException {
+    public void mountVolume(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
         }
-        mListener.mountVolume(mountPath);
+        mConnector.doCommand(String.format("mount %s", mountPath));
     }
 
     /**
      * Attempt to unmount external media to prepare for eject
      */
-    public void unmountMedia(String mountPath) throws IllegalStateException {
+    public void unmountVolume(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
@@ -361,23 +403,51 @@
         // to display the notification
         mShowSafeUnmountNotificationWhenUnmounted = true;
 
-        // tell mountd to unmount the media
-        mListener.unmountVolume(mountPath);
+        mConnector.doCommand(String.format("unmount %s", mountPath));
     }
 
     /**
      * Attempt to format external media
      */
-    public void formatMedia(String formatPath) throws IllegalStateException {
+    public void formatVolume(String formatPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
         }
 
-        mListener.formatVolume(formatPath);
+        mConnector.doCommand(String.format("format %s", formatPath));
     }
 
+    boolean getShareAvailable(String method) throws IllegalStateException  {
+        ArrayList<String> rsp = mConnector.doCommand("share_available " + method);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.ShareAvailabilityResult) {
+                if (tok[2].equals("available"))
+                    return true;
+                return false;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    /**
+     * Enables or disables USB mass storage support.
+     * 
+     * @param enable  true to enable USB mass storage support
+     */
+    void setShareMethodEnabled(String mountPoint, String method,
+                               boolean enable) throws IllegalStateException {
+        mConnector.doCommand(String.format(
+                "%sshare %s %s", (enable ? "" : "un"), mountPoint, method));
+    }
+
+
     /**
      * Returns true if we're playing media notification sounds.
      */
@@ -403,7 +473,7 @@
             Log.w(TAG, "Multiple volumes not currently supported");
             return;
         }
-        Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
+        Log.i(TAG, "State for {" + mountPoint + "} = {" + state + "}");
         mLegacyState = state;
     }
 
@@ -441,14 +511,18 @@
         }
     }
 
-    void onVoldConnected() {
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public void onDaemonConnected() {
         new Thread() {
             public void run() {
                 try {
                     if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
                                  .equals(Environment.MEDIA_MOUNTED)) {
                         try {
-                            mountMedia(Environment.getExternalStorageDirectory().getPath());
+                            mountVolume(Environment.getExternalStorageDirectory().getPath());
                         } catch (Exception ex) {
                             Log.w(TAG, "Connection-mount failed");
                         }
@@ -460,7 +534,7 @@
                 }
 
                 try {
-                    boolean avail = mListener.getShareAvailable("ums");
+                    boolean avail = getShareAvailable("ums");
                     notifyShareAvailabilityChange("ums", avail);
                 } catch (Exception ex) {
                     Log.w(TAG, "Failed to get share availability");
@@ -469,6 +543,49 @@
         }.start();
     }
 
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public boolean onEvent(int code, String raw, String[] cooked) {
+        // Log.d(TAG, "event {" + raw + "}");
+        if (code == VoldResponseCode.VolumeStateChange) {
+            // FMT: NNN Volume <label> <mountpoint> state changed
+            // from <old_#> (<old_str>) to <new_#> (<new_str>)
+            notifyVolumeStateChange(
+                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
+                            Integer.parseInt(cooked[10]));
+        } else if (code == VoldResponseCode.VolumeMountFailedBlank) {
+            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
+            notifyMediaNoFs(cooked[3]);
+            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
+        } else if (code == VoldResponseCode.VolumeMountFailedNoMedia) {
+            notifyMediaRemoved(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeMountFailedDamaged) {
+            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
+            notifyMediaUnmountable(cooked[3]);
+        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
+            // FMT: NNN Share method <method> now <available|unavailable>
+            boolean avail = false;
+            if (cooked[5].equals("available")) {
+                avail = true;
+            }
+            notifyShareAvailabilityChange(cooked[3], avail);
+        } else if (code == VoldResponseCode.VolumeDiskInserted) {
+            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
+            notifyMediaInserted(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
+            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
+            notifyMediaRemoved(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeBadRemoval) {
+            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+            notifyMediaBadRemoval(cooked[3]);
+        } else {
+            return false;
+        }
+       return true;
+    }
+
     void notifyVolumeStateChange(String label, String mountPoint, int oldState,
                                  int newState) throws IllegalStateException {
         String vs = getVolumeState(mountPoint);
@@ -573,7 +690,7 @@
         new Thread() {
             public void run() {
                 try {
-                    mountMedia(path);
+                    mountVolume(path);
                 } catch (Exception ex) {
                     Log.w(TAG, "Failed to mount media on insertion", ex);
                 }
@@ -889,29 +1006,62 @@
     }
 
     public String[] getSecureContainerList() throws IllegalStateException {
-        return mListener.listAsec();
+        ArrayList<String> rsp = mConnector.doCommand("list_asec");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.AsecListResult) {
+                rdata[idx++] = tok[1];
+            } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
     }
 
     public String createSecureContainer(String id, int sizeMb, String fstype,
                                     String key, int ownerUid) throws IllegalStateException {
-        return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
+        String cmd = String.format("create_asec %s %d %s %s %d",
+                                   id, sizeMb, fstype, key, ownerUid);
+        mConnector.doCommand(cmd);
+        return getSecureContainerPath(id);
     }
 
     public void finalizeSecureContainer(String id) throws IllegalStateException {
-        mListener.finalizeAsec(id);
+        mConnector.doCommand(String.format("finalize_asec %s", id));
     }
 
     public void destroySecureContainer(String id) throws IllegalStateException {
-        mListener.destroyAsec(id);
+        mConnector.doCommand(String.format("destroy_asec %s", id));
     }
    
-    public String mountSecureContainer(String id, String key, int ownerUid) throws IllegalStateException {
-        return mListener.mountAsec(id, key, ownerUid);
+    public String mountSecureContainer(String id, String key,
+                                       int ownerUid) throws IllegalStateException {
+        String cmd = String.format("mount_asec %s %s %d",
+                                   id, key, ownerUid);
+        mConnector.doCommand(cmd);
+        return getSecureContainerPath(id);
     }
 
     public String getSecureContainerPath(String id) throws IllegalStateException {
-        return mListener.getAsecPath(id);
-    }
+        ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
 
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.AsecPathResult) {
+                return tok[1];
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
 }
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
new file mode 100644
index 0000000..da3e562
--- /dev/null
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.net.LocalSocketAddress;
+import android.net.LocalSocket;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.lang.IllegalStateException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Generic connector class for interfacing with a native
+ * daemon which uses the libsysutils FrameworkListener
+ * protocol.
+ */
+final class NativeDaemonConnector implements Runnable {
+
+    private BlockingQueue<String> mResponseQueue;
+    private OutputStream          mOutputStream;
+    private String                TAG = "NativeDaemonConnector";
+    private String                mSocket;
+    private INativeDaemonConnectorCallbacks mCallbacks;
+
+    class ResponseCode {
+        public static final int ActionInitiated                = 100;
+
+        public static final int CommandOkay                    = 200;
+
+        // The range of 400 -> 599 is reserved for cmd failures
+        public static final int OperationFailed                = 400;
+        public static final int CommandSyntaxError             = 500;
+        public static final int CommandParameterError          = 501;
+
+        public static final int UnsolicitedInformational       = 600;
+
+        //
+        public static final int FailedRangeStart               = 400;
+        public static final int FailedRangeEnd                 = 599;
+    }
+
+    NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
+                          String socket, int responseQueueSize, String logTag) {
+        mCallbacks = callbacks;
+        if (logTag != null)
+            TAG = logTag;
+        mSocket = socket;
+        mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
+    }
+
+    public void run() {
+
+        while (true) {
+            try {
+                listenToSocket();
+            } catch (Exception e) {
+                Log.e(TAG, "Error in NativeDaemonConnector", e);
+                SystemClock.sleep(1000);
+            }
+        }
+    }
+
+    private void listenToSocket() {
+       LocalSocket socket = null;
+
+        try {
+            socket = new LocalSocket();
+            LocalSocketAddress address = new LocalSocketAddress(mSocket,
+                    LocalSocketAddress.Namespace.RESERVED);
+
+            socket.connect(address);
+            mCallbacks.onDaemonConnected();
+
+            InputStream inputStream = socket.getInputStream();
+            mOutputStream = socket.getOutputStream();
+
+            byte[] buffer = new byte[4096];
+
+            while (true) {
+                int count = inputStream.read(buffer);
+                if (count < 0) break;
+
+                int start = 0;
+                for (int i = 0; i < count; i++) {
+                    if (buffer[i] == 0) {
+                        String event = new String(buffer, start, i - start);
+//                        Log.d(TAG, "Got packet {" + event + "}");
+
+                        String[] tokens = event.split(" ");
+                        try {
+                            int code = Integer.parseInt(tokens[0]);
+
+                            if (code >= ResponseCode.UnsolicitedInformational) {
+                                try {
+                                    if (!mCallbacks.onEvent(code, event, tokens)) {
+                                        Log.w(TAG, String.format(
+                                                "Unhandled event (%s)", event));
+                                    }
+                                } catch (Exception ex) {
+                                    Log.e(TAG, String.format(
+                                            "Error handling '%s'", event), ex);
+                                }
+                            } else {
+                                try {
+                                    mResponseQueue.put(event);
+                                } catch (InterruptedException ex) {
+                                    Log.e(TAG, "Failed to put response onto queue", ex);
+                                }
+                            }
+                        } catch (NumberFormatException nfe) {
+                            Log.w(TAG, String.format("Bad msg (%s)", event));
+                        }
+                        start = i + 1;
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Communications error", ex);
+        }
+
+        synchronized (this) {
+            if (mOutputStream != null) {
+                try {
+                    mOutputStream.close();
+                } catch (IOException e) {
+                    Log.w(TAG, "Failed closing output stream", e);
+                }
+
+                mOutputStream = null;
+            }
+        }
+
+        try {
+            if (socket != null) {
+                socket.close();
+            }
+        } catch (IOException ex) {
+            Log.w(TAG, "Failed closing socket", ex);
+        }
+
+        Log.e(TAG, "Failed to connect to native daemon",
+                new IllegalStateException());
+        SystemClock.sleep(5000);
+    }
+
+    private void sendCommand(String command) {
+        sendCommand(command, null);
+    }
+
+    /**
+     * Sends a command to the daemon with a single argument
+     *
+     * @param command  The command to send to the daemon
+     * @param argument The argument to send with the command (or null)
+     */
+    private void sendCommand(String command, String argument) {
+        synchronized (this) {
+             Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
+            if (mOutputStream == null) {
+                Log.e(TAG, "No connection to daemon", new IllegalStateException());
+            } else {
+                StringBuilder builder = new StringBuilder(command);
+                if (argument != null) {
+                    builder.append(argument);
+                }
+                builder.append('\0');
+
+                try {
+                    mOutputStream.write(builder.toString().getBytes());
+                } catch (IOException ex) {
+                    Log.e(TAG, "IOException in sendCommand", ex);
+                }
+            }
+        }
+    }
+
+    public synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
+        sendCommand(cmd);
+
+        ArrayList<String> response = new ArrayList<String>();
+        boolean complete = false;
+        int code = -1;
+
+        while (!complete) {
+            try {
+                String line = mResponseQueue.take();
+//                Log.d(TAG, "Removed off queue -> " + line);
+                String[] tokens = line.split(" ");
+                try {
+                    code = Integer.parseInt(tokens[0]);
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalStateException(
+                            String.format("Invalid response from daemon (%s)", line));
+                }
+
+                if ((code >= 200) && (code < 600))
+                    complete = true;
+                response.add(line);
+            } catch (InterruptedException ex) {
+                Log.e(TAG, "InterruptedException");
+            }
+        }
+
+        if (code >= ResponseCode.FailedRangeStart &&
+                code <= ResponseCode.FailedRangeEnd) {
+            throw new IllegalStateException(String.format(
+                                               "Command %s failed with code %d",
+                                                cmd, code));
+        }
+        return response;
+    }
+}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 2b12268..170477f 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -135,8 +135,8 @@
     static final int SCAN_NO_DEX = 1<<1;
     static final int SCAN_FORCE_DEX = 1<<2;
     static final int SCAN_UPDATE_SIGNATURE = 1<<3;
-    static final int SCAN_FORWARD_LOCKED = 1<<4;
-    static final int SCAN_NEW_INSTALL = 1<<5;
+    static final int SCAN_NEW_INSTALL = 1<<4;
+    static final int SCAN_NO_PATHS = 1<<5;
 
     final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
             Process.THREAD_PRIORITY_BACKGROUND);
@@ -281,8 +281,10 @@
     final HashMap<String, ArrayList<String>> mPendingBroadcasts
             = new HashMap<String, ArrayList<String>>();
     static final int SEND_PENDING_BROADCAST = 1;
+    static final int DESTROY_SD_CONTAINER = 2;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
+    static final int DESTROY_SD_CONTAINER_DELAY = 30 * 1000;
 
     class PackageHandler extends Handler {
         PackageHandler(Looper looper) {
@@ -290,6 +292,15 @@
         }
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case DESTROY_SD_CONTAINER:
+                    String pkgName = (String) msg.obj;
+                    if (pkgName != null) {
+                        // Too bad we cannot handle the errors from destroying the containers.
+                        if (!destroySdDir(pkgName)) {
+                            Log.e(TAG, "Failed to destroy container for pkg : " + pkgName);
+                        }
+                    }
+                    break;
                 case SEND_PENDING_BROADCAST : {
                     String packages[];
                     ArrayList components[];
@@ -444,7 +455,9 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                     startTime);
 
-            int scanMode = SCAN_MONITOR;
+            // Set flag to monitor and not change apk file paths when
+            // scanning install directories.
+            int scanMode = SCAN_MONITOR | SCAN_NO_PATHS;
             if (mNoDexOpt) {
                 Log.w(TAG, "Running ENG build: no pre-dexopt!");
                 scanMode |= SCAN_NO_DEX;
@@ -594,7 +607,7 @@
             mDrmAppInstallObserver = new AppDirObserver(
                 mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
             mDrmAppInstallObserver.startWatching();
-            scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode | SCAN_FORWARD_LOCKED);
+            scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode);
 
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
@@ -1961,12 +1974,7 @@
         int i;
         for (i=0; i<files.length; i++) {
             File file = new File(dir, files[i]);
-            File resFile = file;
-            // Pick up the resource path from settings for fwd locked apps
-            if ((scanMode & SCAN_FORWARD_LOCKED) != 0) {
-                resFile = null;
-            }
-            PackageParser.Package pkg = scanPackageLI(file, file, resFile,
+            PackageParser.Package pkg = scanPackageLI(file,
                     flags|PackageParser.PARSE_MUST_BE_APK, scanMode);
         }
     }
@@ -2009,14 +2017,15 @@
      *  Returns null in case of errors and the error code is stored in mLastScanError
      */
     private PackageParser.Package scanPackageLI(File scanFile,
-            File destCodeFile, File destResourceFile, int parseFlags,
+            int parseFlags,
             int scanMode) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser(scanFile.getPath());
         pp.setSeparateProcesses(mSeparateProcesses);
         final PackageParser.Package pkg = pp.parsePackage(scanFile,
-                destCodeFile.getAbsolutePath(), mMetrics, parseFlags);
+                scanFile.getPath(),
+                mMetrics, parseFlags);
         if (pkg == null) {
             mLastScanError = pp.getParseError();
             return null;
@@ -2062,16 +2071,12 @@
         }
         // The apk is forward locked (not public) if its code and resources
         // are kept in different files.
+        // TODO grab this value from PackageSettings
         if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
-            scanMode |= SCAN_FORWARD_LOCKED;
-        }
-        File resFile = destResourceFile;
-        if (ps != null && ((scanMode & SCAN_FORWARD_LOCKED) != 0)) {
-            resFile = getFwdLockedResource(ps.name);
+            parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
         }
         // Note that we invoke the following method only if we are about to unpack an application
-        return scanPackageLI(scanFile, destCodeFile, resFile,
-                pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
+        return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
     }
 
     private static String fixProcessName(String defProcessName,
@@ -2138,7 +2143,7 @@
             try {
                 if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
                     ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,
-                            !pkg.mForwardLocked);
+                            !isForwardLocked(pkg));
                     pkg.mDidDexOpt = true;
                     performed = true;
                 }
@@ -2164,9 +2169,8 @@
     }
     
     private PackageParser.Package scanPackageLI(
-        File scanFile, File destCodeFile, File destResourceFile,
         PackageParser.Package pkg, int parseFlags, int scanMode) {
-
+        File scanFile = new File(pkg.mScanPath);
         mScanningPath = scanFile;
         if (pkg == null) {
             mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
@@ -2223,6 +2227,43 @@
             return null;
         }
 
+        // Initialize package source and resource directories
+        File destResourceFile = null;
+        File destCodeFile = null;
+        if ((scanMode & SCAN_NO_PATHS) == 0) {
+            boolean fwdLocked = (parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0;
+            final String pkgFileName = pkgName + ".apk";
+            File destDir = null;
+
+            if (fwdLocked) {
+                destDir = mDrmAppPrivateInstallDir;
+                destResourceFile = new File(mAppInstallDir, pkgName + ".zip");
+            } else {
+                boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
+                if (!onSd) {
+                    destDir = mAppInstallDir;
+                } else {
+                    String cachePath = getSdDir(pkgName);
+                    if (cachePath == null) {
+                        Log.e(TAG, "Secure container path for pkg: " + pkgName + " at location: " + cachePath +
+                                " not found");
+                        mLastScanError = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                        return null;
+                    }
+                    destDir = new File(cachePath);
+                }
+                destResourceFile = new File(destDir, pkgFileName);
+            }
+            destCodeFile = new File(destDir, pkgFileName);
+            pkg.mPath = destCodeFile.getAbsolutePath();
+        } else {
+            pkg.mPath = pkg.mScanPath;
+            destCodeFile = new File(pkg.mScanPath);
+            destResourceFile = new File(pkg.mScanPath);
+        }
+        pkg.applicationInfo.sourceDir = destCodeFile.getAbsolutePath();
+        pkg.applicationInfo.publicSourceDir = destResourceFile.getAbsolutePath();
+
         SharedUserSetting suid = null;
         PackageSetting pkgSetting = null;
 
@@ -2394,7 +2435,6 @@
                 pkg.applicationInfo.packageName,
                 pkg.applicationInfo.processName,
                 pkg.applicationInfo.uid);
-        pkg.applicationInfo.publicSourceDir = destResourceFile.toString();
 
         File dataPath;
         if (mPlatformPackage == pkg) {
@@ -2509,8 +2549,6 @@
                     return null;
                 }
             }
-
-            pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0;
             pkg.mScanPath = path;
 
             if ((scanMode&SCAN_NO_DEX) == 0) {
@@ -2545,7 +2583,7 @@
         }
         synchronized (mPackages) {
             // Add the new setting to mSettings
-            mSettings.insertPackageSettingLP(pkgSetting, pkg, destCodeFile, destResourceFile);
+            mSettings.insertPackageSettingLP(pkgSetting, pkg);
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
             int N = pkg.providers.size();
@@ -3714,11 +3752,11 @@
                 if ((event&ADD_EVENTS) != 0) {
                     PackageParser.Package p = mAppDirs.get(fullPathStr);
                     if (p == null) {
-                        p = scanPackageLI(fullPath, fullPath, fullPath,
+                        p = scanPackageLI(fullPath,
                                 (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
                                 PackageParser.PARSE_CHATTY |
                                 PackageParser.PARSE_MUST_BE_APK,
-                                SCAN_MONITOR);
+                                SCAN_MONITOR | SCAN_NO_PATHS);
                         if (p != null) {
                             synchronized (mPackages) {
                                 grantPermissionsLP(p, false);
@@ -3823,13 +3861,14 @@
     /*
      * Install a non-existing package.
      */
-    private void installNewPackageLI(String pkgName,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+    private void installNewPackageLI(PackageParser.Package pkg,
+            int parseFlags,
+            int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         boolean dataDirExists;
+        String pkgName = pkg.packageName;
+        boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
 
         if (useEncryptedFilesystemForPackage(pkg)) {
             dataDirExists = (new File(mSecureAppDataDir, pkgName)).exists();
@@ -3838,7 +3877,7 @@
         }
         res.name = pkgName;
         synchronized(mPackages) {
-            if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) {
+            if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
                 // Don't allow installation over an existing package with the same name.
                 Log.w(TAG, "Attempt to re-install " + pkgName
                         + " without first uninstalling.");
@@ -3846,32 +3885,37 @@
                 return;
             }
         }
-        if (destPackageFile.exists()) {
-            // It's safe to do this because we know (from the above check) that the file
-            // isn't currently used for an installed package.
-            destPackageFile.delete();
-        }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-        PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                destResourceFile, pkg, 0,
-                SCAN_MONITOR | SCAN_FORCE_DEX
-                | SCAN_UPDATE_SIGNATURE
-                | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                | (newInstall ? SCAN_NEW_INSTALL : 0));
+        if (onSd) {
+            // Create secure container mount point for package
+            String cPath = createSdDir(new File(pkg.mScanPath), pkgName);
+            if (cPath == null) {
+                mLastScanError = res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                return;
+            }
+        }
+        PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode);
         if (newPackage == null) {
-            Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+            Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                 res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
             }
         } else {
-            updateSettingsLI(pkgName, tmpPackageFile,
-                    destFilePath, destPackageFile,
-                    destResourceFile, pkg,
-                    newPackage,
-                    true,
-                    forwardLocked,
+            File destPackageFile = new File(pkg.mPath);
+            if (destPackageFile.exists()) {
+                // It's safe to do this because we know (from the above check) that the file
+                // isn't currently used for an installed package.
+                destPackageFile.delete();
+            }
+            updateSettingsLI(newPackage,
                     installerPackageName,
                     res);
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                // Check if container can be finalized
+                if(onSd && !finalizeSdDir(pkgName)) {
+                    res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                }
+            }
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
             if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -3885,15 +3929,19 @@
                                 res.removedInfo);
             }
         }
+        if (onSd && res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+            // Destroy cache
+            destroySdDir(pkgName);
+        }
     }
 
-    private void replacePackageLI(String pkgName,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+    private void replacePackageLI(PackageParser.Package pkg,
+            int parseFlags,
+            int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
 
         PackageParser.Package oldPackage;
+        String pkgName = pkg.packageName;
         // First find the old package info and check signatures
         synchronized(mPackages) {
             oldPackage = mPackages.get(pkgName);
@@ -3905,21 +3953,15 @@
         }
         boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
         if(sysPkg) {
-            replaceSystemPackageLI(oldPackage,
-                    tmpPackageFile, destFilePath,
-                    destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, installerPackageName, res);
+            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLI(oldPackage, tmpPackageFile, destFilePath,
-                    destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, installerPackageName, res);
+            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+            PackageParser.Package pkg,
+            int parseFlags, int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
@@ -3931,7 +3973,7 @@
             oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
         }
 
-        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
         // First delete the existing package while retaining the data directory
         if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
                 res.removedInfo)) {
@@ -3941,24 +3983,14 @@
         } else {
             // Successfully deleted the old package. Now proceed with re-installation
             mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-            newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                    destResourceFile, pkg, parseFlags,
-                    SCAN_MONITOR | SCAN_FORCE_DEX
-                    | SCAN_UPDATE_SIGNATURE
-                    | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                    | (newInstall ? SCAN_NEW_INSTALL : 0));
+            newPackage = scanPackageLI(pkg, parseFlags, scanMode);
             if (newPackage == null) {
-                    Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+                Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
                 if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                     res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
                 }
             } else {
-                updateSettingsLI(pkgName, tmpPackageFile,
-                        destFilePath, destPackageFile,
-                        destResourceFile, pkg,
-                        newPackage,
-                        true,
-                        forwardLocked,
+                updateSettingsLI(newPackage,
                         installerPackageName,
                         res);
                 updatedSettings = true;
@@ -4028,13 +4060,12 @@
     }
 
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+            PackageParser.Package pkg,
+            int parseFlags, int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
-        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING |
+        parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING |
                 PackageParser.PARSE_IS_SYSTEM;
         String packageName = deletedPackage.packageName;
         res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -4064,26 +4095,14 @@
         // Successfully disabled the old package. Now proceed with re-installation
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                destResourceFile, pkg, parseFlags,
-                SCAN_MONITOR | SCAN_FORCE_DEX
-                | SCAN_UPDATE_SIGNATURE
-                | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                | (newInstall ? SCAN_NEW_INSTALL : 0));
+        newPackage = scanPackageLI(pkg, parseFlags, scanMode);
         if (newPackage == null) {
-            Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+            Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                 res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
             }
         } else {
-            updateSettingsLI(packageName, tmpPackageFile,
-                    destFilePath, destPackageFile,
-                    destResourceFile, pkg,
-                    newPackage,
-                    true,
-                    forwardLocked,
-                    installerPackageName,
-                    res);
+            updateSettingsLI(newPackage, installerPackageName, res);
             updatedSettings = true;
         }
 
@@ -4102,9 +4121,7 @@
                 removePackageLI(newPackage, true);
             }
             // Add back the old system package
-            scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath,
-                    oldPkgSetting.resourcePath,
-                    oldPkg, parseFlags,
+            scanPackageLI(oldPkg, parseFlags,
                     SCAN_MONITOR
                     | SCAN_UPDATE_SIGNATURE);
             // Restore the old system information in Settings
@@ -4119,14 +4136,9 @@
         }
     }
 
-    private void updateSettingsLI(String pkgName, File tmpPackageFile,
-            String destFilePath, File destPackageFile,
-            File destResourceFile,
-            PackageParser.Package pkg,
-            PackageParser.Package newPackage,
-            boolean replacingExistingPackage,
-            boolean forwardLocked,
+    private void updateSettingsLI(PackageParser.Package newPackage,
             String installerPackageName, PackageInstalledInfo res) {
+        String pkgName = newPackage.packageName;
         synchronized (mPackages) {
             //write settings. the installStatus will be incomplete at this stage.
             //note that the new package setting would have already been
@@ -4136,11 +4148,10 @@
         }
 
         int retCode = 0;
-        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            retCode = mInstaller.movedex(tmpPackageFile.toString(),
-                    destPackageFile.toString());
+        if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
             if (retCode != 0) {
-                Log.e(TAG, "Couldn't rename dex file: " + destPackageFile);
+                Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
                 res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                 return;
             }
@@ -4148,22 +4159,25 @@
         // XXX There are probably some big issues here: upon doing
         // the rename, we have reached the point of no return (the
         // original .apk is gone!), so we can't fail.  Yet... we can.
-        if (!tmpPackageFile.renameTo(destPackageFile)) {
-            Log.e(TAG, "Couldn't move package file to: " + destPackageFile);
-            res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+        File scanFile = new File(newPackage.mScanPath);
+        if (!scanFile.renameTo(new File(newPackage.mPath))) {
+            Log.e(TAG, "Couldn't move package file: " + newPackage.mScanPath + " to: " + newPackage.mPath);
+            // TODO rename should work. Workaround
+            if (!FileUtils.copyFile(scanFile, new File(newPackage.mPath))) {
+                Log.e(TAG, "Couldn't move package file to: " + newPackage.mPath);
+                res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
         } else {
-            res.returnCode = setPermissionsLI(pkgName, newPackage, destFilePath,
-                    destResourceFile,
-                    forwardLocked);
+            res.returnCode = setPermissionsLI(newPackage);
             if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                 return;
             } else {
-                Log.d(TAG, "New package installed in " + destPackageFile);
+                Log.d(TAG, "New package installed in " + newPackage.mPath);
             }
         }
         if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
             if (mInstaller != null) {
-                mInstaller.rmdex(tmpPackageFile.getPath());
+                mInstaller.rmdex(newPackage.mScanPath);
             }
         }
 
@@ -4180,11 +4194,6 @@
         }
     }
 
-    private File getFwdLockedResource(String pkgName) {
-        final String publicZipFileName = pkgName + ".zip";
-        return new File(mAppInstallDir, publicZipFileName);
-    }
-
     private File copyTempInstallFile(Uri pPackageURI,
             PackageInstalledInfo res) {
         File tmpPackageFile = createTempPackageFile();
@@ -4246,46 +4255,29 @@
     private void installPackageLI(Uri pPackageURI,
             int pFlags, boolean newInstall, String installerPackageName,
             File tmpPackageFile, PackageInstalledInfo res) {
-        String pkgName = null;
-        boolean forwardLocked = false;
+        boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+        boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
         boolean replacingExistingPackage = false;
+        int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
+                | (newInstall ? SCAN_NEW_INSTALL : 0);
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
         main_flow: try {
-            pkgName = PackageParser.parsePackageName(
-                    tmpPackageFile.getAbsolutePath(), 0);
-            if (pkgName == null) {
-                Log.e(TAG, "Couldn't find a package name in : " + tmpPackageFile);
-                res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
-                break main_flow;
-            }
-            res.name = pkgName;
-            //initialize some variables before installing pkg
-            final String pkgFileName = pkgName + ".apk";
-            final File destDir = ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0)
-                                 ?  mDrmAppPrivateInstallDir
-                                 : mAppInstallDir;
-            final File destPackageFile = new File(destDir, pkgFileName);
-            final String destFilePath = destPackageFile.getAbsolutePath();
-            File destResourceFile;
-            if ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0) {
-                destResourceFile = getFwdLockedResource(pkgName);
-                forwardLocked = true;
-            } else {
-                destResourceFile = destPackageFile;
-            }
             // Retrieve PackageSettings and parse package
-            int parseFlags = PackageParser.PARSE_CHATTY;
+            int parseFlags = PackageParser.PARSE_CHATTY |
+                    (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
+                    (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
             parseFlags |= mDefParseFlags;
             PackageParser pp = new PackageParser(tmpPackageFile.getPath());
             pp.setSeparateProcesses(mSeparateProcesses);
             final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
-                    destPackageFile.getAbsolutePath(), mMetrics, parseFlags);
+                    null, mMetrics, parseFlags);
             if (pkg == null) {
                 res.returnCode = pp.getParseError();
                 break main_flow;
             }
+            String pkgName = res.name = pkg.packageName;
             if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
                 if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
                     res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
@@ -4306,17 +4298,11 @@
             }
 
             if(replacingExistingPackage) {
-                replacePackageLI(pkgName,
-                        tmpPackageFile,
-                        destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall, installerPackageName,
-                        res);
+                replacePackageLI(pkg, parseFlags, scanMode,
+                        installerPackageName, res);
             } else {
-                installNewPackageLI(pkgName,
-                        tmpPackageFile,
-                        destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall, installerPackageName,
-                        res);
+                installNewPackageLI(pkg, parseFlags, scanMode,
+                        installerPackageName,res);
             }
         } finally {
             if (tmpPackageFile != null && tmpPackageFile.exists()) {
@@ -4325,13 +4311,12 @@
         }
     }
 
-    private int setPermissionsLI(String pkgName,
-            PackageParser.Package newPackage,
-            String destFilePath,
-            File destResourceFile,
-            boolean forwardLocked) {
+    private int setPermissionsLI(PackageParser.Package newPackage) {
+        String pkgName = newPackage.packageName;
         int retCode;
-        if (forwardLocked) {
+        if ((newPackage.applicationInfo.flags
+                & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+            File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir);
             try {
                 extractPublicFiles(newPackage, destResourceFile);
             } catch (IOException e) {
@@ -4347,25 +4332,25 @@
             } else {
                 final int filePermissions =
                         FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP;
-                retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1,
+                retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1,
                                                    newPackage.applicationInfo.uid);
             }
         } else {
             final int filePermissions =
                     FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
                     |FileUtils.S_IROTH;
-            retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1);
+            retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, -1);
         }
         if (retCode != 0) {
-            Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath
+            Log.e(TAG, "Couldn't set new package file permissions for " +
+                    newPackage.mPath
                        + ". The return code was: " + retCode);
         }
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
-    private boolean isForwardLocked(PackageParser.Package deletedPackage) {
-        final ApplicationInfo applicationInfo = deletedPackage.applicationInfo;
-        return applicationInfo.sourceDir.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath());
+    private boolean isForwardLocked(PackageParser.Package pkg) {
+        return  ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
     }
 
     private void extractPublicFiles(PackageParser.Package newPackage,
@@ -4643,9 +4628,9 @@
             mSettings.enableSystemPackageLP(p.packageName);
         }
         // Install the system package
-        PackageParser.Package newPkg = scanPackageLI(ps.codePath, ps.codePath, ps.resourcePath,
+        PackageParser.Package newPkg = scanPackageLI(ps.codePath,
                 PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
-                SCAN_MONITOR);
+                SCAN_MONITOR | SCAN_NO_PATHS);
 
         if (newPkg == null) {
             Log.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -4749,14 +4734,32 @@
             Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
             return false;
         }
+        boolean onSd = (p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0;
+        // Mount sd container if needed
+        if (onSd) {
+            // TODO Better error handling from MountService api later
+            mountSdDir(p.packageName, Process.SYSTEM_UID) ;
+        }
+        boolean ret = false;
         if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
             Log.i(TAG, "Removing system package:"+p.packageName);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
-            return deleteSystemPackageLI(p, flags, outInfo);
+            ret = deleteSystemPackageLI(p, flags, outInfo);
+        } else {
+            Log.i(TAG, "Removing non-system package:"+p.packageName);
+            ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
         }
-        Log.i(TAG, "Removing non-system package:"+p.packageName);
-        return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
+        if (ret && onSd) {
+            // Post a delayed destroy on the container since there might
+            // be active processes holding open file handles to package
+            // resources which will get killed by the process killer when
+            // destroying the container. This might even kill the current
+            // process and crash the system. Delay the destroy a bit so
+            // that the active processes get to handle the uninstall broadcasts.
+            sendDelayedDestroySdDir(packageName);
+        }
+        return ret;
     }
 
     public void clearApplicationUserData(final String packageName,
@@ -6343,22 +6346,23 @@
             return p;
         }
 
-        private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg,
-                File codePath, File resourcePath) {
+        private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
             p.pkg = pkg;
+            String codePath = pkg.applicationInfo.sourceDir;
+            String resourcePath = pkg.applicationInfo.publicSourceDir;
             // Update code path if needed
-            if (!codePath.toString().equalsIgnoreCase(p.codePathString)) {
+            if (!codePath.equalsIgnoreCase(p.codePathString)) {
                 Log.w(TAG, "Code path for pkg : " + p.pkg.packageName +
                         " changing from " + p.codePathString + " to " + codePath);
-                p.codePath = codePath;
-                p.codePathString = codePath.toString();
+                p.codePath = new File(codePath);
+                p.codePathString = codePath;
             }
             //Update resource path if needed
-            if (!resourcePath.toString().equalsIgnoreCase(p.resourcePathString)) {
+            if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
                 Log.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
                         " changing from " + p.resourcePathString + " to " + resourcePath);
-                p.resourcePath = resourcePath;
-                p.resourcePathString = resourcePath.toString();
+                p.resourcePath = new File(resourcePath);
+                p.resourcePathString = resourcePath;
             }
             // Update version code if needed
              if (pkg.mVersionCode != p.versionCode) {
@@ -7432,4 +7436,110 @@
                        || packageSettings.enabledComponents.contains(componentInfo.name));
         }
     }
+
+    // ------- apps on sdcard specific code -------
+    static final boolean DEBUG_SD_INSTALL = false;
+    final private String mSdEncryptKey = "none";
+
+    private MountService getMountService() {
+        return (MountService) ServiceManager.getService("mount");
+    }
+
+   private String createSdDir(File tmpPackageFile, String pkgName) {
+        // Create mount point via MountService
+        MountService mountService = getMountService();
+        long len = tmpPackageFile.length();
+        int mbLen = (int) (len/(1024*1024));
+        if ((len - (mbLen * 1024 * 1024)) > 0) {
+            mbLen++;
+        }
+        if (DEBUG_SD_INSTALL) Log.i(TAG, "mbLen="+mbLen);
+        String cachePath = null;
+        // Remove any pending destroy messages
+        mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+        try {
+            cachePath = mountService.createSecureContainer(pkgName,
+                mbLen,
+                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+        }
+        // TODO just fail here and let the user delete later on.
+        try {
+            mountService.destroySecureContainer(pkgName);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Destroying cache for " + pkgName + ", cachePath =" + cachePath);
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to destroy existing cache: " + e);
+            return null;
+        } 
+       try {
+            cachePath = mountService.createSecureContainer(pkgName,
+                mbLen,
+                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+            return null;
+        }
+    }
+
+   private String mountSdDir(String pkgName, int ownerUid) {
+       try {
+           return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid);
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
+       }
+       return null;
+   }
+
+   private String getSdDir(String pkgName) {
+       String cachePath = null;
+       try {
+           cachePath = getMountService().getSecureContainerPath(pkgName);
+       } catch (IllegalStateException e) {
+           Log.e(TAG, "Failed to retrieve secure container path for pkg : " + pkgName + " with exception " + e);
+       }
+       return cachePath;
+   }
+
+   private boolean finalizeSdDir(String pkgName) {
+       try {
+           getMountService().finalizeSecureContainer(pkgName);
+           return true;
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+           return false;
+       }
+   }
+
+   private boolean destroySdDir(String pkgName) {
+       try {
+           if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
+               // Don't have to send message again
+               mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+           }
+           // We need to destroy right away
+           getMountService().destroySecureContainer(pkgName);
+           return true;
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+           return false;
+       }
+   }
+
+   private void sendDelayedDestroySdDir(String pkgName) {
+       if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
+           // Don't have to send message again
+           return;
+       }
+       Message msg = mHandler.obtainMessage(DESTROY_SD_CONTAINER, pkgName);
+       mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
+   }
+
+   public void updateExternalMediaStatus(boolean mediaStatus) {
+       // TODO
+   }
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index de3dcb0..d5de1f0 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -330,6 +330,17 @@
             return false;
         }
 
+        /**
+         * Multiple calls to unregisterReceiver() cause exception and a system crash.
+         * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
+         * disable wifi at the same time.
+         * Avoid doing a disable when the current Wifi state is UNKNOWN
+         * TODO: Handle driver load fail and supplicant lost as seperate states
+         */
+        if (mWifiState == WIFI_STATE_UNKNOWN && !enable) {
+            return false;
+        }
+
         setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
 
         if (enable) {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 0a731a8..2d0f254 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -1496,6 +1496,7 @@
 //        lp.token = mStatusBarView.getWindowToken();
         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
         lp.setTitle("TrackingView");
+        lp.y = mTrackingPosition;
         mTrackingParams = lp;
 
         WindowManagerImpl.getDefault().addView(mTrackingView, lp);
@@ -1575,8 +1576,11 @@
         // Maybe the view was resized.
         if (!mExpandedVisible) {
             if (mTrackingView != null) {
-                mTrackingPosition = mTrackingParams.y = -disph;
-                WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+                mTrackingPosition = -disph;
+                if (mTrackingParams != null) {
+                    mTrackingParams.y = mTrackingPosition;
+                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+                }
             }
             if (mExpandedParams != null) {
                 mExpandedParams.y = -disph;
diff --git a/telephony/java/com/android/internal/telephony/Call.java b/telephony/java/com/android/internal/telephony/Call.java
index b95dd11..4967ab8 100644
--- a/telephony/java/com/android/internal/telephony/Call.java
+++ b/telephony/java/com/android/internal/telephony/Call.java
@@ -18,6 +18,8 @@
 
 import java.util.List;
 
+import android.util.Log;
+
 /**
  * {@hide}
  */
@@ -54,6 +56,8 @@
     // merged, etc.
     protected boolean isGeneric = false;
 
+    protected final String LOG_TAG = "Call";
+
     /* Instance Methods */
 
     /** Do not modify the List result!!! This list is not yours to keep
@@ -235,4 +239,17 @@
     public void setGeneric(boolean generic) {
         isGeneric = generic;
     }
+
+    /**
+     * Hangup call if it is alive
+     */
+    public void hangupIfAlive() {
+        if (getState().isAlive()) {
+            try {
+                hangup();
+            } catch (CallStateException ex) {
+                Log.w(LOG_TAG, " hangupIfActive: caught " + ex);
+            }
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 01b1746..cf89848 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.provider.ContactsContract.PhoneLookup;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import static android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 import android.telephony.TelephonyManager;
 import android.telephony.PhoneNumberUtils;
@@ -118,7 +119,6 @@
      * number. The returned CallerInfo is null if no number is supplied.
      */
     public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
-
         CallerInfo info = new CallerInfo();
         info.photoResource = 0;
         info.phoneLabel = null;
@@ -132,6 +132,9 @@
 
         if (cursor != null) {
             if (cursor.moveToFirst()) {
+                // TODO: photo_id is always available but not taken
+                // care of here. Maybe we should store it in the
+                // CallerInfo object as well.
 
                 int columnIndex;
 
@@ -160,10 +163,34 @@
                     }
                 }
 
-                // Look for the person ID
-                columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
+                // Look for the person ID.
+
+                // TODO: This is pretty ugly now, see bug 2269240 for
+                // more details. With tel: URI the contact id is in
+                // col "_id" while when we use a
+                // content://contacts/data/phones URI, the contact id
+                // is col "contact_id". As a work around we use the
+                // type of the contact url to figure out which column
+                // we should look at to get the contact_id.
+
+                final String mimeType = context.getContentResolver().getType(contactRef);
+
+                columnIndex = -1;
+                if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    // content://com.android.contacts/data/phones URL
+                    columnIndex = cursor.getColumnIndex(RawContacts.CONTACT_ID);
+                } else {
+                    // content://com.android.contacts/phone_lookup URL
+                    // TODO: mime type is null here so we cannot test
+                    // if we have the right url type. phone_lookup URL
+                    // should resolve to a mime type.
+                    columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
+                }
+
                 if (columnIndex != -1) {
                     info.person_id = cursor.getLong(columnIndex);
+                } else {
+                    Log.e(TAG, "Column missing for " + contactRef);
                 }
 
                 // look for the custom ringtone, create from the string stored
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 6892998..7383649 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -271,6 +271,13 @@
     protected abstract void updateSpnDisplay();
     protected abstract void setPowerStateToDesired();
 
+    /**
+     * Clean up existing voice and data connection then turn off radio power.
+     *
+     * Hang up the existing voice calls to decrease call drop rate.
+     */
+    protected abstract void powerOffRadioSafely();
+
     /** Cancel a pending (if any) pollState() operation */
     protected void cancelPollState() {
         // This will effectively cancel the rest of the poll requests.
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index af3cbd5..42feaa9 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -514,7 +514,7 @@
             synchronized(this) {
                 if (mPendingRadioPowerOffAfterDataOff) {
                     if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
-                    cm.setRadioPower(false, null);
+                    hangupAndPowerOff();
                     mPendingRadioPowerOffAfterDataOff = false;
                 }
             }
@@ -541,32 +541,42 @@
                         dcTracker.getStateInString(),
                         dcTracker.getAnyDataEnabled() ? 1 : 0);
             }
-            Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
-            msg.arg1 = 1; // tearDown is true
-            msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
-            dcTracker.sendMessage(msg);
 
-            synchronized(this) {
-                if (!mPendingRadioPowerOffAfterDataOff) {
-                    DataConnectionTracker.State currentState = dcTracker.getState();
-                    if (currentState != DataConnectionTracker.State.CONNECTED
-                            && currentState != DataConnectionTracker.State.DISCONNECTING
-                            && currentState != DataConnectionTracker.State.INITING) {
-                        if (DBG) log("Data disconnected, turn off radio right away.");
-                        cm.setRadioPower(false, null);
+            // If it's on and available and we want it off gracefully
+            powerOffRadioSafely();
+        } // Otherwise, we're in the desired state
+    }
+
+    @Override
+    protected void powerOffRadioSafely(){
+        // clean data connection
+        DataConnectionTracker dcTracker = phone.mDataConnection;
+
+        Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
+        msg.arg1 = 1; // tearDown is true
+        msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
+        dcTracker.sendMessage(msg);
+
+        synchronized(this) {
+            if (!mPendingRadioPowerOffAfterDataOff) {
+                DataConnectionTracker.State currentState = dcTracker.getState();
+                if (currentState != DataConnectionTracker.State.CONNECTED
+                        && currentState != DataConnectionTracker.State.DISCONNECTING
+                        && currentState != DataConnectionTracker.State.INITING) {
+                    if (DBG) log("Data disconnected, turn off radio right away.");
+                    hangupAndPowerOff();
+                }
+                else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
+                    if (DBG) {
+                        log("Wait up to 30 sec for data to disconnect, then turn off radio.");
                     }
-                    else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
-                        if (DBG) {
-                            log("Wait up to 30 sec for data to disconnect, then turn off radio.");
-                        }
-                        mPendingRadioPowerOffAfterDataOff = true;
-                    } else {
-                        Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
-                        cm.setRadioPower(false, null);
-                    }
+                    mPendingRadioPowerOffAfterDataOff = true;
+                } else {
+                    Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+                    hangupAndPowerOff();
                 }
             }
-        } // Otherwise, we're in the desired state
+        }
     }
 
     @Override
@@ -1644,11 +1654,19 @@
             if (mPendingRadioPowerOffAfterDataOff) {
                 if (DBG) log("Process pending request to turn radio off.");
                 removeMessages(EVENT_SET_RADIO_POWER_OFF);
-                cm.setRadioPower(false, null);
+                hangupAndPowerOff();
                 mPendingRadioPowerOffAfterDataOff = false;
                 return true;
             }
             return false;
         }
     }
+
+    private void hangupAndPowerOff() {
+        // hang up all active voice calls
+        phone.mCT.ringingCall.hangupIfAlive();
+        phone.mCT.backgroundCall.hangupIfAlive();
+        phone.mCT.foregroundCall.hangupIfAlive();
+        cm.setRadioPower(false, null);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 3f52eff..6695ccb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -808,7 +808,7 @@
     protected void restartRadio() {
         Log.d(LOG_TAG, "************TURN OFF RADIO**************");
         cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
-        phone.mCM.setRadioPower(false, null);
+        mGsmPhone.mSST.powerOffRadioSafely();
         /* Note: no need to call setRadioPower(true).  Assuming the desired
          * radio power state is still ON (as tracked by ServiceStateTracker),
          * ServiceStateTracker will call setRadioPower when it receives the
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index c2c89c01..48e5c97 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -518,26 +518,41 @@
                 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF,
                         dcTracker.getStateInString(), dcTracker.getAnyDataEnabled() ? 1 : 0);
             }
-            Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
-            msg.arg1 = 1; // tearDown is true
-            msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF;
-            dcTracker.sendMessage(msg);
-
-            // poll data state up to 15 times, with a 100ms delay
-            // totaling 1.5 sec. Normal data disable action will finish in 100ms.
-            for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
-                if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED
-                        && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) {
-                    Log.d(LOG_TAG, "Data shutdown complete.");
-                    break;
-                }
-                SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
-            }
-            // If it's on and available and we want it off..
-            cm.setRadioPower(false, null);
+            // If it's on and available and we want it off gracefully
+            powerOffRadioSafely();
         } // Otherwise, we're in the desired state
     }
 
+    @Override
+    protected void powerOffRadioSafely() {
+        // clean data connection
+        DataConnectionTracker dcTracker = phone.mDataConnection;
+        Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
+        msg.arg1 = 1; // tearDown is true
+        msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF;
+        dcTracker.sendMessage(msg);
+
+        // poll data state up to 15 times, with a 100ms delay
+        // totaling 1.5 sec. Normal data disable action will finish in 100ms.
+        for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
+            if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED
+                    && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) {
+                Log.d(LOG_TAG, "Data shutdown complete.");
+                break;
+            }
+            SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
+        }
+
+        // hang up all active voice calls
+        if (phone.isInCall()) {
+            phone.mCT.ringingCall.hangupIfAlive();
+            phone.mCT.backgroundCall.hangupIfAlive();
+            phone.mCT.foregroundCall.hangupIfAlive();
+        }
+
+        cm.setRadioPower(false, null);
+    }
+
     protected void updateSpnDisplay() {
         int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric());
         String spn = phone.mSIMRecords.getServiceProviderName();
diff --git a/tests/AndroidTests/src/com/android/unit_tests/AndroidTests.java b/tests/AndroidTests/src/com/android/unit_tests/AndroidTests.java
deleted file mode 100644
index 4b86add..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/AndroidTests.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2005 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.unit_tests;
-
-import android.test.FrameworkTests;
-import android.test.suitebuilder.TestSuiteBuilder;
-
-import junit.framework.TestSuite;
-
-public class AndroidTests extends TestSuite {
-
-    public static TestSuite suite() {
-        TestSuiteBuilder suiteBuilder = new TestSuiteBuilder(AndroidTests.class);
-        TestSuite suite = suiteBuilder.includeAllPackagesUnderHere().build();
-        
-        return suite;
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseCursorTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseCursorTest.java
deleted file mode 100644
index 5df499d..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseCursorTest.java
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.database.DataSetObserver;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteCursor;
-import android.database.sqlite.SQLiteCursorDriver;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQuery;
-import android.database.sqlite.SQLiteStatement;
-import android.os.Looper;
-import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Random;
-
-import junit.framework.TestCase;
-
-public class DatabaseCursorTest extends TestCase implements PerformanceTestCase {
-
-    private static final String sString1 = "this is a test";
-    private static final String sString2 = "and yet another test";
-    private static final String sString3 = "this string is a little longer, but still a test";
-
-    private static final int CURRENT_DATABASE_VERSION = 42;
-    private SQLiteDatabase mDatabase;
-    private File mDatabaseFile;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
-        if (mDatabaseFile.exists()) {
-            mDatabaseFile.delete();
-        }
-        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
-        assertNotNull(mDatabase);
-        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mDatabase.close();
-        mDatabaseFile.delete();
-        super.tearDown();
-    }
-
-    public boolean isPerformanceOnly() {
-        return false;
-    }
-
-    // These test can only be run once.
-    public int startPerformance(Intermediates intermediates) {
-        return 1;
-    }
-
-    private void populateDefaultTable() {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
-
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
-    }
-
-    @MediumTest
-    public void testCursorUpdate() {
-        mDatabase.execSQL(
-            "CREATE TABLE test (_id INTEGER PRIMARY KEY, d INTEGER, s INTEGER);");
-        for(int i = 0; i < 20; i++) {
-            mDatabase.execSQL("INSERT INTO test (d, s) VALUES (" + i + 
-                "," + i%2 + ");");
-        }
-        
-        Cursor c = mDatabase.query("test", null, "s = 0", null, null, null, null);
-        int dCol = c.getColumnIndexOrThrow("d");
-        int sCol = c.getColumnIndexOrThrow("s");
-        
-        int count = 0;
-        while (c.moveToNext()) {
-            assertTrue(c.updateInt(dCol, 3));
-            count++;
-        }
-        assertEquals(10, count);
-        
-        assertTrue(c.commitUpdates());
-        
-        assertTrue(c.requery());
-        
-        count = 0;
-        while (c.moveToNext()) {
-            assertEquals(3, c.getInt(dCol));
-            count++;
-        }
-        
-        assertEquals(10, count);
-        assertTrue(c.moveToFirst());
-        assertTrue(c.deleteRow());
-        assertEquals(9, c.getCount());
-        c.close();
-    }
-    
-    @MediumTest
-    public void testBlob() throws Exception {
-        // create table
-        mDatabase.execSQL(
-            "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);");
-        // insert blob
-        Object[] args = new Object[4];
-        
-        byte[] blob = new byte[1000];
-        byte value = 99;
-        Arrays.fill(blob, value);        
-        args[3] = blob;
-        
-        String s = new String("text");        
-        args[0] = s;
-        Double d = 99.9;
-        args[1] = d;
-        Long l = (long)1000;
-        args[2] = l;
-        
-        String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)";
-        mDatabase.execSQL(sql, args);
-        // use cursor to access blob
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);        
-        c.moveToNext();
-        ContentValues cv = new ContentValues();
-        DatabaseUtils.cursorRowToContentValues(c, cv);
-        
-        int bCol = c.getColumnIndexOrThrow("b");
-        int sCol = c.getColumnIndexOrThrow("s");
-        int dCol = c.getColumnIndexOrThrow("d");
-        int lCol = c.getColumnIndexOrThrow("l");
-        byte[] cBlob =  c.getBlob(bCol);
-        assertTrue(Arrays.equals(blob, cBlob));
-        assertEquals(s, c.getString(sCol));
-        assertEquals((double)d, c.getDouble(dCol));
-        assertEquals((long)l, c.getLong(lCol));
-        
-        // new byte[]
-        byte[] newblob = new byte[1000];
-        value = 98;
-        Arrays.fill(blob, value);        
-        
-        c.updateBlob(bCol, newblob);
-        cBlob =  c.getBlob(bCol);
-        assertTrue(Arrays.equals(newblob, cBlob));
-        
-        // commit
-        assertTrue(c.commitUpdates());
-        assertTrue(c.requery());
-        c.moveToNext();
-        cBlob =  c.getBlob(bCol);
-        assertTrue(Arrays.equals(newblob, cBlob));        
-        c.close();
-    }
-    
-    @MediumTest
-    public void testRealColumns() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);");
-        ContentValues values = new ContentValues();
-        values.put("data", 42.11);
-        long id = mDatabase.insert("test", "data", values);
-        assertTrue(id > 0);
-        Cursor c = mDatabase.rawQuery("SELECT data FROM test", null);
-        assertNotNull(c);
-        assertTrue(c.moveToFirst());
-        assertEquals(42.11, c.getDouble(0));
-        c.close();
-    }
-
-    @MediumTest
-    public void testCursor1() throws Exception {
-        populateDefaultTable();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-
-        int dataColumn = c.getColumnIndexOrThrow("data");
-
-        // The cursor should ignore text before the last period when looking for a column. (This
-        // is a temporary hack in all implementations of getColumnIndex.)
-        int dataColumn2 = c.getColumnIndexOrThrow("junk.data");
-        assertEquals(dataColumn, dataColumn2);
-
-        assertSame(3, c.getCount());
-
-        assertTrue(c.isBeforeFirst());
-
-        try {
-            c.getInt(0);
-            fail("CursorIndexOutOfBoundsException expected");
-        } catch (CursorIndexOutOfBoundsException ex) {
-            // expected
-        }
-
-        c.moveToNext();
-        assertEquals(1, c.getInt(0));
-
-        String s = c.getString(dataColumn);
-        assertEquals(sString1, s);
-
-        c.moveToNext();
-        s = c.getString(dataColumn);
-        assertEquals(sString2, s);
-
-        c.moveToNext();
-        s = c.getString(dataColumn);
-        assertEquals(sString3, s);
-
-        c.moveToPosition(-1);
-        c.moveToNext();
-        s = c.getString(dataColumn);
-        assertEquals(sString1, s);
-
-        c.moveToPosition(2);
-        s = c.getString(dataColumn);
-        assertEquals(sString3, s);
-
-        int i;
-
-        for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
-            c.getInt(0);
-        }
-
-        assertEquals(3, i);
-
-        try {
-            c.getInt(0);
-            fail("CursorIndexOutOfBoundsException expected");
-        } catch (CursorIndexOutOfBoundsException ex) {
-            // expected
-        }
-        c.close();
-    }
-
-    @MediumTest
-    public void testCursor2() throws Exception {
-        populateDefaultTable();
-
-        Cursor c = mDatabase.query("test", null, "_id > 1000", null, null, null, null);
-        assertEquals(0, c.getCount());
-        assertTrue(c.isBeforeFirst());
-
-        try {
-            c.getInt(0);
-            fail("CursorIndexOutOfBoundsException expected");
-        } catch (CursorIndexOutOfBoundsException ex) {
-            // expected
-        }
-
-        int i;
-        for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
-            c.getInt(0);
-        }
-        assertEquals(0, i);
-        try {
-            c.getInt(0);
-            fail("CursorIndexOutOfBoundsException expected");
-        } catch (CursorIndexOutOfBoundsException ex) {
-            // expected
-        }
-        c.close();
-    }
-
-    @MediumTest
-    public void testLargeField() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
-
-        StringBuilder sql = new StringBuilder(2100);
-        sql.append("INSERT INTO test (data) VALUES ('");
-        Random random = new Random(System.currentTimeMillis());
-        StringBuilder randomString = new StringBuilder(1979);
-        for (int i = 0; i < 1979; i++) {
-            randomString.append((random.nextInt() & 0xf) % 10);
-        }
-        sql.append(randomString);
-        sql.append("');");
-        mDatabase.execSQL(sql.toString());
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        assertNotNull(c);
-        assertEquals(1, c.getCount());
-
-        assertTrue(c.moveToFirst());
-        assertEquals(0, c.getPosition());
-        String largeString = c.getString(c.getColumnIndexOrThrow("data"));
-        assertNotNull(largeString);
-        assertEquals(randomString.toString(), largeString);
-        c.close();
-    }
-
-    class TestObserver extends DataSetObserver {
-        int total;
-        SQLiteCursor c;
-        boolean quit = false;
-        public TestObserver(int total_, SQLiteCursor cursor) {
-            c = cursor;
-            total = total_;
-        }
-        
-        @Override
-        public void onChanged() {
-            int count = c.getCount();
-            if (total == count) {
-                int i = 0;
-                while (c.moveToNext()) {
-                    assertEquals(i, c.getInt(1));
-                    i++;
-                }
-                assertEquals(count, i);
-                quit = true;
-                Looper.myLooper().quit();
-            }
-        }
-
-        @Override
-        public void onInvalidated() {
-        }
-    }
-    
-    //@Large
-    @Suppress
-    public void testLoadingThreadDelayRegisterData() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-        
-        final int count = 505; 
-        String sql = "INSERT INTO test (data) VALUES (?);";
-        SQLiteStatement s = mDatabase.compileStatement(sql);
-        for (int i = 0; i < count; i++) {
-            s.bindLong(1, i);
-            s.execute();
-        }
-
-        int maxRead = 500;
-        int initialRead = 5;
-        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
-                null, initialRead, maxRead);
-        
-        TestObserver observer = new TestObserver(count, c);
-        c.getCount();
-        c.registerDataSetObserver(observer);
-        if (!observer.quit) {
-            Looper.loop();
-        }
-        c.close();
-    }
-    
-    @LargeTest
-    public void testLoadingThread() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-        
-        final int count = 50000; 
-        String sql = "INSERT INTO test (data) VALUES (?);";
-        SQLiteStatement s = mDatabase.compileStatement(sql);
-        for (int i = 0; i < count; i++) {
-            s.bindLong(1, i);
-            s.execute();
-        }
-
-        int maxRead = 1000;
-        int initialRead = 5;
-        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
-                null, initialRead, maxRead);
-        
-        TestObserver observer = new TestObserver(count, c);
-        c.registerDataSetObserver(observer);
-        c.getCount();
-        
-        Looper.loop();
-        c.close();
-    } 
-    
-    @LargeTest
-    public void testLoadingThreadClose() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-        
-        final int count = 1000; 
-        String sql = "INSERT INTO test (data) VALUES (?);";
-        SQLiteStatement s = mDatabase.compileStatement(sql);
-        for (int i = 0; i < count; i++) {
-            s.bindLong(1, i);
-            s.execute();
-        }
-
-        int maxRead = 11;
-        int initialRead = 5;
-        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
-                null, initialRead, maxRead);
-        
-        TestObserver observer = new TestObserver(count, c);
-        c.registerDataSetObserver(observer);
-        c.getCount();       
-        c.close();
-    }
-    
-    @LargeTest
-    public void testLoadingThreadDeactivate() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-        
-        final int count = 1000; 
-        String sql = "INSERT INTO test (data) VALUES (?);";
-        SQLiteStatement s = mDatabase.compileStatement(sql);
-        for (int i = 0; i < count; i++) {
-            s.bindLong(1, i);
-            s.execute();
-        }
-
-        int maxRead = 11;
-        int initialRead = 5;
-        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
-                null, initialRead, maxRead);
-        
-        TestObserver observer = new TestObserver(count, c);
-        c.registerDataSetObserver(observer);
-        c.getCount();       
-        c.deactivate();
-        c.close();
-    }
-    
-    @LargeTest
-    public void testManyRowsLong() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
-        
-        final int count = 36799; 
-        for (int i = 0; i < count; i++) {
-            mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
-        }
-
-        Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
-        assertNotNull(c);
-
-        int i = 0;
-        while (c.moveToNext()) {
-            assertEquals(i, c.getInt(0));
-            i++;
-        }
-        assertEquals(count, i);
-        assertEquals(count, c.getCount());
-
-        Log.d("testManyRows", "count " + Integer.toString(i));
-        c.close();
-    }
-
-    @LargeTest
-    public void testManyRowsTxt() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
-        StringBuilder sql = new StringBuilder(2100);
-        sql.append("INSERT INTO test (data) VALUES ('");
-        Random random = new Random(System.currentTimeMillis());
-        StringBuilder randomString = new StringBuilder(1979);
-        for (int i = 0; i < 1979; i++) {
-            randomString.append((random.nextInt() & 0xf) % 10);
-        }
-        sql.append(randomString);
-        sql.append("');");
-
-        // if cursor window size changed, adjust this value too  
-        final int count = 600; // more than two fillWindow needed
-        for (int i = 0; i < count; i++) {
-            mDatabase.execSQL(sql.toString());
-        }
-
-        Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
-        assertNotNull(c);
-
-        int i = 0;
-        while (c.moveToNext()) {
-            assertEquals(randomString.toString(), c.getString(0));
-            i++;
-        }
-        assertEquals(count, i);
-        assertEquals(count, c.getCount());
-        c.close();
-    }
-    
-    @LargeTest
-    public void testManyRowsTxtLong() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);");
-        
-        Random random = new Random(System.currentTimeMillis());
-        StringBuilder randomString = new StringBuilder(1979);
-        for (int i = 0; i < 1979; i++) {
-            randomString.append((random.nextInt() & 0xf) % 10);
-        }
-
-        // if cursor window size changed, adjust this value too  
-        final int count = 600;
-        for (int i = 0; i < count; i++) {
-            StringBuilder sql = new StringBuilder(2100);
-            sql.append("INSERT INTO test (txt, data) VALUES ('");
-            sql.append(randomString);
-            sql.append("','");
-            sql.append(i);
-            sql.append("');");
-            mDatabase.execSQL(sql.toString());
-        }
-
-        Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
-        assertNotNull(c);
-
-        int i = 0;
-        while (c.moveToNext()) {
-            assertEquals(randomString.toString(), c.getString(0));
-            assertEquals(i, c.getInt(1));
-            i++;
-        }
-        assertEquals(count, i);
-        assertEquals(count, c.getCount());
-        c.close();
-    }
-   
-    @MediumTest
-    public void testRequery() throws Exception {
-        populateDefaultTable();
-
-        Cursor c = mDatabase.rawQuery("SELECT * FROM test", null);
-        assertNotNull(c);
-        assertEquals(3, c.getCount());
-        c.deactivate();
-        c.requery();
-        assertEquals(3, c.getCount());
-        c.close();
-    }
-
-    @MediumTest
-    public void testRequeryWithSelection() throws Exception {
-        populateDefaultTable();
-
-        Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'",
-                null);
-        assertNotNull(c);
-        assertEquals(1, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-        c.deactivate();
-        c.requery();
-        assertEquals(1, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-        c.close();
-    }
-
-    @MediumTest
-    public void testRequeryWithSelectionArgs() throws Exception {
-        populateDefaultTable();
-
-        Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = ?",
-                new String[]{sString1});
-        assertNotNull(c);
-        assertEquals(1, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-        c.deactivate();
-        c.requery();
-        assertEquals(1, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-        c.close();
-    }
-
-    @MediumTest
-    public void testRequeryWithAlteredSelectionArgs() throws Exception {
-        /**
-         * Test the ability of a subclass of SQLiteCursor to change its query arguments.
-         */
-        populateDefaultTable();
-
-        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
-            public Cursor newCursor(
-                    SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable,
-                    SQLiteQuery query) {
-                return new SQLiteCursor(db, masterQuery, editTable, query) {
-                    @Override
-                    public boolean requery() {
-                        setSelectionArguments(new String[]{"2"});
-                        return super.requery();
-                    }
-                };
-            }
-        };
-        Cursor c = mDatabase.rawQueryWithFactory(
-                factory, "SELECT data FROM test WHERE _id <= ?", new String[]{"1"},
-                null);
-        assertNotNull(c);
-        assertEquals(1, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-
-        // Our hacked requery() changes the query arguments in the cursor.
-        c.requery();
-
-        assertEquals(2, c.getCount());
-        assertTrue(c.moveToFirst());
-        assertEquals(sString1, c.getString(0));
-        assertTrue(c.moveToNext());
-        assertEquals(sString2, c.getString(0));
-
-        // Test that setting query args on a deactivated cursor also works.
-        c.deactivate();
-        c.requery();
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
index 7a4d934..69d55c1 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
@@ -987,4 +987,18 @@
         ih.close();
     }
 
+    @MediumTest
+    public void testDbCloseReleasingAllCachedSql() {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
+                "num1 INTEGER, num2 INTEGER, image BLOB);");
+        final String statement = "DELETE FROM test WHERE _id=?;";
+        SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement);
+        assertTrue(statementDoNotClose.getUniqueId() > 0);
+        int nStatement = statementDoNotClose.getUniqueId();
+        assertTrue(statementDoNotClose.getUniqueId() == nStatement);
+        /* do not close statementDoNotClose object. 
+         * That should leave it in SQLiteDatabase.mPrograms.
+         * mDatabase.close() in tearDown() should release it.
+         */
+    }
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseStatementTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseStatementTest.java
deleted file mode 100644
index 16ca59f..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseStatementTest.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDoneException;
-import android.database.sqlite.SQLiteStatement;
-import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import junit.framework.TestCase;
-
-import java.io.File;
-
-public class DatabaseStatementTest extends TestCase implements PerformanceTestCase {
-
-    private static final String sString1 = "this is a test";
-    private static final String sString2 = "and yet another test";
-    private static final String sString3 = "this string is a little longer, but still a test";
-    
-    private static final int CURRENT_DATABASE_VERSION = 42;
-    private SQLiteDatabase mDatabase;
-    private File mDatabaseFile;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
-        if (mDatabaseFile.exists()) {
-            mDatabaseFile.delete();
-        }
-        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
-        assertNotNull(mDatabase);
-        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mDatabase.close();
-        mDatabaseFile.delete();
-        super.tearDown();
-    }
-
-    public boolean isPerformanceOnly() {
-        return false;
-    }
-
-    // These test can only be run once.
-    public int startPerformance(Intermediates intermediates) {
-        return 1;
-    }
-
-    private void populateDefaultTable() {
-        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
-
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
-        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
-    }
-
-    @MediumTest
-    public void testExecuteStatement() throws Exception {
-        populateDefaultTable();
-        SQLiteStatement statement = mDatabase.compileStatement("DELETE FROM test");
-        statement.execute();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        assertEquals(0, c.getCount());
-        c.deactivate();
-        statement.close();
-    }
-
-    @MediumTest
-    public void testSimpleQuery() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL, str TEXT NOT NULL);");
-        mDatabase.execSQL("INSERT INTO test VALUES (1234, 'hello');");
-        SQLiteStatement statement1 =
-                mDatabase.compileStatement("SELECT num FROM test WHERE str = ?");
-        SQLiteStatement statement2 =
-                mDatabase.compileStatement("SELECT str FROM test WHERE num = ?");
-
-        try {
-            statement1.bindString(1, "hello");
-            long value = statement1.simpleQueryForLong();
-            assertEquals(1234, value);
-
-            statement1.bindString(1, "world");
-            statement1.simpleQueryForLong();
-            fail("shouldn't get here");
-        } catch (SQLiteDoneException e) {
-            // expected
-        }
-
-        try {
-            statement2.bindLong(1, 1234);
-            String value = statement1.simpleQueryForString();
-            assertEquals("hello", value);
-
-            statement2.bindLong(1, 5678);
-            statement1.simpleQueryForString();
-            fail("shouldn't get here");
-        } catch (SQLiteDoneException e) {
-            // expected
-        }
-
-        statement1.close();
-        statement2.close();
-    }
-
-    @MediumTest
-    public void testStatementLongBinding() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
-        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
-
-        for (int i = 0; i < 10; i++) {
-            statement.bindLong(1, i);
-            statement.execute();
-        }
-        statement.close();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        int numCol = c.getColumnIndexOrThrow("num");
-        c.moveToFirst();
-        for (long i = 0; i < 10; i++) {
-            long num = c.getLong(numCol);
-            assertEquals(i, num);
-            c.moveToNext();
-        }
-        c.close();
-    }
-
-    @MediumTest
-    public void testStatementStringBinding() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num TEXT);");
-        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
-
-        for (long i = 0; i < 10; i++) {
-            statement.bindString(1, Long.toHexString(i));
-            statement.execute();
-        }
-        statement.close();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        int numCol = c.getColumnIndexOrThrow("num");
-        c.moveToFirst();
-        for (long i = 0; i < 10; i++) {
-            String num = c.getString(numCol);
-            assertEquals(Long.toHexString(i), num);
-            c.moveToNext();
-        }
-        c.close();
-    }
-
-    @MediumTest
-    public void testStatementClearBindings() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
-        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
-
-        for (long i = 0; i < 10; i++) {
-            statement.bindLong(1, i);
-            statement.clearBindings();
-            statement.execute();
-        }
-        statement.close();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
-        int numCol = c.getColumnIndexOrThrow("num");
-        assertTrue(c.moveToFirst());
-        for (long i = 0; i < 10; i++) {
-            assertTrue(c.isNull(numCol));
-            c.moveToNext();
-        }
-        c.close();
-    }
-
-    @MediumTest
-    public void testSimpleStringBinding() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num TEXT, value TEXT);");
-        String statement = "INSERT INTO test (num, value) VALUES (?,?)";
-
-        String[] args = new String[2];
-        for (int i = 0; i < 2; i++) {
-            args[i] = Integer.toHexString(i);
-        }
-
-        mDatabase.execSQL(statement, args);
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        int numCol = c.getColumnIndexOrThrow("num");
-        int valCol = c.getColumnIndexOrThrow("value");
-        c.moveToFirst();
-        String num = c.getString(numCol);
-        assertEquals(Integer.toHexString(0), num);
-
-        String val = c.getString(valCol);
-        assertEquals(Integer.toHexString(1), val);
-        c.close();
-    }
-
-    @MediumTest
-    public void testStatementMultipleBindings() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
-        SQLiteStatement statement =
-                mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
-
-        for (long i = 0; i < 10; i++) {
-            statement.bindLong(1, i);
-            statement.bindString(2, Long.toHexString(i));
-            statement.execute();
-        }
-        statement.close();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
-        int numCol = c.getColumnIndexOrThrow("num");
-        int strCol = c.getColumnIndexOrThrow("str");
-        assertTrue(c.moveToFirst());
-        for (long i = 0; i < 10; i++) {
-            long num = c.getLong(numCol);
-            String str = c.getString(strCol);
-            assertEquals(i, num);
-            assertEquals(Long.toHexString(i), str);
-            c.moveToNext();
-        }
-        c.close();
-    }
-
-    private static class StatementTestThread extends Thread {
-        private SQLiteDatabase mDatabase;
-        private SQLiteStatement mStatement;
-
-        public StatementTestThread(SQLiteDatabase db, SQLiteStatement statement) {
-            super();
-            mDatabase = db;
-            mStatement = statement;
-        }
-
-        @Override
-        public void run() {
-            mDatabase.beginTransaction();
-            for (long i = 0; i < 10; i++) {
-                mStatement.bindLong(1, i);
-                mStatement.bindString(2, Long.toHexString(i));
-                mStatement.execute();
-            }
-            mDatabase.setTransactionSuccessful();
-            mDatabase.endTransaction();
-
-            Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
-            int numCol = c.getColumnIndexOrThrow("num");
-            int strCol = c.getColumnIndexOrThrow("str");
-            assertTrue(c.moveToFirst());
-            for (long i = 0; i < 10; i++) {
-                long num = c.getLong(numCol);
-                String str = c.getString(strCol);
-                assertEquals(i, num);
-                assertEquals(Long.toHexString(i), str);
-                c.moveToNext();
-            }
-            c.close();
-        }
-    }
-
-    @MediumTest
-    public void testStatementMultiThreaded() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
-        SQLiteStatement statement =
-                mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
-
-        StatementTestThread thread = new StatementTestThread(mDatabase, statement);
-        thread.start();
-        try {
-            thread.join();
-        } finally {
-            statement.close();
-        }
-    }
-
-    @MediumTest
-    public void testStatementConstraint() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL);");
-        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
-
-        // Try to insert NULL, which violates the constraint
-        try {
-            statement.clearBindings();
-            statement.execute();
-            fail("expected exception not thrown");
-        } catch (SQLiteConstraintException e) {
-            // expected
-        }
-
-        // Make sure the statement can still be used
-        statement.bindLong(1, 1);
-        statement.execute();
-        statement.close();
-
-        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
-        int numCol = c.getColumnIndexOrThrow("num");
-        c.moveToFirst();
-        long num = c.getLong(numCol);
-        assertEquals(1, num);
-        c.close();
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseTests.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseTests.java
index a7a1400..a288c73 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseTests.java
@@ -23,8 +23,6 @@
         TestSuite suite = new TestSuite(DatabaseTests.class.getName());
 
         suite.addTestSuite(DatabaseGeneralTest.class);
-        suite.addTestSuite(DatabaseCursorTest.class);
-        suite.addTestSuite(DatabaseStatementTest.class);
         suite.addTestSuite(DatabaseLocaleTest.class);
         suite.addTestSuite(CursorWindowTest.class);
         suite.addTestSuite(DatabaseLockTest.class);
diff --git a/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java b/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java
deleted file mode 100644
index 7252aa9..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.unit_tests;
-
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-import android.telephony.NeighboringCellInfo;
-import android.test. suitebuilder.annotation.SmallTest;
-
-import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_GPRS;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
-
-public class NeighboringCellInfoTest extends AndroidTestCase {
-    @SmallTest
-    public void testConstructor() {
-        int rssi = 31;
-        NeighboringCellInfo nc;
-
-        nc = new NeighboringCellInfo(rssi, "FFFFFFF", NETWORK_TYPE_EDGE);
-        assertEquals(NETWORK_TYPE_EDGE, nc.getNetworkType());
-        assertEquals(rssi, nc.getRssi());
-        assertEquals(0xfff, nc.getLac());
-        assertEquals(0xffff, nc.getCid());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getPsc());
-
-        nc = new NeighboringCellInfo(rssi, "1FF", NETWORK_TYPE_UMTS);
-        assertEquals(NETWORK_TYPE_UMTS, nc.getNetworkType());
-        assertEquals(rssi, nc.getRssi());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getCid());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getLac());
-        assertEquals(0x1ff, nc.getPsc());
-
-        nc = new NeighboringCellInfo(rssi, "1FF", NETWORK_TYPE_UNKNOWN);
-        assertEquals(NETWORK_TYPE_UNKNOWN, nc.getNetworkType());
-        assertEquals(rssi, nc.getRssi());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getCid());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getLac());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getPsc());
-    }
-
-    @SmallTest
-    public void testParcel() {
-        int rssi = 20;
-
-        NeighboringCellInfo nc = new NeighboringCellInfo(rssi, "12345678", NETWORK_TYPE_GPRS);
-        assertEquals(NETWORK_TYPE_GPRS, nc.getNetworkType());
-        assertEquals(rssi, nc.getRssi());
-        assertEquals(0x1234, nc.getLac());
-        assertEquals(0x5678, nc.getCid());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nc.getPsc());
-
-        Parcel p = Parcel.obtain();
-        p.setDataPosition(0);
-        nc.writeToParcel(p, 0);
-
-        p.setDataPosition(0);
-        NeighboringCellInfo nw = new NeighboringCellInfo(p);
-        assertEquals(NETWORK_TYPE_GPRS, nw.getNetworkType());
-        assertEquals(rssi, nw.getRssi());
-        assertEquals(0x1234, nw.getLac());
-        assertEquals(0x5678, nw.getCid());
-        assertEquals(NeighboringCellInfo.UNKNOWN_CID, nw.getPsc());
-     }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ArrayTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ArrayTest.java
deleted file mode 100644
index 4d5b5e7..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ArrayTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.TypedValue;
-import com.android.unit_tests.R;
-
-public class ArrayTest extends AndroidTestCase {
-    private Resources mResources;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResources = mContext.getResources();
-    }
-
-    private void checkEntry(int resid, int index, Object res, Object expected) {
-        assertEquals("in resource 0x" + Integer.toHexString(resid)
-                + " at index " + index, expected, res);
-    }
-    
-    private void checkStringArray(int resid, String[] expected) {
-        String[] res = mResources.getStringArray(resid);
-        assertEquals(res.length, expected.length);
-        for (int i=0; i<expected.length; i++) {
-            checkEntry(resid, i, res[i], expected[i]);
-        }
-    }
-    
-    private void checkTextArray(int resid, String[] expected) {
-        CharSequence[] res = mResources.getTextArray(resid);
-        assertEquals(res.length, expected.length);
-        for (int i=0; i<expected.length; i++) {
-            checkEntry(resid, i, res[i], expected[i]);
-        }
-    }
-    
-    private void checkIntArray(int resid, int[] expected) {
-        int[] res = mResources.getIntArray(resid);
-        assertEquals(res.length, expected.length);
-        for (int i=0; i<expected.length; i++) {
-            assertEquals("in resource 0x" + Integer.toHexString(resid)
-                    + " at index " + i, expected[i], res[i]);
-        }
-    }
-    
-    @SmallTest
-    public void testStrings() throws Exception {
-        checkStringArray(R.array.strings, new String[] {"zero", "1", "here"});
-        checkTextArray(R.array.strings, new String[] {"zero", "1", "here"});
-        checkStringArray(R.array.integers, new String[] {null, null, null});
-        checkTextArray(R.array.integers, new String[] {null, null, null});
-    }
-    
-    @SmallTest
-    public void testIntegers() throws Exception {
-        checkIntArray(R.array.strings, new int[] {0, 0, 0});
-        checkIntArray(R.array.integers, new int[] {0, 1, 101});
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java
deleted file mode 100644
index a065d70..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.WindowManager;
-import com.android.unit_tests.R;
-
-import java.util.Locale;
-
-public class ConfigTest extends AndroidTestCase {
-    enum properties {
-        LANGUAGE,
-        COUNTRY,
-        MCC,
-        MNC,
-        TOUCHSCREEN,
-        KEYBOARD,
-        KEYBOARDHIDDEN,
-        NAVIGATION,
-        ORIENTATION,
-        WIDTH,
-        HEIGHT,
-        DENSITY
-    }
-
-    private static void checkValue(Resources res, int resId, String expectedValue) {
-        try {
-            String actual = res.getString(resId);
-            assertNotNull("Returned wrong configuration-based simple value: expected <nothing>, got '"
-                    + actual + "' from resource 0x"
-                    + Integer.toHexString(resId),
-                    expectedValue);
-            assertEquals("Returned wrong configuration-based simple value: expected '"
-                    + expectedValue + "', got '" + actual + "' from resource 0x"
-                    + Integer.toHexString(resId),
-                    expectedValue, actual);
-        } catch (Resources.NotFoundException e) {
-            assertNull("Resource not found for configuration-based simple value: expecting \""
-                    + expectedValue + "\"",
-                    expectedValue);
-        }
-    }
-
-    private static void checkValue(Resources res, int resId,
-            int[] styleable, String[] expectedValues) {
-        Resources.Theme theme = res.newTheme();
-        TypedArray sa = theme.obtainStyledAttributes(resId, styleable);
-        for (int i = 0; i < styleable.length; i++) {
-            String actual = sa.getString(i);
-            assertEquals("Returned wrong configuration-based style value: expected '"
-                    + expectedValues[i] + "', got '" + actual + "' from attr "
-                    + i + " of resource 0x" + Integer.toHexString(resId),
-                    actual, expectedValues[i]);
-        }
-        sa.recycle();
-    }
-
-    class TotalConfig {
-        Configuration mConfig;
-        DisplayMetrics mMetrics;
-    
-        TotalConfig() {
-            mConfig = new Configuration();
-            // don't rely on build settings - they may change
-            mConfig.locale = new Locale("en", "US");
-            mConfig.mcc = 310;
-            mConfig.mnc = 001; // unused
-            mConfig.touchscreen = Configuration.TOUCHSCREEN_FINGER;
-            mConfig.keyboard = Configuration.KEYBOARD_QWERTY;
-            mConfig.keyboardHidden = Configuration.KEYBOARDHIDDEN_YES;
-            mConfig.navigation = Configuration.NAVIGATION_TRACKBALL;
-            mConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
-
-            mMetrics = new DisplayMetrics();
-            mMetrics.widthPixels = 200;
-            mMetrics.heightPixels = 320;
-            mMetrics.density = 1;
-        }
-
-        void setProperty(properties p, int value) {
-            switch(p) {
-                case MCC:
-                    mConfig.mcc = value;
-                    break;
-                case MNC:
-                    mConfig.mnc = value;
-                    break;
-                case TOUCHSCREEN:
-                    mConfig.touchscreen = value;
-                    break;
-                case KEYBOARD:
-                    mConfig.keyboard = value;
-                    break;
-                case KEYBOARDHIDDEN:
-                    mConfig.keyboardHidden = value;
-                    break;
-                case NAVIGATION:
-                    mConfig.navigation = value;
-                    break;
-                case ORIENTATION:
-                    mConfig.orientation = value;
-                    break;
-                case WIDTH:
-                    mMetrics.widthPixels = value;
-                    break;
-                case HEIGHT:
-                    mMetrics.heightPixels = value;
-                    break;
-                case DENSITY:
-                    // this is the ratio from the standard
-
-                    mMetrics.density = (((float)value)/((float)DisplayMetrics.DENSITY_DEFAULT));
-                    break;
-                default:
-                    assert(false);
-                    break;
-            }
-        }
-
-        public void setProperty(properties p, String value) {
-            switch(p) {
-                case LANGUAGE:
-                    String oldCountry = mConfig.locale.getCountry();
-                    mConfig.locale = new Locale(value, oldCountry);
-                    break;
-                case COUNTRY:
-                    String oldLanguage = mConfig.locale.getLanguage();
-                    mConfig.locale = new Locale(oldLanguage, value);
-                    break;
-                default:
-                    assert(false);
-                    break;
-            }
-        }
-
-        public Resources getResources() {
-            AssetManager assmgr = new AssetManager();
-            assmgr.addAssetPath(mContext.getPackageResourcePath());
-            return new Resources(assmgr, mMetrics, mConfig);
-        }
-    }
-
-    private static void checkPair(Resources res, int[] notResIds,
-            int simpleRes, String simpleString,
-            int bagRes, String bagString) {
-        boolean willHave = true;
-        if (notResIds != null) {
-            for (int i : notResIds) {
-                if (i == simpleRes) {
-                    willHave = false;
-                    break;
-                }
-            }
-        }
-        checkValue(res, simpleRes, willHave ? simpleString : null);
-        checkValue(res, bagRes, R.styleable.TestConfig,
-                new String[]{willHave ? bagString : null});
-    }
-
-    @SmallTest
-    public void testAllConfigs() throws Exception {
-        /**
-         * Test a resource that contains a value for each possible single
-         * configuration value.
-         */
-        TotalConfig config = new TotalConfig();
-        Resources res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple default");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag default"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.LANGUAGE, "xx");
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple xx");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag xx"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.LANGUAGE, "xx");
-        config.setProperty(properties.COUNTRY, "YY");
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple xx-rYY");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag xx-rYY"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.MCC, 111);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mcc111");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mcc111"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.MNC, 222);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mnc222");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mnc222"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.TOUCHSCREEN, Configuration.TOUCHSCREEN_NOTOUCH);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple notouch");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag notouch"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.TOUCHSCREEN, Configuration.TOUCHSCREEN_STYLUS);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple stylus");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag stylus"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.KEYBOARD, Configuration.KEYBOARD_NOKEYS);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple nokeys");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag nokeys"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.KEYBOARD, Configuration.KEYBOARD_12KEY);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 12key");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 12key"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.KEYBOARDHIDDEN, Configuration.KEYBOARDHIDDEN_NO);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple keysexposed");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag keysexposed"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.NAVIGATION, Configuration.NAVIGATION_NONAV);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple nonav");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag nonav"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.NAVIGATION, Configuration.NAVIGATION_DPAD);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple dpad");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag dpad"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.NAVIGATION, Configuration.NAVIGATION_WHEEL);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple wheel");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag wheel"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.HEIGHT, 480);
-        config.setProperty(properties.WIDTH, 320);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 480x320");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 480x320"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 240);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 240dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 240dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.ORIENTATION, Configuration.ORIENTATION_LANDSCAPE);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple landscape");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag landscape"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.ORIENTATION, Configuration.ORIENTATION_SQUARE);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple square");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag square"});
-    }
-
-    @MediumTest
-    public void testDensity() throws Exception {
-        // have 32, 240 and the default 160 content.
-        // rule is that closest wins, with down scaling (larger content)
-        // being twice as nice as upscaling.
-        // transition at H/2 * (-1 +/- sqrt(1+8L/H))
-        // SO, X < 49 goes to 32
-        // 49 >= X < 182 goes to 160
-        // X >= 182 goes to 240
-        TotalConfig config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 2);
-        Resources res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 32dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 32dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 32);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 32dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 32dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 48);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 32dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 32dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 49);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple default");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag default"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 150);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple default");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag default"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 181);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple default");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag default"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 182);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 240dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 240dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 239);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 240dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 240dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 490);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 240dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 240dpi"});
-    }
-
-// TODO - add tests for special cases - ie, other key params seem ignored if 
-// nokeys is set
-
-    @MediumTest
-    public void testCombinations() throws Exception {
-        /**
-         * Verify that proper strings are found for multiple-selectivity case
-         * (ie, a string set for locale and mcc is found only when both are
-         * true).
-         */
-        TotalConfig config = new TotalConfig();
-        config.setProperty(properties.LANGUAGE, "xx");
-        config.setProperty(properties.COUNTRY, "YY");
-        config.setProperty(properties.MCC, 111);
-        Resources res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mcc111 xx-rYY");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mcc111 xx-rYY"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.LANGUAGE, "xx");
-        config.setProperty(properties.COUNTRY, "YY");
-        config.setProperty(properties.MCC, 333);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple xx-rYY");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag xx-rYY"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.MNC, 333);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple default");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag default"});
-    }
-
-    @MediumTest
-    public void testPrecidence() throws Exception {
-        /**
-         * Verify that in cases of ties, the specific ordering is followed
-         */
-
-        /**
-         * Precidence order: mcc, mnc, locale, orientation, density,
-         * touchscreen, hidden, keyboard, navigation, width-height
-         */
-
-        /**
-         * verify mcc trumps mnc.  Have 110-xx, 220-xx but no 110-220
-         * so with is selected?  Should be mcc110-xx. 
-         */
-        TotalConfig config = new TotalConfig();
-        config.setProperty(properties.MCC, 110);
-        config.setProperty(properties.MNC, 220);
-        config.setProperty(properties.LANGUAGE, "xx");
-        Resources res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mcc110 xx");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mcc110 xx"});
-
-        /* full A + B + C doesn't exist.  Do we get A + C or B + C? 
-         */
-        config = new TotalConfig();
-        config.setProperty(properties.MCC, 111);
-        config.setProperty(properties.MNC, 222);
-        config.setProperty(properties.LANGUAGE, "xx");
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mcc111 mnc222");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mcc111 mnc222"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.MNC, 222);
-        config.setProperty(properties.LANGUAGE, "xx");
-        config.setProperty(properties.ORIENTATION, 
-                Configuration.ORIENTATION_SQUARE);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple mnc222 xx");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag mnc222 xx"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.LANGUAGE, "xx");
-        config.setProperty(properties.ORIENTATION, 
-                Configuration.ORIENTATION_SQUARE);
-        config.setProperty(properties.DENSITY, 32);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple xx square");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag xx square"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.ORIENTATION, 
-                Configuration.ORIENTATION_SQUARE);
-        config.setProperty(properties.DENSITY, 32);
-        config.setProperty(properties.TOUCHSCREEN, 
-                Configuration.TOUCHSCREEN_STYLUS);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple square 32dpi");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag square 32dpi"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.DENSITY, 32);
-        config.setProperty(properties.TOUCHSCREEN, 
-                Configuration.TOUCHSCREEN_STYLUS);
-        config.setProperty(properties.KEYBOARDHIDDEN, 
-                Configuration.KEYBOARDHIDDEN_NO);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 32dpi stylus");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 32dpi stylus"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.TOUCHSCREEN, 
-                Configuration.TOUCHSCREEN_STYLUS);
-        config.setProperty(properties.KEYBOARDHIDDEN, 
-                Configuration.KEYBOARDHIDDEN_NO);
-        config.setProperty(properties.KEYBOARD, Configuration.KEYBOARD_12KEY);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple stylus keysexposed");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag stylus keysexposed"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.KEYBOARDHIDDEN, 
-                Configuration.KEYBOARDHIDDEN_NO);
-        config.setProperty(properties.KEYBOARD, Configuration.KEYBOARD_12KEY);
-        config.setProperty(properties.NAVIGATION, 
-                Configuration.NAVIGATION_DPAD);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple keysexposed 12key");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag keysexposed 12key"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.KEYBOARD, Configuration.KEYBOARD_12KEY);
-        config.setProperty(properties.NAVIGATION, 
-                Configuration.NAVIGATION_DPAD);
-        config.setProperty(properties.HEIGHT, 63);
-        config.setProperty(properties.WIDTH, 57);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple 12key dpad");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag 12key dpad"});
-
-        config = new TotalConfig();
-        config.setProperty(properties.NAVIGATION, 
-                Configuration.NAVIGATION_DPAD);
-        config.setProperty(properties.HEIGHT, 640);
-        config.setProperty(properties.WIDTH, 400);
-        res = config.getResources();
-        checkValue(res, R.configVarying.simple, "simple dpad");
-        checkValue(res, R.configVarying.bag,
-                R.styleable.TestConfig, new String[]{"bag dpad"});
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ContentTests.java b/tests/AndroidTests/src/com/android/unit_tests/content/ContentTests.java
index 80318dc..636660f 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ContentTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/content/ContentTests.java
@@ -23,10 +23,6 @@
         TestSuite suite = new TestSuite(ContentTests.class.getName());
 
         suite.addTestSuite(AssetTest.class);
-        suite.addTestSuite(IntentFilterTest.class);
-        suite.addTest(ResourceTests.suite());
-        suite.addTestSuite(PluralResourcesTest.class);
-        suite.addTestSuite(ConfigTest.class);
         return suite;
     }
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/FractionTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/FractionTest.java
deleted file mode 100644
index 74a6b8d..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/FractionTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.TypedValue;
-import com.android.unit_tests.R;
-
-public class FractionTest extends AndroidTestCase {
-
-    private Resources mResources;
-    private final TypedValue mValue = new TypedValue();
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResources = mContext.getResources();
-    }
-
-    @SmallTest
-    public void testFractions() throws Exception {
-        tryFraction(R.dimen.frac100perc, 1, 1, 1);
-        tryFraction(R.dimen.frac1perc, 1, 1, .01f);
-        tryFraction(R.dimen.fracp1perc, 1, 1, .001f);
-        tryFraction(R.dimen.fracp01perc, 1, 1, .0001f);
-        tryFraction(R.dimen.frac0perc, 1, 1, 0);
-        tryFraction(R.dimen.frac1p1perc, 1, 1, .011f);
-        tryFraction(R.dimen.frac100p1perc, 1, 1, 1.001f);
-        tryFraction(R.dimen.frac25510perc, 1, 1, 255.1f);
-        tryFraction(R.dimen.frac25610perc, 1, 1, 256.1f);
-        tryFraction(R.dimen.frac6553510perc, 1, 1, 65535.1f);
-        tryFraction(R.dimen.frac6553610perc, 1, 1, 65536.1f);
-
-        tryFraction(R.dimen.frac100perc, 100, 1, 100);
-        tryFraction(R.dimen.frac1perc, 100, 1, .01f * 100);
-        tryFraction(R.dimen.fracp1perc, 100, 1, .001f * 100);
-        tryFraction(R.dimen.fracp01perc, 100, 1, .0001f * 100);
-        tryFraction(R.dimen.frac0perc, 100, 1, 0);
-        tryFraction(R.dimen.frac1p1perc, 100, 1, .011f * 100);
-        tryFraction(R.dimen.frac100p1perc, 100, 1, 1.001f * 100);
-        tryFraction(R.dimen.frac25510perc, 100, 1, 255.1f * 100);
-        tryFraction(R.dimen.frac25610perc, 100, 1, 256.1f * 100);
-        tryFraction(R.dimen.frac6553510perc, 100, 1, 65535.1f * 100);
-        tryFraction(R.dimen.frac6553610perc, 100, 1, 65536.1f * 100);
-
-        tryFraction(R.dimen.frac100pperc, 100, 2, 2);
-        tryFraction(R.dimen.frac1pperc, 100, 2, .01f * 2);
-        tryFraction(R.dimen.fracp1pperc, 100, 2, .001f * 2);
-        tryFraction(R.dimen.fracp01pperc, 100, 2, .0001f * 2);
-        tryFraction(R.dimen.frac0pperc, 100, 2, 0);
-        tryFraction(R.dimen.frac1p1pperc, 100, 2, .011f * 2);
-        tryFraction(R.dimen.frac100p1pperc, 100, 2, 1.001f * 2);
-        tryFraction(R.dimen.frac25510pperc, 100, 2, 255.1f * 2);
-        tryFraction(R.dimen.frac25610pperc, 100, 2, 256.1f * 2);
-        tryFraction(R.dimen.frac6553510pperc, 100, 2, 65535.1f * 2);
-        tryFraction(R.dimen.frac6553610pperc, 100, 2, 65536.1f * 2);
-    }
-
-    private void tryFraction(int resid, float base, float pbase, float expected) {
-        mResources.getValue(resid, mValue, true);
-        float res = mValue.getFraction(base, pbase);
-        float diff = Math.abs(expected - res);
-        float prec = expected * 1e-4f;
-        if (prec < 1e-5f) {
-            prec = 1e-5f;
-        }
-        //System.out.println(
-        //    "Res 0x" + Integer.toHexString(resid) + ": got=" + res
-        //    + ", expected=" + expected + ", diff=" + diff);
-        assertFalse("Expecting value " + expected + " got " + res
-                            + ": in resource 0x" + Integer.toHexString(resid)
-                            + " " + mValue,
-                diff > prec);
-    }
-}
-
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/IntentFilterTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/IntentFilterTest.java
deleted file mode 100644
index 0335b9d..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/IntentFilterTest.java
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.IntentFilter;
-import android.test.suitebuilder.annotation.SmallTest;
-import static android.os.PatternMatcher.PATTERN_LITERAL;
-import static android.os.PatternMatcher.PATTERN_PREFIX;
-import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
-import android.net.Uri;
-import android.util.StringBuilderPrinter;
-import junit.framework.TestCase;
-
-import java.util.HashSet;
-
-public class IntentFilterTest extends TestCase {
-
-    public static class Match extends IntentFilter {
-        Match(String[] actions, String[] categories, String[] mimeTypes,
-                String[] schemes, String[] authorities, String[] ports) {
-            if (actions != null) {
-                for (int i = 0; i < actions.length; i++) {
-                    addAction(actions[i]);
-                }
-            }
-            if (categories != null) {
-                for (int i = 0; i < categories.length; i++) {
-                    addCategory(categories[i]);
-                }
-            }
-            if (mimeTypes != null) {
-                for (int i = 0; i < mimeTypes.length; i++) {
-                    try {
-                        addDataType(mimeTypes[i]);
-                    } catch (IntentFilter.MalformedMimeTypeException e) {
-                        throw new RuntimeException("Bad mime type", e);
-                    }
-                }
-            }
-            if (schemes != null) {
-                for (int i = 0; i < schemes.length; i++) {
-                    addDataScheme(schemes[i]);
-                }
-            }
-            if (authorities != null) {
-                for (int i = 0; i < authorities.length; i++) {
-                    addDataAuthority(authorities[i],
-                            ports != null ? ports[i] : null);
-                }
-            }
-        }
-
-        Match(String[] actions, String[] categories, String[] mimeTypes,
-                String[] schemes, String[] authorities, String[] ports,
-                String[] paths, int[] pathTypes) {
-            this(actions, categories, mimeTypes, schemes, authorities, ports);
-            if (paths != null) {
-                for (int i = 0; i < paths.length; i++) {
-                    addDataPath(paths[i], pathTypes[i]);
-                }
-            }
-        }
-    }
-
-    public static class MatchCondition {
-        public final int result;
-        public final String action;
-        public final String mimeType;
-        public final Uri data;
-        public final String[] categories;
-
-        public MatchCondition(int _result, String _action, String[] _categories,
-                String _mimeType, String _data) {
-            result = _result;
-            action = _action;
-            mimeType = _mimeType;
-            data = _data != null ? Uri.parse(_data) : null;
-            categories = _categories;
-        }
-    }
-
-    public static void checkMatches(IntentFilter filter,
-            MatchCondition[] results) {
-        for (int i = 0; i < results.length; i++) {
-            MatchCondition mc = results[i];
-            HashSet<String> categories = null;
-            if (mc.categories != null) {
-                for (int j = 0; j < mc.categories.length; j++) {
-                    if (categories == null) {
-                        categories = new HashSet<String>();
-                    }
-                    categories.add(mc.categories[j]);
-                }
-            }
-            int result = filter.match(mc.action, mc.mimeType,
-                    mc.data != null ? mc.data.getScheme() : null, mc.data,
-                    categories, "test");
-            if ( (result & IntentFilter.MATCH_CATEGORY_MASK)
-                    != (mc.result & IntentFilter.MATCH_CATEGORY_MASK) ) {
-                StringBuilder msg = new StringBuilder();
-                msg.append("Error matching against IntentFilter:\n");
-                filter.dump(new StringBuilderPrinter(msg), "    ");
-                msg.append("Match action: ");
-                msg.append(mc.action);
-                msg.append("\nMatch mimeType: ");
-                msg.append(mc.mimeType);
-                msg.append("\nMatch data: ");
-                msg.append(mc.data);
-                msg.append("\nMatch categories: ");
-                if (mc.categories != null) {
-                    for (int j = 0; j < mc.categories.length; j++) {
-                        if (j > 0) msg.append(", ");
-                        msg.append(mc.categories[j]);
-                    }
-                }
-                msg.append("\nExpected result: 0x");
-                msg.append(Integer.toHexString(mc.result));
-                msg.append(", got result: 0x");
-                msg.append(Integer.toHexString(result));
-                throw new RuntimeException(msg.toString());
-            }
-        }
-    }
-
-    @SmallTest
-    public void testActions() throws Exception {
-        IntentFilter filter = new Match(
-                new String[]{"action1"}, null, null, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action1",
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_ACTION, "action2",
-                        null, null, null),
-        });
-
-        filter = new Match(
-                new String[]{"action1", "action2"},
-                null, null, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action1",
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, "action2",
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_ACTION, "action3",
-                        null, null, null),
-        });
-    }
-
-    @SmallTest
-    public void testCategories() throws Exception {
-        IntentFilter filter = new Match(
-                null, new String[]{"category1"}, null, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        new String[]{"category1"}, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_CATEGORY, null,
-                        new String[]{"category2"}, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_CATEGORY, null,
-                        new String[]{"category1", "category2"}, null, null),
-        });
-
-        filter = new Match(
-                null, new String[]{"category1", "category2"}, null, null,
-                null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        new String[]{"category1"}, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        new String[]{"category2"}, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_EMPTY, null,
-                        new String[]{"category1", "category2"}, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_CATEGORY, null,
-                        new String[]{"category3"}, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_CATEGORY, null,
-                        new String[]{"category1", "category2", "category3"},
-                        null, null),
-        });
-    }
-
-    @SmallTest
-    public void testMimeTypes() throws Exception {
-        IntentFilter filter = new Match(
-                null, null, new String[]{"which1/what1"}, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what1", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "*/*", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which2/what2", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which2/*", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which1/what2", null),
-        });
-
-        filter = new Match(null, null,
-                new String[]{"which1/what1", "which2/what2"}, null, null,
-                null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what1", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "*/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which2/what2", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which2/*", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which1/what2", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which3/what3", null),
-        });
-
-        filter = new Match(null, null,
-                new String[]{"which1/*"}, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what1", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "*/*", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which2/what2", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which2/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what2", null),
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null, null,
-                        "which3/what3", null),
-        });
-
-        filter = new Match(null, null,
-                new String[]{"*/*"}, null, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_TYPE, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what1", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "*/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which2/what2", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which2/*", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which1/what2", null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE, null, null,
-                        "which3/what3", null),
-        });
-    }
-
-    @SmallTest
-    public void testSchemes() throws Exception {
-        IntentFilter filter = new Match(null, null, null,
-                new String[]{"scheme1"}, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_SCHEME, null,
-                        null, null, "scheme1:foo"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme2:foo"),
-        });
-
-        filter = new Match(null, null, null,
-                new String[]{"scheme1", "scheme2"}, null, null);
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_SCHEME, null,
-                        null, null, "scheme1:foo"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_SCHEME, null,
-                        null, null, "scheme2:foo"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme3:foo"),
-        });
-    }
-
-    @SmallTest
-    public void testAuthorities() throws Exception {
-        IntentFilter filter = new Match(null, null, null,
-                new String[]{"scheme1"},
-                new String[]{"authority1"}, new String[]{null});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1:foo"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_HOST, null,
-                        null, null, "scheme1://authority1/"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority2/"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_HOST, null,
-                        null, null, "scheme1://authority1:100/"),
-        });
-
-        filter = new Match(null, null, null, new String[]{"scheme1"},
-                new String[]{"authority1"}, new String[]{"100"});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1:foo"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority1/"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority2/"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PORT, null,
-                        null, null, "scheme1://authority1:100/"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority1:200/"),
-        });
-
-        filter = new Match(null, null, null, new String[]{"scheme1"},
-                new String[]{"authority1", "authority2"},
-                new String[]{"100", null});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1:foo"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority1/"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_HOST, null,
-                        null, null, "scheme1://authority2/"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PORT, null,
-                        null, null, "scheme1://authority1:100/"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme1://authority1:200/"),
-        });
-    }
-
-    @SmallTest
-    public void testPaths() throws Exception {
-        IntentFilter filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/literal1", "/2literal"},
-                new int[]{PATTERN_LITERAL, PATTERN_LITERAL});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/literal1"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/2literal"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/literal"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/literal12"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/literal1", "/2literal"},
-                new int[]{PATTERN_PREFIX, PATTERN_PREFIX});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/literal1"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/2literal"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/literal"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/literal12"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/.*"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/literal1"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{".*"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/literal1"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a1*b"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a1b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a11b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a2b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a1bc"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a1*"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a1"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a11"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a1b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a11"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a2"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a\\.*b"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a..b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a2b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a.bc"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a.*b"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.1b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a2b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a.bc"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a.*"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.1b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a2b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.bc"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a.\\*b"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.*b"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a1*b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a2b"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a.bc"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/"),
-        });
-        filter = new Match(null, null, null,
-                new String[]{"scheme"}, new String[]{"authority"}, null,
-                new String[]{"/a.\\*"},
-                new int[]{PATTERN_SIMPLE_GLOB});
-        checkMatches(filter, new MatchCondition[]{
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, null),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/ab"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a.*"),
-                new MatchCondition(IntentFilter.MATCH_CATEGORY_PATH, null,
-                        null, null, "scheme://authority/a1*"),
-                new MatchCondition(IntentFilter.NO_MATCH_DATA, null,
-                        null, null, "scheme://authority/a1b"),
-        });
-    }
-
-}
-
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/PluralResourcesTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/PluralResourcesTest.java
deleted file mode 100644
index c3d1478..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/PluralResourcesTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.TypedValue;
-import android.util.Log;
-import com.android.unit_tests.R;
-
-import junit.framework.Assert;
-
-import java.util.Locale;
-
-public class PluralResourcesTest extends AndroidTestCase {
-    private static final String TAG = "PluralResourcesTest";
-
-    private Resources mResources;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResources = mContext.getResources();
-    }
-
-    Resources resourcesForLanguage(String lang) {
-        Configuration config = new Configuration();
-        config.updateFrom(mResources.getConfiguration());
-        config.locale = new Locale(lang);
-        return new Resources(mResources.getAssets(), mResources.getDisplayMetrics(), config);
-    }
-
-    @SmallTest
-    public void testPlurals() throws Exception {
-        CharSequence cs;
-        Resources res = resourcesForLanguage("en");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 0);
-        Log.d(TAG, "english 0 cs=" + cs);
-        Assert.assertEquals(cs.toString(), "Some dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 1);
-        Log.d(TAG, "english 1 cs=" + cs);
-        Assert.assertEquals(cs.toString(), "A dog");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 2);
-        Assert.assertEquals(cs.toString(), "Some dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 5);
-        Assert.assertEquals(cs.toString(), "Some dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 500);
-        Assert.assertEquals(cs.toString(), "Some dogs");
-    }
-
-    @SmallTest
-    public void testCzech() throws Exception {
-        CharSequence cs;
-        Resources res = resourcesForLanguage("cs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 0);
-        Log.d(TAG, "czech 0 cs=" + cs);
-        Assert.assertEquals(cs.toString(), "Some Czech dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 1);
-        Log.d(TAG, "czech 1 cs=" + cs);
-        Assert.assertEquals(cs.toString(), "A Czech dog");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 2);
-        Log.d(TAG, "czech 2 cs=" + cs);
-        Assert.assertEquals(cs.toString(), "Few Czech dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 5);
-        Assert.assertEquals(cs.toString(), "Some Czech dogs");
-
-        cs = res.getQuantityText(R.plurals.plurals_test, 500);
-        Assert.assertEquals(cs.toString(), "Some Czech dogs");
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/PrimitiveTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/PrimitiveTest.java
deleted file mode 100644
index 44098cc..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/PrimitiveTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.TypedValue;
-import com.android.unit_tests.R;
-
-public class PrimitiveTest extends AndroidTestCase {
-    private Resources mResources;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResources = mContext.getResources();
-    }
-
-    private void tryEnum(int resid, int expected) {
-        TypedArray sa =
-                mContext.obtainStyledAttributes(resid, R.styleable.EnumStyle);
-        int value = sa.getInt(R.styleable.EnumStyle_testEnum, -1);
-        sa.recycle();
-
-        assertEquals("Expecting value " + expected + " got " + value
-                + ": in resource 0x" + Integer.toHexString(resid),
-                expected, value);
-    }
-
-    @SmallTest
-    public void testEnum() throws Exception {
-        tryEnum(R.style.TestEnum1, 1);
-        tryEnum(R.style.TestEnum2, 2);
-        tryEnum(R.style.TestEnum10, 10);
-        tryEnum(R.style.TestEnum1_EmptyInherit, 1);
-    }
-
-    private void tryFlag(int resid, int expected) {
-        TypedArray sa =
-                mContext.obtainStyledAttributes(resid, R.styleable.FlagStyle);
-        int value = sa.getInt(R.styleable.FlagStyle_testFlags, -1);
-        sa.recycle();
-
-        assertEquals("Expecting value " + expected + " got " + value
-                + ": in resource 0x" + Integer.toHexString(resid),
-                expected, value);
-    }
-
-    @SmallTest
-    public void testFlags() throws Exception {
-        tryFlag(R.style.TestFlag1, 0x1);
-        tryFlag(R.style.TestFlag2, 0x2);
-        tryFlag(R.style.TestFlag31, 0x40000000);
-        tryFlag(R.style.TestFlag1And2, 0x3);
-        tryFlag(R.style.TestFlag1And2And31, 0x40000003);
-    }
-
-    private void tryBoolean(int resid, boolean expected) {
-        TypedValue v = new TypedValue();
-        mContext.getResources().getValue(resid, v, true);
-        assertEquals(TypedValue.TYPE_INT_BOOLEAN, v.type);
-        assertEquals("Expecting boolean value " + expected + " got " + v
-                + " from TypedValue: in resource 0x" + Integer.toHexString(resid),
-                expected, v.data != 0);
-        assertEquals("Expecting boolean value " + expected + " got " + v
-                + " from getBoolean(): in resource 0x" + Integer.toHexString(resid),
-                expected, mContext.getResources().getBoolean(resid));
-    }
-
-    @SmallTest
-    public void testBoolean() throws Exception {
-        tryBoolean(R.bool.trueRes, true);
-        tryBoolean(R.bool.falseRes, false);
-    }
-
-    private void tryString(int resid, String expected) {
-        TypedValue v = new TypedValue();
-        mContext.getResources().getValue(resid, v, true);
-        assertEquals(TypedValue.TYPE_STRING, v.type);
-        assertEquals("Expecting string value " + expected + " got " + v
-                + ": in resource 0x" + Integer.toHexString(resid),
-                expected, v.string);
-    }
-
-    @SmallTest
-    public void testStringCoerce() throws Exception {
-        tryString(R.string.coerceIntegerToString, "100");
-        tryString(R.string.coerceBooleanToString, "true");
-        tryString(R.string.coerceColorToString, "#fff");
-        tryString(R.string.coerceFloatToString, "100.0");
-        tryString(R.string.coerceDimensionToString, "100px");
-        tryString(R.string.coerceFractionToString, "100%");
-    }
-
-    private static void checkString(int resid, String actual, String expected) {
-        assertEquals("Expecting string value \"" + expected + "\" got \""
-                + actual + "\" in resources 0x" + Integer.toHexString(resid),
-                expected, actual);
-    }
-
-    @SmallTest
-    public void testFormattedString() throws Exception {
-        // Make sure the regular one doesn't format anything
-        checkString(R.string.formattedStringNone,
-                mResources.getString(R.string.formattedStringNone),
-                "Format[]");
-        checkString(R.string.formattedStringOne,
-                mResources.getString(R.string.formattedStringOne),
-                "Format[%d]");
-        checkString(R.string.formattedStringTwo,
-                mResources.getString(R.string.formattedStringTwo),
-                "Format[%3$d,%2$s]");
-        // Make sure the formatted one works
-        checkString(R.string.formattedStringNone,
-                mResources.getString(R.string.formattedStringNone),
-                "Format[]");
-        checkString(R.string.formattedStringOne,
-                mResources.getString(R.string.formattedStringOne, 42),
-                "Format[42]");
-        checkString(R.string.formattedStringTwo,
-                mResources.getString(R.string.formattedStringTwo, "unused", "hi", 43),
-                "Format[43,hi]");
-    }
-}
-
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/RawResourceTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/RawResourceTest.java
deleted file mode 100644
index 1786dc4..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/RawResourceTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.unit_tests.R;
-
-import java.io.InputStream;
-
-public class RawResourceTest extends AndroidTestCase {
-    private Resources mResources;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResources = mContext.getResources();
-    }
-
-    @SmallTest
-    public void testReadToEnd() throws Exception {
-        InputStream is = mResources.openRawResource(R.raw.text);
-        AssetTest.verifyTextAsset(is);
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ResourceNameTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ResourceNameTest.java
deleted file mode 100644
index 2a56243..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ResourceNameTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.unit_tests.R;
-
-public class ResourceNameTest extends AndroidTestCase {
-
-    @SmallTest
-    public void testGetResourceName() {
-        Resources res = mContext.getResources();
-        
-        String fullName = res.getResourceName(R.configVarying.simple);
-        assertEquals("com.android.unit_tests:configVarying/simple", fullName);
-        
-        String packageName = res.getResourcePackageName(R.configVarying.simple);
-        assertEquals("com.android.unit_tests", packageName);
-        
-        String typeName = res.getResourceTypeName(R.configVarying.simple);
-        assertEquals("configVarying", typeName);
-        
-        String entryName = res.getResourceEntryName(R.configVarying.simple);
-        assertEquals("simple", entryName);
-    }
-    
-    @SmallTest
-    public void testGetResourceIdentifier() {
-        Resources res = mContext.getResources();
-        int resid = res.getIdentifier(
-                "com.android.unit_tests:configVarying/simple",
-                null, null);
-        assertEquals(R.configVarying.simple, resid);
-        
-        resid = res.getIdentifier("configVarying/simple", null,
-                "com.android.unit_tests");
-        assertEquals(R.configVarying.simple, resid);
-        
-        resid = res.getIdentifier("simple", "configVarying",
-                "com.android.unit_tests");
-        assertEquals(R.configVarying.simple, resid);
-    }    
-}
-
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ResourceTests.java b/tests/AndroidTests/src/com/android/unit_tests/content/ResourceTests.java
deleted file mode 100644
index 943941e..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ResourceTests.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2006 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.unit_tests.content;
-
-import junit.framework.TestSuite;
-
-public class ResourceTests {
-    public static TestSuite suite() {
-        TestSuite suite = new TestSuite(ResourceTests.class.getName());
-
-        suite.addTestSuite(FractionTest.class);
-        suite.addTestSuite(PrimitiveTest.class);
-        suite.addTestSuite(ArrayTest.class);
-        suite.addTestSuite(ConfigTest.class);
-        suite.addTestSuite(RawResourceTest.class);
-        suite.addTestSuite(ResourceNameTest.class);
-        return suite;
-    }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestSuite.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestSuite.java
deleted file mode 100644
index f3d1c5e..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestSuite.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.vcard;
-
-import com.android.unit_tests.AndroidTests;
-
-import android.test.suitebuilder.TestSuiteBuilder;
-
-import junit.framework.TestSuite;
-
-public class VCardTestSuite extends TestSuite {
-    public static TestSuite suite() {
-        TestSuiteBuilder suiteBuilder = new TestSuiteBuilder(AndroidTests.class);
-        suiteBuilder.includeAllPackagesUnderHere();
-        return suiteBuilder.build();
-    }
-}
\ No newline at end of file
diff --git a/tests/CoreTests/android/core/CoreTests.java b/tests/CoreTests/android/core/CoreTests.java
index e4f835c..442fe0f 100644
--- a/tests/CoreTests/android/core/CoreTests.java
+++ b/tests/CoreTests/android/core/CoreTests.java
@@ -59,7 +59,6 @@
         suite.addTestSuite(LocationManagerProximityTest.class);
         suite.addTestSuite(AndroidTestRunnerTest.class);
         suite.addTestSuite(InstrumentationTestRunnerTest.class);
-        suite.addTestSuite(CookieTest.class);
         
         return suite;
     }
diff --git a/tests/CoreTests/android/webkit/CookieTest.java b/tests/CoreTests/android/webkit/CookieTest.java
deleted file mode 100644
index 5c5a6a8..0000000
--- a/tests/CoreTests/android/webkit/CookieTest.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.util.Log;
-import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
-
-public class CookieTest extends AndroidTestCase {
-
-    /**
-     * To run these tests: $ mmm frameworks/base/tests/CoreTests/android && adb
-     * remount && adb sync $ adb shell am instrument -w \ -e class
-     * android.webkit.CookieTest \
-     * android.core/android.test.InstrumentationTestRunner
-     */
-
-    private CookieManager mCookieManager;
-
-    @Override
-    public void setContext(Context context) {
-        assertTrue(mContext == null);
-        super.setContext(context);
-        CookieSyncManager.createInstance(context);
-        mCookieManager = CookieManager.getInstance();
-        mCookieManager.removeAllCookie();
-    }
-
-    public void testParse() {
-        mCookieManager.removeAllCookie();
-        String url = "http://www.foo.com";
-
-        // basic
-        mCookieManager.setCookie(url, "a=b");
-        String cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b"));
-
-        // quoted
-        mCookieManager.setCookie(url, "c=\"d;\"");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b; c=\"d;\""));
-
-        // empty
-        mCookieManager.setCookie(url, "; path=/");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b; c=\"d;\""));
-    }
-
-    public void testDomain() {
-        mCookieManager.removeAllCookie();
-        String url = "http://www.foo.com";
-
-        // basic
-        mCookieManager.setCookie(url, "a=b");
-        String cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b"));
-
-        // no cross domain cookie
-        cookie = mCookieManager.getCookie("http://bar.com");
-        assertTrue(cookie == null);
-
-        // more than one cookie
-        mCookieManager.setCookie(url, "c=d; domain=.foo.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b; c=d"));
-
-        // host cookie should not be accessible from a sub-domain.
-        cookie = mCookieManager.getCookie("http://bar.www.foo.com");
-        assertTrue(cookie.equals("c=d"));
-
-        // test setting a domain= that doesn't start w/ a dot, should
-        // treat it as a domain cookie, as if there was a pre-pended dot.
-        mCookieManager.setCookie(url, "e=f; domain=www.foo.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b; c=d; e=f"));
-        cookie = mCookieManager.getCookie("http://sub.www.foo.com");
-        assertTrue(cookie.equals("c=d; e=f"));
-        cookie = mCookieManager.getCookie("http://foo.com");
-        assertTrue(cookie.equals("c=d"));
-    }
-
-    public void testSubDomain() {
-        mCookieManager.removeAllCookie();
-        String url_abcd = "http://a.b.c.d.com";
-        String url_bcd = "http://b.c.d.com";
-        String url_cd = "http://c.d.com";
-        String url_d = "http://d.com";
-
-        mCookieManager.setCookie(url_abcd, "a=1; domain=.a.b.c.d.com");
-        mCookieManager.setCookie(url_abcd, "b=2; domain=.b.c.d.com");
-        mCookieManager.setCookie(url_abcd, "c=3; domain=.c.d.com");
-        mCookieManager.setCookie(url_abcd, "d=4; domain=.d.com");
-
-        String cookie = mCookieManager.getCookie(url_abcd);
-        assertTrue(cookie.equals("a=1; b=2; c=3; d=4"));
-        cookie = mCookieManager.getCookie(url_bcd);
-        assertTrue(cookie.equals("b=2; c=3; d=4"));
-        cookie = mCookieManager.getCookie(url_cd);
-        assertTrue(cookie.equals("c=3; d=4"));
-        cookie = mCookieManager.getCookie(url_d);
-        assertTrue(cookie.equals("d=4"));
-
-        // check that the same cookie can exist on different sub-domains.
-        mCookieManager.setCookie(url_bcd, "x=bcd; domain=.b.c.d.com");
-        mCookieManager.setCookie(url_bcd, "x=cd; domain=.c.d.com");
-        cookie = mCookieManager.getCookie(url_bcd);
-        assertTrue(cookie.equals("b=2; c=3; d=4; x=bcd; x=cd"));
-        cookie = mCookieManager.getCookie(url_cd);
-        assertTrue(cookie.equals("c=3; d=4; x=cd"));
-    }
-
-    public void testInvalidDomain() {
-        mCookieManager.removeAllCookie();
-        String url = "http://foo.bar.com";
-
-        mCookieManager.setCookie(url, "a=1; domain=.yo.foo.bar.com");
-        String cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "b=2; domain=.foo.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "c=3; domain=.bar.foo.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "d=4; domain=.foo.bar.com.net");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "e=5; domain=.ar.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "f=6; domain=.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "g=7; domain=.co.uk");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "h=8; domain=.foo.bar.com.com");
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-    }
-
-    public void testPath() {
-        mCookieManager.removeAllCookie();
-        String url = "http://www.foo.com";
-
-        mCookieManager.setCookie(url, "a=b; path=/wee");
-        String cookie = mCookieManager.getCookie(url + "/wee");
-        assertTrue(cookie.equals("a=b"));
-        cookie = mCookieManager.getCookie(url + "/wee/");
-        assertTrue(cookie.equals("a=b"));
-        cookie = mCookieManager.getCookie(url + "/wee/hee");
-        assertTrue(cookie.equals("a=b"));
-        cookie = mCookieManager.getCookie(url + "/wee/hee/more");
-        assertTrue(cookie.equals("a=b"));
-        cookie = mCookieManager.getCookie(url + "/weehee");
-        assertTrue(cookie == null);
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie == null);
-
-        mCookieManager.setCookie(url, "a=c; path=");
-        cookie = mCookieManager.getCookie(url + "/wee");
-        assertTrue(cookie.equals("a=b; a=c"));
-        cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=c"));
-
-        mCookieManager.setCookie(url, "a=d");
-        cookie = mCookieManager.getCookie(url + "/wee");
-        assertTrue(cookie.equals("a=b; a=d"));
-    }
-}
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/expandablelistview/ExpandableListWithHeadersTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/expandablelistview/ExpandableListWithHeadersTest.java
deleted file mode 100644
index 49b5106..0000000
--- a/tests/FrameworkTest/tests/src/com/android/frameworktest/expandablelistview/ExpandableListWithHeadersTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.frameworktest.expandablelistview;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.KeyEvent;
-import android.widget.ExpandableListView;
-
-import com.android.frameworktest.expandablelistview.ExpandableListWithHeaders;
-import com.android.frameworktest.util.ListUtil;
-
-public class ExpandableListWithHeadersTest extends ActivityInstrumentationTestCase<ExpandableListWithHeaders> {
-    private ExpandableListView mExpandableListView;
-    private ListUtil mListUtil;
-    
-    public ExpandableListWithHeadersTest() {
-        super("com.android.frameworktest",
-                ExpandableListWithHeaders.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        
-        mExpandableListView = getActivity().getExpandableListView();
-        mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
-    }
-    
-    @MediumTest
-    public void testPreconditions() {
-        assertNotNull(mExpandableListView);
-    }
-    
-    @MediumTest
-    public void testExpandOnFirstPosition() {
-        // Should be a header, and hence the first group should NOT have expanded
-        mListUtil.arrowScrollToSelectedPosition(0);
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertFalse(mExpandableListView.isGroupExpanded(0));
-    }
-
-    @LargeTest
-    public void testExpandOnFirstGroup() {
-        mListUtil.arrowScrollToSelectedPosition(getActivity().getNumOfHeadersAndFooters());
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertTrue(mExpandableListView.isGroupExpanded(0));
-    }
-}
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/view/RemoteViewsActivityTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/view/RemoteViewsActivityTest.java
deleted file mode 100644
index 3dcb252..0000000
--- a/tests/FrameworkTest/tests/src/com/android/frameworktest/view/RemoteViewsActivityTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.frameworktest.view;
-
-import android.os.Parcel;
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.InflateException;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RemoteViews;
-
-import com.android.frameworktest.R;
-import com.android.frameworktest.view.RemoteViewsActivity;
-
-public class RemoteViewsActivityTest extends ActivityInstrumentationTestCase<RemoteViewsActivity> {
-    public RemoteViewsActivityTest() {
-        super("com.android.frameworktest", RemoteViewsActivity.class);
-    }
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @MediumTest
-    public void testGood() throws Exception {
-        final RemoteViewsActivity activity = getActivity();
-
-        RemoteViews orig = new RemoteViews("com.android.frameworktest",
-                R.layout.remote_view_test_good);
-        Parcel p = Parcel.obtain();
-        orig.writeToParcel(p, 0);
-        p.setDataPosition(0);
-        
-        RemoteViews r = RemoteViews.CREATOR.createFromParcel(p);
-        
-        ViewGroup parent = (ViewGroup) activity.findViewById(R.id.parent);
-        
-        View result = r.apply(activity, parent);
-        
-        p.recycle();
-        
-        assertTrue("LinearLayout not inflated", result.findViewById(R.id.linear) != null);
-        assertTrue("TextView not inflated", result.findViewById(R.id.text) != null);
-        assertTrue("ImageView not inflated", result.findViewById(R.id.image) != null);
-        assertTrue("FrameLayout not inflated", result.findViewById(R.id.frame) != null);
-        assertTrue("RelateiveLayout not inflated", result.findViewById(R.id.relative) != null);
-        assertTrue("AbsoluteLayout not inflated", result.findViewById(R.id.absolute) != null);
-        assertTrue("ProgressBar not inflated", result.findViewById(R.id.progress) != null);
-        assertTrue("ImageButton not inflated", result.findViewById(R.id.image_button) != null);
-        assertTrue("Button not inflated", result.findViewById(R.id.button) != null);
-    }
-    
-    @MediumTest
-    public void testDerivedClass() throws Exception {
-        final RemoteViewsActivity activity = getActivity();
-        
-        RemoteViews orig = new RemoteViews("com.android.frameworktest",
-                R.layout.remote_view_test_bad_1);
-        Parcel p = Parcel.obtain();
-        orig.writeToParcel(p, 0);
-        p.setDataPosition(0);
-        
-        RemoteViews r = RemoteViews.CREATOR.createFromParcel(p);
-        
-        ViewGroup parent = (ViewGroup) activity.findViewById(R.id.parent);
-        
-        boolean exceptionThrown = false;
-        View result = null;
-        
-        try {
-            result = r.apply(activity, parent);
-        } catch (InflateException e) {
-            exceptionThrown = true;
-        }
-        
-        p.recycle();
-        
-        assertTrue("Derived class (EditText) allowed to be inflated", exceptionThrown);
-        assertNull("Derived class (EditText) allowed to be inflated", result);
-    }
-    
-    @MediumTest
-    public void testWebView() throws Exception {
-        final RemoteViewsActivity activity = getActivity();
-        
-        RemoteViews orig = new RemoteViews("com.android.frameworktest",
-                R.layout.remote_view_test_bad_2);
-        Parcel p = Parcel.obtain();
-        orig.writeToParcel(p, 0);
-        p.setDataPosition(0);
-        
-        RemoteViews r = RemoteViews.CREATOR.createFromParcel(p);
-        
-        ViewGroup parent = (ViewGroup) activity.findViewById(R.id.parent);
-        
-        boolean exceptionThrown = false;
-        View result = null;
-        
-        try {
-            result = r.apply(activity, parent);
-        } catch (InflateException e) {
-            exceptionThrown = true;
-        }
-        
-        p.recycle();
-        
-        assertTrue("WebView allowed to be inflated", exceptionThrown);
-        assertNull("WebView allowed to be inflated", result);
-    }
-}
diff --git a/tests/TransformTest/Android.mk b/tests/TransformTest/Android.mk
new file mode 100644
index 0000000..2d3637d
--- /dev/null
+++ b/tests/TransformTest/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TransformTest
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/TransformTest/AndroidManifest.xml b/tests/TransformTest/AndroidManifest.xml
new file mode 100644
index 0000000..5c9995f
--- /dev/null
+++ b/tests/TransformTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test.transform">
+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
+    <application android:label="TransformTest">
+        <activity android:name="TransformTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/TransformTest/res/drawable/logo.png b/tests/TransformTest/res/drawable/logo.png
new file mode 100644
index 0000000..4d717a8
--- /dev/null
+++ b/tests/TransformTest/res/drawable/logo.png
Binary files differ
diff --git a/tests/TransformTest/res/values/strings.xml b/tests/TransformTest/res/values/strings.xml
new file mode 100644
index 0000000..a0eb81f
--- /dev/null
+++ b/tests/TransformTest/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="act_title">TransformTest</string>
+</resources>
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
new file mode 100644
index 0000000..52286d1
--- /dev/null
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.transform;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.TransformGestureDetector;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class TransformTestActivity extends Activity {
+    public TransformTestActivity() {
+        super();
+        init(false);
+    }
+    
+    public TransformTestActivity(boolean noCompat) {
+        super();
+        init(noCompat);
+    }
+    
+    public void init(boolean noCompat) {
+
+    }
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LayoutInflater li = (LayoutInflater)getSystemService(
+                LAYOUT_INFLATER_SERVICE);
+        
+        this.setTitle(R.string.act_title);
+        LinearLayout root = new LinearLayout(this);
+        root.setOrientation(LinearLayout.VERTICAL);
+
+        TransformView view = new TransformView(getApplicationContext());
+        Drawable drawable = getResources().getDrawable(R.drawable.logo);
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicWidth());
+        view.setDrawable(drawable);
+
+        root.addView(view);
+        setContentView(root);
+    }
+    
+    private class TransformView extends View {
+        private Drawable mDrawable;
+        private float mPosX;
+        private float mPosY;
+        private float mScale = 1.f;
+        private Matrix mMatrix;
+        private TransformGestureDetector mDetector;
+        
+        private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+
+            public boolean onTransform(TransformGestureDetector detector) {
+                Log.d("ttest", "Translation: (" + detector.getTranslateX() +
+                        ", " + detector.getTranslateY() + ")");
+                float scale = detector.getScaleFactor();
+                Log.d("ttest", "Scale: " + scale);
+                if (mScale * scale > 0.1f) {
+                    if (mScale * scale < 10.f) {
+                        mScale *= scale;
+                    } else {
+                        mScale = 10.f;
+                    }
+                } else {
+                    mScale = 0.1f;
+                }
+
+                mPosX += detector.getTranslateX();
+                mPosY += detector.getTranslateY();
+                
+                Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
+                
+                float sizeX = mDrawable.getIntrinsicWidth()/2;
+                float sizeY = mDrawable.getIntrinsicHeight()/2;
+                float centerX = detector.getCenterX();
+                float centerY = detector.getCenterY();
+                float diffX = centerX - mPosX;
+                float diffY = centerY - mPosY;
+                diffX = diffX*scale - diffX;
+                diffY = diffY*scale - diffY;
+                mPosX -= diffX;
+                mPosY -= diffY;
+                mMatrix.reset();
+                mMatrix.postTranslate(-sizeX, -sizeY);
+                mMatrix.postScale(mScale, mScale);
+                mMatrix.postTranslate(mPosX, mPosY);
+                                
+                invalidate();
+
+                return true;
+            }
+
+            public boolean onTransformBegin(TransformGestureDetector detector) {
+                return true;
+            }
+
+            public boolean onTransformEnd(TransformGestureDetector detector) {
+                return true;
+            }
+
+            public boolean onTransformFling(TransformGestureDetector detector) {
+                return false;
+            }
+            
+        }
+        
+        public TransformView(Context context) {
+            super(context);
+            mMatrix = new Matrix();
+            mDetector = new TransformGestureDetector(context, new Listener());
+            DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+            mPosX = metrics.widthPixels/2;
+            mPosY = metrics.heightPixels/2;
+        }
+        
+        public void setDrawable(Drawable d) {
+            mDrawable = d;
+            
+            float sizeX = mDrawable.getIntrinsicWidth()/2;
+            float sizeY = mDrawable.getIntrinsicHeight()/2;
+            mMatrix.reset();
+            mMatrix.postTranslate(-sizeX, -sizeY);
+            mMatrix.postScale(mScale, mScale);
+            mMatrix.postTranslate(mPosX, mPosY);
+        }
+        
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            boolean handled = mDetector.onTouchEvent(event);
+            
+            int pointerCount = event.getPointerCount();
+            Log.d("ttest", "pointerCount: " + pointerCount);
+
+            return handled;
+        }
+        
+        @Override
+        public void onDraw(Canvas canvas) {
+            int saveCount = canvas.getSaveCount();
+            canvas.save();
+            canvas.concat(mMatrix);
+            mDrawable.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        }
+    }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index de6ff14..ae8f242 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1768,31 +1768,33 @@
                     fprintf(stderr, "ERROR: %s\n", error.string());
                     return -1;
                 }
-                // asdf     --> package.asdf
-                // .asdf  .a.b  --> package.asdf package.a.b
-                // asdf.adsf --> asdf.asdf
-                String8 rule("-keep class ");
-                const char* p = name.string();
-                const char* q = strchr(p, '.');
-                if (p == q) {
-                    rule += pkg;
-                    rule += name;
-                } else if (q == NULL) {
-                    rule += pkg;
-                    rule += ".";
-                    rule += name;
-                } else {
-                    rule += name;
+                if (name.length() > 0) {
+                    // asdf     --> package.asdf
+                    // .asdf  .a.b  --> package.asdf package.a.b
+                    // asdf.adsf --> asdf.asdf
+                    String8 rule("-keep class ");
+                    const char* p = name.string();
+                    const char* q = strchr(p, '.');
+                    if (p == q) {
+                        rule += pkg;
+                        rule += name;
+                    } else if (q == NULL) {
+                        rule += pkg;
+                        rule += ".";
+                        rule += name;
+                    } else {
+                        rule += name;
+                    }
+
+                    String8 location = tag;
+                    location += " ";
+                    location += assFile->getSourceFile();
+                    char lineno[20];
+                    sprintf(lineno, ":%d", tree.getLineNumber());
+                    location += lineno;
+
+                    keep->add(rule, location);
                 }
-
-                String8 location = tag;
-                location += " ";
-                location += assFile->getSourceFile();
-                char lineno[20];
-                sprintf(lineno, ":%d", tree.getLineNumber());
-                location += lineno;
-
-                keep->add(rule, location);
             }
         }
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index 1e9f573..b670eee 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -259,7 +259,18 @@
 
         // resolve the defStyleAttr value into a IStyleResourceValue
         IStyleResourceValue defStyleValues = null;
-        if (defStyleAttr != 0) {
+
+        // look for a custom style.
+        String customStyle = parser.getAttributeValue(null /* namespace*/, "style");
+        if (customStyle != null) {
+            IResourceValue item = findResValue(customStyle);
+
+            if (item instanceof IStyleResourceValue) {
+                defStyleValues = (IStyleResourceValue)item;
+            }
+        }
+
+        if (defStyleValues == null && defStyleAttr != 0) {
             // get the name from the int.
             String defStyleName = searchAttr(defStyleAttr);
 
@@ -1104,7 +1115,7 @@
            Bundle initialExtras) {
         // TODO Auto-generated method stub
     }
-    
+
     @Override
     public void setTheme(int arg0) {
         // TODO Auto-generated method stub
@@ -1137,7 +1148,7 @@
             throws IntentSender.SendIntentException {
         // TODO Auto-generated method stub
     }
-    
+
     @Override
     public boolean startInstrumentation(ComponentName arg0, String arg1,
             Bundle arg2) {