Merge "Code refactoring for TextLayoutCache"
diff --git a/api/current.xml b/api/current.xml
index e17bce5..1f3b5fd 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -180297,6 +180297,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_ORIGIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.speech.extra.ORIGIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_PARTIAL_RESULTS"
  type="java.lang.String"
  transient="false"
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 8641c30..d7a9ef6 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -1,13 +1,34 @@
 ifneq ($(TARGET_SIMULATOR),true)
 
 LOCAL_PATH := $(call my-dir)
+
+common_src_files := \
+    commands.c utils.c
+
+#
+# Static library used in testing and executable
+#
+
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-    installd.c commands.c utils.c
+    $(common_src_files)
 
-#LOCAL_C_INCLUDES := \
-#    $(call include-path-for, system-core)/cutils
+LOCAL_MODULE := libinstalld
+
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Executable
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    installd.c \
+    $(common_src_files)
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 4d49c30..80ba1e9 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -17,6 +17,13 @@
 #include "installd.h"
 #include <diskusage/dirsize.h>
 
+/* Directory records that are used in execution of commands. */
+dir_rec_t android_data_dir;
+dir_rec_t android_asec_dir;
+dir_rec_t android_app_dir;
+dir_rec_t android_app_private_dir;
+dir_rec_array_t android_system_dirs;
+
 int install(const char *pkgname, uid_t uid, gid_t gid)
 {
     char pkgdir[PKG_PATH_MAX];
@@ -27,10 +34,15 @@
         return -1;
     }
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
+        LOGE("cannot create package path\n");
         return -1;
-    if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX))
+    }
+
+    if (create_pkg_path(libdir, pkgname, PKG_LIB_POSTFIX, 0)) {
+        LOGE("cannot create package lib path\n");
         return -1;
+    }
 
     if (mkdir(pkgdir, 0751) < 0) {
         LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
@@ -59,7 +71,7 @@
 {
     char pkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0))
         return -1;
 
         /* delete contents AND directory, no exceptions */
@@ -71,9 +83,9 @@
     char oldpkgdir[PKG_PATH_MAX];
     char newpkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+    if (create_pkg_path(oldpkgdir, oldpkgname, PKG_DIR_POSTFIX, 0))
         return -1;
-    if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+    if (create_pkg_path(newpkgdir, newpkgname, PKG_DIR_POSTFIX, 0))
         return -1;
 
     if (rename(oldpkgdir, newpkgdir) < 0) {
@@ -87,7 +99,7 @@
 {
     char pkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0))
         return -1;
 
         /* delete contents, excluding "lib", but not the directory itself */
@@ -98,7 +110,7 @@
 {
     char cachedir[PKG_PATH_MAX];
 
-    if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX))
+    if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, 0))
         return -1;
 
         /* delete contents, not the directory, no exceptions */
@@ -108,10 +120,10 @@
 static int64_t disk_free()
 {
     struct statfs sfs;
-    if (statfs(PKG_DIR_PREFIX, &sfs) == 0) {
+    if (statfs(android_data_dir.path, &sfs) == 0) {
         return sfs.f_bavail * sfs.f_bsize;
     } else {
-        LOGE("Couldn't statfs " PKG_DIR_PREFIX ": %s\n", strerror(errno));
+        LOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno));
         return -1;
     }
 }
@@ -137,9 +149,9 @@
     LOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
     if (avail >= free_size) return 0;
 
-    d = opendir(PKG_DIR_PREFIX);
+    d = opendir(android_data_dir.path);
     if (d == NULL) {
-        LOGE("cannot open %s: %s\n", PKG_DIR_PREFIX, strerror(errno));
+        LOGE("cannot open %s: %s\n", android_data_dir.path, strerror(errno));
         return -1;
     }
     dfd = dirfd(d);
@@ -172,43 +184,13 @@
     return -1;
 }
 
-/* used by move_dex, rm_dex, etc to ensure that the provided paths
- * don't point anywhere other than at the APK_DIR_PREFIX
- */
-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)) {
-            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 ((nosubdircheck != 1) && strchr(path + len, '/')) {
-        LOGE("invalid apk path '%s' (subdir?)\n", path);
-        return 0;
-    }
-    if (path[len] == '.') {
-        LOGE("invalid apk path '%s' (trickery)\n", path);
-        return 0;
-    }
-    return 1;
-}
-
 int move_dex(const char *src, const char *dst)
 {
     char src_dex[PKG_PATH_MAX];
     char dst_dex[PKG_PATH_MAX];
 
-    if (!is_valid_apk_path(src)) return -1;
-    if (!is_valid_apk_path(dst)) return -1;
+    if (validate_apk_path(src)) return -1;
+    if (validate_apk_path(dst)) return -1;
 
     if (create_cache_path(src_dex, src)) return -1;
     if (create_cache_path(dst_dex, dst)) return -1;
@@ -226,7 +208,7 @@
 {
     char dex_path[PKG_PATH_MAX];
 
-    if (!is_valid_apk_path(path)) return -1;
+    if (validate_apk_path(path)) return -1;
     if (create_cache_path(dex_path, path)) return -1;
 
     LOGI("unlink %s\n", dex_path);
@@ -245,7 +227,7 @@
 
     if (gid < AID_SYSTEM) return -1;
 
-    if (create_pkg_path(pkgpath, PROTECTED_DIR_PREFIX, pkgname, ".apk"))
+    if (create_pkg_path_in_dir(pkgpath, &android_app_private_dir, pkgname, ".apk"))
         return -1;
 
     if (stat(pkgpath, &s) < 0) return -1;
@@ -280,8 +262,8 @@
         /* count the source apk as code -- but only if it's not
          * on the /system partition and its not on the sdcard.
          */
-    if (strncmp(apkpath, "/system", 7) != 0 &&
-            strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) {
+    if (validate_system_app_path(apkpath) &&
+            strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) {
         if (stat(apkpath, &s) == 0) {
             codesize += stat_size(&s);
         }
@@ -300,7 +282,7 @@
         }
     }
 
-    if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) {
+    if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) {
         goto done;
     }
 
@@ -310,10 +292,10 @@
     }
     dfd = dirfd(d);
 
