Merge "Simplify the code in the stagefright commandline utility." into kraken
diff --git a/api/current.xml b/api/current.xml
index d002534..9930f44 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -77980,7 +77980,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.0010f"
+ value="0.001f"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -124950,6 +124950,116 @@
 </parameter>
 </method>
 </class>
+<class name="StrictMode"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getThreadBlockingPolicy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setThreadBlockingPolicy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="policyMask" type="int">
+</parameter>
+</method>
+<field name="DISALLOW_DISK_READ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISALLOW_DISK_WRITE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISALLOW_NETWORK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PENALTY_DEATH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PENALTY_DIALOG"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PENALTY_DROPBOX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PENALTY_LOG"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="SystemClock"
  extends="java.lang.Object"
  abstract="false"
@@ -218779,7 +218889,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 4f684b7..690169a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -371,10 +371,7 @@
 status_t CameraService::Client::checkPid() const {
     int callingPid = getCallingPid();
     if (callingPid == mClientPid) return NO_ERROR;
-    if (callingPid == getpid()) {
-        LOGW("FIXME: use camera from mediaserver without permission.");
-        return NO_ERROR;
-    }
+
     LOGW("attempt to use a locked camera from a different process"
          " (old pid %d, new pid %d)", mClientPid, callingPid);
     return EBUSY;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b78d22f..b4c7edc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1062,6 +1062,15 @@
             return true;
         }
 
+        case HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder app = data.readStrongBinder();
+            ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
+            handleApplicationStrictModeViolation(app, ci);
+            reply.writeNoException();
+            return true;
+        }
+
         case SIGNAL_PERSISTENT_PROCESSES_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int sig = data.readInt();
@@ -2523,6 +2532,7 @@
         reply.recycle();
         data.recycle();
     }
+
     public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
     {
@@ -2540,6 +2550,20 @@
         return res;
     }
 
