#include <algorithm>
#include <functional>
#include <limits>
#include <ostream>
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerService.h>
#include <ui/DisplayInfo.h>
#include <utils/String8.h>
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
using ui::ColorMode;
namespace {
const String8 DISPLAY_NAME("Credentials Display Test");
const String8 SURFACE_NAME("Test Surface Name");
const uint32_t ROTATION = 0;
const float FRAME_SCALE = 1.0f;
} // namespace
/**
* This class tests the CheckCredentials method in SurfaceFlinger.
* Methods like EnableVsyncInjections and InjectVsync are not tested since they do not
* return anything meaningful.
*/
class CredentialsTest : public ::testing::Test {
protected:
void SetUp() override {
// Start the tests as root.
seteuid(AID_ROOT);
ASSERT_NO_FATAL_FAILURE(initClient());
}
void TearDown() override {
mComposerClient->dispose();
mBGSurfaceControl.clear();
mComposerClient.clear();
// Finish the tests as root.
seteuid(AID_ROOT);
}
sp<IBinder> mDisplay;
sp<IBinder> mVirtualDisplay;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mVirtualSurfaceControl;
void initClient() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
}
void setupBackgroundSurface() {
mDisplay = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
DisplayInfo info;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
const ssize_t displayWidth = info.w;
const ssize_t displayHeight = info.h;
// Background surface
mBGSurfaceControl =
mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
Transaction t;
t.setDisplayLayerStack(mDisplay, 0);
ASSERT_EQ(NO_ERROR,
t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
}
void setupVirtualDisplay() {
mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
const ssize_t displayWidth = 100;
const ssize_t displayHeight = 100;
// Background surface
mVirtualSurfaceControl =
mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
ASSERT_TRUE(mVirtualSurfaceControl->isValid());
Transaction t;
t.setDisplayLayerStack(mVirtualDisplay, 0);
ASSERT_EQ(NO_ERROR,
t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
.show(mVirtualSurfaceControl)
.apply());
}
/**
* Sets UID to imitate Graphic's process.
*/
void setGraphicsUID() {
seteuid(AID_ROOT);
seteuid(AID_GRAPHICS);
}
/**
* Sets UID to imitate System's process.
*/
void setSystemUID() {
seteuid(AID_ROOT);
seteuid(AID_SYSTEM);
}
/**
* Sets UID to imitate a process that doesn't have any special privileges in
* our code.
*/
void setBinUID() {
seteuid(AID_ROOT);
seteuid(AID_BIN);
}
/**
* Template function the check a condition for different types of users: root
* graphics, system, and non-supported user. Root, graphics, and system should
* always equal privilegedValue, and non-supported user should equal unprivilegedValue.
*/
template <typename T>
void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) {
// Check with root.
seteuid(AID_ROOT);
ASSERT_EQ(privilegedValue, condition());
// Check as a Graphics user.
setGraphicsUID();
ASSERT_EQ(privilegedValue, condition());
// Check as a system user.
setSystemUID();
ASSERT_EQ(privilegedValue, condition());
// Check as a non-supported user.
setBinUID();
ASSERT_EQ(unprivilegedValue, condition());
}
};
TEST_F(CredentialsTest, ClientInitTest) {
// Root can init can init the client.
ASSERT_NO_FATAL_FAILURE(initClient());
// Graphics can init the client.
setGraphicsUID();
ASSERT_NO_FATAL_FAILURE(initClient());
// System can init the client.
setSystemUID();
ASSERT_NO_FATAL_FAILURE(initClient());
// Anyone else can init the client.
setBinUID();
mComposerClient = new SurfaceComposerClient;
ASSERT_NO_FATAL_FAILURE(initClient());
}
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
std::function<bool()> condition = [] {
return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
};
// Anyone can access display information.
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
}
TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
setBinUID();
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_TRUE(display != nullptr);
DisplayInfo info;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
Vector<DisplayInfo> configs;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
SurfaceComposerClient::getActiveColorMode(display));
}
TEST_F(CredentialsTest, GetDisplayColorModesTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
Vector<ui::ColorMode> outColorModes;
return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
ui::DisplayPrimaries primaries;
return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
TEST_F(CredentialsTest, SetActiveConfigTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setActiveConfig(display, 0);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
TEST_F(CredentialsTest, SetActiveColorModeTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
TEST_F(CredentialsTest, CreateDisplayTest) {
std::function<bool()> condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
return testDisplay.get() != nullptr;
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
return testDisplay.get() != nullptr;
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
setupVirtualDisplay();
DisplayInfo info;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
// This test currently fails. TODO(b/112002626): Find a way to properly create
// a display in the test environment, so that destroy display can remove it.
ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
}
TEST_F(CredentialsTest, CaptureTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
0 /*reqHeight*/, false, ROTATION, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
Rect(), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
/**
* The following tests are for methods accessible directly through SurfaceFlinger.
*/
/**
* An app can pass a buffer queue to the media server and ask the media server to decode a DRM video
* to that buffer queue. The media server is the buffer producer in this case. Because the app may create
* its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid
* sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check
* the consumer of a buffer queue is SurfaceFlinger.
*/
TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) {
setupBackgroundSurface();
sp<IGraphicBufferProducer> producer =
mBGSurfaceControl->getSurface()->getIGraphicBufferProducer();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); };
// Anyone should be able to check if the consumer of the buffer queue is SF.
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
}
TEST_F(CredentialsTest, GetLayerDebugInfo) {
setupBackgroundSurface();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
// Historically, only root and shell can access the getLayerDebugInfo which
// is called when we call dumpsys. I don't see a reason why we should change this.
std::vector<LayerDebugInfo> outLayers;
// Check with root.
seteuid(AID_ROOT);
ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
// Check as a shell.
seteuid(AID_SHELL);
ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
// Check as anyone else.
seteuid(AID_ROOT);
seteuid(AID_BIN);
ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
}
TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
bool result = false;
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
Vector<ColorMode> colorModes;
SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
case ColorMode::DISPLAY_P3:
case ColorMode::ADOBE_RGB:
case ColorMode::DCI_P3:
hasWideColorMode = true;
break;
default:
break;
}
}
ASSERT_EQ(hasWideColorMode, result);
}
TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
std::function<status_t()> condition = [=]() {
bool result = false;
return SurfaceComposerClient::isWideColorDisplay(display, &result);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display);
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
} // namespace android