-        /* most stuff in the pkgdir is data, except for the "cache"
-         * directory and below, which is cache, and the "lib" directory
-         * and below, which is code...
-         */
+    /* most stuff in the pkgdir is data, except for the "cache"
+     * directory and below, which is cache, and the "lib" directory
+     * and below, which is code...
+     */
     while ((de = readdir(d))) {
         const char *name = de->d_name;
 
@@ -544,15 +526,15 @@
 }
 
 int create_move_path(char path[PKG_PATH_MAX],
-    const char* prefix,
     const char* pkgname,
-    const char* leaf)
+    const char* leaf,
+    uid_t persona)
 {
-    if ((strlen(prefix) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) {
+    if ((android_data_dir.len + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) {
         return -1;
     }
     
-    sprintf(path, "%s%s/%s", prefix, pkgname, leaf);
+    sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf);
     return 0;
 }
 
@@ -720,8 +702,8 @@
                             // Skip -- source package no longer exists.
                         } else {
                             LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
-                            if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) &&
-                                    !create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) {
+                            if (!create_move_path(srcpath, srcpkg, buf+bufp, 0) &&
+                                    !create_move_path(dstpath, dstpkg, buf+bufp, 0)) {
                                 movefileordir(srcpath, dstpath,
                                         strlen(dstpath)-strlen(buf+bufp),
                                         dstuid, dstgid, &s);
@@ -750,8 +732,7 @@
                                         UPDATE_COMMANDS_DIR_PREFIX, name, div);
                             }
                             if (srcpkg[0] != 0) {
-                                if (!create_pkg_path(srcpath, PKG_DIR_PREFIX, srcpkg,
-                                        PKG_DIR_POSTFIX)) {
+                                if (!create_pkg_path(srcpath, srcpkg, PKG_DIR_POSTFIX, 0)) {
                                     if (lstat(srcpath, &s) < 0) {
                                         // Package no longer exists -- skip.
                                         srcpkg[0] = 0;
@@ -762,8 +743,7 @@
                                             div, UPDATE_COMMANDS_DIR_PREFIX, name);
                                 }
                                 if (srcpkg[0] != 0) {
-                                    if (!create_pkg_path(dstpath, PKG_DIR_PREFIX, dstpkg,
-                                            PKG_DIR_POSTFIX)) {
+                                    if (!create_pkg_path(dstpath, dstpkg, PKG_DIR_POSTFIX, 0)) {
                                         if (lstat(dstpath, &s) == 0) {
                                             dstuid = s.st_uid;
                                             dstgid = s.st_gid;
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index d2b2f7f..e0d0f97 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -21,7 +21,6 @@
 #define TOKEN_MAX     8     /* max number of arguments in buffer */
 #define REPLY_MAX     256   /* largest reply allowed */
 
-
 static int do_ping(char **arg, char reply[REPLY_MAX])
 {
     return 0;
@@ -235,12 +234,77 @@
     return 0;
 }
 
-int main(const int argc, const char *argv[]) {    
+/**
+ * Initialize all the global variables that are used elsewhere. Returns 0 upon
+ * success and -1 on error.
+ */
+void free_globals() {
+    size_t i;
+
+    for (i = 0; i < android_system_dirs.count; i++) {
+        if (android_system_dirs.dirs[i].path != NULL) {
+            free(android_system_dirs.dirs[i].path);
+        }
+    }
+
+    free(android_system_dirs.dirs);
+}
+
+int initialize_globals() {
+    // Get the android data directory.
+    if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) {
+        return -1;
+    }
+
+    // Get the android app directory.
+    if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) {
+        return -1;
+    }
+
+    // Get the android protected app directory.
+    if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) {
+        return -1;
+    }
+
+    // Get the sd-card ASEC mount point.
+    if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {
+        return -1;
+    }
+
+    // Take note of the system and vendor directories.
+    android_system_dirs.count = 2;
+
+    android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t));
+    if (android_system_dirs.dirs == NULL) {
+        LOGE("Couldn't allocate array for dirs; aborting\n");
+        return -1;
+    }
+
+    // system
+    if (get_path_from_env(&android_system_dirs.dirs[0], "ANDROID_ROOT") < 0) {
+        free_globals();
+        return -1;
+    }
+
+    // vendor
+    // TODO replace this with an environment variable (doesn't exist yet)
+    android_system_dirs.dirs[1].path = "/vendor/";
+    android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
+
+    return 0;
+}
+
+int main(const int argc, const char *argv[]) {
     char buf[BUFFER_MAX];
     struct sockaddr addr;
     socklen_t alen;
     int lsocket, s, count;
 
+    if (initialize_globals() < 0) {
+        LOGE("Could not initialize globals; exiting.\n");
+        exit(1);
+    }
+
     lsocket = android_get_control_socket(SOCKET_PATH);
     if (lsocket < 0) {
         LOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 77b58ec..cbca135 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -49,37 +49,60 @@
 
 /* elements combined with a valid package name to form paths */
 
-#define PKG_DIR_PREFIX         "/data/data/"
+#define PRIMARY_USER_PREFIX    "data/"
+#define SECONDARY_USER_PREFIX  "user/"
+
 #define PKG_DIR_POSTFIX        ""
 
-#define PKG_LIB_PREFIX         "/data/data/"
 #define PKG_LIB_POSTFIX        "/lib"
 
-#define CACHE_DIR_PREFIX       "/data/data/"
 #define CACHE_DIR_POSTFIX      "/cache"
 
-#define APK_DIR_PREFIX         "/data/app/"
+#define APP_SUBDIR             "app/" // sub-directory under ANDROID_DATA
 
 /* other handy constants */
 
-#define PROTECTED_DIR_PREFIX  "/data/app-private/"
-#define SDCARD_DIR_PREFIX  getenv("ASEC_MOUNTPOINT")
+#define PRIVATE_APP_SUBDIR     "app-private/" // sub-directory under ANDROID_DATA
 
-#define DALVIK_CACHE_PREFIX   "/data/dalvik-cache/"
-#define DALVIK_CACHE_POSTFIX  "/classes.dex"
+#define DALVIK_CACHE_PREFIX    "/data/dalvik-cache/"
+#define DALVIK_CACHE_POSTFIX   "/classes.dex"
 
 #define UPDATE_COMMANDS_DIR_PREFIX  "/system/etc/updatecmds/"
 
 #define PKG_NAME_MAX  128   /* largest allowed package name */
 #define PKG_PATH_MAX  256   /* max size of any path we use */
 
+/* data structures */
+
+typedef struct {
+    char* path;
+    size_t len;
+} dir_rec_t;
+
+typedef struct {
+    size_t count;
+    dir_rec_t* dirs;
+} dir_rec_array_t;
+
+extern dir_rec_t android_app_dir;
+extern dir_rec_t android_app_private_dir;
+extern dir_rec_t android_data_dir;
+extern dir_rec_t android_asec_dir;
+extern dir_rec_array_t android_system_dirs;
 
 /* util.c */
 
+int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
+                                const dir_rec_t* dir,
+                                const char* pkgname,
+                                const char* postfix);
+
 int create_pkg_path(char path[PKG_PATH_MAX],
-                    const char *prefix,
                     const char *pkgname,
-                    const char *postfix);
+                    const char *postfix,
+                    uid_t persona);
+
+int is_valid_package_name(const char* pkgname);
 
 int create_cache_path(char path[PKG_PATH_MAX], const char *src);
 
@@ -89,6 +112,18 @@
 
 int delete_dir_contents_fd(int dfd, const char *name);
 
+int validate_system_app_path(const char* path);
+
+int get_path_from_env(dir_rec_t* rec, const char* var);
+
+int get_path_from_string(dir_rec_t* rec, const char* path);
+
+int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix);
+
+int validate_apk_path(const char *path);
+
+int append_and_increment(char** dst, const char* src, size_t* dst_size);
+
 /* commands.c */
 
 int install(const char *pkgname, uid_t uid, gid_t gid);
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
new file mode 100644
index 0000000..e53378d
--- /dev/null
+++ b/cmds/installd/tests/Android.mk
@@ -0,0 +1,42 @@
+# Build the unit tests for installd
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
+test_src_files := \
+    installd_utils_test.cpp
+
+shared_libraries := \
+    libutils \
+    libcutils \
+    libstlport
+
+static_libraries := \
+    libinstalld \
+    libdiskusage \
+    libgtest \
+    libgtest_main
+
+c_includes := \
+    frameworks/base/cmds/installd \
+    bionic \
+    bionic/libstdc++/include \
+    external/gtest/include \
+    external/stlport/stlport
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+    $(eval include $(BUILD_EXECUTABLE)) \
+)
+
+endif
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
new file mode 100644
index 0000000..1128fce
--- /dev/null
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2011 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 <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "utils_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "installd.h"
+}
+
+#define TEST_DATA_DIR "/data/"
+#define TEST_APP_DIR "/data/app/"
+#define TEST_APP_PRIVATE_DIR "/data/app-private/"
+#define TEST_ASEC_DIR "/mnt/asec/"
+
+#define TEST_SYSTEM_DIR1 "/system/app/"
+#define TEST_SYSTEM_DIR2 "/vendor/app/"
+
+namespace android {
+
+class UtilsTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+        android_app_dir.path = TEST_APP_DIR;
+        android_app_dir.len = strlen(TEST_APP_DIR);
+
+        android_app_private_dir.path = TEST_APP_PRIVATE_DIR;
+        android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
+
+        android_data_dir.path = TEST_DATA_DIR;
+        android_data_dir.len = strlen(TEST_DATA_DIR);
+
+        android_asec_dir.path = TEST_ASEC_DIR;
+        android_asec_dir.len = strlen(TEST_ASEC_DIR);
+
+        android_system_dirs.count = 2;
+
+        android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
+        android_system_dirs.dirs[0].path = TEST_SYSTEM_DIR1;
+        android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1);
+
+        android_system_dirs.dirs[1].path = TEST_SYSTEM_DIR2;
+        android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+    }
+
+    virtual void TearDown() {
+        free(android_system_dirs.dirs);
+    }
+};
+
+TEST_F(UtilsTest, IsValidApkPath_BadPrefix) {
+    // Bad prefixes directories
+    const char *badprefix1 = "/etc/passwd";
+    EXPECT_EQ(-1, validate_apk_path(badprefix1))
+            << badprefix1 << " should be allowed as a valid path";
+
+    const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah";
+    EXPECT_EQ(-1, validate_apk_path(badprefix2))
+            << badprefix2 << " should be allowed as a valid path";
+
+    const char *badprefix3 = "init.rc";
+    EXPECT_EQ(-1, validate_apk_path(badprefix3))
+            << badprefix3 << " should be allowed as a valid path";
+
+    const char *badprefix4 = "/init.rc";
+    EXPECT_EQ(-1, validate_apk_path(badprefix4))
+            << badprefix4 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_Internal) {
+    // Internal directories
+    const char *internal1 = TEST_APP_DIR "example.apk";
+    EXPECT_EQ(0, validate_apk_path(internal1))
+            << internal1 << " should be allowed as a valid path";
+
+    const char *badint1 = TEST_APP_DIR "../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badint1))
+            << badint1 << " should be rejected as a invalid path";
+
+    const char *badint2 = TEST_APP_DIR "/../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badint2))
+            << badint2 << " should be rejected as a invalid path";
+
+    const char *badint3 = TEST_APP_DIR "example.com/pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badint3))
+            << badint3 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_Private) {
+    // Internal directories
+    const char *private1 = TEST_APP_PRIVATE_DIR "example.apk";
+    EXPECT_EQ(0, validate_apk_path(private1))
+            << private1 << " should be allowed as a valid path";
+
+    const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badpriv1))
+            << badpriv1 << " should be rejected as a invalid path";
+
+    const char *badpriv2 = TEST_APP_PRIVATE_DIR "/../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badpriv2))
+            << badpriv2 << " should be rejected as a invalid path";
+
+    const char *badpriv3 = TEST_APP_PRIVATE_DIR "example.com/pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badpriv3))
+            << badpriv3 << " should be rejected as a invalid path";
+}
+
+
+TEST_F(UtilsTest, IsValidApkPath_AsecGood1) {
+    const char *asec1 = TEST_ASEC_DIR "example.apk";
+    EXPECT_EQ(0, validate_apk_path(asec1))
+            << asec1 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_AsecGood2) {
+    const char *asec2 = TEST_ASEC_DIR "com.example.asec/pkg.apk";
+    EXPECT_EQ(0, validate_apk_path(asec2))
+            << asec2 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_EscapeFail) {
+    const char *badasec1 = TEST_ASEC_DIR "../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec1))
+            << badasec1 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_DoubleSlashFail) {
+    const char *badasec2 = TEST_ASEC_DIR "com.example.asec//pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec2))
+            << badasec2 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeFail) {
+    const char *badasec3 = TEST_ASEC_DIR "com.example.asec/../../../pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec3))
+            << badasec3  << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SlashEscapeFail) {
+    const char *badasec4 = TEST_ASEC_DIR "/../example.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec4))
+            << badasec4 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_CrazyDirFail) {
+    const char *badasec5 = TEST_ASEC_DIR ".//../..";
+    EXPECT_EQ(-1, validate_apk_path(badasec5))
+            << badasec5 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) {
+    const char *badasec6 = TEST_ASEC_DIR "com.example.asec/../pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec6))
+            << badasec6 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
+    const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec7))
+            << badasec7 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_Dir1) {
+    const char *sysapp1 = TEST_SYSTEM_DIR1 "Voice.apk";
+    EXPECT_EQ(0, validate_system_app_path(sysapp1))
+            << sysapp1 << " should be allowed as a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_Dir2) {
+    const char *sysapp2 = TEST_SYSTEM_DIR2 "com.example.myapp.apk";
+    EXPECT_EQ(0, validate_system_app_path(sysapp2))
+            << sysapp2 << " should be allowed as a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_EscapeFail) {
+    const char *badapp1 = TEST_SYSTEM_DIR1 "../com.example.apk";
+    EXPECT_EQ(-1, validate_system_app_path(badapp1))
+            << badapp1 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_DoubleEscapeFail) {
+    const char *badapp2 = TEST_SYSTEM_DIR2 "/../../com.example.apk";
+    EXPECT_EQ(-1, validate_system_app_path(badapp2))
+            << badapp2 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) {
+    const char *badapp3 = TEST_APP_DIR "/../../com.example.apk";
+    EXPECT_EQ(-1, validate_system_app_path(badapp3))
+            << badapp3 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, GetPathFromString_NullPathFail) {
+    dir_rec_t test1;
+    EXPECT_EQ(-1, get_path_from_string(&test1, NULL))
+            << "Should not allow NULL as a path.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_EmptyPathFail) {
+    dir_rec_t test1;
+    EXPECT_EQ(-1, get_path_from_string(&test1, ""))
+            << "Should not allow empty paths.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_RelativePathFail) {
+    dir_rec_t test1;
+    EXPECT_EQ(-1, get_path_from_string(&test1, "mnt/asec"))
+            << "Should not allow relative paths.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_NonCanonical) {
+    dir_rec_t test1;
+
+    EXPECT_EQ(0, get_path_from_string(&test1, "/mnt/asec"))
+            << "Should be able to canonicalize directory /mnt/asec";
+    EXPECT_STREQ("/mnt/asec/", test1.path)
+            << "/mnt/asec should be canonicalized to /mnt/asec/";
+    EXPECT_EQ(10, (ssize_t) test1.len)
+            << "path len should be equal to the length of /mnt/asec/ (10)";
+    free(test1.path);
+}
+
+TEST_F(UtilsTest, GetPathFromString_CanonicalPath) {
+    dir_rec_t test3;
+    EXPECT_EQ(0, get_path_from_string(&test3, "/data/app/"))
+            << "Should be able to canonicalize directory /data/app/";
+    EXPECT_STREQ("/data/app/", test3.path)
+            << "/data/app/ should be canonicalized to /data/app/";
+    EXPECT_EQ(10, (ssize_t) test3.len)
+            << "path len should be equal to the length of /data/app/ (10)";
+    free(test3.path);
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPkgNameSuccess) {
+    char path[PKG_PATH_MAX];
+
+    // Create long packagename of "aaaaa..."
+    size_t pkgnameSize = PKG_NAME_MAX;
+    char pkgname[pkgnameSize + 1];
+    memset(pkgname, 'a', pkgnameSize);
+    pkgname[pkgnameSize] = '\0';
+
+    EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
+            << "Should successfully be able to create package name.";
+
+    const char *prefix = TEST_DATA_DIR PRIMARY_USER_PREFIX;
+    size_t offset = strlen(prefix);
+    EXPECT_STREQ(pkgname, path + offset)
+             << "Package path should be a really long string of a's";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) {
+    char path[PKG_PATH_MAX];
+
+    // Create long packagename of "aaaaa..."
+    size_t pkgnameSize = PKG_NAME_MAX + 1;
+    char pkgname[pkgnameSize + 1];
+    memset(pkgname, 'a', pkgnameSize);
+    pkgname[pkgnameSize] = '\0';
+
+    EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0))
+            << "Should return error because package name is too long.";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
+    char path[PKG_PATH_MAX];
+
+    // Create long packagename of "aaaaa..."
+    size_t postfixSize = PKG_PATH_MAX;
+    char postfix[postfixSize + 1];
+    memset(postfix, 'a', postfixSize);
+    postfix[postfixSize] = '\0';
+
+    EXPECT_EQ(-1, create_pkg_path(path, "com.example.package", postfix, 0))
+            << "Should return error because postfix is too long.";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_PrimaryUser) {
+    char path[PKG_PATH_MAX];
+
+    EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 0))
+            << "Should return error because postfix is too long.";
+
+    EXPECT_STREQ(TEST_DATA_DIR PRIMARY_USER_PREFIX "com.example.package", path)
+            << "Package path should be in /data/data/";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_SecondaryUser) {
+    char path[PKG_PATH_MAX];
+
+    EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 1))
+            << "Should successfully create package path.";
+
+    EXPECT_STREQ(TEST_DATA_DIR SECONDARY_USER_PREFIX "1/com.example.package", path)
+            << "Package path should be in /data/user/";
+}
+
+TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) {
+    char path[PKG_PATH_MAX];
+
+    dir_rec_t dir;
+    dir.path = "/data/app-private/";
+    dir.len = strlen(dir.path);
+
+    EXPECT_EQ(0, create_pkg_path_in_dir(path, &dir, "com.example.package", ".apk"))
+            << "Should successfully create package path.";
+
+    EXPECT_STREQ("/data/app-private/com.example.package.apk", path)
+            << "Package path should be in /data/app-private/";
+}
+
+TEST_F(UtilsTest, CopyAndAppend_Normal) {
+    //int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix)
+    dir_rec_t dst;
+    dir_rec_t src;
+
+    src.path = "/data/";
+    src.len = strlen(src.path);
+
+    EXPECT_EQ(0, copy_and_append(&dst, &src, "app/"))
+            << "Should return error because postfix is too long.";
+
+    EXPECT_STREQ("/data/app/", dst.path)
+            << "Appended path should be correct";
+
+    EXPECT_EQ(10, (ssize_t) dst.len)
+            << "Appended path should be length of '/data/app/' (10)";
+}
+
+TEST_F(UtilsTest, AppendAndIncrement_Normal) {
+    size_t dst_size = 10;
+    char dst[dst_size];
+    char *dstp = dst;
+    const char* src = "FOO";
+
+    EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+            << "String should append successfully";
+
+    EXPECT_STREQ("FOO", dst)
+            << "String should append correctly";
+
+    EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+            << "String should append successfully again";
+
+    EXPECT_STREQ("FOOFOO", dst)
+            << "String should append correctly again";
+}
+
+TEST_F(UtilsTest, AppendAndIncrement_TooBig) {
+    size_t dst_size = 5;
+    char dst[dst_size];
+    char *dstp = dst;
+    const char* src = "FOO";
+
+    EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+            << "String should append successfully";
+
+    EXPECT_STREQ("FOO", dst)
+            << "String should append correctly";
+
+    EXPECT_EQ(-1, append_and_increment(&dstp, src, &dst_size))
+            << "String should fail because it's too large to fit";
+}
+
+}
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index a5e4b5a..f37a6fb 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -16,24 +16,93 @@
 
 #include "installd.h"
 
