Add a C-api for retrieving display info.
As HWUI will need to depend on ABI-stable interfaces moving forward, we
need such an interface against SurfaceComposer for retrieving display
metadata.
This CL won't contain a map file to stabilize the ABI yet, as I expect
that there will be some flux due to refresh rate apis.
Bug: 136263392
Test: builds
Change-Id: I7227e2e72789d36a21534840eac88817135251c3
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
new file mode 100644
index 0000000..6665635
--- /dev/null
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2019 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 <apex/display.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
+
+#include <algorithm>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+ /**
+ * The width in pixels of the display configuration.
+ */
+ int32_t width{0};
+
+ /**
+ * The height in pixels of the display configuration.
+ */
+
+ int32_t height{0};
+
+ /**
+ * The display density.
+ */
+ float density{0};
+
+ /**
+ * The refresh rate of the display configuration, in frames per second.
+ */
+ float fps{0.0};
+
+ /**
+ * The vsync offset at which surfaceflinger runs, in nanoseconds.
+ */
+ int64_t sfOffset{0};
+
+ /**
+ * The vsync offset at which applications run, in nanoseconds.
+ */
+ int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+ /**
+ * A physical display ID, unique to this display.
+ */
+ PhysicalDisplayId id;
+
+ /**
+ * The type of the display, i.e. whether it is an internal or external
+ * display.
+ */
+ ADisplayType type;
+
+ /**
+ * Number of supported configs
+ */
+ size_t numConfigs;
+
+ /**
+ * Set of supported configs by this display.
+ */
+ DisplayConfigImpl* configs;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+#define CHECK_NOT_NULL(name) \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+namespace {
+sp<IBinder> getToken(ADisplay* display) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
+}
+
+int64_t computeSfOffset(const DisplayInfo& info) {
+ // This should probably be part of the config instead of extrapolated from
+ // the presentation deadline and fudged here, but the way the math works out
+ // here we do get the right offset.
+ return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
+}
+} // namespace
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+ const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ const size_t size = ids.size();
+ if (size == 0) {
+ return NO_INIT;
+ }
+
+ std::vector<DisplayConfigImpl> configsPerDisplay[size];
+ int numConfigs = 0;
+ for (int i = 0; i < size; ++i) {
+ const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
+ Vector<DisplayInfo> configs;
+ const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ if (status != OK) {
+ return status;
+ }
+ if (configs.empty()) {
+ return NO_INIT;
+ }
+
+ numConfigs += configs.size();
+ configsPerDisplay[i].reserve(configs.size());
+ for (int j = 0; j < configs.size(); ++j) {
+ const DisplayInfo config = configs[j];
+ configsPerDisplay[i].emplace_back(
+ DisplayConfigImpl{static_cast<int32_t>(config.w),
+ static_cast<int32_t>(config.h), config.density, config.fps,
+ computeSfOffset(config), config.appVsyncOffset});
+ }
+ }
+
+ const std::optional<PhysicalDisplayId> internalId =
+ SurfaceComposerClient::getInternalDisplayId();
+
+ // Here we allocate all our required memory in one block. The layout is as
+ // follows:
+ // ------------------------------------------------------------
+ // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
+ // ------------------------------------------------------------
+ //
+ // The caller will be given a DisplayImpl** which points to the beginning of
+ // the block of DisplayImpl pointers.
+ // Each DisplayImpl* points to a DisplayImpl in the second block.
+ // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
+ // contiguous block of DisplayConfigImpls specific to that display.
+ DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+ malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
+ sizeof(DisplayConfigImpl) * numConfigs));
+ DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
+ DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
+
+ for (size_t i = 0; i < size; ++i) {
+ const PhysicalDisplayId id = ids[i];
+ const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
+ : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+ const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+ memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
+
+ displayData[i] = DisplayImpl{id, type, configs.size(), configData};
+ impls[i] = displayData + i;
+ // Advance the configData pointer so that future configs are written to
+ // the correct display.
+ configData += configs.size();
+ }
+
+ *outDisplays = reinterpret_cast<ADisplay**>(impls);
+ return size;
+}
+
+void ADisplay_release(ADisplay** displays) {
+ if (displays == nullptr) {
+ return;
+ }
+ free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ float maxFps = 0.0;
+ for (int i = 0; i < impl->numConfigs; ++i) {
+ maxFps = std::max(maxFps, impl->configs[i].fps);
+ }
+ return maxFps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+
+ return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+ CHECK_NOT_NULL(display);
+
+ sp<IBinder> token = getToken(display);
+ const int index = SurfaceComposerClient::getActiveConfig(token);
+ if (index < 0) {
+ return index;
+ }
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
+ return OK;
+}
+
+float ADisplayConfig_getDensity(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->density;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
new file mode 100644
index 0000000..66ebdfd
--- /dev/null
+++ b/libs/nativedisplay/Android.bp
@@ -0,0 +1,50 @@
+// Copyright 2019 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.
+
+cc_library_headers {
+ name: "libnativedisplay_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libnativedisplay",
+ export_include_dirs: [
+ "include",
+ ],
+
+ clang: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ srcs: [
+ "ADisplay.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libnativedisplay_headers",
+ ],
+
+}
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
new file mode 100644
index 0000000..7af452a
--- /dev/null
+++ b/libs/nativedisplay/include/apex/display.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native display
+ */
+typedef struct ADisplay ADisplay;
+
+/**
+ * Enum describing the possible types of a display
+ */
+enum ADisplayType {
+ /**
+ * A display that is the internal, or "primary" display for a device.
+ */
+ DISPLAY_TYPE_INTERNAL = 0,
+
+ /**
+ * A display that is externally connected for a device.
+ */
+ DISPLAY_TYPE_EXTERNAL = 1,
+};
+
+/**
+ * Opaque handle for display metadata
+ */
+typedef struct ADisplayConfig ADisplayConfig;
+
+/**
+ * Acquires a list of display handles. Memory is allocated for the list and is
+ * owned by the caller. The caller is responsible for freeing this memory by
+ * calling ADisplayList_release.
+ *
+ * Returns the size of the returned list on success.
+ * Returns -errno on error.
+ */
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);
+
+/**
+ * Releases a list of display handles created by
+ * ADisplayList_acquirePhysicalDisplays.
+ */
+void ADisplay_release(ADisplay** displays);
+
+/**
+ * Queries the maximum supported fps for the given display.
+ */
+float ADisplay_getMaxSupportedFps(ADisplay* display);
+
+/**
+ * Queries the display's type.
+ */
+ADisplayType ADisplay_getDisplayType(ADisplay* display);
+
+/**
+ * Gets the current display configuration for the given display.
+ *
+ * Memory is *not* allocated for the caller. As such, the returned output
+ * configuration's lifetime will not be longer than the ADisplay* passed to this
+ * function - if ADisplay_release is called destroying the ADisplay object then
+ * it is invalid to access the ADisplayConfig returned here.
+ *
+ * Note that the current display configuration can change. Listening to updates
+ * to the current display configuration should be done via Choreographer. If
+ * such an update is observed, then this method should be recalled to get the
+ * new current configuration.
+ *
+ * Returns OK on success, -errno on failure.
+ */
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
+
+/**
+ * Queries the density for a given display configuration.
+ */
+float ADisplayConfig_getDensity(ADisplayConfig* config);
+
+/**
+ * Queries the width in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
+
+/**
+ * Queries the height in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config);
+
+/**
+ * Queries the display refresh rate for a given display configuration.
+ */
+float ADisplayConfig_getFps(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which the system compositor is scheduled to
+ * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
+ * this returns S in nanoseconds.
+ */
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which applications are scheduled to run. If a
+ * vsync occurs at time T, and applications run at time T + S, then this returns
+ * S in nanoseconds.
+ */
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
+
+__END_DECLS