Merge "Remove fall through for KEYCODE_VOICE_ASSIST"
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 6550d22..48a34e7 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,20 +16,14 @@
 
 #define LOG_TAG "BootAnimation"
 
-#include <cutils/properties.h>
-
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
-
+#include <cutils/properties.h>
+#include <sys/resource.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
 
-#if defined(HAVE_PTHREADS)
-# include <pthread.h>
-# include <sys/resource.h>
-#endif
-
 #include "BootAnimation.h"
 
 using namespace android;
@@ -38,9 +32,7 @@
 
 int main()
 {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
-#endif
 
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.nobootanimation", value, "0");
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 71df60a..6159e1e 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -200,7 +200,7 @@
         vendorInfo = info;
     }
 
-    public void setDomains(String domains) {
-        domains = domains;
+    public void setDomains(String newDomains) {
+        domains = newDomains;
     }
 }
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5a273cf..37ee961 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -76,15 +76,22 @@
 
     /**
      * Returns the network routes specified by this object. Will typically include a
-     * directly-connected route for the IP address's local subnet and a default route.
+     * directly-connected route for the IP address's local subnet and a default route. If the
+     * default gateway is not covered by the directly-connected route, it will also contain a host
+     * route to the gateway as well. This configuration is arguably invalid, but it used to work
+     * in K and earlier, and other OSes appear to accept it.
      */
     public List<RouteInfo> getRoutes(String iface) {
-        List<RouteInfo> routes = new ArrayList<RouteInfo>(2);
+        List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
         if (ipAddress != null) {
-            routes.add(new RouteInfo(ipAddress, null, iface));
+            RouteInfo connectedRoute = new RouteInfo(ipAddress, null, iface);
+            routes.add(connectedRoute);
+            if (gateway != null && !connectedRoute.matches(gateway)) {
+                routes.add(RouteInfo.makeHostRoute(gateway, iface));
+            }
         }
         if (gateway != null) {
-            routes.add(new RouteInfo((LinkAddress) null, gateway, iface));
+            routes.add(new RouteInfo((IpPrefix) null, gateway, iface));
         }
         return routes;
     }
@@ -107,6 +114,7 @@
         for (InetAddress dns : dnsServers) {
             lp.addDnsServer(dns);
         }
+        lp.setDomains(domains);
         return lp;
     }
 
@@ -180,6 +188,7 @@
         for (InetAddress dnsServer : dnsServers) {
             NetworkUtils.parcelInetAddress(dest, dnsServer, flags);
         }