-int create_pkg_path(char path[PKG_PATH_MAX],
-                    const char *prefix,
-                    const char *pkgname,
-                    const char *postfix)
+int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
+                                const dir_rec_t* dir,
+                                const char* pkgname,
+                                const char* postfix)
 {
-    int len;
-    const char *x;
+     const size_t postfix_len = strlen(postfix);
 
-    len = strlen(pkgname);
-    if (len > PKG_NAME_MAX) {
-        return -1;
+     const size_t pkgname_len = strlen(pkgname);
+     if (pkgname_len > PKG_NAME_MAX) {
+         return -1;
+     }
+
+     if (is_valid_package_name(pkgname) < 0) {
+         return -1;
+     }
+
+     if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) {
+         return -1;
+     }
+
+     char *dst = path;
+     size_t dst_size = PKG_PATH_MAX;
+
+     if (append_and_increment(&dst, dir->path, &dst_size) < 0
+             || append_and_increment(&dst, pkgname, &dst_size) < 0
+             || append_and_increment(&dst, postfix, &dst_size) < 0) {
+         LOGE("Error building APK path");
+         return -1;
+     }
+
+     return 0;
+}
+
+/**
+ * Create the package path name for a given package name with a postfix for
+ * a certain persona. Returns 0 on success, and -1 on failure.
+ */
+int create_pkg_path(char path[PKG_PATH_MAX],
+                    const char *pkgname,
+                    const char *postfix,
+                    uid_t persona)
+{
+    size_t uid_len;
+    char* persona_prefix;
+    if (persona == 0) {
+        persona_prefix = PRIMARY_USER_PREFIX;
+        uid_len = 0;
+    } else {
+        persona_prefix = SECONDARY_USER_PREFIX;
+        uid_len = snprintf(NULL, 0, "%d", persona);
     }
-    if ((len + strlen(prefix) + strlen(postfix)) >= PKG_PATH_MAX) {
+
+    const size_t prefix_len = android_data_dir.len + strlen(persona_prefix) + uid_len + 1 /*slash*/;
+    char prefix[prefix_len + 1];
+
+    char *dst = prefix;
+    size_t dst_size = sizeof(prefix);
+
+    if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
+            || append_and_increment(&dst, persona_prefix, &dst_size) < 0) {
+        LOGE("Error building prefix for APK path");
         return -1;
     }
 
-    x = pkgname;
+    if (persona != 0) {
+        int ret = snprintf(dst, dst_size, "%d/", persona);
+        if (ret < 0 || (size_t) ret != uid_len + 1) {
+            LOGW("Error appending UID to APK path");
+            return -1;
+        }
+    }
+
+    dir_rec_t dir;
+    dir.path = prefix;
+    dir.len = prefix_len;
+
+    return create_pkg_path_in_dir(path, &dir, pkgname, postfix);
+}
+
+/**
+ * Checks whether the package name is valid. Returns -1 on error and
+ * 0 on success.
+ */
+int is_valid_package_name(const char* pkgname) {
+    const char *x = pkgname;
     int alpha = -1;
+
     while (*x) {
         if (isalnum(*x) || (*x == '_')) {
                 /* alphanumeric or underscore are fine */
@@ -47,13 +116,15 @@
             /* Suffix -X is fine to let versioning of packages.
                But whatever follows should be alphanumeric.*/
             alpha = 1;
-        }else {
+        } else {
                 /* anything not A-Z, a-z, 0-9, _, or . is invalid */
             LOGE("invalid package name '%s'\n", pkgname);
             return -1;
         }
+
         x++;
     }
+
     if (alpha == 1) {
         // Skip current character
         x++;
@@ -66,7 +137,6 @@
         }
     }
 
