From 134f0422866e8985188ed10dfbdcb8e6c34b87f7 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Tue, 8 Mar 2011 12:18:54 -0800 Subject: [PATCH] ANativeWindow: add queues-to-window-composer check. This change adds a new 'method' to the ANativeWindow interface to check whether buffers queued to the window will be sent directly to the system window compositor. Change-Id: I4d4b199e328c110b68b250029aea650f03c8724d Bug: 3495535 --- include/surfaceflinger/ISurfaceComposer.h | 7 +- include/ui/egl/android_natives.h | 15 ++++ libs/gui/SurfaceTextureClient.cpp | 4 + libs/gui/tests/Android.mk | 53 ++++++++++++ libs/gui/tests/SurfaceTextureClient_test.cpp | 47 +++++++++++ .../ISurfaceComposer.cpp | 44 +++++++++- libs/surfaceflinger_client/Surface.cpp | 4 + libs/surfaceflinger_client/tests/Android.mk | 52 ++++++++++++ .../tests/Surface_test.cpp | 80 +++++++++++++++++++ services/surfaceflinger/SurfaceFlinger.cpp | 34 ++++++++ services/surfaceflinger/SurfaceFlinger.h | 1 + 11 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 libs/gui/tests/Android.mk create mode 100644 libs/gui/tests/SurfaceTextureClient_test.cpp create mode 100644 libs/surfaceflinger_client/tests/Surface_test.cpp diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index 361e7dc28..dea1b101e 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -138,6 +138,10 @@ public: * This is an ASYNCHRONOUS call. */ virtual void signal() const = 0; + + /* verify that an ISurface was created by SurfaceFlinger. + */ + virtual bool authenticateSurface(const sp& surface) const = 0; }; // ---------------------------------------------------------------------------- @@ -161,7 +165,8 @@ public: SIGNAL, CAPTURE_SCREEN, TURN_ELECTRON_BEAM_OFF, - TURN_ELECTRON_BEAM_ON + TURN_ELECTRON_BEAM_ON, + AUTHENTICATE_SURFACE, }; virtual status_t onTransact( uint32_t code, diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h index fd83f469c..0ac34d04d 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -95,6 +95,21 @@ enum { * 5. Queue, dequeue, queue, dequeue, ad infinitum */ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + + /* Check whether queueBuffer operations on the ANativeWindow send the buffer + * to the window compositor. The query sets the returned 'value' argument + * to 1 if the ANativeWindow DOES send queued buffers directly to the window + * compositor and 0 if the buffers do not go directly to the window + * compositor. + * + * This can be used to determine whether protected buffer content should be + * sent to the ANativeWindow. Note, however, that a result of 1 does NOT + * indicate that queued buffers will be protected from applications or users + * capturing their contents. If that behavior is desired then some other + * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in + * conjunction with this query. + */ + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, }; /* valid operations for the (*perform)() hook */ diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 43b330c79..a40fac9e9 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -156,6 +156,10 @@ int SurfaceTextureClient::query(int what, int* value) { case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = MIN_UNDEQUEUED_BUFFERS; return NO_ERROR; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: + // SurfaceTextureClient currently never queues frames to SurfaceFlinger. + *value = 0; + return NO_ERROR; } return BAD_VALUE; } diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk new file mode 100644 index 000000000..1dd888541 --- /dev/null +++ b/libs/gui/tests/Android.mk @@ -0,0 +1,53 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. +test_src_files := \ + SurfaceTextureClient_test.cpp \ + +shared_libraries := \ + libcutils \ + libutils \ + libbinder \ + libgui \ + libstlport \ + +static_libraries := \ + libgtest \ + libgtest_main \ + +c_includes := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +module_tags := tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +# Build the manual test programs. +include $(call all-subdir-makefiles) + +endif + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp new file mode 100644 index 000000000..0f140ffae --- /dev/null +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 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 +#include + +namespace android { + +class SurfaceTextureClientTest : public ::testing::Test { +protected: + virtual void SetUp() { + mST = new SurfaceTexture(123); + mSTC = new SurfaceTextureClient(mST); + } + + virtual void TearDown() { + mST.clear(); + mSTC.clear(); + } + + sp mST; + sp mSTC; +}; + +TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { + sp anw(mSTC); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(0, result); +} + +} diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index 01ae23fe2..8951c3f04 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -25,9 +25,11 @@ #include #include +#include + #include -#include +#include // --------------------------------------------------------------------------- @@ -178,6 +180,40 @@ public: data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual bool authenticateSurface(const sp& surface) const + { + Parcel data, reply; + int err = NO_ERROR; + err = data.writeInterfaceToken( + ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing " + "interface descriptor: %s (%d)", strerror(-err), -err); + return false; + } + err = data.writeStrongBinder(surface->asBinder()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing strong " + "binder to parcel: %s (%d)", strerror(-err), -err); + return false; + } + err = remote()->transact(BnSurfaceComposer::AUTHENTICATE_SURFACE, data, + &reply); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error performing " + "transaction: %s (%d)", strerror(-err), -err); + return false; + } + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error retrieving " + "result: %s (%d)", strerror(-err), -err); + return false; + } + return result != 0; + } }; IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); @@ -273,6 +309,12 @@ status_t BnSurfaceComposer::onTransact( status_t res = turnElectronBeamOn(mode); reply->writeInt32(res); } break; + case AUTHENTICATE_SURFACE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + int32_t result = authenticateSurface(surface) ? 1 : 0; + reply->writeInt32(result); + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 818114aea..afabbf4db 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -712,6 +712,10 @@ int Surface::query(int what, int* value) case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = MIN_UNDEQUEUED_BUFFERS; return NO_ERROR; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: + sp sf(ComposerService::getComposerService()); + *value = sf->authenticateSurface(mSurface) ? 1 : 0; + return NO_ERROR; } return BAD_VALUE; } diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk index 5053e7d64..212b8e759 100644 --- a/libs/surfaceflinger_client/tests/Android.mk +++ b/libs/surfaceflinger_client/tests/Android.mk @@ -1 +1,53 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. +test_src_files := \ + Surface_test.cpp \ + +shared_libraries := \ + libcutils \ + libutils \ + libbinder \ + libsurfaceflinger_client \ + libstlport \ + +static_libraries := \ + libgtest \ + libgtest_main \ + +c_includes := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +module_tags := tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +# Build the manual test programs. include $(call all-subdir-makefiles) + +endif + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/surfaceflinger_client/tests/Surface_test.cpp b/libs/surfaceflinger_client/tests/Surface_test.cpp new file mode 100644 index 000000000..b39631ce8 --- /dev/null +++ b/libs/surfaceflinger_client/tests/Surface_test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 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 +#include +#include +#include + +#include + +namespace android { + +class SurfaceTest : public ::testing::Test { +protected: + virtual sp getSurfaceComposerClient() { + return sp(new SurfaceComposerClient); + } + + virtual void SetUp() { + mComposerClient = getSurfaceComposerClient(); + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface(getpid(), + String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + + mSurface = mSurfaceControl->getSurface(); + ASSERT_TRUE(mSurface != NULL); + } + + virtual void TearDown() { + mComposerClient->dispose(); + } + + sp mSurface; + sp mComposerClient; + sp mSurfaceControl; +}; + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { + sp anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { + mSurfaceControl.clear(); + + sp anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +} diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 554fa4301..7b19a4c7d 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -327,6 +327,40 @@ void SurfaceFlinger::signal() const { const_cast(this)->signalEvent(); } +bool SurfaceFlinger::authenticateSurface(const sp& surface) const { + Mutex::Autolock _l(mStateLock); + sp surfBinder(surface->asBinder()); + + // Check the visible layer list for the ISurface + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + size_t count = currentLayers.size(); + for (size_t i=0 ; i& layer(currentLayers[i]); + sp lbc(layer->getLayerBaseClient()); + if (lbc != NULL && lbc->getSurfaceBinder() == surfBinder) { + return true; + } + } + + // Check the layers in the purgatory. This check is here so that if a + // Surface gets destroyed before all the clients are done using it, the + // error will not be reported as "surface XYZ is not authenticated", but + // will instead fail later on when the client tries to use the surface, + // which should be reported as "surface XYZ returned an -ENODEV". The + // purgatorized layers are no less authentic than the visible ones, so this + // should not cause any harm. + size_t purgatorySize = mLayerPurgatory.size(); + for (size_t i=0 ; i& layer(mLayerPurgatory.itemAt(i)); + sp lbc(layer->getLayerBaseClient()); + if (lbc != NULL && lbc->getSurfaceBinder() == surfBinder) { + return true; + } + } + + return false; +} + status_t SurfaceFlinger::postMessageAsync(const sp& msg, nsecs_t reltime, uint32_t flags) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6dd91ac70..95668190f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -209,6 +209,7 @@ 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 bool authenticateSurface(const sp& surface) const; virtual status_t captureScreen(DisplayID dpy, sp* heap,