diff options
Diffstat (limited to 'widget/gonk/nativewindow/FakeSurfaceComposer.cpp')
-rw-r--r-- | widget/gonk/nativewindow/FakeSurfaceComposer.cpp | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp new file mode 100644 index 000000000..7e4a2a9d8 --- /dev/null +++ b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * 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 "FakeSurfaceComposer" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <sys/types.h> +#include <errno.h> +#include <cutils/atomic.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include <private/android_filesystem_config.h> + +#include <gui/IDisplayEventConnection.h> +#include <gui/GraphicBufferAlloc.h> +#include <gui/Surface.h> +#include <ui/DisplayInfo.h> + +#if ANDROID_VERSION >= 21 +#include <ui/Rect.h> +#endif + +#include "../libdisplay/GonkDisplay.h" +#include "../nsScreenManagerGonk.h" +#include "FakeSurfaceComposer.h" +#include "gfxPrefs.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +using namespace mozilla; + +namespace android { + +/* static */ +void FakeSurfaceComposer::instantiate() { + defaultServiceManager()->addService( + String16("SurfaceFlinger"), new FakeSurfaceComposer()); +} + +FakeSurfaceComposer::FakeSurfaceComposer() + : BnSurfaceComposer() +{ +} + +FakeSurfaceComposer::~FakeSurfaceComposer() +{ +} + +status_t FakeSurfaceComposer::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) +{ + switch (code) { + case CREATE_CONNECTION: + case CREATE_DISPLAY: + case SET_TRANSACTION_STATE: + case CAPTURE_SCREEN: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + // Accept request only when uid is root. + if (uid != AID_ROOT) { + ALOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + break; + } + } + + return BnSurfaceComposer::onTransact(code, data, reply, flags); +} + +sp<ISurfaceComposerClient> FakeSurfaceComposer::createConnection() +{ + return nullptr; +} + +sp<IGraphicBufferAlloc> FakeSurfaceComposer::createGraphicBufferAlloc() +{ + sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc()); + return gba; +} + +class DestroyDisplayRunnable : public Runnable { +public: + DestroyDisplayRunnable(FakeSurfaceComposer* aComposer, ssize_t aIndex) + : mComposer(aComposer), mIndex(aIndex) { } + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); + Mutex::Autolock _l(mComposer->mStateLock); + RefPtr<nsScreenManagerGonk> screenManager = + nsScreenManagerGonk::GetInstance(); + screenManager->RemoveScreen(GonkDisplay::DISPLAY_VIRTUAL); + mComposer->mDisplays.removeItemsAt(mIndex); + return NS_OK; + } + sp<FakeSurfaceComposer> mComposer; + ssize_t mIndex; +}; + +sp<IBinder> FakeSurfaceComposer::createDisplay(const String8& displayName, + bool secure) +{ +#if ANDROID_VERSION >= 19 + class DisplayToken : public BBinder { + sp<FakeSurfaceComposer> composer; + virtual ~DisplayToken() { + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); + // no more references, this display must be terminated + Mutex::Autolock _l(composer->mStateLock); + ssize_t idx = composer->mDisplays.indexOfKey(this); + if (idx >= 0) { + nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(composer.get(), idx)); + NS_DispatchToMainThread(task); + } + } + public: + DisplayToken(const sp<FakeSurfaceComposer>& composer) + : composer(composer) { + } + }; + + sp<BBinder> token = new DisplayToken(this); + + Mutex::Autolock _l(mStateLock); + DisplayDeviceState info(HWC_DISPLAY_VIRTUAL); + info.displayName = displayName; + info.displayId = GonkDisplay::DISPLAY_VIRTUAL; + info.isSecure = secure; + mDisplays.add(token, info); + return token; +#else + return nullptr; +#endif +} + +#if ANDROID_VERSION >= 19 +void FakeSurfaceComposer::destroyDisplay(const sp<IBinder>& display) +{ + Mutex::Autolock _l(mStateLock); + + ssize_t idx = mDisplays.indexOfKey(display); + if (idx < 0) { + ALOGW("destroyDisplay: invalid display token"); + return; + } + + nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(this, idx)); + NS_DispatchToMainThread(task); +} +#endif + +sp<IBinder> FakeSurfaceComposer::getBuiltInDisplay(int32_t id) +{ + // support only primary display + if (uint32_t(id) != HWC_DISPLAY_PRIMARY) { + return NULL; + } + + if (!mPrimaryDisplay.get()) { + mPrimaryDisplay = new BBinder(); + } + return mPrimaryDisplay; +} + +void FakeSurfaceComposer::setTransactionState( + const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, + uint32_t flags) +{ + Mutex::Autolock _l(mStateLock); + size_t count = displays.size(); + for (size_t i=0 ; i<count ; i++) { + const DisplayState& s(displays[i]); + setDisplayStateLocked(s); + } +} + +uint32_t FakeSurfaceComposer::setDisplayStateLocked(const DisplayState& s) +{ + ssize_t dpyIdx = mDisplays.indexOfKey(s.token); + if (dpyIdx < 0) { + return 0; + } + + uint32_t flags = 0; + DisplayDeviceState& disp(mDisplays.editValueAt(dpyIdx)); + + if (!disp.isValid()) { + return 0; + } + + const uint32_t what = s.what; + if (what & DisplayState::eSurfaceChanged) { + if (disp.surface->asBinder() != s.surface->asBinder()) { + disp.surface = s.surface; + flags |= eDisplayTransactionNeeded; + } + } + if (what & DisplayState::eLayerStackChanged) { + if (disp.layerStack != s.layerStack) { + disp.layerStack = s.layerStack; + flags |= eDisplayTransactionNeeded; + } + } + if (what & DisplayState::eDisplayProjectionChanged) { + if (disp.orientation != s.orientation) { + disp.orientation = s.orientation; + flags |= eDisplayTransactionNeeded; + } + if (disp.frame != s.frame) { + disp.frame = s.frame; + flags |= eDisplayTransactionNeeded; + } + if (disp.viewport != s.viewport) { + disp.viewport = s.viewport; + flags |= eDisplayTransactionNeeded; + } + } +#if ANDROID_VERSION >= 21 + if (what & DisplayState::eDisplaySizeChanged) { + if (disp.width != s.width) { + disp.width = s.width; + flags |= eDisplayTransactionNeeded; + } + if (disp.height != s.height) { + disp.height = s.height; + flags |= eDisplayTransactionNeeded; + } + } +#endif + + if (what & DisplayState::eSurfaceChanged) { + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction([&]() { + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); + RefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance(); + screenManager->AddScreen(GonkDisplay::DISPLAY_VIRTUAL, disp.surface.get()); + }); + NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC); + } + + return flags; +} + +void FakeSurfaceComposer::bootFinished() +{ +} + +bool FakeSurfaceComposer::authenticateSurfaceTexture( + const sp<IGraphicBufferProducer>& bufferProducer) const { + return false; +} + +sp<IDisplayEventConnection> FakeSurfaceComposer::createDisplayEventConnection() { + return nullptr; +} + +// --------------------------------------------------------------------------- +// Capture screen into an IGraphiBufferProducer +// --------------------------------------------------------------------------- + +class Barrier { +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + + // Release any threads waiting at the Barrier. + // Provides release semantics: preceding loads and stores will be visible + // to other threads before they wake up. + void open() { + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + + // Reset the Barrier, so wait() will block until open() has been called. + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + + // Wait until the Barrier is OPEN. + // Provides acquire semantics: no subsequent loads or stores will occur + // until wait() returns. + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +/* The code below is here to handle b/8734824 + * + * We create a IGraphicBufferProducer wrapper that forwards all calls + * to the calling binder thread, where they are executed. This allows + * the calling thread to be reused (on the other side) and not + * depend on having "enough" binder threads to handle the requests. + * + */ + +class GraphicProducerWrapper : public BBinder, public MessageHandler { + sp<IGraphicBufferProducer> impl; + sp<Looper> looper; + status_t result; + bool exitPending; + bool exitRequested; + mutable Barrier barrier; + volatile int32_t memoryBarrier; + uint32_t code; + Parcel const* data; + Parcel* reply; + + enum { + MSG_API_CALL, + MSG_EXIT + }; + + /* + * this is called by our "fake" BpGraphicBufferProducer. We package the + * data and reply Parcel and forward them to the calling thread. + */ + virtual status_t transact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t flags) { + this->code = code; + this->data = &data; + this->reply = reply; + android_atomic_acquire_store(0, &memoryBarrier); + if (exitPending) { + // if we've exited, we run the message synchronously right here + handleMessage(Message(MSG_API_CALL)); + } else { + barrier.close(); + looper->sendMessage(this, Message(MSG_API_CALL)); + barrier.wait(); + } + return NO_ERROR; + } + + /* + * here we run on the binder calling thread. All we've got to do is + * call the real BpGraphicBufferProducer. + */ + virtual void handleMessage(const Message& message) { + android_atomic_release_load(&memoryBarrier); + if (message.what == MSG_API_CALL) { + impl->asBinder()->transact(code, data[0], reply); + barrier.open(); + } else if (message.what == MSG_EXIT) { + exitRequested = true; + } + } + +public: + GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) : + impl(impl), looper(new Looper(true)), result(NO_ERROR), + exitPending(false), exitRequested(false) { + } + + status_t waitForResponse() { + do { + looper->pollOnce(-1); + } while (!exitRequested); + return result; + } + + void exit(status_t result) { + this->result = result; + exitPending = true; + looper->sendMessage(this, Message(MSG_EXIT)); + } +}; + +status_t +FakeSurfaceComposer::captureScreen(const sp<IBinder>& display + , const sp<IGraphicBufferProducer>& producer +#if ANDROID_VERSION >= 21 + , Rect sourceCrop +#endif + , uint32_t reqWidth + , uint32_t reqHeight + , uint32_t minLayerZ + , uint32_t maxLayerZ +#if ANDROID_VERSION >= 21 + , bool useIdentityTransform + , Rotation rotation +#elif ANDROID_VERSION < 19 + , bool isCpuConsumer +#endif + ) +{ + if (display == 0 || producer == 0) { + return BAD_VALUE; + } + + // Limit only to primary display + if (display != mPrimaryDisplay) { + return BAD_VALUE; + } + + // this creates a "fake" BBinder which will serve as a "fake" remote + // binder to receive the marshaled calls and forward them to the + // real remote (a BpGraphicBufferProducer) + sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer); + // the asInterface() call below creates our "fake" BpGraphicBufferProducer + // which does the marshaling work forwards to our "fake remote" above. + sp<IGraphicBufferProducer> fakeProducer = IGraphicBufferProducer::asInterface(wrapper); + + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction([&]() { + captureScreenImp(fakeProducer, reqWidth, reqHeight, wrapper.get()); + }); + NS_DispatchToMainThread(runnable); + + status_t result = wrapper->waitForResponse(); + + return result; +} + +class RunnableCallTask final : public Runnable +{ +public: + explicit RunnableCallTask(nsIRunnable* aRunnable) + : mRunnable(aRunnable) {} + + NS_IMETHOD Run() override + { + return mRunnable->Run(); + } +protected: + nsCOMPtr<nsIRunnable> mRunnable; +}; + +void +FakeSurfaceComposer::captureScreenImp(const sp<IGraphicBufferProducer>& producer, + uint32_t reqWidth, + uint32_t reqHeight, + const sp<GraphicProducerWrapper>& wrapper) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(wrapper.get()); + + RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen(); + + // get screen geometry + nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect(); + const uint32_t hw_w = screenBounds.width; + const uint32_t hw_h = screenBounds.height; + + if (reqWidth > hw_w || reqHeight > hw_h) { + ALOGE("size mismatch (%d, %d) > (%d, %d)", + reqWidth, reqHeight, hw_w, hw_h); + static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE); + return; + } + + reqWidth = (!reqWidth) ? hw_w : reqWidth; + reqHeight = (!reqHeight) ? hw_h : reqHeight; + + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction([screen, reqWidth, reqHeight, producer, wrapper]() { + // create a surface (because we're a producer, and we need to + // dequeue/queue a buffer) + sp<Surface> sur = new Surface(producer); + ANativeWindow* window = sur.get(); + // The closure makes screen const and we can't call forget() on it. + RefPtr<nsScreenGonk> screenAlias = screen; + + if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != NO_ERROR) { + static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE); + NS_ReleaseOnMainThread(screenAlias.forget()); + return; + } + uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + + int err = 0; + err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight); + err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888); + err |= native_window_set_usage(window, usage); + + status_t result = NO_ERROR; + if (err == NO_ERROR) { + ANativeWindowBuffer* buffer; + result = native_window_dequeue_buffer_and_wait(window, &buffer); + if (result == NO_ERROR) { + nsresult rv = screen->MakeSnapshot(buffer); + if (rv != NS_OK) { + result = INVALID_OPERATION; + } + window->queueBuffer(window, buffer, -1); + } + } else { + result = BAD_VALUE; + } + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result); + NS_ReleaseOnMainThread(screenAlias.forget()); + }); + + layers::CompositorThreadHolder::Loop()->PostTask( + MakeAndAddRef<RunnableCallTask>(runnable)); +} + +#if ANDROID_VERSION >= 21 +void +FakeSurfaceComposer::setPowerMode(const sp<IBinder>& display, int mode) +{ +} + +status_t +FakeSurfaceComposer::getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs) +{ + if (configs == NULL) { + return BAD_VALUE; + } + + // Limit DisplayConfigs only to primary display + if (!display.get() || display != mPrimaryDisplay) { + return NAME_NOT_FOUND; + } + + configs->clear(); + DisplayInfo info = DisplayInfo(); + + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction([&]() { + MOZ_ASSERT(NS_IsMainThread()); + getPrimaryDisplayInfo(&info); + }); + NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC); + + configs->push_back(info); + return NO_ERROR; +} + +status_t +FakeSurfaceComposer::getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) +{ + return INVALID_OPERATION; +} + +int +FakeSurfaceComposer::getActiveConfig(const sp<IBinder>& display) +{ + // Only support primary display. + if (display.get() && (display == mPrimaryDisplay)) { + return 0; + } + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::setActiveConfig(const sp<IBinder>& display, int id) +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::clearAnimationFrameStats() +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::getAnimationFrameStats(FrameStats* outStats) const +{ + return INVALID_OPERATION; +} +#else +void +FakeSurfaceComposer::blank(const sp<IBinder>& display) +{ +} + +void +FakeSurfaceComposer::unblank(const sp<IBinder>& display) +{ +} + +status_t +FakeSurfaceComposer::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) +{ + if (info == NULL) { + return BAD_VALUE; + } + + // Limit DisplayConfigs only to primary display + if (!display.get() || display != mPrimaryDisplay) { + return NAME_NOT_FOUND; + } + + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction([&]() { + MOZ_ASSERT(NS_IsMainThread()); + getPrimaryDisplayInfo(info); + }); + NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC); + + return NO_ERROR; +} +#endif + +#define VSYNC_EVENT_PHASE_OFFSET_NS 0 +#define SF_VSYNC_EVENT_PHASE_OFFSET_NS 0 + +void +FakeSurfaceComposer::getPrimaryDisplayInfo(DisplayInfo* info) +{ + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); + + // Implementation mimic android SurfaceFlinger::getDisplayConfigs(). + + class Density { + static int getDensityFromProperty(char const* propName) { + char property[PROPERTY_VALUE_MAX]; + int density = 0; + if (property_get(propName, property, NULL) > 0) { + density = atoi(property); + } + return density; + } + public: + static int getEmuDensity() { + return getDensityFromProperty("qemu.sf.lcd_density"); } + static int getBuildDensity() { + return getDensityFromProperty("ro.sf.lcd_density"); } + }; + + RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen(); + + float xdpi = screen->GetDpi(); + float ydpi = screen->GetDpi(); + int fps = 60; // XXX set a value from hwc hal + nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect(); + + // The density of the device is provided by a build property + float density = Density::getBuildDensity() / 160.0f; + if (density == 0) { + // the build doesn't provide a density -- this is wrong! + // use xdpi instead + ALOGE("ro.sf.lcd_density must be defined as a build property"); + density = xdpi / 160.0f; + } + info->density = density; + info->orientation = screen->EffectiveScreenRotation(); + + info->w = screenBounds.width; + info->h = screenBounds.height; + info->xdpi = xdpi; + info->ydpi = ydpi; + info->fps = fps; +#if ANDROID_VERSION >= 21 + info->appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; + + // This is how far in advance a buffer must be queued for + // presentation at a given time. If you want a buffer to appear + // on the screen at time N, you must submit the buffer before + // (N - presentationDeadline). + // + // Normally it's one full refresh period (to give SF a chance to + // latch the buffer), but this can be reduced by configuring a + // DispSync offset. Any additional delays introduced by the hardware + // composer or panel must be accounted for here. + // + // We add an additional 1ms to allow for processing time and + // differences between the ideal and actual refresh rate. + info->presentationDeadline = + (1e9 / fps) - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000; +#endif + // All non-virtual displays are currently considered secure. + info->secure = true; +} + +}; // namespace android |