+    public void handleApplicationStrictModeViolation(IBinder app,
+            ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(app);
+        crashInfo.writeToParcel(data, 0);
+        mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+
     public void signalPersistentProcesses(int sig) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 773c344..49f1a8f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -42,6 +42,7 @@
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -53,6 +54,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.AndroidRuntimeException;
 import android.util.Config;
@@ -4132,6 +4134,20 @@
         data.info = getPackageInfoNoCheck(data.appInfo);
 
         /**
+         * For system applications on userdebug/eng builds, log stack
+         * traces of disk and network access to dropbox for analysis.
+         */
+        if ((data.appInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 &&
+            !"user".equals(Build.TYPE)) {
+            StrictMode.setDropBoxManager(ContextImpl.createDropBoxManager());
+            StrictMode.setThreadBlockingPolicy(
+                StrictMode.DISALLOW_DISK_WRITE |
+                StrictMode.DISALLOW_DISK_READ |
+                StrictMode.DISALLOW_NETWORK |
+                StrictMode.PENALTY_DROPBOX);
+        }
+
+        /**
          * Switch this process to density compatibility mode if needed.
          */
         if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 11b7b02..bcdfe59 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1157,12 +1157,16 @@
         return mAudioManager;
     }
 
+    /* package */ static DropBoxManager createDropBoxManager() {
+        IBinder b = ServiceManager.getService(DROPBOX_SERVICE);
+        IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
+        return new DropBoxManager(service);
+    }
+
     private DropBoxManager getDropBoxManager() {
         synchronized (mSync) {
             if (mDropBoxManager == null) {
-                IBinder b = ServiceManager.getService(DROPBOX_SERVICE);
-                IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
-                mDropBoxManager = new DropBoxManager(service);
+                mDropBoxManager = createDropBoxManager();
             }
         }
         return mDropBoxManager;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cd24fa6..ca09290 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -256,7 +256,9 @@
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
     public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
-    
+    public void handleApplicationStrictModeViolation(IBinder app,
+            ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
+
     /*
      * This will deliver the specified signal to all the persistent processes. Currently only 
      * SIGUSR1 is delivered. All others are ignored.
@@ -516,4 +518,5 @@
     int START_ACTIVITY_WITH_CONFIG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+106;
     int GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+107;
     int FINISH_HEAVY_WEIGHT_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+108;
+    int HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+109;
 }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
new file mode 100644
index 0000000..876ec39
--- /dev/null
+++ b/core/java/android/os/StrictMode.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.app.ActivityManagerNative;
+import android.app.ApplicationErrorReport;
+import android.util.Log;
+
+import com.android.internal.os.RuntimeInit;
+
+import dalvik.system.BlockGuard;
+
+/**
+ * <p>StrictMode lets you impose stricter rules under which your
+ * application runs.</p>
+ */
+public final class StrictMode {
+    private static final String TAG = "StrictMode";
+
+    private StrictMode() {}
+
+    public static final int DISALLOW_DISK_WRITE = 0x01;
+    public static final int DISALLOW_DISK_READ = 0x02;
+    public static final int DISALLOW_NETWORK = 0x04;
+
+    /** @hide */
+    public static final int DISALLOW_MASK =
+            DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
+
+    /**
+     * Flag to log to the system log.
+     */
+    public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
+
+    /**
+     * Show an annoying dialog to the user.  Will be rate-limited to be only
+     * a little annoying.
+     */
+    public static final int PENALTY_DIALOG = 0x20;
+
+    /**
+     * Crash hard if policy is violated.
+     */
+    public static final int PENALTY_DEATH = 0x40;
+
+    /**
+     * Log a stacktrace to the DropBox on policy violation.
+     */
+    public static final int PENALTY_DROPBOX = 0x80;
+
+    /** @hide */
+    public static final int PENALTY_MASK =
+            PENALTY_LOG | PENALTY_DIALOG |
+            PENALTY_DROPBOX | PENALTY_DEATH;
+
+    /**
+     * Sets the policy for what actions the current thread is denied,
+     * as well as the penalty for violating the policy.
+     *
+     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
+     */
+    public static void setThreadBlockingPolicy(final int policyMask) {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
+        } else {
+            AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
+            androidPolicy.setPolicyMask(policyMask);
+        }
+    }
+
+    /**
+     * Returns the bitmask of the current thread's blocking policy.
+     *
+     * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
+     */
+    public static int getThreadBlockingPolicy() {
+        return BlockGuard.getThreadPolicy().getPolicyMask();
+    }
+
+    /** @hide */
+    public static void setDropBoxManager(DropBoxManager dropBoxManager) {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            policy = new AndroidBlockGuardPolicy(0);
+            BlockGuard.setThreadPolicy(policy);
+        }
+        ((AndroidBlockGuardPolicy) policy).setDropBoxManager(dropBoxManager);
+    }
+
+    private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
+        private int mPolicyMask;
+        private DropBoxManager mDropBoxManager = null;
+
+        public AndroidBlockGuardPolicy(final int policyMask) {
+            mPolicyMask = policyMask;
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public int getPolicyMask() {
+            return mPolicyMask;
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onWriteToDisk() {
+            if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
+                return;
+            }
+            handleViolation(DISALLOW_DISK_WRITE);
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onReadFromDisk() {
+            if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
+                return;
+            }
+            handleViolation(DISALLOW_DISK_READ);
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onNetwork() {
+            if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
+                return;
+            }
+            handleViolation(DISALLOW_NETWORK);
+        }
+
+        public void setPolicyMask(int policyMask) {
+            mPolicyMask = policyMask;
+        }
+
+        public void setDropBoxManager(DropBoxManager dropBoxManager) {
+            mDropBoxManager = dropBoxManager;
+        }
+
+        private void handleViolation(int violationBit) {
+            final BlockGuard.BlockGuardPolicyException violation =
+                    new BlockGuard.BlockGuardPolicyException(mPolicyMask, violationBit);
+            violation.fillInStackTrace();
+
+            Looper looper = Looper.myLooper();
+            if (looper == null) {
+                // Without a Looper, we're unable to time how long the
+                // violation takes place.  This case should be rare,
+                // as most users will care about timing violations
+                // that happen on their main UI thread.
+                handleViolationWithTime(violation, -1L /* no time */);
+            } else {
+                MessageQueue queue = Looper.myQueue();
+                final long violationTime = SystemClock.uptimeMillis();
+                queue.addIdleHandler(new MessageQueue.IdleHandler() {
+                        public boolean queueIdle() {
+                            long afterViolationTime = SystemClock.uptimeMillis();
+                            handleViolationWithTime(violation, afterViolationTime - violationTime);
+                            return false;  // remove this idle handler from the array
+                        }
+                    });
+            }
+        }
+
+        private void handleViolationWithTime(
+            BlockGuard.BlockGuardPolicyException violation,
+            long durationMillis) {
+
+            // It's possible (even quite likely) that mPolicyMask has
+            // changed from the time the violation fired and now
+            // (after the violating code ran) due to people who
+            // push/pop temporary policy in regions of code.  So use
+            // the old policy here.
+            int policy = violation.getPolicy();
+
+            if ((policy & PENALTY_LOG) != 0) {
+                if (durationMillis != -1) {
+                    Log.d(TAG, "StrictMode policy violation; ~duration=" + durationMillis + " ms",
+                          violation);
+                } else {
+                    Log.d(TAG, "StrictMode policy violation.", violation);
+                }
+            }
+
+            if ((policy & PENALTY_DIALOG) != 0) {
+                // Currently this is just used for the dialog.
+                try {
+                    ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
+                        RuntimeInit.getApplicationObject(),
+                        new ApplicationErrorReport.CrashInfo(violation));
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException trying to open strict mode dialog", e);
+                }
+            }
+
+            if ((policy & PENALTY_DROPBOX) != 0) {
+                // TODO: call into ActivityManagerNative to do the dropboxing.
+                // But do the first-layer signature dup-checking first client-side.
+                // This conditional should be combined with the above, too, along
+                // with PENALTY_DEATH below.
+            }
+
+            if ((policy & PENALTY_DEATH) != 0) {
+                System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
+                Process.killProcess(Process.myPid());
+                System.exit(10);
+            }
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ac23832..bf751f5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -238,7 +238,7 @@
  *
  * <p>The screen density of a device is based on the screen resolution. A screen with low density
  * has fewer available pixels per inch, where a screen with high density
- * has more — sometimes significantly more — pixels per inch. The density of a
+ * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
  * screen is important because, other things being equal, a UI element (such as a button) whose
  * height and width are defined in terms of screen pixels will appear larger on the lower density
  * screen and smaller on the higher density screen.
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 599a7fe..59600dc 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -342,6 +342,10 @@
         mApplicationObject = app;
     }
 
+    public static final IBinder getApplicationObject() {
+        return mApplicationObject;
+    }
+
     /**
      * Enable debugging features.
      */
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fc1db18..3c1fdbc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1917,7 +1917,7 @@
     <string name="noApplications">No applications can perform this action.</string>
     <!-- Title of the alert when an application has crashed. -->
     <string name="aerr_title">Sorry!</string>
-    <!-- Text of the alert that is displayed when an application is not responding. -->
+    <!-- Text of the alert that is displayed when an application has crashed. -->
     <string name="aerr_application">The application <xliff:g id="application">%1$s</xliff:g>
         (process <xliff:g id="process">%2$s</xliff:g>) has stopped unexpectedly. Please try again.</string>
     <!-- Text of the alert that is displayed when an application has crashed. -->
@@ -1940,6 +1940,13 @@
     <!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
     <string name="wait">Wait</string>
 
+    <!-- Text of the alert that is displayed when an application has violated StrictMode. -->
+    <string name="smv_application">The application <xliff:g id="application">%1$s</xliff:g>
+        (process <xliff:g id="process">%2$s</xliff:g>) has violated its self-enforced StrictMode policy.</string>
+    <!-- Text of the alert that is displayed when an application has violated StrictMode. -->
+    <string name="smv_process">The process <xliff:g id="process">%1$s</xliff:g> has
+      has violated its self-enforced StrictMode policy.</string>
+
     <!-- Notification text to tell the user that a heavy-weight application is running. -->
     <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
     
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index 96d57e7..9cc94c8 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -20,6 +20,7 @@
 
 #include <sys/types.h>
 
+#include <media/stagefright/MediaErrors.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -83,6 +84,13 @@
         int64_t mLatenessUs;
     };
 
+    // Causes this source to suspend pulling data from its upstream source
+    // until a subsequent read-with-seek. Currently only supported by
+    // OMXCodec.
+    virtual status_t pause() {
+        return ERROR_UNSUPPORTED;
+    }
+
 protected:
     virtual ~MediaSource();
 
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 3fbb469..c95fc02 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -52,6 +52,8 @@
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
+    virtual status_t pause();
+
     void on_message(const omx_message &msg);
 
     // from MediaBufferObserver
@@ -144,6 +146,8 @@
     Mutex mLock;
     Condition mAsyncCompletion;
 
+    bool mPaused;
+
     // A list of indices into mPortStatus[kPortIndexOutput] filled with data.
     List<size_t> mFilledBuffers;
     Condition mBufferFilled;
diff --git a/include/media/stagefright/foundation/AHandlerReflector.h b/include/media/stagefright/foundation/AHandlerReflector.h
new file mode 100644
index 0000000..857866a
--- /dev/null
+++ b/include/media/stagefright/foundation/AHandlerReflector.h
@@ -0,0 +1,32 @@
+#ifndef A_HANDLER_REFLECTOR_H_
+
+#define A_HANDLER_REFLECTOR_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+template<class T>
+struct AHandlerReflector : public AHandler {
+    AHandlerReflector(T *target)
+        : mTarget(target) {
+    }
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg) {
+        sp<T> target = mTarget.promote();
+        if (target != NULL) {
+            target->onMessageReceived(msg);
+        }
+    }
+
+private:
+    wp<T> mTarget;
+
+    AHandlerReflector(const AHandlerReflector<T> &);
+    AHandlerReflector<T> &operator=(const AHandlerReflector<T> &);
+};
+
+}  // namespace android
+
+#endif  // A_HANDLER_REFLECTOR_H_
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 636cd83..82776f4 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -301,8 +301,9 @@
 
 status_t String8::setTo(const char* other)
 {
+    const char *newString = allocFromUTF8(other, strlen(other));
     SharedBuffer::bufferFromData(mString)->release();
-    mString = allocFromUTF8(other, strlen(other));
+    mString = newString;
     if (mString) return NO_ERROR;
 
     mString = getEmptyString();
@@ -311,8 +312,9 @@
 
 status_t String8::setTo(const char* other, size_t len)
 {
+    const char *newString = allocFromUTF8(other, len);
     SharedBuffer::bufferFromData(mString)->release();
-    mString = allocFromUTF8(other, len);
+    mString = newString;
     if (mString) return NO_ERROR;
 
     mString = getEmptyString();
@@ -321,8 +323,9 @@
 
 status_t String8::setTo(const char16_t* other, size_t len)
 {
+    const char *newString = allocFromUTF16(other, len);
     SharedBuffer::bufferFromData(mString)->release();
-    mString = allocFromUTF16(other, len);
+    mString = newString;
     if (mString) return NO_ERROR;
 
     mString = getEmptyString();
@@ -331,8 +334,9 @@
 
 status_t String8::setTo(const char32_t* other, size_t len)
 {
+    const char *newString = allocFromUTF32(other, len);
     SharedBuffer::bufferFromData(mString)->release();
-    mString = allocFromUTF32(other, len);
+    mString = newString;
     if (mString) return NO_ERROR;
 
     mString = getEmptyString();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 1a684a9..2f3b075 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -114,25 +114,27 @@
 }
 
 status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) {
-    LOGV("setCamera: pid %d pid %d", IPCThreadState::self()->getCallingPid(), getpid());
+    LOGV("setCamera");
     if (camera == 0) {
         LOGE("camera is NULL");
         return UNKNOWN_ERROR;
     }
 
-    mFlags &= ~ FLAGS_SET_CAMERA | FLAGS_HOT_CAMERA;
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    mFlags &= ~FLAGS_HOT_CAMERA;
     mCamera = Camera::create(camera);
     if (mCamera == 0) {
         LOGE("Unable to connect to camera");
+        IPCThreadState::self()->restoreCallingIdentity(token);
         return UNKNOWN_ERROR;
     }
 
     LOGV("Connected to camera");
-    mFlags |= FLAGS_SET_CAMERA;
     if (mCamera->previewEnabled()) {
         LOGV("camera is hot");
         mFlags |= FLAGS_HOT_CAMERA;
     }
+    IPCThreadState::self()->restoreCallingIdentity(token);
 
     return OK;
 }
@@ -584,7 +586,12 @@
     }
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
-        CHECK(mCamera != NULL);
+
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
+        if (mCamera == 0) {
+            mCamera = Camera::connect(0);
+            mCamera->lock();
+        }
 
         // Set the actual video recording frame size
         CameraParameters params(mCamera->getParameters());
@@ -601,6 +608,7 @@
             frameHeight < 0 || frameHeight != mVideoHeight) {
             LOGE("Failed to set the video frame size to %dx%d",
                     mVideoWidth, mVideoHeight);
+            IPCThreadState::self()->restoreCallingIdentity(token);
             return UNKNOWN_ERROR;
         }
 
