Merge "libgui: Remove unnecessary casts in SurfaceControl"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 34dc9fe..099c3df 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -40,7 +40,7 @@
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
-enum { MAX_SYS_FILES = 8 };
+enum { MAX_SYS_FILES = 10 };
const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines";
@@ -99,6 +99,12 @@
{ REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
} },
{ "disk", "Disk I/O", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" },
{ REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
{ REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
{ REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 8f4da65..671d031 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -1022,7 +1022,13 @@
// Make the profile directory write-only for group and other. Owner can rwx it.
if (chmod(profile_dir, 0711) < 0) {
ALOGE("cannot chown profile dir '%s': %s\n", profile_dir, strerror(errno));
- unlink(profile_dir);
+ rmdir(profile_dir);
+ return -1;
+ }
+
+ if (selinux_android_restorecon(profile_dir, 0) < 0) {
+ ALOGE("cannot restorecon profile dir '%s': %s\n", profile_dir, strerror(errno));
+ rmdir(profile_dir);
return -1;
}
}
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 129ea3e..b4e7ebe 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -281,6 +281,28 @@
*/
int ASensor_getMinDelay(ASensor const* sensor);
+/*
+ * Returns the maximum size of batches for this sensor. Batches will often be
+ * smaller, as the hardware fifo might be used for other sensors.
+ */
+int ASensor_getFifoMaxEventCount(ASensor const* sensor);
+
+/*
+ * Returns the hardware batch fifo size reserved to this sensor.
+ */
+int ASensor_getFifoReservedEventCount(ASensor const* sensor);
+
+/*
+ * Returns this sensor's string type.
+ */
+const char* ASensor_getStringType(ASensor const* sensor);
+
+/*
+ * Returns the permission required to see or access this sensor, or the
+ * empty string if none is required.
+ */
+const char* ASensor_getRequiredPermission(ASensor const* sensor);
+
#ifdef __cplusplus
};
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 99147cd..311926d 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -199,6 +199,10 @@
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachProducerBuffer(int slot);
+ // See IGraphicBufferProducer::detachNextBuffer
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
+
// See IGraphicBufferProducer::attachBuffer
virtual status_t attachProducerBuffer(int* slot,
const sp<GraphicBuffer>& buffer);
@@ -228,16 +232,8 @@
// will usually be the one obtained from dequeueBuffer.
virtual void cancelBuffer(int buf, const sp<Fence>& fence);
- // connect attempts to connect a producer API to the BufferQueue. This
- // must be called before any other IGraphicBufferProducer methods are
- // called except for getAllocator. A consumer must already be connected.
- //
- // This method will fail if connect was previously called on the
- // BufferQueue and no corresponding disconnect call was made (i.e. if
- // it's still connected to a producer).
- //
- // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
- virtual status_t connect(const sp<IBinder>& token,
+ // See IGraphicBufferProducer::connect
+ virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
// disconnect attempts to disconnect a producer API from the BufferQueue.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 89f2779..cfcb7a5 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -46,9 +46,9 @@
namespace android {
class BufferItem;
-class IBinder;
class IConsumerListener;
class IGraphicBufferAlloc;
+class IProducerListener;
class BufferQueueCore : public virtual RefBase {
@@ -162,7 +162,7 @@
// mConnectedProducerToken is used to set a binder death notification on
// the producer.
- sp<IBinder> mConnectedProducerToken;
+ sp<IProducerListener> mConnectedProducerListener;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 0013b0a..9df3633 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -99,6 +99,10 @@
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachBuffer(int slot);
+ // See IGraphicBufferProducer::detachNextBuffer
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
+
// See IGraphicBufferProducer::attachBuffer
virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
@@ -140,7 +144,7 @@
// it's still connected to a producer).
//
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
- virtual status_t connect(const sp<IBinder>& token,
+ virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
// disconnect attempts to disconnect a producer API from the BufferQueue.
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index b0d4c76..e58612f 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -27,6 +27,9 @@
#include <binder/IInterface.h>
#include <ui/Rect.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
namespace android {
// ----------------------------------------------------------------------------
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 0874f03..d9e116b 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -32,6 +32,7 @@
namespace android {
// ----------------------------------------------------------------------------
+class IProducerListener;
class NativeHandle;
class Surface;
@@ -184,6 +185,27 @@
// it refers to is not currently dequeued and requested.
virtual status_t detachBuffer(int slot) = 0;
+ // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer,
+ // and detachBuffer in sequence, except for two things:
+ //
+ // 1) It is unnecessary to know the dimensions, format, or usage of the
+ // next buffer.
+ // 2) It will not block, since if it cannot find an appropriate buffer to
+ // return, it will return an error instead.
+ //
+ // Only slots that are free but still contain a GraphicBuffer will be
+ // considered, and the oldest of those will be returned. outBuffer is
+ // equivalent to outBuffer from the requestBuffer call, and outFence is
+ // equivalent to fence from the dequeueBuffer call.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned.
+ // * BAD_VALUE - either outBuffer or outFence were NULL.
+ // * NO_MEMORY - no slots were found that were both free and contained a
+ // GraphicBuffer.
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) = 0;
+
// attachBuffer attempts to transfer ownership of a buffer to the buffer
// queue. If this call succeeds, it will be as if this buffer was dequeued
// from the returned slot number. As such, this call will fail if attaching
@@ -344,9 +366,11 @@
// This method will fail if the connect was previously called on the
// IGraphicBufferProducer and no corresponding disconnect call was made.
//
- // The token needs to be any opaque binder object that lives in the
- // producer process -- it is solely used for obtaining a death notification
- // when the producer is killed.
+ // The listener is an optional binder callback object that can be used if
+ // the producer wants to be notified when the consumer releases a buffer
+ // back to the BufferQueue. It is also used to detect the death of the
+ // producer. If only the latter functionality is desired, there is a
+ // DummyProducerListener class in IProducerListener.h that can be used.
//
// The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
//
@@ -370,7 +394,7 @@
//
// Additional negative errors may be returned by the internals, they
// should be treated as opaque fatal unrecoverable errors.
- virtual status_t connect(const sp<IBinder>& token,
+ virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;
// disconnect attempts to disconnect a client API from the
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
new file mode 100644
index 0000000..3848a6c
--- /dev/null
+++ b/include/gui/IProducerListener.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 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_IPRODUCERLISTENER_H
+#define ANDROID_GUI_IPRODUCERLISTENER_H
+
+#include <binder/IInterface.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+// ProducerListener is the interface through which the BufferQueue notifies the
+// producer of events that the producer may wish to react to. Because the
+// producer will generally have a mutex that is locked during calls from the
+// producer to the BufferQueue, these calls from the BufferQueue to the
+// producer *MUST* be called only when the BufferQueue mutex is NOT locked.
+
+class ProducerListener : public virtual RefBase
+{
+public:
+ ProducerListener() {}
+ virtual ~ProducerListener() {}
+
+ // onBufferReleased is called from IGraphicBufferConsumer::releaseBuffer to
+ // notify the producer that a new buffer is free and ready to be dequeued.
+ //
+ // This is called without any lock held and can be called concurrently by
+ // multiple threads.
+ virtual void onBufferReleased() = 0; // Asynchronous
+};
+
+class IProducerListener : public ProducerListener, public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ProducerListener)
+};
+
+class BnProducerListener : public BnInterface<IProducerListener>
+{
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
+class DummyProducerListener : public BnProducerListener
+{
+public:
+ virtual void onBufferReleased() {}
+};
+
+} // namespace android
+
+#endif
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 35dcd4e..efde5db 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -22,9 +22,12 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
#include <binder/IInterface.h>
+#include <ui/FrameStats.h>
#include <ui/PixelFormat.h>
#include <gui/IGraphicBufferAlloc.h>
@@ -122,6 +125,19 @@
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
bool useIdentityTransform) = 0;
+
+
+ /* Clears the frame statistics for animations.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t clearAnimationFrameStats() = 0;
+
+ /* Gets the frame statistics for animations.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
};
// ----------------------------------------------------------------------------
@@ -145,6 +161,8 @@
GET_DISPLAY_INFO,
CONNECT_DISPLAY,
CAPTURE_SCREEN,
+ CLEAR_ANIMATION_FRAME_STATS,
+ GET_ANIMATION_FRAME_STATS
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h
index cb9816f..58c1f6a 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -25,6 +25,7 @@
#include <binder/IInterface.h>
+#include <ui/FrameStats.h>
#include <ui/PixelFormat.h>
namespace android {
@@ -65,6 +66,16 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t destroySurface(const sp<IBinder>& handle) = 0;
+
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
+ virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const = 0;
+
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
+ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 0c81426..033b262 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -69,6 +69,8 @@
int32_t getVersion() const;
int32_t getFifoReservedEventCount() const;
int32_t getFifoMaxEventCount() const;
+ const String8& getStringType() const;
+ const String8& getRequiredPermission() const;
// LightFlattenable protocol
inline bool isFixedSize() const { return false; }
@@ -89,6 +91,10 @@
int32_t mVersion;
int32_t mFifoReservedEventCount;
int32_t mFifoMaxEventCount;
+ String8 mStringType;
+ String8 mRequiredPermission;
+ static void flattenString8(void*& buffer, size_t& size, const String8& string8);
+ static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/StreamSplitter.h b/include/gui/StreamSplitter.h
new file mode 100644
index 0000000..f927953
--- /dev/null
+++ b/include/gui/StreamSplitter.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2014 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_STREAMSPLITTER_H
+#define ANDROID_GUI_STREAMSPLITTER_H
+
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+
+#include <utils/Condition.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+class IGraphicBufferConsumer;
+class IGraphicBufferProducer;
+
+// StreamSplitter is an autonomous class that manages one input BufferQueue
+// and multiple output BufferQueues. By using the buffer attach and detach logic
+// in BufferQueue, it is able to present the illusion of a single split
+// BufferQueue, where each buffer queued to the input is available to be
+// acquired by each of the outputs, and is able to be dequeued by the input
+// again only once all of the outputs have released it.
+class StreamSplitter : public BnConsumerListener {
+public:
+ // createSplitter creates a new splitter, outSplitter, using inputQueue as
+ // the input BufferQueue. Output BufferQueues must be added using addOutput
+ // before queueing any buffers to the input.
+ //
+ // A return value other than NO_ERROR means that an error has occurred and
+ // outSplitter has not been modified. BAD_VALUE is returned if inputQueue or
+ // outSplitter is NULL. See IGraphicBufferConsumer::consumerConnect for
+ // explanations of other error codes.
+ static status_t createSplitter(const sp<IGraphicBufferConsumer>& inputQueue,
+ sp<StreamSplitter>* outSplitter);
+
+ // addOutput adds an output BufferQueue to the splitter. The splitter
+ // connects to outputQueue as a CPU producer, and any buffers queued
+ // to the input will be queued to each output. It is assumed that all of the
+ // outputs are added before any buffers are queued on the input. If any
+ // output is abandoned by its consumer, the splitter will abandon its input
+ // queue (see onAbandoned).
+ //
+ // A return value other than NO_ERROR means that an error has occurred and
+ // outputQueue has not been added to the splitter. BAD_VALUE is returned if
+ // outputQueue is NULL. See IGraphicBufferProducer::connect for explanations
+ // of other error codes.
+ status_t addOutput(const sp<IGraphicBufferProducer>& outputQueue);
+
+ // setName sets the consumer name of the input queue
+ void setName(const String8& name);
+
+private:
+ // From IConsumerListener
+ //
+ // During this callback, we store some tracking information, detach the
+ // buffer from the input, and attach it to each of the outputs. This call
+ // can block if there are too many outstanding buffers. If it blocks, it
+ // will resume when onBufferReleasedByOutput releases a buffer back to the
+ // input.
+ virtual void onFrameAvailable();
+
+ // From IConsumerListener
+ // We don't care about released buffers because we detach each buffer as
+ // soon as we acquire it. See the comment for onBufferReleased below for
+ // some clarifying notes about the name.
+ virtual void onBuffersReleased() {}
+
+ // From IConsumerListener
+ // We don't care about sideband streams, since we won't be splitting them
+ virtual void onSidebandStreamChanged() {}
+
+ // This is the implementation of the onBufferReleased callback from
+ // IProducerListener. It gets called from an OutputListener (see below), and
+ // 'from' is which producer interface from which the callback was received.
+ //
+ // During this callback, we detach the buffer from the output queue that
+ // generated the callback, update our state tracking to see if this is the
+ // last output releasing the buffer, and if so, release it to the input.
+ // If we release the buffer to the input, we allow a blocked
+ // onFrameAvailable call to proceed.
+ void onBufferReleasedByOutput(const sp<IGraphicBufferProducer>& from);
+
+ // When this is called, the splitter disconnects from (i.e., abandons) its
+ // input queue and signals any waiting onFrameAvailable calls to wake up.
+ // It still processes callbacks from other outputs, but only detaches their
+ // buffers so they can continue operating until they run out of buffers to
+ // acquire. This must be called with mMutex locked.
+ void onAbandonedLocked();
+
+ // This is a thin wrapper class that lets us determine which BufferQueue
+ // the IProducerListener::onBufferReleased callback is associated with. We
+ // create one of these per output BufferQueue, and then pass the producer
+ // into onBufferReleasedByOutput above.
+ class OutputListener : public BnProducerListener,
+ public IBinder::DeathRecipient {
+ public:
+ OutputListener(const sp<StreamSplitter>& splitter,
+ const sp<IGraphicBufferProducer>& output);
+ virtual ~OutputListener();
+
+ // From IProducerListener
+ virtual void onBufferReleased();
+
+ // From IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ sp<StreamSplitter> mSplitter;
+ sp<IGraphicBufferProducer> mOutput;
+ };
+
+ class BufferTracker : public LightRefBase<BufferTracker> {
+ public:
+ BufferTracker(const sp<GraphicBuffer>& buffer);
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<Fence>& getMergedFence() const { return mMergedFence; }
+
+ void mergeFence(const sp<Fence>& with);
+
+ // Returns the new value
+ // Only called while mMutex is held
+ size_t incrementReleaseCountLocked() { return ++mReleaseCount; }
+
+ private:
+ // Only destroy through LightRefBase
+ friend LightRefBase<BufferTracker>;
+ ~BufferTracker();
+
+ // Disallow copying
+ BufferTracker(const BufferTracker& other);
+ BufferTracker& operator=(const BufferTracker& other);
+
+ sp<GraphicBuffer> mBuffer; // One instance that holds this native handle
+ sp<Fence> mMergedFence;
+ size_t mReleaseCount;
+ };
+
+ // Only called from createSplitter
+ StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue);
+
+ // Must be accessed through RefBase
+ virtual ~StreamSplitter();
+
+ static const int MAX_OUTSTANDING_BUFFERS = 2;
+
+ // mIsAbandoned is set to true when an output dies. Once the StreamSplitter
+ // has been abandoned, it will continue to detach buffers from other
+ // outputs, but it will disconnect from the input and not attempt to
+ // communicate with it further.
+ bool mIsAbandoned;
+
+ Mutex mMutex;
+ Condition mReleaseCondition;
+ int mOutstandingBuffers;
+ sp<IGraphicBufferConsumer> mInput;
+ Vector<sp<IGraphicBufferProducer> > mOutputs;
+
+ // Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking
+ // objects (which are mostly for counting how many outputs have released the
+ // buffer, but also contain merged release fences).
+ KeyedVector<uint64_t, sp<BufferTracker> > mBuffers;
+};
+
+} // namespace android
+
+#endif
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index ac53f02..6a53ccb 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -28,6 +28,7 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <ui/FrameStats.h>
#include <ui/PixelFormat.h>
#include <gui/CpuConsumer.h>
@@ -125,6 +126,12 @@
status_t setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
status_t destroySurface(const sp<IBinder>& id);
+ status_t clearLayerFrameStats(const sp<IBinder>& token) const;
+ status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
+
+ static status_t clearAnimationFrameStats();
+ static status_t getAnimationFrameStats(FrameStats* outStats);
+
static void setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
static void setDisplayLayerStack(const sp<IBinder>& token,
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index f27754c..84fb9f9 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -24,6 +24,7 @@
#include <utils/RefBase.h>
#include <utils/threads.h>
+#include <ui/FrameStats.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
@@ -52,10 +53,10 @@
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
-
+
// release surface data from java
void clear();
-
+
status_t setLayerStack(int32_t layerStack);
status_t setLayer(int32_t layer);
status_t setPosition(float x, float y);
@@ -73,6 +74,9 @@
sp<Surface> getSurface() const;
+ status_t clearLayerFrameStats() const;
+ status_t getLayerFrameStats(FrameStats* outStats) const;
+
private:
// can't be copied
SurfaceControl& operator = (SurfaceControl& rhs);
@@ -90,7 +94,7 @@
status_t validate() const;
void destroy();
-
+
sp<SurfaceComposerClient> mClient;
sp<IBinder> mHandle;
sp<IGraphicBufferProducer> mGraphicBufferProducer;
diff --git a/include/input/Input.h b/include/input/Input.h
index bb5ceaf..077a03b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -178,13 +178,9 @@
/* These flags are set by the input reader policy as it intercepts each event. */
- // Indicates that the screen was off when the event was received and the event
- // should wake the device.
- POLICY_FLAG_WOKE_HERE = 0x10000000,
-
- // Indicates that the screen was dim when the event was received and the event
- // should brighten the device.
- POLICY_FLAG_BRIGHT_HERE = 0x20000000,
+ // Indicates that the device was in an interactive state when the
+ // event was intercepted.
+ POLICY_FLAG_INTERACTIVE = 0x20000000,
// Indicates that the event should be dispatched to applications.
// The input event should still be sent to the InputDispatcher so that it can see all
diff --git a/include/ui/FrameStats.h b/include/ui/FrameStats.h
new file mode 100644
index 0000000..5fdf94d
--- /dev/null
+++ b/include/ui/FrameStats.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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_UI_FRAME_STATS_H
+#define ANDROID_UI_FRAME_STATS_H
+
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class FrameStats : public LightFlattenable<FrameStats> {
+public:
+
+ /*
+ * Approximate refresh time, in nanoseconds.
+ */
+ nsecs_t refreshPeriodNano;
+
+ /*
+ * The times in nanoseconds for when the frame contents were posted by the producer (e.g.
+ * the application). They are either explicitly set or defaulted to the time when
+ * Surface::queueBuffer() was called.
+ */
+ Vector<nsecs_t> desiredPresentTimesNano;
+
+ /*
+ * The times in milliseconds for when the frame contents were presented on the screen.
+ */
+ Vector<nsecs_t> actualPresentTimesNano;
+
+ /*
+ * The times in nanoseconds for when the frame contents were ready to be presented. Note that
+ * a frame can be posted and still it contents being rendered asynchronously in GL. In such a
+ * case these are the times when the frame contents were completely rendered (i.e. their fences
+ * signaled).
+ */
+ Vector<nsecs_t> frameReadyTimesNano;
+
+ // LightFlattenable
+ bool isFixedSize() const;
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_FRAME_STATS_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3cf628c..a92424c 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -88,7 +88,8 @@
uint32_t getUsage() const { return usage; }
PixelFormat getPixelFormat() const { return format; }
Rect getBounds() const { return Rect(width, height); }
-
+ uint64_t getId() const { return mId; }
+
status_t reallocate(uint32_t w, uint32_t h, PixelFormat f, uint32_t usage);
status_t lock(uint32_t usage, void** vaddr);
@@ -146,6 +147,8 @@
// If we're wrapping another buffer then this reference will make sure it
// doesn't get freed.
sp<ANativeWindowBuffer> mWrappedBuffer;
+
+ uint64_t mId;
};
}; // namespace android
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 0a77317..ca94aa3 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -21,6 +21,7 @@
IDisplayEventConnection.cpp \
IGraphicBufferAlloc.cpp \
IGraphicBufferProducer.cpp \
+ IProducerListener.cpp \
ISensorEventConnection.cpp \
ISensorServer.cpp \
ISurfaceComposer.cpp \
@@ -29,6 +30,7 @@
Sensor.cpp \
SensorEventQueue.cpp \
SensorManager.cpp \
+ StreamSplitter.cpp \
Surface.cpp \
SurfaceControl.cpp \
SurfaceComposerClient.cpp \
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 782afcc..da980a7 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -103,6 +103,11 @@
return mProducer->detachBuffer(slot);
}
+status_t BufferQueue::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ return mProducer->detachNextBuffer(outBuffer, outFence);
+}
+
status_t BufferQueue::attachProducerBuffer(int* slot,
const sp<GraphicBuffer>& buffer) {
return mProducer->attachBuffer(slot, buffer);
@@ -117,9 +122,9 @@
mProducer->cancelBuffer(buf, fence);
}
-status_t BufferQueue::connect(const sp<IBinder>& token,
+status_t BufferQueue::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
- return mProducer->connect(token, api, producerControlledByApp, output);
+ return mProducer->connect(listener, api, producerControlledByApp, output);
}
status_t BufferQueue::disconnect(int api) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 756cd61..e811ee0 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -22,6 +22,7 @@
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
namespace android {
@@ -242,11 +243,27 @@
mSlots[*outSlot].mGraphicBuffer = buffer;
mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
mSlots[*outSlot].mAttachedByConsumer = true;
- mSlots[*outSlot].mAcquireCalled = true;
mSlots[*outSlot].mNeedsCleanupOnRelease = false;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mFrameNumber = 0;
+ // mAcquireCalled tells BufferQueue that it doesn't need to send a valid
+ // GraphicBuffer pointer on the next acquireBuffer call, which decreases
+ // Binder traffic by not un/flattening the GraphicBuffer. However, it
+ // requires that the consumer maintain a cached copy of the slot <--> buffer
+ // mappings, which is why the consumer doesn't need the valid pointer on
+ // acquire.
+ //
+ // The StreamSplitter is one of the primary users of the attach/detach
+ // logic, and while it is running, all buffers it acquires are immediately
+ // detached, and all buffers it eventually releases are ones that were
+ // attached (as opposed to having been obtained from acquireBuffer), so it
+ // doesn't make sense to maintain the slot/buffer mappings, which would
+ // become invalid for every buffer during detach/attach. By setting this to
+ // false, the valid GraphicBuffer pointer will always be sent with acquire
+ // for attached buffers.
+ mSlots[*outSlot].mAcquireCalled = false;
+
return NO_ERROR;
}
@@ -261,42 +278,52 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ sp<IProducerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
- // If the frame number has changed because the buffer has been reallocated,
- // we can ignore this releaseBuffer for the old buffer
- if (frameNumber != mSlots[slot].mFrameNumber) {
- return STALE_BUFFER_SLOT;
- }
+ // If the frame number has changed because the buffer has been reallocated,
+ // we can ignore this releaseBuffer for the old buffer
+ if (frameNumber != mSlots[slot].mFrameNumber) {
+ return STALE_BUFFER_SLOT;
+ }
- // Make sure this buffer hasn't been queued while acquired by the consumer
- BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
- while (current != mCore->mQueue.end()) {
- if (current->mSlot == slot) {
- BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
- "currently queued", slot);
+ // Make sure this buffer hasn't been queued while acquired by the consumer
+ BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mSlot == slot) {
+ BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
+ "currently queued", slot);
+ return BAD_VALUE;
+ }
+ ++current;
+ }
+
+ if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+ mSlots[slot].mEglDisplay = eglDisplay;
+ mSlots[slot].mEglFence = eglFence;
+ mSlots[slot].mFence = releaseFence;
+ mSlots[slot].mBufferState = BufferSlot::FREE;
+ listener = mCore->mConnectedProducerListener;
+ BQ_LOGV("releaseBuffer: releasing slot %d", slot);
+ } else if (mSlots[slot].mNeedsCleanupOnRelease) {
+ BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ mSlots[slot].mNeedsCleanupOnRelease = false;
+ return STALE_BUFFER_SLOT;
+ } else {
+ BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
+ "but its state was %d", slot, mSlots[slot].mBufferState);
return BAD_VALUE;
}
- ++current;
- }
- if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
- mSlots[slot].mEglDisplay = eglDisplay;
- mSlots[slot].mEglFence = eglFence;
- mSlots[slot].mFence = releaseFence;
- mSlots[slot].mBufferState = BufferSlot::FREE;
- } else if (mSlots[slot].mNeedsCleanupOnRelease) {
- BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
- "(state = %d)", slot, mSlots[slot].mBufferState);
- mSlots[slot].mNeedsCleanupOnRelease = false;
- return STALE_BUFFER_SLOT;
- } else {
- BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
- "but its state was %d", slot, mSlots[slot].mBufferState);
- return BAD_VALUE;
- }
+ mCore->mDequeueCondition.broadcast();
+ } // Autolock scope
- mCore->mDequeueCondition.broadcast();
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBufferReleased();
+ }
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 40a6cbb..593b6f1 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -26,6 +26,7 @@
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
@@ -49,7 +50,7 @@
mConsumerListener(),
mConsumerUsageBits(0),
mConnectedApi(NO_CONNECTED_API),
- mConnectedProducerToken(),
+ mConnectedProducerListener(),
mSlots(),
mQueue(),
mOverrideMaxBufferCount(0),
@@ -190,7 +191,7 @@
mSlots[slot].mNeedsCleanupOnRelease = true;
}
mSlots[slot].mBufferState = BufferSlot::FREE;
- mSlots[slot].mFrameNumber = 0;
+ mSlots[slot].mFrameNumber = UINT32_MAX;
mSlots[slot].mAcquireCalled = false;
// Destroy fence as BufferQueue now takes ownership
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9dd90ba..61846dd 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -25,6 +25,7 @@
#include <gui/BufferQueueProducer.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
+#include <gui/IProducerListener.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -392,6 +393,50 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ ATRACE_CALL();
+
+ if (outBuffer == NULL) {
+ BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
+ return BAD_VALUE;
+ } else if (outFence == NULL) {
+ BQ_LOGE("detachNextBuffer: outFence must not be NULL");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ // Find the oldest valid slot
+ int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == BufferSlot::FREE &&
+ mSlots[s].mGraphicBuffer != NULL) {
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = s;
+ }
+ }
+ }
+
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ return NO_MEMORY;
+ }
+
+ BQ_LOGV("detachNextBuffer detached slot %d", found);
+
+ *outBuffer = mSlots[found].mGraphicBuffer;
+ *outFence = mSlots[found].mFence;
+ mCore->freeBufferLocked(found);
+
+ return NO_ERROR;
+}
+
status_t BufferQueueProducer::attachBuffer(int* outSlot,
const sp<android::GraphicBuffer>& buffer) {
ATRACE_CALL();
@@ -654,7 +699,7 @@
return NO_ERROR;
}
-status_t BufferQueueProducer::connect(const sp<android::IBinder> &token,
+status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
@@ -711,16 +756,16 @@
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
- if (token != NULL && token->remoteBinder() != NULL) {
- status = token->linkToDeath(
+ if (listener != NULL &&
+ listener->asBinder()->remoteBinder() != NULL) {
+ status = listener->asBinder()->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
- if (status == NO_ERROR) {
- mCore->mConnectedProducerToken = token;
- } else {
+ if (status != NO_ERROR) {
BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
strerror(-status), status);
}
}
+ mCore->mConnectedProducerListener = listener;
break;
default:
BQ_LOGE("connect(P): unknown API %d", api);
@@ -759,14 +804,15 @@
mCore->freeAllBuffersLocked();
// Remove our death notification callback if we have one
- sp<IBinder> token = mCore->mConnectedProducerToken;
- if (token != NULL) {
+ if (mCore->mConnectedProducerListener != NULL) {
+ sp<IBinder> token =
+ mCore->mConnectedProducerListener->asBinder();
// This can fail if we're here because of the death
// notification, but we just ignore it
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
- mCore->mConnectedProducerToken = NULL;
+ mCore->mConnectedProducerListener = NULL;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();
@@ -793,8 +839,16 @@
}
status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
- Mutex::Autolock _l(mCore->mMutex);
- mCore->mSidebandStream = stream;
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock _l(mCore->mMutex);
+ mCore->mSidebandStream = stream;
+ listener = mCore->mConsumerListener;
+ } // Autolock scope
+
+ if (listener != NULL) {
+ listener->onSidebandStreamChanged();
+ }
return NO_ERROR;
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 1b19626..9ef2544 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-#define EGL_EGLEXT_PROTOTYPES
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-
#include <stdint.h>
#include <sys/types.h>
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 7c50315..aa6acb9 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -27,6 +27,7 @@
#include <binder/IInterface.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -36,6 +37,7 @@
SET_BUFFER_COUNT,
DEQUEUE_BUFFER,
DETACH_BUFFER,
+ DETACH_NEXT_BUFFER,
ATTACH_BUFFER,
QUEUE_BUFFER,
CANCEL_BUFFER,
@@ -122,6 +124,37 @@
return result;
}
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ if (outBuffer == NULL) {
+ ALOGE("detachNextBuffer: outBuffer must not be NULL");
+ return BAD_VALUE;
+ } else if (outFence == NULL) {
+ ALOGE("detachNextBuffer: outFence must not be NULL");
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ status_t result = remote()->transact(DETACH_NEXT_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ if (result == NO_ERROR) {
+ bool nonNull = reply.readInt32();
+ if (nonNull) {
+ *outBuffer = new GraphicBuffer;
+ reply.read(**outBuffer);
+ }
+ nonNull = reply.readInt32();
+ if (nonNull) {
+ *outFence = new Fence;
+ reply.read(**outFence);
+ }
+ }
+ return result;
+ }
+
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -171,11 +204,16 @@
return result;
}
- virtual status_t connect(const sp<IBinder>& token,
+ virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
- data.writeStrongBinder(token);
+ if (listener != NULL) {
+ data.writeInt32(1);
+ data.writeStrongBinder(listener->asBinder());
+ } else {
+ data.writeInt32(0);
+ }
data.writeInt32(api);
data.writeInt32(producerControlledByApp);
status_t result = remote()->transact(CONNECT, data, &reply);
@@ -268,6 +306,24 @@
reply->writeInt32(result);
return NO_ERROR;
} break;
+ case DETACH_NEXT_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ int32_t result = detachNextBuffer(&buffer, &fence);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ reply->writeInt32(buffer != NULL);
+ if (buffer != NULL) {
+ reply->write(*buffer);
+ }
+ reply->writeInt32(fence != NULL);
+ if (fence != NULL) {
+ reply->write(*fence);
+ }
+ }
+ return NO_ERROR;
+ } break;
case ATTACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer = new GraphicBuffer();
@@ -308,13 +364,16 @@
} break;
case CONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- sp<IBinder> token = data.readStrongBinder();
+ sp<IProducerListener> listener;
+ if (data.readInt32() == 1) {
+ listener = IProducerListener::asInterface(data.readStrongBinder());
+ }
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
QueueBufferOutput* const output =
reinterpret_cast<QueueBufferOutput *>(
reply->writeInplace(sizeof(QueueBufferOutput)));
- status_t res = connect(token, api, producerControlledByApp, output);
+ status_t res = connect(listener, api, producerControlledByApp, output);
reply->writeInt32(res);
return NO_ERROR;
} break;
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
new file mode 100644
index 0000000..efe4069
--- /dev/null
+++ b/libs/gui/IProducerListener.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <binder/Parcel.h>
+
+#include <gui/IProducerListener.h>
+
+namespace android {
+
+enum {
+ ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpProducerListener : public BpInterface<IProducerListener>
+{
+public:
+ BpProducerListener(const sp<IBinder>& impl)
+ : BpInterface<IProducerListener>(impl) {}
+
+ virtual void onBufferReleased() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ProducerListener, "android.gui.IProducerListener")
+
+status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case ON_BUFFER_RELEASED:
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ onBufferReleased();
+ return NO_ERROR;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index e96cc54..c067244 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -229,6 +229,21 @@
memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
return reply.readInt32();
}
+
+ virtual status_t clearAnimationFrameStats() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t getAnimationFrameStats(FrameStats* outStats) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, data, &reply);
+ reply.read(*outStats);
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
@@ -351,6 +366,20 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case CLEAR_ANIMATION_FRAME_STATS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ status_t result = clearAnimationFrameStats();
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+ case GET_ANIMATION_FRAME_STATS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ FrameStats stats;
+ status_t result = getAnimationFrameStats(&stats);
+ reply->write(stats);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 1adc134..3da6423 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -39,7 +39,9 @@
enum {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
- DESTROY_SURFACE
+ DESTROY_SURFACE,
+ CLEAR_LAYER_FRAME_STATS,
+ GET_LAYER_FRAME_STATS
};
class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -73,6 +75,23 @@
remote()->transact(DESTROY_SURFACE, data, &reply);
return reply.readInt32();
}
+
+ virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ data.writeStrongBinder(handle);
+ remote()->transact(CLEAR_LAYER_FRAME_STATS, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ data.writeStrongBinder(handle);
+ remote()->transact(GET_LAYER_FRAME_STATS, data, &reply);
+ reply.read(*outStats);
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient");
@@ -101,7 +120,23 @@
} break;
case DESTROY_SURFACE: {
CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- reply->writeInt32( destroySurface( data.readStrongBinder() ) );
+ reply->writeInt32(destroySurface( data.readStrongBinder() ) );
+ return NO_ERROR;
+ } break;
+ case CLEAR_LAYER_FRAME_STATS: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<IBinder> handle = data.readStrongBinder();
+ status_t result = clearLayerFrameStats(handle);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case GET_LAYER_FRAME_STATS: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<IBinder> handle = data.readStrongBinder();
+ FrameStats stats;
+ status_t result = getLayerFrameStats(handle, &stats);
+ reply->write(stats);
+ reply->writeInt32(result);
return NO_ERROR;
} break;
default:
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index da6b0f9..6f1a3f2 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -48,14 +48,90 @@
mResolution = hwSensor->resolution;
mPower = hwSensor->power;
mMinDelay = hwSensor->minDelay;
+
// Set fifo event count zero for older devices which do not support batching. Fused
// sensors also have their fifo counts set to zero.
if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
mFifoReservedEventCount = hwSensor->fifoReservedEventCount;
mFifoMaxEventCount = hwSensor->fifoMaxEventCount;
- } else {
- mFifoReservedEventCount = 0;
- mFifoMaxEventCount = 0;
+ }
+
+ // Ensure existing sensors have correct string type and required
+ // permissions.
+ switch (mType) {
+ case SENSOR_TYPE_ACCELEROMETER:
+ mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+ break;
+ case SENSOR_TYPE_AMBIENT_TEMPERATURE:
+ mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
+ break;
+ case SENSOR_TYPE_GAME_ROTATION_VECTOR:
+ mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
+ break;
+ case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+ mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
+ break;
+ case SENSOR_TYPE_GRAVITY:
+ mStringType = SENSOR_STRING_TYPE_GRAVITY;
+ break;
+ case SENSOR_TYPE_GYROSCOPE:
+ mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
+ break;
+ case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
+ mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
+ break;
+ case SENSOR_TYPE_HEART_RATE:
+ mStringType = SENSOR_STRING_TYPE_HEART_RATE;
+ mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
+ break;
+ case SENSOR_TYPE_LIGHT:
+ mStringType = SENSOR_STRING_TYPE_LIGHT;
+ break;
+ case SENSOR_TYPE_LINEAR_ACCELERATION:
+ mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
+ break;
+ case SENSOR_TYPE_MAGNETIC_FIELD:
+ mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
+ break;
+ case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+ mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
+ break;
+ case SENSOR_TYPE_ORIENTATION:
+ mStringType = SENSOR_STRING_TYPE_ORIENTATION;
+ break;
+ case SENSOR_TYPE_PRESSURE:
+ mStringType = SENSOR_STRING_TYPE_PRESSURE;
+ break;
+ case SENSOR_TYPE_PROXIMITY:
+ mStringType = SENSOR_STRING_TYPE_PROXIMITY;
+ break;
+ case SENSOR_TYPE_RELATIVE_HUMIDITY:
+ mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
+ break;
+ case SENSOR_TYPE_ROTATION_VECTOR:
+ mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
+ break;
+ case SENSOR_TYPE_SIGNIFICANT_MOTION:
+ mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
+ break;
+ case SENSOR_TYPE_STEP_COUNTER:
+ mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
+ break;
+ case SENSOR_TYPE_STEP_DETECTOR:
+ mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
+ break;
+ case SENSOR_TYPE_TEMPERATURE:
+ mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
+ break;
+ default:
+ // Only pipe the stringType and requiredPermission for custom sensors.
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_2 && hwSensor->stringType) {
+ mStringType = hwSensor->stringType;
+ }
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_2 && hwSensor->requiredPermission) {
+ mRequiredPermission = hwSensor->requiredPermission;
+ }
+ break;
}
}
@@ -115,6 +191,14 @@
return mFifoMaxEventCount;
}
+const String8& Sensor::getStringType() const {
+ return mStringType;
+}
+
+const String8& Sensor::getRequiredPermission() const {
+ return mRequiredPermission;
+}
+
size_t Sensor::getFlattenedSize() const
{
size_t fixedSize =
@@ -123,8 +207,10 @@
sizeof(int32_t) * 3;
size_t variableSize =
- sizeof(int32_t) + FlattenableUtils::align<4>(mName.length()) +
- sizeof(int32_t) + FlattenableUtils::align<4>(mVendor.length());
+ sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
+ sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
+ sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
+ sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
return fixedSize + variableSize;
}
@@ -134,14 +220,8 @@
return NO_MEMORY;
}
- FlattenableUtils::write(buffer, size, mName.length());
- memcpy(static_cast<char*>(buffer), mName.string(), mName.length());
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mName.length()));
-
- FlattenableUtils::write(buffer, size, mVendor.length());
- memcpy(static_cast<char*>(buffer), mVendor.string(), mVendor.length());
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mVendor.length()));
-
+ flattenString8(buffer, size, mName);
+ flattenString8(buffer, size, mVendor);
FlattenableUtils::write(buffer, size, mVersion);
FlattenableUtils::write(buffer, size, mHandle);
FlattenableUtils::write(buffer, size, mType);
@@ -152,38 +232,23 @@
FlattenableUtils::write(buffer, size, mMinDelay);
FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
+ flattenString8(buffer, size, mStringType);
+ flattenString8(buffer, size, mRequiredPermission);
return NO_ERROR;
}
status_t Sensor::unflatten(void const* buffer, size_t size) {
- size_t len;
-
- if (size < sizeof(size_t)) {
+ if (!unflattenString8(buffer, size, mName)) {
return NO_MEMORY;
}
- FlattenableUtils::read(buffer, size, len);
- if (size < len) {
+ if (!unflattenString8(buffer, size, mVendor)) {
return NO_MEMORY;
}
- mName.setTo(static_cast<char const*>(buffer), len);
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-
-
- if (size < sizeof(size_t)) {
- return NO_MEMORY;
- }
- FlattenableUtils::read(buffer, size, len);
- if (size < len) {
- return NO_MEMORY;
- }
- mVendor.setTo(static_cast<char const*>(buffer), len);
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
size_t fixedSize =
sizeof(int32_t) * 3 +
sizeof(float) * 4 +
sizeof(int32_t) * 3;
-
if (size < fixedSize) {
return NO_MEMORY;
}
@@ -198,8 +263,37 @@
FlattenableUtils::read(buffer, size, mMinDelay);
FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
+
+ if (!unflattenString8(buffer, size, mStringType)) {
+ return NO_MEMORY;
+ }
+ if (!unflattenString8(buffer, size, mRequiredPermission)) {
+ return NO_MEMORY;
+ }
return NO_ERROR;
}
+void Sensor::flattenString8(void*& buffer, size_t& size,
+ const String8& string8) {
+ uint32_t len = string8.length();
+ FlattenableUtils::write(buffer, size, len);
+ memcpy(static_cast<char*>(buffer), string8.string(), len);
+ FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+}
+
+bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
+ uint32_t len;
+ if (size < sizeof(len)) {
+ return false;
+ }
+ FlattenableUtils::read(buffer, size, len);
+ if (size < len) {
+ return false;
+ }
+ outputString8.setTo(static_cast<char const*>(buffer), len);
+ FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+ return true;
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
new file mode 100644
index 0000000..83e08fb
--- /dev/null
+++ b/libs/gui/StreamSplitter.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2014 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 "StreamSplitter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/StreamSplitter.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <binder/ProcessState.h>
+
+#include <utils/Trace.h>
+
+namespace android {
+
+status_t StreamSplitter::createSplitter(
+ const sp<IGraphicBufferConsumer>& inputQueue,
+ sp<StreamSplitter>* outSplitter) {
+ if (inputQueue == NULL) {
+ ALOGE("createSplitter: inputQueue must not be NULL");
+ return BAD_VALUE;
+ }
+ if (outSplitter == NULL) {
+ ALOGE("createSplitter: outSplitter must not be NULL");
+ return BAD_VALUE;
+ }
+
+ sp<StreamSplitter> splitter(new StreamSplitter(inputQueue));
+ status_t status = splitter->mInput->consumerConnect(splitter, false);
+ if (status == NO_ERROR) {
+ splitter->mInput->setConsumerName(String8("StreamSplitter"));
+ *outSplitter = splitter;
+ }
+ return status;
+}
+
+StreamSplitter::StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue)
+ : mIsAbandoned(false), mMutex(), mReleaseCondition(),
+ mOutstandingBuffers(0), mInput(inputQueue), mOutputs(), mBuffers() {}
+
+StreamSplitter::~StreamSplitter() {
+ mInput->consumerDisconnect();
+ Vector<sp<IGraphicBufferProducer> >::iterator output = mOutputs.begin();
+ for (; output != mOutputs.end(); ++output) {
+ (*output)->disconnect(NATIVE_WINDOW_API_CPU);
+ }
+
+ if (mBuffers.size() > 0) {
+ ALOGE("%d buffers still being tracked", mBuffers.size());
+ }
+}
+
+status_t StreamSplitter::addOutput(
+ const sp<IGraphicBufferProducer>& outputQueue) {
+ if (outputQueue == NULL) {
+ ALOGE("addOutput: outputQueue must not be NULL");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
+ sp<OutputListener> listener(new OutputListener(this, outputQueue));
+ outputQueue->asBinder()->linkToDeath(listener);
+ status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU,
+ /* producerControlledByApp */ false, &queueBufferOutput);
+ if (status != NO_ERROR) {
+ ALOGE("addOutput: failed to connect (%d)", status);
+ return status;
+ }
+
+ mOutputs.push_back(outputQueue);
+
+ return NO_ERROR;
+}
+
+void StreamSplitter::setName(const String8 &name) {
+ Mutex::Autolock lock(mMutex);
+ mInput->setConsumerName(name);
+}
+
+void StreamSplitter::onFrameAvailable() {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ // The current policy is that if any one consumer is consuming buffers too
+ // slowly, the splitter will stall the rest of the outputs by not acquiring
+ // any more buffers from the input. This will cause back pressure on the
+ // input queue, slowing down its producer.
+
+ // If there are too many outstanding buffers, we block until a buffer is
+ // released back to the input in onBufferReleased
+ while (mOutstandingBuffers >= MAX_OUTSTANDING_BUFFERS) {
+ mReleaseCondition.wait(mMutex);
+
+ // If the splitter is abandoned while we are waiting, the release
+ // condition variable will be broadcast, and we should just return
+ // without attempting to do anything more (since the input queue will
+ // also be abandoned).
+ if (mIsAbandoned) {
+ return;
+ }
+ }
+ ++mOutstandingBuffers;
+
+ // Acquire and detach the buffer from the input
+ IGraphicBufferConsumer::BufferItem bufferItem;
+ status_t status = mInput->acquireBuffer(&bufferItem, /* presentWhen */ 0);
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "acquiring buffer from input failed (%d)", status);
+
+ ALOGV("acquired buffer %#llx from input",
+ bufferItem.mGraphicBuffer->getId());
+
+ status = mInput->detachBuffer(bufferItem.mBuf);
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "detaching buffer from input failed (%d)", status);
+
+ // Initialize our reference count for this buffer
+ mBuffers.add(bufferItem.mGraphicBuffer->getId(),
+ new BufferTracker(bufferItem.mGraphicBuffer));
+
+ IGraphicBufferProducer::QueueBufferInput queueInput(
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
+ bufferItem.mCrop, bufferItem.mScalingMode,
+ bufferItem.mTransform, bufferItem.mIsDroppable,
+ bufferItem.mFence);
+
+ // Attach and queue the buffer to each of the outputs
+ Vector<sp<IGraphicBufferProducer> >::iterator output = mOutputs.begin();
+ for (; output != mOutputs.end(); ++output) {
+ int slot;
+ status = (*output)->attachBuffer(&slot, bufferItem.mGraphicBuffer);
+ if (status == NO_INIT) {
+ // If we just discovered that this output has been abandoned, note
+ // that, increment the release count so that we still release this
+ // buffer eventually, and move on to the next output
+ onAbandonedLocked();
+ mBuffers.editValueFor(bufferItem.mGraphicBuffer->getId())->
+ incrementReleaseCountLocked();
+ continue;
+ } else {
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "attaching buffer to output failed (%d)", status);
+ }
+
+ IGraphicBufferProducer::QueueBufferOutput queueOutput;
+ status = (*output)->queueBuffer(slot, queueInput, &queueOutput);
+ if (status == NO_INIT) {
+ // If we just discovered that this output has been abandoned, note
+ // that, increment the release count so that we still release this
+ // buffer eventually, and move on to the next output
+ onAbandonedLocked();
+ mBuffers.editValueFor(bufferItem.mGraphicBuffer->getId())->
+ incrementReleaseCountLocked();
+ continue;
+ } else {
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "queueing buffer to output failed (%d)", status);
+ }
+
+ ALOGV("queued buffer %#llx to output %p",
+ bufferItem.mGraphicBuffer->getId(), output->get());
+ }
+}
+
+void StreamSplitter::onBufferReleasedByOutput(
+ const sp<IGraphicBufferProducer>& from) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ status_t status = from->detachNextBuffer(&buffer, &fence);
+ if (status == NO_INIT) {
+ // If we just discovered that this output has been abandoned, note that,
+ // but we can't do anything else, since buffer is invalid
+ onAbandonedLocked();
+ return;
+ } else {
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "detaching buffer from output failed (%d)", status);
+ }
+
+ ALOGV("detached buffer %#llx from output %p", buffer->getId(), from.get());
+
+ const sp<BufferTracker>& tracker = mBuffers.editValueFor(buffer->getId());
+
+ // Merge the release fence of the incoming buffer so that the fence we send
+ // back to the input includes all of the outputs' fences
+ tracker->mergeFence(fence);
+
+ // Check to see if this is the last outstanding reference to this buffer
+ size_t releaseCount = tracker->incrementReleaseCountLocked();
+ ALOGV("buffer %#llx reference count %d (of %d)", buffer->getId(),
+ releaseCount, mOutputs.size());
+ if (releaseCount < mOutputs.size()) {
+ return;
+ }
+
+ // If we've been abandoned, we can't return the buffer to the input, so just
+ // stop tracking it and move on
+ if (mIsAbandoned) {
+ mBuffers.removeItem(buffer->getId());
+ return;
+ }
+
+ // Attach and release the buffer back to the input
+ int consumerSlot;
+ status = mInput->attachBuffer(&consumerSlot, tracker->getBuffer());
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "attaching buffer to input failed (%d)", status);
+
+ status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker->getMergedFence());
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "releasing buffer to input failed (%d)", status);
+
+ ALOGV("released buffer %#llx to input", buffer->getId());
+
+ // We no longer need to track the buffer once it has been returned to the
+ // input
+ mBuffers.removeItem(buffer->getId());
+
+ // Notify any waiting onFrameAvailable calls
+ --mOutstandingBuffers;
+ mReleaseCondition.signal();
+}
+
+void StreamSplitter::onAbandonedLocked() {
+ ALOGE("one of my outputs has abandoned me");
+ if (!mIsAbandoned) {
+ mInput->consumerDisconnect();
+ }
+ mIsAbandoned = true;
+ mReleaseCondition.broadcast();
+}
+
+StreamSplitter::OutputListener::OutputListener(
+ const sp<StreamSplitter>& splitter,
+ const sp<IGraphicBufferProducer>& output)
+ : mSplitter(splitter), mOutput(output) {}
+
+StreamSplitter::OutputListener::~OutputListener() {}
+
+void StreamSplitter::OutputListener::onBufferReleased() {
+ mSplitter->onBufferReleasedByOutput(mOutput);
+}
+
+void StreamSplitter::OutputListener::binderDied(const wp<IBinder>& /* who */) {
+ Mutex::Autolock lock(mSplitter->mMutex);
+ mSplitter->onAbandonedLocked();
+}
+
+StreamSplitter::BufferTracker::BufferTracker(const sp<GraphicBuffer>& buffer)
+ : mBuffer(buffer), mMergedFence(Fence::NO_FENCE), mReleaseCount(0) {}
+
+StreamSplitter::BufferTracker::~BufferTracker() {}
+
+void StreamSplitter::BufferTracker::mergeFence(const sp<Fence>& with) {
+ mMergedFence = Fence::merge(String8("StreamSplitter"), mMergedFence, with);
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 95f4084..d1ef503 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -27,6 +27,7 @@
#include <ui/Fence.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/GLConsumer.h>
@@ -513,10 +514,10 @@
int Surface::connect(int api) {
ATRACE_CALL();
ALOGV("Surface::connect");
- static sp<BBinder> sLife = new BBinder();
+ static sp<IProducerListener> listener = new DummyProducerListener();
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
- int err = mGraphicBufferProducer->connect(sLife, api, mProducerControlledByApp, &output);
+ int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6b20eaf..b7af415 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -515,6 +515,21 @@
return err;
}
+status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ return mClient->clearLayerFrameStats(token);
+}
+
+status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token,
+ FrameStats* outStats) const {
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ return mClient->getLayerFrameStats(token, outStats);
+}
+
inline Composer& SurfaceComposerClient::getComposer() {
return mComposer;
}
@@ -622,6 +637,14 @@
ComposerService::getComposerService()->unblank(token);
}
+status_t SurfaceComposerClient::clearAnimationFrameStats() {
+ return ComposerService::getComposerService()->clearAnimationFrameStats();
+}
+
+status_t SurfaceComposerClient::getAnimationFrameStats(FrameStats* outStats) {
+ return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a12e967..7597c99 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -145,6 +145,20 @@
return mClient->setCrop(mHandle, crop);
}
+status_t SurfaceControl::clearLayerFrameStats() const {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->clearLayerFrameStats(mHandle);
+}
+
+status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->getLayerFrameStats(mHandle, outStats);
+}
+
status_t SurfaceControl::validate() const
{
if (mHandle==0 || mClient==0) {
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 2eeb5c7..e460290 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -14,6 +14,7 @@
IGraphicBufferProducer_test.cpp \
MultiTextureConsumer_test.cpp \
SRGB_test.cpp \
+ StreamSplitter_test.cpp \
SurfaceTextureClient_test.cpp \
SurfaceTextureFBO_test.cpp \
SurfaceTextureGLThreadToGL_test.cpp \
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index e02c780..c781366 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,17 +17,19 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
-#include <gtest/gtest.h>
-
-#include <utils/String8.h>
-#include <utils/threads.h>
+#include <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
#include <ui/GraphicBuffer.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-#include <gui/BufferQueue.h>
+
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <gtest/gtest.h>
namespace android {
@@ -141,7 +143,8 @@
sp<DummyConsumer> dc(new DummyConsumer);
mConsumer->consumerConnect(dc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo);
+ mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbo);
mProducer->setBufferCount(4);
int slot;
@@ -207,8 +210,8 @@
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK,
- mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
@@ -260,8 +263,8 @@
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK,
- mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -298,7 +301,7 @@
ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL));
ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mBuf, 0, EGL_NO_DISPLAY,
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY,
EGL_NO_SYNC_KHR, Fence::NO_FENCE));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
@@ -318,8 +321,8 @@
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK,
- mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9c06fae..aadfe61 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -25,13 +25,14 @@
#include <ui/GraphicBuffer.h>
#include <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
#include <vector>
#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
-#define TEST_TOKEN ((IBinder*)(NULL))
+#define TEST_TOKEN ((IProducerListener*)(NULL))
#define TEST_API NATIVE_WINDOW_API_CPU
#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API
#define TEST_CONTROLLED_BY_APP false
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
new file mode 100644
index 0000000..32ec90d
--- /dev/null
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2014 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 "StreamSplitter_test"
+//#define LOG_NDEBUG 0
+
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/StreamSplitter.h>
+#include <private/gui/ComposerService.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class StreamSplitterTest : public ::testing::Test {
+
+protected:
+ StreamSplitterTest() {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
+ testInfo->name());
+ }
+
+ ~StreamSplitterTest() {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGV("End test: %s.%s", testInfo->test_case_name(),
+ testInfo->name());
+ }
+};
+
+struct DummyListener : public BnConsumerListener {
+ virtual void onFrameAvailable() {}
+ virtual void onBuffersReleased() {}
+ virtual void onSidebandStreamChanged() {}
+};
+
+class CountedAllocator : public BnGraphicBufferAlloc {
+public:
+ CountedAllocator() : mAllocCount(0) {
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ mAllocator = composer->createGraphicBufferAlloc();
+ }
+
+ virtual ~CountedAllocator() {}
+
+ virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage, status_t* error) {
+ ++mAllocCount;
+ sp<GraphicBuffer> buffer = mAllocator->createGraphicBuffer(w, h, format,
+ usage, error);
+ return buffer;
+ }
+
+ int getAllocCount() const { return mAllocCount; }
+
+private:
+ sp<IGraphicBufferAlloc> mAllocator;
+ int mAllocCount;
+};
+
+TEST_F(StreamSplitterTest, OneInputOneOutput) {
+ sp<CountedAllocator> allocator(new CountedAllocator);
+
+ sp<IGraphicBufferProducer> inputProducer;
+ sp<IGraphicBufferConsumer> inputConsumer;
+ BufferQueue::createBufferQueue(&inputProducer, &inputConsumer, allocator);
+
+ sp<IGraphicBufferProducer> outputProducer;
+ sp<IGraphicBufferConsumer> outputConsumer;
+ BufferQueue::createBufferQueue(&outputProducer, &outputConsumer, allocator);
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+
+ sp<StreamSplitter> splitter;
+ status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
+ ASSERT_EQ(OK, status);
+ ASSERT_EQ(OK, splitter->addOutput(outputProducer));
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &qbOutput));
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
+
+ uint32_t* dataIn;
+ ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&dataIn)));
+ *dataIn = 0x12345678;
+ ASSERT_EQ(OK, buffer->unlock());
+
+ IGraphicBufferProducer::QueueBufferInput qbInput(0, false,
+ Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false,
+ Fence::NO_FENCE);
+ ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput));
+
+ IGraphicBufferConsumer::BufferItem item;
+ ASSERT_EQ(OK, outputConsumer->acquireBuffer(&item, 0));
+
+ uint32_t* dataOut;
+ ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&dataOut)));
+ ASSERT_EQ(*dataOut, 0x12345678);
+ ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
+
+ ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ ASSERT_EQ(1, allocator->getAllocCount());
+}
+
+TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
+ const int NUM_OUTPUTS = 4;
+ sp<CountedAllocator> allocator(new CountedAllocator);
+
+ sp<IGraphicBufferProducer> inputProducer;
+ sp<IGraphicBufferConsumer> inputConsumer;
+ BufferQueue::createBufferQueue(&inputProducer, &inputConsumer, allocator);
+
+ sp<IGraphicBufferProducer> outputProducers[NUM_OUTPUTS] = {};
+ sp<IGraphicBufferConsumer> outputConsumers[NUM_OUTPUTS] = {};
+ for (int output = 0; output < NUM_OUTPUTS; ++output) {
+ BufferQueue::createBufferQueue(&outputProducers[output],
+ &outputConsumers[output], allocator);
+ ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(
+ new DummyListener, false));
+ }
+
+ sp<StreamSplitter> splitter;
+ status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
+ ASSERT_EQ(OK, status);
+ for (int output = 0; output < NUM_OUTPUTS; ++output) {
+ ASSERT_EQ(OK, splitter->addOutput(outputProducers[output]));
+ }
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &qbOutput));
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
+
+ uint32_t* dataIn;
+ ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&dataIn)));
+ *dataIn = 0x12345678;
+ ASSERT_EQ(OK, buffer->unlock());
+
+ IGraphicBufferProducer::QueueBufferInput qbInput(0, false,
+ Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false,
+ Fence::NO_FENCE);
+ ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput));
+
+ for (int output = 0; output < NUM_OUTPUTS; ++output) {
+ IGraphicBufferConsumer::BufferItem item;
+ ASSERT_EQ(OK, outputConsumers[output]->acquireBuffer(&item, 0));
+
+ uint32_t* dataOut;
+ ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&dataOut)));
+ ASSERT_EQ(*dataOut, 0x12345678);
+ ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
+
+ ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mBuf,
+ item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+ Fence::NO_FENCE));
+ }
+
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ ASSERT_EQ(1, allocator->getAllocCount());
+}
+
+TEST_F(StreamSplitterTest, OutputAbandonment) {
+ sp<IGraphicBufferProducer> inputProducer;
+ sp<IGraphicBufferConsumer> inputConsumer;
+ BufferQueue::createBufferQueue(&inputProducer, &inputConsumer);
+
+ sp<IGraphicBufferProducer> outputProducer;
+ sp<IGraphicBufferConsumer> outputConsumer;
+ BufferQueue::createBufferQueue(&outputProducer, &outputConsumer);
+ ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false));
+
+ sp<StreamSplitter> splitter;
+ status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter);
+ ASSERT_EQ(OK, status);
+ ASSERT_EQ(OK, splitter->addOutput(outputProducer));
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &qbOutput));
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
+
+ // Abandon the output
+ outputConsumer->consumerDisconnect();
+
+ IGraphicBufferProducer::QueueBufferInput qbInput(0, false,
+ Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false,
+ Fence::NO_FENCE);
+ ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput));
+
+ // Input should be abandoned
+ ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, false, 0, 0,
+ 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+}
+
+} // namespace android
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 008446b..eec97be 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES:= \
Fence.cpp \
FramebufferNativeWindow.cpp \
+ FrameStats.cpp \
GraphicBuffer.cpp \
GraphicBufferAllocator.cpp \
GraphicBufferMapper.cpp \
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 93ec0ce..3c0306c 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -138,7 +138,7 @@
if (size < getFlattenedSize() || count < getFdCount()) {
return NO_MEMORY;
}
- FlattenableUtils::write(buffer, size, getFdCount());
+ FlattenableUtils::write(buffer, size, (uint32_t)getFdCount());
if (isValid()) {
*fds++ = mFenceFd;
count--;
@@ -156,7 +156,7 @@
return NO_MEMORY;
}
- size_t numFds;
+ uint32_t numFds;
FlattenableUtils::read(buffer, size, numFds);
if (numFds > 1) {
diff --git a/libs/ui/FrameStats.cpp b/libs/ui/FrameStats.cpp
new file mode 100644
index 0000000..acbe27e
--- /dev/null
+++ b/libs/ui/FrameStats.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <ui/FrameStats.h>
+
+namespace android {
+
+bool FrameStats::isFixedSize() const {
+ return false;
+}
+
+size_t FrameStats::getFlattenedSize() const {
+ const size_t timestampSize = sizeof(nsecs_t);
+
+ size_t size = timestampSize;
+ size += 3 * desiredPresentTimesNano.size() * timestampSize;
+
+ return size;
+}
+
+status_t FrameStats::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ nsecs_t* timestamps = reinterpret_cast<nsecs_t*>(buffer);
+ const size_t timestampSize = sizeof(nsecs_t);
+ size_t frameCount = desiredPresentTimesNano.size();
+
+ memcpy(timestamps, &refreshPeriodNano, timestampSize);
+ timestamps += 1;
+
+ memcpy(timestamps, desiredPresentTimesNano.array(), frameCount * timestampSize);
+ timestamps += frameCount;
+
+ memcpy(timestamps, actualPresentTimesNano.array(), frameCount * timestampSize);
+ timestamps += frameCount;
+
+ memcpy(timestamps, frameReadyTimesNano.array(), frameCount * timestampSize);
+
+ return NO_ERROR;
+}
+
+status_t FrameStats::unflatten(void const* buffer, size_t size) {
+ const size_t timestampSize = sizeof(nsecs_t);
+
+ if (size < timestampSize) {
+ return NO_MEMORY;
+ }
+
+ nsecs_t const* timestamps = reinterpret_cast<nsecs_t const*>(buffer);
+ size_t frameCount = (size - timestampSize) / (3 * timestampSize);
+
+ memcpy(&refreshPeriodNano, timestamps, timestampSize);
+ timestamps += 1;
+
+ desiredPresentTimesNano.resize(frameCount);
+ memcpy(desiredPresentTimesNano.editArray(), timestamps, frameCount * timestampSize);
+ timestamps += frameCount;
+
+ actualPresentTimesNano.resize(frameCount);
+ memcpy(actualPresentTimesNano.editArray(), timestamps, frameCount * timestampSize);
+ timestamps += frameCount;
+
+ frameReadyTimesNano.resize(frameCount);
+ memcpy(frameReadyTimesNano.editArray(), timestamps, frameCount * timestampSize);
+
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index c4e4efa..99601ed 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -34,9 +34,17 @@
// Buffer and implementation of ANativeWindowBuffer
// ===========================================================================
+static uint64_t getUniqueId() {
+ static volatile int32_t nextId = 0;
+ uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+ id |= static_cast<uint32_t>(android_atomic_inc(&nextId));
+ return id;
+}
+
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR) {
+ mInitCheck(NO_ERROR), mId(getUniqueId())
+{
width =
height =
stride =
@@ -48,7 +56,7 @@
GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
PixelFormat reqFormat, uint32_t reqUsage)
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR)
+ mInitCheck(NO_ERROR), mId(getUniqueId())
{
width =
height =
@@ -64,7 +72,7 @@
uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR)
+ mInitCheck(NO_ERROR), mId(getUniqueId())
{
width = w;
height = h;
@@ -77,7 +85,7 @@
GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mWrappedBuffer(buffer)
+ mInitCheck(NO_ERROR), mWrappedBuffer(buffer), mId(getUniqueId())
{
width = buffer->width;
height = buffer->height;
@@ -201,7 +209,7 @@
}
size_t GraphicBuffer::getFlattenedSize() const {
- return (8 + (handle ? handle->numInts : 0))*sizeof(int);
+ return (10 + (handle ? handle->numInts : 0))*sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
@@ -215,22 +223,24 @@
size_t fdCountNeeded = GraphicBuffer::getFdCount();
if (count < fdCountNeeded) return NO_MEMORY;
- int* buf = static_cast<int*>(buffer);
+ int32_t* buf = static_cast<int32_t*>(buffer);
buf[0] = 'GBFR';
buf[1] = width;
buf[2] = height;
buf[3] = stride;
buf[4] = format;
buf[5] = usage;
- buf[6] = 0;
- buf[7] = 0;
+ buf[6] = static_cast<int32_t>(mId >> 32);
+ buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
+ buf[8] = 0;
+ buf[9] = 0;
if (handle) {
- buf[6] = handle->numFds;
- buf[7] = handle->numInts;
+ buf[8] = handle->numFds;
+ buf[9] = handle->numInts;
native_handle_t const* const h = handle;
memcpy(fds, h->data, h->numFds*sizeof(int));
- memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
+ memcpy(&buf[10], h->data + h->numFds, h->numInts*sizeof(int));
}
buffer = reinterpret_cast<void*>(static_cast<int*>(buffer) + sizeNeeded);
@@ -250,10 +260,10 @@
int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE;
- const size_t numFds = buf[6];
- const size_t numInts = buf[7];
+ const size_t numFds = buf[8];
+ const size_t numInts = buf[9];
- const size_t sizeNeeded = (8 + numInts) * sizeof(int);
+ const size_t sizeNeeded = (10 + numInts) * sizeof(int);
if (size < sizeNeeded) return NO_MEMORY;
size_t fdCountNeeded = 0;
@@ -272,13 +282,16 @@
usage = buf[5];
native_handle* h = native_handle_create(numFds, numInts);
memcpy(h->data, fds, numFds*sizeof(int));
- memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
+ memcpy(h->data + numFds, &buf[10], numInts*sizeof(int));
handle = h;
} else {
width = height = stride = format = usage = 0;
handle = NULL;
}
+ mId = static_cast<uint64_t>(buf[6]) << 32;
+ mId |= static_cast<uint32_t>(buf[7]);
+
mOwner = ownHandle;
if (handle != 0) {
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index dbfc957..f219f95 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -251,10 +251,10 @@
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
- // Reset the key repeat timer whenever we disallow key events, even if the next event
- // is not a key. This is to ensure that we abort a key repeat if the device is just coming
- // out of sleep.
- if (!mPolicy->isKeyRepeatEnabled()) {
+ // Reset the key repeat timer whenever normal dispatch is suspended while the
+ // device is in a non-interactive state. This is to ensure that we abort a key
+ // repeat if the device is just coming out of sleep.
+ if (!mDispatchEnabled) {
resetKeyRepeatLocked();
}
@@ -1138,30 +1138,6 @@
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
- //
- // FIXME In the original code, screenWasOff could never be set to true.
- // The reason is that the POLICY_FLAG_WOKE_HERE
- // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
- // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
- // actually enqueued using the policyFlags that appeared in the final EV_SYN
- // events upon which no preprocessing took place. So policyFlags was always 0.
- // In the new native input dispatcher we're a bit more careful about event
- // preprocessing so the touches we receive can actually have non-zero policyFlags.
- // Unfortunately we obtain undesirable behavior.
- //
- // Here's what happens:
- //
- // When the device dims in anticipation of going to sleep, touches
- // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
- // the device to brighten and reset the user activity timer.
- // Touches on other windows (such as the launcher window)
- // are dropped. Then after a moment, the device goes to sleep. Oops.
- //
- // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
- // instead of POLICY_FLAG_WOKE_HERE...
- //
- bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
-
int32_t displayId = entry->displayId;
int32_t action = entry->action;
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
@@ -1246,10 +1222,7 @@
isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- if (! screenWasOff
- || (flags & InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING)) {
- newTouchedWindowHandle = windowHandle;
- }
+ newTouchedWindowHandle = windowHandle;
break; // found touched window, exit window loop
}
}
@@ -2412,10 +2385,6 @@
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
- if (policyFlags & POLICY_FLAG_WOKE_HERE) {
- flags |= AKEY_EVENT_FLAG_WOKE_HERE;
- }
-
bool needWake;
{ // acquire lock
mLock.lock();
@@ -2595,10 +2564,6 @@
mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
}
- if (policyFlags & POLICY_FLAG_WOKE_HERE) {
- flags |= AKEY_EVENT_FLAG_WOKE_HERE;
- }
-
mLock.lock();
firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
keyEvent->getDeviceId(), keyEvent->getSource(),
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 29854b2..9439124 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -211,9 +211,6 @@
/* Gets the input dispatcher configuration. */
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
- /* Returns true if automatic key repeating is enabled. */
- virtual bool isKeyRepeatEnabled() = 0;
-
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index fc89a9b..7aac6ed 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -65,10 +65,6 @@
*outConfig = mConfig;
}
- virtual bool isKeyRepeatEnabled() {
- return true;
- }
-
virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
return true;
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index f0bfe2c..8837a4d 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -206,7 +206,7 @@
String8 result;
if (!PermissionCache::checkCallingPermission(sDump)) {
result.appendFormat("Permission Denial: "
- "can't dump SurfaceFlinger from pid=%d, uid=%d\n",
+ "can't dump SensorService from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
} else {
@@ -216,21 +216,24 @@
const Sensor& s(mSensorList[i]);
const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle()));
result.appendFormat(
- "%-48s| %-32s | 0x%08x | ",
+ "%-48s| %-32s| %-48s| 0x%08x | \"%s\"\n\t",
s.getName().string(),
s.getVendor().string(),
- s.getHandle());
+ s.getStringType().string(),
+ s.getHandle(),
+ s.getRequiredPermission().string());
if (s.getMinDelay() > 0) {
result.appendFormat(
- "maxRate=%7.2fHz | ", 1e6f / s.getMinDelay());
+ "maxRate=%7.2fHz | ", 1e6f / s.getMinDelay());
} else {
result.append(s.getMinDelay() == 0
? "on-demand | "
: "one-shot | ");
}
if (s.getFifoMaxEventCount() > 0) {
- result.appendFormat("getFifoMaxEventCount=%d events | ", s.getFifoMaxEventCount());
+ result.appendFormat("FifoMax=%d events | ",
+ s.getFifoMaxEventCount());
} else {
result.append("no batching support | ");
}
@@ -491,10 +494,23 @@
{
char value[PROPERTY_VALUE_MAX];
property_get("debug.sensors", value, "0");
- if (atoi(value)) {
- return mUserSensorListDebug;
+ const Vector<Sensor>& initialSensorList = (atoi(value)) ?
+ mUserSensorListDebug : mUserSensorList;
+ Vector<Sensor> accessibleSensorList;
+ for (size_t i = 0; i < initialSensorList.size(); i++) {
+ Sensor sensor = initialSensorList[i];
+ if (canAccessSensor(sensor)) {
+ accessibleSensorList.add(sensor);
+ } else {
+ String8 infoMessage;
+ infoMessage.appendFormat(
+ "Skipped sensor %s because it requires permission %s",
+ sensor.getName().string(),
+ sensor.getRequiredPermission().string());
+ ALOGI(infoMessage.string());
+ }
}
- return mUserSensorList;
+ return accessibleSensorList;
}
sp<ISensorEventConnection> SensorService::createSensorEventConnection()
@@ -540,6 +556,10 @@
BatteryService::cleanup(c->getUid());
}
+Sensor SensorService::getSensorFromHandle(int handle) const {
+ return mSensorMap.valueFor(handle)->getSensor();
+}
+
status_t SensorService::enable(const sp<SensorEventConnection>& connection,
int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags)
{
@@ -550,6 +570,11 @@
if (sensor == NULL) {
return BAD_VALUE;
}
+
+ if (!verifyCanAccessSensor(sensor->getSensor(), "Tried enabling")) {
+ return BAD_VALUE;
+ }
+
Mutex::Autolock _l(mLock);
SensorRecord* rec = mActiveSensors.valueFor(handle);
if (rec == 0) {
@@ -671,6 +696,10 @@
if (!sensor)
return BAD_VALUE;
+ if (!verifyCanAccessSensor(sensor->getSensor(), "Tried configuring")) {
+ return BAD_VALUE;
+ }
+
if (ns < 0)
return BAD_VALUE;
@@ -684,17 +713,44 @@
status_t SensorService::flushSensor(const sp<SensorEventConnection>& connection,
int handle) {
- if (mInitCheck != NO_ERROR) return mInitCheck;
- SensorInterface* sensor = mSensorMap.valueFor(handle);
- if (sensor == NULL) {
- return BAD_VALUE;
- }
- if (sensor->getSensor().getType() == SENSOR_TYPE_SIGNIFICANT_MOTION) {
- ALOGE("flush called on Significant Motion sensor");
- return INVALID_OPERATION;
- }
- return sensor->flush(connection.get(), handle);
+ if (mInitCheck != NO_ERROR) return mInitCheck;
+ SensorInterface* sensor = mSensorMap.valueFor(handle);
+ if (sensor == NULL) {
+ return BAD_VALUE;
+ }
+
+ if (!verifyCanAccessSensor(sensor->getSensor(), "Tried flushing")) {
+ return BAD_VALUE;
+ }
+
+ if (sensor->getSensor().getType() == SENSOR_TYPE_SIGNIFICANT_MOTION) {
+ ALOGE("flush called on Significant Motion sensor");
+ return INVALID_OPERATION;
+ }
+ return sensor->flush(connection.get(), handle);
}
+
+
+bool SensorService::canAccessSensor(const Sensor& sensor) {
+ String16 permissionString(sensor.getRequiredPermission());
+ return permissionString.size() == 0 ||
+ PermissionCache::checkCallingPermission(permissionString);
+}
+
+bool SensorService::verifyCanAccessSensor(const Sensor& sensor, const char* operation) {
+ if (canAccessSensor(sensor)) {
+ return true;
+ } else {
+ String8 errorMessage;
+ errorMessage.appendFormat(
+ "%s a sensor (%s) without holding its required permission: %s",
+ operation,
+ sensor.getName().string(),
+ sensor.getRequiredPermission().string());
+ return false;
+ }
+}
+
// ---------------------------------------------------------------------------
SensorService::SensorRecord::SensorRecord(
@@ -763,6 +819,9 @@
bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
Mutex::Autolock _l(mConnectionLock);
+ if (!verifyCanAccessSensor(mService->getSensorFromHandle(handle), "Tried adding")) {
+ return false;
+ }
if (mSensorInfo.indexOfKey(handle) < 0) {
mSensorInfo.add(handle, FlushInfo());
return true;
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 1dc2dd3..e88ffc8 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -131,6 +131,7 @@
String8 getSensorName(int handle) const;
bool isVirtualSensor(int handle) const;
+ Sensor getSensorFromHandle(int handle) const;
void recordLastValue(const sensors_event_t* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
Sensor registerSensor(SensorInterface* sensor);
@@ -141,7 +142,8 @@
const sp<SensorEventConnection>& connection, int handle);
void cleanupAutoDisabledSensor(const sp<SensorEventConnection>& connection,
sensors_event_t const* buffer, const int count);
-
+ static bool canAccessSensor(const Sensor& sensor);
+ static bool verifyCanAccessSensor(const Sensor& sensor, const char* operation);
// constants
Vector<Sensor> mSensorList;
Vector<Sensor> mUserSensorListDebug;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 975631c..f7d32d0 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -155,5 +155,23 @@
return mFlinger->onLayerRemoved(this, handle);
}
+status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
+ sp<Layer> layer = getLayerUser(handle);
+ if (layer == NULL) {
+ return NAME_NOT_FOUND;
+ }
+ layer->clearFrameStats();
+ return NO_ERROR;
+}
+
+status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
+ sp<Layer> layer = getLayerUser(handle);
+ if (layer == NULL) {
+ return NAME_NOT_FOUND;
+ }
+ layer->getFrameStats(outStats);
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 84e649f..b6d7381 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -60,6 +60,10 @@
virtual status_t destroySurface(const sp<IBinder>& handle);
+ virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
+
+ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
+
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index f4cb8b5..85e2ee5 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -380,6 +380,12 @@
return INVALID_OPERATION;
}
+status_t VirtualDisplaySurface::detachNextBuffer(
+ sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
+ VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
const sp<GraphicBuffer>& /* buffer */) {
VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
@@ -454,11 +460,12 @@
return mSource[SOURCE_SINK]->query(what, value);
}
-status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
+status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
QueueBufferOutput* output) {
QueueBufferOutput qbo;
- status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
+ status_t result = mSource[SOURCE_SINK]->connect(listener, api,
+ producerControlledByApp, &qbo);
if (result == NO_ERROR) {
updateQueueBufferOutput(qbo);
*output = mQueueBufferOutput;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 09e5544..144f871 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -27,6 +27,7 @@
// ---------------------------------------------------------------------------
class HWComposer;
+class IProducerListener;
/* This DisplaySurface implementation supports virtual displays, where GLES
* and/or HWC compose into a buffer that is then passed to an arbitrary
@@ -100,12 +101,14 @@
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
virtual status_t detachBuffer(int slot);
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
virtual status_t queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output);
virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
virtual int query(int what, int* value);
- virtual status_t connect(const sp<IBinder>& token,
+ virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output);
virtual status_t disconnect(int api);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 2fb665e..c09bbe4 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -22,6 +22,7 @@
#include <cutils/log.h>
#include <ui/Fence.h>
+#include <ui/FrameStats.h>
#include <utils/String8.h>
@@ -100,7 +101,7 @@
processFencesLocked();
}
-void FrameTracker::clear() {
+void FrameTracker::clearStats() {
Mutex::Autolock lock(mMutex);
for (size_t i = 0; i < NUM_FRAME_RECORDS; i++) {
mFrameRecords[i].desiredPresentTime = 0;
@@ -115,6 +116,32 @@
mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
}
+void FrameTracker::getStats(FrameStats* outStats) const {
+ Mutex::Autolock lock(mMutex);
+ processFencesLocked();
+
+ outStats->refreshPeriodNano = mDisplayPeriod;
+
+ const size_t offset = mOffset;
+ for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
+ const size_t index = (offset + i) % NUM_FRAME_RECORDS;
+
+ // Skip frame records with no data (if buffer not yet full).
+ if (mFrameRecords[index].desiredPresentTime == 0) {
+ continue;
+ }
+
+ nsecs_t desiredPresentTimeNano = mFrameRecords[index].desiredPresentTime;
+ outStats->desiredPresentTimesNano.push_back(desiredPresentTimeNano);
+
+ nsecs_t actualPresentTimeNano = mFrameRecords[index].actualPresentTime;
+ outStats->actualPresentTimesNano.push_back(actualPresentTimeNano);
+
+ nsecs_t frameReadyTimeNano = mFrameRecords[index].frameReadyTime;
+ outStats->frameReadyTimesNano.push_back(frameReadyTimeNano);
+ }
+}
+
void FrameTracker::logAndResetStats(const String8& name) {
Mutex::Autolock lock(mMutex);
logStatsLocked(name);
@@ -206,7 +233,7 @@
mFrameRecords[idx].actualPresentTime < INT64_MAX;
}
-void FrameTracker::dump(String8& result) const {
+void FrameTracker::dumpStats(String8& result) const {
Mutex::Autolock lock(mMutex);
processFencesLocked();
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 9233247..cd5e3f3 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -78,15 +78,18 @@
// advanceFrame advances the frame tracker to the next frame.
void advanceFrame();
- // clear resets all the tracked frame data to zero.
- void clear();
+ // clearStats clears the tracked frame stats.
+ void clearStats();
+
+ // getStats gets the tracked frame stats.
+ void getStats(FrameStats* outStats) const;
// logAndResetStats dumps the current statistics to the binary event log
// and then resets the accumulated statistics to their initial values.
void logAndResetStats(const String8& name);
- // dump appends the current frame display time history to the result string.
- void dump(String8& result) const;
+ // dumpStats dump appends the current frame display time history to the result string.
+ void dumpStats(String8& result) const;
private:
struct FrameRecord {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 58a3040..1b86204 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -959,7 +959,7 @@
bool Layer::isVisible() const {
const Layer::State& s(mDrawingState);
return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
- && (mActiveBuffer != NULL);
+ && (mActiveBuffer != NULL || mSidebandStream != NULL);
}
Region Layer::latchBuffer(bool& recomputeVisibleRegions)
@@ -1244,18 +1244,22 @@
}
}
-void Layer::dumpStats(String8& result) const {
- mFrameTracker.dump(result);
+void Layer::dumpFrameStats(String8& result) const {
+ mFrameTracker.dumpStats(result);
}
-void Layer::clearStats() {
- mFrameTracker.clear();
+void Layer::clearFrameStats() {
+ mFrameTracker.clearStats();
}
void Layer::logFrameStats() {
mFrameTracker.logAndResetStats(mName);
}
+void Layer::getFrameStats(FrameStats* outStats) const {
+ mFrameTracker.getStats(outStats);
+}
+
// ---------------------------------------------------------------------------
Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8f8989e..62970c3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -27,6 +27,7 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
@@ -288,9 +289,10 @@
/* always call base class first */
void dump(String8& result, Colorizer& colorizer) const;
- void dumpStats(String8& result) const;
- void clearStats();
+ void dumpFrameStats(String8& result) const;
+ void clearFrameStats();
void logFrameStats();
+ void getFrameStats(FrameStats* outStats) const;
protected:
// constant
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index cd3fdf1..d0e81bc 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -69,6 +69,11 @@
return mProducer->detachBuffer(slot);
}
+status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ return mProducer->detachNextBuffer(outBuffer, outFence);
+}
+
status_t MonitoredProducer::attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer) {
return mProducer->attachBuffer(outSlot, buffer);
@@ -87,9 +92,9 @@
return mProducer->query(what, value);
}
-status_t MonitoredProducer::connect(const sp<IBinder>& token, int api,
- bool producerControlledByApp, QueueBufferOutput* output) {
- return mProducer->connect(token, api, producerControlledByApp, output);
+status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp, QueueBufferOutput* output) {
+ return mProducer->connect(listener, api, producerControlledByApp, output);
}
status_t MonitoredProducer::disconnect(int api) {
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 7ac6f06..f034e39 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -21,7 +21,7 @@
namespace android {
-class IBinder;
+class IProducerListener;
class NativeHandle;
class SurfaceFlinger;
@@ -39,13 +39,15 @@
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, bool async,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
virtual status_t detachBuffer(int slot);
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
virtual status_t attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer);
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output);
virtual void cancelBuffer(int slot, const sp<Fence>& fence);
virtual int query(int what, int* value);
- virtual status_t connect(const sp<IBinder>& token, int api,
+ virtual status_t connect(const sp<IProducerListener>& token, int api,
bool producerControlledByApp, QueueBufferOutput* output);
virtual status_t disconnect(int api);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d084bf5..a346520 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -574,6 +574,18 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::clearAnimationFrameStats() {
+ Mutex::Autolock _l(mStateLock);
+ mAnimFrameTracker.clearStats();
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const {
+ Mutex::Autolock _l(mStateLock);
+ mAnimFrameTracker.getStats(outStats);
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -2255,14 +2267,14 @@
result.appendFormat("%" PRId64 "\n", period);
if (name.isEmpty()) {
- mAnimFrameTracker.dump(result);
+ mAnimFrameTracker.dumpStats(result);
} else {
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(currentLayers[i]);
if (name == layer->getName()) {
- layer->dumpStats(result);
+ layer->dumpFrameStats(result);
}
}
}
@@ -2282,11 +2294,11 @@
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(currentLayers[i]);
if (name.isEmpty() || (name == layer->getName())) {
- layer->clearStats();
+ layer->clearFrameStats();
}
}
- mAnimFrameTracker.clear();
+ mAnimFrameTracker.clearStats();
}
// This should only be called from the main thread. Otherwise it would need
@@ -2500,6 +2512,8 @@
case BOOT_FINISHED:
case BLANK:
case UNBLANK:
+ case CLEAR_ANIMATION_FRAME_STATS:
+ case GET_ANIMATION_FRAME_STATS:
{
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 630f4b7..717ee66 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -210,6 +210,8 @@
// called when screen is turning back on
virtual void unblank(const sp<IBinder>& display);
virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info);
+ virtual status_t clearAnimationFrameStats();
+ virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
/* ------------------------------------------------------------------------
* DeathRecipient interface