diff options
Diffstat (limited to 'services/surfaceflinger/SurfaceFlinger.cpp')
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index dc241d4..2b06f6f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -75,6 +75,7 @@ SurfaceFlinger::SurfaceFlinger() mBootTime(systemTime()), mHardwareTest("android.permission.HARDWARE_TEST"), mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"), + mReadFramebuffer("android.permission.READ_FRAME_BUFFER"), mDump("android.permission.DUMP"), mVisibleRegionsDirty(false), mDeferReleaseConsole(false), @@ -1465,8 +1466,23 @@ status_t SurfaceFlinger::onTransact( "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } + break; + } + case CAPTURE_SCREEN: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) { + LOGE("Permission Denial: " + "can't read framebuffer pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + break; } } + status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1530,6 +1546,139 @@ status_t SurfaceFlinger::onTransact( // --------------------------------------------------------------------------- +status_t SurfaceFlinger::captureScreen(DisplayID dpy, + sp<IMemoryHeap>* heap, + uint32_t* width, uint32_t* height, PixelFormat* format) +{ + // only one display supported for now + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + if (!GLExtensions::getInstance().haveFramebufferObject()) + return INVALID_OPERATION; + + class MessageCaptureScreen : public MessageBase { + SurfaceFlinger* flinger; + DisplayID dpy; + sp<IMemoryHeap>* heap; + uint32_t* w; + uint32_t* h; + PixelFormat* f; + status_t result; + public: + MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy, + sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f) + : flinger(flinger), dpy(dpy), + heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED) + { + } + status_t getResult() const { + return result; + } + virtual bool handler() { + Mutex::Autolock _l(flinger->mStateLock); + + // if we have secure windows, never allow the screen capture + if (flinger->mSecureFrameBuffer) + return true; + + // make sure to clear all GL error flags + while ( glGetError() != GL_NO_ERROR ) ; + + // get screen geometry + const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware()); + const uint32_t sw = hw.getWidth(); + const uint32_t sh = hw.getHeight(); + const Region screenBounds(hw.bounds()); + const size_t size = sw * sh * 4; + + // create a FBO + GLuint name, tname; + glGenRenderbuffersOES(1, &tname); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname); + glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh); + glGenFramebuffersOES(1, &name); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, + GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname); + + GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + if (status == GL_FRAMEBUFFER_COMPLETE_OES) { + + // invert everything, b/c glReadPixel() below will invert the FB + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrthof(0, sw, 0, sh, 0, 1); + glMatrixMode(GL_MODELVIEW); + + // redraw the screen entirely... + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + const Vector< sp<LayerBase> >& layers( + flinger->mVisibleLayersSortedByZ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer(layers[i]); + if (!strcmp(layer->getTypeId(), "LayerBuffer")) { + // we cannot render LayerBuffer because it doens't + // use OpenGL, and won't show-up in the FBO. + continue; + } + layer->draw(screenBounds); + } + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + // check for errors and return screen capture + if (glGetError() != GL_NO_ERROR) { + // error while rendering + result = INVALID_OPERATION; + } else { + // allocate shared memory large enough to hold the + // screen capture + sp<MemoryHeapBase> base( + new MemoryHeapBase(size, 0, "screen-capture") ); + void* const ptr = base->getBase(); + if (ptr) { + // capture the screen with glReadPixels() + glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr); + if (glGetError() == GL_NO_ERROR) { + *heap = base; + *w = sw; + *h = sh; + *f = PIXEL_FORMAT_RGBA_8888; + result = NO_ERROR; + } + } else { + result = NO_MEMORY; + } + } + } else { + result = BAD_VALUE; + } + + // release FBO resources + glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); + glDeleteRenderbuffersOES(1, &tname); + glDeleteFramebuffersOES(1, &name); + return true; + } + }; + + sp<MessageBase> msg = new MessageCaptureScreen(this, + dpy, heap, width, height, format); + status_t res = postMessageSync(msg); + if (res == NO_ERROR) { + res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult(); + } + return res; +} + +// --------------------------------------------------------------------------- + sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const { sp<Layer> result; |