-    sprintf(path, "%s%s%s", prefix, pkgname, postfix);
     return 0;
 }
 
@@ -171,3 +241,170 @@
     closedir(d);
     return res;
 }
+
+/**
+ * Checks whether a path points to a system app (.apk file). Returns 0
+ * if it is a system app or -1 if it is not.
+ */
+int validate_system_app_path(const char* path) {
+    size_t i;
+
+    for (i = 0; i < android_system_dirs.count; i++) {
+        const size_t dir_len = android_system_dirs.dirs[i].len;
+        if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) {
+            if (path[dir_len] == '.' || strchr(path + dir_len, '/') != NULL) {
+                LOGE("invalid system apk path '%s' (trickery)\n", path);
+                return -1;
+            }
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+/**
+ * Get the contents of a environment variable that contains a path. Caller
+ * owns the string that is inserted into the directory record. Returns
+ * 0 on success and -1 on error.
+ */
+int get_path_from_env(dir_rec_t* rec, const char* var) {
+    const char* path = getenv(var);
+    int ret = get_path_from_string(rec, path);
+    if (ret < 0) {
+        LOGW("Problem finding value for environment variable %s\n", var);
+    }
+    return ret;
+}
+
+/**
+ * Puts the string into the record as a directory. Appends '/' to the end
+ * of all paths. Caller owns the string that is inserted into the directory
+ * record. A null value will result in an error.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int get_path_from_string(dir_rec_t* rec, const char* path) {
+    if (path == NULL) {
+        return -1;
+    } else {
+        const size_t path_len = strlen(path);
+        if (path_len <= 0) {
+            return -1;
+        }
+
+        // Make sure path is absolute.
+        if (path[0] != '/') {
+            return -1;
+        }
+
+        if (path[path_len - 1] == '/') {
+            // Path ends with a forward slash. Make our own copy.
+
+            rec->path = strdup(path);
+            if (rec->path == NULL) {
+                return -1;
+            }
+
+            rec->len = path_len;
+        } else {
+            // Path does not end with a slash. Generate a new string.
+            char *dst;
+
+            // Add space for slash and terminating null.
+            size_t dst_size = path_len + 2;
+
+            rec->path = malloc(dst_size);
+            if (rec->path == NULL) {
+                return -1;
+            }
+
+            dst = rec->path;
+
+            if (append_and_increment(&dst, path, &dst_size) < 0
+                    || append_and_increment(&dst, "/", &dst_size)) {
+                LOGE("Error canonicalizing path");
+                return -1;
+            }
+
+            rec->len = dst - rec->path;
+        }
+    }
+    return 0;
+}
+
+int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) {
+    dst->len = src->len + strlen(suffix);
+    const size_t dstSize = dst->len + 1;
+    dst->path = (char*) malloc(dstSize);
+
+    if (dst->path == NULL
+            || snprintf(dst->path, dstSize, "%s%s", src->path, suffix)
+                    != (ssize_t) dst->len) {
+        LOGE("Could not allocate memory to hold appended path; aborting\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Check whether path points to a valid path for an APK file. An ASEC
+ * directory is allowed to have one level of subdirectory names. Returns -1
+ * when an invalid path is encountered and 0 when a valid path is encountered.
+ */
+int validate_apk_path(const char *path)
+{
+    int allowsubdir = 0;
+    char *subdir = NULL;
+    size_t dir_len;
+    size_t path_len;
+
+    if (!strncmp(path, android_app_dir.path, android_app_dir.len)) {
+        dir_len = android_app_dir.len;
+    } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) {
+        dir_len = android_app_private_dir.len;
+    } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) {
+        dir_len = android_asec_dir.len;
+        allowsubdir = 1;
+    } else {
+        LOGE("invalid apk path '%s' (bad prefix)\n", path);
+        return -1;
+    }
+
+    path_len = strlen(path);
+
+    /*
+     * Only allow the path to have a subdirectory if it's been marked as being allowed.
+     */
+    if ((subdir = strchr(path + dir_len, '/')) != NULL) {
+        ++subdir;
+        if (!allowsubdir
+                || (path_len > (size_t) (subdir - path) && (strchr(subdir, '/') != NULL))) {
+            LOGE("invalid apk path '%s' (subdir?)\n", path);
+            return -1;
+        }
+    }
+
+    /*
+     *  Directories can't have a period directly after the directory markers
+     *  to prevent ".."
+     */
+    if (path[dir_len] == '.'
+            || (subdir != NULL && ((*subdir == '.') || (strchr(subdir, '/') != NULL)))) {
+        LOGE("invalid apk path '%s' (trickery)\n", path);
+        return -1;
+    }
+
+    return 0;
+}
+
+int append_and_increment(char** dst, const char* src, size_t* dst_size) {
+    ssize_t ret = strlcpy(*dst, src, *dst_size);
+    if (ret < 0 || (size_t) ret >= *dst_size) {
+        return -1;
+    }
+    *dst += ret;
+    *dst_size -= ret;
+    return 0;
+}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index bb8d874..02fd6e4 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -346,6 +346,13 @@
          */
         public interface FileColumns extends MediaColumns {
             /**
+             * The MTP storage ID of the file
+             * <P>Type: INTEGER</P>
+             * @hide
+             */
+            public static final String STORAGE_ID = "storage_id";
+
+            /**
              * The MTP format code of the file
              * <P>Type: INTEGER</P>
              * @hide
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 3d25651..fd709f2 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -100,6 +100,7 @@
      *   <li>{@link #EXTRA_MAX_RESULTS}
      *   <li>{@link #EXTRA_PARTIAL_RESULTS}
      *   <li>{@link #EXTRA_WEB_SEARCH_ONLY}
+     *   <li>{@link #EXTRA_ORIGIN}
      * </ul>
      * 
      * <p> Result extras (returned in the result, not to be specified in the request):
@@ -182,6 +183,13 @@
      * {@link java.util.Locale#getDefault()}.
      */
     public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
+    
+    /**
+     * Optional value which can be used to indicate the referer url of a page in which
+     * speech was requested. For example, a web browser may choose to provide this for
+     * uses of speech on a given page.
+     */
+    public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
 
     /** 
      * Optional limit on the maximum number of results to return. If omitted the recognizer
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 56bc1d3..5d65b72 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -114,6 +114,23 @@
          removable. -->
     <bool name="config_externalStorageRemovable" product="default">true</bool>
 
+    <!-- List of mount points for external storage devices.
+         The first item on the list should be the primary external storage and should match the
+         value returned by Environment.getExternalStorageDirectory (/mnt/sdcard).
+         MTP storage IDs will be generated based on the position of the mountpoint in this list:
+            0x00010001 - ID for primary external storage (/mnt/sdcard)
+            0x00020001 - ID for first secondary external storage
+            0x00030001 - ID for second secondary external storage
+         etc. -->
+    <string-array translatable="false" name="config_externalStoragePaths">
+        <item>"/mnt/sdcard"</item>
+    </string-array>
+
+    <!-- User visible descriptions of the volumes in the config_externalStoragePaths array. -->
+    <string-array translatable="true" name="config_externalStorageDescriptions">
+        <item>"SD card"</item>
+    </string-array>
+
     <!-- Number of megabytes of space to leave unallocated by MTP.
          MTP will subtract this value from the free space it reports back
          to the host via GetStorageInfo, and will not allow new files to
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 4610135..1827c3e 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -83,13 +83,12 @@
 
                 uint8_t     frameSize;
                 uint8_t     channelCount;
-                uint16_t    flags;
-
                 uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
-                uint16_t    waitTimeMs;      // Cumulated wait time
 
+                uint16_t    waitTimeMs;      // Cumulated wait time
                 uint16_t    sendLevel;
-                uint16_t    reserved;
+    volatile    int32_t     flags;
+
                 // Cache line boundary (32 bytes)
                             audio_track_cblk_t();
                 uint32_t    stepUser(uint32_t frameCount);
@@ -98,6 +97,7 @@
                 uint32_t    framesAvailable();
                 uint32_t    framesAvailable_l();
                 uint32_t    framesReady();
+                bool        tryLock();
 };
 
 
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index b4a4689..b900671 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -50,7 +50,8 @@
     private final IContentProvider mMediaProvider;
     private final String mVolumeName;
     private final Uri mObjectsUri;
-    private final String mMediaStoragePath;
+    private final String mMediaStoragePath; // path to primary storage
+    private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
 
     // cached property groups for single properties
     private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
@@ -67,9 +68,6 @@
     private SharedPreferences mDeviceProperties;
     private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
 
-    // FIXME - this should be passed in via the constructor
-    private final int mStorageID = 0x00010001;
-
     private static final String[] ID_PROJECTION = new String[] {
             Files.FileColumns._ID, // 0
     };
@@ -85,17 +83,22 @@
     };
     private static final String[] OBJECT_INFO_PROJECTION = new String[] {
             Files.FileColumns._ID, // 0
-            Files.FileColumns.DATA, // 1
+            Files.FileColumns.STORAGE_ID, // 1
             Files.FileColumns.FORMAT, // 2
             Files.FileColumns.PARENT, // 3
-            Files.FileColumns.SIZE, // 4
-            Files.FileColumns.DATE_MODIFIED, // 5
+            Files.FileColumns.DATA, // 4
+            Files.FileColumns.SIZE, // 5
+            Files.FileColumns.DATE_MODIFIED, // 6
     };
     private static final String ID_WHERE = Files.FileColumns._ID + "=?";
     private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
     private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                             + Files.FileColumns.FORMAT + "=?";
+    private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
+                                            + Files.FileColumns.STORAGE_ID + "=?";
+    private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
+                                            + Files.FileColumns.FORMAT + "=?";
 
     private final MediaScanner mMediaScanner;
 
@@ -124,6 +127,14 @@
         }
     }
 
+    public void addStorage(MtpStorage storage) {
+        mStorageMap.put(storage.getPath(), storage);
+    }
+
+    public void removeStorage(MtpStorage storage) {
+        mStorageMap.remove(storage.getPath());
+    }
+
     private void initDeviceProperties(Context context) {
         final String devicePropertiesName = "device-properties";
         mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
@@ -160,7 +171,7 @@
     }
 
     private int beginSendObject(String path, int format, int parent,
-                         int storage, long size, long modified) {
+                         int storageId, long size, long modified) {
         // first make sure the object does not exist
         if (path != null) {
             Cursor c = null;
@@ -185,7 +196,7 @@
         values.put(Files.FileColumns.DATA, path);
         values.put(Files.FileColumns.FORMAT, format);
         values.put(Files.FileColumns.PARENT, parent);
-        // storage is ignored for now
+        values.put(Files.FileColumns.STORAGE_ID, storageId);
         values.put(Files.FileColumns.SIZE, size);
         values.put(Files.FileColumns.DATE_MODIFIED, modified);
 
@@ -237,19 +248,35 @@
         }
     }
 
-    private int[] getObjectList(int storageID, int format, int parent) {
-        // we can ignore storageID until we support multiple storages
-        Cursor c = null;
-        try {
+    private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
+        if (storageID != 0) {
             if (format != 0) {
-                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                        PARENT_STORAGE_FORMAT_WHERE,
+                        new String[] { Integer.toString(parent), Integer.toString(storageID),
+                                Integer.toString(format) }, null);
+            } else {
+                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                        PARENT_STORAGE_WHERE, new String[]
+                                { Integer.toString(parent), Integer.toString(storageID) }, null);
+            }
+        } else {
+            if (format != 0) {
+                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                             PARENT_FORMAT_WHERE,
                             new String[] { Integer.toString(parent), Integer.toString(format) },
                              null);
             } else {
-                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                             PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
             }
+        }
+    }
+
+    private int[] getObjectList(int storageID, int format, int parent) {
+        Cursor c = null;
+        try {
+            c = createObjectQuery(storageID, format, parent);
             if (c == null) {
                 return null;
             }
@@ -273,18 +300,9 @@
     }
 
     private int getNumObjects(int storageID, int format, int parent) {
-        // we can ignore storageID until we support multiple storages
         Cursor c = null;
         try {
-            if (format != 0) {
-                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
-                            PARENT_FORMAT_WHERE,
-                            new String[] { Integer.toString(parent), Integer.toString(format) },
-                             null);
-            } else {
-                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
-                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
-            }
+            c = createObjectQuery(storageID, format, parent);
             if (c != null) {
                 return c.getCount();
             }
@@ -508,7 +526,7 @@
             }
         }
 
-        return propertyGroup.getPropertyList((int)handle, format, depth, mStorageID);
+        return propertyGroup.getPropertyList((int)handle, format, depth);
     }
 
     private int renameFile(int handle, String newName) {
@@ -631,12 +649,12 @@
             c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null);
             if (c != null && c.moveToNext()) {
-                outStorageFormatParent[0] = mStorageID;
+                outStorageFormatParent[0] = c.getInt(1);
                 outStorageFormatParent[1] = c.getInt(2);
                 outStorageFormatParent[2] = c.getInt(3);
 
                 // extract name from path
-                String path = c.getString(1);
+                String path = c.getString(4);
                 int lastSlash = path.lastIndexOf('/');
                 int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
                 int end = path.length();
@@ -646,8 +664,8 @@
                 path.getChars(start, end, outName, 0);
                 outName[end - start] = 0;
 
-                outSizeModified[0] = c.getLong(4);
-                outSizeModified[1] = c.getLong(5);
+                outSizeModified[0] = c.getLong(5);
+                outSizeModified[1] = c.getLong(6);
                 return true;
             }
         } catch (RemoteException e) {
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index fceedd2..b75b11a 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -93,7 +93,7 @@
 
          switch (code) {
             case MtpConstants.PROPERTY_STORAGE_ID:
-                // no query needed until we support multiple storage units
+                column = Files.FileColumns.STORAGE_ID;
                 type = MtpConstants.TYPE_UINT32;
                 break;
              case MtpConstants.PROPERTY_OBJECT_FORMAT:
@@ -134,6 +134,7 @@
                 break;
             case MtpConstants.PROPERTY_PERSISTENT_UID:
                 // PUID is concatenation of storageID and object handle
+                column = Files.FileColumns.STORAGE_ID;
                 type = MtpConstants.TYPE_UINT128;
                 break;
             case MtpConstants.PROPERTY_DURATION:
@@ -280,7 +281,7 @@
         return path.substring(start, end);
     }
 
-    MtpPropertyList getPropertyList(int handle, int format, int depth, int storageID) {
+    MtpPropertyList getPropertyList(int handle, int format, int depth) {
         //Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth);
         if (depth > 1) {
             // we only support depth 0 and 1
@@ -348,10 +349,6 @@
 
                     // handle some special cases
                     switch (propertyCode) {
-                        case MtpConstants.PROPERTY_STORAGE_ID:
-                            result.append(handle, propertyCode, MtpConstants.TYPE_UINT32,
-                                    storageID);
-                            break;
                         case MtpConstants.PROPERTY_PROTECTION_STATUS:
                             // protection status is always 0
                             result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
@@ -398,7 +395,7 @@
                             break;
                         case MtpConstants.PROPERTY_PERSISTENT_UID:
                             // PUID is concatenation of storageID and object handle
-                            long puid = storageID;
+                            long puid = c.getLong(column);
                             puid <<= 32;
                             puid += handle;
                             result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid);
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 006fa6d..c065ca8 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -33,8 +33,8 @@
         System.loadLibrary("media_jni");
     }
 
-    public MtpServer(MtpDatabase database, String storagePath, long reserveSpace) {
-        native_setup(database, storagePath, reserveSpace);
+    public MtpServer(MtpDatabase database) {
+        native_setup(database);
     }
 
     public void start() {
@@ -65,18 +65,20 @@
         native_set_ptp_mode(usePtp);
     }
 
-    // Used to disable MTP by removing all storage units.
-    // This is done to disable access to file transfer when the device is locked.
-    public void setLocked(boolean locked) {
-        native_set_locked(locked);
+    public void addStorage(MtpStorage storage) {
+        native_add_storage(storage);
     }
 
-    private native final void native_setup(MtpDatabase database, String storagePath,
-            long reserveSpace);
+    public void removeStorage(MtpStorage storage) {
+        native_remove_storage(storage.getStorageId());
+    }
+
+    private native final void native_setup(MtpDatabase database);
     private native final void native_start();
     private native final void native_stop();
     private native final void native_send_object_added(int handle);
     private native final void native_send_object_removed(int handle);
     private native final void native_set_ptp_mode(boolean usePtp);
-    private native final void native_set_locked(boolean locked);
+    private native final void native_add_storage(MtpStorage storage);
+    private native final void native_remove_storage(int storageId);
 }
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
new file mode 100644
index 0000000..33146e7
--- /dev/null
+++ b/media/java/android/mtp/MtpStorage.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 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.mtp;
+
+/**
+ * This class represents a storage unit on an MTP device.
+ * Used only for MTP support in USB responder mode.
+ * MtpStorageInfo is used in MTP host mode
+ *
+ * @hide
+ */
+public class MtpStorage {
+
+    private final int mStorageId;
+    private final String mPath;
+    private final String mDescription;
+    private final long mReserveSpace;
+
+    public MtpStorage(int id, String path, String description, long reserveSpace) {
+        mStorageId = id;
+        mPath = path;
+        mDescription = description;
+        mReserveSpace = reserveSpace;
+    }
+
+    /**
+     * Returns the storage ID for the storage unit
+     *
+     * @return the storage ID
+     */
+    public final int getStorageId() {
+        return mStorageId;
+    }
+
+    /**
+     * Generates a storage ID for storage of given index.
+     * Index 0 is for primary external storage
+     *
+     * @return the storage ID
+     */
+    public static int getStorageId(int index) {
+        // storage ID is 0x00010001 for primary storage,
+        // then 0x00020001, 0x00030001, etc. for secondary storages
+        return ((index + 1) << 16) + 1;
+    }
+
+   /**
+     * Returns the file path for the storage unit's storage in the file system
+     *
+     * @return the storage file path
+     */
+    public final String getPath() {
+        return mPath;
+    }
+
+   /**
+     * Returns the description string for the storage unit
+     *
+     * @return the storage unit description
+     */
+    public final String getDescription() {
+        return mDescription;
+    }
+
+   /**
+     * Returns the amount of space to reserve on the storage file system.
+     * This can be set to a non-zero value to prevent MTP from filling up the entire storage.
+     *
+     * @return the storage unit description
+     */
+    public final long getReserveSpace() {
+        return mReserveSpace;
+    }
+
+}
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index e025ef1..c55189f 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -39,6 +39,17 @@
 
 using namespace android;
 
+// MtpStorage class
+jclass clazz_MtpStorage;
+
+// MtpStorage fields
+static jfieldID field_MtpStorage_storageId;
+static jfieldID field_MtpStorage_path;
+static jfieldID field_MtpStorage_description;
+static jfieldID field_MtpStorage_reserveSpace;
+
+static Mutex sMutex;
+
 // ----------------------------------------------------------------------------
 
 // in android_mtp_MtpDatabase.cpp
@@ -57,70 +68,77 @@
 private:
     MtpDatabase*    mDatabase;
     MtpServer*      mServer;
-    MtpStorage*     mStorage;
-    Mutex           mMutex;
+    MtpStorageList  mStorageList;
     bool            mUsePtp;
-    bool            mLocked;
     int             mFd;
 
 public:
-    MtpThread(MtpDatabase* database, MtpStorage* storage)
+    MtpThread(MtpDatabase* database)
         :   mDatabase(database),
             mServer(NULL),
-            mStorage(storage),
             mUsePtp(false),
-            mLocked(false),
             mFd(-1)
     {
     }
 
     virtual ~MtpThread() {
-        delete mStorage;
     }
 
     void setPtpMode(bool usePtp) {
-        mMutex.lock();
         mUsePtp = usePtp;
-        mMutex.unlock();
     }
 
-    void setLocked(bool locked) {
-        mMutex.lock();
-        if (locked != mLocked) {
-            if (mServer) {
-                if (locked)
-                    mServer->removeStorage(mStorage);
-                else
-                    mServer->addStorage(mStorage);
+    void addStorage(MtpStorage *storage) {
+        mStorageList.push(storage);
+        if (mServer)
+            mServer->addStorage(storage);
+    }
+
+    void removeStorage(MtpStorageID id) {
+        MtpStorage* storage = mServer->getStorage(id);
+        if (storage) {
+            for (int i = 0; i < mStorageList.size(); i++) {
+                if (mStorageList[i] == storage) {
+                    mStorageList.removeAt(i);
+                    break;
+                }
             }
-            mLocked = locked;
+            if (mServer)
+                mServer->removeStorage(storage);
+            delete storage;
         }
-        mMutex.unlock();
+    }
+
+    void start() {
+        run("MtpThread");
     }
 
     virtual bool threadLoop() {
-        mMutex.lock();
+        sMutex.lock();
+
         mFd = open("/dev/mtp_usb", O_RDWR);
         if (mFd >= 0) {
             ioctl(mFd, MTP_SET_INTERFACE_MODE,
                     (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
 
             mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
-            if (!mLocked)
-                mServer->addStorage(mStorage);
-
-            mMutex.unlock();
-            mServer->run();
-            mMutex.lock();
-
-            close(mFd);
-            mFd = -1;
-            delete mServer;
-            mServer = NULL;
+            for (int i = 0; i < mStorageList.size(); i++) {
+                mServer->addStorage(mStorageList[i]);
+            }
         } else {
             LOGE("could not open MTP driver, errno: %d", errno);
         }
-        mMutex.unlock();
+
+        sMutex.unlock();
+        mServer->run();
+        sMutex.lock();
+
+        close(mFd);
+        mFd = -1;
+        delete mServer;
+        mServer = NULL;
+
+        sMutex.unlock();
         // delay a bit before retrying to avoid excessive spin
         if (!exitPending()) {
             sleep(1);
@@ -130,17 +148,13 @@
     }
 
     void sendObjectAdded(MtpObjectHandle handle) {
-        mMutex.lock();
         if (mServer)
             mServer->sendObjectAdded(handle);
-        mMutex.unlock();
     }
 
     void sendObjectRemoved(MtpObjectHandle handle) {
-        mMutex.lock();
         if (mServer)
             mServer->sendObjectRemoved(handle);
-        mMutex.unlock();
     }
 };
 
@@ -150,18 +164,11 @@
 #endif // HAVE_ANDROID_OS
 
 static void
-android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase,
-        jstring storagePath, jlong reserveSpace)
+android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase)
 {
 #ifdef HAVE_ANDROID_OS
-    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
-    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
-
     // create the thread and assign it to the smart pointer
-    MtpStorage* storage = new MtpStorage(MTP_FIRST_STORAGE_ID, storagePathStr, reserveSpace);
-    sThread = new MtpThread(database, storage);
-
-    env->ReleaseStringUTFChars(storagePath, storagePathStr);
+    sThread = new MtpThread(getMtpDatabase(env, javaDatabase));
 #endif
 }
 
@@ -169,9 +176,11 @@
 android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
+   sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread)
-        thread->run("MtpThread");
+        thread->start();
+    sMutex.unlock();
 #endif // HAVE_ANDROID_OS
 }
 
@@ -179,11 +188,13 @@
 android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
 {
 #ifdef HAVE_ANDROID_OS
+    sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread) {
         thread->requestExitAndWait();
         sThread = NULL;
     }
+    sMutex.unlock();
 #endif
 }
 
@@ -191,9 +202,11 @@
 android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
 {
 #ifdef HAVE_ANDROID_OS
+    sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread)
         thread->sendObjectAdded(handle);
+    sMutex.unlock();
 #endif
 }
 
