diff options
23 files changed, 1521 insertions, 24 deletions
diff --git a/api/current.xml b/api/current.xml index d002534f6f36..9930f44b84d7 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 4f684b7ba3d3..690169af8a82 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -371,10 +371,7 @@ CameraService::Client::~Client() { 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 b78d22f7653a..b4c7edc71ba9 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1062,6 +1062,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); data.recycle(); } + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException { @@ -2540,6 +2550,20 @@ class ActivityManagerProxy implements IActivityManager 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 773c344bb3f6..49f1a8f92018 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -42,6 +42,7 @@ import android.database.sqlite.SQLiteDebug; 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.ParcelFileDescriptor; 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 @@ public final class ActivityThread { 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 11b7b0227934..bcdfe59e2861 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1157,12 +1157,16 @@ class ContextImpl extends Context { 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 cd24fa6c8278..ca09290125e3 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -256,7 +256,9 @@ public interface IActivityManager extends IInterface { 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 @@ public interface IActivityManager extends IInterface { 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 000000000000..876ec3956d6c --- /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 ac23832900cc..bf751f56b90b 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -238,7 +238,7 @@ import junit.framework.Assert; * * <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 599a7fe4b681..59600dcdc455 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -342,6 +342,10 @@ public class RuntimeInit { 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 fc1db183b8ce..3c1fdbc68dd9 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 96d57e724d21..9cc94c8391aa 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 @@ struct MediaSource : public RefBase { 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 3fbb4697379a..c95fc021b140 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -52,6 +52,8 @@ struct OMXCodec : public MediaSource, 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 @@ private: 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 000000000000..857866a1af00 --- /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/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 1a684a96dcd4..2f3b075cfb48 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -114,25 +114,27 @@ status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { } 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 @@ status_t StagefrightRecorder::startMPEG4Recording() { } 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 @@ status_t StagefrightRecorder::startMPEG4Recording() { 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 @@ status_t StagefrightRecorder::startMPEG4Recording() { } CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); + IPCThreadState::self()->restoreCallingIdentity(token); sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera); @@ -698,14 +707,14 @@ status_t StagefrightRecorder::close() { 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 67759c05e92b..0ab76b3f22db 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 @@ CameraSource::CameraSource(const sp<Camera> &camera) 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 @@ status_t CameraSource::start(MetaData *) { && (!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 @@ status_t CameraSource::stop() { 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 @@ void CameraSource::signalBufferReturned(MediaBuffer *buffer) { 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 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, 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 000000000000..63ba00078d7f --- /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 000000000000..8587c1b13602 --- /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 14682af042a5..6be41b449920 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1145,7 +1145,8 @@ OMXCodec::OMXCodec( mNoMoreOutputData(false), mOutputPortSettingsHaveChanged(false), mSeekTimeUs(-1), - mLeftOverBuffer(NULL) { + mLeftOverBuffer(NULL), + mPaused(false) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; @@ -1735,6 +1736,9 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { 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 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { return; } + if (mPaused) { + return; + } + status_t err; bool signalEOS = false; @@ -2554,6 +2562,7 @@ status_t OMXCodec::start(MetaData *) { mOutputPortSettingsHaveChanged = false; mSeekTimeUs = -1; mFilledBuffers.clear(); + mPaused = false; return init(); } @@ -2660,6 +2669,7 @@ status_t OMXCodec::read( // There's no reason to trigger the code below, there's // nothing to flush yet. seeking = false; + mPaused = false; } drainInputBuffers(); @@ -3220,6 +3230,14 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { } } +status_t OMXCodec::pause() { + Mutex::Autolock autoLock(mLock); + + mPaused = true; + + return OK; +} + //////////////////////////////////////////////////////////////////////////////// status_t QueryCodecs( diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h new file mode 100644 index 000000000000..c30f02999b1c --- /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 000000000000..33339e2e81f3 --- /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 07a74da8b4f0..414b69fabdc8 100644 --- a/services/java/com/android/server/InputDevice.java +++ b/services/java/com/android/server/InputDevice.java @@ -671,6 +671,8 @@ public class InputDevice { 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 b3c51992399f..cfcac86c8b23 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1023,6 +1023,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 000000000000..f4329e2d09ab --- /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(); + } + }; +} |