Merge "Import translations. DO NOT MERGE"
diff --git a/api/16.txt b/api/16.txt
index 8e07844..02a5104 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -15052,7 +15052,7 @@
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 574af6b..2037515 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -562,6 +562,7 @@
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 @@
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 @@
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 e4cfc99..bf632a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -134,7 +134,7 @@
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 @@
}
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 fcbcd81..d9bbb4a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -119,7 +119,7 @@
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 faee873..2023f82 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2581,6 +2581,11 @@
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 612e345..7ee84ab 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -42,10 +42,17 @@
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 cdf235d..0746af8 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -119,7 +119,19 @@
* 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 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 dbd49fb..92c59ab 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 e3c2bd8..1b91643 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 0000000..991a181
--- /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 a8c7672..4318f0f 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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 b6f5b9e..2f4ac62 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -3,6 +3,7 @@
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 0000000..c7e2c0f
--- /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 3abe84a..be1bcd1 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 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 @@
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 @@
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 @@
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 5c53902..10a0efe 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -835,7 +835,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
service.app.removed = true;
}
service.app = null;
+ service.isolatedProc = null;
services.add(service);
}
}
@@ -4434,12 +4465,13 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
}
// 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 @@
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 @@
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 @@
throw new IllegalArgumentException("null uri");
}
- grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
+ grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags,
null);
}
}
@@ -5811,8 +5848,7 @@
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 @@
return;
}
- revokeUriPermissionLocked(r.info.uid, uri, modeFlags);
+ revokeUriPermissionLocked(r.uid, uri, modeFlags);
}
}
@@ -6532,13 +6568,13 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
// =========================================================
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 @@
= (ApplicationInfo)apps.get(i);
if (info != null &&
!info.packageName.equals("android")) {
- addAppLocked(info);
+ addAppLocked(info, false);
}
}
}
@@ -7961,14 +8027,18 @@
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 @@
// 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 @@
}
}
- 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 @@
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 @@
}
}
}
-
+
+ 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 @@
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 @@
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 @@
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 @@
}
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 @@
+ 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 a dead object exception was thrown -- fall through to
- // restart the application.
+ 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.
+ }
+ } 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 @@
: 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 @@
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 @@
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 @@
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 0d21760..f59f0c1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -761,7 +761,7 @@
}
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 72292be..b64261d 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.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 @@
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 @@
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 @@
}
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 @@
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 75ba9474..daa3653 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -80,6 +80,7 @@
// 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 @@
}
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);