@@ -201,9 +214,11 @@
 android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
 {
 #ifdef HAVE_ANDROID_OS
+    sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread)
         thread->sendObjectRemoved(handle);
+    sMutex.unlock();
 #endif
 }
 
@@ -211,33 +226,68 @@
 android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
 {
 #ifdef HAVE_ANDROID_OS
+    sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread)
         thread->setPtpMode(usePtp);
+    sMutex.unlock();
 #endif
 }
 
 static void
-android_mtp_MtpServer_set_locked(JNIEnv *env, jobject thiz, jboolean locked)
+android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
 {
 #ifdef HAVE_ANDROID_OS
+    sMutex.lock();
+    MtpThread *thread = sThread.get();
+    if (thread) {
+        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
+        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
+        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
+        jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
+
+        const char *pathStr = env->GetStringUTFChars(path, NULL);
+        const char *descriptionStr = env->GetStringUTFChars(description, NULL);
+
+        MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace);
+        thread->addStorage(storage);
+
+        env->ReleaseStringUTFChars(path, pathStr);
+        env->ReleaseStringUTFChars(description, descriptionStr);
+    } else {
+        LOGE("MtpThread is null in add_storage");
+    }
+    sMutex.unlock();
+#endif
+}
+
+static void
+android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
+{
+#ifdef HAVE_ANDROID_OS
+    sMutex.lock();
     MtpThread *thread = sThread.get();
     if (thread)
-        thread->setLocked(locked);
+        thread->removeStorage(storageId);
+    else
+        LOGE("MtpThread is null in remove_storage");
+    sMutex.unlock();
 #endif
 }
 
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"native_setup",                "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V",
+    {"native_setup",                "(Landroid/mtp/MtpDatabase;)V",
                                             (void *)android_mtp_MtpServer_setup},
     {"native_start",                "()V",  (void *)android_mtp_MtpServer_start},
     {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
     {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
     {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
     {"native_set_ptp_mode",         "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
-    {"native_set_locked",           "(Z)V", (void *)android_mtp_MtpServer_set_locked},
+    {"native_add_storage",          "(Landroid/mtp/MtpStorage;)V",
+                                            (void *)android_mtp_MtpServer_add_storage},
+    {"native_remove_storage",       "(I)V", (void *)android_mtp_MtpServer_remove_storage},
 };
 
 static const char* const kClassPathName = "android/mtp/MtpServer";
@@ -246,6 +296,33 @@
 {
     jclass clazz;
 
+    clazz = env->FindClass("android/mtp/MtpStorage");
+    if (clazz == NULL) {
+        LOGE("Can't find android/mtp/MtpStorage");
+        return -1;
+    }
+    field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
+    if (field_MtpStorage_storageId == NULL) {
+        LOGE("Can't find MtpStorage.mStorageId");
+        return -1;
+    }
+    field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
+    if (field_MtpStorage_path == NULL) {
+        LOGE("Can't find MtpStorage.mPath");
+        return -1;
+    }
+    field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
+    if (field_MtpStorage_description == NULL) {
+        LOGE("Can't find MtpStorage.mDescription");
+        return -1;
+    }
+    field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
+    if (field_MtpStorage_reserveSpace == NULL) {
+        LOGE("Can't find MtpStorage.mStorageId");
+        return -1;
+    }
+    clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz);
+
     clazz = env->FindClass("android/mtp/MtpServer");
     if (clazz == NULL) {
         LOGE("Can't find android/mtp/MtpServer");
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index cee1c75..5d74a0a 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -35,6 +35,7 @@
 #include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
 #include <utils/Timers.h>
+#include <utils/Atomic.h>
 
 #define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))
 #define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
@@ -299,7 +300,7 @@
             ret = mAudioRecord->start();
             cblk->lock.lock();
             if (ret == DEAD_OBJECT) {
-                cblk->flags |= CBLK_INVALID_MSK;
+                android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
             }
         }
         if (cblk->flags & CBLK_INVALID_MSK) {
@@ -467,7 +468,7 @@
     mCblkMemory = cblk;
     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
     mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
-    mCblk->flags &= ~CBLK_DIRECTION_MSK;
+    android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags);
     mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
     mCblk->waitTimeMs = 0;
     return NO_ERROR;
@@ -522,7 +523,7 @@
                     result = mAudioRecord->start();
                     cblk->lock.lock();
                     if (result == DEAD_OBJECT) {
-                        cblk->flags |= CBLK_INVALID_MSK;
+                        android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
 create_new_record:
                         result = AudioRecord::restoreRecord_l(cblk);
                     }
@@ -722,12 +723,8 @@
     // Manage overrun callback
     if (mActive && (cblk->framesAvailable() == 0)) {
         LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
-        AutoMutex _l(cblk->lock);
-        if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
-            cblk->flags |= CBLK_UNDERRUN_ON;
-            cblk->lock.unlock();
+        if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) {
             mCbf(EVENT_OVERRUN, mUserData, 0);
-            cblk->lock.lock();
         }
     }
 
@@ -746,10 +743,8 @@
 {
     status_t result;
 
-    if (!(cblk->flags & CBLK_RESTORING_MSK)) {
+    if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) {
         LOGW("dead IAudioRecord, creating a new one");
-
-        cblk->flags |= CBLK_RESTORING_ON;
         // signal old cblk condition so that other threads waiting for available buffers stop
         // waiting now
         cblk->cv.broadcast();
@@ -768,10 +763,8 @@
         }
 
         // signal old cblk condition for other threads waiting for restore completion
-        cblk->lock.lock();
-        cblk->flags |= CBLK_RESTORED_MSK;
+        android_atomic_or(CBLK_RESTORED_ON, &cblk->flags);
         cblk->cv.broadcast();
-        cblk->lock.unlock();
     } else {
         if (!(cblk->flags & CBLK_RESTORED_MSK)) {
             LOGW("dead IAudioRecord, waiting for a new one to be created");
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index fb2ee0f..66e11d2 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -35,6 +35,7 @@
 #include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
 #include <utils/Timers.h>
+#include <utils/Atomic.h>
 
 #define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))
 #define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
@@ -332,7 +333,7 @@
         cblk->lock.lock();
         cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
         cblk->waitTimeMs = 0;
-        cblk->flags &= ~CBLK_DISABLED_ON;
+        android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
         if (t != 0) {
            t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
         } else {
@@ -345,7 +346,7 @@
             status = mAudioTrack->start();
             cblk->lock.lock();
             if (status == DEAD_OBJECT) {
-                cblk->flags |= CBLK_INVALID_MSK;
+                android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
             }
         }
         if (cblk->flags & CBLK_INVALID_MSK) {
@@ -636,7 +637,7 @@
     if (position > mCblk->user) return BAD_VALUE;
 
     mCblk->server = position;
-    mCblk->flags |= CBLK_FORCEREADY_ON;
+    android_atomic_or(CBLK_FORCEREADY_ON, &mCblk->flags);
 
     return NO_ERROR;
 }
@@ -793,7 +794,7 @@
     mCblkMemory.clear();
     mCblkMemory = cblk;
     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
-    mCblk->flags |= CBLK_DIRECTION_OUT;
+    android_atomic_or(CBLK_DIRECTION_OUT, &mCblk->flags);
     if (sharedBuffer == 0) {
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
     } else {
@@ -873,7 +874,7 @@
                         result = mAudioTrack->start();
                         cblk->lock.lock();
                         if (result == DEAD_OBJECT) {
-                            cblk->flags |= CBLK_INVALID_MSK;
+                            android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
 create_new_track:
                             result = restoreTrack_l(cblk, false);
                         }
@@ -900,14 +901,9 @@
 
     // restart track if it was disabled by audioflinger due to previous underrun
     if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
-        AutoMutex _l(cblk->lock);
-        if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
-            cblk->flags &= ~CBLK_DISABLED_ON;
-            cblk->lock.unlock();
-            LOGW("obtainBuffer() track %p disabled, restarting", this);
-            mAudioTrack->start();
-            cblk->lock.lock();
-        }
+        android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
+        LOGW("obtainBuffer() track %p disabled, restarting", this);
+        mAudioTrack->start();
     }
 
     cblk->waitTimeMs = 0;
@@ -1026,17 +1022,13 @@
     mLock.unlock();
 
     // Manage underrun callback
-    if (mActive && (cblk->framesReady() == 0)) {
+    if (mActive && (cblk->framesAvailable() == cblk->frameCount)) {
         LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
-        AutoMutex _l(cblk->lock);
-        if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
-            cblk->flags |= CBLK_UNDERRUN_ON;
-            cblk->lock.unlock();
+        if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) {
             mCbf(EVENT_UNDERRUN, mUserData, 0);
             if (cblk->server == cblk->frameCount) {
                 mCbf(EVENT_BUFFER_END, mUserData, 0);
             }
-            cblk->lock.lock();
             if (mSharedBuffer != 0) return false;
         }
     }
@@ -1150,12 +1142,10 @@
 {
     status_t result;
 
-    if (!(cblk->flags & CBLK_RESTORING_MSK)) {
+    if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) {
         LOGW("dead IAudioTrack, creating a new one from %s",
              fromStart ? "start()" : "obtainBuffer()");
 
-        cblk->flags |= CBLK_RESTORING_ON;
-
         // signal old cblk condition so that other threads waiting for available buffers stop
         // waiting now
         cblk->cv.broadcast();
@@ -1198,10 +1188,8 @@
         }
 
         // signal old cblk condition for other threads waiting for restore completion
-        cblk->lock.lock();
-        cblk->flags |= CBLK_RESTORED_MSK;
+        android_atomic_or(CBLK_RESTORED_ON, &cblk->flags);
         cblk->cv.broadcast();
-        cblk->lock.unlock();
     } else {
         if (!(cblk->flags & CBLK_RESTORED_MSK)) {
             LOGW("dead IAudioTrack, waiting for a new one");
@@ -1275,11 +1263,12 @@
 
 // =========================================================================
 
+
 audio_track_cblk_t::audio_track_cblk_t()
     : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
     userBase(0), serverBase(0), buffers(0), frameCount(0),
     loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
-    flags(0), sendLevel(0)
+    sendLevel(0), flags(0)
 {
 }
 
@@ -1307,10 +1296,7 @@
 
     // Clear flow control error condition as new data has been written/read to/from buffer.
     if (flags & CBLK_UNDERRUN_MSK) {
-        AutoMutex _l(lock);
-        if (flags & CBLK_UNDERRUN_MSK) {
-            flags &= ~CBLK_UNDERRUN_MSK;
-        }
+        android_atomic_and(~CBLK_UNDERRUN_MSK, &flags);
     }
 
     return u;
@@ -1318,18 +1304,8 @@
 
 bool audio_track_cblk_t::stepServer(uint32_t frameCount)
 {
-    // the code below simulates lock-with-timeout
-    // we MUST do this to protect the AudioFlinger server
-    // as this lock is shared with the client.
-    status_t err;
-
-    err = lock.tryLock();
-    if (err == -EBUSY) { // just wait a bit
-        usleep(1000);
-        err = lock.tryLock();
-    }
-    if (err != NO_ERROR) {
-        // probably, the client just died.
+    if (!tryLock()) {
+        LOGW("stepServer() could not lock cblk");
         return false;
     }
 
@@ -1406,18 +1382,42 @@
         if (u < loopEnd) {
             return u - s;
         } else {
-            Mutex::Autolock _l(lock);
-            if (loopCount >= 0) {
-                return (loopEnd - loopStart)*loopCount + u - s;
-            } else {
-                return UINT_MAX;
+            // do not block on mutex shared with client on AudioFlinger side
+            if (!tryLock()) {
+                LOGW("framesReady() could not lock cblk");
+                return 0;
             }
+            uint32_t frames = UINT_MAX;
+            if (loopCount >= 0) {
+                frames = (loopEnd - loopStart)*loopCount + u - s;
+            }
+            lock.unlock();
+            return frames;
         }
     } else {
         return s - u;
     }
 }
 
+bool audio_track_cblk_t::tryLock()
+{
+    // the code below simulates lock-with-timeout
+    // we MUST do this to protect the AudioFlinger server
+    // as this lock is shared with the client.
+    status_t err;
+
+    err = lock.tryLock();
+    if (err == -EBUSY) { // just wait a bit
+        usleep(1000);
+        err = lock.tryLock();
+    }
+    if (err != NO_ERROR) {
+        // probably, the client just died.
+        return false;
+    }
+    return true;
+}
+
 // -------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 1efa715..fa729a8 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -70,6 +70,9 @@
                                     int fileGroup, int filePerm, int directoryPerm);
     virtual             ~MtpServer();
 
+    MtpStorage*         getStorage(MtpStorageID id);
+    inline bool         hasStorage() { return mStorages.size() > 0; }
+    bool                hasStorage(MtpStorageID id);
     void                addStorage(MtpStorage* storage);
     void                removeStorage(MtpStorage* storage);
 
@@ -79,9 +82,6 @@
     void                sendObjectRemoved(MtpObjectHandle handle);
 
 private:
-    MtpStorage*         getStorage(MtpStorageID id);
-    inline bool         hasStorage() { return mStorages.size() > 0; }
-    bool                hasStorage(MtpStorageID id);
     void                sendStoreAdded(MtpStorageID id);
     void                sendStoreRemoved(MtpStorageID id);
     void                sendEvent(MtpEventCode code, uint32_t param1);
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
index 6cb88b3..fff0b5f 100644
--- a/media/mtp/MtpStorage.cpp
+++ b/media/mtp/MtpStorage.cpp
@@ -32,9 +32,11 @@
 
 namespace android {
 
-MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, uint64_t reserveSpace)
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
+        const char* description, uint64_t reserveSpace)
     :   mStorageID(id),
         mFilePath(filePath),
+        mDescription(description),
         mMaxCapacity(0),
         mReserveSpace(reserveSpace)
 {
@@ -75,7 +77,7 @@
 }
 
 const char* MtpStorage::getDescription() const {
-    return "Device Storage";
+    return (const char *)mDescription;
 }
 
 }  // namespace android
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
index 858c9d3..d6ad25f 100644
--- a/media/mtp/MtpStorage.h
+++ b/media/mtp/MtpStorage.h
@@ -29,13 +29,14 @@
 private:
     MtpStorageID            mStorageID;
     MtpString               mFilePath;
+    MtpString               mDescription;
     uint64_t                mMaxCapacity;
     // amount of free space to leave unallocated
     uint64_t                mReserveSpace;
 
 public:
                             MtpStorage(MtpStorageID id, const char* filePath,
-                                    uint64_t reserveSpace);
+                                    const char* description, uint64_t reserveSpace);
     virtual                 ~MtpStorage();
 
     inline MtpStorageID     getStorageID() const { return mStorageID; }
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 6fedc16..8bc2e22 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -22,8 +22,6 @@
 
 #define MTP_STANDARD_VERSION            100
 
-#define MTP_FIRST_STORAGE_ID            0x00010001
-
 // Container Types
 #define MTP_CONTAINER_TYPE_UNDEFINED    0
 #define MTP_CONTAINER_TYPE_COMMAND      1
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2702242..e27a10f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -31,6 +31,7 @@
 #include <binder/IPCThreadState.h>
 #include <utils/String16.h>
 #include <utils/threads.h>
+#include <utils/Atomic.h>
 
 #include <cutils/properties.h>
 
@@ -1738,10 +1739,7 @@
                     LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
                     tracksToRemove->add(track);
                     // indicate to client process that the track was disabled because of underrun
-                    {
-                        AutoMutex _l(cblk->lock);
-                        cblk->flags |= CBLK_DISABLED_ON;
-                    }
+                    android_atomic_or(CBLK_DISABLED_ON, &cblk->flags);
                 } else if (mixerStatus != MIXER_TRACKS_READY) {
                     mixerStatus = MIXER_TRACKS_ENABLED;
                 }
@@ -1790,8 +1788,7 @@
     for (size_t i = 0; i < size; i++) {
         sp<Track> t = mTracks[i];
         if (t->type() == streamType) {
-            AutoMutex _lcblk(t->mCblk->lock);
-            t->mCblk->flags |= CBLK_INVALID_ON;
+            android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags);
             t->mCblk->cv.signal();
         }
     }
