diff options
| -rw-r--r-- | api/16.txt | 2 | ||||
| -rw-r--r-- | api/current.txt | 4 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 8 | ||||
| -rw-r--r-- | core/java/android/app/LoadedApk.java | 2 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 5 | ||||
| -rw-r--r-- | core/java/android/content/pm/ServiceInfo.java | 9 | ||||
| -rw-r--r-- | core/java/android/os/Process.java | 23 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 6 | ||||
| -rw-r--r-- | include/gui/BufferQueue.h | 343 | ||||
| -rw-r--r-- | include/gui/SurfaceTexture.h | 298 | ||||
| -rw-r--r-- | libs/gui/Android.mk | 1 | ||||
| -rw-r--r-- | libs/gui/BufferQueue.cpp | 722 | ||||
| -rw-r--r-- | libs/gui/SurfaceTexture.cpp | 674 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 347 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 29 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ServiceRecord.java | 4 |
18 files changed, 1398 insertions, 1085 deletions
diff --git a/api/16.txt b/api/16.txt index 8e07844725c3..02a5104abd1c 100644 --- a/api/16.txt +++ b/api/16.txt @@ -15052,7 +15052,7 @@ package android.os { method public static final deprecated boolean supportsProcesses(); field public static final int BLUETOOTH_GID = 2000; // 0x7d0 field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710 - field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f + field public static final int LAST_APPLICATION_UID = 89999; // 0x1869f field public static final int PHONE_UID = 1001; // 0x3e9 field public static final int SIGNAL_KILL = 9; // 0x9 field public static final int SIGNAL_QUIT = 3; // 0x3 diff --git a/api/current.txt b/api/current.txt index 574af6b9f55d..2037515e5632 100644 --- a/api/current.txt +++ b/api/current.txt @@ -562,6 +562,7 @@ package android { field public static final int isRepeatable = 16843336; // 0x1010248 field public static final int isScrollContainer = 16843342; // 0x101024e field public static final int isSticky = 16843335; // 0x1010247 + field public static final int isolatedProcess = 16843687; // 0x10103a7 field public static final int itemBackground = 16843056; // 0x1010130 field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131 field public static final int itemPadding = 16843565; // 0x101032d @@ -6469,6 +6470,7 @@ package android.content.pm { method public int describeContents(); method public void dump(android.util.Printer, java.lang.String); field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2 field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1 field public int flags; field public java.lang.String permission; @@ -15178,7 +15180,7 @@ package android.os { method public static final deprecated boolean supportsProcesses(); field public static final int BLUETOOTH_GID = 2000; // 0x7d0 field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710 - field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f + field public static final int LAST_APPLICATION_UID = 89999; // 0x15f8f field public static final int PHONE_UID = 1001; // 0x3e9 field public static final int SIGNAL_KILL = 9; // 0x9 field public static final int SIGNAL_QUIT = 3; // 0x3 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e4cfc9948704..bf632a9b0caa 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -134,7 +134,7 @@ public final class ActivityThread { private static final boolean DEBUG_RESULTS = false; private static final boolean DEBUG_BACKUP = true; private static final boolean DEBUG_CONFIGURATION = false; - private static final boolean DEBUG_SERVICE = true; + private static final boolean DEBUG_SERVICE = false; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; @@ -3764,13 +3764,17 @@ public final class ActivityThread { } private void setupGraphicsSupport(LoadedApk info) { + if (Process.isIsolated()) { + // Isolated processes aren't going to do UI. + return; + } try { int uid = Process.myUid(); String[] packages = getPackageManager().getPackagesForUid(uid); // If there are several packages in this application we won't // initialize the graphics disk caches - if (packages.length == 1) { + if (packages != null && packages.length == 1) { ContextImpl appContext = new ContextImpl(); appContext.init(info, null, this); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index fcbcd8172c47..d9bbb4a33b4a 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -119,7 +119,7 @@ public final class LoadedApk { final int myUid = Process.myUid(); mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; - if (!UserId.isSameUser(aInfo.uid, myUid)) { + if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) { aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid), mPackageName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index faee873a5660..2023f82cb6d1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2581,6 +2581,11 @@ public class PackageParser { false)) { s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestService_isolatedProcess, + false)) { + s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS; + } sa.recycle(); diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 612e34574469..7ee84ab734b9 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -42,10 +42,17 @@ public class ServiceInfo extends ComponentInfo public static final int FLAG_STOP_WITH_TASK = 0x0001; /** + * Bit in {@link #flags}: If set, the service will run in its own + * isolated process. Set from the + * {@link android.R.attr#isolatedProcess} attribute. + */ + public static final int FLAG_ISOLATED_PROCESS = 0x0002; + + /** * Options that have been set in the service declaration in the * manifest. * These include: - * {@link #FLAG_STOP_WITH_TASK} + * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS}. */ public int flags; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index cdf235df9a08..0746af8de8d5 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -119,7 +119,19 @@ public class Process { * Last of application-specific UIDs starting at * {@link #FIRST_APPLICATION_UID}. */ - public static final int LAST_APPLICATION_UID = 99999; + public static final int LAST_APPLICATION_UID = 89999; + + /** + * First uid used for fully isolated sandboxed processes (with no permissions of their own) + * @hide + */ + public static final int FIRST_ISOLATED_UID = 99000; + + /** + * Last uid used for fully isolated sandboxed processes (with no permissions of their own) + * @hide + */ + public static final int LAST_ISOLATED_UID = 99999; /** * Defines a secondary group id for access to the bluetooth hardware. @@ -576,6 +588,15 @@ public class Process { public static final native int myUid(); /** + * Returns whether the current process is in an isolated sandbox. + * @hide + */ + public static final boolean isIsolated() { + int uid = UserId.getAppId(myUid()); + return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID; + } + + /** * Returns the UID assigned to a particular user name, or -1 if there is * none. If the given string consists of only numbers, it is converted * directly to a uid. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index dbd49fbcc462..92c59ab99391 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1235,6 +1235,10 @@ when the user remove a task rooted in an activity owned by the application. The default is false. --> <attr name="stopWithTask" format="boolean" /> + <!-- If set to true, this service will run under a special process + that is isolated from the rest of the system. The only communication + with it is through the Service API (binding and starting). --> + <attr name="isolatedProcess" format="boolean" /> </declare-styleable> <!-- The <code>receiver</code> tag declares an diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e3c2bd8636ef..1b9164322be2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3482,4 +3482,10 @@ <public type="color" name="holo_orange_dark" id="0x01060019" /> <public type="color" name="holo_purple" id="0x0106001a" /> <public type="color" name="holo_blue_bright" id="0x0106001b" /> + +<!-- =============================================================== + Resources added in version 16 of the platform (Jelly Bean) + =============================================================== --> + <public type="attr" name="isolatedProcess" id="0x010103a7" /> + </resources> diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h new file mode 100644 index 000000000000..991a1817acdd --- /dev/null +++ b/include/gui/BufferQueue.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2012 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 ANDROID_GUI_BUFFERQUEUE_H +#define ANDROID_GUI_BUFFERQUEUE_H + +#include <EGL/egl.h> + +#include <gui/ISurfaceTexture.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class BufferQueue : public BnSurfaceTexture { +public: + enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + enum { + MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, + MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called from queueBuffer() each time an + // additional frame becomes available for consumption. This means that + // frames that are queued while in asynchronous mode only trigger the + // callback if no previous frames are pending. Frames queued while in + // synchronous mode always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + // BufferQueue manages a pool of gralloc memory slots to be used + // by producers and consumers. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. + BufferQueue(bool allowSynchronousMode = true); + virtual ~BufferQueue(); + + // setBufferCount updates the number of available buffer slots. After + // calling this all buffer slots are both unallocated and owned by the + // BufferQueue object (i.e. they are not owned by the client). + virtual status_t setBufferCount(int bufferCount); + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); + + // dequeueBuffer gets the next buffer slot index for the client to use. If a + // buffer slot is available then that slot index is written to the location + // pointed to by the buf argument and a status of OK is returned. If no + // slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. + virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the BufferQueue. In addition, a + // timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are client-dependent and should be documented by the + // client. + virtual status_t queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + virtual void cancelBuffer(int buf); + virtual status_t setCrop(const Rect& reg); + virtual status_t setTransform(uint32_t transform); + virtual status_t setScalingMode(int mode); + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled); + + // connect attempts to connect a producer client API to the BufferQueue. + // This must be called before any other ISurfaceTexture methods are called + // except for getAllocator. + // + // This method will fail if the connect was previously called on the + // BufferQueue and no corresponding disconnect call was made. + virtual status_t connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + + // disconnect attempts to disconnect a producer client API from the + // BufferQueue. Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the BufferQueue is not currently + // connected to the specified client API. + virtual status_t disconnect(int api); + +protected: + + // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) + // for the given slot. + void freeBufferLocked(int index); + + // freeAllBuffersLocked frees the resources (both GraphicBuffer and + // EGLImage) for all slots. + void freeAllBuffersLocked(); + + // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer + // and EGLImage) for all slots except the head of mQueue + void freeAllBuffersExceptHeadLocked(); + + // drainQueueLocked drains the buffer queue if we're in synchronous mode + // returns immediately otherwise. It returns NO_INIT if the BufferQueue + // became abandoned or disconnected during this call. + status_t drainQueueLocked(); + + // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in + // synchronous mode and free all buffers. In asynchronous mode, all buffers + // are freed except the current buffer. + status_t drainQueueAndFreeBuffersLocked(); + + status_t setBufferCountServerLocked(int bufferCount); + + enum { INVALID_BUFFER_SLOT = -1 }; + + struct BufferSlot { + + BufferSlot() + : mEglImage(EGL_NO_IMAGE_KHR), + mEglDisplay(EGL_NO_DISPLAY), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mFrameNumber(0), + mFence(EGL_NO_SYNC_KHR) { + mCrop.makeInvalid(); + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp<GraphicBuffer> mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEglDisplay is the EGLDisplay used to create mEglImage. + EGLDisplay mEglDisplay; + + // BufferState represents the different states in which a buffer slot + // can be. + enum BufferState { + // FREE indicates that the buffer is not currently being used and + // will not be used in the future until it gets dequeued and + // subsequently queued by the client. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // client, but has not yet been queued or canceled. The buffer is + // considered 'owned' by the client, and the server should not use + // it for anything. + // + // Note that when in synchronous-mode (mSynchronousMode == true), + // the buffer that's currently attached to the texture may be + // dequeued by the client. That means that the current buffer can + // be in either the DEQUEUED or QUEUED state. In asynchronous mode, + // however, the current buffer is always in the QUEUED state. + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been queued by the client, + // and has not since been made available for the client to dequeue. + // Attaching the buffer to the texture does NOT transition the + // buffer away from the QUEUED state. However, in Synchronous mode + // the current buffer may be dequeued by the client under some + // circumstances. See the note about the current buffer in the + // documentation for DEQUEUED. + QUEUED = 2, + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the client did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching client bugs. + bool mRequestBufferCalled; + + // mCrop is the current crop rectangle for this buffer slot. This gets + // set to mNextCrop each time queueBuffer gets called for this buffer. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. This + // gets set to mNextTransform each time queueBuffer gets called for this + // slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. This + // gets set to mNextScalingMode each time queueBuffer gets called for + // this slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + // on a compile-time option) set to a new sync object in updateTexImage. + EGLSyncKHR mFence; + }; + + // mSlots is the array of buffer slots that must be mirrored on the client + // side. This allows buffer ownership to be transferred between the client + // and server without sending a GraphicBuffer over binder. The entire array + // is initialized to NULL at construction time, and buffers are allocated + // for a slot when requestBuffer is called with that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mPixelFormat holds the pixel format of allocated buffers. It is used + // in requestBuffers() if a format of zero is specified. + uint32_t mPixelFormat; + + // mBufferCount is the number of buffer slots that the client and server + // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed + // by calling setBufferCount or setBufferCountServer + int mBufferCount; + + // mClientBufferCount is the number of buffer slots requested by the client. + // The default is zero, which means the client doesn't care how many buffers + // there is. + int mClientBufferCount; + + // mServerBufferCount buffer count requested by the server-side + int mServerBufferCount; + + // mCurrentTexture is the buffer slot index of the buffer that is currently + // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + // indicating that no buffer slot is currently bound to the texture. Note, + // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + // that no buffer is bound to the texture. A call to setBufferCount will + // reset mCurrentTexture to INVALID_BUFFER_SLOT. + int mCurrentTexture; + + // mNextCrop is the crop rectangle that will be used for the next buffer + // that gets queued. It is set by calling setCrop. + Rect mNextCrop; + + // mNextTransform is the transform identifier that will be used for the next + // buffer that gets queued. It is set by calling setTransform. + uint32_t mNextTransform; + + // mNextScalingMode is the scaling mode that will be used for the next + // buffers that get queued. It is set by calling setScalingMode. + int mNextScalingMode; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp<IGraphicBufferAlloc> mGraphicBufferAlloc; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mAllowSynchronousMode whether we allow synchronous mode or not + const bool mAllowSynchronousMode; + + // mConnectedApi indicates the API that is currently connected to this + // BufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector<int> Fifo; + Fifo mQueue; + + // mAbandoned indicates that the BufferQueue will no longer be used to + // consume images buffers pushed to it using the ISurfaceTexture interface. + // It is initialized to false, and set to true in the abandon method. A + // BufferQueue that has been abandoned will return the NO_INIT error from + // all ISurfaceTexture methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the BufferQueue in log messages. + // It is set by the setName method. + String8 mName; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of BufferQueue objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // mFrameCounter is the free running counter, incremented for every buffer queued + // with the surface Texture. + uint64_t mFrameCounter; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index a8c76725e7c9..4318f0fa4841 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -23,6 +23,7 @@ #include <GLES2/gl2ext.h> #include <gui/ISurfaceTexture.h> +#include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> @@ -35,30 +36,11 @@ namespace android { // ---------------------------------------------------------------------------- -class IGraphicBufferAlloc; + class String8; -class SurfaceTexture : public BnSurfaceTexture { +class SurfaceTexture : public BufferQueue { public: - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - enum { - MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, - MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS - }; - enum { NUM_BUFFER_SLOTS = 32 }; - enum { NO_CONNECTED_API = 0 }; - - struct FrameAvailableListener : public virtual RefBase { - // onFrameAvailable() is called from queueBuffer() each time an - // additional frame becomes available for consumption. This means that - // frames that are queued while in asynchronous mode only trigger the - // callback if no previous frames are pending. Frames queued while in - // synchronous mode always trigger the callback. - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onFrameAvailable() = 0; - }; // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the // name of the OpenGL ES texture to which images are to be streamed. This @@ -73,65 +55,8 @@ public: virtual ~SurfaceTexture(); - // setBufferCount updates the number of available buffer slots. After - // calling this all buffer slots are both unallocated and owned by the - // SurfaceTexture object (i.e. they are not owned by the client). - virtual status_t setBufferCount(int bufferCount); - - virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); - - // dequeueBuffer gets the next buffer slot index for the client to use. If a - // buffer slot is available then that slot index is written to the location - // pointed to by the buf argument and a status of OK is returned. If no - // slot is available then a status of -EBUSY is returned and buf is - // unmodified. - // The width and height parameters must be no greater than the minimum of - // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - // An error due to invalid dimensions might not be reported until - // updateTexImage() is called. - virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, - uint32_t format, uint32_t usage); - - // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a - // timestamp must be provided for the buffer. The timestamp is in - // nanoseconds, and must be monotonically increasing. Its other semantics - // (zero point, etc) are client-dependent and should be documented by the - // client. - virtual status_t queueBuffer(int buf, int64_t timestamp, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - virtual void cancelBuffer(int buf); - virtual status_t setCrop(const Rect& reg); - virtual status_t setTransform(uint32_t transform); - virtual status_t setScalingMode(int mode); - virtual int query(int what, int* value); - // setSynchronousMode set whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be retired in order. - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled); - - // connect attempts to connect a client API to the SurfaceTexture. This - // must be called before any other ISurfaceTexture methods are called except - // for getAllocator. - // - // This method will fail if the connect was previously called on the - // SurfaceTexture and no corresponding disconnect call was made. - virtual status_t connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - - // disconnect attempts to disconnect a client API from the SurfaceTexture. - // Calling this method will cause any subsequent calls to other - // ISurfaceTexture methods to fail except for getAllocator and connect. - // Successfully calling connect after this will allow the other methods to - // succeed again. - // - // This method will fail if the the SurfaceTexture is not currently - // connected to the specified client API. - virtual status_t disconnect(int api); - // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // @@ -233,28 +158,6 @@ public: protected: - // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) - // for the given slot. - void freeBufferLocked(int index); - - // freeAllBuffersLocked frees the resources (both GraphicBuffer and - // EGLImage) for all slots. - void freeAllBuffersLocked(); - - // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer - // and EGLImage) for all slots except the head of mQueue - void freeAllBuffersExceptHeadLocked(); - - // drainQueueLocked drains the buffer queue if we're in synchronous mode - // returns immediately otherwise. return NO_INIT if SurfaceTexture - // became abandoned or disconnected during this call. - status_t drainQueueLocked(); - - // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in - // synchronous mode and free all buffers. In asynchronous mode, all buffers - // are freed except the current buffer. - status_t drainQueueAndFreeBuffersLocked(); - static bool isExternalFormat(uint32_t format); private: @@ -263,146 +166,11 @@ private: EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); - status_t setBufferCountServerLocked(int bufferCount); - // computeCurrentTransformMatrix computes the transform matrix for the // current texture. It uses mCurrentTransform and the current GraphicBuffer // to compute this matrix and stores it in mCurrentTransformMatrix. void computeCurrentTransformMatrix(); - enum { INVALID_BUFFER_SLOT = -1 }; - - struct BufferSlot { - - BufferSlot() - : mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mBufferState(BufferSlot::FREE), - mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mFence(EGL_NO_SYNC_KHR) { - mCrop.makeInvalid(); - } - - // mGraphicBuffer points to the buffer allocated for this slot or is NULL - // if no buffer has been allocated. - sp<GraphicBuffer> mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEglDisplay is the EGLDisplay used to create mEglImage. - EGLDisplay mEglDisplay; - - // BufferState represents the different states in which a buffer slot - // can be. - enum BufferState { - // FREE indicates that the buffer is not currently being used and - // will not be used in the future until it gets dequeued and - // subsequently queued by the client. - FREE = 0, - - // DEQUEUED indicates that the buffer has been dequeued by the - // client, but has not yet been queued or canceled. The buffer is - // considered 'owned' by the client, and the server should not use - // it for anything. - // - // Note that when in synchronous-mode (mSynchronousMode == true), - // the buffer that's currently attached to the texture may be - // dequeued by the client. That means that the current buffer can - // be in either the DEQUEUED or QUEUED state. In asynchronous mode, - // however, the current buffer is always in the QUEUED state. - DEQUEUED = 1, - - // QUEUED indicates that the buffer has been queued by the client, - // and has not since been made available for the client to dequeue. - // Attaching the buffer to the texture does NOT transition the - // buffer away from the QUEUED state. However, in Synchronous mode - // the current buffer may be dequeued by the client under some - // circumstances. See the note about the current buffer in the - // documentation for DEQUEUED. - QUEUED = 2, - }; - - // mBufferState is the current state of this buffer slot. - BufferState mBufferState; - - // mRequestBufferCalled is used for validating that the client did - // call requestBuffer() when told to do so. Technically this is not - // needed but useful for debugging and catching client bugs. - bool mRequestBufferCalled; - - // mCrop is the current crop rectangle for this buffer slot. This gets - // set to mNextCrop each time queueBuffer gets called for this buffer. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. This - // gets set to mNextTransform each time queueBuffer gets called for this - // slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. This - // gets set to mNextScalingMode each time queueBuffer gets called for - // this slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. - uint64_t mFrameNumber; - - // mFence is the EGL sync object that must signal before the buffer - // associated with this buffer slot may be dequeued. It is initialized - // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - // on a compile-time option) set to a new sync object in updateTexImage. - EGLSyncKHR mFence; - }; - - // mSlots is the array of buffer slots that must be mirrored on the client - // side. This allows buffer ownership to be transferred between the client - // and server without sending a GraphicBuffer over binder. The entire array - // is initialized to NULL at construction time, and buffers are allocated - // for a slot when requestBuffer is called with that slot's index. - BufferSlot mSlots[NUM_BUFFER_SLOTS]; - - // mDefaultWidth holds the default width of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultWidth; - - // mDefaultHeight holds the default height of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultHeight; - - // mPixelFormat holds the pixel format of allocated buffers. It is used - // in requestBuffers() if a format of zero is specified. - uint32_t mPixelFormat; - - // mBufferCount is the number of buffer slots that the client and server - // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed - // by calling setBufferCount or setBufferCountServer - int mBufferCount; - - // mClientBufferCount is the number of buffer slots requested by the client. - // The default is zero, which means the client doesn't care how many buffers - // there is. - int mClientBufferCount; - - // mServerBufferCount buffer count requested by the server-side - int mServerBufferCount; - - // mCurrentTexture is the buffer slot index of the buffer that is currently - // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - // indicating that no buffer slot is currently bound to the texture. Note, - // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - // that no buffer is bound to the texture. A call to setBufferCount will - // reset mCurrentTexture to INVALID_BUFFER_SLOT. - int mCurrentTexture; - // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. @@ -429,72 +197,17 @@ private: // gets set each time updateTexImage is called. int64_t mCurrentTimestamp; - // mNextCrop is the crop rectangle that will be used for the next buffer - // that gets queued. It is set by calling setCrop. - Rect mNextCrop; - - // mNextTransform is the transform identifier that will be used for the next - // buffer that gets queued. It is set by calling setTransform. - uint32_t mNextTransform; - - // mNextScalingMode is the scaling mode that will be used for the next - // buffers that get queued. It is set by calling setScalingMode. - int mNextScalingMode; - // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; - // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to - // allocate new GraphicBuffer objects. - sp<IGraphicBufferAlloc> mGraphicBufferAlloc; - - // mFrameAvailableListener is the listener object that will be called when a - // new frame becomes available. If it is not NULL it will be called from - // queueBuffer. - sp<FrameAvailableListener> mFrameAvailableListener; - - // mSynchronousMode whether we're in synchronous mode or not - bool mSynchronousMode; - - // mAllowSynchronousMode whether we allow synchronous mode or not - const bool mAllowSynchronousMode; - - // mConnectedApi indicates the API that is currently connected to this - // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated - // by the connect and disconnect methods. - int mConnectedApi; - - // mDequeueCondition condition used for dequeueBuffer in synchronous mode - mutable Condition mDequeueCondition; - - // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector<int> Fifo; - Fifo mQueue; - - // mAbandoned indicates that the SurfaceTexture will no longer be used to - // consume images buffers pushed to it using the ISurfaceTexture interface. - // It is initialized to false, and set to true in the abandon method. A - // SurfaceTexture that has been abandoned will return the NO_INIT error from - // all ISurfaceTexture methods capable of returning an error. - bool mAbandoned; - - // mName is a string used to identify the SurfaceTexture in log messages. - // It is set by the setName method. - String8 mName; - // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before // it's safe for them to be written. It gets set at construction time and // never changes. const bool mUseFenceSync; - // mMutex is the mutex used to prevent concurrent access to the member - // variables of SurfaceTexture objects. It must be locked whenever the - // member variables are accessed. - mutable Mutex mMutex; - // mTexTarget is the GL texture target with which the GL texture object is // associated. It is set in the constructor and never changed. It is // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android @@ -504,11 +217,6 @@ private: // browser's tile cache exceeds. const GLenum mTexTarget; - // mFrameCounter is the free running counter, incremented for every buffer queued - // with the surface Texture. - uint64_t mFrameCounter; - - }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index b6f5b9e8e855..2f4ac62944a5 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BitTube.cpp \ + BufferQueue.cpp \ DisplayEventReceiver.cpp \ IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp new file mode 100644 index 000000000000..c7e2c0f21fe4 --- /dev/null +++ b/libs/gui/BufferQueue.cpp @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2012 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 "BufferQueue" + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueue.h> +#include <private/gui/ComposerService.h> +#include <surfaceflinger/ISurfaceComposer.h> + +#include <utils/Log.h> + +// This compile option causes SurfaceTexture to return the buffer that is currently +// attached to the GL texture from dequeueBuffer when no other buffers are +// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do +// implicit cross-process synchronization to prevent the buffer from being +// written to before the buffer has (a) been detached from the GL texture and +// (b) all GL reads from the buffer have completed. +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true +#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" +#else +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false +#endif + +// Macros for including the BufferQueue name in log messages +#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +BufferQueue::BufferQueue( bool allowSynchronousMode ) : + mDefaultWidth(1), + mDefaultHeight(1), + mPixelFormat(PIXEL_FORMAT_RGBA_8888), + mBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mClientBufferCount(0), + mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mCurrentTexture(INVALID_BUFFER_SLOT), + mNextTransform(0), + mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mSynchronousMode(false), + mAllowSynchronousMode(allowSynchronousMode), + mConnectedApi(NO_CONNECTED_API), + mAbandoned(false), + mFrameCounter(0) +{ + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + ST_LOGV("BufferQueue"); + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); + mNextCrop.makeInvalid(); +} + +BufferQueue::~BufferQueue() { + ST_LOGV("~BufferQueue"); +} + +status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { + if (bufferCount > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + // special-case, nothing to do + if (bufferCount == mBufferCount) + return OK; + + if (!mClientBufferCount && + bufferCount >= mBufferCount) { + // easy, we just have more buffers + mBufferCount = bufferCount; + mServerBufferCount = bufferCount; + mDequeueCondition.signal(); + } else { + // we're here because we're either + // - reducing the number of available buffers + // - or there is a client-buffer-count in effect + + // less than 2 buffers is never allowed + if (bufferCount < 2) + return BAD_VALUE; + + // when there is non client-buffer-count in effect, the client is not + // allowed to dequeue more than one buffer at a time, + // so the next time they dequeue a buffer, we know that they don't + // own one. the actual resizing will happen during the next + // dequeueBuffer. + + mServerBufferCount = bufferCount; + } + return OK; +} + +status_t BufferQueue::setBufferCount(int bufferCount) { + ST_LOGV("setBufferCount: count=%d", bufferCount); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (bufferCount > NUM_BUFFER_SLOTS) { + ST_LOGE("setBufferCount: bufferCount larger than slots available"); + return BAD_VALUE; + } + + // Error out if the user has dequeued buffers + for (int i=0 ; i<mBufferCount ; i++) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + ST_LOGE("setBufferCount: client owns some buffers"); + return -EINVAL; + } + } + + const int minBufferSlots = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + if (bufferCount == 0) { + mClientBufferCount = 0; + bufferCount = (mServerBufferCount >= minBufferSlots) ? + mServerBufferCount : minBufferSlots; + return setBufferCountServerLocked(bufferCount); + } + + if (bufferCount < minBufferSlots) { + ST_LOGE("setBufferCount: requested buffer count (%d) is less than " + "minimum (%d)", bufferCount, minBufferSlots); + return BAD_VALUE; + } + + // here we're guaranteed that the client doesn't have dequeued buffers + // and will release all of its buffer references. + freeAllBuffersLocked(); + mBufferCount = bufferCount; + mClientBufferCount = bufferCount; + mCurrentTexture = INVALID_BUFFER_SLOT; + mQueue.clear(); + mDequeueCondition.signal(); + return OK; +} + +status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + ST_LOGV("requestBuffer: slot=%d", slot); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (slot < 0 || mBufferCount <= slot) { + ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, slot); + return BAD_VALUE; + } + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { + ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); + + if ((w && !h) || (!w && h)) { + ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); + return BAD_VALUE; + } + + status_t returnFlags(OK); + EGLDisplay dpy = EGL_NO_DISPLAY; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + int found = -1; + int foundSync = -1; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; + } + + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + } + + // if buffer is FREE it CANNOT be current + ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { + foundSync = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + bool isOlder = mSlots[i].mFrameNumber < + mSlots[found].mFrameNumber; + if (found < 0 || isOlder) { + foundSync = i; + found = i; + } + } + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the + // MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } + + // we're in synchronous mode and didn't find a buffer, we need to + // wait for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); + } + } + + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; + } + + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + const int buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } + + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, + mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } + + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + } + + if (fence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, fence); + } + + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t BufferQueue::setSynchronousMode(bool enabled) { + ST_LOGV("setSynchronousMode: enabled=%d", enabled); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + status_t err = OK; + if (!mAllowSynchronousMode && enabled) + return err; + + if (!enabled) { + // going to asynchronous mode, drain the queue + err = drainQueueLocked(); + if (err != NO_ERROR) + return err; + } + + if (mSynchronousMode != enabled) { + // - if we're going to asynchronous mode, the queue is guaranteed to be + // empty here + // - if the client set the number of buffers, we're guaranteed that + // we have at least 3 (because we don't allow less) + mSynchronousMode = enabled; + mDequeueCondition.signal(); + } + return err; +} + +status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); + + sp<FrameAvailableListener> listener; + + { // scope for the lock + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("queueBuffer: slot %d is not owned by the client " + "(state=%d)", buf, mSlots[buf].mBufferState); + return -EINVAL; + } else if (buf == mCurrentTexture) { + ST_LOGE("queueBuffer: slot %d is current!", buf); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { + ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " + "buffer", buf); + return -EINVAL; + } + + if (mSynchronousMode) { + // In synchronous mode we queue all buffers in a FIFO. + mQueue.push_back(buf); + + // Synchronous mode always signals that an additional frame should + // be consumed. + listener = mFrameAvailableListener; + } else { + // In asynchronous mode we only keep the most recent buffer. + if (mQueue.empty()) { + mQueue.push_back(buf); + + // Asynchronous mode only signals that a frame should be + // consumed if no previous frame was pending. If a frame were + // pending then the consumer would have already been notified. + listener = mFrameAvailableListener; + } else { + Fifo::iterator front(mQueue.begin()); + // buffer currently queued is freed + mSlots[*front].mBufferState = BufferSlot::FREE; + // and we record the new buffer index in the queued list + *front = buf; + } + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mCrop = mNextCrop; + mSlots[buf].mTransform = mNextTransform; + mSlots[buf].mScalingMode = mNextScalingMode; + mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + mDequeueCondition.signal(); + + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } // scope for the lock + + // call back without lock held + if (listener != 0) { + listener->onFrameAvailable(); + } + return OK; +} + +void BufferQueue::cancelBuffer(int buf) { + ST_LOGV("cancelBuffer: slot=%d", buf); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGW("cancelBuffer: BufferQueue has been abandoned!"); + return; + } + + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return; + } + mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; + mDequeueCondition.signal(); +} + +status_t BufferQueue::setCrop(const Rect& crop) { + ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, + crop.bottom); + + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setCrop: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextCrop = crop; + return OK; +} + +status_t BufferQueue::setTransform(uint32_t transform) { + ST_LOGV("setTransform: xform=%#x", transform); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setTransform: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextTransform = transform; + return OK; +} + +status_t BufferQueue::setScalingMode(int mode) { + ST_LOGV("setScalingMode: mode=%d", mode); + + switch (mode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + break; + default: + ST_LOGE("unknown scaling mode: %d", mode); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mNextScalingMode = mode; + return OK; +} + +status_t BufferQueue::connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("connect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("connect: BufferQueue has been abandoned!"); + return NO_INIT; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi != NO_CONNECTED_API) { + ST_LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } else { + mConnectedApi = api; + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t BufferQueue::disconnect(int api) { + ST_LOGV("disconnect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + drainQueueAndFreeBuffersLocked(); + mConnectedApi = NO_CONNECTED_API; + mNextCrop.makeInvalid(); + mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mNextTransform = 0; + mDequeueCondition.signal(); + } else { + ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } + break; + default: + ST_LOGE("disconnect: unknown API %d", api); + err = -EINVAL; + break; + } + return err; +} + +void BufferQueue::freeBufferLocked(int i) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; + if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + } +} + +void BufferQueue::freeAllBuffersLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersLocked called but mQueue is not empty"); + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } +} + +void BufferQueue::freeAllBuffersExceptHeadLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); + int head = -1; + if (!mQueue.empty()) { + Fifo::iterator front(mQueue.begin()); + head = *front; + } + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (i != head) { + freeBufferLocked(i); + } + } +} + +status_t BufferQueue::drainQueueLocked() { + while (mSynchronousMode && !mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + if (mAbandoned) { + ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!"); + return NO_INIT; + } + if (mConnectedApi == NO_CONNECTED_API) { + ST_LOGE("drainQueueLocked: BufferQueue is not connected!"); + return NO_INIT; + } + } + return NO_ERROR; +} + +status_t BufferQueue::drainQueueAndFreeBuffersLocked() { + status_t err = drainQueueLocked(); + if (err == NO_ERROR) { + if (mSynchronousMode) { + freeAllBuffersLocked(); + } else { + freeAllBuffersExceptHeadLocked(); + } + } + return err; +} + +}; // namespace android diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 3abe84ad8b6e..be1bcd1a8104 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -38,19 +38,6 @@ #include <utils/Log.h> #include <utils/String8.h> -// This compile option causes SurfaceTexture to return the buffer that is currently -// attached to the GL texture from dequeueBuffer when no other buffers are -// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do -// implicit cross-process synchronization to prevent the buffer from being -// written to before the buffer has (a) been detached from the GL texture and -// (b) all GL reads from the buffer have completed. -#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true -#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" -#else -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false -#endif - // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension // to synchronize access to the buffers. It will cause dequeueBuffer to stall, // waiting for the GL reads for the buffer being dequeued to complete before @@ -110,44 +97,22 @@ static float mtxRot270[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); -// Get an ID that's unique within this process. -static int32_t createProcessUniqueId() { - static volatile int32_t globalCounter = 0; - return android_atomic_inc(&globalCounter); -} SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, GLenum texTarget, bool useFenceSync) : - mDefaultWidth(1), - mDefaultHeight(1), - mPixelFormat(PIXEL_FORMAT_RGBA_8888), - mBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mClientBufferCount(0), - mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mCurrentTexture(INVALID_BUFFER_SLOT), + BufferQueue(allowSynchronousMode), mCurrentTransform(0), mCurrentTimestamp(0), - mNextTransform(0), - mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTexName(tex), - mSynchronousMode(false), - mAllowSynchronousMode(allowSynchronousMode), - mConnectedApi(NO_CONNECTED_API), - mAbandoned(false), #ifdef USE_FENCE_SYNC mUseFenceSync(useFenceSync), #else mUseFenceSync(false), #endif - mTexTarget(texTarget), - mFrameCounter(0) { - // Choose a name using the PID and a process-unique ID. - mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + mTexTarget(texTarget) +{ ST_LOGV("SurfaceTexture"); - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); - mNextCrop.makeInvalid(); memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); } @@ -157,91 +122,11 @@ SurfaceTexture::~SurfaceTexture() { freeAllBuffersLocked(); } -status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) { - if (bufferCount > NUM_BUFFER_SLOTS) - return BAD_VALUE; - - // special-case, nothing to do - if (bufferCount == mBufferCount) - return OK; - - if (!mClientBufferCount && - bufferCount >= mBufferCount) { - // easy, we just have more buffers - mBufferCount = bufferCount; - mServerBufferCount = bufferCount; - mDequeueCondition.signal(); - } else { - // we're here because we're either - // - reducing the number of available buffers - // - or there is a client-buffer-count in effect - - // less than 2 buffers is never allowed - if (bufferCount < 2) - return BAD_VALUE; - - // when there is non client-buffer-count in effect, the client is not - // allowed to dequeue more than one buffer at a time, - // so the next time they dequeue a buffer, we know that they don't - // own one. the actual resizing will happen during the next - // dequeueBuffer. - - mServerBufferCount = bufferCount; - } - return OK; -} - status_t SurfaceTexture::setBufferCountServer(int bufferCount) { Mutex::Autolock lock(mMutex); return setBufferCountServerLocked(bufferCount); } -status_t SurfaceTexture::setBufferCount(int bufferCount) { - ST_LOGV("setBufferCount: count=%d", bufferCount); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (bufferCount > NUM_BUFFER_SLOTS) { - ST_LOGE("setBufferCount: bufferCount larger than slots available"); - return BAD_VALUE; - } - - // Error out if the user has dequeued buffers - for (int i=0 ; i<mBufferCount ; i++) { - if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { - ST_LOGE("setBufferCount: client owns some buffers"); - return -EINVAL; - } - } - - const int minBufferSlots = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - if (bufferCount == 0) { - mClientBufferCount = 0; - bufferCount = (mServerBufferCount >= minBufferSlots) ? - mServerBufferCount : minBufferSlots; - return setBufferCountServerLocked(bufferCount); - } - - if (bufferCount < minBufferSlots) { - ST_LOGE("setBufferCount: requested buffer count (%d) is less than " - "minimum (%d)", bufferCount, minBufferSlots); - return BAD_VALUE; - } - - // here we're guaranteed that the client doesn't have dequeued buffers - // and will release all of its buffer references. - freeAllBuffersLocked(); - mBufferCount = bufferCount; - mClientBufferCount = bufferCount; - mCurrentTexture = INVALID_BUFFER_SLOT; - mQueue.clear(); - mDequeueCondition.signal(); - return OK; -} status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { @@ -258,496 +143,6 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - ST_LOGV("requestBuffer: slot=%d", slot); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (slot < 0 || mBufferCount <= slot) { - ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - mBufferCount, slot); - return BAD_VALUE; - } - mSlots[slot].mRequestBufferCalled = true; - *buf = mSlots[slot].mGraphicBuffer; - return NO_ERROR; -} - -status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) { - ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); - - if ((w && !h) || (!w && h)) { - ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); - return BAD_VALUE; - } - - status_t returnFlags(OK); - EGLDisplay dpy = EGL_NO_DISPLAY; - EGLSyncKHR fence = EGL_NO_SYNC_KHR; - - { // Scope for the lock - Mutex::Autolock lock(mMutex); - - int found = -1; - int foundSync = -1; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } - - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } - - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; - } - - // if buffer is FREE it CANNOT be current - ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", - i); - - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } - } - } else { - if (state == BufferSlot::FREE) { - /* We return the oldest of the free buffers to avoid - * stalling the producer if possible. This is because - * the consumer may still have pending reads of the - * buffers in flight. - */ - bool isOlder = mSlots[i].mFrameNumber < - mSlots[found].mFrameNumber; - if (found < 0 || isOlder) { - foundSync = i; - found = i; - } - } - } - } - - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " - "setting the buffer count"); - return -EINVAL; - } - - // See whether a buffer has been queued since the last - // setBufferCount so we know whether to perform the - // MIN_UNDEQUEUED_BUFFERS check below. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; - } - } - - // we're in synchronous mode and didn't find a buffer, we need to - // wait for some buffers to be consumed - tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); - if (tryAgain) { - mDequeueCondition.wait(mMutex); - } - } - - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } - - if (found == INVALID_BUFFER_SLOT) { - // This should not happen. - ST_LOGE("dequeueBuffer: no available buffer slots"); - return -EBUSY; - } - - const int buf = found; - *outBuf = found; - - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } - - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; - } - - // buffer is now in DEQUEUED (but can also be current at the same time, - // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; - } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; - if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[buf].mEglDisplay, - mSlots[buf].mEglImage); - mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - } - if (mCurrentTexture == buf) { - // The current texture no longer references the buffer in this slot - // since we just allocated a new buffer. - mCurrentTexture = INVALID_BUFFER_SLOT; - } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; - } - - dpy = mSlots[buf].mEglDisplay; - fence = mSlots[buf].mFence; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; - } - - if (fence != EGL_NO_SYNC_KHR) { - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - // If something goes wrong, log the error, but return the buffer without - // synchronizing access to it. It's too late at this point to abort the - // dequeue operation. - if (result == EGL_FALSE) { - ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("dequeueBuffer: timeout waiting for fence"); - } - eglDestroySyncKHR(dpy, fence); - } - - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, - mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); - - return returnFlags; -} - -status_t SurfaceTexture::setSynchronousMode(bool enabled) { - ST_LOGV("setSynchronousMode: enabled=%d", enabled); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - status_t err = OK; - if (!mAllowSynchronousMode && enabled) - return err; - - if (!enabled) { - // going to asynchronous mode, drain the queue - err = drainQueueLocked(); - if (err != NO_ERROR) - return err; - } - - if (mSynchronousMode != enabled) { - // - if we're going to asynchronous mode, the queue is guaranteed to be - // empty here - // - if the client set the number of buffers, we're guaranteed that - // we have at least 3 (because we don't allow less) - mSynchronousMode = enabled; - mDequeueCondition.signal(); - } - return err; -} - -status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); - - sp<FrameAvailableListener> listener; - - { // scope for the lock - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return -EINVAL; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("queueBuffer: slot %d is not owned by the client " - "(state=%d)", buf, mSlots[buf].mBufferState); - return -EINVAL; - } else if (buf == mCurrentTexture) { - ST_LOGE("queueBuffer: slot %d is current!", buf); - return -EINVAL; - } else if (!mSlots[buf].mRequestBufferCalled) { - ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " - "buffer", buf); - return -EINVAL; - } - - if (mSynchronousMode) { - // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); - - // Synchronous mode always signals that an additional frame should - // be consumed. - listener = mFrameAvailableListener; - } else { - // In asynchronous mode we only keep the most recent buffer. - if (mQueue.empty()) { - mQueue.push_back(buf); - - // Asynchronous mode only signals that a frame should be - // consumed if no previous frame was pending. If a frame were - // pending then the consumer would have already been notified. - listener = mFrameAvailableListener; - } else { - Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; - // and we record the new buffer index in the queued list - *front = buf; - } - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mCrop = mNextCrop; - mSlots[buf].mTransform = mNextTransform; - mSlots[buf].mScalingMode = mNextScalingMode; - mSlots[buf].mTimestamp = timestamp; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - - mDequeueCondition.signal(); - - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } // scope for the lock - - // call back without lock held - if (listener != 0) { - listener->onFrameAvailable(); - } - return OK; -} - -void SurfaceTexture::cancelBuffer(int buf) { - ST_LOGV("cancelBuffer: slot=%d", buf); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); - return; - } - - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", - buf, mSlots[buf].mBufferState); - return; - } - mSlots[buf].mBufferState = BufferSlot::FREE; - mSlots[buf].mFrameNumber = 0; - mDequeueCondition.signal(); -} - -status_t SurfaceTexture::setCrop(const Rect& crop) { - ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, - crop.bottom); - - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setCrop: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextCrop = crop; - return OK; -} - -status_t SurfaceTexture::setTransform(uint32_t transform) { - ST_LOGV("setTransform: xform=%#x", transform); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setTransform: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextTransform = transform; - return OK; -} - -status_t SurfaceTexture::connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("connect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("connect: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi != NO_CONNECTED_API) { - ST_LOGE("connect: already connected (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } else { - mConnectedApi = api; - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::disconnect(int api) { - ST_LOGV("disconnect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // it is not really an error to disconnect after the surface - // has been abandoned, it should just be a no-op. - return NO_ERROR; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); - mConnectedApi = NO_CONNECTED_API; - mNextCrop.makeInvalid(); - mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; - mNextTransform = 0; - mDequeueCondition.signal(); - } else { - ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } - break; - default: - ST_LOGE("disconnect: unknown API %d", api); - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::setScalingMode(int mode) { - ST_LOGV("setScalingMode: mode=%d", mode); - - switch (mode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - break; - default: - ST_LOGE("unknown scaling mode: %d", mode); - return BAD_VALUE; - } - - Mutex::Autolock lock(mMutex); - mNextScalingMode = mode; - return OK; -} - status_t SurfaceTexture::updateTexImage() { ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -980,69 +375,6 @@ void SurfaceTexture::setFrameAvailableListener( mFrameAvailableListener = listener; } -void SurfaceTexture::freeBufferLocked(int i) { - mSlots[i].mGraphicBuffer = 0; - mSlots[i].mBufferState = BufferSlot::FREE; - mSlots[i].mFrameNumber = 0; - if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); - mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[i].mEglDisplay = EGL_NO_DISPLAY; - } -} - -void SurfaceTexture::freeAllBuffersLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersLocked called but mQueue is not empty"); - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - freeBufferLocked(i); - } -} - -void SurfaceTexture::freeAllBuffersExceptHeadLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { - freeBufferLocked(i); - } - } -} - -status_t SurfaceTexture::drainQueueLocked() { - while (mSynchronousMode && !mQueue.isEmpty()) { - mDequeueCondition.wait(mMutex); - if (mAbandoned) { - ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (mConnectedApi == NO_CONNECTED_API) { - ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!"); - return NO_INIT; - } - } - return NO_ERROR; -} - -status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() { - status_t err = drainQueueLocked(); - if (err == NO_ERROR) { - if (mSynchronousMode) { - freeAllBuffersLocked(); - } else { - freeAllBuffersExceptHeadLocked(); - } - } - return err; -} - EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 5c53902db41c..10a0efef37e3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -835,7 +835,7 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, - (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0)) + (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false)) == null) { // Ah, this recipient is unavailable. Finish it if necessary, // and mark the broadcast record as ready for the next. @@ -1136,6 +1136,17 @@ public final class ActivityManagerService extends ActivityManagerNative final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>(); /** + * The currently running isolated processes. + */ + final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>(); + + /** + * Counter for assigning isolated process uids, to avoid frequently reusing the + * same ones. + */ + int mNextIsolatedProcessUid = 0; + + /** * The currently running heavy-weight process, if any. */ ProcessRecord mHeavyWeightProcess = null; @@ -2099,11 +2110,11 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mSelf) { ProcessRecord app = mSelf.newProcessRecordLocked( mSystemThread.getApplicationThread(), info, - info.processName); + info.processName, false); app.persistent = true; app.pid = MY_PID; app.maxAdj = ProcessList.SYSTEM_ADJ; - mSelf.mProcessNames.put(app.processName, app.info.uid, app); + mSelf.mProcessNames.put(app.processName, app.uid, app); synchronized (mSelf.mPidsSelfLocked) { mSelf.mPidsSelfLocked.put(app.pid, app); } @@ -2621,8 +2632,15 @@ public final class ActivityManagerService extends ActivityManagerNative final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, - String hostingType, ComponentName hostingName, boolean allowWhileBooting) { - ProcessRecord app = getProcessRecordLocked(processName, info.uid); + String hostingType, ComponentName hostingName, boolean allowWhileBooting, + boolean isolated) { + ProcessRecord app; + if (!isolated) { + app = getProcessRecordLocked(processName, info.uid); + } else { + // If this is an isolated process, it can't re-use an existing process. + app = null; + } // We don't have to do anything more if: // (1) There is an existing application record; and // (2) The caller doesn't think it is dead, OR there is no thread @@ -2651,36 +2669,46 @@ public final class ActivityManagerService extends ActivityManagerNative String hostingNameStr = hostingName != null ? hostingName.flattenToShortString() : null; - - if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) { - // If we are in the background, then check to see if this process - // is bad. If so, we will just silently fail. - if (mBadProcesses.get(info.processName, info.uid) != null) { - if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + + if (!isolated) { + if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) { + // If we are in the background, then check to see if this process + // is bad. If so, we will just silently fail. + if (mBadProcesses.get(info.processName, info.uid) != null) { + if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + + "/" + info.processName); + return null; + } + } else { + // When the user is explicitly starting a process, then clear its + // crash count so that we won't make it bad until they see at + // least one crash dialog again, and make the process good again + // if it had been bad. + if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + info.processName); - return null; - } - } else { - // When the user is explicitly starting a process, then clear its - // crash count so that we won't make it bad until they see at - // least one crash dialog again, and make the process good again - // if it had been bad. - if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid - + "/" + info.processName); - mProcessCrashTimes.remove(info.processName, info.uid); - if (mBadProcesses.get(info.processName, info.uid) != null) { - EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid, - info.processName); - mBadProcesses.remove(info.processName, info.uid); - if (app != null) { - app.bad = false; + mProcessCrashTimes.remove(info.processName, info.uid); + if (mBadProcesses.get(info.processName, info.uid) != null) { + EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid, + info.processName); + mBadProcesses.remove(info.processName, info.uid); + if (app != null) { + app.bad = false; + } } } } - + if (app == null) { - app = newProcessRecordLocked(null, info, processName); - mProcessNames.put(processName, info.uid, app); + app = newProcessRecordLocked(null, info, processName, isolated); + if (app == null) { + Slog.w(TAG, "Failed making new process record for " + + processName + "/" + info.uid + " isolated=" + isolated); + return null; + } + mProcessNames.put(processName, app.uid, app); + if (isolated) { + mIsolatedProcesses.put(app.uid, app); + } } else { // If this is a new package in the process, add the package to the list app.addPackage(info.packageName); @@ -2726,14 +2754,16 @@ public final class ActivityManagerService extends ActivityManagerNative mProcDeaths[0] = 0; try { - int uid = app.info.uid; + int uid = app.uid; int[] gids = null; - try { - gids = mContext.getPackageManager().getPackageGids( - app.info.packageName); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Unable to retrieve gids", e); + if (!app.isolated) { + try { + gids = mContext.getPackageManager().getPackageGids( + app.info.packageName); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Unable to retrieve gids", e); + } } if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL @@ -3405,7 +3435,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mPidsSelfLocked) { for (int i=0; i<mPidsSelfLocked.size(); i++) { ProcessRecord p = mPidsSelfLocked.valueAt(i); - if (p.info.uid != uid) { + if (p.uid != uid) { continue; } if (p.pid == initialPid) { @@ -4389,6 +4419,7 @@ public final class ActivityManagerService extends ActivityManagerNative service.app.removed = true; } service.app = null; + service.isolatedProc = null; services.add(service); } } @@ -4434,12 +4465,13 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; - final int uid = app.info.uid; + final int uid = app.uid; if (DEBUG_PROCESSES) Slog.d( TAG, "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); mProcessNames.remove(name, uid); + mIsolatedProcesses.remove(app.uid); if (mHeavyWeightProcess == app) { mHeavyWeightProcess = null; mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG); @@ -4456,9 +4488,9 @@ public final class ActivityManagerService extends ActivityManagerNative mLruProcesses.remove(app); Process.killProcessQuiet(pid); - if (app.persistent) { + if (app.persistent && !app.isolated) { if (!callerWillRestart) { - addAppLocked(app.info); + addAppLocked(app.info, false); } else { needRestart = true; } @@ -4483,9 +4515,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (gone) { Slog.w(TAG, "Process " + app + " failed to attach"); - EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid, + EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.uid, app.processName); - mProcessNames.remove(app.processName, app.info.uid); + mProcessNames.remove(app.processName, app.uid); + mIsolatedProcesses.remove(app.uid); if (mHeavyWeightProcess == app) { mHeavyWeightProcess = null; mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG); @@ -4495,9 +4528,11 @@ public final class ActivityManagerService extends ActivityManagerNative // Take care of any services that are waiting for the process. for (int i=0; i<mPendingServices.size(); i++) { ServiceRecord sr = mPendingServices.get(i); - if (app.info.uid == sr.appInfo.uid - && app.processName.equals(sr.processName)) { + if ((app.uid == sr.appInfo.uid + && app.processName.equals(sr.processName)) + || sr.isolatedProc == app) { Slog.w(TAG, "Forcing bringing down service: " + sr); + sr.isolatedProc = null; mPendingServices.remove(i); i--; bringDownServiceLocked(sr, true); @@ -4678,7 +4713,7 @@ public final class ActivityManagerService extends ActivityManagerNative // See if the top visible activity is waiting to run in this process... ActivityRecord hr = mMainStack.topRunningActivityLocked(null); if (hr != null && normalMode) { - if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid + if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { if (mMainStack.realStartActivityLocked(hr, app, true, true)) { @@ -4700,8 +4735,8 @@ public final class ActivityManagerService extends ActivityManagerNative try { for (int i=0; i<mPendingServices.size(); i++) { sr = mPendingServices.get(i); - if (app.info.uid != sr.appInfo.uid - || !processName.equals(sr.processName)) { + if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid + || !processName.equals(sr.processName))) { continue; } @@ -4728,7 +4763,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // Check whether the next backup agent is in this process... - if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { + if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) { if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app); ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { @@ -5183,7 +5218,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mPidsSelfLocked) { ProcessRecord pr = mPidsSelfLocked.get(pid); - if (pr == null) { + if (pr == null && isForeground) { Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid); return; } @@ -5191,7 +5226,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (oldToken != null) { oldToken.token.unlinkToDeath(oldToken, 0); mForegroundProcesses.remove(pid); - pr.forcingToForeground = null; + if (pr != null) { + pr.forcingToForeground = null; + } changed = true; } if (isForeground && token != null) { @@ -5680,7 +5717,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("null uri"); } - grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags, + grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags, null); } } @@ -5811,8 +5848,7 @@ public final class ActivityManagerService extends ActivityManagerNative final String authority = uri.getAuthority(); ProviderInfo pi = null; - ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, - UserId.getUserId(r.info.uid)); + ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId); if (cpr != null) { pi = cpr.info; } else { @@ -5828,7 +5864,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - revokeUriPermissionLocked(r.info.uid, uri, modeFlags); + revokeUriPermissionLocked(r.uid, uri, modeFlags); } } @@ -6532,13 +6568,13 @@ public final class ActivityManagerService extends ActivityManagerNative List<ProviderInfo> providers = null; try { providers = AppGlobals.getPackageManager(). - queryContentProviders(app.processName, app.info.uid, + queryContentProviders(app.processName, app.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS); } catch (RemoteException ex) { } if (DEBUG_MU) - Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid); - int userId = UserId.getUserId(app.info.uid); + Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid); + int userId = app.userId; if (providers != null) { final int N = providers.size(); for (int i=0; i<N; i++) { @@ -6564,7 +6600,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final String checkContentProviderPermissionLocked( ProviderInfo cpi, ProcessRecord r) { final int callingPid = (r != null) ? r.pid : Binder.getCallingPid(); - final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid(); + final int callingUid = (r != null) ? r.uid : Binder.getCallingUid(); if (checkComponentPermission(cpi.readPermission, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { @@ -6680,7 +6716,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // First check if this content provider has been published... - int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid()); + int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid()); cpr = mProviderMap.getProviderByName(name, userId); boolean providerRunning = cpr != null; if (providerRunning) { @@ -6815,7 +6851,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_PROVIDER) { RuntimeException e = new RuntimeException("here"); - Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid + Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.uid + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e); } @@ -6849,7 +6885,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, - cpi.name), false); + cpi.name), false, false); if (proc == null) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" @@ -6987,7 +7023,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) - Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid); + Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); if (r == null) { throw new SecurityException( "Unable to find app for caller " + caller @@ -7098,22 +7134,52 @@ public final class ActivityManagerService extends ActivityManagerNative // ========================================================= final ProcessRecord newProcessRecordLocked(IApplicationThread thread, - ApplicationInfo info, String customProcess) { + ApplicationInfo info, String customProcess, boolean isolated) { String proc = customProcess != null ? customProcess : info.processName; BatteryStatsImpl.Uid.Proc ps = null; BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + int uid = info.uid; + if (isolated) { + int userId = UserId.getUserId(uid); + int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1; + uid = 0; + while (true) { + if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID + || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) { + mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID; + } + uid = UserId.getUid(userId, mNextIsolatedProcessUid); + mNextIsolatedProcessUid++; + if (mIsolatedProcesses.indexOfKey(uid) < 0) { + // No process for this uid, use it. + break; + } + stepsLeft--; + if (stepsLeft <= 0) { + return null; + } + } + } synchronized (stats) { ps = stats.getProcessStatsLocked(info.uid, proc); } - return new ProcessRecord(ps, thread, info, proc); + return new ProcessRecord(ps, thread, info, proc, uid); } - final ProcessRecord addAppLocked(ApplicationInfo info) { - ProcessRecord app = getProcessRecordLocked(info.processName, info.uid); + final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) { + ProcessRecord app; + if (!isolated) { + app = getProcessRecordLocked(info.processName, info.uid); + } else { + app = null; + } if (app == null) { - app = newProcessRecordLocked(null, info, null); - mProcessNames.put(info.processName, info.uid, app); + app = newProcessRecordLocked(null, info, null, isolated); + mProcessNames.put(info.processName, app.uid, app); + if (isolated) { + mIsolatedProcesses.put(app.uid, app); + } updateLruProcessLocked(app, true, true); } @@ -7862,7 +7928,7 @@ public final class ActivityManagerService extends ActivityManagerNative = (ApplicationInfo)apps.get(i); if (info != null && !info.packageName.equals("android")) { - addAppLocked(info); + addAppLocked(info, false); } } } @@ -7961,14 +8027,18 @@ public final class ActivityManagerService extends ActivityManagerNative private boolean handleAppCrashLocked(ProcessRecord app) { long now = SystemClock.uptimeMillis(); - Long crashTime = mProcessCrashTimes.get(app.info.processName, - app.info.uid); + Long crashTime; + if (!app.isolated) { + crashTime = mProcessCrashTimes.get(app.info.processName, app.uid); + } else { + crashTime = null; + } if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { // This process loses! Slog.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, - app.info.processName, app.info.uid); + app.info.processName, app.uid); for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.app == app) { @@ -7982,11 +8052,15 @@ public final class ActivityManagerService extends ActivityManagerNative // explicitly does so... but for persistent process, we really // need to keep it running. If a persistent process is actually // repeatedly crashing, then badness for everyone. - EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.info.uid, + EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.uid, app.info.processName); - mBadProcesses.put(app.info.processName, app.info.uid, now); + if (!app.isolated) { + // XXX We don't have a way to mark isolated processes + // as bad, since they don't have a peristent identity. + mBadProcesses.put(app.info.processName, app.uid, now); + mProcessCrashTimes.remove(app.info.processName, app.uid); + } app.bad = true; - mProcessCrashTimes.remove(app.info.processName, app.info.uid); app.removed = true; // Don't let services in this process be restarted and potentially // annoy the user repeatedly. Unless it is persistent, since those @@ -8058,7 +8132,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } - mProcessCrashTimes.put(app.info.processName, app.info.uid, now); + if (!app.isolated) { + // XXX Can't keep track of crash times for isolated processes, + // because they don't have a perisistent identity. + mProcessCrashTimes.put(app.info.processName, app.uid, now); + } + return true; } @@ -8556,8 +8635,10 @@ public final class ActivityManagerService extends ActivityManagerNative Intent appErrorIntent = null; synchronized (this) { - if (r != null) { - mProcessCrashTimes.put(r.info.processName, r.info.uid, + if (r != null && !r.isolated) { + // XXX Can't keep track of crash time for isolated processes, + // since they don't have a persistent identity. + mProcessCrashTimes.put(r.info.processName, r.uid, SystemClock.uptimeMillis()); } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { @@ -9095,7 +9176,21 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - + + if (mIsolatedProcesses.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Isolated process list (sorted by uid):"); + for (int i=0; i<mIsolatedProcesses.size(); i++) { + ProcessRecord r = mIsolatedProcesses.valueAt(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } + pw.println(String.format("%sIsolated #%2d: %s", + " ", i, r.toString())); + } + } + if (mLruProcesses.size() > 0) { if (needSep) pw.println(" "); needSep = true; @@ -10880,6 +10975,7 @@ public final class ActivityManagerService extends ActivityManagerNative sr.stats.stopLaunchedLocked(); } sr.app = null; + sr.isolatedProc = null; sr.executeNesting = 0; if (mStoppingServices.remove(sr)) { if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); @@ -11115,10 +11211,11 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - if (!app.persistent) { + if (!app.persistent || app.isolated) { if (DEBUG_PROCESSES) Slog.v(TAG, "Removing non-persistent process during cleanup: " + app); - mProcessNames.remove(app.processName, app.info.uid); + mProcessNames.remove(app.processName, app.uid); + mIsolatedProcesses.remove(app.uid); if (mHeavyWeightProcess == app) { mHeavyWeightProcess = null; mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG); @@ -11143,10 +11240,10 @@ public final class ActivityManagerService extends ActivityManagerNative mPreviousProcess = null; } - if (restart) { + if (restart && !app.isolated) { // We have components that still need to be running in the // process, so re-launch it. - mProcessNames.put(app.processName, app.info.uid, app); + mProcessNames.put(app.processName, app.uid, app); startProcessLocked(app, "restart", app.processName); } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! @@ -11548,7 +11645,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_MU) Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid - + ", ProcessRecord.uid = " + app.info.uid); + + ", ProcessRecord.uid = " + app.uid); r.app = app; r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); @@ -11743,35 +11840,53 @@ public final class ActivityManagerService extends ActivityManagerNative + r.packageName + ": " + e); } + final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String appName = r.processName; - ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); - if (DEBUG_MU) - Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); - if (app != null && app.thread != null) { - try { - app.addPackage(r.appInfo.packageName); - realStartServiceLocked(r, app); - return true; - } catch (RemoteException e) { - Slog.w(TAG, "Exception when starting service " + r.shortName, e); - } + ProcessRecord app; + + if (!isolated) { + app = getProcessRecordLocked(appName, r.appInfo.uid); + if (DEBUG_MU) + Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); + if (app != null && app.thread != null) { + try { + app.addPackage(r.appInfo.packageName); + realStartServiceLocked(r, app); + return true; + } catch (RemoteException e) { + Slog.w(TAG, "Exception when starting service " + r.shortName, e); + } - // If a dead object exception was thrown -- fall through to - // restart the application. + // If a dead object exception was thrown -- fall through to + // restart the application. + } + } else { + // If this service runs in an isolated process, then each time + // we call startProcessLocked() we will get a new isolated + // process, starting another process if we are currently waiting + // for a previous process to come up. To deal with this, we store + // in the service any current isolated process it is running in or + // waiting to have come up. + app = r.isolatedProc; } // Not running -- get it started, and enqueue this service record // to be executed when the app comes up. - if (startProcessLocked(appName, r.appInfo, true, intentFlags, - "service", r.name, false) == null) { - Slog.w(TAG, "Unable to launch app " - + r.appInfo.packageName + "/" - + r.appInfo.uid + " for service " - + r.intent.getIntent() + ": process is bad"); - bringDownServiceLocked(r, true); - return false; + if (app == null) { + if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags, + "service", r.name, false, isolated)) == null) { + Slog.w(TAG, "Unable to launch app " + + r.appInfo.packageName + "/" + + r.appInfo.uid + " for service " + + r.intent.getIntent() + ": process is bad"); + bringDownServiceLocked(r, true); + return false; + } + if (isolated) { + r.isolatedProc = app; + } } - + if (!mPendingServices.contains(r)) { mPendingServices.add(r); } @@ -12687,7 +12802,7 @@ public final class ActivityManagerService extends ActivityManagerNative : new ComponentName("android", "FullBackupAgent"); // startProcessLocked() returns existing proc's record if it's already running ProcessRecord proc = startProcessLocked(app.processName, app, - false, 0, "backup", hostingName, false); + false, 0, "backup", hostingName, false, false); if (proc == null) { Slog.e(TAG, "Unable to start backup agent process " + r); return false; @@ -13632,7 +13747,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); // Instrumentation can kill and relaunch even persistent processes forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true); - ProcessRecord app = addAppLocked(ai); + ProcessRecord app = addAppLocked(ai, false); app.instrumentationClass = className; app.instrumentationInfo = ai; app.instrumentationProfileFile = profileFile; @@ -14845,6 +14960,20 @@ public final class ActivityManagerService extends ActivityManagerNative Process.killProcessQuiet(app.pid); } } + if (!app.killedBackground && app.isolated && app.services.size() <= 0) { + // If this is an isolated process, and there are no + // services running in it, then the process is no longer + // needed. We agressively kill these because we can by + // definition not re-use the same process again, and it is + // good to avoid having whatever code was running in them + // left sitting around after no longer needed. + Slog.i(TAG, "Isolated process " + app.processName + + " (pid " + app.pid + ") no longer needed"); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "isolated not needed"); + app.killedBackground = true; + Process.killProcessQuiet(app.pid); + } } } @@ -14983,7 +15112,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.persistent) { if (app.persistent) { - addAppLocked(app.info); + addAppLocked(app.info, false); } } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 0d217606386a..f59f0c1a9055 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -761,7 +761,7 @@ final class ActivityStack { } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, - "activity", r.intent.getComponent(), false); + "activity", r.intent.getComponent(), false, false); } void stopIfSleepingLocked() { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 72292beb56d5..b64261d9805d 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -28,7 +28,9 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.SystemClock; +import android.os.UserId; import android.util.PrintWriterPrinter; import android.util.TimeUtils; @@ -44,6 +46,9 @@ import java.util.HashSet; class ProcessRecord { final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics final ApplicationInfo info; // all about the first app in the process + final boolean isolated; // true if this is a special isolated process + final int uid; // uid of process; may be different from 'info' if isolated + final int userId; // user of process. final String processName; // name of the process // List of packages running in the process final HashSet<String> pkgList = new HashSet<String>(); @@ -147,6 +152,12 @@ class ProcessRecord { void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); + pw.print(prefix); pw.print("user #"); pw.print(userId); + pw.print(" uid="); pw.print(info.uid); + if (uid != info.uid) { + pw.print(" ISOLATED uid="); pw.print(uid); + } + pw.println(); if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); } @@ -267,9 +278,12 @@ class ProcessRecord { } ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread, - ApplicationInfo _info, String _processName) { + ApplicationInfo _info, String _processName, int _uid) { batteryStats = _batteryStats; info = _info; + isolated = _info.uid != _uid; + uid = _uid; + userId = UserId.getUserId(_uid); processName = _processName; pkgList.add(_info.packageName); thread = _thread; @@ -343,7 +357,18 @@ class ProcessRecord { sb.append(':'); sb.append(processName); sb.append('/'); - sb.append(info.uid); + if (info.uid < Process.FIRST_APPLICATION_UID) { + sb.append(uid); + } else { + sb.append('u'); + sb.append(userId); + sb.append('a'); + sb.append(info.uid%Process.FIRST_APPLICATION_UID); + if (uid != info.uid) { + sb.append('i'); + sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID); + } + } } public String toString() { diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 75ba9474b780..daa3653d0bc2 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -80,6 +80,7 @@ class ServiceRecord extends Binder { // IBinder -> ConnectionRecord of all bound clients ProcessRecord app; // where this service is running or null. + ProcessRecord isolatedProc; // keep track of isolated process, if requested boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. @@ -210,6 +211,9 @@ class ServiceRecord extends Binder { } pw.print(prefix); pw.print("dataDir="); pw.println(dataDir); pw.print(prefix); pw.print("app="); pw.println(app); + if (isolatedProc != null) { + pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc); + } if (isForeground || foregroundId != 0) { pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); pw.print(" foregroundId="); pw.print(foregroundId); |