@@ -612,6 +620,7 @@
         }
 
         CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
+        IPCThreadState::self()->restoreCallingIdentity(token);
 
         sp<CameraSource> cameraSource =
             CameraSource::CreateFromCamera(mCamera);
@@ -698,14 +707,14 @@
     stop();
 
     if (mCamera != 0) {
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
         if ((mFlags & FLAGS_HOT_CAMERA) == 0) {
             LOGV("Camera was cold when we started, stopping preview");
             mCamera->stopPreview();
         }
-        if (mFlags & FLAGS_SET_CAMERA) {
-            LOGV("Unlocking camera");
-            mCamera->unlock();
-        }
+        mCamera->unlock();
+        mCamera = NULL;
+        IPCThreadState::self()->restoreCallingIdentity(token);
         mFlags = 0;
     }
     return OK;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 67759c0..0ab76b3 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -19,7 +19,7 @@
 #include <utils/Log.h>
 
 #include <OMX_Component.h>
-
+#include <binder/IPCThreadState.h>
 #include <media/stagefright/CameraSource.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
@@ -125,7 +125,11 @@
       mNumFramesDropped(0),
       mCollectStats(false),
       mStarted(false) {
+
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     String8 s = mCamera->getParameters();
+    IPCThreadState::self()->restoreCallingIdentity(token);
+
     printf("params: \"%s\"\n", s.string());
 
     int32_t width, height, stride, sliceHeight;
@@ -166,8 +170,11 @@
         && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
         mCollectStats = true;
     }
+
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     mCamera->setListener(new CameraSourceListener(this));
     CHECK_EQ(OK, mCamera->startRecording());
+    IPCThreadState::self()->restoreCallingIdentity(token);
 
     mStarted = true;
     return OK;