@@ -2950,9 +2947,8 @@
 
     if (mCblk->framesReady() >= mCblk->frameCount ||
             (mCblk->flags & CBLK_FORCEREADY_MSK)) {
-        AutoMutex _l(mCblk->lock);
         mFillingUpStatus = FS_FILLED;
-        mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+        android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
         return true;
     }
     return false;
@@ -3066,26 +3062,25 @@
         // STOPPED state
         mState = STOPPED;
 
-        // NOTE: reset() will reset cblk->user and cblk->server with
-        // the risk that at the same time, the AudioMixer is trying to read
-        // data. In this case, getNextBuffer() would return a NULL pointer
-        // as audio buffer => the AudioMixer code MUST always test that pointer
-        // returned by getNextBuffer() is not NULL!
-        reset();
+        // do not reset the track if it is still in the process of being stopped or paused.
+        // this will be done by prepareTracks_l() when the track is stopped.
+        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+        if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+            reset();
+        }
     }
 }
 
 void AudioFlinger::PlaybackThread::Track::reset()
 {
-    AutoMutex _l(mCblk->lock);
     // Do not reset twice to avoid discarding data written just after a flush and before
     // the audioflinger thread detects the track is stopped.
     if (!mResetDone) {
         TrackBase::reset();
         // Force underrun condition to avoid false underrun callback until first data is
         // written to buffer
-        mCblk->flags |= CBLK_UNDERRUN_ON;
-        mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+        android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
+        android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
         mFillingUpStatus = FS_FILLING;
         mResetDone = true;
     }
@@ -3211,13 +3206,10 @@
     if (thread != 0) {
         RecordThread *recordThread = (RecordThread *)thread.get();
         recordThread->stop(this);
-        {
-            AutoMutex _l(mCblk->lock);
-            TrackBase::reset();
-            // Force overerrun condition to avoid false overrun callback until first data is
-            // read from buffer
-            mCblk->flags |= CBLK_UNDERRUN_ON;
-        }
+        TrackBase::reset();
+        // Force overerrun condition to avoid false overrun callback until first data is
+        // read from buffer
+        android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
     }
 }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 7e5fd29..c18ccc8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -448,34 +448,56 @@
                  * to make the media scanner run.
                  */
                 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-                    notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
