add support for [1974164] Be able to take a screen shot on the device
screenshots are taken using ISurfaceComposer::captureScreen() which returns the size of the screenshot and an IMemoryHeap containing the data. screenshots have limitations: - they will always fail if a secure window is up on screen - require GL_OES_framebuffer_object extension - in some situation, video planes won't been captured Change-Id: I741c68a2d2984fb139039301c3349e6780e2cd58
This commit is contained in:
parent
05c53113e0
commit
1b0b30d043
|
@ -110,6 +110,13 @@ public:
|
|||
*/
|
||||
virtual void bootFinished() = 0;
|
||||
|
||||
/* Capture the specified screen. requires READ_FRAME_BUFFER permission
|
||||
* This function will fail if there is a secure window on screen.
|
||||
*/
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
|
||||
|
||||
/* Signal surfaceflinger that there might be some work to do
|
||||
* This is an ASYNCHRONOUS call.
|
||||
*/
|
||||
|
@ -133,7 +140,8 @@ public:
|
|||
SET_ORIENTATION,
|
||||
FREEZE_DISPLAY,
|
||||
UNFREEZE_DISPLAY,
|
||||
SIGNAL
|
||||
SIGNAL,
|
||||
CAPTURE_SCREEN
|
||||
};
|
||||
|
||||
virtual status_t onTransact( uint32_t code,
|
||||
|
|
|
@ -124,6 +124,21 @@ public:
|
|||
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
|
||||
}
|
||||
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width, uint32_t* height, PixelFormat* format)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
|
||||
data.writeInt32(dpy);
|
||||
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
|
||||
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
|
||||
*width = reply.readInt32();
|
||||
*height = reply.readInt32();
|
||||
*format = reply.readInt32();
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual void signal() const
|
||||
{
|
||||
Parcel data, reply;
|
||||
|
@ -190,6 +205,19 @@ status_t BnSurfaceComposer::onTransact(
|
|||
sp<IBinder> b = getCblk()->asBinder();
|
||||
reply->writeStrongBinder(b);
|
||||
} break;
|
||||
case CAPTURE_SCREEN: {
|
||||
CHECK_INTERFACE(ISurfaceComposer, data, reply);
|
||||
DisplayID dpy = data.readInt32();
|
||||
sp<IMemoryHeap> heap;
|
||||
uint32_t w, h;
|
||||
PixelFormat f;
|
||||
status_t res = captureScreen(dpy, &heap, &w, &h, &f);
|
||||
reply->writeStrongBinder(heap->asBinder());
|
||||
reply->writeInt32(w);
|
||||
reply->writeInt32(h);
|
||||
reply->writeInt32(f);
|
||||
reply->writeInt32(res);
|
||||
} break;
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,10 @@ void GLExtensions::initWithGLStrings(
|
|||
// hack for Adreno 200
|
||||
mHaveTextureExternal = true;
|
||||
}
|
||||
|
||||
if (hasExtension("GL_OES_framebuffer_object")) {
|
||||
mHaveFramebufferObject = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GLExtensions::hasExtension(char const* extension) const
|
||||
|
|
|
@ -39,6 +39,7 @@ class GLExtensions : public Singleton<GLExtensions>
|
|||
bool mHaveTextureExternal : 1;
|
||||
bool mHaveNpot : 1;
|
||||
bool mHaveDirectTexture : 1;
|
||||
bool mHaveFramebufferObject : 1;
|
||||
|
||||
String8 mVendor;
|
||||
String8 mRenderer;
|
||||
|
@ -66,6 +67,10 @@ public:
|
|||
return mHaveDirectTexture;
|
||||
}
|
||||
|
||||
inline bool haveFramebufferObject() const {
|
||||
return mHaveFramebufferObject;
|
||||
}
|
||||
|
||||
void initWithGLStrings(
|
||||
GLubyte const* vendor,
|
||||
GLubyte const* renderer,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -193,6 +193,11 @@ public:
|
|||
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
|
||||
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
|
||||
virtual void signal() const;
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width,
|
||||
uint32_t* height,
|
||||
PixelFormat* format);
|
||||
|
||||
void screenReleased(DisplayID dpy);
|
||||
void screenAcquired(DisplayID dpy);
|
||||
|
@ -361,6 +366,7 @@ private:
|
|||
nsecs_t mBootTime;
|
||||
Permission mHardwareTest;
|
||||
Permission mAccessSurfaceFlinger;
|
||||
Permission mReadFramebuffer;
|
||||
Permission mDump;
|
||||
|
||||
// Can only accessed from the main thread, these members
|
||||
|
|
Loading…
Reference in New Issue