+        dest.writeString(domains);
     }
 
     protected static void readFromParcel(StaticIpConfiguration s, Parcel in) {
@@ -190,5 +199,6 @@
         for (int i = 0; i < size; i++) {
             s.dnsServers.add(NetworkUtils.unparcelInetAddress(in));
         }
+        s.domains = in.readString();
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index aba4bd0..0eb52cb 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -800,7 +800,12 @@
             if (args.niceName != null) {
                 String property = "wrap." + args.niceName;
                 if (property.length() > 31) {
-                    property = property.substring(0, 31);
+                    // Avoid creating an illegal property name when truncating.
+                    if (property.charAt(30) != '.') {
+                        property = property.substring(0, 31);
+                    } else {
+                        property = property.substring(0, 30);
+                    }
                 }
                 args.invokeWith = SystemProperties.get(property);
                 if (args.invokeWith != null && args.invokeWith.length() == 0) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ce7f0d5..8e2159a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -357,22 +357,37 @@
 }
 
 /*
- * Read the persistent locale.
+ * Read the persistent locale. Attempts to read to persist.sys.locale
+ * and falls back to the default locale (ro.product.locale) if
+ * persist.sys.locale is empty.
  */
-static void readLocale(char* language, char* region)
+static void readLocale(char* locale)
 {
-    char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
+    // Allocate 4 extra bytes because we might read a property into
+    // this array at offset 4.
+    char propLocale[PROPERTY_VALUE_MAX + 4];
 
-    property_get("persist.sys.language", propLang, "");
-    property_get("persist.sys.country", propRegn, "");
-    if (*propLang == 0 && *propRegn == 0) {
-        /* Set to ro properties, default is en_US */
-        property_get("ro.product.locale.language", propLang, "en");
-        property_get("ro.product.locale.region", propRegn, "US");
+    property_get("persist.sys.locale", propLocale, "");
+    if (propLocale[0] == 0) {
+        property_get("ro.product.locale", propLocale, "");
+
+        if (propLocale[0] == 0) {
+            // If persist.sys.locale and ro.product.locale are missing,
+            // construct a locale value from the individual locale components.
+            property_get("ro.product.locale.language", propLocale, "en");
+
+            // The language code is either two or three chars in length. If it
+            // isn't 2 chars long, assume three. Anything else is an error
+            // anyway.
+            const int offset = (propLocale[2] == 0) ? 2 : 3;
+            propLocale[offset] = '-';
+
+            property_get("ro.product.locale.region", propLocale + offset + 1, "US");
+        }
     }
-    strncat(language, propLang, 3);
-    strncat(region, propRegn, 3);
-    //ALOGD("language=%s region=%s\n", language, region);
+
+    strncat(locale, propLocale, PROPERTY_VALUE_MAX);
+    // ALOGD("[DEBUG] locale=%s", locale);
 }
 
 void AndroidRuntime::addOption(const char* optionString, void* extraInfo)
@@ -559,8 +574,7 @@
                                     PROPERTY_VALUE_MAX];
     char profileType[sizeof("-Xprofile-type:")-1 + PROPERTY_VALUE_MAX];
     char profileMaxStackDepth[sizeof("-Xprofile-max-stack-depth:")-1 + PROPERTY_VALUE_MAX];
-    char langOption[sizeof("-Duser.language=") + 3];
-    char regionOption[sizeof("-Duser.region=") + 3];
+    char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
     char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
 
@@ -717,11 +731,9 @@
 
     /* Set the properties for locale */
     {
-        strcpy(langOption, "-Duser.language=");
-        strcpy(regionOption, "-Duser.region=");
-        readLocale(langOption, regionOption);
-        addOption(langOption);
-        addOption(regionOption);
+        strcpy(localeOption, "-Duser.locale=");
+        readLocale(localeOption);
+        addOption(localeOption);
     }
 
     /*
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7804dd2..18c83b4 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -75,7 +75,7 @@
     <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
 
     <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
-    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}" />
+    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215" />
 
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 403e164..9dfe75e 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,10 +16,8 @@
 
 #include "RenderThread.h"
 
-#if defined(HAVE_PTHREADS)
-#include <sys/resource.h>
-#endif
 #include <gui/DisplayEventReceiver.h>
+#include <sys/resource.h>
 #include <utils/Log.h>
 
 #include "../RenderState.h"
@@ -245,9 +243,7 @@
 }
 
 bool RenderThread::threadLoop() {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
-#endif
     initThreadLocals();
 
     int timeoutMillis = -1;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index cb5401c..3c30aab 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-#include <sys/sysinfo.h>
-#if defined(HAVE_PTHREADS)
 #include <sys/resource.h>
-#endif
+#include <sys/sysinfo.h>
 
 #include "TaskManager.h"
 #include "Task.h"
@@ -83,9 +81,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 status_t TaskManager::WorkerThread::readyToRun() {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
-#endif
     return NO_ERROR;
 }
 
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 4f74bdd..32d5b82 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -543,11 +543,6 @@
 
         public int load(String path, int priority)
         {
-            // pass network streams to player
-            if (path.startsWith("http:"))
-                return _load(path, priority);
-
-            // try local path
             int id = 0;
             try {
                 File f = new File(path);
@@ -562,6 +557,7 @@
             return id;
         }
 
+        @Override
         public int load(Context context, int resId, int priority) {
             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
             int id = 0;
@@ -576,6 +572,7 @@
             return id;
         }
 
+        @Override
         public int load(AssetFileDescriptor afd, int priority) {
             if (afd != null) {
                 long len = afd.getLength();
@@ -588,16 +585,17 @@
             }
         }
 
+        @Override
         public int load(FileDescriptor fd, long offset, long length, int priority) {
             return _load(fd, offset, length, priority);
         }
 
-        private native final int _load(String uri, int priority);
-
         private native final int _load(FileDescriptor fd, long offset, long length, int priority);
 
+        @Override
         public native final boolean unload(int soundID);
 
+        @Override
         public final int play(int soundID, float leftVolume, float rightVolume,
                 int priority, int loop, float rate) {
             if (isRestricted()) {
@@ -620,16 +618,22 @@
             }
         }
 
+        @Override
         public native final void pause(int streamID);
 
+        @Override
         public native final void resume(int streamID);
 
+        @Override
         public native final void autoPause();
 
+        @Override
         public native final void autoResume();
 
+        @Override
         public native final void stop(int streamID);
 
+        @Override
         public final void setVolume(int streamID, float leftVolume, float rightVolume) {
             if (isRestricted()) {
                 return;
@@ -639,16 +643,21 @@
 
         private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
 
+        @Override
         public void setVolume(int streamID, float volume) {
             setVolume(streamID, volume, volume);
         }
 
+        @Override
         public native final void setPriority(int streamID, int priority);
 
+        @Override
         public native final void setLoop(int streamID, int loop);
 
+        @Override
         public native final void setRate(int streamID, float rate);
 
+        @Override
         public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
         {
             synchronized(mLock) {
@@ -729,52 +738,69 @@
             return 0;
         }
 
+        @Override
         public int load(Context context, int resId, int priority) {
             return 0;
         }
 
+        @Override
         public int load(AssetFileDescriptor afd, int priority) {
             return 0;
         }
 
+        @Override
         public int load(FileDescriptor fd, long offset, long length, int priority) {
             return 0;
         }
 
+        @Override
         public final boolean unload(int soundID) {
             return true;
         }
 
+        @Override
         public final int play(int soundID, float leftVolume, float rightVolume,
                 int priority, int loop, float rate) {
             return 0;
         }
 
+        @Override
         public final void pause(int streamID) { }
 
+        @Override
         public final void resume(int streamID) { }
 
+        @Override
         public final void autoPause() { }
 
+        @Override
         public final void autoResume() { }
 
+        @Override
         public final void stop(int streamID) { }
 
+        @Override
         public final void setVolume(int streamID,
                 float leftVolume, float rightVolume) { }
 
+        @Override
         public void setVolume(int streamID, float volume) {
         }
 
+        @Override
         public final void setPriority(int streamID, int priority) { }
 
+        @Override
         public final void setLoop(int streamID, int loop) { }
 
+        @Override
         public final void setRate(int streamID, float rate) { }
 
+        @Override
         public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
         }
 
+        @Override
         public final void release() { }
     }
 }
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 3382512..71ab013 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    android_media_SoundPool_SoundPoolImpl.cpp
+    android_media_SoundPool_SoundPoolImpl.cpp \
+    SoundPool.cpp \
+    SoundPoolThread.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     liblog \
@@ -10,7 +12,9 @@
     libutils \
     libandroid_runtime \
     libnativehelper \
-    libmedia
+    libmedia \
+    libmediandk \
+    libbinder
 
 LOCAL_MODULE:= libsoundpool
 
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
new file mode 100644
index 0000000..a8b91d2
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool"
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+
+#define USE_SHARED_MEM_BUFFER
+
+#include <media/AudioTrack.h>
+#include <media/IMediaHTTPService.h>
+#include <media/mediaplayer.h>
+#include <media/stagefright/MediaExtractor.h>
+#include "SoundPool.h"
+#include "SoundPoolThread.h"
+#include <media/AudioPolicyHelper.h>
+#include <ndk/NdkMediaCodec.h>
+#include <ndk/NdkMediaExtractor.h>
+#include <ndk/NdkMediaFormat.h>
+
+namespace android
+{
+
+int kDefaultBufferCount = 4;
+uint32_t kMaxSampleRate = 48000;
+uint32_t kDefaultSampleRate = 44100;
+uint32_t kDefaultFrameCount = 1200;
+size_t kDefaultHeapSize = 1024 * 1024; // 1MB
+
+
+SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
+{
+    ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s",
+            maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags);
+
+    // check limits
+    mMaxChannels = maxChannels;
+    if (mMaxChannels < 1) {
+        mMaxChannels = 1;
+    }
+    else if (mMaxChannels > 32) {
+        mMaxChannels = 32;
+    }
+    ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
+
+    mQuit = false;
+    mDecodeThread = 0;
+    memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
+    mAllocated = 0;
+    mNextSampleID = 0;
+    mNextChannelID = 0;
+
+    mCallback = 0;
+    mUserData = 0;
+
+    mChannelPool = new SoundChannel[mMaxChannels];
+    for (int i = 0; i < mMaxChannels; ++i) {
+        mChannelPool[i].init(this);
+        mChannels.push_back(&mChannelPool[i]);
+    }
+
+    // start decode thread
+    startThreads();
+}
+
+SoundPool::~SoundPool()
+{
+    ALOGV("SoundPool destructor");
+    mDecodeThread->quit();
+    quit();
+
+    Mutex::Autolock lock(&mLock);
+
+    mChannels.clear();
+    if (mChannelPool)
+        delete [] mChannelPool;
+    // clean up samples
+    ALOGV("clear samples");
+    mSamples.clear();
+
+    if (mDecodeThread)
+        delete mDecodeThread;
+}
+
+void SoundPool::addToRestartList(SoundChannel* channel)
+{
+    Mutex::Autolock lock(&mRestartLock);
+    if (!mQuit) {
+        mRestart.push_back(channel);
+        mCondition.signal();
+    }
+}
+
+void SoundPool::addToStopList(SoundChannel* channel)
+{
+    Mutex::Autolock lock(&mRestartLock);
+    if (!mQuit) {
+        mStop.push_back(channel);
+        mCondition.signal();
+    }
+}
+
+int SoundPool::beginThread(void* arg)
+{
+    SoundPool* p = (SoundPool*)arg;
+    return p->run();
+}
+
+int SoundPool::run()
+{
+    mRestartLock.lock();
+    while (!mQuit) {
+        mCondition.wait(mRestartLock);
+        ALOGV("awake");
+        if (mQuit) break;
+
+        while (!mStop.empty()) {
+            SoundChannel* channel;
+            ALOGV("Getting channel from stop list");
+            List<SoundChannel* >::iterator iter = mStop.begin();
+            channel = *iter;
+            mStop.erase(iter);
+            mRestartLock.unlock();
+            if (channel != 0) {
+                Mutex::Autolock lock(&mLock);
+                channel->stop();
+            }
+            mRestartLock.lock();
+            if (mQuit) break;
+        }
+
+        while (!mRestart.empty()) {
+            SoundChannel* channel;
+            ALOGV("Getting channel from list");
+            List<SoundChannel*>::iterator iter = mRestart.begin();
+            channel = *iter;
+            mRestart.erase(iter);
+            mRestartLock.unlock();
+            if (channel != 0) {
+                Mutex::Autolock lock(&mLock);
+                channel->nextEvent();
+            }
+            mRestartLock.lock();
+            if (mQuit) break;
+        }
+    }
+
+    mStop.clear();
+    mRestart.clear();
+    mCondition.signal();
+    mRestartLock.unlock();
+    ALOGV("goodbye");
+    return 0;
+}
+
+void SoundPool::quit()
+{
+    mRestartLock.lock();
+    mQuit = true;
+    mCondition.signal();
+    mCondition.wait(mRestartLock);
+    ALOGV("return from quit");
+    mRestartLock.unlock();
+}
+
+bool SoundPool::startThreads()
+{
+    createThreadEtc(beginThread, this, "SoundPool");
+    if (mDecodeThread == NULL)
+        mDecodeThread = new SoundPoolThread(this);
+    return mDecodeThread != NULL;
+}
+
+SoundChannel* SoundPool::findChannel(int channelID)
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        if (mChannelPool[i].channelID() == channelID) {
+            return &mChannelPool[i];
+        }
+    }
+    return NULL;
+}
+
+SoundChannel* SoundPool::findNextChannel(int channelID)
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        if (mChannelPool[i].nextChannelID() == channelID) {
+            return &mChannelPool[i];
+        }
+    }
+    return NULL;
+}
+
+int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
+{
+    ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
+            fd, offset, length, priority);
+    Mutex::Autolock lock(&mLock);
+    sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
+    mSamples.add(sample->sampleID(), sample);
+    doLoad(sample);
+    return sample->sampleID();
+}
+
+void SoundPool::doLoad(sp<Sample>& sample)
+{
+    ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID());
+    sample->startLoad();
+    mDecodeThread->loadSample(sample->sampleID());
+}
+
+bool SoundPool::unload(int sampleID)
+{
+    ALOGV("unload: sampleID=%d", sampleID);
+    Mutex::Autolock lock(&mLock);
+    return mSamples.removeItem(sampleID);
+}
+
+int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
+        int priority, int loop, float rate)
+{
+    ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
+            sampleID, leftVolume, rightVolume, priority, loop, rate);
+    sp<Sample> sample;
+    SoundChannel* channel;
+    int channelID;
+
+    Mutex::Autolock lock(&mLock);
+
+    if (mQuit) {
+        return 0;
+    }
+    // is sample ready?
+    sample = findSample(sampleID);
+    if ((sample == 0) || (sample->state() != Sample::READY)) {
+        ALOGW("  sample %d not READY", sampleID);
+        return 0;
+    }
+
+    dump();
+
+    // allocate a channel
+    channel = allocateChannel_l(priority);
+
+    // no channel allocated - return 0
+    if (!channel) {
+        ALOGV("No channel allocated");
+        return 0;
+    }
+
+    channelID = ++mNextChannelID;
+
+    ALOGV("play channel %p state = %d", channel, channel->state());
+    channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
+    return channelID;
+}
+
+SoundChannel* SoundPool::allocateChannel_l(int priority)
+{
+    List<SoundChannel*>::iterator iter;
+    SoundChannel* channel = NULL;
+
+    // allocate a channel
+    if (!mChannels.empty()) {
+        iter = mChannels.begin();
+        if (priority >= (*iter)->priority()) {
+            channel = *iter;
+            mChannels.erase(iter);
+            ALOGV("Allocated active channel");
+        }
+    }
+
+    // update priority and put it back in the list
+    if (channel) {
+        channel->setPriority(priority);
+        for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+            if (priority < (*iter)->priority()) {
+                break;
+            }
+        }
+        mChannels.insert(iter, channel);
+    }
+    return channel;
+}
+
+// move a channel from its current position to the front of the list
+void SoundPool::moveToFront_l(SoundChannel* channel)
+{
+    for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+        if (*iter == channel) {
+            mChannels.erase(iter);
+            mChannels.push_front(channel);
+            break;
+        }
+    }
+}
+
+void SoundPool::pause(int channelID)
+{
+    ALOGV("pause(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->pause();
+    }
+}
+
+void SoundPool::autoPause()
+{
+    ALOGV("autoPause()");
+    Mutex::Autolock lock(&mLock);
+    for (int i = 0; i < mMaxChannels; ++i) {
+        SoundChannel* channel = &mChannelPool[i];
+        channel->autoPause();
+    }
+}
+
+void SoundPool::resume(int channelID)
+{
+    ALOGV("resume(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->resume();
+    }
+}
+
+void SoundPool::autoResume()
+{
+    ALOGV("autoResume()");
+    Mutex::Autolock lock(&mLock);
+    for (int i = 0; i < mMaxChannels; ++i) {
+        SoundChannel* channel = &mChannelPool[i];
+        channel->autoResume();
+    }
+}
+
+void SoundPool::stop(int channelID)
+{
+    ALOGV("stop(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->stop();
+    } else {
+        channel = findNextChannel(channelID);
+        if (channel)
+            channel->clearNextEvent();
+    }
+}
+
+void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
+{
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setVolume(leftVolume, rightVolume);
+    }
+}
+
+void SoundPool::setPriority(int channelID, int priority)
+{
+    ALOGV("setPriority(%d, %d)", channelID, priority);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setPriority(priority);
+    }
+}
+
+void SoundPool::setLoop(int channelID, int loop)
+{
+    ALOGV("setLoop(%d, %d)", channelID, loop);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setLoop(loop);
+    }
+}
+
+void SoundPool::setRate(int channelID, float rate)
+{
+    ALOGV("setRate(%d, %f)", channelID, rate);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setRate(rate);
+    }
+}
+
+// call with lock held
+void SoundPool::done_l(SoundChannel* channel)
+{
+    ALOGV("done_l(%d)", channel->channelID());
+    // if "stolen", play next event
+    if (channel->nextChannelID() != 0) {
+        ALOGV("add to restart list");
+        addToRestartList(channel);
+    }
+
+    // return to idle state
+    else {
+        ALOGV("move to front");
+        moveToFront_l(channel);
+    }
+}
+
+void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
+{
+    Mutex::Autolock lock(&mCallbackLock);
+    mCallback = callback;
+    mUserData = user;
+}
+
+void SoundPool::notify(SoundPoolEvent event)
+{
+    Mutex::Autolock lock(&mCallbackLock);
+    if (mCallback != NULL) {
+        mCallback(event, this, mUserData);
+    }
+}
+
+void SoundPool::dump()
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        mChannelPool[i].dump();
+    }
+}
+
+
+Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
+{
+    init();
+    mSampleID = sampleID;
+    mFd = dup(fd);
+    mOffset = offset;
+    mLength = length;
+    ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
+        mSampleID, mFd, mLength, mOffset);
+}
+
+void Sample::init()
+{
+    mSize = 0;
+    mRefCount = 0;
+    mSampleID = 0;
+    mState = UNLOADED;
+    mFd = -1;
+    mOffset = 0;
+    mLength = 0;
+}
+
+Sample::~Sample()
+{
+    ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
+    if (mFd > 0) {
+        ALOGV("close(%d)", mFd);
+        ::close(mFd);
+    }
+}
+
+static status_t decode(int fd, int64_t offset, int64_t length,
+        uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
+        sp<MemoryHeapBase> heap, size_t *memsize) {
+
+    ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
+    AMediaExtractor *ex = AMediaExtractor_new();
+    status_t err = AMediaExtractor_setDataSourceFd(ex, fd, offset, length);
+
+    if (err != AMEDIA_OK) {
+        return err;
+    }
+
+    *audioFormat = AUDIO_FORMAT_PCM_16_BIT;
+
+    size_t numTracks = AMediaExtractor_getTrackCount(ex);
+    for (size_t i = 0; i < numTracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            AMediaExtractor_delete(ex);
+            AMediaFormat_delete(format);
+            return UNKNOWN_ERROR;
+        }
+        if (strncmp(mime, "audio/", 6) == 0) {
+
+            AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
+            if (AMediaCodec_configure(codec, format,
+                    NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
+                || AMediaCodec_start(codec) != AMEDIA_OK
+                || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
+                AMediaExtractor_delete(ex);
+                AMediaCodec_delete(codec);
+                AMediaFormat_delete(format);
+                return UNKNOWN_ERROR;
+            }
+
+            bool sawInputEOS = false;
+            bool sawOutputEOS = false;
+            uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
+            size_t available = heap->getSize();
+            size_t written = 0;
+
+            AMediaFormat_delete(format);
+            format = AMediaCodec_getOutputFormat(codec);
+
+            while (!sawOutputEOS) {
+                if (!sawInputEOS) {
+                    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
+                    ALOGV("input buffer %zd", bufidx);
+                    if (bufidx >= 0) {
+                        size_t bufsize;
+                        uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+                        int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+                        ALOGV("read %d", sampleSize);
+                        if (sampleSize < 0) {
+                            sampleSize = 0;
+                            sawInputEOS = true;
+                            ALOGV("EOS");
+                        }
+                        int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+                        AMediaCodec_queueInputBuffer(codec, bufidx,
+                                0 /* offset */, sampleSize, presentationTimeUs,
+                                sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+                        AMediaExtractor_advance(ex);
+                    }
+                }
+
+                AMediaCodecBufferInfo info;
+                int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
+                ALOGV("dequeueoutput returned: %d", status);
+                if (status >= 0) {
+                    if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                        ALOGV("output EOS");
+                        sawOutputEOS = true;
+                    }
+                    ALOGV("got decoded buffer size %d", info.size);
+
+                    uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
+                    size_t dataSize = info.size;
+                    if (dataSize > available) {
+                        dataSize = available;
+                    }
+                    memcpy(writePos, buf + info.offset, dataSize);
+                    writePos += dataSize;
+                    written += dataSize;
+                    available -= dataSize;
+                    AMediaCodec_releaseOutputBuffer(codec, status, false /* render */);
+                    if (available == 0) {
+                        // there might be more data, but there's no space for it
+                        sawOutputEOS = true;
+                    }
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+                    ALOGV("output buffers changed");
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+                    AMediaFormat_delete(format);
+                    format = AMediaCodec_getOutputFormat(codec);
+                    ALOGV("format changed to: %s", AMediaFormat_toString(format));
+                } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                    ALOGV("no output buffer right now");
+                } else {
+                    ALOGV("unexpected info code: %d", status);
+                }
+            }
+
+            AMediaCodec_stop(codec);
+            AMediaCodec_delete(codec);
+            AMediaExtractor_delete(ex);
+            if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
+                    !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
+                AMediaFormat_delete(format);
+                return UNKNOWN_ERROR;
+            }
+            AMediaFormat_delete(format);
+            *memsize = written;
+            return OK;
+        }
+        AMediaFormat_delete(format);
+    }
+    AMediaExtractor_delete(ex);
+    return UNKNOWN_ERROR;
+}
+
+status_t Sample::doLoad()
+{
+    uint32_t sampleRate;
+    int numChannels;
+    audio_format_t format;
+    status_t status;
+    mHeap = new MemoryHeapBase(kDefaultHeapSize);
+
+    ALOGV("Start decode");
+    status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
+                                 mHeap, &mSize);
+    ALOGV("close(%d)", mFd);
+    ::close(mFd);
+    mFd = -1;
+    if (status != NO_ERROR) {
+        ALOGE("Unable to load sample");
+        goto error;
+    }
+    ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
+          mHeap->getBase(), mSize, sampleRate, numChannels);
+
+    if (sampleRate > kMaxSampleRate) {
+       ALOGE("Sample rate (%u) out of range", sampleRate);
+       status = BAD_VALUE;
+       goto error;
+    }
+
+    if ((numChannels < 1) || (numChannels > 2)) {
+        ALOGE("Sample channel count (%d) out of range", numChannels);
+        status = BAD_VALUE;
+        goto error;
+    }
+
+    mData = new MemoryBase(mHeap, 0, mSize);
+    mSampleRate = sampleRate;
+    mNumChannels = numChannels;
+    mFormat = format;
+    mState = READY;
+    return NO_ERROR;
+
+error:
+    mHeap.clear();
+    return status;
+}
+
+
+void SoundChannel::init(SoundPool* soundPool)
+{
+    mSoundPool = soundPool;
+}
+
+// call with sound pool lock held
+void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
+        float rightVolume, int priority, int loop, float rate)
+{
+    sp<AudioTrack> oldTrack;
+    sp<AudioTrack> newTrack;
+    status_t status;
+
+    { // scope for the lock
+        Mutex::Autolock lock(&mLock);
+
+        ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f,"
+                " priority=%d, loop=%d, rate=%f",
+                this, sample->sampleID(), nextChannelID, leftVolume, rightVolume,
+                priority, loop, rate);
+
+        // if not idle, this voice is being stolen
+        if (mState != IDLE) {
+            ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
+            mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+            stop_l();
+            return;
+        }
+
+        // initialize track
+        size_t afFrameCount;
+        uint32_t afSampleRate;
+        audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes());
+        if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+            afFrameCount = kDefaultFrameCount;
+        }
+        if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+            afSampleRate = kDefaultSampleRate;
+        }
+        int numChannels = sample->numChannels();
+        uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
+        size_t frameCount = 0;
+
+        if (loop) {
+            frameCount = sample->size()/numChannels/
+                ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+        }
+
+#ifndef USE_SHARED_MEM_BUFFER
+        uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
+        // Ensure minimum audio buffer size in case of short looped sample
+        if(frameCount < totalFrames) {
+            frameCount = totalFrames;
+        }
+#endif
+
+        // mToggle toggles each time a track is started on a given channel.
+        // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
+        // as callback user data. This enables the detection of callbacks received from the old
+        // audio track while the new one is being started and avoids processing them with
+        // wrong audio audio buffer size  (mAudioBufferSize)
+        unsigned long toggle = mToggle ^ 1;
+        void *userData = (void *)((unsigned long)this | toggle);
+        audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
+
+        // do not create a new audio track if current track is compatible with sample parameters
+#ifdef USE_SHARED_MEM_BUFFER
+        newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+                channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData);
+#else
+        uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
+        newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+                channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
+                bufferFrames);
+#endif
+        oldTrack = mAudioTrack;
+        status = newTrack->initCheck();
+        if (status != NO_ERROR) {
+            ALOGE("Error creating AudioTrack");
+            goto exit;
+        }
+        ALOGV("setVolume %p", newTrack.get());
+        newTrack->setVolume(leftVolume, rightVolume);
+        newTrack->setLoop(0, frameCount, loop);
+
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+        mToggle = toggle;
+        mAudioTrack = newTrack;
+        mPos = 0;
+        mSample = sample;
+        mChannelID = nextChannelID;
+        mPriority = priority;
+        mLoop = loop;
+        mLeftVolume = leftVolume;
+        mRightVolume = rightVolume;
+        mNumChannels = numChannels;
+        mRate = rate;
+        clearNextEvent();
+        mState = PLAYING;
+        mAudioTrack->start();
+        mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
+    }
+
+exit:
+    ALOGV("delete oldTrack %p", oldTrack.get());
+    if (status != NO_ERROR) {
+        mAudioTrack.clear();
+    }
+}
+
+void SoundChannel::nextEvent()
+{
+    sp<Sample> sample;
+    int nextChannelID;
+    float leftVolume;
+    float rightVolume;
+    int priority;
+    int loop;
+    float rate;
+
+    // check for valid event
+    {
+        Mutex::Autolock lock(&mLock);
+        nextChannelID = mNextEvent.channelID();
+        if (nextChannelID  == 0) {
+            ALOGV("stolen channel has no event");
+            return;
+        }
+
+        sample = mNextEvent.sample();
+        leftVolume = mNextEvent.leftVolume();
+        rightVolume = mNextEvent.rightVolume();
+        priority = mNextEvent.priority();
+        loop = mNextEvent.loop();
+        rate = mNextEvent.rate();
+    }
+
+    ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
+    play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+}
+
+void SoundChannel::callback(int event, void* user, void *info)
+{
+    SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
+
+    channel->process(event, info, (unsigned long)user & 1);
+}
+
+void SoundChannel::process(int event, void *info, unsigned long toggle)
+{
+    //ALOGV("process(%d)", mChannelID);
+
+    Mutex::Autolock lock(&mLock);
+
+    AudioTrack::Buffer* b = NULL;
+    if (event == AudioTrack::EVENT_MORE_DATA) {
+       b = static_cast<AudioTrack::Buffer *>(info);
+    }
+
+    if (mToggle != toggle) {
+        ALOGV("process wrong toggle %p channel %d", this, mChannelID);
+        if (b != NULL) {
+            b->size = 0;
+        }
+        return;
+    }
+
+    sp<Sample> sample = mSample;
+
+//    ALOGV("SoundChannel::process event %d", event);
+
+    if (event == AudioTrack::EVENT_MORE_DATA) {
+
+        // check for stop state
+        if (b->size == 0) return;
+
+        if (mState == IDLE) {
+            b->size = 0;
+            return;
+        }
+
+        if (sample != 0) {
+            // fill buffer
+            uint8_t* q = (uint8_t*) b->i8;
+            size_t count = 0;
+
+            if (mPos < (int)sample->size()) {
+                uint8_t* p = sample->data() + mPos;
+                count = sample->size() - mPos;
+                if (count > b->size) {
+                    count = b->size;
+                }
+                memcpy(q, p, count);
+//              ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
+//                      count);
+            } else if (mPos < mAudioBufferSize) {
+                count = mAudioBufferSize - mPos;
+                if (count > b->size) {
+                    count = b->size;
+                }
+                memset(q, 0, count);
+//              ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
+            }
+
+            mPos += count;
+            b->size = count;
+            //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
+        }
+    } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
+        ALOGV("process %p channel %d event %s",
+              this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
+                      "BUFFER_END");
+        mSoundPool->addToStopList(this);
+    } else if (event == AudioTrack::EVENT_LOOP_END) {
+        ALOGV("End loop %p channel %d", this, mChannelID);
+    } else if (event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
+        ALOGV("process %p channel %d NEW_IAUDIOTRACK", this, mChannelID);
+    } else {
+        ALOGW("SoundChannel::process unexpected event %d", event);
+    }
+}
+
+
+// call with lock held
+bool SoundChannel::doStop_l()
+{
+    if (mState != IDLE) {
+        setVolume_l(0, 0);
+        ALOGV("stop");
+        mAudioTrack->stop();
+        mSample.clear();
+        mState = IDLE;
+        mPriority = IDLE_PRIORITY;
+        return true;
+    }
+    return false;
+}
+
+// call with lock held and sound pool lock held
+void SoundChannel::stop_l()
+{
+    if (doStop_l()) {
+        mSoundPool->done_l(this);
+    }
+}
+
+// call with sound pool lock held
+void SoundChannel::stop()
+{
+    bool stopped;
+    {
+        Mutex::Autolock lock(&mLock);
+        stopped = doStop_l();
+    }
+
+    if (stopped) {
+        mSoundPool->done_l(this);
+    }
+}
+
+//FIXME: Pause is a little broken right now
+void SoundChannel::pause()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PLAYING) {
+        ALOGV("pause track");
+        mState = PAUSED;
+        mAudioTrack->pause();
+    }
+}
+
+void SoundChannel::autoPause()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PLAYING) {
+        ALOGV("pause track");
+        mState = PAUSED;
+        mAutoPaused = true;
+        mAudioTrack->pause();
+    }
+}
+
+void SoundChannel::resume()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PAUSED) {
+        ALOGV("resume track");
+        mState = PLAYING;
+        mAutoPaused = false;
+        mAudioTrack->start();
+    }
+}
+
+void SoundChannel::autoResume()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAutoPaused && (mState == PAUSED)) {
+        ALOGV("resume track");
+        mState = PLAYING;
+        mAutoPaused = false;
+        mAudioTrack->start();
+    }
+}
+
+void SoundChannel::setRate(float rate)
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAudioTrack != NULL && mSample != 0) {
+        uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
+        mAudioTrack->setSampleRate(sampleRate);
+        mRate = rate;
+    }
+}
+
+// call with lock held
+void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
+{
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    if (mAudioTrack != NULL)
+        mAudioTrack->setVolume(leftVolume, rightVolume);
+}
+
+void SoundChannel::setVolume(float leftVolume, float rightVolume)
+{
+    Mutex::Autolock lock(&mLock);
+    setVolume_l(leftVolume, rightVolume);
+}
+
+void SoundChannel::setLoop(int loop)
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAudioTrack != NULL && mSample != 0) {
+        uint32_t loopEnd = mSample->size()/mNumChannels/
+            ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+        mAudioTrack->setLoop(0, loopEnd, loop);
+        mLoop = loop;
+    }
+}
+
+SoundChannel::~SoundChannel()
+{
+    ALOGV("SoundChannel destructor %p", this);
+    {
+        Mutex::Autolock lock(&mLock);
+        clearNextEvent();
+        doStop_l();
+    }
+    // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
+    // callback thread to exit which may need to execute process() and acquire the mLock.
+    mAudioTrack.clear();
+}
+
+void SoundChannel::dump()
+{
+    ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
+            mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
+}
+
+void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
+            float rightVolume, int priority, int loop, float rate)
+{
+    mSample = sample;
+    mChannelID = channelID;
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    mPriority = priority;
+    mLoop = loop;
+    mRate =rate;
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
new file mode 100644
index 0000000..9d9cbdf
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#ifndef SOUNDPOOL_H_
+#define SOUNDPOOL_H_
+
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <media/AudioTrack.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+namespace android {
+
+static const int IDLE_PRIORITY = -1;
+
+// forward declarations
+class SoundEvent;
+class SoundPoolThread;
+class SoundPool;
+
+// for queued events
+class SoundPoolEvent {
+public:
+    SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
+        mMsg(msg), mArg1(arg1), mArg2(arg2) {}
+    int         mMsg;
+    int         mArg1;
+    int         mArg2;
+    enum MessageType { INVALID, SAMPLE_LOADED };
+};
+
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
+
+// tracks samples used by application
+class Sample  : public RefBase {
+public:
+    enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
+    Sample(int sampleID, int fd, int64_t offset, int64_t length);
+    ~Sample();
+    int sampleID() { return mSampleID; }
+    int numChannels() { return mNumChannels; }
+    int sampleRate() { return mSampleRate; }
+    audio_format_t format() { return mFormat; }
+    size_t size() { return mSize; }
+    int state() { return mState; }
+    uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
+    status_t doLoad();
+    void startLoad() { mState = LOADING; }
+    sp<IMemory> getIMemory() { return mData; }
+
+private:
+    void init();
+
+    size_t              mSize;
+    volatile int32_t    mRefCount;
+    uint16_t            mSampleID;
+    uint16_t            mSampleRate;
+    uint8_t             mState : 3;
+    uint8_t             mNumChannels : 2;
+    audio_format_t      mFormat;
+    int                 mFd;
+    int64_t             mOffset;
+    int64_t             mLength;
+    sp<IMemory>         mData;
+    sp<MemoryHeapBase>  mHeap;
+};
+
+// stores pending events for stolen channels
+class SoundEvent
+{
+public:
+    SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
+            mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
+    void set(const sp<Sample>& sample, int channelID, float leftVolume,
+            float rightVolume, int priority, int loop, float rate);
+    sp<Sample>      sample() { return mSample; }
+    int             channelID() { return mChannelID; }
+    float           leftVolume() { return mLeftVolume; }
+    float           rightVolume() { return mRightVolume; }
+    int             priority() { return mPriority; }
+    int             loop() { return mLoop; }
+    float           rate() { return mRate; }
+    void            clear() { mChannelID = 0; mSample.clear(); }
+
+protected:
+    sp<Sample>      mSample;
+    int             mChannelID;
+    float           mLeftVolume;
+    float           mRightVolume;
+    int             mPriority;
+    int             mLoop;
+    float           mRate;
+};
+
+// for channels aka AudioTracks
+class SoundChannel : public SoundEvent {
+public:
+    enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
+    SoundChannel() : mState(IDLE), mNumChannels(1),
+            mPos(0), mToggle(0), mAutoPaused(false) {}
+    ~SoundChannel();
+    void init(SoundPool* soundPool);
+    void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
+            int priority, int loop, float rate);
+    void setVolume_l(float leftVolume, float rightVolume);
+    void setVolume(float leftVolume, float rightVolume);
+    void stop_l();
+    void stop();
+    void pause();
+    void autoPause();
+    void resume();
+    void autoResume();
+    void setRate(float rate);
+    int state() { return mState; }
+    void setPriority(int priority) { mPriority = priority; }
+    void setLoop(int loop);
+    int numChannels() { return mNumChannels; }
+    void clearNextEvent() { mNextEvent.clear(); }
+    void nextEvent();
+    int nextChannelID() { return mNextEvent.channelID(); }
+    void dump();
+
+private:
+    static void callback(int event, void* user, void *info);
+    void process(int event, void *info, unsigned long toggle);
+    bool doStop_l();
+
+    SoundPool*          mSoundPool;
+    sp<AudioTrack>      mAudioTrack;
+    SoundEvent          mNextEvent;
+    Mutex               mLock;
+    int                 mState;
+    int                 mNumChannels;
+    int                 mPos;
+    int                 mAudioBufferSize;
+    unsigned long       mToggle;
+    bool                mAutoPaused;
+};
+
+// application object for managing a pool of sounds
+class SoundPool {
+    friend class SoundPoolThread;
+    friend class SoundChannel;
+public:
+    SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
+    ~SoundPool();
+    int load(int fd, int64_t offset, int64_t length, int priority);
+    bool unload(int sampleID);
+    int play(int sampleID, float leftVolume, float rightVolume, int priority,
+            int loop, float rate);
+    void pause(int channelID);
+    void autoPause();
+    void resume(int channelID);
+    void autoResume();
+    void stop(int channelID);
+    void setVolume(int channelID, float leftVolume, float rightVolume);
+    void setPriority(int channelID, int priority);
+    void setLoop(int channelID, int loop);
+    void setRate(int channelID, float rate);
+    const audio_attributes_t* attributes() { return &mAttributes; }
+
+    // called from SoundPoolThread
+    void sampleLoaded(int sampleID);
+
+    // called from AudioTrack thread
+    void done_l(SoundChannel* channel);
+
+    // callback function
+    void setCallback(SoundPoolCallback* callback, void* user);
+    void* getUserData() { return mUserData; }
+
+private:
+    SoundPool() {} // no default constructor
+    bool startThreads();
+    void doLoad(sp<Sample>& sample);
+    sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); }
+    SoundChannel* findChannel (int channelID);
+    SoundChannel* findNextChannel (int channelID);
+    SoundChannel* allocateChannel_l(int priority);
+    void moveToFront_l(SoundChannel* channel);
+    void notify(SoundPoolEvent event);
+    void dump();
+
+    // restart thread
+    void addToRestartList(SoundChannel* channel);
+    void addToStopList(SoundChannel* channel);
+    static int beginThread(void* arg);
+    int run();
+    void quit();
+
+    Mutex                   mLock;
+    Mutex                   mRestartLock;
+    Condition               mCondition;
+    SoundPoolThread*        mDecodeThread;
+    SoundChannel*           mChannelPool;
+    List<SoundChannel*>     mChannels;
+    List<SoundChannel*>     mRestart;
+    List<SoundChannel*>     mStop;
+    DefaultKeyedVector< int, sp<Sample> >   mSamples;
+    int                     mMaxChannels;
+    audio_attributes_t      mAttributes;
+    int                     mAllocated;
+    int                     mNextSampleID;
+    int                     mNextChannelID;
+    bool                    mQuit;
+
+    // callback
+    Mutex                   mCallbackLock;
+    SoundPoolCallback*      mCallback;
+    void*                   mUserData;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOL_H_*/
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
new file mode 100644
index 0000000..ba3b482
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPoolThread"
+#include "utils/Log.h"
+
+#include "SoundPoolThread.h"
+
+namespace android {
+
+void SoundPoolThread::write(SoundPoolMsg msg) {
+    Mutex::Autolock lock(&mLock);
+    while (mMsgQueue.size() >= maxMessages) {
+        mCondition.wait(mLock);
+    }
+
+    // if thread is quitting, don't add to queue
+    if (mRunning) {
+        mMsgQueue.push(msg);
+        mCondition.signal();
+    }
+}
+
+const SoundPoolMsg SoundPoolThread::read() {
+    Mutex::Autolock lock(&mLock);
+    while (mMsgQueue.size() == 0) {
+        mCondition.wait(mLock);
+    }
+    SoundPoolMsg msg = mMsgQueue[0];
+    mMsgQueue.removeAt(0);
+    mCondition.signal();
+    return msg;
+}
+
+void SoundPoolThread::quit() {
+    Mutex::Autolock lock(&mLock);
+    if (mRunning) {
+        mRunning = false;
+        mMsgQueue.clear();
+        mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
+        mCondition.signal();
+        mCondition.wait(mLock);
+    }
+    ALOGV("return from quit");
+}
+
+SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
+    mSoundPool(soundPool)
+{
+    mMsgQueue.setCapacity(maxMessages);
+    if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
+        mRunning = true;
+    }
+}
+
+SoundPoolThread::~SoundPoolThread()
+{
+    quit();
+}
+
+int SoundPoolThread::beginThread(void* arg) {
+    ALOGV("beginThread");
+    SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
+    return soundPoolThread->run();
+}
+
+int SoundPoolThread::run() {
+    ALOGV("run");
+    for (;;) {
+        SoundPoolMsg msg = read();
+        ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
+        switch (msg.mMessageType) {
+        case SoundPoolMsg::KILL:
+            ALOGV("goodbye");
+            return NO_ERROR;
+        case SoundPoolMsg::LOAD_SAMPLE:
+            doLoadSample(msg.mData);
+            break;
+        default:
+            ALOGW("run: Unrecognized message %d\n",
+                    msg.mMessageType);
+            break;
+        }
+    }
+}
+
+void SoundPoolThread::loadSample(int sampleID) {
+    write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
+}
+
+void SoundPoolThread::doLoadSample(int sampleID) {
+    sp <Sample> sample = mSoundPool->findSample(sampleID);
+    status_t status = -1;
+    if (sample != 0) {
+        status = sample->doLoad();
+    }
+    mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
new file mode 100644
index 0000000..d388388
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef SOUNDPOOLTHREAD_H_
+#define SOUNDPOOLTHREAD_H_
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include <media/AudioTrack.h>
+
+#include "SoundPool.h"
+
+namespace android {
+
+class SoundPoolMsg {
+public:
+    enum MessageType { INVALID, KILL, LOAD_SAMPLE };
+    SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
+    SoundPoolMsg(MessageType MessageType, int data) :
+        mMessageType(MessageType), mData(data) {}
+    uint16_t         mMessageType;
+    uint16_t         mData;
+};
+
+/*
+ * This class handles background requests from the SoundPool
+ */
+class SoundPoolThread {
+public:
+    SoundPoolThread(SoundPool* SoundPool);
+    ~SoundPoolThread();
+    void loadSample(int sampleID);
+    void quit();
+    void write(SoundPoolMsg msg);
+
+private:
+    static const size_t maxMessages = 5;
+
+    static int beginThread(void* arg);
+    int run();
+    void doLoadSample(int sampleID);
+    const SoundPoolMsg read();
+
+    Mutex                   mLock;
+    Condition               mCondition;
+    Vector<SoundPoolMsg>    mMsgQueue;
+    SoundPool*              mSoundPool;
+    bool                    mRunning;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOLTHREAD_H_*/
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index baf61d5..b2333f8 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -23,7 +23,7 @@
 #include <nativehelper/jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <media/SoundPool.h>
+#include "SoundPool.h"
 
 using namespace android;
 
@@ -45,20 +45,6 @@
 static audio_attributes_fields_t javaAudioAttrFields;
 
 // ----------------------------------------------------------------------------
-static jint
-android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
-{
-    ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
-    SoundPool *ap = MusterSoundPool(env, thiz);
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return 0;
-    }
-    const char* s = env->GetStringUTFChars(path, NULL);
-    int id = ap->load(s, priority);
-    env->ReleaseStringUTFChars(path, s);
-    return (jint) id;
-}
 
 static jint
 android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
@@ -249,10 +235,6 @@
 // Dalvik VM type signatures
 static JNINativeMethod gMethods[] = {
     {   "_load",
-        "(Ljava/lang/String;I)I",
-        (void *)android_media_SoundPool_SoundPoolImpl_load_URL
-    },
-    {   "_load",
         "(Ljava/io/FileDescriptor;JJI)I",
         (void *)android_media_SoundPool_SoundPoolImpl_load_FD
     },
diff --git a/media/mca/filterfw/native/core/value.cpp b/media/mca/filterfw/native/core/value.cpp
index 04bf0ef..c0ea763 100644
--- a/media/mca/filterfw/native/core/value.cpp
+++ b/media/mca/filterfw/native/core/value.cpp
@@ -16,6 +16,7 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "value.h"
 
diff --git a/media/mca/filterpacks/native/imageproc/brightness.c b/media/mca/filterpacks/native/imageproc/brightness.c
index f4addf1..3c3652f 100644
--- a/media/mca/filterpacks/native/imageproc/brightness.c
+++ b/media/mca/filterpacks/native/imageproc/brightness.c
@@ -16,6 +16,7 @@
 
 #include <android/log.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define  LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
 #define  LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
diff --git a/media/mca/filterpacks/native/imageproc/contrast.c b/media/mca/filterpacks/native/imageproc/contrast.c
index ea8c8d2..31c975c 100644
--- a/media/mca/filterpacks/native/imageproc/contrast.c
+++ b/media/mca/filterpacks/native/imageproc/contrast.c
@@ -16,6 +16,7 @@
 
 #include <android/log.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define  LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
 #define  LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index c6b5b0d..287b3f1 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -118,7 +118,10 @@
      */
     public enum DataType {
         NONE (0, 0),
-        //FLOAT_16 (1, 2),
+        /**
+         *     @hide
+         */
+        FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
         FLOAT_64 (3, 8),
         SIGNED_8 (4, 1),
@@ -386,6 +389,16 @@
         return rs.mElement_I64;
     }
 
+    /**
+     *     @hide
+     */
+    public static Element F16(RenderScript rs) {
+        if(rs.mElement_F16 == null) {
+            rs.mElement_F16 = createUser(rs, DataType.FLOAT_16);
+        }
+        return rs.mElement_F16;
+    }
+
     public static Element F32(RenderScript rs) {
         if(rs.mElement_F32 == null) {
             rs.mElement_F32 = createUser(rs, DataType.FLOAT_32);
@@ -520,6 +533,36 @@
         return rs.mElement_RGBA_8888;
     }
 
+    /**
+     *     @hide
+     */
+    public static Element F16_2(RenderScript rs) {
+        if(rs.mElement_HALF_2 == null) {
+            rs.mElement_HALF_2 = createVector(rs, DataType.FLOAT_16, 2);
+        }
+        return rs.mElement_HALF_2;
+    }
+
+    /**
+     *     @hide
+     */
+    public static Element F16_3(RenderScript rs) {
+        if(rs.mElement_FLOAT_3 == null) {
+            rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_16, 3);
+        }
+        return rs.mElement_HALF_3;
+    }
+
+    /**
+     *     @hide
+     */
+    public static Element F16_4(RenderScript rs) {
+        if(rs.mElement_HALF_4 == null) {
+            rs.mElement_HALF_4 = createVector(rs, DataType.FLOAT_16, 4);
+        }
+        return rs.mElement_HALF_4;
+    }
+
     public static Element F32_2(RenderScript rs) {
         if(rs.mElement_FLOAT_2 == null) {
             rs.mElement_FLOAT_2 = createVector(rs, DataType.FLOAT_32, 2);
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index fb8ae5d..f37519e 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -330,10 +330,10 @@
       rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
     }
 
-    native long rsnScriptGroup2Create(long con, long[] closures);
-    synchronized long nScriptGroup2Create(long[] closures) {
+    native long rsnScriptGroup2Create(long con, String cachePath, long[] closures);
+    synchronized long nScriptGroup2Create(String cachePath, long[] closures) {
       validate();
-      return rsnScriptGroup2Create(mContext, closures);
+      return rsnScriptGroup2Create(mContext, cachePath, closures);
     }
 
     native void rsnScriptGroup2Execute(long con, long groupID);
@@ -871,6 +871,7 @@
     Element mElement_I32;
     Element mElement_U64;
     Element mElement_I64;
+    Element mElement_F16;
     Element mElement_F32;
     Element mElement_F64;
     Element mElement_BOOLEAN;
@@ -894,6 +895,10 @@
     Element mElement_RGBA_4444;
     Element mElement_RGBA_8888;
 
+    Element mElement_HALF_2;
+    Element mElement_HALF_3;
+    Element mElement_HALF_4;
+
     Element mElement_FLOAT_2;
     Element mElement_FLOAT_3;
     Element mElement_FLOAT_4;
@@ -1042,8 +1047,10 @@
      * their priority to LOW to avoid starving forground processes.
      */
     public enum Priority {
-        LOW (Process.THREAD_PRIORITY_BACKGROUND + (5 * Process.THREAD_PRIORITY_LESS_FAVORABLE)),
-        NORMAL (Process.THREAD_PRIORITY_DISPLAY);
+        // These values used to represent official thread priority values
+        // now they are simply enums to be used by the runtime side
+        LOW (15),
+        NORMAL (-8);
 
         int mID;
         Priority(int id) {
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index dcad787..366039e 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -8,9 +8,6 @@
 import java.util.Map;
 
 /**
-   @hide Pending Android public API approval.
- */
-/**
 
 ******************************
 You have tried to change the API from what has been previously approved.
@@ -240,12 +237,10 @@
     for (int i = 0; i < closureIDs.length; i++) {
       closureIDs[i] = closures.get(i).getID(rs);
     }
-    long id = rs.nScriptGroup2Create(closureIDs);
+    long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
     setID(id);
   }
 
-  // TODO: If this was reflected method, we could enforce the number of
-  // arguments.
   public Object[] execute(Object... inputs) {
     if (inputs.length < mInputs.size()) {
       Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
@@ -309,8 +304,6 @@
     }
 
     public ScriptGroup2 create(Future... outputs) {
-      // TODO: Save all script groups that have been created and return one that was
-      // saved and matches the outputs.
       ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
       return ret;
     }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index dced99a..cbcba39 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -258,7 +258,9 @@
 
 static long
 nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
-                    jlongArray closureArray) {
+                    jstring cacheDir, jlongArray closureArray) {
+  AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
+
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
   jsize numClosures = _env->GetArrayLength(closureArray);
   RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
@@ -266,8 +268,9 @@
     closures[i] = (RsClosure)jClosures[i];
   }
 
-  return (jlong)(uintptr_t)rsScriptGroup2Create((RsContext)con, closures,
-                                                numClosures);
+  return (jlong)(uintptr_t)rsScriptGroup2Create(
+      (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+      closures, numClosures);
 }
 
 static void
@@ -1382,6 +1385,14 @@
         sc.zStart     = limit_ptr[4];
         sc.zEnd       = limit_ptr[5];
         sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
+        sc.arrayStart = 0;
+        sc.arrayEnd = 0;
+        sc.array2Start = 0;
+        sc.array2End = 0;
+        sc.array3Start = 0;
+        sc.array3End = 0;
+        sc.array4Start = 0;
+        sc.array4End = 0;
 
         sca = &sc;
     }
@@ -2000,7 +2011,7 @@
 {"rsnScriptKernelIDCreate",          "(JJII)J",                               (void*)nScriptKernelIDCreate },
 {"rsnScriptFieldIDCreate",           "(JJI)J",                                (void*)nScriptFieldIDCreate },
 {"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create",            "(J[J)J",                                (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create",            "(JLjava/lang/String;[J)J",               (void*)nScriptGroup2Create },
 {"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
 {"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
 {"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 79cd867..49177fc 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -859,6 +859,10 @@
 
         // Temporary workaround for http://b/17945169.
         Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service");
+        SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
+
+        // TODO: Stop setting these properties once we've removed all
+        // references to them.
         SystemProperties.set("persist.sys.language", locale.getLanguage());
         SystemProperties.set("persist.sys.country", locale.getCountry());
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 668d62b..f7d6e8a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16400,15 +16400,27 @@
     }
 
     /**
-     * Save the locale.  You must be inside a synchronized (this) block.
+     * Save the locale. You must be inside a synchronized (this) block.
      */
     private void saveLocaleLocked(Locale l, boolean isDiff, boolean isPersist) {
-        if(isDiff) {
+        final String languageTag = l.toLanguageTag();
+        if (isDiff) {
+            SystemProperties.set("user.locale", languageTag);
+
+            // TODO: Who uses these ? There are no references to these system
+            // properties in documents or code. Did the author intend to call
+            // System.setProperty() instead ? Even that wouldn't have any effect.
             SystemProperties.set("user.language", l.getLanguage());
             SystemProperties.set("user.region", l.getCountry());
-        } 
+        }
 
-        if(isPersist) {
+        if (isPersist) {
+            SystemProperties.set("persist.sys.locale", languageTag);
+
+            // These values are *deprecated*, use persist.sys.locale instead.
+            //
+            // TODO: Stop setting these values once all code that references
+            // them has been removed.
             SystemProperties.set("persist.sys.language", l.getLanguage());
             SystemProperties.set("persist.sys.country", l.getCountry());
             SystemProperties.set("persist.sys.localevar", l.getVariant());
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c03dbc2..152f8d7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1553,14 +1553,14 @@
                 // Now the task above it has to return to the home task instead.
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
                 mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-            } else {
-                if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
+            } else if (!isOnHomeDisplay()) {
+                return false;
+            } else if (!isHomeStack()){
+                if (DEBUG_STATES) Slog.d(TAG,
                         "resumeTopActivityLocked: Launching home next");
-                // Only resume home if on home display
                 final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
                         HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
-                return isOnHomeDisplay() &&
-                        mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+                return mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
             }
         }
 
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 30799f8..d002d9b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2285,9 +2285,13 @@
     }
 
     private static String getCurrentIdp(boolean useNanp) {
-        // in case, there is no IDD is found, we shouldn't convert it.
-        String ps = SystemProperties.get(
-                PROPERTY_OPERATOR_IDP_STRING, useNanp ? NANP_IDP_STRING : PLUS_SIGN_STRING);
+        String ps = null;
+        if (useNanp) {
+            ps = NANP_IDP_STRING;
+        } else {
+            // in case, there is no IDD is found, we shouldn't convert it.
+            ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
+        }
         return ps;
     }
 
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 9ff56d6..78d7253 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -39,7 +39,6 @@
 LOCAL_CFLAGS := -Wall -Werror
 LOCAL_SRC_FILES := pbkdf2gen.cpp
 LOCAL_LDLIBS += -ldl
-LOCAL_C_INCLUDES := external/openssl/include $(LOCAL_C_INCLUDES)
 LOCAL_STATIC_LIBRARIES := libcrypto_static
 
 include $(BUILD_HOST_EXECUTABLE)