+                    notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia,
+                            VolumeState.Mounted);
                     return;
                 }
                 new Thread() {
                     @Override
                     public void run() {
                         try {
+                            // it is not safe to call vold with mVolumeStates locked
+                            // so we make a copy of the paths and states and process them
+                            // outside the lock
+                            String[] paths, states;
+                            int count;
                             synchronized (mVolumeStates) {
-                                for (String path : mVolumeStates.keySet()) {
-                                    String state = mVolumeStates.get(path);
-
-                                    if (state.equals(Environment.MEDIA_UNMOUNTED)) {
-                                        int rc = doMountVolume(path);
-                                        if (rc != StorageResultCode.OperationSucceeded) {
-                                            Slog.e(TAG, String.format("Boot-time mount failed (%d)",
-                                                    rc));
-                                        }
-                                    } else if (state.equals(Environment.MEDIA_SHARED)) {
-                                        /*
-                                         * Bootstrap UMS enabled state since vold indicates
-                                         * the volume is shared (runtime restart while ums enabled)
-                                         */
-                                        notifyVolumeStateChange(null, path, VolumeState.NoMedia,
-                                                VolumeState.Shared);
-                                    }
+                                Set<String> keys = mVolumeStates.keySet();
+                                count = keys.size();
+                                paths = (String[])keys.toArray(new String[count]);
+                                states = new String[count];
+                                for (int i = 0; i < count; i++) {
+                                    states[i] = mVolumeStates.get(paths[i]);
                                 }
                             }
 
+                            for (int i = 0; i < count; i++) {
+                                String path = paths[i];
+                                String state = states[i];
+
+                                if (state.equals(Environment.MEDIA_UNMOUNTED)) {
+                                    int rc = doMountVolume(path);
+                                    if (rc != StorageResultCode.OperationSucceeded) {
+                                        Slog.e(TAG, String.format("Boot-time mount failed (%d)",
+                                                rc));
+                                    }
+                                } else if (state.equals(Environment.MEDIA_SHARED)) {
+                                    /*
+                                     * Bootstrap UMS enabled state since vold indicates
+                                     * the volume is shared (runtime restart while ums enabled)
+                                     */
+                                    notifyVolumeStateChange(null, path, VolumeState.NoMedia,
+                                            VolumeState.Shared);
+                                }
+                            }
+
+                            /* notify external storage has mounted to trigger media scanner */
+                            if (mEmulateExternalStorage) {
+                                notifyVolumeStateChange(null,
+                                        Environment.getExternalStorageDirectory().getPath(),
+                                        VolumeState.NoMedia, VolumeState.Mounted);
+                            }
+
                             /*
                              * If UMS was connected on boot, send the connected event
                              * now that we're up.