Merge "CommandEntry: Initialize 'enabled' field"
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 04167f7..c44a24b 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -32,6 +32,14 @@
#include <assert.h>
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
#include <unistd.h>
#include <cstddef>
#include <string>
@@ -130,7 +138,7 @@
/**
* This baseclass owns a single object, used to make various classes RAII.
*/
-template <typename T, typename R, R (*Destroy)(T), T DEFAULT>
+template <typename T, void (*Destroy)(T), T DEFAULT>
class ScopedAResource {
public:
/**
@@ -198,7 +206,7 @@
/**
* Convenience wrapper. See AParcel.
*/
-class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
+class ScopedAParcel : public impl::ScopedAResource<AParcel*, AParcel_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -219,7 +227,7 @@
/**
* Convenience wrapper. See AStatus.
*/
-class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
+class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -291,7 +299,7 @@
* Convenience wrapper. See AIBinder_DeathRecipient.
*/
class ScopedAIBinder_DeathRecipient
- : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
+ : public impl::ScopedAResource<AIBinder_DeathRecipient*, AIBinder_DeathRecipient_delete,
nullptr> {
public:
/**
@@ -308,7 +316,7 @@
* Convenience wrapper. See AIBinder_Weak.
*/
class ScopedAIBinder_Weak
- : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
+ : public impl::ScopedAResource<AIBinder_Weak*, AIBinder_Weak_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -324,10 +332,22 @@
SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
};
+namespace internal {
+
+static void closeWithError(int fd) {
+ if (fd == -1) return;
+ int ret = close(fd);
+ if (ret != 0) {
+ syslog(LOG_ERR, "Could not close FD %d: %s", fd, strerror(errno));
+ }
+}
+
+} // namespace internal
+
/**
* Convenience wrapper for a file descriptor.
*/
-class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> {
+class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWithError, -1> {
public:
/**
* Takes ownership of a.
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index bf0386f..8328322 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -22,8 +22,13 @@
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueueConsumer.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
#include <gui/GLConsumer.h>
+#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <utils/Singleton.h>
#include <utils/Trace.h>
@@ -118,7 +123,7 @@
mSize(width, height),
mRequestedSize(mSize),
mNextTransaction(nullptr) {
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
@@ -454,4 +459,103 @@
return new BBQSurface(mProducer, true, scHandle, this);
}
+// Maintains a single worker thread per process that services a list of runnables.
+class AsyncWorker : public Singleton<AsyncWorker> {
+private:
+ std::thread mThread;
+ bool mDone = false;
+ std::deque<std::function<void()>> mRunnables;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ void run() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mDone) {
+ mCv.wait(lock);
+ while (!mRunnables.empty()) {
+ std::function<void()> runnable = mRunnables.front();
+ mRunnables.pop_front();
+ runnable();
+ }
+ }
+ }
+
+public:
+ AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); }
+
+ ~AsyncWorker() {
+ mDone = true;
+ mCv.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+ }
+
+ void post(std::function<void()> runnable) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mRunnables.emplace_back(std::move(runnable));
+ mCv.notify_one();
+ }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker);
+
+// Asynchronously calls ProducerListener functions so we can emulate one way binder calls.
+class AsyncProducerListener : public BnProducerListener {
+private:
+ const sp<IProducerListener> mListener;
+
+public:
+ AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+
+ void onBufferReleased() override {
+ AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
+ }
+
+ void onBuffersDiscarded(const std::vector<int32_t>& slots) override {
+ AsyncWorker::getInstance().post(
+ [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); });
+ }
+};
+
+// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
+// can be non-blocking when the producer is in the client process.
+class BBQBufferQueueProducer : public BufferQueueProducer {
+public:
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
+ : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+
+ status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ if (!listener) {
+ return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
+ }
+
+ return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+ producerControlledByApp, output);
+ }
+};
+
+// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
+// This BQP allows invoking client specified ProducerListeners and invoke them asynchronously,
+// emulating one way binder call behavior. Without this, if the listener calls back into the queue,
+// we can deadlock.
+void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer) {
+ LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");
+
+ sp<BufferQueueCore> core(new BufferQueueCore());
+ LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
+
+ sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+ LOG_ALWAYS_FATAL_IF(producer == nullptr,
+ "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
+
+ sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+ LOG_ALWAYS_FATAL_IF(consumer == nullptr,
+ "BLASTBufferQueue: failed to create BufferQueueConsumer");
+
+ *outProducer = producer;
+ *outConsumer = consumer;
+}
+
} // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1139390..9edea31 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -97,6 +97,8 @@
// can't be copied
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
BLASTBufferQueue(const BLASTBufferQueue& rhs);
+ void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer);
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 4282ef9..17f8b97 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -475,6 +475,46 @@
/*border*/ 0, /*outsideRegion*/ true));
}
+class TestProducerListener : public BnProducerListener {
+public:
+ sp<IGraphicBufferProducer> mIgbp;
+ TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {}
+ void onBufferReleased() override {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ mIgbp->detachNextBuffer(&buffer, &fence);
+ }
+};
+
+TEST_F(BLASTBufferQueueTest, CustomProducerListener) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer();
+ ASSERT_NE(nullptr, igbProducer.get());
+ ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(NO_ERROR,
+ igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU,
+ false, &qbOutput));
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+ for (int i = 0; i < 3; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ }
+ adapter.waitForCallbacks();
+}
+
class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
public:
void test(uint32_t tr) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index d8d989e..53c873a 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -154,6 +154,9 @@
int backgroundBlurRadius = 0;
std::vector<BlurRegion> blurRegions;
+
+ // Name associated with the layer for debugging purposes.
+ std::string name;
};
// Keep in sync with custom comparison function in
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index ee0a70a..1f98a46 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -477,10 +477,23 @@
mGrContext.get());
SkCanvas* canvas = mCapture.tryCapture(surface.get());
+ if (canvas == nullptr) {
+ ALOGE("Cannot acquire canvas from Skia.");
+ return BAD_VALUE;
+ }
// Clear the entire canvas with a transparent black to prevent ghost images.
canvas->clear(SK_ColorTRANSPARENT);
canvas->save();
+ if (mCapture.isCaptureRunning()) {
+ // Record display settings when capture is running.
+ std::stringstream displaySettings;
+ PrintTo(display, &displaySettings);
+ // Store the DisplaySettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
+ SkData::MakeWithCString(displaySettings.str().c_str()));
+ }
+
// Before doing any drawing, let's make sure that we'll start at the origin of the display.
// Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
// displays might have different scaling when compared to the physical screen.
@@ -511,6 +524,15 @@
for (const auto& layer : layers) {
canvas->save();
+ if (mCapture.isCaptureRunning()) {
+ // Record the name of the layer if the capture is running.
+ std::stringstream layerSettings;
+ PrintTo(*layer, &layerSettings);
+ // Store the LayerSettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
+ SkData::MakeWithCString(layerSettings.str().c_str()));
+ }
+
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h
index 52717a7..eaaf598 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.h
+++ b/libs/renderengine/skia/debug/SkiaCapture.h
@@ -45,6 +45,8 @@
SkCanvas* tryCapture(SkSurface* surface);
// Called at the end of every frame.
void endCapture();
+ // Returns whether the capture is running.
+ bool isCaptureRunning() { return mCaptureRunning; }
private:
// Performs the first-frame work of a multi frame SKP capture. Returns true if successful.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d5b599a..0e861ab 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -656,6 +656,8 @@
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
layerSettings.blurRegions = getBlurRegions();
}
+ // Record the name of the layer for debugging further down the stack.
+ layerSettings.name = getName();
return layerSettings;
}