@@ -179,16 +186,17 @@
     mStarted = false;
     mFrameAvailableCondition.signal();
 
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     mCamera->setListener(NULL);
     mCamera->stopRecording();
-
     releaseQueuedFrames();
-
     while (!mFramesBeingEncoded.empty()) {
         LOGI("Waiting for outstanding frames being encoded: %d",
                 mFramesBeingEncoded.size());
         mFrameCompleteCondition.wait(mLock);
     }
+    mCamera = NULL;
+    IPCThreadState::self()->restoreCallingIdentity(token);
 
     if (mCollectStats) {
         LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
@@ -219,7 +227,11 @@
     for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
          it != mFramesBeingEncoded.end(); ++it) {
         if ((*it)->pointer() ==  buffer->data()) {
+
+            int64_t token = IPCThreadState::self()->clearCallingIdentity();
             mCamera->releaseRecordingFrame((*it));
+            IPCThreadState::self()->restoreCallingIdentity(token);
+
             mFramesBeingEncoded.erase(it);
             ++mNumFramesEncoded;
             buffer->setObserver(0);
@@ -273,7 +285,9 @@
     LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
     Mutex::Autolock autoLock(mLock);
     if (!mStarted) {
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
         mCamera->releaseRecordingFrame(data);
+        IPCThreadState::self()->restoreCallingIdentity(token);
         ++mNumFramesReceived;
         ++mNumFramesDropped;
         return;
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
new file mode 100644
index 0000000..63ba000
--- /dev/null
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NuCachedSource2"
+#include <utils/Log.h>
+
+#include "include/NuCachedSource2.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+struct PageCache {
+    PageCache(size_t pageSize);
+    ~PageCache();
+
+    struct Page {
+        void *mData;
+        size_t mSize;
+    };
+
+    Page *acquirePage();
+    void releasePage(Page *page);
+
+    void appendPage(Page *page);
+    size_t releaseFromStart(size_t maxBytes);
+
+    size_t totalSize() const {
+        return mTotalSize;
+    }
+
+    void copy(size_t from, void *data, size_t size);
+
+private:
+    size_t mPageSize;
+    size_t mTotalSize;
+
+    List<Page *> mActivePages;
+    List<Page *> mFreePages;
+
+    void freePages(List<Page *> *list);
+
+    DISALLOW_EVIL_CONSTRUCTORS(PageCache);
+};
+
+PageCache::PageCache(size_t pageSize)
+    : mPageSize(pageSize),
+      mTotalSize(0) {
+}
+
+PageCache::~PageCache() {
+    freePages(&mActivePages);
+    freePages(&mFreePages);
+}
+
+void PageCache::freePages(List<Page *> *list) {
+    List<Page *>::iterator it = list->begin();
+    while (it != list->end()) {
+        Page *page = *it;
+
+        free(page->mData);
+        delete page;
+        page = NULL;
+
+        ++it;
+    }
+}
+
+PageCache::Page *PageCache::acquirePage() {
+    if (!mFreePages.empty()) {
+        List<Page *>::iterator it = mFreePages.begin();
+        Page *page = *it;
+        mFreePages.erase(it);
+
+        return page;
+    }
+
+    Page *page = new Page;
+    page->mData = malloc(mPageSize);
+    page->mSize = 0;
+
+    return page;
+}
+
+void PageCache::releasePage(Page *page) {
+    page->mSize = 0;
+    mFreePages.push_back(page);
+}
+
+void PageCache::appendPage(Page *page) {
+    mTotalSize += page->mSize;
+    mActivePages.push_back(page);
+}
+
+size_t PageCache::releaseFromStart(size_t maxBytes) {
+    size_t bytesReleased = 0;
+
+    while (maxBytes > 0 && !mActivePages.empty()) {
+        List<Page *>::iterator it = mActivePages.begin();
+
+        Page *page = *it;
+
+        if (maxBytes < page->mSize) {
+            break;
+        }
+
+        mActivePages.erase(it);
+
+        maxBytes -= page->mSize;
+        bytesReleased += page->mSize;
+
+        releasePage(page);
+    }
+
+    mTotalSize -= bytesReleased;
+    return bytesReleased;
+}
+
+void PageCache::copy(size_t from, void *data, size_t size) {
+    LOG(VERBOSE) << "copy from " << from << " size " << size;
+
+    CHECK_LE(from + size, mTotalSize);
+
+    size_t offset = 0;
+    List<Page *>::iterator it = mActivePages.begin();
+    while (from >= offset + (*it)->mSize) {
+        offset += (*it)->mSize;
+        ++it;
+    }
+
+    size_t delta = from - offset;
+    size_t avail = (*it)->mSize - delta;
+
+    if (avail >= size) {
+        memcpy(data, (const uint8_t *)(*it)->mData + delta, size);
+        return;
+    }
+
+    memcpy(data, (const uint8_t *)(*it)->mData + delta, avail);
+    ++it;
+    data = (uint8_t *)data + avail;
+    size -= avail;
+
+    while (size > 0) {
+        size_t copy = (*it)->mSize;
+        if (copy > size) {
+            copy = size;
+        }
+        memcpy(data, (*it)->mData, copy);
+        data = (uint8_t *)data + copy;
+        size -= copy;
+        ++it;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuCachedSource2::NuCachedSource2(const sp<DataSource> &source)
+    : mSource(source),
+      mReflector(new AHandlerReflector<NuCachedSource2>(this)),
+      mLooper(new ALooper),
+      mCache(new PageCache(kPageSize)),
+      mCacheOffset(0),
+      mFinalStatus(OK),
+      mLastAccessPos(0),
+      mFetching(true) {
+    mLooper->registerHandler(mReflector);
+    mLooper->start();
+
+    Mutex::Autolock autoLock(mLock);
+    (new AMessage(kWhatFetchMore, mReflector->id()))->post();
+}
+
+NuCachedSource2::~NuCachedSource2() {
+    mLooper->stop();
+    mLooper->unregisterHandler(mReflector->id());
+
+    delete mCache;
+    mCache = NULL;
+}
+
+status_t NuCachedSource2::initCheck() const {
+    return mSource->initCheck();
+}
+
+status_t NuCachedSource2::getSize(off_t *size) {
+    return mSource->getSize(size);
+}
+
+uint32_t NuCachedSource2::flags() {
+    return mSource->flags();
+}
+
+void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatFetchMore:
+        {
+            onFetch();
+            break;
+        }
+
+        case kWhatRead:
+        {
+            onRead(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void NuCachedSource2::fetchInternal() {
+    LOG(VERBOSE) << "fetchInternal";
+
+    CHECK_EQ(mFinalStatus, (status_t)OK);
+
+    PageCache::Page *page = mCache->acquirePage();
+
+    ssize_t n = mSource->readAt(
+            mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (n < 0) {
+        LOG(ERROR) << "source returned error " << n;
+        mFinalStatus = n;
+        mCache->releasePage(page);
+    } else if (n == 0) {
+        LOG(INFO) << "ERROR_END_OF_STREAM";
+        mFinalStatus = ERROR_END_OF_STREAM;
+        mCache->releasePage(page);
+    } else {
+        page->mSize = n;
+        mCache->appendPage(page);
+    }
+}
+
+void NuCachedSource2::onFetch() {
+    LOG(VERBOSE) << "onFetch";
+
+    if (mFinalStatus != OK) {
+        LOG(VERBOSE) << "EOS reached, done prefetching for now";
+        mFetching = false;
+    }
+
+    if (mFetching) {
+        fetchInternal();
+
+        if (mCache->totalSize() >= kHighWaterThreshold) {
+            LOG(INFO) << "Cache full, done prefetching for now";
+            mFetching = false;
+        }
+    } else {
+        restartPrefetcherIfNecessary_l();
+    }
+
+    (new AMessage(kWhatFetchMore, mReflector->id()))->post(
+            mFetching ? 0 : 100000ll);
+}
+
+void NuCachedSource2::onRead(const sp<AMessage> &msg) {
+    LOG(VERBOSE) << "onRead";
+
+    int64_t offset;
+    CHECK(msg->findInt64("offset", &offset));
+
+    void *data;
+    CHECK(msg->findPointer("data", &data));
+
+    size_t size;
+    CHECK(msg->findSize("size", &size));
+
+    ssize_t result = readInternal(offset, data, size);
+
+    if (result == -EAGAIN) {
+        msg->post(50000);
+        return;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mAsyncResult == NULL);
+
+    mAsyncResult = new AMessage;
+    mAsyncResult->setInt32("result", result);
+
+    mCondition.signal();
+}
+
+void NuCachedSource2::restartPrefetcherIfNecessary_l() {
+    static const size_t kGrayArea = 256 * 1024;
+
+    if (mFetching || mFinalStatus != OK) {
+        return;
+    }
+
+    if (mCacheOffset + mCache->totalSize() - mLastAccessPos
+            >= kLowWaterThreshold) {
+        return;
+    }
+
+    size_t maxBytes = mLastAccessPos - mCacheOffset;
+    if (maxBytes < kGrayArea) {
+        return;
+    }
+
+    maxBytes -= kGrayArea;
+
+    size_t actualBytes = mCache->releaseFromStart(maxBytes);
+    mCacheOffset += actualBytes;
+
+    LOG(INFO) << "restarting prefetcher, totalSize = " << mCache->totalSize();
+    mFetching = true;
+}
+
+ssize_t NuCachedSource2::readAt(off_t offset, void *data, size_t size) {
+    Mutex::Autolock autoSerializer(mSerializer);
+
+    LOG(VERBOSE) << "readAt offset " << offset << " size " << size;
+
+    Mutex::Autolock autoLock(mLock);
+
+    // If the request can be completely satisfied from the cache, do so.
+
+    if (offset >= mCacheOffset
+            && offset + size <= mCacheOffset + mCache->totalSize()) {
+        size_t delta = offset - mCacheOffset;
+        mCache->copy(delta, data, size);
+
+        mLastAccessPos = offset + size;
+
+        return size;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatRead, mReflector->id());
+    msg->setInt64("offset", offset);
+    msg->setPointer("data", data);
+    msg->setSize("size", size);
+
+    CHECK(mAsyncResult == NULL);
+    msg->post();
+
+    while (mAsyncResult == NULL) {
+        mCondition.wait(mLock);
+    }
+
+    int32_t result;
+    CHECK(mAsyncResult->findInt32("result", &result));
+
+    mAsyncResult.clear();
+
+    if (result > 0) {
+        mLastAccessPos = offset + result;
+    }
+
+    return (ssize_t)result;
+}
+
+size_t NuCachedSource2::cachedSize() {
+    Mutex::Autolock autoLock(mLock);
+    return mCacheOffset + mCache->totalSize();
+}
+
+size_t NuCachedSource2::approxDataRemaining(bool *eos) {
+    Mutex::Autolock autoLock(mLock);
+    return approxDataRemaining_l(eos);
+}
+
+size_t NuCachedSource2::approxDataRemaining_l(bool *eos) {
+    *eos = (mFinalStatus != OK);
+    off_t lastBytePosCached = mCacheOffset + mCache->totalSize();
+    if (mLastAccessPos < lastBytePosCached) {
+        return lastBytePosCached - mLastAccessPos;
+    }
+    return 0;
+}
+
+ssize_t NuCachedSource2::readInternal(off_t offset, void *data, size_t size) {
+    LOG(VERBOSE) << "readInternal offset " << offset << " size " << size;
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset < mCacheOffset
+            || offset >= (off_t)(mCacheOffset + mCache->totalSize())) {
+        static const off_t kPadding = 32768;
+
+        // In the presence of multiple decoded streams, once of them will
+        // trigger this seek request, the other one will request data "nearby"
+        // soon, adjust the seek position so that that subsequent request
+        // does not trigger another seek.
+        off_t seekOffset = (offset > kPadding) ? offset - kPadding : 0;
+
+        seekInternal_l(seekOffset);
+    }
+
+    size_t delta = offset - mCacheOffset;
+
+    if (mFinalStatus != OK) {
+        if (delta >= mCache->totalSize()) {
+            return mFinalStatus;
+        }
+
+        size_t avail = mCache->totalSize() - offset;
+        mCache->copy(delta, data, avail);
+
+        return avail;
+    }
+
+    if (offset + size <= mCacheOffset + mCache->totalSize()) {
+        mCache->copy(delta, data, size);
+
+        return size;
+    }
+
+    LOG(VERBOSE) << "deferring read";
+
+    return -EAGAIN;
+}
+
+status_t NuCachedSource2::seekInternal_l(off_t offset) {
+    mLastAccessPos = offset;
+
+    if (offset >= mCacheOffset
+            && offset <= (off_t)(mCacheOffset + mCache->totalSize())) {
+        return OK;
+    }
+
+    LOG(INFO) << "new range: offset= " << offset;
+
+    mCacheOffset = offset;
+
+    size_t totalSize = mCache->totalSize();
+    CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
+
+    mFinalStatus = OK;
+    mFetching = true;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
new file mode 100644
index 0000000..8587c1b
--- /dev/null
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -0,0 +1,265 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuHTTPDataSource"
+#include <utils/Log.h>
+
+#include "include/NuHTTPDataSource.h"
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+static bool ParseSingleUnsignedLong(
+        const char *from, unsigned long *x) {
+    char *end;
+    *x = strtoul(from, &end, 10);
+
+    if (end == from || *end != '\0') {
+        return false;
+    }
+
+    return true;
+}
+
+static bool ParseURL(
+        const char *url, String8 *host, unsigned *port, String8 *path) {
+    host->setTo("");
+    *port = 0;
+    path->setTo("");
+
+    if (strncasecmp("http://", url, 7)) {
+        return false;
+    }
+
+    const char *slashPos = strchr(&url[7], '/');
+
+    if (slashPos == NULL) {
+        host->setTo(&url[7]);
+        path->setTo("/");
+    } else {
+        host->setTo(&url[7], slashPos - &url[7]);
+        path->setTo(slashPos);
+    }
+
+    char *colonPos = strchr(host->string(), ':');
+
+    if (colonPos != NULL) {
+        unsigned long x;
+        if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) {
+            return false;
+        }
+
+        *port = x;
+
+        size_t colonOffset = colonPos - host->string();
+        String8 tmp(host->string(), colonOffset);
+        *host = tmp;
+    } else {
+        *port = 80;
+    }
+
+    return true;
+}
+
+NuHTTPDataSource::NuHTTPDataSource()
+    : mState(DISCONNECTED),
+      mPort(0),
+      mOffset(0),
+      mContentLength(0),
+      mContentLengthValid(false) {
+}
+
+NuHTTPDataSource::~NuHTTPDataSource() {
+}
+
+status_t NuHTTPDataSource::connect(const char *uri, off_t offset) {
+    String8 host, path;
+    unsigned port;
+    if (!ParseURL(uri, &host, &port, &path)) {
+        return ERROR_MALFORMED;
+    }
+
+    return connect(host, port, path, offset);
+}
+
+status_t NuHTTPDataSource::connect(
+        const char *host, unsigned port, const char *path, off_t offset) {
+    LOGI("connect to %s:%u%s @%ld", host, port, path, offset);
+
+    bool needsToReconnect = true;
+
+    if (mState == CONNECTED && host == mHost && port == mPort
+            && offset == mOffset) {
+        if (mContentLengthValid && mOffset == mContentLength) {
+            LOGI("Didn't have to reconnect, old one's still good.");
+            needsToReconnect = false;
+        }
+    }
+
+    mHost = host;
+    mPort = port;
+    mPath = path;
+
+    status_t err = OK;
+
+    mState = CONNECTING;
+
+    if (needsToReconnect) {
+        mHTTP.disconnect();
+        err = mHTTP.connect(host, port);
+    }
+
+    if (err != OK) {
+        mState = DISCONNECTED;
+    } else if (mState != CONNECTING) {
+        err = UNKNOWN_ERROR;
+    } else {
+        mState = CONNECTED;
+
+        mOffset = offset;
+        mContentLength = 0;
+        mContentLengthValid = false;
+
+        String8 request("GET ");
+        request.append(mPath);
+        request.append(" HTTP/1.1\r\n");
+        request.append("Host: ");
+        request.append(mHost);
+        request.append("\r\n");
+
+        if (offset != 0) {
+            char rangeHeader[128];
+            sprintf(rangeHeader, "Range: bytes=%ld-\r\n", offset);
+            request.append(rangeHeader);
+        }
+
+        request.append("\r\n");
+
+        int httpStatus;
+        if ((err = mHTTP.send(request.string(), request.size())) != OK
+                || (err = mHTTP.receive_header(&httpStatus)) != OK) {
+            mHTTP.disconnect();
+            mState = DISCONNECTED;
+            return err;
+        }
+
+        if (httpStatus == 302) {
+            string value;
+            CHECK(mHTTP.find_header_value("Location", &value));
+
+            mState = DISCONNECTED;
+
+            mHTTP.disconnect();
+
+            return connect(value.c_str());
+        }
+
+        CHECK(httpStatus >= 200 && httpStatus < 300);
+
+        if (offset == 0) {
+            string value;
+            unsigned long x;
+            if (mHTTP.find_header_value(string("Content-Length"), &value)
+                    && ParseSingleUnsignedLong(value.c_str(), &x)) {
+                mContentLength = (off_t)x;
+                mContentLengthValid = true;
+            }
+        } else {
+            string value;
+            unsigned long x;
+            if (mHTTP.find_header_value(string("Content-Range"), &value)) {
+                const char *slashPos = strchr(value.c_str(), '/');
+                if (slashPos != NULL
+                        && ParseSingleUnsignedLong(slashPos + 1, &x)) {
+                    mContentLength = x;
+                    mContentLengthValid = true;
+                }
+            }
+        }
+    }
+
+    return err;
+}
+
+void NuHTTPDataSource::disconnect() {
+    if (mState == CONNECTING || mState == CONNECTED) {
+        mHTTP.disconnect();
+    }
+    mState = DISCONNECTED;
+}
+
+status_t NuHTTPDataSource::initCheck() const {
+    return mState == CONNECTED ? OK : NO_INIT;
+}
+
+ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
+    LOGV("readAt offset %ld, size %d", offset, size);
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset != mOffset) {
+        String8 host = mHost;
+        String8 path = mPath;
+        status_t err = connect(host, mPort, path, offset);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    if (mContentLengthValid) {
+        size_t avail =
+            (offset >= mContentLength) ? 0 : mContentLength - offset;
+
+        if (size > avail) {
+            size = avail;
+        }
+    }
+
+    size_t numBytesRead = 0;
+    while (numBytesRead < size) {
+        ssize_t n =
+            mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead);
+
+        if (n < 0) {
+            return n;
+        }
+
+        numBytesRead += (size_t)n;
+
+        if (n == 0) {
+            if (mContentLengthValid) {
+                // We know the content length and made sure not to read beyond
+                // it and yet the server closed the connection on us.
+                return ERROR_IO;
+            }
+
+            break;
+        }
+    }
+
+    mOffset += numBytesRead;
+
+    return numBytesRead;
+}
+
+status_t NuHTTPDataSource::getSize(off_t *size) {
+    *size = 0;
+
+    if (mState != CONNECTED) {
+        return ERROR_IO;
+    }
+
+    if (mContentLengthValid) {
+        *size = mContentLength;
+        return OK;
+    }
+
+    return ERROR_UNSUPPORTED;
+}
+
+uint32_t NuHTTPDataSource::flags() {
+    return kWantsPrefetching;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 14682af..6be41b4 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1145,7 +1145,8 @@
       mNoMoreOutputData(false),
       mOutputPortSettingsHaveChanged(false),
       mSeekTimeUs(-1),
-      mLeftOverBuffer(NULL) {
+      mLeftOverBuffer(NULL),
+      mPaused(false) {
     mPortStatus[kPortIndexInput] = ENABLED;
     mPortStatus[kPortIndexOutput] = ENABLED;
 
@@ -1735,6 +1736,9 @@
                     CODEC_LOGV("Finished flushing both ports, now continuing from"
                          " seek-time.");
 
+                    // We implicitly resume pulling on our upstream source.
+                    mPaused = false;
+
                     drainInputBuffers();
                     fillOutputBuffers();
                 }
@@ -2034,6 +2038,10 @@
         return;
     }
 
+    if (mPaused) {
+        return;
+    }
+
     status_t err;
 
     bool signalEOS = false;
@@ -2554,6 +2562,7 @@
     mOutputPortSettingsHaveChanged = false;
     mSeekTimeUs = -1;
     mFilledBuffers.clear();
+    mPaused = false;
 
     return init();
 }
@@ -2660,6 +2669,7 @@
             // There's no reason to trigger the code below, there's
             // nothing to flush yet.
             seeking = false;
+            mPaused = false;
         }
 
         drainInputBuffers();
@@ -3220,6 +3230,14 @@
     }
 }
 
+status_t OMXCodec::pause() {
+    Mutex::Autolock autoLock(mLock);
+
+    mPaused = true;
+
+    return OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 status_t QueryCodecs(
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 9e3aa7b..001afc4 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -18,12 +18,10 @@
 #include <utils/Log.h>
 
 #include "include/LiveSource.h"
-
-#include "include/HTTPStream.h"
 #include "include/M3UParser.h"
+#include "include/NuHTTPDataSource.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/MediaDebug.h>
 
 namespace android {
@@ -33,6 +31,7 @@
       mInitCheck(NO_INIT),
       mPlaylistIndex(0),
       mLastFetchTimeUs(-1),
+      mSource(new NuHTTPDataSource),
       mSourceSize(0),
       mOffsetBias(0) {
     if (switchToNext()) {
@@ -115,8 +114,7 @@
     CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri));
     LOGI("switching to %s", uri.c_str());
 
-    mSource = new HTTPDataSource(uri.c_str());
-    if (mSource->connect() != OK
+    if (mSource->connect(uri.c_str()) != OK
             || mSource->getSize(&mSourceSize) != OK) {
         return false;
     }
@@ -156,8 +154,7 @@
 status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
     *out = NULL;
 
-    mSource = new HTTPDataSource(url);
-    status_t err = mSource->connect();
+    status_t err = mSource->connect(url);
 
     if (err != OK) {
         return err;
diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h
index 3218633..c55508c 100644
--- a/media/libstagefright/include/LiveSource.h
+++ b/media/libstagefright/include/LiveSource.h
@@ -26,7 +26,7 @@
 namespace android {
 
 struct ABuffer;
-struct HTTPDataSource;
+struct NuHTTPDataSource;
 struct M3UParser;
 
 struct LiveSource : public DataSource {
@@ -52,7 +52,7 @@
     size_t mPlaylistIndex;
     int64_t mLastFetchTimeUs;
 
-    sp<HTTPDataSource> mSource;
+    sp<NuHTTPDataSource> mSource;
     off_t mSourceSize;
     off_t mOffsetBias;
 
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
new file mode 100644
index 0000000..c30f029
--- /dev/null
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NU_CACHED_SOURCE_2_H_
+
+#define NU_CACHED_SOURCE_2_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/DataSource.h>
+
+namespace android {
+
+struct ALooper;
+struct PageCache;
+
+struct NuCachedSource2 : public DataSource {
+    NuCachedSource2(const sp<DataSource> &source);
+
+    virtual status_t initCheck() const;
+
+    virtual ssize_t readAt(off_t offset, void *data, size_t size);
+
+    virtual status_t getSize(off_t *size);
+    virtual uint32_t flags();
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    size_t cachedSize();
+    size_t approxDataRemaining(bool *eos);
+
+protected:
+    virtual ~NuCachedSource2();
+
+private:
+    friend struct AHandlerReflector<NuCachedSource2>;
+
+    enum {
+        kPageSize           = 16384,
+        kHighWaterThreshold = 3 * 1024 * 1024,
+        kLowWaterThreshold  = 512 * 1024,
+    };
+
+    enum {
+        kWhatFetchMore  = 'fetc',
+        kWhatRead       = 'read',
+    };
+
+    sp<DataSource> mSource;
+    sp<AHandlerReflector<NuCachedSource2> > mReflector;
+    sp<ALooper> mLooper;
+
+    Mutex mSerializer;
+    Mutex mLock;
+    Condition mCondition;
+
+    PageCache *mCache;
+    off_t mCacheOffset;
+    status_t mFinalStatus;
+    off_t mLastAccessPos;
+    sp<AMessage> mAsyncResult;
+    bool mFetching;
+
+    void onMessageReceived(const sp<AMessage> &msg);
+    void onFetch();
+    void onRead(const sp<AMessage> &msg);
+
+    void fetchInternal();
+    ssize_t readInternal(off_t offset, void *data, size_t size);
+    status_t seekInternal_l(off_t offset);
+
+    size_t approxDataRemaining_l(bool *eos);
+    void restartPrefetcherIfNecessary_l();
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuCachedSource2);
+};
+
+}  // namespace android
+
+#endif  // NU_CACHED_SOURCE_2_H_
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
new file mode 100644
index 0000000..33339e2
--- /dev/null
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -0,0 +1,59 @@
+#ifndef NU_HTTP_DATA_SOURCE_H_
+
+#define NU_HTTP_DATA_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include "HTTPStream.h"
+
+namespace android {
+
+struct NuHTTPDataSource : public DataSource {
+    NuHTTPDataSource();
+
+    status_t connect(const char *uri, off_t offset = 0);
+
+    status_t connect(
+            const char *host, unsigned port, const char *path,
+            off_t offset = 0);
+
+    void disconnect();
+
+    virtual status_t initCheck() const;
+
+    virtual ssize_t readAt(off_t offset, void *data, size_t size);
+    virtual status_t getSize(off_t *size);
+    virtual uint32_t flags();
+
+protected:
+    virtual ~NuHTTPDataSource();
+
+private:
+    enum State {
+        DISCONNECTED,
+        CONNECTING,
+        CONNECTED
+    };
+
+    Mutex mLock;
+
+    State mState;
+
+    String8 mHost;
+    unsigned mPort;
+    String8 mPath;
+
+    HTTPStream mHTTP;
+    off_t mOffset;
+    off_t mContentLength;
+    bool mContentLengthValid;
+
+    NuHTTPDataSource(const NuHTTPDataSource &);
+    NuHTTPDataSource &operator=(const NuHTTPDataSource &);
+};
+
+}  // namespace android
+
+#endif  // NU_HTTP_DATA_SOURCE_H_
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 07a74da..414b69f 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -671,6 +671,8 @@
                         System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
                                 lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
                                 (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
+                        System.arraycopy(next2Last, i, next2Last,
+                                i+1, lastNumPointers-i);
                         break;
                     }
                     i++;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b3c5199..cfcac86 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1023,6 +1023,7 @@
     static final int FINALIZE_PENDING_INTENT_MSG = 23;
     static final int POST_HEAVY_NOTIFICATION_MSG = 24;
     static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
+    static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
 
     AlertDialog mUidAlert;
 
@@ -1076,6 +1077,31 @@
                 
                 ensureBootCompleted();
             } break;
+            case SHOW_STRICT_MODE_VIOLATION_MSG: {
+                HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
+                synchronized (ActivityManagerService.this) {
+                    ProcessRecord proc = (ProcessRecord) data.get("app");
+                    if (proc == null) {
+                        Slog.e(TAG, "App not found when showing strict mode dialog.");
+                        break;
+                    }
+                    if (proc.crashDialog != null) {
+                        Slog.e(TAG, "App already has strict mode dialog: " + proc);
+                        return;
+                    }
+                    AppErrorResult res = (AppErrorResult) data.get("result");
+                    if (!mSleeping && !mShuttingDown) {
+                        Dialog d = new StrictModeViolationDialog(mContext, res, proc);
+                        d.show();
+                        proc.crashDialog = d;
+                    } else {
+                        // The device is asleep, so just pretend that the user
+                        // saw a crash dialog and hit "force quit".
+                        res.set(0);
+                    }
+                }
+                ensureBootCompleted();
+            } break;
             case SHOW_FACTORY_ERROR_MSG: {
                 Dialog d = new FactoryErrorDialog(
                     mContext, msg.getData().getCharSequence("msg"));
@@ -9311,6 +9337,30 @@
         crashApplication(r, crashInfo);
     }
 
+    public void handleApplicationStrictModeViolation(
+        IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
+        ProcessRecord r = findAppProcess(app);
+        // TODO: implement
+        Log.w(TAG, "handleApplicationStrictModeViolation.");
+
+        AppErrorResult result = new AppErrorResult();
+        synchronized (this) {
+            final long origId = Binder.clearCallingIdentity();
+
+            Message msg = Message.obtain();
+            msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+            HashMap<String, Object> data = new HashMap<String, Object>();
+            data.put("result", result);
+            data.put("app", r);
+            msg.obj = data;
+            mHandler.sendMessage(msg);
+
+            Binder.restoreCallingIdentity(origId);
+        }
+        int res = result.get();
+        Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+    }
+
     /**
      * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
      * @param app object of the crashing app, null for the system server
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
new file mode 100644
index 0000000..f4329e2
--- /dev/null
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Slog;
+
+class StrictModeViolationDialog extends BaseErrorDialog {
+    private final static String TAG = "StrictModeViolationDialog";
+
+    private final AppErrorResult mResult;
+    private final ProcessRecord mProc;
+
+    // Event 'what' codes
+    static final int ACTION_OK = 0;
+    static final int ACTION_OK_AND_REPORT = 1;
+
+    // 1-minute timeout, then we automatically dismiss the violation
+    // dialog
+    static final long DISMISS_TIMEOUT = 1000 * 60 * 1;
+
+    public StrictModeViolationDialog(Context context, AppErrorResult result, ProcessRecord app) {
+        super(context);
+
+        Resources res = context.getResources();
+
+        mProc = app;
+        mResult = result;
+        CharSequence name;
+        if ((app.pkgList.size() == 1) &&
+                (name=context.getPackageManager().getApplicationLabel(app.info)) != null) {
+            setMessage(res.getString(
+                    com.android.internal.R.string.smv_application,
+                    name.toString(), app.info.processName));
+        } else {
+            name = app.processName;
+            setMessage(res.getString(
+                    com.android.internal.R.string.smv_process,
+                    name.toString()));
+        }
+
+        setCancelable(false);
+
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                  res.getText(com.android.internal.R.string.dlg_ok),
+                  mHandler.obtainMessage(ACTION_OK));
+
+        if (app.errorReportReceiver != null) {
+            setButton(DialogInterface.BUTTON_NEGATIVE,
+                      res.getText(com.android.internal.R.string.report),
+                      mHandler.obtainMessage(ACTION_OK_AND_REPORT));
+        }
+
+        setTitle(res.getText(com.android.internal.R.string.aerr_title));
+        getWindow().addFlags(FLAG_SYSTEM_ERROR);
+        getWindow().setTitle("Strict Mode Violation: " + app.info.processName);
+
+        // After the timeout, pretend the user clicked the quit button
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(ACTION_OK),
+                DISMISS_TIMEOUT);
+    }
+
+    public void onStop() {
+    }
+
+    private final Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            synchronized (mProc) {
+                if (mProc != null && mProc.crashDialog == StrictModeViolationDialog.this) {
+                    mProc.crashDialog = null;
+                }
+            }
+            mResult.set(msg.what);
+
+            // If this is a timeout we won't be automatically closed, so go
+            // ahead and explicitly dismiss ourselves just in case.
+            dismiss();
+        }
+    };
+}