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 — sometimes significantly more — 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();
+ }
+ };
+}