Merge master@5406228 into git_qt-dev-plus-aosp.
am: b499f51e28
Change-Id: I29cd8210f7b384874050ad149c5f7907e72ef0f4
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1fbd2b3..2cc1b32 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <chrono>
+#include <fstream>
#include <functional>
#include <future>
#include <memory>
@@ -194,6 +195,15 @@
return true;
}
+static bool IsFileEmpty(const std::string& file_path) {
+ std::ifstream file(file_path, std::ios::binary | std::ios::ate);
+ if(file.bad()) {
+ MYLOGE("Cannot open file: %s\n", file_path.c_str());
+ return true;
+ }
+ return file.tellg() <= 0;
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -2149,7 +2159,7 @@
"--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
- if (ds.options_->do_fb) {
+ if (ds.options_->do_fb && !android::os::IsFileEmpty(ds.screenshot_path_)) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
am_args.push_back(ds.screenshot_path_);
@@ -2225,13 +2235,13 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
options->telephony_only = true;
- options->do_fb = true;
+ options->do_fb = false;
options->do_broadcast = true;
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
options->wifi_only = true;
options->do_zip_file = true;
- options->do_fb = true;
+ options->do_fb = false;
options->do_broadcast = true;
break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
@@ -2735,8 +2745,8 @@
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
- consent_callback_.get());
+ calling_uid, calling_package, String16(), String16(),
+ 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index eb73d41..e6a7735 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -373,7 +373,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_FALSE(options_.do_fb);
EXPECT_TRUE(options_.do_broadcast);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
@@ -404,7 +404,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_FALSE(options_.do_fb);
EXPECT_TRUE(options_.do_broadcast);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.wifi_only);
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 92fac66..c730ab9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -171,7 +171,7 @@
}
binder::Status checkArgumentPackageName(const std::string& packageName) {
- if (is_valid_package_name(packageName.c_str())) {
+ if (is_valid_package_name(packageName)) {
return ok();
} else {
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 20142aa..a5cc0df 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1564,7 +1564,7 @@
if (vdex_fd >= 0) {
AddArg(vdex_fd_arg);
}
- AddArg(zip_fd_arg.c_str());
+ AddArg(zip_fd_arg);
if (profile_was_updated) {
AddArg(assume_profile_changed);
}
@@ -1572,9 +1572,9 @@
AddArg(downgrade_flag);
}
if (class_loader_context != nullptr) {
- AddArg(class_loader_context_arg.c_str());
+ AddArg(class_loader_context_arg);
if (!class_loader_context_fds.empty()) {
- AddArg(class_loader_context_fds_arg.c_str());
+ AddArg(class_loader_context_fds_arg);
}
}
@@ -2259,7 +2259,7 @@
drop_capabilities(uid);
const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
_exit(kReconcileSecondaryDexValidationError);
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
index f1ac717..60d6492 100644
--- a/cmds/installd/view_compiler.cpp
+++ b/cmds/installd/view_compiler.cpp
@@ -45,7 +45,7 @@
// and pass file descriptors.
// Open input file
- unique_fd infd{open(apk_path, 0)};
+ unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open)
if (infd.get() < 0) {
PLOG(ERROR) << "Could not open input file: " << apk_path;
return false;
@@ -53,7 +53,7 @@
// Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so
// we close stdout and open it towards the right output.
- unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)};
+ unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)};
if (outfd.get() < 0) {
PLOG(ERROR) << "Could not open output file: " << out_dex_file;
return false;
@@ -62,10 +62,6 @@
PLOG(ERROR) << "Could not change output file permissions";
return false;
}
- if (close(STDOUT_FILENO) != 0) {
- PLOG(ERROR) << "Could not close stdout";
- return false;
- }
if (dup2(outfd, STDOUT_FILENO) < 0) {
PLOG(ERROR) << "Could not duplicate output file descriptor";
return false;
@@ -96,4 +92,4 @@
}
} // namespace installd
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/installd/view_compiler.h b/cmds/installd/view_compiler.h
index f7c6e57..aa141ca 100644
--- a/cmds/installd/view_compiler.h
+++ b/cmds/installd/view_compiler.h
@@ -26,4 +26,4 @@
} // namespace installd
} // namespace android
-#endif // VIEW_COMPILER_H_
\ No newline at end of file
+#endif // VIEW_COMPILER_H_
diff --git a/data/etc/android.hardware.nfc.ese.xml b/data/etc/android.hardware.nfc.ese.xml
new file mode 100644
index 0000000..6642bb2
--- /dev/null
+++ b/data/etc/android.hardware.nfc.ese.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<!-- This feature indicates that the device supports eSE-based NFC card
+ emulation -->
+<permissions>
+ <feature name="android.hardware.nfc.ese" />
+</permissions>
diff --git a/data/etc/android.hardware.nfc.uicc.xml b/data/etc/android.hardware.nfc.uicc.xml
new file mode 100644
index 0000000..4f12de4
--- /dev/null
+++ b/data/etc/android.hardware.nfc.uicc.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<!-- This feature indicates that the device supports uicc-based NFC card
+ emulation -->
+<permissions>
+ <feature name="android.hardware.nfc.uicc" />
+</permissions>
diff --git a/include/android/font.h b/include/android/font.h
new file mode 100644
index 0000000..7e5a945
--- /dev/null
+++ b/include/android/font.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 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.
+ */
+
+/**
+ * @file font.h
+ * @brief Provides some constants used in system_fonts.h or fonts_matcher.h
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_FONT_H
+#define ANDROID_FONT_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+enum {
+ /** The minimum value fot the font weight value. */
+ AFONT_WEIGHT_MIN = 0,
+
+ /** A font weight value for the thin weight. */
+ AFONT_WEIGHT_THIN = 100,
+
+ /** A font weight value for the extra-light weight. */
+ AFONT_WEIGHT_EXTRA_LIGHT = 200,
+
+ /** A font weight value for the light weight. */
+ AFONT_WEIGHT_LIGHT = 300,
+
+ /** A font weight value for the normal weight. */
+ AFONT_WEIGHT_NORMAL = 400,
+
+ /** A font weight value for the medium weight. */
+ AFONT_WEIGHT_MEDIUM = 500,
+
+ /** A font weight value for the semi-bold weight. */
+ AFONT_WEIGHT_SEMI_BOLD = 600,
+
+ /** A font weight value for the bold weight. */
+ AFONT_WEIGHT_BOLD = 700,
+
+ /** A font weight value for the extra-bold weight. */
+ AFONT_WEIGHT_EXTRA_BOLD = 800,
+
+ /** A font weight value for the black weight. */
+ AFONT_WEIGHT_BLACK = 900,
+
+ /** The maximum value for the font weight value. */
+ AFONT_WEIGHT_MAX = 1000
+};
+
+/**
+ * AFont provides information of the single font configuration.
+ */
+struct AFont;
+
+/**
+ * Close an AFont.
+ *
+ * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match.
+ * Do nothing if NULL is passed.
+ */
+void AFont_close(AFont* _Nullable font) __INTRODUCED_IN(29);
+
+/**
+ * Return an absolute path to the current font file.
+ *
+ * Here is a list of font formats returned by this method:
+ * <ul>
+ * <li>OpenType</li>
+ * <li>OpenType Font Collection</li>
+ * <li>TrueType</li>
+ * <li>TrueType Collection</li>
+ * </ul>
+ * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc.
+ *
+ * The font file returned is guaranteed to be opend with O_RDONLY.
+ * Note that the returned pointer is valid until AFont_close() is called for the given font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a string of the font file path.
+ */
+const char* _Nonnull AFont_getFontFilePath(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a weight value associated with the current font.
+ *
+ * The weight values are positive and less than or equal to 1000.
+ * Here are pairs of the common names and their values.
+ * <p>
+ * <table>
+ * <tr>
+ * <th align="center">Value</th>
+ * <th align="center">Name</th>
+ * <th align="center">NDK Definition</th>
+ * </tr>
+ * <tr>
+ * <td align="center">100</td>
+ * <td align="center">Thin</td>
+ * <td align="center">{@link AFONT_WEIGHT_THIN}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">200</td>
+ * <td align="center">Extra Light (Ultra Light)</td>
+ * <td align="center">{@link AFONT_WEIGHT_EXTRA_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">300</td>
+ * <td align="center">Light</td>
+ * <td align="center">{@link AFONT_WEIGHT_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">400</td>
+ * <td align="center">Normal (Regular)</td>
+ * <td align="center">{@link AFONT_WEIGHT_NORMAL}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">500</td>
+ * <td align="center">Medium</td>
+ * <td align="center">{@link AFONT_WEIGHT_MEDIUM}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">600</td>
+ * <td align="center">Semi Bold (Demi Bold)</td>
+ * <td align="center">{@link AFONT_WEIGHT_SEMI_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">700</td>
+ * <td align="center">Bold</td>
+ * <td align="center">{@link AFONT_WEIGHT_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">800</td>
+ * <td align="center">Extra Bold (Ultra Bold)</td>
+ * <td align="center">{@link AFONT_WEIGHT_EXTRA_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">900</td>
+ * <td align="center">Black (Heavy)</td>
+ * <td align="center">{@link AFONT_WEIGHT_BLACK}</td>
+ * </tr>
+ * </table>
+ * </p>
+ * Note that the weight value may fall in between above values, e.g. 250 weight.
+ *
+ * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ */
+uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return true if the current font is italic, otherwise returns false.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return true if italic, otherwise false.
+ */
+bool AFont_isItalic(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a IETF BCP47 compliant language tag associated with the current font.
+ *
+ * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)")
+ *
+ * Note that the returned pointer is valid until AFont_close() is called.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a IETF BCP47 compliant language tag or nullptr if not available.
+ */
+const char* _Nullable AFont_getLocale(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a font collection index value associated with the current font.
+ *
+ * In case the target font file is a font collection (e.g. .ttc or .otc), this
+ * returns a non-negative value as an font offset in the collection. This
+ * always returns 0 if the target font file is a regular font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a font collection index.
+ */
+size_t AFont_getCollectionIndex(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a count of font variation settings associated with the current font
+ *
+ * The font variation settings are provided as multiple tag-values pairs.
+ *
+ * For example, bold italic font may have following font variation settings:
+ * 'wght' 700, 'slnt' -12
+ * In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag
+ * and AFont_getAxisValue will return following values.
+ * \code{.cpp}
+ * AFont* font = AFontIterator_next(ite);
+ *
+ * // Returns the number of axes
+ * AFont_getAxisCount(font); // Returns 2
+ *
+ * // Returns the tag-value pair for the first axis.
+ * AFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874)
+ * AFont_getAxisValue(font, 0); // Returns 700.0
+ *
+ * // Returns the tag-value pair for the second axis.
+ * AFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74)
+ * AFont_getAxisValue(font, 1); // Returns -12.0
+ * \endcode
+ *
+ * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a number of font variation settings.
+ */
+size_t AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+
+/**
+ * Return an OpenType axis tag associated with the current font.
+ *
+ * See AFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ * equal to {@link AFont_getAxisCount} is not allowed.
+ * \return an OpenType axis tag value for the given font variation setting.
+ */
+uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+/**
+ * Return an OpenType axis value associated with the current font.
+ *
+ * See AFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ * equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ * \return a float value for the given font variation setting.
+ */
+float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_FONT_H
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
new file mode 100644
index 0000000..ad5a4da
--- /dev/null
+++ b/include/android/font_matcher.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 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.
+ */
+
+/**
+ * @file font_matcher.h
+ * @brief Provides the font matching logic with various inputs.
+ *
+ * You can use this class for deciding what font is to be used for drawing text.
+ *
+ * A matcher is created from text style, locales and UI compatibility. The match function for
+ * matcher object can be called multiple times until close function is called.
+ *
+ * Even if no font can render the given text, the match function will return a non-null result for
+ * drawing Tofu character.
+ *
+ * Examples:
+ * \code{.cpp}
+ * // Simple font query for the ASCII character.
+ * std::vector<uint16_t> text = { 'A' };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a valid font file.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Querying font for CJK characters
+ * std::vector<uint16_t> text = { 0x9AA8 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * AFontMatcher_setLocales(matcher, "zh-CN,ja-JP");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Simplified Chinese font.
+ * AFontMatcher_setLocales(matcher, "ja-JP,zh-CN");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Japanese font.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Querying font for text/color emoji
+ * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 8 and the font will points a color emoji font.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Mixture of multiple script of characters.
+ * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
+ * std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Hebrew font.
+ * AFontMatcher_destroy(matcher);
+ * \endcode
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_FONT_MATCHER_H
+#define ANDROID_FONT_MATCHER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+#include <android/font.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+enum {
+ /** A family variant value for the system default variant. */
+ AFAMILY_VARIANT_DEFAULT = 0,
+
+ /**
+ * A family variant value for the compact font family variant.
+ *
+ * The compact font family has Latin-based vertical metrics.
+ */
+ AFAMILY_VARIANT_COMPACT = 1,
+
+ /**
+ * A family variant value for the elegant font family variant.
+ *
+ * The elegant font family may have larger vertical metrics than Latin font.
+ */
+ AFAMILY_VARIANT_ELEGANT = 2,
+};
+
+/**
+ * AFontMatcher performs match operation on given parameters and available font files.
+ * This matcher is not a thread-safe object. Do not pass this matcher to other threads.
+ */
+struct AFontMatcher;
+
+/**
+ * Select the best font from given parameters.
+ *
+ */
+
+/**
+ * Creates a new AFontMatcher object
+ */
+AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29);
+
+/**
+ * Destroy the matcher object.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ */
+void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29);
+
+/**
+ * Set font style to matcher.
+ *
+ * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
+ * with non-italic style.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param weight a font weight value. Only from 0 to 1000 value is valid
+ * \param italic true if italic, otherwise false.
+ */
+void AFontMatcher_setStyle(
+ AFontMatcher* _Nonnull matcher,
+ uint16_t weight,
+ bool italic) __INTRODUCED_IN(29);
+
+/**
+ * Set font locales to matcher.
+ *
+ * If this function is not called, the matcher performs with empty locale list.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
+ * tags.
+ */
+void AFontMatcher_setLocales(
+ AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull languageTags) __INTRODUCED_IN(29);
+
+/**
+ * Set family variant to matcher.
+ *
+ * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT},
+ * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid.
+ */
+void AFontMatcher_setFamilyVariant(
+ AFontMatcher* _Nonnull matcher,
+ uint32_t familyVariant) __INTRODUCED_IN(29);
+
+/**
+ * Performs the matching from the generic font family for the text and select one font.
+ *
+ * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families)
+ *
+ * Even if no font can render the given text, this function will return a non-null result for
+ * drawing Tofu character.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param familyName a null character terminated font family name
+ * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
+ * \param textLength a length of the given text buffer. This must not be zero.
+ * \param runLengthOut if not null, the font run length will be filled.
+ * \return a font to be used for given text and params. You need to release the returned font by
+ * ASystemFont_close when it is no longer needed.
+ */
+AFont* _Nonnull AFontMatcher_match(
+ const AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull familyName,
+ const uint16_t* _Nonnull text,
+ const uint32_t textLength,
+ uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_FONT_MATCHER_H
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
index 38f036e..3facf82 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -62,6 +62,8 @@
#include <stddef.h>
#include <sys/cdefs.h>
+#include <android/font.h>
+
/******************************************************************
*
* IMPORTANT NOTICE:
@@ -82,41 +84,6 @@
#if __ANDROID_API__ >= 29
-enum {
- /** The minimum value fot the font weight value. */
- ASYSTEM_FONT_WEIGHT_MIN = 0,
-
- /** A font weight value for the thin weight. */
- ASYSTEM_FONT_WEIGHT_THIN = 100,
-
- /** A font weight value for the extra-light weight. */
- ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT = 200,
-
- /** A font weight value for the light weight. */
- ASYSTEM_FONT_WEIGHT_LIGHT = 300,
-
- /** A font weight value for the normal weight. */
- ASYSTEM_FONT_WEIGHT_NORMAL = 400,
-
- /** A font weight value for the medium weight. */
- ASYSTEM_FONT_WEIGHT_MEDIUM = 500,
-
- /** A font weight value for the semi-bold weight. */
- ASYSTEM_FONT_WEIGHT_SEMI_BOLD = 600,
-
- /** A font weight value for the bold weight. */
- ASYSTEM_FONT_WEIGHT_BOLD = 700,
-
- /** A font weight value for the extra-bold weight. */
- ASYSTEM_FONT_WEIGHT_EXTRA_BOLD = 800,
-
- /** A font weight value for the black weight. */
- ASYSTEM_FONT_WEIGHT_BLACK = 900,
-
- /** The maximum value for the font weight value. */
- ASYSTEM_FONT_WEIGHT_MAX = 1000
-};
-
/**
* ASystemFontIterator provides access to the system font configuration.
*
@@ -126,11 +93,6 @@
struct ASystemFontIterator;
/**
- * ASystemFont provides information of the single system font configuration.
- */
-struct ASystemFont;
-
-/**
* Create a system font iterator.
*
* Use ASystemFont_close() to close the iterator.
@@ -153,254 +115,7 @@
* \return a font. If no more font is available, returns nullptr. You need to release the returned
* font by ASystemFont_close when it is no longer needed.
*/
-ASystemFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29);
-
-/**
- * Close an ASystemFont returned by ASystemFontIterator_next.
- *
- * \param font a font returned by ASystemFontIterator_next or ASystemFont_matchFamilyStyleCharacter.
- * Do nothing if NULL is passed.
- */
-void ASystemFont_close(ASystemFont* _Nullable font) __INTRODUCED_IN(29);
-
-
-/**
- * Select the best font from given parameters.
- *
- * Only generic font families are supported.
- * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families)
- *
- * Even if no font can render the given text, this function will return a non-null result for
- * drawing Tofu character.
- *
- * Examples:
- * \code{.cpp}
- * // Simple font query for the ASCII character.
- * std::vector<uint16_t> text = { 'A' };
- * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
- * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
- * // runLength will be 1 and the font will points a valid font file.
- *
- * // Querying font for CJK characters
- * std::vector<uint16_t> text = { 0x9AA8 };
- * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
- * "sans", 400, false, "zh-CN,ja-JP", text.data(), text.length(), &runLength);
- * // runLength will be 1 and the font will points a Simplified Chinese font.
- * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
- * "sans", 400, false, "ja-JP,zh-CN", text.data(), text.length(), &runLength);
- * // runLength will be 1 and the font will points a Japanese font.
- *
- * // Querying font for text/color emoji
- * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
- * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
- * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
- * // runLength will be 8 and the font will points a color emoji font.
- *
- * // Mixture of multiple script of characters.
- * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
- * std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
- * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
- * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
- * // runLength will be 1 and the font will points a Hebrew font.
- * \endcode
- *
- * \param familyName a null character terminated font family name
- * \param weight a font weight value. Only from 0 to 1000 value is valid
- * \param italic true if italic, otherwise false.
- * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
- * tags.
- * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
- * \param textLength a length of the given text buffer. This must not be zero.
- * \param runLengthOut if not null, the font run length will be filled.
- * \return a font to be used for given text and params. You need to release the returned font by
- * ASystemFont_close when it is no longer needed.
- */
-ASystemFont* _Nonnull ASystemFont_matchFamilyStyleCharacter(
- const char* _Nonnull familyName,
- uint16_t weight,
- bool italic,
- const char* _Nonnull languageTags,
- const uint16_t* _Nonnull text,
- uint32_t textLength,
- uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29);
-
-/**
- * Return an absolute path to the current font file.
- *
- * Here is a list of font formats returned by this method:
- * <ul>
- * <li>OpenType</li>
- * <li>OpenType Font Collection</li>
- * <li>TrueType</li>
- * <li>TrueType Collection</li>
- * </ul>
- * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc.
- *
- * The font file returned is guaranteed to be opend with O_RDONLY.
- * Note that the returned pointer is valid until ASystemFont_close() is called for the given font.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return a string of the font file path.
- */
-const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-/**
- * Return a weight value associated with the current font.
- *
- * The weight values are positive and less than or equal to 1000.
- * Here are pairs of the common names and their values.
- * <p>
- * <table>
- * <tr>
- * <th align="center">Value</th>
- * <th align="center">Name</th>
- * <th align="center">NDK Definition</th>
- * </tr>
- * <tr>
- * <td align="center">100</td>
- * <td align="center">Thin</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_THIN}</td>
- * </tr>
- * <tr>
- * <td align="center">200</td>
- * <td align="center">Extra Light (Ultra Light)</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT}</td>
- * </tr>
- * <tr>
- * <td align="center">300</td>
- * <td align="center">Light</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_LIGHT}</td>
- * </tr>
- * <tr>
- * <td align="center">400</td>
- * <td align="center">Normal (Regular)</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_NORMAL}</td>
- * </tr>
- * <tr>
- * <td align="center">500</td>
- * <td align="center">Medium</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_MEDIUM}</td>
- * </tr>
- * <tr>
- * <td align="center">600</td>
- * <td align="center">Semi Bold (Demi Bold)</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_SEMI_BOLD}</td>
- * </tr>
- * <tr>
- * <td align="center">700</td>
- * <td align="center">Bold</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BOLD}</td>
- * </tr>
- * <tr>
- * <td align="center">800</td>
- * <td align="center">Extra Bold (Ultra Bold)</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_BOLD}</td>
- * </tr>
- * <tr>
- * <td align="center">900</td>
- * <td align="center">Black (Heavy)</td>
- * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td>
- * </tr>
- * </table>
- * </p>
- * Note that the weight value may fall in between above values, e.g. 250 weight.
- *
- * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
- */
-uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-/**
- * Return true if the current font is italic, otherwise returns false.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return true if italic, otherwise false.
- */
-bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-/**
- * Return a IETF BCP47 compliant language tag associated with the current font.
- *
- * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)")
- *
- * Note that the returned pointer is valid until ASystemFont_close() is called.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return a IETF BCP47 compliant langauge tag or nullptr if not available.
- */
-const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-/**
- * Return a font collection index value associated with the current font.
- *
- * In case the target font file is a font collection (e.g. .ttc or .otc), this
- * returns a non-negative value as an font offset in the collection. This
- * always returns 0 if the target font file is a regular font.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return a font collection index.
- */
-size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-/**
- * Return a count of font variation settings associated with the current font
- *
- * The font variation settings are provided as multiple tag-values pairs.
- *
- * For example, bold italic font may have following font variation settings:
- * 'wght' 700, 'slnt' -12
- * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag
- * and ASystemFont_getAxisValue will return following values.
- * \code{.cpp}
- * ASystemFont* font = ASystemFontIterator_next(ite);
- *
- * // Returns the number of axes
- * ASystemFont_getAxisCount(font); // Returns 2
- *
- * // Returns the tag-value pair for the first axis.
- * ASystemFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874)
- * ASystemFont_getAxisValue(font, 0); // Returns 700.0
- *
- * // Returns the tag-value pair for the second axis.
- * ASystemFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74)
- * ASystemFont_getAxisValue(font, 1); // Returns -12.0
- * \endcode
- *
- * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
- *
- * \param font a font object. Passing NULL is not allowed.
- * \return a number of font variation settings.
- */
-size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
-
-
-/**
- * Return an OpenType axis tag associated with the current font.
- *
- * See ASystemFont_getAxisCount for more details.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \param axisIndex an index to the font variation settings. Passing value larger than or
- * equal to {@link ASystemFont_getAxisCount} is not allowed.
- * \return an OpenType axis tag value for the given font variation setting.
- */
-uint32_t ASystemFont_getAxisTag(const ASystemFont* _Nonnull font, uint32_t axisIndex)
- __INTRODUCED_IN(29);
-
-/**
- * Return an OpenType axis value associated with the current font.
- *
- * See ASystemFont_getAxisCount for more details.
- *
- * \param font a font object. Passing NULL is not allowed.
- * \param axisIndex an index to the font variation settings. Passing value larger than or
- * equal to {@link ASYstemFont_getAxisCount} is not allwed.
- * \return a float value for the given font variation setting.
- */
-float ASystemFont_getAxisValue(const ASystemFont* _Nonnull font, uint32_t axisIndex)
- __INTRODUCED_IN(29);
+AFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29);
#endif // __ANDROID_API__ >= 29
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 71a8f20..7f04611 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -50,19 +50,19 @@
class InputApplicationHandle : public RefBase {
public:
inline const InputApplicationInfo* getInfo() const {
- return mInfo;
+ return &mInfo;
}
inline std::string getName() const {
- return mInfo ? mInfo->name : "<invalid>";
+ return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
- return mInfo ? mInfo->token : nullptr;
+ return mInfo.token;
}
/**
@@ -75,18 +75,11 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
- InputApplicationInfo* mInfo;
+ InputApplicationInfo mInfo;
};
} // namespace android
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
new file mode 100644
index 0000000..9284acb
--- /dev/null
+++ b/libs/android_runtime_lazy/Android.bp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 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.
+ */
+
+// libandroid_runtime_lazy is a shim library.
+// This provides very limited small set of APIs from libandroid_runtime.
+//
+// By depending on this instead of libandroid_runtime,
+// a library can be loaded without paying the cost of libandroid_runtime
+// which is quite huge. The cost will be paid when libandroid_runtime is actually used.
+//
+// For Partial-source PDK build, there is a constraint that
+// frameworks/native modules should not depend on frameworks/base.
+// This library can be used to cut down the dependency between them.
+// (e.g. libbinder_ndk)
+//
+// Some libraries which serve as LL-NDK and NDK as well may depend on this
+// instead of libandroid_runtime. When they are used by a vendor process,
+// depending on libandroid_runtime is meaningless. In this case,
+// they can depend on libandroid_runtime_lazy.
+cc_library {
+ name: "libandroid_runtime_lazy",
+ vendor_available: true,
+ double_loadable: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ srcs: [
+ "android_runtime_lazy.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ required: [
+ "libandroid_runtime",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "jni_headers",
+ "libbinder_headers",
+ ],
+}
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
new file mode 100644
index 0000000..98d8e8a
--- /dev/null
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 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.
+ */
+#define LOG_TAG "ANDROID_RUNTIME_LAZY"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
+
+#include <dlfcn.h>
+#include <mutex>
+
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+std::once_flag loadFlag;
+
+typedef JNIEnv* (*getJNIEnv_t)();
+typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
+typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
+
+getJNIEnv_t _getJNIEnv;
+ibinderForJavaObject_t _ibinderForJavaObject;
+javaObjectForIBinder_t _javaObjectForIBinder;
+
+void load() {
+ std::call_once(loadFlag, []() {
+ void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
+ if (handle == nullptr) {
+ ALOGE("Could not open libandroid_runtime.");
+ return;
+ }
+
+ _getJNIEnv = reinterpret_cast<getJNIEnv_t>(
+ dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv"));
+ if (_getJNIEnv == nullptr) {
+ ALOGW("Could not find getJNIEnv.");
+ // no return
+ }
+
+ _ibinderForJavaObject = reinterpret_cast<ibinderForJavaObject_t>(
+ dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (_ibinderForJavaObject == nullptr) {
+ ALOGW("Could not find ibinderForJavaObject.");
+ // no return
+ }
+
+ _javaObjectForIBinder = reinterpret_cast<javaObjectForIBinder_t>(
+ dlsym(handle,
+ "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
+ if (_javaObjectForIBinder == nullptr) {
+ ALOGW("Could not find javaObjectForIBinder.");
+ // no return
+ }
+ });
+}
+
+} // namespace
+
+// exports delegate functions
+
+JNIEnv* AndroidRuntime::getJNIEnv() {
+ load();
+ if (_getJNIEnv == nullptr) {
+ return nullptr;
+ }
+ return _getJNIEnv();
+}
+
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
+ load();
+ if (_ibinderForJavaObject == nullptr) {
+ return nullptr;
+ }
+ return _ibinderForJavaObject(env, obj);
+}
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
+ load();
+ if (_javaObjectForIBinder == nullptr) {
+ return nullptr;
+ }
+ return _javaObjectForIBinder(env, val);
+}
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
new file mode 100644
index 0000000..85231fa
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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 "jni.h"
+
+namespace android {
+
+// Intentionally use the same name with AndroidRuntime in frameworks/base/core/jni/
+// to make the client use this in the same way with the original class.
+class AndroidRuntime {
+public:
+ static JNIEnv* getJNIEnv();
+};
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_util_Binder.h b/libs/android_runtime_lazy/include/android_util_Binder.h
new file mode 100644
index 0000000..e47390e
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_util_Binder.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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 <binder/IBinder.h>
+#include "jni.h"
+
+namespace android {
+
+// The name of this file is same with the file in frameworks/base/core/jni/
+// This is intentional to make the client use these exported functions
+// in the same way with the original.
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val);
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index a494e22..525685c 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -93,6 +93,14 @@
: APP_OPS_MANAGER_UNAVAILABLE_MODE;
}
+int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
+ const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != nullptr
+ ? service->checkAudioOperation(op, usage, uid, callingPackage)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+}
+
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
sp<IAppOpsService> service = getService();
return service != nullptr
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index f38bbb2..a1c2a8b 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -221,7 +221,11 @@
for (word = 0; word < bytesPerLine; ) {
- const size_t startIndex = word+(alignment-(alignment?1:0));
+ size_t align_offset = alignment-(alignment?1:0);
+ if (remain > 0 && (size_t)remain <= align_offset) {
+ align_offset = remain - 1;
+ }
+ const size_t startIndex = word+align_offset;
for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) {
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index fb0d521..66d6e31 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -123,6 +123,22 @@
if (reply.readExceptionCode() != 0) return -1;
return reply.readInt32();
}
+
+ virtual int32_t checkAudioOperation(int32_t code, int32_t usage,
+ int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(usage);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(CHECK_AUDIO_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) {
+ return MODE_ERRORED;
+ }
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
@@ -209,6 +225,17 @@
reply->writeInt32(opCode);
return NO_ERROR;
} break;
+ case CHECK_AUDIO_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ const int32_t code = data.readInt32();
+ const int32_t usage = data.readInt32();
+ const int32_t uid = data.readInt32();
+ const String16 packageName = data.readString16();
+ const int32_t res = checkAudioOperation(code, usage, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index 6b77291..59d51ed 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -47,21 +47,3 @@
// ---------------------------------------------------------------------------
}; // namespace android
-
-extern "C" {
-
-void _ZN7android10IInterface8asBinderEv(void *retval, void* self) {
- ALOGW("deprecated asBinder call, please update your code");
- //ALOGI("self: %p, retval: %p", self, retval);
- android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>;
- *ret = android::IInterface::asBinder((android::IInterface*)self);
-}
-
-void _ZNK7android10IInterface8asBinderEv(void *retval, void *self) {
- ALOGW("deprecated asBinder call, please update your code");
- //ALOGI("self: %p, retval: %p", self, retval);
- android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>;
- *ret = android::IInterface::asBinder((android::IInterface*)self);
-}
-
-} // extern "C"
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a595c01..5427ff8 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -76,14 +76,6 @@
// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
#define STRICT_MODE_PENALTY_GATHER (1 << 31)
-// XXX This can be made public if we want to provide
-// support for typed data.
-struct small_flat_data
-{
- uint32_t type;
- uint32_t data;
-};
-
namespace android {
static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 79db0cb..63f49dd 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -214,15 +214,6 @@
// already be invalid.
ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf)
{
- // TODO: remove these when they are defined by bionic's binder.h
- struct binder_node_debug_info {
- binder_uintptr_t ptr;
- binder_uintptr_t cookie;
- __u32 has_strong_ref;
- __u32 has_weak_ref;
- };
-#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
-
binder_node_debug_info info = {};
uintptr_t* end = buf ? buf + buf_count : nullptr;
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index ccde12a..b1c577e 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -59,4 +59,22 @@
* Unknown packages are mapped to false.
*/
boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames);
+
+ /* ApplicationInfo.isSystemApp() == true */
+ const int LOCATION_SYSTEM = 0x1;
+ /* ApplicationInfo.isVendor() == true */
+ const int LOCATION_VENDOR = 0x2;
+ /* ApplicationInfo.isProduct() == true */
+ const int LOCATION_PRODUCT = 0x4;
+ /* ApplicationInfo.isProductServices() == true */
+ const int LOCATION_PRODUCT_SERVICES = 0x8;
+
+ /**
+ * Returns a set of bitflags about package location.
+ * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp()
+ * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor()
+ * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct()
+ * LOCATION_PRODUCT_SERVICES: getApplicationInfo(packageName).isProductService()
+ */
+ int getLocationFlags(in @utf8InCpp String packageName);
}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 7b086d0..5f324c7 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -47,23 +47,24 @@
PROCESS_STATE_PERSISTENT_UI = 1,
PROCESS_STATE_TOP = 2,
PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_FOREGROUND_SERVICE = 4,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
- PROCESS_STATE_BACKUP = 9,
- PROCESS_STATE_SERVICE = 10,
- PROCESS_STATE_RECEIVER = 11,
- PROCESS_STATE_TOP_SLEEPING = 12,
- PROCESS_STATE_HEAVY_WEIGHT = 13,
- PROCESS_STATE_HOME = 14,
- PROCESS_STATE_LAST_ACTIVITY = 15,
- PROCESS_STATE_CACHED_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
- PROCESS_STATE_CACHED_RECENT = 18,
- PROCESS_STATE_CACHED_EMPTY = 19,
- PROCESS_STATE_NONEXISTENT = 20,
+ PROCESS_STATE_BOUND_TOP = 4,
+ PROCESS_STATE_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
+ PROCESS_STATE_BACKUP = 10,
+ PROCESS_STATE_SERVICE = 11,
+ PROCESS_STATE_RECEIVER = 12,
+ PROCESS_STATE_TOP_SLEEPING = 13,
+ PROCESS_STATE_HEAVY_WEIGHT = 14,
+ PROCESS_STATE_HOME = 15,
+ PROCESS_STATE_LAST_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY = 17,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
+ PROCESS_STATE_CACHED_RECENT = 19,
+ PROCESS_STATE_CACHED_EMPTY = 20,
+ PROCESS_STATE_NONEXISTENT = 21,
};
ActivityManager();
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 37237df..17493b4 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -114,6 +114,8 @@
AppOpsManager();
int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
+ const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 7807851..3dbd0d9 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -43,6 +43,8 @@
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
virtual int32_t permissionToOpCode(const String16& permission) = 0;
+ virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
+ const String16& packageName) = 0;
enum {
CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -53,6 +55,7 @@
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+ CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
};
enum {
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a96c9a0..21bef2e 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,17 +39,12 @@
],
shared_libs: [
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
],
- required: [
- // libbinder_ndk may be used by Java and non-Java things. When lower-level things use it,
- // they shouldn't have to take on the cost of loading libandroid_runtime.
- "libandroid_runtime",
- ],
-
header_libs: [
"jni_headers",
],
diff --git a/libs/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp
index 4a31080..d931785 100644
--- a/libs/binder/ndk/ibinder_jni.cpp
+++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -17,69 +17,19 @@
#include <android/binder_ibinder_jni.h>
#include "ibinder_internal.h"
-#include <android-base/logging.h>
-#include <binder/IBinder.h>
-
-#include <mutex>
-
-#include <dlfcn.h>
+#include <android_util_Binder.h>
using ::android::IBinder;
+using ::android::ibinderForJavaObject;
+using ::android::javaObjectForIBinder;
using ::android::sp;
-struct LazyAndroidRuntime {
- typedef sp<IBinder> (*FromJava)(JNIEnv* env, jobject obj);
- typedef jobject (*ToJava)(JNIEnv* env, const sp<IBinder>& val);
-
- static FromJava ibinderForJavaObject;
- static ToJava javaObjectForIBinder;
-
- static void load() {
- std::call_once(mLoadFlag, []() {
- void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
- if (handle == nullptr) {
- LOG(WARNING) << "Could not open libandroid_runtime.";
- return;
- }
-
- ibinderForJavaObject = reinterpret_cast<FromJava>(
- dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
- if (ibinderForJavaObject == nullptr) {
- LOG(WARNING) << "Could not find ibinderForJavaObject.";
- // no return
- }
-
- javaObjectForIBinder = reinterpret_cast<ToJava>(dlsym(
- handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
- if (javaObjectForIBinder == nullptr) {
- LOG(WARNING) << "Could not find javaObjectForIBinder.";
- // no return
- }
- });
- }
-
- private:
- static std::once_flag mLoadFlag;
-
- LazyAndroidRuntime(){};
-};
-
-LazyAndroidRuntime::FromJava LazyAndroidRuntime::ibinderForJavaObject = nullptr;
-LazyAndroidRuntime::ToJava LazyAndroidRuntime::javaObjectForIBinder = nullptr;
-std::once_flag LazyAndroidRuntime::mLoadFlag;
-
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
if (binder == nullptr) {
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::ibinderForJavaObject == nullptr) {
- return nullptr;
- }
-
- sp<IBinder> ibinder = (LazyAndroidRuntime::ibinderForJavaObject)(env, binder);
-
+ sp<IBinder> ibinder = ibinderForJavaObject(env, binder);
sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder);
AIBinder_incStrong(cbinder.get());
@@ -91,10 +41,5 @@
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::javaObjectForIBinder == nullptr) {
- return nullptr;
- }
-
- return (LazyAndroidRuntime::javaObjectForIBinder)(env, binder->getBinder());
+ return javaObjectForIBinder(env, binder->getBinder());
}
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index b29b6e7..8cd4e03 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -41,7 +41,7 @@
name: "test_libbinder_ndk_test_defaults",
defaults: ["test_libbinder_ndk_defaults"],
shared_libs: [
- "libandroid_runtime",
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 0571dcc..56521bf 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -27,6 +27,7 @@
"libbase",
"libbinder",
"libcutils",
+ "libdl_android",
"liblog",
"libutils",
],
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4c2e653..fcbfba9 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -83,8 +83,10 @@
"SyncFeatures.cpp",
"view/Surface.cpp",
"bufferqueue/1.0/B2HProducerListener.cpp",
+ "bufferqueue/1.0/Conversion.cpp",
"bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
"bufferqueue/1.0/H2BProducerListener.cpp",
+ "bufferqueue/1.0/WProducerListener.cpp",
"bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
"bufferqueue/2.0/B2HProducerListener.cpp",
"bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5fb3f0b..d87228f 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -59,6 +59,13 @@
}
}
+void BufferQueue::ProxyConsumerListener::onBufferAllocated(const BufferItem& item) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onBufferAllocated(item);
+ }
+}
+
void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != nullptr) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index f2d5c8e..776c6e6 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -58,7 +58,7 @@
int numDroppedBuffers = 0;
sp<IProducerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
// Check that the consumer doesn't currently have the maximum number of
// buffers acquired. We allow the max buffer count to be exceeded by one
@@ -203,7 +203,7 @@
if (sharedBufferAvailable && mCore->mQueue.empty()) {
// make sure the buffer has finished allocating before acquiring it
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
slot = mCore->mSharedBufferSlot;
@@ -264,7 +264,7 @@
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
@@ -286,7 +286,7 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
@@ -312,7 +312,7 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
return NO_ERROR;
@@ -330,7 +330,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mSharedBufferMode) {
BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
@@ -422,7 +422,7 @@
sp<IProducerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
@@ -461,7 +461,7 @@
listener = mCore->mConnectedProducerListener;
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
} // Autolock scope
@@ -485,7 +485,7 @@
BQ_LOGV("connect: controlledByApp=%s",
controlledByApp ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("connect: BufferQueue has been abandoned");
@@ -503,7 +503,7 @@
BQ_LOGV("disconnect");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mConsumerListener == nullptr) {
BQ_LOGE("disconnect: no consumer is connected");
@@ -515,7 +515,7 @@
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
return NO_ERROR;
}
@@ -527,7 +527,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
@@ -569,7 +569,7 @@
BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultWidth = width;
mCore->mDefaultHeight = height;
return NO_ERROR;
@@ -583,7 +583,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("setMaxBufferCount: producer is already connected");
@@ -623,8 +623,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setMaxAcquiredBufferCount: consumer is abandoned");
@@ -684,7 +684,7 @@
status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
BQ_LOGV("setConsumerName: '%s'", name.string());
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
return NO_ERROR;
@@ -693,7 +693,7 @@
status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
ATRACE_CALL();
BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultBufferFormat = defaultFormat;
return NO_ERROR;
}
@@ -702,7 +702,7 @@
android_dataspace defaultDataSpace) {
ATRACE_CALL();
BQ_LOGV("setDefaultBufferDataSpace: %u", defaultDataSpace);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultBufferDataSpace = defaultDataSpace;
return NO_ERROR;
}
@@ -710,7 +710,7 @@
status_t BufferQueueConsumer::setConsumerUsageBits(uint64_t usage) {
ATRACE_CALL();
BQ_LOGV("setConsumerUsageBits: %#" PRIx64, usage);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerUsageBits = usage;
return NO_ERROR;
}
@@ -718,7 +718,7 @@
status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) {
ATRACE_CALL();
BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerIsProtected = isProtected;
return NO_ERROR;
}
@@ -726,26 +726,26 @@
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
ATRACE_CALL();
BQ_LOGV("setTransformHint: %#x", hint);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mTransformHint = hint;
return NO_ERROR;
}
status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outStream = mCore->mSidebandStream;
return NO_ERROR;
}
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
std::vector<OccupancyTracker::Segment>* outHistory) {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
return NO_ERROR;
}
status_t BufferQueueConsumer::discardFreeBuffers() {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->discardFreeBuffersLocked();
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 960b194..96c55ac 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -110,7 +110,7 @@
BufferQueueCore::~BufferQueueCore() {}
void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
- Mutex::Autolock lock(mMutex);
+ std::lock_guard<std::mutex> lock(mMutex);
outResult->appendFormat("%s- BufferQueue ", prefix.string());
outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n",
@@ -306,10 +306,10 @@
return true;
}
-void BufferQueueCore::waitWhileAllocatingLocked() const {
+void BufferQueueCore::waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const {
ATRACE_CALL();
while (mIsAllocating) {
- mIsAllocatingCondition.wait(mMutex);
+ mIsAllocatingCondition.wait(lock);
}
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a462b03..72ae375 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -59,14 +59,15 @@
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
mCallbackCondition(),
- mDequeueTimeout(-1) {}
+ mDequeueTimeout(-1),
+ mDequeueWaitingForAllocation(false) {}
BufferQueueProducer::~BufferQueueProducer() {}
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
@@ -101,8 +102,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been "
@@ -163,7 +164,7 @@
if (delta < 0) {
listener = mCore->mConsumerListener;
}
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
} // Autolock scope
// Call back without lock held
@@ -180,8 +181,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setAsyncMode: BufferQueue has been abandoned");
@@ -215,7 +216,7 @@
}
mCore->mAsyncMode = async;
VALIDATE_CONSISTENCY();
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
if (delta < 0) {
listener = mCore->mConsumerListener;
}
@@ -247,7 +248,7 @@
}
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
- int* found) const {
+ std::unique_lock<std::mutex>& lock, int* found) const {
auto callerString = (caller == FreeSlotCaller::Dequeue) ?
"dequeueBuffer" : "attachBuffer";
bool tryAgain = true;
@@ -334,13 +335,13 @@
return WOULD_BLOCK;
}
if (mDequeueTimeout >= 0) {
- status_t result = mCore->mDequeueCondition.waitRelative(
- mCore->mMutex, mDequeueTimeout);
- if (result == TIMED_OUT) {
- return result;
+ std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
+ std::chrono::nanoseconds(mDequeueTimeout));
+ if (result == std::cv_status::timeout) {
+ return TIMED_OUT;
}
} else {
- mCore->mDequeueCondition.wait(mCore->mMutex);
+ mCore->mDequeueCondition.wait(lock);
}
}
} // while (tryAgain)
@@ -354,7 +355,7 @@
FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
if (mCore->mIsAbandoned) {
@@ -381,7 +382,16 @@
bool attachedByConsumer = false;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+ // If we don't have a free buffer, but we are currently allocating, we wait until allocation
+ // is finished such that we don't allocate in parallel.
+ if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
+ mDequeueWaitingForAllocation = true;
+ mCore->waitWhileAllocatingLocked(lock);
+ mDequeueWaitingForAllocation = false;
+ mDequeueWaitingForAllocationCondition.notify_all();
+ }
if (format == 0) {
format = mCore->mDefaultBufferFormat;
@@ -398,8 +408,7 @@
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
- status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
- &found);
+ status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
if (status != NO_ERROR) {
return status;
}
@@ -506,7 +515,7 @@
status_t error = graphicBuffer->initCheck();
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
@@ -514,7 +523,7 @@
}
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
@@ -530,6 +539,13 @@
return NO_INIT;
}
+ if (mCore->mConsumerListener != nullptr) {
+ BufferItem item;
+ item.mGraphicBuffer = graphicBuffer;
+ item.mSlot = *outSlot;
+ mCore->mConsumerListener->onBufferAllocated(item);
+ }
+
VALIDATE_CONSISTENCY();
} // Autolock scope
}
@@ -573,7 +589,7 @@
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
@@ -608,7 +624,7 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
listener = mCore->mConsumerListener;
}
@@ -634,7 +650,7 @@
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
@@ -652,7 +668,7 @@
return BAD_VALUE;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mFreeBuffers.empty()) {
return NO_MEMORY;
@@ -690,7 +706,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("attachBuffer: BufferQueue has been abandoned");
@@ -714,11 +730,11 @@
return BAD_VALUE;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
status_t returnFlags = NO_ERROR;
int found;
- status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found);
+ status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found);
if (status != NO_ERROR) {
return status;
}
@@ -791,7 +807,7 @@
uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
@@ -932,7 +948,7 @@
}
mCore->mBufferHasBeenQueued = true;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
mCore->mLastQueuedSlot = slot;
output->width = mCore->mDefaultWidth;
@@ -968,9 +984,9 @@
sp<Fence> lastQueuedFence;
{ // scope for the lock
- Mutex::Autolock lock(mCallbackMutex);
+ std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
- mCallbackCondition.wait(mCallbackMutex);
+ mCallbackCondition.wait(lock);
}
if (frameAvailableListener != nullptr) {
@@ -987,7 +1003,7 @@
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
- mCallbackCondition.broadcast();
+ mCallbackCondition.notify_all();
}
// Wait without lock held
@@ -1015,7 +1031,7 @@
status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
ATRACE_CALL();
BQ_LOGV("cancelBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
@@ -1060,7 +1076,7 @@
}
mSlots[slot].mFence = fence;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
return NO_ERROR;
@@ -1068,7 +1084,7 @@
int BufferQueueProducer::query(int what, int *outValue) {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (outValue == nullptr) {
BQ_LOGE("query: outValue was NULL");
@@ -1136,7 +1152,7 @@
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
producerControlledByApp ? "true" : "false");
@@ -1231,7 +1247,7 @@
int status = NO_ERROR;
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mode == DisconnectMode::AllLocal) {
if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) {
@@ -1240,7 +1256,7 @@
api = BufferQueueCore::CURRENTLY_CONNECTED_API;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
// It's not really an error to disconnect after the surface has
@@ -1284,7 +1300,7 @@
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("disconnect: not connected (req=%d)", api);
@@ -1314,7 +1330,7 @@
status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock _l(mCore->mMutex);
+ std::lock_guard<std::mutex> _l(mCore->mMutex);
mCore->mSidebandStream = stream;
listener = mCore->mConsumerListener;
} // Autolock scope
@@ -1336,8 +1352,8 @@
uint64_t allocUsage = 0;
std::string allocName;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (!mCore->mAllowAllocation) {
BQ_LOGE("allocateBuffers: allocation is not allowed for this "
@@ -1372,16 +1388,16 @@
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %#" PRIx64 ")", width, height, format, usage);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
return;
}
buffers.push_back(graphicBuffer);
}
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
PixelFormat checkFormat = format != 0 ?
@@ -1392,7 +1408,7 @@
// Something changed while we released the lock. Retry.
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
continue;
}
@@ -1414,14 +1430,27 @@
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d",
*slot);
+ if (mCore->mConsumerListener != nullptr) {
+ BufferItem item;
+ item.mGraphicBuffer = buffers[i];
+ item.mSlot = *slot;
+ mCore->mConsumerListener->onBufferAllocated(item);
+ }
+
// Make sure the erase is done after all uses of the slot
// iterator since it will be invalid after this point.
mCore->mFreeSlots.erase(slot);
}
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
VALIDATE_CONSISTENCY();
+
+ // If dequeue is waiting for to allocate a buffer, release the lock until it's not
+ // waiting anymore so it can use the buffer we just allocated.
+ while (mDequeueWaitingForAllocation) {
+ mDequeueWaitingForAllocationCondition.wait(lock);
+ }
} // Autolock scope
}
}
@@ -1430,7 +1459,7 @@
ATRACE_CALL();
BQ_LOGV("allowAllocation: %s", allow ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mAllowAllocation = allow;
return NO_ERROR;
}
@@ -1439,14 +1468,14 @@
ATRACE_CALL();
BQ_LOGV("setGenerationNumber: %u", generationNumber);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mGenerationNumber = generationNumber;
return NO_ERROR;
}
String8 BufferQueueProducer::getConsumerName() const {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
BQ_LOGV("getConsumerName: %s", mConsumerName.string());
return mConsumerName;
}
@@ -1455,7 +1484,7 @@
ATRACE_CALL();
BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (!sharedBufferMode) {
mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
}
@@ -1467,7 +1496,7 @@
ATRACE_CALL();
BQ_LOGV("setAutoRefresh: %d", autoRefresh);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mAutoRefresh = autoRefresh;
return NO_ERROR;
@@ -1477,7 +1506,7 @@
ATRACE_CALL();
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
if (!mCore->adjustAvailableSlotsLocked(delta)) {
@@ -1498,7 +1527,7 @@
ATRACE_CALL();
BQ_LOGV("getLastQueuedBuffer");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
*outBuffer = nullptr;
*outFence = Fence::NO_FENCE;
@@ -1534,7 +1563,7 @@
BQ_LOGV("addAndGetFrameTimestamps");
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != nullptr) {
@@ -1561,7 +1590,7 @@
status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const {
BQ_LOGV("getConsumerUsage");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outUsage = mCore->mConsumerUsageBits;
return NO_ERROR;
}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index abd9921..1e94cc1 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -131,6 +131,8 @@
}
}
+void ConsumerBase::onBufferAllocated(const BufferItem& /*item*/) {}
+
void ConsumerBase::onBuffersReleased() {
Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 85ac304..ea9045c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -28,7 +28,8 @@
ON_FRAME_REPLACED,
ON_BUFFERS_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
- LAST = ON_SIDEBAND_STREAM_CHANGED,
+ ON_BUFFER_ALLOCATED,
+ LAST = ON_BUFFER_ALLOCATED,
};
} // Anonymous namespace
@@ -54,6 +55,11 @@
item);
}
+ void onBufferAllocated(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onBufferAllocated)>(Tag::ON_BUFFER_ALLOCATED,
+ item);
+ }
+
void onBuffersReleased() override {
callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
}
@@ -89,6 +95,8 @@
return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
case Tag::ON_FRAME_REPLACED:
return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
+ case Tag::ON_BUFFER_ALLOCATED:
+ return callLocalAsync(data, reply, &IConsumerListener::onBufferAllocated);
case Tag::ON_BUFFERS_RELEASED:
return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
case Tag::ON_SIDEBAND_STREAM_CHANGED:
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 9dde15d..bf44121 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -360,7 +360,7 @@
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint64(usage);
- status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, TF_ONE_WAY);
+ status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("allocateBuffers failed to transact: %d", result);
}
@@ -781,6 +781,10 @@
int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge,
getTimestamps ? &frameTimestamps : nullptr);
+ if (fence == nullptr) {
+ ALOGE("dequeueBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+ fence = Fence::NO_FENCE;
+ }
reply->writeInt32(buf);
reply->write(*fence);
reply->writeUint64(bufferAge);
@@ -963,6 +967,10 @@
ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
return result;
}
+ if (fence == nullptr) {
+ ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+ fence = Fence::NO_FENCE;
+ }
result = reply->write(*fence);
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index bc63d31..b586bf3 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -69,7 +69,8 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& commands,
int64_t desiredPresentTime,
- const cached_buffer_t& uncacheBuffer) {
+ const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -89,6 +90,14 @@
data.writeInt64(desiredPresentTime);
data.writeStrongBinder(uncacheBuffer.token);
data.writeUint64(uncacheBuffer.cacheId);
+
+ if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ data.writeStrongBinder(IInterface::asBinder(listener));
+ data.writeInt64Vector(callbackIds);
+ }
+ }
+
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -542,8 +551,8 @@
ALOGE("enableVSyncInjections failed to writeBool: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
- data, &reply, TF_ONE_WAY);
+ result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("enableVSyncInjections failed to transact: %d", result);
return result;
@@ -563,7 +572,8 @@
ALOGE("injectVSync failed to writeInt64: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+ result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("injectVSync failed to transact: %d", result);
return result;
@@ -978,8 +988,18 @@
uncachedBuffer.token = data.readStrongBinder();
uncachedBuffer.cacheId = data.readUint64();
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int32_t listenersSize = data.readInt32();
+ for (int32_t i = 0; i < listenersSize; i++) {
+ auto listener =
+ interface_cast<ITransactionCompletedListener>(data.readStrongBinder());
+ std::vector<CallbackId> callbackIds;
+ data.readInt64Vector(&callbackIds);
+ listenerCallbacks.emplace_back(listener, callbackIds);
+ }
+
setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime, uncachedBuffer);
+ desiredPresentTime, uncachedBuffer, listenerCallbacks);
return NO_ERROR;
}
case BOOT_FINISHED: {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index ce88d7b..74cd4f1 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -76,7 +76,11 @@
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeInt64(latchTime);
+ status_t err = output->writeInt64Vector(callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->writeInt64(latchTime);
if (err != NO_ERROR) {
return err;
}
@@ -96,7 +100,11 @@
}
status_t TransactionStats::readFromParcel(const Parcel* input) {
- status_t err = input->readInt64(&latchTime);
+ status_t err = input->readInt64Vector(&callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = input->readInt64(&latchTime);
if (err != NO_ERROR) {
return err;
}
@@ -120,16 +128,11 @@
if (err != NO_ERROR) {
return err;
}
-
- for (const auto& [callbackIds, stats] : transactionStats) {
+ for (const auto& stats : transactionStats) {
err = output->writeParcelable(stats);
if (err != NO_ERROR) {
return err;
}
- err = output->writeInt64Vector(callbackIds);
- if (err != NO_ERROR) {
- return err;
- }
}
return NO_ERROR;
}
@@ -139,18 +142,11 @@
for (int i = 0; i < transactionStats_size; i++) {
TransactionStats stats;
- std::vector<CallbackId> callbackIds;
-
status_t err = input->readParcelable(&stats);
if (err != NO_ERROR) {
return err;
}
- err = input->readInt64Vector(&callbackIds);
- if (err != NO_ERROR) {
- return err;
- }
-
- transactionStats.emplace(callbackIds, stats);
+ transactionStats.push_back(stats);
}
return NO_ERROR;
}
@@ -159,11 +155,8 @@
const std::unordered_set<CallbackId>& callbackIds) {
ListenerStats listenerStats;
listenerStats.listener = listener;
- TransactionStats transactionStats;
- listenerStats.transactionStats.emplace(std::piecewise_construct,
- std::forward_as_tuple(callbackIds.begin(),
- callbackIds.end()),
- std::forward_as_tuple(transactionStats));
+ listenerStats.transactionStats.emplace_back(callbackIds);
+
return listenerStats;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f6ca9e8..3077b21 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,14 +86,7 @@
memcpy(output.writeInplace(16 * sizeof(float)),
colorTransform.asArray(), 16 * sizeof(float));
output.writeFloat(cornerRadius);
-
- if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- output.writeStrongBinder(IInterface::asBinder(listener));
- output.writeInt64Vector(callbackIds);
- }
- }
-
+ output.writeBool(hasListenerCallbacks);
output.writeStrongBinder(cachedBuffer.token);
output.writeUint64(cachedBuffer.cacheId);
output.writeParcelable(metadata);
@@ -163,15 +156,7 @@
colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
cornerRadius = input.readFloat();
-
- int32_t listenersSize = input.readInt32();
- for (int32_t i = 0; i < listenersSize; i++) {
- auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder());
- std::vector<CallbackId> callbackIds;
- input.readInt64Vector(&callbackIds);
- listenerCallbacks.emplace_back(listener, callbackIds);
- }
-
+ hasListenerCallbacks = input.readBool();
cachedBuffer.token = input.readStrongBinder();
cachedBuffer.cacheId = input.readUint64();
input.readParcelable(&metadata);
@@ -376,9 +361,9 @@
what |= eColorTransformChanged;
colorTransform = other.colorTransform;
}
- if (other.what & eListenerCallbacksChanged) {
- what |= eListenerCallbacksChanged;
- listenerCallbacks = other.listenerCallbacks;
+ if (other.what & eHasListenerCallbacksChanged) {
+ what |= eHasListenerCallbacksChanged;
+ hasListenerCallbacks = other.hasListenerCallbacks;
}
#ifndef NO_INPUT
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 39cd62f..83cf40c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -203,15 +203,15 @@
* that could possibly exist for the callbacks.
*/
std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
- for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- for (auto callbackId : callbackIds) {
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.callbackIds) {
auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
}
}
- for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- for (auto callbackId : callbackIds) {
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.callbackIds) {
auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
@@ -381,7 +381,7 @@
s.state.parentHandleForChild = nullptr;
composerStates.add(s);
- sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1, {});
+ sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1, {}, {});
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -391,7 +391,7 @@
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.cacheId = cacheId;
- sf->setTransactionState({}, {}, 0, nullptr, {}, -1, uncacheBuffer);
+ sf->setTransactionState({}, {}, 0, nullptr, {}, -1, uncacheBuffer, {});
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -406,10 +406,16 @@
continue;
}
+ // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
+ // time trying to cache them.
+ if (!s->buffer) {
+ continue;
+ }
+
uint64_t cacheId = 0;
status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
if (ret == NO_ERROR) {
- s->what &= ~static_cast<uint32_t>(layer_state_t::eBufferChanged);
+ s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
s->buffer = nullptr;
} else {
cacheId = BufferCache::getInstance().cache(s->buffer);
@@ -434,6 +440,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ std::vector<ListenerCallbacks> listenerCallbacks;
+
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -441,11 +449,7 @@
continue;
}
- // If the listener does not have any SurfaceControls set on this Transaction, send the
- // callback now
- if (surfaceControls.empty()) {
- listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
- }
+ listenerCallbacks.emplace_back(listener, std::move(callbackIds));
// If the listener has any SurfaceControls set on this Transaction update the surface state
for (const auto& surfaceControl : surfaceControls) {
@@ -454,8 +458,8 @@
ALOGE("failed to get layer state");
continue;
}
- s->what |= layer_state_t::eListenerCallbacksChanged;
- s->listenerCallbacks.emplace_back(listener, std::move(callbackIds));
+ s->what |= layer_state_t::eHasListenerCallbacksChanged;
+ s->hasListenerCallbacks = true;
}
}
mListenerCallbacks.clear();
@@ -494,7 +498,8 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
mDesiredPresentTime,
- {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/);
+ {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
+ listenerCallbacks);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
new file mode 100644
index 0000000..5cb3593
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -0,0 +1,1542 @@
+/*
+ * Copyright 2018, 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 <gui/bufferqueue/1.0/Conversion.h>
+
+namespace android {
+namespace conversion {
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+native_handle_t* native_handle_create_from_fd(int fd) {
+ if (fd < 2) {
+ return native_handle_create(0, 0);
+ }
+ native_handle_t* nh = native_handle_create(1, 0);
+ if (nh == nullptr) {
+ return nullptr;
+ }
+ nh->data[0] = fd;
+ return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+int native_handle_read_fd(native_handle_t const* nh, int index) {
+ return ((nh == nullptr) || (nh->numFds == 0) ||
+ (nh->numFds <= index) || (index < 0)) ?
+ -1 : nh->data[index];
+}
+
+/**
+ * Conversion functions
+ * ====================
+ *
+ * There are two main directions of conversion:
+ * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the
+ * input. The wrapper has type `TargetType`.
+ * - `toTargetType(...)`: Create a standalone object of type `TargetType` that
+ * corresponds to the input. The lifetime of the output does not depend on the
+ * lifetime of the input.
+ * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType`
+ * that cannot be copied and/or moved efficiently, or when there are multiple
+ * output arguments.
+ * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for
+ * `TargetType` that cannot be copied and/or moved efficiently, or when there
+ * are multiple output arguments.
+ *
+ * `wrapIn()` and `convertTo()` functions will take output arguments before
+ * input arguments. Some of these functions might return a value to indicate
+ * success or error.
+ *
+ * In converting or wrapping something as a Treble type that contains a
+ * `hidl_handle`, `native_handle_t*` will need to be created and returned as
+ * an additional output argument, hence only `wrapIn()` or `convertTo()` would
+ * be available. The caller must call `native_handle_delete()` to deallocate the
+ * returned native handle when it is no longer needed.
+ *
+ * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do
+ * not perform duplication of file descriptors, while `toTargetType()` and
+ * `convertTo()` do.
+ */
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t) {
+ return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
+}
+
+/**
+ * \brief Convert `Return<void>` to `binder::Status`.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `binder::Status`.
+ */
+// convert: Return<void> -> ::android::binder::Status
+::android::binder::Status toBinderStatus(
+ Return<void> const& t) {
+ return ::android::binder::Status::fromExceptionCode(
+ toStatusT(t),
+ t.description().c_str());
+}
+
+/**
+ * \brief Wrap `native_handle_t*` in `hidl_handle`.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \return The `hidl_handle` that points to \p nh.
+ */
+// wrap: native_handle_t* -> hidl_handle
+hidl_handle inHidlHandle(native_handle_t const* nh) {
+ return hidl_handle(nh);
+}
+
+/**
+ * \brief Convert `int32_t` to `Dataspace`.
+ *
+ * \param[in] l The source `int32_t`.
+ * \result The corresponding `Dataspace`.
+ */
+// convert: int32_t -> Dataspace
+Dataspace toHardwareDataspace(int32_t l) {
+ return static_cast<Dataspace>(l);
+}
+
+/**
+ * \brief Convert `Dataspace` to `int32_t`.
+ *
+ * \param[in] t The source `Dataspace`.
+ * \result The corresponding `int32_t`.
+ */
+// convert: Dataspace -> int32_t
+int32_t toRawDataspace(Dataspace const& t) {
+ return static_cast<int32_t>(t);
+}
+
+/**
+ * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that points to the buffer.
+ */
+// wrap: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) {
+ hidl_vec<uint8_t> t;
+ t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false);
+ return t;
+}
+
+/**
+ * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
+ */
+// convert: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size) {
+ hidl_vec<uint8_t> t;
+ t.resize(size);
+ uint8_t const* src = static_cast<uint8_t const*>(l);
+ std::copy(src, src + size, t.data());
+ return t;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+ t->attr.width = l.getWidth();
+ t->attr.height = l.getHeight();
+ t->attr.stride = l.getStride();
+ t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+ t->attr.layerCount = l.getLayerCount();
+ t->attr.usage = static_cast<uint32_t>(l.getUsage());
+ t->attr.id = l.getId();
+ t->attr.generationNumber = l.getGenerationNumber();
+ t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+ native_handle_t* handle = t.nativeHandle == nullptr ?
+ nullptr : native_handle_clone(t.nativeHandle);
+
+ size_t const numInts = 12 + static_cast<size_t>(handle ? handle->numInts : 0);
+ int32_t* ints = new int32_t[numInts];
+
+ size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+ int* fds = new int[numFds];
+
+ ints[0] = 'GBFR';
+ ints[1] = static_cast<int32_t>(t.attr.width);
+ ints[2] = static_cast<int32_t>(t.attr.height);
+ ints[3] = static_cast<int32_t>(t.attr.stride);
+ ints[4] = static_cast<int32_t>(t.attr.format);
+ ints[5] = static_cast<int32_t>(t.attr.layerCount);
+ ints[6] = static_cast<int32_t>(t.attr.usage);
+ ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+ ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+ ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+ ints[10] = 0;
+ ints[11] = 0;
+ if (handle) {
+ ints[10] = static_cast<int32_t>(handle->numFds);
+ ints[11] = static_cast<int32_t>(handle->numInts);
+ int* intsStart = handle->data + handle->numFds;
+ std::copy(handle->data, intsStart, fds);
+ std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+ }
+
+ void const* constBuffer = static_cast<void const*>(ints);
+ size_t size = numInts * sizeof(int32_t);
+ int const* constFds = static_cast<int const*>(fds);
+ status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+ delete [] fds;
+ delete [] ints;
+ native_handle_delete(handle);
+ return status == NO_ERROR;
+}
+
+/**
+ * Conversion functions for types outside media
+ * ============================================
+ *
+ * Some objects in libui and libgui that were made to go through binder calls do
+ * not expose ways to read or write their fields to the public. To pass an
+ * object of this kind through the HIDL boundary, translation functions need to
+ * work around the access restriction by using the publicly available
+ * `flatten()` and `unflatten()` functions.
+ *
+ * All `flatten()` and `unflatten()` overloads follow the same convention as
+ * follows:
+ *
+ * status_t flatten(ObjectType const& object,
+ * [OtherType const& other, ...]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * status_t unflatten(ObjectType* object,
+ * [OtherType* other, ...,]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * The number of `other` parameters varies depending on the `ObjectType`. For
+ * example, in the process of unflattening an object that contains
+ * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
+ * be created.
+ *
+ * The last four parameters always work the same way in all overloads of
+ * `flatten()` and `unflatten()`:
+ * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
+ * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
+ * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
+ * size (in ints) of the fd buffer pointed to by `fds`.
+ * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
+ * from, `size` is the size (in bytes) of the non-fd buffer pointed to by
+ * `buffer`, `fds` is the pointer to the fd buffer to be read from, and
+ * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
+ * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
+ * will be advanced, while `size` and `numFds` will be decreased to reflect
+ * how much storage/data of the two buffers (fd and non-fd) have been used.
+ * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
+ * `numFds` are invalid.
+ *
+ * The return value of a successful `flatten()` or `unflatten()` call will be
+ * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
+ *
+ * For each object type that supports flattening, there will be two accompanying
+ * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
+ * return the size of the non-fd buffer that the object will need for
+ * flattening. `getFdCount()` will return the size of the fd buffer that the
+ * object will need for flattening.
+ *
+ * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
+ * `flatten()` and `unflatten()`, are similar to functions of the same name in
+ * the abstract class `Flattenable`. The only difference is that functions in
+ * this file are not member functions of the object type. For example, we write
+ *
+ * flatten(x, buffer, size, fds, numFds)
+ *
+ * instead of
+ *
+ * x.flatten(buffer, size, fds, numFds)
+ *
+ * because we cannot modify the type of `x`.
+ *
+ * There is one exception to the naming convention: `hidl_handle` that
+ * represents a fence. The four functions for this "Fence" type have the word
+ * "Fence" attched to their names because the object type, which is
+ * `hidl_handle`, does not carry the special meaning that the object itself can
+ * only contain zero or one file descriptor.
+ */
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+ return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+size_t getFenceFdCount(hidl_handle const& fence) {
+ return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numFdsInHandle;
+ FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+ if (numFdsInHandle > 1) {
+ return BAD_VALUE;
+ }
+
+ if (numFds < numFdsInHandle) {
+ return NO_MEMORY;
+ }
+
+ if (numFdsInHandle) {
+ *nh = native_handle_create_from_fd(*fds);
+ if (*nh == nullptr) {
+ return NO_MEMORY;
+ }
+ *fence = *nh;
+ ++fds;
+ --numFds;
+ } else {
+ *nh = nullptr;
+ *fence = hidl_handle();
+ }
+
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] t The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFenceFlattenedSize(fence) ||
+ numFds < getFenceFdCount(fence)) {
+ return NO_MEMORY;
+ }
+ // Cast to uint32_t since the size of a size_t can vary between 32- and
+ // 64-bit processes
+ FlattenableUtils::write(buffer, size,
+ static_cast<uint32_t>(getFenceFdCount(fence)));
+ int fd = native_handle_read_fd(fence);
+ if (fd != -1) {
+ *fds = fd;
+ ++fds;
+ --numFds;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+ != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+bool convertTo(Fence* l, hidl_handle const& t) {
+ int fd = native_handle_read_fd(t);
+ if (fd != -1) {
+ fd = dup(fd);
+ if (fd == -1) {
+ return false;
+ }
+ }
+ native_handle_t* nh = native_handle_create_from_fd(fd);
+ if (nh == nullptr) {
+ if (fd != -1) {
+ close(fd);
+ }
+ return false;
+ }
+
+ size_t const baseSize = getFenceFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ size_t const baseNumFds = getFenceFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+ native_handle_delete(nh);
+ return false;
+ }
+ native_handle_delete(nh);
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ constexpr size_t min = sizeof(t.state);
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ return min;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ return min + getFenceFlattenedSize(t.fence);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ return min + sizeof(
+ ::android::FenceTime::Snapshot::signalTime);
+ }
+ return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ return t.state ==
+ HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+ getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`.
+ */
+status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::EMPTY);
+ return NO_ERROR;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::FENCE);
+ return flattenFence(t.fence, buffer, size, fds, numFds);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+ FlattenableUtils::write(buffer, size, t.signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `FenceTimeSnapshot`.
+ *
+ * \param[out] t The destination `FenceTimeSnapshot`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and the constructed snapshot contains a
+ * file descriptor, \p nh will be created to hold that file descriptor. In this
+ * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < sizeof(t->state)) {
+ return NO_MEMORY;
+ }
+
+ *nh = nullptr;
+ ::android::FenceTime::Snapshot::State state;
+ FlattenableUtils::read(buffer, size, state);
+ switch (state) {
+ case ::android::FenceTime::Snapshot::State::EMPTY:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY;
+ return NO_ERROR;
+ case ::android::FenceTime::Snapshot::State::FENCE:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE;
+ return unflattenFence(&t->fence, nh, buffer, size, fds, numFds);
+ case ::android::FenceTime::Snapshot::State::SIGNAL_TIME:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME;
+ if (size < sizeof(t->signalTimeNs)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, t->signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+ return sizeof(uint64_t) + // mFrameNumber
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(nsecs_t) + // mPostedTime
+ sizeof(nsecs_t) + // mRequestedPresentTime
+ sizeof(nsecs_t) + // mLatchTime
+ sizeof(nsecs_t) + // mFirstRefreshStartTime
+ sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return minFlattenedSize(t) +
+ getFlattenedSize(t.gpuCompositionDoneFence) +
+ getFlattenedSize(t.displayPresentFence) +
+ getFlattenedSize(t.displayRetireFence) +
+ getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return getFdCount(t.gpuCompositionDoneFence) +
+ getFdCount(t.displayPresentFence) +
+ getFdCount(t.displayRetireFence) +
+ getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Unflatten `FrameEventsDelta`.
+ *
+ * \param[out] t The destination `FrameEventsDelta`.
+ * \param[out] nh The underlying array of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
+ * populated with `nullptr` or newly created handles. Each non-null slot in \p
+ * nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
+ std::vector<native_handle_t*>* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, t->frameNumber);
+
+ // These were written as uint8_t for alignment.
+ uint8_t temp = 0;
+ FlattenableUtils::read(buffer, size, temp);
+ size_t index = static_cast<size_t>(temp);
+ if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ t->index = static_cast<uint32_t>(index);
+
+ FlattenableUtils::read(buffer, size, temp);
+ t->addPostCompositeCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ t->addRetireCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ t->addReleaseCalled = static_cast<bool>(temp);
+
+ FlattenableUtils::read(buffer, size, t->postedTimeNs);
+ FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs);
+ FlattenableUtils::read(buffer, size, t->latchTimeNs);
+ FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs);
+ FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs);
+ FlattenableUtils::read(buffer, size, t->dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4];
+ tSnapshot[0] = &t->gpuCompositionDoneFence;
+ tSnapshot[1] = &t->displayPresentFence;
+ tSnapshot[2] = &t->displayRetireFence;
+ tSnapshot[3] = &t->releaseFence;
+ nh->resize(4);
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = unflatten(
+ tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (snapshotIndex > 0) {
+ --snapshotIndex;
+ if ((*nh)[snapshotIndex] != nullptr) {
+ native_handle_delete((*nh)[snapshotIndex]);
+ }
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t numFds) {
+ // Check that t.index is within a valid range.
+ if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+ || t.index > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, t.frameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, t.postedTimeNs);
+ FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+ FlattenableUtils::write(buffer, size, t.latchTimeNs);
+ FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+ tSnapshot[0] = &t.gpuCompositionDoneFence;
+ tSnapshot[1] = &t.displayPresentFence;
+ tSnapshot[2] = &t.displayRetireFence;
+ tSnapshot[3] = &t.releaseFence;
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = flatten(
+ *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t size = 4 + // mDeltas.size()
+ sizeof(t.compositorTiming);
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ size += getFlattenedSize(t.deltas[i]);
+ }
+ return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t numFds = 0;
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ numFds += getFdCount(t.deltas[i]);
+ }
+ return numFds;
+}
+
+/**
+ * \brief Unflatten `FrameEventHistoryDelta`.
+ *
+ * \param[out] t The destination `FrameEventHistoryDelta`.
+ * \param[out] nh The underlying array of arrays of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
+ * newly created handles. The second dimension of \p nh will be 4. Each non-null
+ * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->compositorTiming);
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (static_cast<size_t>(deltaCount) >
+ ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ t->deltas.resize(deltaCount);
+ nh->resize(deltaCount);
+ for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) {
+ status_t status = unflatten(
+ &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+ for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+ status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `::android::FrameEventHistoryData` in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `::android::FrameEventHistoryDelta`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ ::android::FrameEventHistoryDelta const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Return the size of the buffer required to flatten `Region`.
+ *
+ * \param[in] t The input `Region`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(Region const& t) {
+ return sizeof(uint32_t) + t.size() * sizeof(::android::Rect);
+}
+
+/**
+ * \brief Unflatten `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t unflatten(Region* t, void const*& buffer, size_t& size) {
+ if (size < sizeof(uint32_t)) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numRects = 0;
+ FlattenableUtils::read(buffer, size, numRects);
+ if (size < numRects * sizeof(Rect)) {
+ return NO_MEMORY;
+ }
+ if (numRects > (UINT32_MAX / sizeof(Rect))) {
+ return NO_MEMORY;
+ }
+
+ t->resize(numRects);
+ for (size_t r = 0; r < numRects; ++r) {
+ ::android::Rect rect(::android::Rect::EMPTY_RECT);
+ status_t status = rect.unflatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ (*t)[r] = Rect{
+ static_cast<int32_t>(rect.left),
+ static_cast<int32_t>(rect.top),
+ static_cast<int32_t>(rect.right),
+ static_cast<int32_t>(rect.bottom)};
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `Region`.
+ *
+ * \param[in] t The source `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flatten(Region const& t, void*& buffer, size_t& size) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.size()));
+ for (size_t r = 0; r < t.size(); ++r) {
+ ::android::Rect rect(
+ static_cast<int32_t>(t[r].left),
+ static_cast<int32_t>(t[r].top),
+ static_cast<int32_t>(t[r].right),
+ static_cast<int32_t>(t[r].bottom));
+ status_t status = rect.flatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Convert `::android::Region` to `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in] l The source `::android::Region`.
+ */
+// convert: ::android::Region -> Region
+bool convertTo(Region* t, ::android::Region const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ if (l.flatten(buffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ if (unflatten(t, constBuffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `Region` to `::android::Region`.
+ *
+ * \param[out] l The destination `::android::Region`.
+ * \param[in] t The source `Region`.
+ */
+// convert: Region -> ::android::Region
+bool convertTo(::android::Region* l, Region const& t) {
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ if (flatten(t, buffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ if (l->unflatten(constBuffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+ return sizeof(int64_t) + // timestamp
+ sizeof(int) + // isAutoTimestamp
+ sizeof(android_dataspace) + // dataSpace
+ sizeof(::android::Rect) + // crop
+ sizeof(int) + // scalingMode
+ sizeof(uint32_t) + // transform
+ sizeof(uint32_t) + // stickyTransform
+ sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Return the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) {
+ return minFlattenedSize(t) +
+ getFenceFlattenedSize(t.fence) +
+ getFlattenedSize(t.surfaceDamage) +
+ sizeof(HdrMetadata::validTypes);
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::QueueBufferInput const& t) {
+ return getFenceFdCount(t.fence);
+}
+
+/**
+ * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The native handle cloned from `t.fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence`. */
+status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.timestamp);
+ FlattenableUtils::write(buffer, size, static_cast<int>(t.isAutoTimestamp));
+ FlattenableUtils::write(buffer, size,
+ static_cast<android_dataspace_t>(t.dataSpace));
+ FlattenableUtils::write(buffer, size, ::android::Rect(
+ static_cast<int32_t>(t.crop.left),
+ static_cast<int32_t>(t.crop.top),
+ static_cast<int32_t>(t.crop.right),
+ static_cast<int32_t>(t.crop.bottom)));
+ FlattenableUtils::write(buffer, size, static_cast<int>(t.scalingMode));
+ FlattenableUtils::write(buffer, size, t.transform);
+ FlattenableUtils::write(buffer, size, t.stickyTransform);
+ FlattenableUtils::write(buffer, size, t.getFrameTimestamps);
+
+ *nh = t.fence.getNativeHandle() == nullptr ?
+ nullptr : native_handle_clone(t.fence);
+ status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = flatten(t.surfaceDamage, buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0));
+ return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->timestamp);
+ int lIsAutoTimestamp;
+ FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+ t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+ android_dataspace_t lDataSpace;
+ FlattenableUtils::read(buffer, size, lDataSpace);
+ t->dataSpace = static_cast<Dataspace>(lDataSpace);
+ Rect lCrop;
+ FlattenableUtils::read(buffer, size, lCrop);
+ t->crop = Rect{
+ static_cast<int32_t>(lCrop.left),
+ static_cast<int32_t>(lCrop.top),
+ static_cast<int32_t>(lCrop.right),
+ static_cast<int32_t>(lCrop.bottom)};
+ int lScalingMode;
+ FlattenableUtils::read(buffer, size, lScalingMode);
+ t->scalingMode = static_cast<int32_t>(lScalingMode);
+ FlattenableUtils::read(buffer, size, t->transform);
+ FlattenableUtils::read(buffer, size, t->stickyTransform);
+ FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+ status_t status = unflattenFence(&(t->fence), nh,
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ // HdrMetadata ignored
+ return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
+ * `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If `t.fence` has a valid file descriptor, it will be duplicated.
+ */
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferInput* l,
+ HGraphicBufferProducer::QueueBufferInput const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ native_handle_t* nh;
+ if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
+ return false;
+ }
+
+ native_handle_delete(nh);
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+// wrap: BGraphicBufferProducer::QueueBufferOutput ->
+// HGraphicBufferProducer::QueueBufferOutput
+bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ BGraphicBufferProducer::QueueBufferOutput const& l) {
+ if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) {
+ return false;
+ }
+ t->width = l.width;
+ t->height = l.height;
+ t->transformHint = l.transformHint;
+ t->numPendingBuffers = l.numPendingBuffers;
+ t->nextFrameNumber = l.nextFrameNumber;
+ t->bufferReplaced = l.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// BGraphicBufferProducer::QueueBufferOutput
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t) {
+ if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+ return false;
+ }
+ l->width = t.width;
+ l->height = t.height;
+ l->transformHint = t.transformHint;
+ l->numPendingBuffers = t.numPendingBuffers;
+ l->nextFrameNumber = t.nextFrameNumber;
+ l->bufferReplaced = t.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l) {
+ switch (l) {
+ case BGraphicBufferProducer::DisconnectMode::Api:
+ return HGraphicBufferProducer::DisconnectMode::API;
+ case BGraphicBufferProducer::DisconnectMode::AllLocal:
+ return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+ }
+ return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
+ * `BGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
+ */
+BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+ HGraphicBufferProducer::DisconnectMode t) {
+ switch (t) {
+ case HGraphicBufferProducer::DisconnectMode::API:
+ return BGraphicBufferProducer::DisconnectMode::Api;
+ case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL:
+ return BGraphicBufferProducer::DisconnectMode::AllLocal;
+ }
+ return BGraphicBufferProducer::DisconnectMode::Api;
+}
+
+} // namespace conversion
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
new file mode 100644
index 0000000..78dc4e8
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016, 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 <gui/bufferqueue/1.0/WProducerListener.h>
+
+namespace android {
+
+// TWProducerListener
+TWProducerListener::TWProducerListener(
+ sp<BProducerListener> const& base):
+ mBase(base) {
+}
+
+Return<void> TWProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+ return Void();
+}
+
+Return<bool> TWProducerListener::needsReleaseNotify() {
+ return mBase->needsReleaseNotify();
+}
+
+// LWProducerListener
+LWProducerListener::LWProducerListener(
+ sp<HProducerListener> const& base):
+ mBase(base) {
+}
+
+void LWProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+}
+
+bool LWProducerListener::needsReleaseNotify() {
+ return static_cast<bool>(mBase->needsReleaseNotify());
+}
+
+} // namespace android
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index da95274..721427b 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -61,6 +61,7 @@
void onDisconnect() override;
void onFrameAvailable(const BufferItem& item) override;
void onFrameReplaced(const BufferItem& item) override;
+ void onBufferAllocated(const BufferItem& item) override;
void onBuffersReleased() override;
void onSidebandStreamChanged() override;
void addAndGetFrameTimestamps(
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index b377a41..0e80283 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -22,8 +22,6 @@
#include <gui/BufferSlot.h>
#include <gui/OccupancyTracker.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
#include <utils/NativeHandle.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
@@ -33,6 +31,8 @@
#include <list>
#include <set>
+#include <mutex>
+#include <condition_variable>
#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
@@ -134,7 +134,7 @@
bool adjustAvailableSlotsLocked(int delta);
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
- void waitWhileAllocatingLocked() const;
+ void waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const;
#if DEBUG_ONLY_CODE
// validateConsistencyLocked ensures that the free lists are in sync with
@@ -145,7 +145,7 @@
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueueCore objects. It must be locked whenever any
// member variable is accessed.
- mutable Mutex mMutex;
+ mutable std::mutex mMutex;
// mIsAbandoned indicates that the BufferQueue will no longer be used to
// consume image buffers pushed to it using the IGraphicBufferProducer
@@ -219,7 +219,7 @@
// mDequeueCondition is a condition variable used for dequeueBuffer in
// synchronous mode.
- mutable Condition mDequeueCondition;
+ mutable std::condition_variable mDequeueCondition;
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
// block. This flag is set during connect when both the producer and
@@ -282,7 +282,7 @@
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
// becomes false.
- mutable Condition mIsAllocatingCondition;
+ mutable std::condition_variable mIsAllocatingCondition;
// mAllowAllocation determines whether dequeueBuffer is allowed to allocate
// new buffers
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 73bc5dd..415e2a6 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -211,7 +211,8 @@
Dequeue,
Attach,
};
- status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const;
+ status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock<std::mutex>& lock,
+ int* found) const;
sp<BufferQueueCore> mCore;
@@ -243,15 +244,22 @@
// (mCore->mMutex) is held, a ticket is retained by the producer. After
// dropping the BufferQueue lock, the producer must wait on the condition
// variable until the current callback ticket matches its retained ticket.
- Mutex mCallbackMutex;
+ std::mutex mCallbackMutex;
int mNextCallbackTicket; // Protected by mCore->mMutex
int mCurrentCallbackTicket; // Protected by mCallbackMutex
- Condition mCallbackCondition;
+ std::condition_variable mCallbackCondition;
// Sets how long dequeueBuffer or attachBuffer will block if a buffer or
// slot is not yet available.
nsecs_t mDequeueTimeout;
+ // If set to true, dequeueBuffer() is currently waiting for buffer allocation to complete.
+ bool mDequeueWaitingForAllocation;
+
+ // Condition variable to signal allocateBuffers() that dequeueBuffer() is no longer waiting for
+ // allocation to complete.
+ std::condition_variable mDequeueWaitingForAllocationCondition;
+
}; // class BufferQueueProducer
} // namespace android
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 366ced3..7c26482 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -141,6 +141,7 @@
// classes if they want the notification.
virtual void onFrameAvailable(const BufferItem& item) override;
virtual void onFrameReplaced(const BufferItem& item) override;
+ virtual void onBufferAllocated(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index c082882..03fefbe 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -61,6 +61,13 @@
// This is called without any lock held and can be called concurrently by multiple threads.
virtual void onFrameReplaced(const BufferItem& /* item */) {} /* Asynchronous */
+ // onBufferAllocated is called to notify the buffer consumer that the BufferQueue has allocated
+ // a GraphicBuffer for a particular slot. Only the GraphicBuffer pointer and the slot ID will
+ // be populated.
+ //
+ // This is called without any lock held and can be called concurrently by multiple threads.
+ virtual void onBufferAllocated(const BufferItem& /* item */) {} /* Asynchronous */
+
// onBuffersReleased is called to notify the buffer consumer that the BufferQueue has released
// its references to one or more GraphicBuffers contained in its slots. The buffer consumer
// should then call BufferQueue::getReleasedBuffers to retrieve the list of buffers.
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 2f538ad..9f7e22b 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -25,6 +25,7 @@
#include <binder/IInterface.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -75,10 +76,10 @@
enum {
// A flag returned by dequeueBuffer when the client needs to call
// requestBuffer immediately thereafter.
- BUFFER_NEEDS_REALLOCATION = 0x1,
+ BUFFER_NEEDS_REALLOCATION = BufferQueueDefs::BUFFER_NEEDS_REALLOCATION,
// A flag returned by dequeueBuffer when all mirrored slots should be
// released by the client. This flag should always be processed first.
- RELEASE_ALL_BUFFERS = 0x2,
+ RELEASE_ALL_BUFFERS = BufferQueueDefs::RELEASE_ALL_BUFFERS,
};
enum {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0ef5b39..fe85fdf 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -20,13 +20,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
#include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
+
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -34,6 +31,11 @@
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
#include <optional>
#include <vector>
@@ -133,7 +135,8 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime,
- const cached_buffer_t& uncacheBuffer) = 0;
+ const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 29ab026..774ad46 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -34,18 +34,6 @@
using CallbackId = int64_t;
-struct CallbackIdsHash {
- // CallbackId vectors have several properties that let us get away with this simple hash.
- // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
- // empty we can still hash 0.
- // 2) CallbackId vectors for the same listener either are identical or contain none of the
- // same members. It is sufficient to just check the first CallbackId in the vectors. If
- // they match, they are the same. If they do not match, they are not the same.
- std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
- return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
- }
-};
-
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -65,6 +53,15 @@
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
+ TransactionStats() = default;
+ TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
+ TransactionStats(const std::unordered_set<CallbackId>& ids)
+ : callbackIds(ids.begin(), ids.end()) {}
+ TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
+ const std::vector<SurfaceStats>& surfaces)
+ : callbackIds(ids), latchTime(latch), presentFence(present), surfaceStats(surfaces) {}
+
+ std::vector<CallbackId> callbackIds;
nsecs_t latchTime = -1;
sp<Fence> presentFence = nullptr;
std::vector<SurfaceStats> surfaceStats;
@@ -79,7 +76,7 @@
const std::unordered_set<CallbackId>& callbackIds);
sp<ITransactionCompletedListener> listener;
- std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+ std::vector<TransactionStats> transactionStats;
};
class ITransactionCompletedListener : public IInterface {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 77bf8f1..2256497 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,7 +23,6 @@
#include <utils/Errors.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#ifndef NO_INPUT
@@ -86,7 +85,7 @@
eApiChanged = 0x04000000,
eSidebandStreamChanged = 0x08000000,
eColorTransformChanged = 0x10000000,
- eListenerCallbacksChanged = 0x20000000,
+ eHasListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
eFrameChanged = 0x1'00000000,
@@ -120,6 +119,7 @@
surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
+ hasListenerCallbacks(false),
bgColorAlpha(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
colorSpaceAgnostic(false) {
@@ -182,7 +182,7 @@
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
- std::vector<ListenerCallbacks> listenerCallbacks;
+ bool hasListenerCallbacks;
#ifndef NO_INPUT
InputWindowInfo inputInfo;
#endif
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
new file mode 100644
index 0000000..627845c
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -0,0 +1,765 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
+
+#include <vector>
+#include <list>
+
+#include <unistd.h>
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <binder/Status.h>
+#include <ui/FenceTime.h>
+#include <cutils/native_handle.h>
+#include <gui/IGraphicBufferProducer.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace conversion {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::status_t;
+
+using ::android::String8;
+
+using ::android::hardware::media::V1_0::Rect;
+using ::android::hardware::media::V1_0::Region;
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::GraphicBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+ HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer
+ BGraphicBufferProducer;
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+native_handle_t* native_handle_create_from_fd(int fd);
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+int native_handle_read_fd(native_handle_t const* nh, int index = 0);
+
+/**
+ * Conversion functions
+ * ====================
+ *
+ * There are two main directions of conversion:
+ * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the
+ * input. The wrapper has type `TargetType`.
+ * - `toTargetType(...)`: Create a standalone object of type `TargetType` that
+ * corresponds to the input. The lifetime of the output does not depend on the
+ * lifetime of the input.
+ * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType`
+ * that cannot be copied and/or moved efficiently, or when there are multiple
+ * output arguments.
+ * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for
+ * `TargetType` that cannot be copied and/or moved efficiently, or when there
+ * are multiple output arguments.
+ *
+ * `wrapIn()` and `convertTo()` functions will take output arguments before
+ * input arguments. Some of these functions might return a value to indicate
+ * success or error.
+ *
+ * In converting or wrapping something as a Treble type that contains a
+ * `hidl_handle`, `native_handle_t*` will need to be created and returned as
+ * an additional output argument, hence only `wrapIn()` or `convertTo()` would
+ * be available. The caller must call `native_handle_delete()` to deallocate the
+ * returned native handle when it is no longer needed.
+ *
+ * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do
+ * not perform duplication of file descriptors, while `toTargetType()` and
+ * `convertTo()` do.
+ */
+
+/**
+ * \brief Convert `Return<void>` to `binder::Status`.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `binder::Status`.
+ */
+// convert: Return<void> -> ::android::binder::Status
+::android::binder::Status toBinderStatus(Return<void> const& t);
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t);
+
+/**
+ * \brief Wrap `native_handle_t*` in `hidl_handle`.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \return The `hidl_handle` that points to \p nh.
+ */
+// wrap: native_handle_t* -> hidl_handle
+hidl_handle inHidlHandle(native_handle_t const* nh);
+
+/**
+ * \brief Convert `int32_t` to `Dataspace`.
+ *
+ * \param[in] l The source `int32_t`.
+ * \result The corresponding `Dataspace`.
+ */
+// convert: int32_t -> Dataspace
+Dataspace toHardwareDataspace(int32_t l);
+
+/**
+ * \brief Convert `Dataspace` to `int32_t`.
+ *
+ * \param[in] t The source `Dataspace`.
+ * \result The corresponding `int32_t`.
+ */
+// convert: Dataspace -> int32_t
+int32_t toRawDataspace(Dataspace const& t);
+
+/**
+ * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that points to the buffer.
+ */
+// wrap: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size);
+
+/**
+ * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
+ */
+// convert: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size);
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+void wrapAs(AnwBuffer* t, GraphicBuffer const& l);
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+bool convertTo(GraphicBuffer* l, AnwBuffer const& t);
+
+/**
+ * Conversion functions for types outside media
+ * ============================================
+ *
+ * Some objects in libui and libgui that were made to go through binder calls do
+ * not expose ways to read or write their fields to the public. To pass an
+ * object of this kind through the HIDL boundary, translation functions need to
+ * work around the access restriction by using the publicly available
+ * `flatten()` and `unflatten()` functions.
+ *
+ * All `flatten()` and `unflatten()` overloads follow the same convention as
+ * follows:
+ *
+ * status_t flatten(ObjectType const& object,
+ * [OtherType const& other, ...]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * status_t unflatten(ObjectType* object,
+ * [OtherType* other, ...,]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * The number of `other` parameters varies depending on the `ObjectType`. For
+ * example, in the process of unflattening an object that contains
+ * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
+ * be created.
+ *
+ * The last four parameters always work the same way in all overloads of
+ * `flatten()` and `unflatten()`:
+ * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
+ * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
+ * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
+ * size (in ints) of the fd buffer pointed to by `fds`.
+ * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
+ * from, `size` is the size (in bytes) of the non-fd buffer pointed to by
+ * `buffer`, `fds` is the pointer to the fd buffer to be read from, and
+ * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
+ * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
+ * will be advanced, while `size` and `numFds` will be decreased to reflect
+ * how much storage/data of the two buffers (fd and non-fd) have been used.
+ * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
+ * `numFds` are invalid.
+ *
+ * The return value of a successful `flatten()` or `unflatten()` call will be
+ * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
+ *
+ * For each object type that supports flattening, there will be two accompanying
+ * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
+ * return the size of the non-fd buffer that the object will need for
+ * flattening. `getFdCount()` will return the size of the fd buffer that the
+ * object will need for flattening.
+ *
+ * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
+ * `flatten()` and `unflatten()`, are similar to functions of the same name in
+ * the abstract class `Flattenable`. The only difference is that functions in
+ * this file are not member functions of the object type. For example, we write
+ *
+ * flatten(x, buffer, size, fds, numFds)
+ *
+ * instead of
+ *
+ * x.flatten(buffer, size, fds, numFds)
+ *
+ * because we cannot modify the type of `x`.
+ *
+ * There is one exception to the naming convention: `hidl_handle` that
+ * represents a fence. The four functions for this "Fence" type have the word
+ * "Fence" attched to their names because the object type, which is
+ * `hidl_handle`, does not carry the special meaning that the object itself can
+ * only contain zero or one file descriptor.
+ */
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+size_t getFenceFlattenedSize(hidl_handle const& fence);
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+size_t getFenceFdCount(hidl_handle const& fence);
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] t The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l);
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+bool convertTo(Fence* l, hidl_handle const& t);
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::FenceTimeSnapshot const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+size_t getFdCount(HGraphicBufferProducer::FenceTimeSnapshot const& t);
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`.
+ */
+status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Unflatten `FenceTimeSnapshot`.
+ *
+ * \param[out] t The destination `FenceTimeSnapshot`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and the constructed snapshot contains a
+ * file descriptor, \p nh will be created to hold that file descriptor. In this
+ * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::FrameEventsDelta const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(HGraphicBufferProducer::FrameEventsDelta const& t);
+
+/**
+ * \brief Unflatten `FrameEventsDelta`.
+ *
+ * \param[out] t The destination `FrameEventsDelta`.
+ * \param[out] nh The underlying array of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
+ * populated with `nullptr` or newly created handles. Each non-null slot in \p
+ * nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
+ std::vector<native_handle_t*>* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t numFds);
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+/**
+ * \brief Unflatten `FrameEventHistoryDelta`.
+ *
+ * \param[out] t The destination `FrameEventHistoryDelta`.
+ * \param[out] nh The underlying array of arrays of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
+ * newly created handles. The second dimension of \p nh will be 4. Each non-null
+ * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `::android::FrameEventHistoryData` in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `::android::FrameEventHistoryDelta`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ ::android::FrameEventHistoryDelta const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Return the size of the buffer required to flatten `Region`.
+ *
+ * \param[in] t The input `Region`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(Region const& t);
+
+/**
+ * \brief Unflatten `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t unflatten(Region* t, void const*& buffer, size_t& size);
+
+/**
+ * \brief Flatten `Region`.
+ *
+ * \param[in] t The source `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flatten(Region const& t, void*& buffer, size_t& size);
+
+/**
+ * \brief Convert `::android::Region` to `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in] l The source `::android::Region`.
+ */
+// convert: ::android::Region -> Region
+bool convertTo(Region* t, ::android::Region const& l);
+
+/**
+ * \brief Convert `Region` to `::android::Region`.
+ *
+ * \param[out] l The destination `::android::Region`.
+ * \param[in] t The source `Region`.
+ */
+// convert: Region -> ::android::Region
+bool convertTo(::android::Region* l, Region const& t);
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::QueueBufferInput const& t);
+/**
+ * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The native handle cloned from `t.fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence`. */
+status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
+ * `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If `t.fence` has a valid file descriptor, it will be duplicated.
+ */
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferInput* l,
+ HGraphicBufferProducer::QueueBufferInput const& t);
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+// wrap: BGraphicBufferProducer::QueueBufferOutput ->
+// HGraphicBufferProducer::QueueBufferOutput
+bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ BGraphicBufferProducer::QueueBufferOutput const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// BGraphicBufferProducer::QueueBufferOutput
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t);
+
+/**
+ * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
+ * `BGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
+ */
+BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+ HGraphicBufferProducer::DisconnectMode t);
+
+} // namespace conversion
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
new file mode 100644
index 0000000..0e57f20
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/bufferqueue/1.0/Conversion.h>
+#include <gui/bufferqueue/1.0/WProducerListener.h>
+#include <system/window.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+ IProducerListener HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+typedef ::android::IProducerListener BProducerListener;
+using ::android::BnGraphicBufferProducer;
+
+#ifndef LOG
+struct LOG_dummy {
+ template <typename T>
+ LOG_dummy& operator<< (const T&) { return *this; }
+};
+
+#define LOG(x) LOG_dummy()
+#endif
+
+// Instantiate only if HGraphicBufferProducer is base of BASE.
+template <typename BASE,
+ typename = typename std::enable_if<std::is_base_of<HGraphicBufferProducer, BASE>::value>::type>
+struct TWGraphicBufferProducer : public BASE {
+ TWGraphicBufferProducer(sp<BGraphicBufferProducer> const& base) : mBase(base) {}
+ Return<void> requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> buf;
+ status_t status = mBase->requestBuffer(slot, &buf);
+ AnwBuffer anwBuffer;
+ if (buf != nullptr) {
+ ::android::conversion::wrapAs(&anwBuffer, *buf);
+ }
+ _hidl_cb(static_cast<int32_t>(status), anwBuffer);
+ return Void();
+ }
+
+ Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) override {
+ return static_cast<int32_t>(mBase->setMaxDequeuedBufferCount(
+ static_cast<int>(maxDequeuedBuffers)));
+ }
+
+ Return<int32_t> setAsyncMode(bool async) override {
+ return static_cast<int32_t>(mBase->setAsyncMode(async));
+ }
+
+ Return<void> dequeueBuffer(
+ uint32_t width, uint32_t height,
+ ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage,
+ bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override {
+ int slot;
+ sp<Fence> fence;
+ ::android::FrameEventHistoryDelta outTimestamps;
+ status_t status = mBase->dequeueBuffer(
+ &slot, &fence, width, height,
+ static_cast<::android::PixelFormat>(format), usage, nullptr,
+ getFrameTimestamps ? &outTimestamps : nullptr);
+ hidl_handle tFence;
+ HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps;
+
+ native_handle_t* nh = nullptr;
+ if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ return Void();
+ }
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (getFrameTimestamps && !::android::conversion::wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output timestamps";
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ native_handle_delete(nh);
+ if (getFrameTimestamps) {
+ for (auto& nhA : nhAA) {
+ for (auto& handle : nhA) {
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> detachBuffer(int32_t slot) override {
+ return static_cast<int32_t>(mBase->detachBuffer(slot));
+ }
+
+ Return<void> detachNextBuffer(HGraphicBufferProducer::detachNextBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> outBuffer;
+ sp<Fence> outFence;
+ status_t status = mBase->detachNextBuffer(&outBuffer, &outFence);
+ AnwBuffer tBuffer;
+ hidl_handle tFence;
+
+ if (outBuffer == nullptr) {
+ LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output buffer";
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ return Void();
+ }
+ ::android::conversion::wrapAs(&tBuffer, *outBuffer);
+ native_handle_t* nh = nullptr;
+ if ((outFence != nullptr) && !::android::conversion::wrapAs(&tFence, &nh, *outFence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ Return<void> attachBuffer(const AnwBuffer& buffer, HGraphicBufferProducer::attachBuffer_cb _hidl_cb) override {
+ int outSlot;
+ sp<GraphicBuffer> lBuffer = new GraphicBuffer();
+ if (!::android::conversion::convertTo(lBuffer.get(), buffer)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - "
+ "Invalid input native window buffer";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), -1);
+ return Void();
+ }
+ status_t status = mBase->attachBuffer(&outSlot, lBuffer);
+
+ _hidl_cb(static_cast<int32_t>(status), static_cast<int32_t>(outSlot));
+ return Void();
+ }
+
+ Return<void> queueBuffer(
+ int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input,
+ HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override {
+ HGraphicBufferProducer::QueueBufferOutput tOutput;
+ BGraphicBufferProducer::QueueBufferInput lInput(
+ 0, false, HAL_DATASPACE_UNKNOWN,
+ ::android::Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ 0, ::android::Fence::NO_FENCE);
+ if (!::android::conversion::convertTo(&lInput, input)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+ "Invalid input";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+ return Void();
+ }
+ BGraphicBufferProducer::QueueBufferOutput lOutput;
+ status_t status = mBase->queueBuffer(
+ static_cast<int>(slot), lInput, &lOutput);
+
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+ "Invalid output";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> cancelBuffer(int32_t slot, const hidl_handle& fence) override {
+ sp<Fence> lFence = new Fence();
+ if (!::android::conversion::convertTo(lFence.get(), fence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - "
+ "Invalid input fence";
+ return static_cast<int32_t>(BAD_VALUE);
+ }
+ return static_cast<int32_t>(mBase->cancelBuffer(static_cast<int>(slot), lFence));
+ }
+
+ Return<void> query(int32_t what, HGraphicBufferProducer::query_cb _hidl_cb) override {
+ int lValue;
+ int lReturn = mBase->query(static_cast<int>(what), &lValue);
+ _hidl_cb(static_cast<int32_t>(lReturn), static_cast<int32_t>(lValue));
+ return Void();
+ }
+
+ Return<void> connect(const sp<HProducerListener>& listener,
+ int32_t api, bool producerControlledByApp,
+ HGraphicBufferProducer::connect_cb _hidl_cb) override {
+ sp<BProducerListener> lListener = listener == nullptr ?
+ nullptr : new LWProducerListener(listener);
+ BGraphicBufferProducer::QueueBufferOutput lOutput;
+ status_t status = mBase->connect(lListener,
+ static_cast<int>(api),
+ producerControlledByApp,
+ &lOutput);
+
+ HGraphicBufferProducer::QueueBufferOutput tOutput;
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::connect - "
+ "Invalid output";
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> disconnect(
+ int32_t api,
+ HGraphicBufferProducer::DisconnectMode mode) override {
+ return static_cast<int32_t>(mBase->disconnect(
+ static_cast<int>(api),
+ ::android::conversion::toGuiDisconnectMode(mode)));
+ }
+
+ Return<int32_t> setSidebandStream(const hidl_handle& stream) override {
+ return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
+ stream ? native_handle_clone(stream) : NULL, true)));
+ }
+
+ Return<void> allocateBuffers(
+ uint32_t width, uint32_t height,
+ ::android::hardware::graphics::common::V1_0::PixelFormat format,
+ uint32_t usage) override {
+ mBase->allocateBuffers(
+ width, height,
+ static_cast<::android::PixelFormat>(format),
+ usage);
+ return Void();
+ }
+
+ Return<int32_t> allowAllocation(bool allow) override {
+ return static_cast<int32_t>(mBase->allowAllocation(allow));
+ }
+
+ Return<int32_t> setGenerationNumber(uint32_t generationNumber) override {
+ return static_cast<int32_t>(mBase->setGenerationNumber(generationNumber));
+ }
+
+ Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override {
+ _hidl_cb(mBase->getConsumerName().string());
+ return Void();
+ }
+
+ Return<int32_t> setSharedBufferMode(bool sharedBufferMode) override {
+ return static_cast<int32_t>(mBase->setSharedBufferMode(sharedBufferMode));
+ }
+
+ Return<int32_t> setAutoRefresh(bool autoRefresh) override {
+ return static_cast<int32_t>(mBase->setAutoRefresh(autoRefresh));
+ }
+
+ Return<int32_t> setDequeueTimeout(int64_t timeoutNs) override {
+ return static_cast<int32_t>(mBase->setDequeueTimeout(timeoutNs));
+ }
+
+ Return<void> getLastQueuedBuffer(HGraphicBufferProducer::getLastQueuedBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> lOutBuffer = new GraphicBuffer();
+ sp<Fence> lOutFence = new Fence();
+ float lOutTransformMatrix[16];
+ status_t status = mBase->getLastQueuedBuffer(
+ &lOutBuffer, &lOutFence, lOutTransformMatrix);
+
+ AnwBuffer tOutBuffer;
+ if (lOutBuffer != nullptr) {
+ ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer);
+ }
+ hidl_handle tOutFence;
+ native_handle_t* nh = nullptr;
+ if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status),
+ tOutBuffer,
+ tOutFence,
+ hidl_array<float, 16>());
+ return Void();
+ }
+ hidl_array<float, 16> tOutTransformMatrix(lOutTransformMatrix);
+
+ _hidl_cb(static_cast<int32_t>(status), tOutBuffer, tOutFence, tOutTransformMatrix);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ Return<void> getFrameTimestamps(HGraphicBufferProducer::getFrameTimestamps_cb _hidl_cb) override {
+ ::android::FrameEventHistoryDelta lDelta;
+ mBase->getFrameTimestamps(&lDelta);
+
+ HGraphicBufferProducer::FrameEventHistoryDelta tDelta;
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - "
+ "Invalid output frame timestamps";
+ _hidl_cb(tDelta);
+ return Void();
+ }
+
+ _hidl_cb(tDelta);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<void> getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override {
+ uint64_t outId;
+ status_t status = mBase->getUniqueId(&outId);
+ _hidl_cb(static_cast<int32_t>(status), outId);
+ return Void();
+ }
+
+private:
+ sp<BGraphicBufferProducer> mBase;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
new file mode 100644
index 0000000..51dff5b
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+ HProducerListener;
+typedef ::android::IProducerListener
+ BProducerListener;
+using ::android::BnProducerListener;
+
+struct TWProducerListener : public HProducerListener {
+ sp<BProducerListener> mBase;
+ TWProducerListener(sp<BProducerListener> const& base);
+ Return<void> onBufferReleased() override;
+ Return<bool> needsReleaseNotify() override;
+};
+
+class LWProducerListener : public BnProducerListener {
+public:
+ sp<HProducerListener> mBase;
+ LWProducerListener(sp<HProducerListener> const& base);
+ void onBufferReleased() override;
+ bool needsReleaseNotify() override;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 94b669d..0ee9bff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -561,7 +561,9 @@
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/,
- const cached_buffer_t& /*cachedBuffer*/) override {}
+ const cached_buffer_t& /*cachedBuffer*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
+ }
void bootFinished() override {}
bool authenticateSurfaceTexture(
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
index 45eab00..63411b9 100644
--- a/libs/incidentcompanion/Android.bp
+++ b/libs/incidentcompanion/Android.bp
@@ -35,8 +35,12 @@
},
srcs: [
":incidentcompanion_aidl",
+ "src/IncidentManager.cpp",
],
- export_include_dirs: ["binder"],
+ export_include_dirs: [
+ "binder",
+ "include",
+ ],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
index 6bf98d2..98c2814 100644
--- a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IIncidentAuthListener;
+import android.os.IncidentManager;
/**
* Helper service for incidentd and dumpstated to provide user feedback
@@ -35,6 +36,10 @@
* returns via the callback whether the application should be trusted. It is up
* to the caller to actually implement the restriction to take or not take
* the incident or bug report.
+ * @param receiverClass The class that will be the eventual broacast receiver for the
+ * INCIDENT_REPORT_READY message. Used as part of the id in incidentd.
+ * @param reportId The incident report ID. Incidentd should call with this parameter, but
+ * everyone else should pass null or empty string.
* @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
* a dialog will be shown as a notification.
* @param callback Interface to receive results. The results may not come back for
@@ -44,6 +49,7 @@
* to send their report.
*/
oneway void authorizeReport(int callingUid, String callingPackage,
+ String receiverClass, String reportId,
int flags, IIncidentAuthListener callback);
/**
@@ -52,6 +58,11 @@
oneway void cancelAuthorization(IIncidentAuthListener callback);
/**
+ * Send the report ready broadcast on behalf of incidentd.
+ */
+ oneway void sendReportReadyBroadcast(String pkg, String cls);
+
+ /**
* Return the list of pending approvals.
*/
List<String> getPendingReports();
@@ -69,4 +80,26 @@
* @param uri the report.
*/
void denyReport(String uri);
+
+ /**
+ * List the incident reports for the given ComponentName. The receiver
+ * must be for a package inside the caller.
+ */
+ List<String> getIncidentReportList(String pkg, String cls);
+
+ /**
+ * Get the IncidentReport object.
+ */
+ IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with this incident report and it can be deleted.
+ */
+ void deleteIncidentReports(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with all incident reports from this package.
+ * Especially useful for testing.
+ */
+ void deleteAllIncidentReports(String pkg);
}
diff --git a/libs/incidentcompanion/binder/android/os/IncidentManager.aidl b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
new file mode 100644
index 0000000..d17823e
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package android.os;
+
+parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h";
+
diff --git a/libs/incidentcompanion/include/android/os/IncidentManager.h b/libs/incidentcompanion/include/android/os/IncidentManager.h
new file mode 100644
index 0000000..07b6d82
--- /dev/null
+++ b/libs/incidentcompanion/include/android/os/IncidentManager.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+class IncidentManager : public virtual RefBase {
+public:
+ class IncidentReport : public Parcelable {
+ public:
+ IncidentReport();
+ virtual ~IncidentReport();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ void setTimestampNs(int64_t val) { mTimestampNs = val; }
+ int64_t getTimestampNs() const { return mTimestampNs; }
+ int64_t getTimestampMs() const { return mTimestampNs / 1000000; }
+
+ void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; }
+ // This was accidentally published as a long in the java api.
+ int64_t getPrivacyPolicy() const { return mPrivacyPolicy; }
+ // Dups the fd, so you retain ownership of the original one. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ status_t setFileDescriptor(int fd);
+
+ // Does not dup the fd, so ownership is passed to this object. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ void takeFileDescriptor(int fd);
+
+ // Returns the fd, which you don't own. Call dup if you need a copy.
+ int getFileDescriptor() const { return mFileDescriptor; }
+
+ private:
+ int64_t mTimestampNs;
+ int32_t mPrivacyPolicy;
+ int mFileDescriptor;
+ };
+
+
+private:
+ // Not implemented for now.
+ IncidentManager();
+ virtual ~IncidentManager();
+};
+}
+}
+
diff --git a/libs/incidentcompanion/src/IncidentManager.cpp b/libs/incidentcompanion/src/IncidentManager.cpp
new file mode 100644
index 0000000..f7c8a5e
--- /dev/null
+++ b/libs/incidentcompanion/src/IncidentManager.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2016, 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 <android/os/IncidentManager.h>
+
+namespace android {
+namespace os {
+
+// ============================================================
+IncidentManager::IncidentReport::IncidentReport()
+ :mTimestampNs(0),
+ mPrivacyPolicy(0),
+ mFileDescriptor(-1) {
+}
+
+IncidentManager::IncidentReport::~IncidentReport() {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+}
+
+status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const {
+ status_t err;
+
+ err = out->writeInt64(mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+
+ err = out->writeInt32(mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (mFileDescriptor >= 0) {
+ err = out->writeInt32(1);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeDupParcelFileDescriptor(mFileDescriptor);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ } else {
+ err = out->writeInt32(0);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) {
+ status_t err;
+ int32_t hasField;
+
+ err = in->readInt64(&mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&hasField);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (hasField) {
+ int fd = in->readParcelFileDescriptor();
+ if (fd >= 0) {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ return NO_ERROR;
+}
+
+void IncidentManager::IncidentReport::takeFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = fd;
+ }
+}
+
+// ============================================================
+IncidentManager::~IncidentManager() {
+}
+
+}
+}
+
+
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index 7936f50..1d9f8a7 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -24,19 +24,10 @@
// --- InputApplicationHandle ---
-InputApplicationHandle::InputApplicationHandle() :
- mInfo(nullptr) {
+InputApplicationHandle::InputApplicationHandle() {
}
InputApplicationHandle::~InputApplicationHandle() {
- delete mInfo;
-}
-
-void InputApplicationHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = nullptr;
- }
}
InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index e13b40e..2a5a604 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -27,12 +27,16 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/Trace.h>
-#include <binder/Parcel.h>
#include <input/InputTransport.h>
+using android::base::StringPrintf;
+
namespace android {
// Socket buffer size. The default is typically about 128KB, which is much larger than
@@ -420,6 +424,11 @@
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
+ mChannel->getName().c_str(), keyCode);
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
@@ -472,6 +481,12 @@
uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
+ mChannel->getName().c_str(), action);
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index bf80481..9bd3095 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -96,7 +96,12 @@
int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
int32_t fence, const ARect* rect, void** outVirtualAddress,
int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
- if (!buffer) return BAD_VALUE;
+ if (outBytesPerPixel) *outBytesPerPixel = -1;
+ if (outBytesPerStride) *outBytesPerStride = -1;
+
+ if (!buffer) {
+ return BAD_VALUE;
+ }
if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
@@ -127,15 +132,19 @@
} else {
bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
}
- int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, outBytesPerPixel, outBytesPerStride);
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride);
// if hardware returns -1 for bytes per pixel or bytes per stride, we fail
// and unlock the buffer
- if (*outBytesPerPixel == -1 || *outBytesPerStride == -1) {
+ if (bytesPerPixel == -1 || bytesPerStride == -1) {
gbuffer->unlock();
return INVALID_OPERATION;
}
+ if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel;
+ if (outBytesPerStride) *outBytesPerStride = bytesPerStride;
return result;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index f651309..faf6d2a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -248,7 +248,8 @@
bool useContextPriority = extensions.hasContextPriority() &&
(featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
EGLContext protectedContext = EGL_NO_CONTEXT;
- if (extensions.hasProtectedContent()) {
+ if ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) &&
+ extensions.hasProtectedContent()) {
protectedContext = createEglContext(display, config, nullptr, useContextPriority,
Protection::PROTECTED);
ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
@@ -595,11 +596,7 @@
}
void GLESRenderEngine::setScissor(const Rect& region) {
- // Invert y-coordinate to map to GL-space.
- int32_t canvasHeight = mFboHeight;
- int32_t glBottom = canvasHeight - region.bottom;
-
- glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+ glScissor(region.left, region.top, region.getWidth(), region.getHeight());
glEnable(GL_SCISSOR_TEST);
}
@@ -630,23 +627,26 @@
}
}
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> bufferFence) {
+status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return cacheExternalTextureBufferLocked(buffer);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
}
-status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
- sp<GraphicBuffer> buffer,
- sp<Fence> bufferFence) {
+status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
if (buffer == nullptr) {
return BAD_VALUE;
}
- ATRACE_CALL();
- auto cachedImage = mImageCache.find(buffer->getId());
- if (cachedImage != mImageCache.end()) {
- bindExternalTextureImage(texName, *cachedImage->second);
+ ATRACE_CALL();
+
+ if (mImageCache.count(buffer->getId()) > 0) {
return NO_ERROR;
}
@@ -658,11 +658,32 @@
ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
buffer->getPixelFormat());
+ return NO_INIT;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+
+ return NO_ERROR;
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
+ ATRACE_CALL();
+ status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
+
+ if (cacheResult != NO_ERROR) {
+ return cacheResult;
+ }
+
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
bindExternalTextureImage(texName, *createImage());
return NO_INIT;
}
- bindExternalTextureImage(texName, *newImage);
+ bindExternalTextureImage(texName, *cachedImage->second);
// Wait for the new buffer to be ready.
if (bufferFence != nullptr && bufferFence->isValid()) {
@@ -684,7 +705,6 @@
}
}
}
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
return NO_ERROR;
}
@@ -719,6 +739,59 @@
return cropWin;
}
+void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
+ const LayerSettings& layer, const Mesh& mesh) {
+ // We separate the layer into 3 parts essentially, such that we only turn on blending for the
+ // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
+ FloatRect bounds = layer.geometry.roundedCornersCrop;
+
+ // Firstly, we need to convert the coordination from layer native coordination space to
+ // device coordination space.
+ const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1],
+ rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]);
+
+ // Secondly, if the display is rotated, we need to undo the rotation on coordination and
+ // align the (left, top) and (right, bottom) coordination with the device coordination
+ // space.
+ switch (display.orientation) {
+ case ui::Transform::ROT_90:
+ std::swap(bounds.left, bounds.right);
+ break;
+ case ui::Transform::ROT_180:
+ std::swap(bounds.left, bounds.right);
+ std::swap(bounds.top, bounds.bottom);
+ break;
+ case ui::Transform::ROT_270:
+ std::swap(bounds.top, bounds.bottom);
+ break;
+ default:
+ break;
+ }
+
+ // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
+ // and the middle part without rounded corners.
+ const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
+ setScissor(topRect);
+ drawMesh(mesh);
+ const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
+ setScissor(bottomRect);
+ drawMesh(mesh);
+
+ // The middle part of the layer can turn off blending.
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ disableScissor();
+}
+
status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
ATRACE_CALL();
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
@@ -738,8 +811,6 @@
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
- mFboHeight = glFramebuffer->getBufferHeight();
-
uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
@@ -750,7 +821,6 @@
void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
ATRACE_CALL();
- mFboHeight = 0;
// back to main framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -785,12 +855,14 @@
return success;
}
EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
- bool isProtected) {
+ bool isProtected,
+ bool useFramebufferCache) {
sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
- uint64_t bufferId = graphicBuffer->getId();
- for (const auto& image : mFramebufferImageCache) {
- if (image.first == bufferId) {
- return image.second;
+ if (useFramebufferCache) {
+ for (const auto& image : mFramebufferImageCache) {
+ if (image.first == graphicBuffer->getId()) {
+ return image.second;
+ }
}
}
EGLint attributes[] = {
@@ -800,13 +872,15 @@
};
EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
nativeBuffer, attributes);
- if (image != EGL_NO_IMAGE_KHR) {
- if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
+ if (useFramebufferCache) {
+ if (image != EGL_NO_IMAGE_KHR) {
+ if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
}
- mFramebufferImageCache.push_back({bufferId, image});
}
return image;
}
@@ -814,7 +888,8 @@
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* const buffer,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
ATRACE_CALL();
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
@@ -834,7 +909,7 @@
{
std::lock_guard<std::mutex> lock(mRenderingMutex);
- BindNativeBufferAsFramebuffer fbo(*this, buffer);
+ BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
if (fbo.getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
@@ -917,7 +992,14 @@
}
setSourceDataSpace(layer.sourceDataspace);
- drawMesh(mesh);
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
// Cleanup if there's a buffer source
if (layer.source.buffer.buffer != nullptr) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index efb6ef0..de793c2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -69,13 +69,12 @@
void clearWithColor(float red, float green, float blue, float alpha) override;
void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) override;
- void setScissor(const Rect& region) override;
- void disableScissor() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence)
- EXCLUDES(mRenderingMutex);
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
@@ -85,14 +84,16 @@
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override;
+ ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence)
+ EXCLUDES(mRenderingMutex) override;
// internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
EGLConfig getEGLConfig() const { return mEGLConfig; }
// Creates an output image for rendering to
- EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
+ EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ bool useFramebufferCache);
// Test-only methods
// Returns true iff mImageCache contains an image keyed by bufferId
@@ -141,6 +142,8 @@
Protection protection);
static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
+ void setScissor(const Rect& region);
+ void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
// A data space is considered HDR data space if it has BT2020 color space
@@ -156,6 +159,13 @@
// coordinates for the mesh.
FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+ // We do a special handling for rounded corners when it's possible to turn off blending
+ // for the majority of the layer. The rounded corners needs to turn on blending such that
+ // we can set the alpha value correctly, however, only the corners need this, and since
+ // blending is an expensive operation, we want to turn off blending when it's not necessary.
+ void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
+ const Mesh& mesh);
+
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
@@ -185,7 +195,6 @@
bool mInProtectedContext = false;
// If set to true, then enables tracing flush() and finish() to systrace.
bool mTraceGpuCompletion = false;
- int32_t mFboHeight = 0;
// Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
// the last recently used buffer should be kicked out.
uint32_t mFramebufferImageCacheSize = 0;
@@ -213,8 +222,12 @@
// See bindExternalTextureBuffer above, but requiring that mRenderingMutex
// is held.
- status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> fence) REQUIRES(mRenderingMutex);
+ status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) REQUIRES(mRenderingMutex);
+ // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
+ // is held.
+ status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
+ REQUIRES(mRenderingMutex);
std::unique_ptr<Framebuffer> mDrawingBuffer;
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index c45598c..dacf8d3 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -41,19 +41,25 @@
glDeleteTextures(1, &mTextureName);
}
-bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) {
ATRACE_CALL();
if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ if (!usingFramebufferCache) {
+ eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+ }
mEGLImage = EGL_NO_IMAGE_KHR;
mBufferWidth = 0;
mBufferHeight = 0;
}
if (nativeBuffer) {
- mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected);
+ mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected,
+ useFramebufferCache);
if (mEGLImage == EGL_NO_IMAGE_KHR) {
return false;
}
+ usingFramebufferCache = useFramebufferCache;
mBufferWidth = nativeBuffer->width;
mBufferHeight = nativeBuffer->height;
}
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 1289fbf..b7650bb 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -35,7 +35,8 @@
explicit GLFramebuffer(GLESRenderEngine& engine);
~GLFramebuffer() override;
- bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override;
+ bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) override;
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
@@ -46,6 +47,7 @@
GLESRenderEngine& mEngine;
EGLDisplay mEGLDisplay;
EGLImageKHR mEGLImage;
+ bool usingFramebufferCache = false;
uint32_t mTextureName, mFramebufferName;
int32_t mBufferHeight = 0;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index af8de23..9c9884a 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -20,6 +20,7 @@
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Transform.h>
namespace android {
namespace renderengine {
@@ -56,6 +57,9 @@
// globalTransform, so that it will be in the same coordinate space as the
// rendered layers.
Region clearRegion = Region::INVALID_REGION;
+
+ // The orientation of the physical display.
+ uint32_t orientation = ui::Transform::ROT_0;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h
index 66eb9ef..6511127 100644
--- a/libs/renderengine/include/renderengine/Framebuffer.h
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -27,7 +27,8 @@
public:
virtual ~Framebuffer() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) = 0;
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) = 0;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ab34274..e707004 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -63,6 +63,9 @@
enum FeatureFlag {
USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color
USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
+
+ // Create a protected context when if possible
+ ENABLE_PROTECTED_CONTEXT = 1 << 2,
};
static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags,
@@ -103,14 +106,17 @@
virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) = 0;
-
- virtual void setScissor(const Rect& region) = 0;
- virtual void disableScissor() = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
- virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> fence) = 0;
+ // Legacy public method used by devices that don't support native fence
+ // synchronization in their GPU driver, as this method provides implicit
+ // synchronization for latching buffers.
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) = 0;
+ // Caches Image resources for this buffer, but does not bind the buffer to
+ // a particular texture.
+ virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
// Removes internal resources referenced by the bufferId. This method should be
// invoked when the caller will no longer hold a reference to a GraphicBuffer
// and needs to clean up its resources.
@@ -173,6 +179,9 @@
// @param layers The layers to draw onto the display, in Z-order.
// @param buffer The buffer which will be drawn to. This buffer will be
// ready once drawFence fires.
+ // @param useFramebufferCache True if the framebuffer cache should be used.
+ // If an implementation does not cache output framebuffers, then this
+ // parameter does nothing.
// @param bufferFence Fence signalling that the buffer is ready to be drawn
// to.
// @param drawFence A pointer to a fence, which will fire when the buffer
@@ -183,8 +192,8 @@
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
- ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) = 0;
+ ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
protected:
// Gets a framebuffer to render to. This framebuffer may or may not be
@@ -198,14 +207,16 @@
class BindNativeBufferAsFramebuffer {
public:
- BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
+ BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
+ const bool useFramebufferCache)
: mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
- mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected())
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+ useFramebufferCache)
? mEngine.bindFrameBuffer(mFramebuffer)
: NO_MEMORY;
}
~BindNativeBufferAsFramebuffer() {
- mFramebuffer->setNativeWindowBuffer(nullptr, false);
+ mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
mEngine.unbindFrameBuffer(mFramebuffer);
}
status_t getStatus() const { return mStatus; }
diff --git a/libs/renderengine/include/renderengine/mock/Framebuffer.h b/libs/renderengine/include/renderengine/mock/Framebuffer.h
index 7695885..dfb6a4e 100644
--- a/libs/renderengine/include/renderengine/mock/Framebuffer.h
+++ b/libs/renderengine/include/renderengine/mock/Framebuffer.h
@@ -28,7 +28,7 @@
Framebuffer();
~Framebuffer() override;
- MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool));
+ MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool));
};
} // namespace mock
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index ddf7420..e33bcfd 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -48,12 +48,12 @@
bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
MOCK_METHOD4(clearWithColor, void(float, float, float, float));
MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
- MOCK_METHOD1(setScissor, void(const Rect&));
- MOCK_METHOD0(disableScissor, void());
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
- MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>));
+ MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&));
+ MOCK_METHOD3(bindExternalTextureBuffer,
+ status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
MOCK_CONST_METHOD0(checkErrors, void());
MOCK_METHOD4(setViewportAndProjection,
@@ -79,9 +79,9 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD5(drawLayers,
+ MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
- ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*));
+ ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
};
} // namespace mock
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 8c93cf4..7acaecf 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -118,7 +118,7 @@
void invokeDraw(renderengine::DisplaySettings settings,
std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
+ status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
base::unique_fd(), &fence);
sCurrentBuffer = buffer;
@@ -770,7 +770,7 @@
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layers.push_back(layer);
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);
+ status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
ASSERT_EQ(BAD_VALUE, status);
}
@@ -787,13 +787,33 @@
layer.alpha = 1.0;
layers.push_back(layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
+ status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
+TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(layer);
+
+ status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
+ base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
fillRedBuffer<ColorSourceVariant>();
}
@@ -1003,4 +1023,18 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
+ status_t result = sRE->cacheExternalTextureBuffer(nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint64_t bufferId = buf->getId();
+ sRE->cacheExternalTextureBuffer(buf);
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 40df260..3fc6a2d 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -194,6 +194,7 @@
if (inFormat != format) return true;
if (inLayerCount != layerCount) return true;
if ((usage & inUsage) != inUsage) return true;
+ if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true;
return false;
}
@@ -394,7 +395,7 @@
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
#ifndef LIBUI_IN_VNDK
if (mBufferHubBuffer != nullptr) {
- return flattenBufferHubBuffer(buffer, size, fds, count);
+ return flattenBufferHubBuffer(buffer, size);
}
#endif
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
@@ -437,6 +438,11 @@
status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count) {
+ // Check if size is not smaller than buf[0] is supposed to take.
+ if (size < sizeof(int)) {
+ return NO_MEMORY;
+ }
+
int const* buf = static_cast<int const*>(buffer);
// NOTE: it turns out that some media code generates a flattened GraphicBuffer manually!!!!!
@@ -450,7 +456,7 @@
flattenWordCount = 12;
} else if (buf[0] == 'BHBB') { // BufferHub backed buffer.
#ifndef LIBUI_IN_VNDK
- return unflattenBufferHubBuffer(buffer, size, fds, count);
+ return unflattenBufferHubBuffer(buffer, size);
#else
return BAD_TYPE;
#endif
@@ -561,8 +567,7 @@
}
#ifndef LIBUI_IN_VNDK
-status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size, int*& fds,
- size_t& count) const {
+status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const {
sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate();
if (tokenHandle == nullptr || tokenHandle->handle() == nullptr ||
tokenHandle->handle()->numFds != 0) {
@@ -586,14 +591,10 @@
memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int));
buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber);
- // Do not pass fds if it is BufferHubBuffer backed GraphicBuffer. Not modifying fds or count.
- fds += 0;
- count -= 0;
return NO_ERROR;
}
-status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size, int const*& fds,
- size_t& count) {
+status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) {
const int* buf = static_cast<const int*>(buffer);
int numIntsInToken = buf[1];
// Size needed for one label, one number of ints inside the token, one generation number and
@@ -627,10 +628,6 @@
mBufferId = bufferHubBuffer->id();
mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
- // BufferHubBuffer backed GraphicBuffer does not have flattened handle. Not modifying fds or
- // count.
- fds += 0;
- count -= 0;
return NO_ERROR;
}
diff --git a/libs/ui/include/ui/BufferQueueDefs.h b/libs/ui/include/ui/BufferQueueDefs.h
index 56de181..0fecda9 100644
--- a/libs/ui/include/ui/BufferQueueDefs.h
+++ b/libs/ui/include/ui/BufferQueueDefs.h
@@ -23,6 +23,16 @@
// Attempts at runtime to increase the number of buffers past this
// will fail.
static constexpr int NUM_BUFFER_SLOTS = 64;
+
+ enum {
+ // A flag returned by dequeueBuffer when the client needs to call
+ // requestBuffer immediately thereafter.
+ BUFFER_NEEDS_REALLOCATION = 0x1,
+ // A flag returned by dequeueBuffer when all mirrored slots should be
+ // released by the client. This flag should always be processed first.
+ RELEASE_ALL_BUFFERS = 0x2,
+ };
+
} // namespace BufferQueueDefs
} // namespace android
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index c137860..c195342 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -302,7 +302,7 @@
#ifndef LIBUI_IN_VNDK
// Flatten this GraphicBuffer object if backed by BufferHubBuffer.
- status_t flattenBufferHubBuffer(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const;
// Unflatten into BufferHubBuffer backed GraphicBuffer.
// Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a
@@ -310,8 +310,7 @@
// to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the
// token. Race condition occurs between the invalidation of the token in bufferhub process and
// process/thread B trying to unflatten and import the buffer with that token.
- status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size, int const*& fds,
- size_t& count);
+ status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size);
// Stores a BufferHubBuffer that handles buffer signaling, identification.
std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index efc1a80..0c73a72 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <sys/epoll.h>
+#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
#include <gmock/gmock.h>
@@ -40,6 +41,7 @@
using ::android::BufferHubDefs::isClientPosted;
using ::android::BufferHubDefs::isClientReleased;
using ::android::BufferHubDefs::kMetadataHeaderSize;
+using ::android::frameworks::bufferhub::V1_0::IBufferHub;
using ::testing::IsNull;
using ::testing::NotNull;
@@ -54,7 +56,20 @@
class BufferHubBufferTest : public ::testing::Test {
protected:
- void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); }
+ void SetUp() override {
+ android::hardware::ProcessState::self()->startThreadPool();
+
+ if (!BufferHubServiceRunning()) {
+ // TODO(b/112940221): Enforce the test cross all devices once BufferHub lands in Android
+ // R for all Android varieties.
+ GTEST_SKIP() << "Skip test as the BufferHub service is not running.";
+ }
+ }
+
+ bool BufferHubServiceRunning() {
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ return bufferhub.get() != nullptr;
+ }
};
bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) {
@@ -67,6 +82,13 @@
protected:
void SetUp() override {
BufferHubBufferTest::SetUp();
+
+ if (IsSkipped()) {
+ // If the base class' SetUp() stated the test should be skipped, we should short
+ // circuit this sub-class' logic.
+ return;
+ }
+
CreateTwoClientsOfABuffer();
}
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index fa92830..6d202ae 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -59,9 +59,6 @@
"libbufferhub_headers",
"libnativebase_headers",
],
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
cc_test {
@@ -70,7 +67,4 @@
shared_libs: sharedLibraries,
header_libs: headerLibraries,
name: "buffer_hub-test",
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 20894e3..9f72c05 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -59,9 +59,6 @@
static_libs: staticLibraries,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 357dffe..3260447 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -49,9 +49,6 @@
"-g",
],
name: "dvr_api-test",
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
cc_test {
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index ed72577..7b3717e 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -28,6 +28,8 @@
namespace {
+using ::testing::Test;
+
DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
DvrSurfaceAttribute attribute;
attribute.key = key;
@@ -141,6 +143,23 @@
return {UniqueDvrWriteBufferQueue(queue)};
}
+Status<std::vector<uint8_t>> GetConfigData(int config_type) {
+ uint8_t* data = nullptr;
+ size_t data_size = 0;
+ int error = dvrConfigurationDataGet(config_type, &data, &data_size);
+ if (error < 0) {
+ return ErrorStatus(-error);
+ }
+
+ if (!data || data_size == 0) {
+ return ErrorStatus(EINVAL);
+ }
+ std::vector<uint8_t> data_result(data, data + data_size);
+ dvrConfigurationDataDestroy(data);
+ std::string s(data, data + data_size);
+ return {std::move(data_result)};
+}
+
class TestDisplayManager {
public:
TestDisplayManager(UniqueDvrDisplayManager display_manager,
@@ -275,23 +294,6 @@
return {std::move(queue_ids)};
}
- Status<std::vector<uint8_t>> GetConfigData(int config_type) {
- uint8_t* data = nullptr;
- size_t data_size = 0;
- int error = dvrConfigurationDataGet(config_type, &data, &data_size);
- if (error < 0) {
- return ErrorStatus(-error);
- }
-
- if (!data || data_size == 0) {
- return ErrorStatus(EINVAL);
- }
- std::vector<uint8_t> data_result(data, data + data_size);
- dvrConfigurationDataDestroy(data);
- std::string s(data, data + data_size);
- return {std::move(data_result)};
- }
-
private:
UniqueDvrDisplayManager display_manager_;
UniqueDvrSurfaceState surface_state_;
@@ -303,9 +305,17 @@
void operator=(const TestDisplayManager&) = delete;
};
-class DvrDisplayManagerTest : public ::testing::Test {
+class DvrDisplayManagerTest : public Test {
protected:
void SetUp() override {
+ // dvr display manager test doesn't apply to standalone vr devices because
+ // tests cannot create display manager client on these devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ GTEST_SKIP()
+ << "All tests in DvrDisplayManagerTest test case are skipped "
+ "because the device boot to VR.";
+ }
+
int ret;
DvrDisplayManager* display_manager;
DvrSurfaceState* surface_state;
@@ -429,7 +439,7 @@
#if 0
// Verify utility predicate/macro functionality. This section is commented out
// because it is designed to fail in some cases to validate the helpers.
-TEST_F(DvrDisplayManagerTest, ExpectVoid) {
+TEST_F(Test, ExpectVoid) {
Status<void> status_error{ErrorStatus{EINVAL}};
Status<void> status_ok{};
@@ -444,7 +454,7 @@
EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
}
-TEST_F(DvrDisplayManagerTest, ExpectInt) {
+TEST_F(Test, ExpectInt) {
Status<int> status_error{ErrorStatus{EINVAL}};
Status<int> status_ok{10};
@@ -480,11 +490,6 @@
#endif
TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -524,11 +529,6 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -768,11 +768,6 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
@@ -841,11 +836,6 @@
}
TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
@@ -884,16 +874,17 @@
dvrWriteBufferDestroy(buffer);
}
-TEST_F(DvrDisplayManagerTest, ConfigurationData) {
- // TODO(hendrikw): Move this out of the display manager tests.
- auto data1 = manager_->GetConfigData(-1);
+TEST_F(Test, ConfigurationData) {
+ // TODO(hendrikw): Move this test and GetConfigData helper function out of the
+ // display manager tests.
+ auto data1 = GetConfigData(-1);
ASSERT_STATUS_ERROR(data1);
const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
// This should be run on devices with and without built in metrics.
bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
- auto data2 = manager_->GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
+ auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
if (has_metric) {
ASSERT_STATUS_OK(data2);
ASSERT_NE(0u, data2.get().size());
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index c884cb3..410e234 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -33,7 +33,4 @@
"-Werror",
],
name: "vrflinger_test",
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md
new file mode 100644
index 0000000..bfc44db
--- /dev/null
+++ b/opengl/libs/EGL/GLES_layers.md
@@ -0,0 +1,258 @@
+# GLES Layers
+
+## EGL Loader Initialization
+After standard entrypoints have all been populated unmodified, a GLES LayerLoader will be instantiated. If debug layers are enabled, the LayerLoader will scan specified directories for layers, just like the Vulkan loader does.
+
+If layering is enabled, the loader will search for and enumerate a specified layer list. The layer list will be specified by colon separated filenames (see [Enabling layers](#Enabling-layers) below).
+
+The layers will be traversed in the order they are specified, so the first layer will be directly below the application. For each layer, it will track two entrypoints from the layer. `AndroidGLESLayer_Initialize` and `AndroidGLESLayer_GetProcAddress`.
+```cpp
+typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
+```
+
+`AndroidGLESLayer_Initialize` is a new function that provides an identifier for the layer to use (layer_id) and an entrypoint that can be called to look up functions below the layer. The entrypoint can be used like so:
+```cpp
+const char* func = "eglFoo";
+void* gpa = get_next_layer_proc_address(layer_id, func);
+```
+
+Note that only GLES2+ entrypoints will be provided. If a layer tries to make independent GLES 1.x calls, they will be routed to GLES2+ libraries, which may not behave as expected. Application calls to 1.x will not be affected.
+
+AndroidGLESLayer_GetProcAddress is a new function designed for this layering system. It takes the address of the next call in the chain that the layer should call when finished. If there is only one layer, next will point directly to the driver for most functions.
+```cpp
+void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
+```
+
+For each layer found, the GLES LayerLoader will call `AndroidGLESLayer_Initialize`, and then walk libEGL’s function lists and call `AndroidGLESLayer_GetProcAddress` for all known functions. The layer can track that next address with any means it wants. If the layer does not intercept the function, `AndroidGLESLayer_GetProcAddress` must return the same function address it was passed. The LayerLoader will then update the function hook list to point to the layer’s entrypoint.
+
+The layers are not required to do anything with the info provided by `AndroidGLESLayer_Initialize` or get_next_layer_proc_address, but providing them makes it easier for existing layers (like GAPID and RenderDoc) to support Android. That way a layer can look up functions independently (i.e. not wait for calls to `AndroidGLESLayer_GetProcAddress`). Layers must be sure to use gen_next_layer_proc_address if they look up function calls instead of eglGetProcAddress or they will not get an accurate answer. eglGetProcAddress must be passed down the chain to the platform.
+
+## Placing layers
+
+Where layers can be found, in order of priority
+ 1. System location for root
+ This requires root access
+ ```bash
+ adb root
+ adb disable-verity
+ adb reboot
+ adb root
+ adb shell setenforce 0
+ adb shell mkdir -p /data/local/debug/gles
+ adb push <layer>.so /data/local/debug/gles/
+ ```
+
+ 2. Application's base directory
+ Target application must be debuggable, or you must have root access:
+ ```bash
+ adb push libGLTrace.so /data/local/tmp
+ adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so .
+ adb shell run-as com.android.gl2jni ls | grep libGLTrace
+ libGLTrace.so
+ ```
+
+ 3. External APK
+ Determine the ABI of your target application, then install an APK containing the layers you wish to load:
+ ```bash
+ adb install --abi armeabi-v7a layers.apk
+ ```
+
+ 4. In the target application's APK
+
+## Enabling layers
+
+### Per application
+Note these settings will persist across reboots:
+```bash
+# Enable layers
+adb shell settings put global enable_gpu_debug_layers 1
+
+# Specify target application
+adb shell settings put global gpu_debug_app <package_name>
+
+# Specify layer list (from top to bottom)
+adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN>
+
+# Specify a package to search for layers
+adb shell settings put global gpu_debug_layer_app <layer_package>
+```
+To disable the per-app layers:
+```
+adb shell settings delete global enable_gpu_debug_layers
+adb shell settings delete global gpu_debug_app
+adb shell settings delete global gpu_debug_layers_gles
+adb shell settings delete global gpu_debug_layer_app
+```
+
+### Globally
+These will be cleared on reboot:
+```bash
+# This will attempt to load layers for all applications, including native executables
+adb shell setprop debug.gles.layers <layer1:layer2:layerN>
+```
+
+
+## Creating a layer
+
+Layers must expose the following two functions described above:
+```cpp
+AndroidGLESLayer_Initialize
+AndroidGLESLayer_GetProcAddress
+```
+
+For a simple layer that just wants to intercept a handful of functions, a passively initialized layer is the way to go. It can simply wait for the EGL Loader to initialize the function it cares about. See below for an example of creating a passive layer.
+
+For more formalized layers that need to fully initialize up front, or layers that needs to look up extensions not known to the EGL loader, active layer initialization is the way to go. The layer can utilize get_next_layer_proc_address provided by `AndroidGLESLayer_Initialize` to look up a function at any time. The layer must still respond to `AndroidGLESLayer_GetProcAddress` requests from the loader so the platform knows where to route calls. See below for an example of creating an active layer.
+
+### Example Passive Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ // This function is purposefully empty, since this layer does not proactively
+ // look up any entrypoints
+ }
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ funcMap[std::string(funcName)] = next;
+ return entry;
+ }
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+### Example Active Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+
+ // Note: This is where the layer would populate its function map with all the
+ // functions it cares about
+ const char* func = “eglChooseConfig”;
+ funcMap[func] = get_next_layer_proc_address(layer_id, func);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ return entry;
+ }
+
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+## Caveats
+Only supports GLES 2.0+.
+
+When layering is enabled, GLES 1.x exclusive functions will continue to route to GLES 1.x drivers. But functions shared with GLES 2.0+ (like glGetString) will be routed to 2.0+ drivers, which can cause confusion.
+
+## FAQ
+ - Who can use layers?
+ - GLES Layers can be loaded by any debuggable application, or for any application if you have root access
+ - How do we know if layers are working on a device?
+ - This feature is backed by Android CTS, so you can run `atest CtsGpuToolsHostTestCases`
+ - How does a app determine if this feature is supported?
+ - There are two ways. First you can check against the version of Android.
+ ```bash
+ # Q is the first that will support this, so look for `Q` or 10 for release
+ adb shell getprop ro.build.version.sdk
+ # Or look for the SDK version, which should be 29 for Q
+ adb shell getprop ro.build.version.sdk
+ ```
+ - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system:
+ ```cpp
+ std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos)
+ {
+ // Layers are supported!
+ }
+ ```
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 54fa89f..12947b2 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -340,6 +340,17 @@
mVersionString = sVersionString15;
cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
} else if ((cnx->major == 1) && (cnx->minor == 4)) {
+ /* Querying extension strings for type Client */
+ std::string typesExtString;
+ static const char* clientExtensions =
+ cnx->egl.eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (clientExtensions != nullptr && strlen(clientExtensions) > 0) {
+ typesExtString.append(clientExtensions);
+ typesExtString.append(" ");
+ }
+
+ /* Adding extension strings for type Display */
+ typesExtString.append(disp.queryString.extensions);
mVersionString = sVersionString14;
// Extensions needed for an EGL 1.4 implementation to be
// able to support EGL 1.5 functionality
@@ -356,9 +367,9 @@
};
bool extensionsFound = true;
for (const auto& name : egl15extensions) {
- extensionsFound &= findExtension(disp.queryString.extensions, name);
+ extensionsFound &= findExtension(typesExtString.c_str(), name);
ALOGV("Extension %s: %s", name,
- findExtension(disp.queryString.extensions, name) ? "Found" : "Missing");
+ findExtension(typesExtString.c_str(), name) ? "Found" : "Missing");
}
// NOTE: From the spec:
// Creation of fence sync objects requires support from the bound
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index f936ac0..ac01dc8 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -37,9 +37,8 @@
// 2. If none enabled, check system properties
//
// - Layer initializing -
-// TODO: ADD DETAIL ABOUT NEW INTERFACES
-// - InitializeLayer (provided by layer, called by loader)
-// - GetLayerProcAddress (provided by layer, called by loader)
+// - AndroidGLESLayer_Initialize (provided by layer, called by loader)
+// - AndroidGLESLayer_GetProcAddress (provided by layer, called by loader)
// - getNextLayerProcAddress (provided by loader, called by layer)
//
// 1. Walk through defs for egl and each gl version
@@ -73,27 +72,30 @@
const void* getNextLayerProcAddress(void* layer_id, const char* name) {
// Use layer_id to find funcs for layer below current
- // This is the same key provided in InitializeLayer
+ // This is the same key provided in AndroidGLESLayer_Initialize
auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
EGLFuncPointer val;
+ ALOGV("getNextLayerProcAddress servicing %s", name);
+
if (func_indices.find(name) == func_indices.end()) {
// No entry for this function - it is an extension
// call down the GPA chain directly to the impl
- ALOGV("getNextLayerProcAddress servicing %s", name);
+ ALOGV("getNextLayerProcAddress - name(%s) no func_indices entry found", name);
// Look up which GPA we should use
int gpaIndex = func_indices["eglGetProcAddress"];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
- ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
// Call it for the requested function
typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
val = reinterpret_cast<EGLFuncPointer>(next(name));
- ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
// We should store it now, but to do that, we need to move func_idx to the class so we can
// increment it separately
@@ -101,10 +103,10 @@
return reinterpret_cast<void*>(val);
}
- // int index = func_indices[name];
- // val = (*next_layer_funcs)[index];
- // return reinterpret_cast<void*>(val);
- return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+ int index = func_indices[name];
+ val = (*next_layer_funcs)[index];
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ return reinterpret_cast<void*>(val);
}
void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
@@ -115,14 +117,20 @@
// Some names overlap, only fill with initial entry
// This does mean that some indices will not be used
if (func_indices.find(name) == func_indices.end()) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
func_names[func_idx] = name;
func_indices[name] = func_idx;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
}
// Populate layer_functions once with initial value
// These values will arrive in priority order, starting with platform entries
if (functions[func_idx] == nullptr) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
functions[func_idx] = *curr;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
}
entries++;
@@ -400,7 +408,7 @@
}
// Find the layer's Initialize function
- std::string init_func = "InitializeLayer";
+ std::string init_func = "AndroidGLESLayer_Initialize";
ALOGV("Looking for entrypoint %s", init_func.c_str());
layer_init_func LayerInit =
@@ -414,7 +422,7 @@
}
// Find the layer's setup function
- std::string setup_func = "GetLayerProcAddress";
+ std::string setup_func = "AndroidGLESLayer_GetProcAddress";
ALOGV("Looking for entrypoint %s", setup_func.c_str());
layer_setup_func LayerSetup =
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 872631f..34262f3 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -139,7 +139,8 @@
char const * const gClientExtensionString =
"EGL_EXT_client_extensions "
"EGL_KHR_platform_android "
- "EGL_ANGLE_platform_angle";
+ "EGL_ANGLE_platform_angle "
+ "EGL_ANDROID_GLES_layers";
// clang-format on
// extensions not exposed to applications but used by the ANDROID system
diff --git a/opengl/specs/EGL_ANDROID_GLES_layers.txt b/opengl/specs/EGL_ANDROID_GLES_layers.txt
new file mode 100644
index 0000000..eb2a7d9
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_GLES_layers.txt
@@ -0,0 +1,64 @@
+Name
+
+ ANDROID_GLES_layers
+
+Name Strings
+
+ EGL_ANDROID_GLES_layers
+
+Contributors
+
+ Cody Northrop
+
+Contact
+
+ Cody Northrop, Google LLC (cnorthrop 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, March 3, 2019
+
+Number
+
+ EGL Extension #132
+
+Extension Type
+
+ EGL client extension
+
+Dependencies
+
+ Requires EGL 1.5 or EGL_EXT_client_extensions
+
+Overview
+
+ This extension indicates the EGL loader supports GLES layering on Android.
+ It does not add any requirements to drivers or hardware.
+
+ See frameworks/native/opengl/libs/EGL/GLES_layers.md in Android for
+ more information.
+
+New Types
+
+ None
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ None
+
+Issues
+
+ None
+
+Revision History
+
+#1 (Cody Northrop, March 3, 2019)
+ - Initial draft.
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 1317620..2549d9b 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "InputDispatcher"
#define ATRACE_TAG ATRACE_TAG_INPUT
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
// Log detailed debug messages about each inbound event notification to the dispatcher.
#define DEBUG_INBOUND_EVENT_DETAILS 0
@@ -58,7 +58,6 @@
#include <log/log.h>
#include <utils/Trace.h>
#include <powermanager/PowerManager.h>
-#include <ui/Region.h>
#include <binder/Binder.h>
#define INDENT " "
@@ -129,7 +128,7 @@
static std::string keyActionToString(int32_t action) {
// Convert KeyEvent action to string
- switch(action) {
+ switch (action) {
case AKEY_EVENT_ACTION_DOWN:
return "DOWN";
case AKEY_EVENT_ACTION_UP:
@@ -140,6 +139,24 @@
return StringPrintf("%" PRId32, action);
}
+static std::string dispatchModeToString(int32_t dispatchMode) {
+ switch (dispatchMode) {
+ case InputTarget::FLAG_DISPATCH_AS_IS:
+ return "DISPATCH_AS_IS";
+ case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
+ return "DISPATCH_AS_OUTSIDE";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
+ return "DISPATCH_AS_HOVER_ENTER";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
+ return "DISPATCH_AS_HOVER_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
+ return "DISPATCH_AS_SLIPPERY_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
+ return "DISPATCH_AS_SLIPPERY_ENTER";
+ }
+ return StringPrintf("%" PRId32, dispatchMode);
+}
+
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -560,6 +577,32 @@
return nullptr;
}
+std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+ std::vector<TouchedMonitor> touchedMonitors;
+
+ std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
+ addGestureMonitors(monitors, touchedMonitors);
+ for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
+ const InputWindowInfo* windowInfo = portalWindow->getInfo();
+ monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
+ addGestureMonitors(monitors, touchedMonitors,
+ -windowInfo->frameLeft, -windowInfo->frameTop);
+ }
+ return touchedMonitors;
+}
+
+void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset, float yOffset) {
+ if (monitors.empty()) {
+ return;
+ }
+ outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+ for (const Monitor& monitor : monitors) {
+ outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+ }
+}
+
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -870,7 +913,7 @@
}
// Add monitor channels from event's or focused display.
- addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+ addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
@@ -891,6 +934,7 @@
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ ATRACE_CALL();
// Preprocessing.
if (! entry->dispatchInProgress) {
entry->dispatchInProgress = true;
@@ -938,7 +982,7 @@
}
// Add monitor channels from event's or focused display.
- addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+ addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
if (isPointerEvent) {
ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
@@ -949,7 +993,7 @@
// the corresponding displays as well.
for (size_t i = 0; i < state.portalWindows.size(); i++) {
const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo();
- addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+ addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
-windowInfo->frameLeft, -windowInfo->frameTop);
}
}
@@ -1003,6 +1047,7 @@
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
+ ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
#endif
@@ -1242,6 +1287,7 @@
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
+ ATRACE_CALL();
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
@@ -1320,8 +1366,13 @@
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
+ bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
- displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
+ displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+
+ std::vector<TouchedMonitor> newGestureMonitors = isDown
+ ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+ : std::vector<TouchedMonitor>{};
// Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr
@@ -1338,39 +1389,44 @@
if (newTouchedWindowHandle == nullptr) {
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
- if (newTouchedWindowHandle == nullptr) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d) in display "
- "%" PRId32 ".", x, y, displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
+ }
+
+ if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
+ ALOGI("Dropping event because there is no touchable window or gesture monitor at "
+ "(%d, %d) in display %" PRId32 ".", x, y, displayId);
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ if (newTouchedWindowHandle != nullptr) {
+ // Set target flags.
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
}
+ if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
+
+ // Update hover state.
+ if (isHoverAction) {
+ newHoverWindowHandle = newTouchedWindowHandle;
+ } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ newHoverWindowHandle = mLastHoverWindowHandle;
+ }
+
+ // Update the temporary touch state.
+ BitSet32 pointerIds;
+ if (isSplit) {
+ uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+ pointerIds.markBit(pointerId);
+ }
+ mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
- // Set target flags.
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- }
-
- // Update hover state.
- if (isHoverAction) {
- newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
- }
-
- // Update the temporary touch state.
- BitSet32 pointerIds;
- if (isSplit) {
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
- pointerIds.markBit(pointerId);
- }
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ mTempTouchState.addGestureMonitors(newGestureMonitors);
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -1396,6 +1452,7 @@
sp<InputWindowHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y);
if (oldTouchedWindowHandle != newTouchedWindowHandle
+ && oldTouchedWindowHandle != nullptr
&& newTouchedWindowHandle != nullptr) {
#if DEBUG_FOCUS
ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
@@ -1467,10 +1524,11 @@
}
}
}
- if (! haveForegroundWindow) {
+ bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+ if (!haveForegroundWindow && !hasGestureMonitor) {
#if DEBUG_FOCUS
- ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
- " to receive it.", displayId);
+ ALOGD("Dropping event because there is no touched foreground window in display %"
+ PRId32 " or gesture monitor to receive it.", displayId);
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
@@ -1485,13 +1543,15 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
- const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
- sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
- if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ if (foregroundWindowHandle) {
+ const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
+ for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
+ if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
+ mTempTouchState.addOrUpdateWindow(inputWindowHandle,
+ InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ }
}
}
}
@@ -1520,7 +1580,7 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
- if (foregroundWindowHandle->getInfo()->hasWallpaper) {
+ if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
const std::vector<sp<InputWindowHandle>> windowHandles =
getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -1546,6 +1606,11 @@
touchedWindow.pointerIds, inputTargets);
}
+ for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+ addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
+ touchedMonitor.yOffset, inputTargets);
+ }
+
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
mTempTouchState.filterNonAsIsTouchWindows();
@@ -1675,31 +1740,32 @@
inputTargets.push_back(target);
}
-void InputDispatcher::addMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId, float xOffset, float yOffset) {
- std::unordered_map<int32_t, std::vector<sp<InputChannel>>>::const_iterator it =
- mMonitoringChannelsByDisplay.find(displayId);
+void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
+ int32_t displayId, float xOffset, float yOffset) {
- if (it != mMonitoringChannelsByDisplay.end()) {
- const std::vector<sp<InputChannel>>& monitoringChannels = it->second;
- const size_t numChannels = monitoringChannels.size();
- for (size_t i = 0; i < numChannels; i++) {
- InputTarget target;
- target.inputChannel = monitoringChannels[i];
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = xOffset;
- target.yOffset = yOffset;
- target.pointerIds.clear();
- target.globalScaleFactor = 1.0f;
- inputTargets.push_back(target);
+ std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
+ mGlobalMonitorsByDisplay.find(displayId);
+
+ if (it != mGlobalMonitorsByDisplay.end()) {
+ const std::vector<Monitor>& monitors = it->second;
+ for (const Monitor& monitor : monitors) {
+ addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
}
- } else {
- // If there is no monitor channel registered or all monitor channel unregistered,
- // the display can't detect the extra system gesture by a copy of input events.
- ALOGW("There is no monitor channel found in display %" PRId32, displayId);
}
}
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor,
+ float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) {
+ InputTarget target;
+ target.inputChannel = monitor.inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.xOffset = xOffset;
+ target.yOffset = yOffset;
+ target.pointerIds.clear();
+ target.globalScaleFactor = 1.0f;
+ inputTargets.push_back(target);
+}
+
bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState) {
if (injectionState
@@ -1905,6 +1971,12 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
"xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
@@ -1955,20 +2027,27 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ ATRACE_NAME(message.c_str());
+ }
+
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1977,9 +2056,16 @@
}
}
-void InputDispatcher::enqueueDispatchEntry(
+void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
+ connection->getInputChannelName().c_str(),
+ dispatchModeToString(dispatchMode).c_str());
+ ATRACE_NAME(message.c_str());
+ }
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return;
@@ -2054,6 +2140,10 @@
delete dispatchEntry;
return; // skip the inconsistent event
}
+
+ dispatchPointerDownOutsideFocus(motionEntry->source,
+ dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
+
break;
}
}
@@ -2066,10 +2156,44 @@
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
traceOutboundQueueLength(connection);
+
+}
+
+void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) {
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
+ if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+ return;
+ }
+
+ sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
+ if (inputWindowHandle == nullptr) {
+ return;
+ }
+
+ int32_t displayId = inputWindowHandle->getInfo()->displayId;
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+ bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
+
+ if (!hasFocusChanged) {
+ return;
+ }
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+ commandEntry->newToken = newToken;
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
+ connection->getInputChannelName().c_str());
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ startDispatchCycle",
connection->getInputChannelName().c_str());
@@ -2317,11 +2441,17 @@
void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
const CancelationOptions& options) {
- for (auto& it : mMonitoringChannelsByDisplay) {
- const std::vector<sp<InputChannel>>& monitoringChannels = it.second;
- const size_t numChannels = monitoringChannels.size();
- for (size_t i = 0; i < numChannels; i++) {
- synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options);
+ synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay);
+ synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay);
+}
+
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
+ for (const auto& it : monitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options);
}
}
}
@@ -2385,7 +2515,7 @@
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
+ enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
&target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
@@ -3221,13 +3351,11 @@
if (oldFocusedApplicationHandle != inputApplicationHandle) {
if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
}
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
@@ -3265,8 +3393,9 @@
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (inputChannel != nullptr) {
CancelationOptions options(
- CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS,
+ CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"The display which contains this window no longer has focus.");
+ options.displayId = ADISPLAY_ID_NONE;
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
}
@@ -3576,18 +3705,19 @@
dump += INDENT "Displays: <none>\n";
}
- if (!mMonitoringChannelsByDisplay.empty()) {
- for (auto& it : mMonitoringChannelsByDisplay) {
- const std::vector<sp<InputChannel>>& monitoringChannels = it.second;
- dump += StringPrintf(INDENT "MonitoringChannels in display %" PRId32 ":\n", it.first);
- const size_t numChannels = monitoringChannels.size();
- for (size_t i = 0; i < numChannels; i++) {
- const sp<InputChannel>& channel = monitoringChannels[i];
- dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
- }
+ if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) {
+ for (auto& it : mGlobalMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first);
+ dumpMonitors(dump, monitors);
+ }
+ for (auto& it : mGestureMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first);
+ dumpMonitors(dump, monitors);
}
} else {
- dump += INDENT "MonitoringChannels: <none>\n";
+ dump += INDENT "Monitors: <none>\n";
}
nsecs_t currentTime = now();
@@ -3702,7 +3832,18 @@
mConfig.keyRepeatTimeout * 0.000001f);
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, int32_t displayId) {
+void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
+ const size_t numMonitors = monitors.size();
+ for (size_t i = 0; i < numMonitors; i++) {
+ const Monitor& monitor = monitors[i];
+ const sp<InputChannel>& channel = monitor.inputChannel;
+ dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
+ dump += "\n";
+ }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
+ int32_t displayId) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
inputChannel->getName().c_str(), displayId);
@@ -3711,32 +3852,18 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
- // treat inputChannel as monitor channel for displayId.
- bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
- if (monitor) {
- inputChannel->setToken(new BBinder());
- }
-
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().c_str());
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel, monitor);
+ sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
- // Store monitor channel by displayId.
- if (monitor) {
- std::vector<sp<InputChannel>>& monitoringChannels =
- mMonitoringChannelsByDisplay[displayId];
- monitoringChannels.push_back(inputChannel);
- }
-
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
@@ -3745,6 +3872,40 @@
return OK;
}
+status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (displayId < 0) {
+ ALOGW("Attempted to register input monitor without a specified display.");
+ return BAD_VALUE;
+ }
+
+ if (inputChannel->getToken() == nullptr) {
+ ALOGW("Attempted to register input monitor without an identifying token.");
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = new Connection(inputChannel, true /*monitor*/);
+
+ const int fd = inputChannel->getFd();
+ mConnectionsByFd.add(fd, connection);
+ mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+
+ auto& monitorsByDisplay = isGestureMonitor
+ ? mGestureMonitorsByDisplay
+ : mGlobalMonitorsByDisplay;
+ monitorsByDisplay[displayId].emplace_back(inputChannel);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+
+ }
+ // Wake the looper because some connections have changed.
+ mLooper->wake();
+ return OK;
+}
+
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
@@ -3793,24 +3954,89 @@
}
void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- for (auto it = mMonitoringChannelsByDisplay.begin();
- it != mMonitoringChannelsByDisplay.end(); ) {
- std::vector<sp<InputChannel>>& monitoringChannels = it->second;
- const size_t numChannels = monitoringChannels.size();
- for (size_t i = 0; i < numChannels; i++) {
- if (monitoringChannels[i] == inputChannel) {
- monitoringChannels.erase(monitoringChannels.begin() + i);
+ removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
+ removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
+}
+
+void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
+ for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) {
+ std::vector<Monitor>& monitors = it->second;
+ const size_t numMonitors = monitors.size();
+ for (size_t i = 0; i < numMonitors; i++) {
+ if (monitors[i].inputChannel == inputChannel) {
+ monitors.erase(monitors.begin() + i);
break;
}
}
- if (monitoringChannels.empty()) {
- it = mMonitoringChannelsByDisplay.erase(it);
+ if (monitors.empty()) {
+ it = monitorsByDisplay.erase(it);
} else {
++it;
}
}
}
+status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ std::optional<int32_t> foundDisplayId = findGestureMonitorDisplayByTokenLocked(token);
+
+ if (!foundDisplayId) {
+ ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token");
+ return BAD_VALUE;
+ }
+ int32_t displayId = foundDisplayId.value();
+
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+ if (stateIndex < 0) {
+ ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
+ return BAD_VALUE;
+ }
+
+ TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ std::optional<int32_t> foundDeviceId;
+ for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
+ if (touchedMonitor.monitor.inputChannel->getToken() == token) {
+ foundDeviceId = state.deviceId;
+ }
+ }
+ if (!foundDeviceId || !state.down) {
+ ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
+ " Ignoring.");
+ return BAD_VALUE;
+ }
+ int32_t deviceId = foundDeviceId.value();
+
+ // Send cancel events to all the input channels we're stealing from.
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "gesture monitor stole pointer stream");
+ options.deviceId = deviceId;
+ options.displayId = displayId;
+ for (const TouchedWindow& window : state.windows) {
+ sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+ synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ }
+ // Then clear the current touch state so we stop dispatching to them as well.
+ state.filterNonMonitors();
+ }
+ return OK;
+}
+
+
+std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
+ const sp<IBinder>& token) {
+ for (const auto& it : mGestureMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ if (monitor.inputChannel->getToken() == token) {
+ return it.first;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
if (inputChannel == nullptr) {
return -1;
@@ -3969,6 +4195,12 @@
entry->release();
}
+void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+ mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
+ mLock.lock();
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
@@ -4779,11 +5011,15 @@
bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
const CancelationOptions& options) {
- if (options.keyCode != -1 && memento.keyCode != options.keyCode) {
+ if (options.keyCode && memento.keyCode != options.keyCode.value()) {
return false;
}
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
@@ -4793,8 +5029,6 @@
return true;
case CancelationOptions::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
- case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
- return memento.displayId == ADISPLAY_ID_NONE;
default:
return false;
}
@@ -4802,7 +5036,11 @@
bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
const CancelationOptions& options) {
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
@@ -4813,8 +5051,6 @@
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
- case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
- return memento.displayId == ADISPLAY_ID_NONE;
default:
return false;
}
@@ -4867,9 +5103,14 @@
return nullptr;
}
+// --- InputDispatcher::Monitor
+InputDispatcher::Monitor::Monitor(const sp<InputChannel>& inputChannel) :
+ inputChannel(inputChannel) {
+}
+
// --- InputDispatcher::CommandEntry ---
-
+//
InputDispatcher::CommandEntry::CommandEntry(Command command) :
command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0),
seq(0), handled(false) {
@@ -4878,6 +5119,10 @@
InputDispatcher::CommandEntry::~CommandEntry() {
}
+// --- InputDispatcher::TouchedMonitor ---
+InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset,
+ float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {
+}
// --- InputDispatcher::TouchState ---
@@ -4896,6 +5141,7 @@
displayId = ADISPLAY_ID_NONE;
windows.clear();
portalWindows.clear();
+ gestureMonitors.clear();
}
void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4906,6 +5152,7 @@
displayId = other.displayId;
windows = other.windows;
portalWindows = other.portalWindows;
+ gestureMonitors = other.gestureMonitors;
}
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4943,6 +5190,14 @@
portalWindows.push_back(windowHandle);
}
+void InputDispatcher::TouchState::addGestureMonitors(
+ const std::vector<TouchedMonitor>& newMonitors) {
+ const size_t newSize = gestureMonitors.size() + newMonitors.size();
+ gestureMonitors.reserve(newSize);
+ gestureMonitors.insert(std::end(gestureMonitors),
+ std::begin(newMonitors), std::end(newMonitors));
+}
+
void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
for (size_t i = 0; i < windows.size(); i++) {
if (windows[i].windowHandle == windowHandle) {
@@ -4975,6 +5230,11 @@
}
}
+void InputDispatcher::TouchState::filterNonMonitors() {
+ windows.clear();
+ portalWindows.clear();
+}
+
sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const {
for (size_t i = 0; i < windows.size(); i++) {
const TouchedWindow& window = windows[i];
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 4d2c216..753b748 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -23,6 +23,8 @@
#include <input/InputTransport.h>
#include <input/InputWindow.h>
#include <input/ISetInputWindowsListener.h>
+#include <optional>
+#include <ui/Region.h>
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
@@ -272,6 +274,13 @@
*/
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid) = 0;
+
+ /* Notifies the policy that a pointer down event has occurred outside the current focused
+ * window.
+ *
+ * The touchedToken passed as an argument is the window that received the input event.
+ */
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
};
@@ -351,19 +360,35 @@
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
/* Registers input channels that may be used as targets for input events.
- * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE,
- * the channel will receive a copy of all input events form the specific displayId.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual status_t registerInputChannel(
const sp<InputChannel>& inputChannel, int32_t displayId) = 0;
+ /* Registers input channels to be used to monitor input events.
+ *
+ * Each monitor must target a specific display and will only receive input events sent to that
+ * display. If the monitor is a gesture monitor, it will only receive pointer events on the
+ * targeted display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t registerInputMonitor(
+ const sp<InputChannel>& inputChannel, int32_t displayId, bool gestureMonitor) = 0;
+
/* Unregister input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Allows an input monitor steal the current pointer stream away from normal input windows.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+
};
/* Dispatches events to input targets. Some functions of the input dispatcher, such as
@@ -390,35 +415,39 @@
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- virtual void dump(std::string& dump);
- virtual void monitor();
+ virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
- virtual void dispatchOnce();
+ virtual void dispatchOnce() override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags);
+ uint32_t policyFlags) override;
virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr);
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
virtual void setFocusedApplication(int32_t displayId,
- const sp<InputApplicationHandle>& inputApplicationHandle);
- virtual void setFocusedDisplay(int32_t displayId);
- virtual void setInputDispatchMode(bool enabled, bool frozen);
- virtual void setInputFilterEnabled(bool enabled);
+ const sp<InputApplicationHandle>& inputApplicationHandle) override;
+ virtual void setFocusedDisplay(int32_t displayId) override;
+ virtual void setInputDispatchMode(bool enabled, bool frozen) override;
+ virtual void setInputFilterEnabled(bool enabled) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken)
+ override;
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- int32_t displayId);
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
+ int32_t displayId) override;
+ virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) override;
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual status_t pilferPointers(const sp<IBinder>& token) override;
private:
template <typename T>
@@ -712,10 +741,6 @@
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
-
- /* Cancel events where the display not specified. These events would go to the focused
- * display. */
- CANCEL_DISPLAY_UNSPECIFIED_EVENTS = 4,
};
// The criterion to use to determine which events should be canceled.
@@ -724,14 +749,16 @@
// Descriptive reason for the cancelation.
const char* reason;
- // The specific keycode of the key event to cancel, or -1 to cancel any key event.
- int32_t keyCode;
+ // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
+ std::optional<int32_t> keyCode = std::nullopt;
- // The specific device id of events to cancel, or -1 to cancel events from any device.
- int32_t deviceId;
+ // The specific device id of events to cancel, or nullopt to cancel events from any device.
+ std::optional<int32_t> deviceId = std::nullopt;
- CancelationOptions(Mode mode, const char* reason) :
- mode(mode), reason(reason), keyCode(-1), deviceId(-1) { }
+ // The specific display id of events to cancel, or nullopt to cancel events on any display.
+ std::optional<int32_t> displayId = std::nullopt;
+
+ CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { }
};
/* Tracks dispatched key and motion event state so that cancelation events can be
@@ -871,6 +898,12 @@
DispatchEntry* findWaitQueueEntry(uint32_t seq);
};
+ struct Monitor {
+ sp<InputChannel> inputChannel; // never null
+
+ explicit Monitor(const sp<InputChannel>& inputChannel);
+ };
+
enum DropReason {
DROP_REASON_NOT_DROPPED = 0,
DROP_REASON_POLICY = 1,
@@ -936,12 +969,24 @@
std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
GUARDED_BY(mLock);
+ // Finds the display ID of the gesture monitor identified by the provided token.
+ std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
+ REQUIRES(mLock);
+
ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
- std::unordered_map<int32_t, std::vector<sp<InputChannel>>> mMonitoringChannelsByDisplay
+ std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay
GUARDED_BY(mLock);
+ // Input channels that will receive pointer events that start within the corresponding display.
+ // These are a bit special when compared to global monitors since they'll cause gesture streams
+ // to continue even when there isn't a touched window,and have the ability to steal the rest of
+ // the pointer stream in order to claim it for a system gesture.
+ std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay
+ GUARDED_BY(mLock);
+
+
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
@@ -1016,6 +1061,16 @@
int32_t targetFlags;
BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
};
+
+ // For tracking the offsets we need to apply when adding gesture monitor targets.
+ struct TouchedMonitor {
+ Monitor monitor;
+ float xOffset = 0.f;
+ float yOffset = 0.f;
+
+ explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
+ };
+
struct TouchState {
bool down;
bool split;
@@ -1029,6 +1084,8 @@
// monitoring channels of the displays touched.
std::vector<sp<InputWindowHandle>> portalWindows;
+ std::vector<TouchedMonitor> gestureMonitors;
+
TouchState();
~TouchState();
void reset();
@@ -1036,9 +1093,11 @@
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
+ void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
void removeWindow(const sp<InputWindowHandle>& windowHandle);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
+ void filterNonMonitors();
sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
};
@@ -1108,12 +1167,18 @@
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) REQUIRES(mLock);
+ std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId,
+ const std::vector<sp<InputWindowHandle>>& portalWindows) REQUIRES(mLock);
+ void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, float yOffset = 0);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
REQUIRES(mLock);
- void addMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
- float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+ void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+ std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
+ int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
@@ -1136,8 +1201,9 @@
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntry(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
+ REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
@@ -1147,11 +1213,16 @@
void drainDispatchQueue(Queue<DispatchEntry>* queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
+ // The action sent should only be of type AMOTION_EVENT_*
+ void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) REQUIRES(mLock);
void synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options) REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
@@ -1165,10 +1236,14 @@
// Dump state.
void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
+ void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
// Registration.
void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay)
+ REQUIRES(mLock);
status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
REQUIRES(mLock);
@@ -1200,6 +1275,8 @@
DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+ void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
// Statistics gathering.
void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d63ff8c..9fe6481 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -52,6 +52,7 @@
mTime = -1;
mAction = -1;
mDisplayId = -1;
+ mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
@@ -87,11 +88,18 @@
<< "Expected filterInputEvent() to not have been called.";
}
+ void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+ ASSERT_EQ(mOnPointerDownToken, touchedToken)
+ << "Expected token from onPointerDownOutsideFocus was not matched";
+ reset();
+ }
+
private:
bool mInputEventFiltered;
nsecs_t mTime;
int32_t mAction;
int32_t mDisplayId;
+ sp<IBinder> mOnPointerDownToken;
virtual void notifyConfigurationChanged(nsecs_t) {
}
@@ -161,11 +169,16 @@
return false;
}
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+ mOnPointerDownToken = newToken;
+ }
+
void reset() {
mInputEventFiltered = false;
mTime = -1;
mAction = -1;
mDisplayId = -1;
+ mOnPointerDownToken.clear();
}
};
@@ -340,10 +353,7 @@
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() {
- if (!mInfo) {
- mInfo = new InputApplicationInfo();
- }
- mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
return true;
}
};
@@ -419,7 +429,6 @@
sp<InputDispatcher> mDispatcher;
sp<InputChannel> mServerChannel, mClientChannel;
- sp<IBinder> mToken;
InputConsumer *mConsumer;
PreallocatedInputEventFactory mEventFactory;
@@ -435,10 +444,10 @@
FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
FakeInputReceiver(dispatcher, name, displayId),
- mFocused(false) {
+ mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
mServerChannel->setToken(new BBinder());
mDispatcher->registerInputChannel(mServerChannel, displayId);
-
+
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
@@ -446,15 +455,15 @@
virtual bool updateInfo() {
mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
mInfo.name = mName;
- mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsFlags = mLayoutParamFlags;
mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frameLeft = 0;
- mInfo.frameTop = 0;
- mInfo.frameRight = WIDTH;
- mInfo.frameBottom = HEIGHT;
+ mInfo.frameLeft = mFrame.left;
+ mInfo.frameTop = mFrame.top;
+ mInfo.frameRight = mFrame.right;
+ mInfo.frameBottom = mFrame.bottom;
mInfo.globalScaleFactor = 1.0;
- mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.addTouchableRegion(mFrame);
mInfo.visible = true;
mInfo.canReceiveKeys = true;
mInfo.hasFocus = mFocused;
@@ -473,6 +482,14 @@
mFocused = true;
}
+ void setFrame(const Rect& frame) {
+ mFrame.set(frame);
+ }
+
+ void setLayoutParamFlags(int32_t flags) {
+ mLayoutParamFlags = flags;
+ }
+
void releaseChannel() {
mServerChannel.clear();
InputWindowHandle::releaseChannel();
@@ -483,6 +500,8 @@
}
bool mFocused;
+ Rect mFrame;
+ int32_t mLayoutParamFlags;
};
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -503,7 +522,7 @@
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId) {
+ int32_t displayId, int32_t x = 100, int32_t y = 200) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -513,13 +532,13 @@
pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
event.initialize(DEVICE_ID, source, displayId,
- AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+ AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
/* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
/* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
@@ -779,8 +798,10 @@
class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) {
- mDispatcher->registerInputChannel(mServerChannel, displayId);
+ int32_t displayId, bool isGestureMonitor = false)
+ : FakeInputReceiver(dispatcher, name, displayId) {
+ mServerChannel->setToken(new BBinder());
+ mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
}
};
@@ -910,4 +931,98 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
+ virtual void SetUp() {
+ InputDispatcherTest::SetUp();
+
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+ mWindowFocused->setFrame(Rect(50, 50, 100, 100));
+ mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mWindowFocusedTouchPoint = 60;
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mWindowFocused->setFocus();
+
+ // Expect one focus window exist in display.
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(mUnfocusedWindow);
+ inputWindowHandles.push_back(mWindowFocused);
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ }
+
+ virtual void TearDown() {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mWindowFocused.clear();
+ }
+
+protected:
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mWindowFocused;
+ int32_t mWindowFocusedTouchPoint;
+};
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
+// the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TRACKBALL and action
+// DOWN on the window that doesn't have focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
+// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that already has focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus,
+ OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
+ mWindowFocusedTouchPoint))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
} // namespace android
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 189ae36..a6ed75f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -359,7 +359,7 @@
for (const auto & s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
- if (info.batchParams.isEmpty()) continue;
+ if (info.numActiveClients() == 0) continue;
result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
@@ -730,6 +730,15 @@
return mDisabledClients.indexOf(ident) >= 0;
}
+bool SensorDevice::isSensorActive(int handle) const {
+ Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ return false;
+ }
+ return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
+}
+
void SensorDevice::enableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 2a69654..71b918f 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -120,6 +120,8 @@
return mReconnecting;
}
+ bool isSensorActive(int handle) const;
+
// Dumpable
virtual std::string dump() const;
private:
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index b66cbcf..c4cfdc6 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -78,7 +78,14 @@
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tOperating Mode: %s\n",mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
+ result.appendFormat("\tOperating Mode: ");
+ if (!mService->isWhiteListedPackage(getPackageName())) {
+ result.append("RESTRICTED\n");
+ } else if (mDataInjectionMode) {
+ result.append("DATA_INJECTION\n");
+ } else {
+ result.append("NORMAL\n");
+ }
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
"max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3fbd61e..e3dfde4 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -441,12 +441,15 @@
}
result.append("Active sensors:\n");
+ SensorDevice& dev = SensorDevice::getInstance();
for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
int handle = mActiveSensors.keyAt(i);
- result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
- getSensorName(handle).string(),
- handle,
- mActiveSensors.valueAt(i)->getNumConnections());
+ if (dev.isSensorActive(handle)) {
+ result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ }
}
result.appendFormat("Socket Buffer size = %zd events\n",
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index ac1d492..52c68df 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -47,7 +47,7 @@
"libhwbinder",
"liblayers_proto",
"liblog",
- "libnativewindow",
+ "libnativewindow",
"libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
@@ -56,7 +56,7 @@
"libui",
"libinput",
"libutils",
- "libSurfaceFlingerProperties",
+ "libSurfaceFlingerProp",
],
static_libs: [
"libcompositionengine",
@@ -138,8 +138,9 @@
"LayerVector.cpp",
"MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
- "RenderArea.cpp",
+ "RefreshRateOverlay.cpp",
"RegionSamplingThread.cpp",
+ "RenderArea.cpp",
"Scheduler/DispSync.cpp",
"Scheduler/DispSyncSource.cpp",
"Scheduler/EventControlThread.cpp",
@@ -221,7 +222,7 @@
srcs: [":surfaceflinger_binary_sources"],
shared_libs: [
"libsurfaceflinger",
- "libSurfaceFlingerProperties",
+ "libSurfaceFlingerProp",
],
}
@@ -232,10 +233,9 @@
]
cc_library_shared {
- name: "libSurfaceFlingerProperties",
+ name: "libSurfaceFlingerProp",
srcs: [
"SurfaceFlingerProperties.cpp",
- "sysprop/*.sysprop",
],
shared_libs: [
"android.hardware.configstore-utils",
@@ -247,6 +247,10 @@
"libhwbinder",
"libui",
"libutils",
+ "liblog",
+ ],
+ static_libs: [
+ "SurfaceFlingerProperties",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
@@ -254,4 +258,7 @@
"libhidltransport",
"libhwbinder",
],
+ export_static_lib_headers: [
+ "SurfaceFlingerProperties",
+ ],
}
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index f2d4c51..fc98dc8 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -217,7 +217,11 @@
// If item->mGraphicBuffer is not null, this buffer has not been acquired
// before, so we need to clean up old references.
if (item->mGraphicBuffer != nullptr) {
- mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
+ mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
+ mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+ }
}
return NO_ERROR;
@@ -238,7 +242,12 @@
// Hang onto the pointer so that it isn't freed in the call to
// releaseBufferLocked() if we're in shared buffer mode and both buffers are
// the same.
- std::shared_ptr<Image> nextTextureBuffer = mImages[slot];
+
+ std::shared_ptr<Image> nextTextureBuffer;
+ {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ nextTextureBuffer = mImages[slot];
+ }
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
@@ -436,6 +445,7 @@
void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
@@ -468,6 +478,23 @@
}
}
+void BufferLayerConsumer::onBufferAllocated(const BufferItem& item) {
+ if (item.mGraphicBuffer != nullptr) {
+ std::shared_ptr<Image> image = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ std::shared_ptr<Image> oldImage;
+ {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ oldImage = mImages[item.mSlot];
+ if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
+ oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
+ mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ }
+ image = mImages[item.mSlot];
+ }
+ mRE.cacheExternalTextureBuffer(image->graphicBuffer());
+ }
+}
+
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
sp<Layer> l = mLayer.promote();
@@ -480,6 +507,7 @@
BLC_LOGV("abandonLocked");
mCurrentTextureBuffer = nullptr;
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
mImages[i] = nullptr;
}
ConsumerBase::abandonLocked();
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f4ca846..0f0655d 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_BUFFERLAYERCONSUMER_H
#define ANDROID_BUFFERLAYERCONSUMER_H
+#include <android-base/thread_annotations.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <gui/HdrMetadata.h>
@@ -179,7 +180,7 @@
protected:
// abandonLocked overrides the ConsumerBase method to clear
// mCurrentTextureImage in addition to the ConsumerBase behavior.
- virtual void abandonLocked();
+ virtual void abandonLocked() EXCLUDES(mImagesMutex);
// dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
// specific info in addition to the ConsumerBase behavior.
@@ -187,7 +188,8 @@
// See ConsumerBase::acquireBufferLocked
virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override;
+ uint64_t maxFrameNumber = 0) override
+ EXCLUDES(mImagesMutex);
bool canUseImageCrop(const Rect& crop) const;
@@ -206,7 +208,8 @@
// completion of the method will instead be returned to the caller, so that
// it may call releaseBufferLocked itself later.
status_t updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease = nullptr);
+ PendingRelease* pendingRelease = nullptr)
+ EXCLUDES(mImagesMutex);
// Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
// If the bind succeeds, this calls doFenceWait.
@@ -234,10 +237,11 @@
// that slot. Otherwise it has no effect.
//
// This method must be called with mMutex locked.
- virtual void freeBufferLocked(int slotIndex);
+ virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
// IConsumerListener interface
void onDisconnect() override;
+ void onBufferAllocated(const BufferItem& item) override EXCLUDES(mImagesMutex);
void onSidebandStreamChanged() override;
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override;
@@ -344,7 +348,12 @@
int mCurrentTexture;
// Shadow buffer cache for cleaning up renderengine references.
- std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
+
+ // Separate mutex guarding the shadow buffer cache.
+ // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
+ // which is contentious enough that we can't just use mMutex.
+ mutable std::mutex mImagesMutex;
// A release that is pending on the receipt of a new release fence from
// presentDisplay
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e4179ee..b623991 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -23,6 +23,7 @@
#include "BufferQueueLayer.h"
#include "LayerRejecter.h"
+#include "SurfaceInterceptor.h"
#include "TimeStats/TimeStats.h"
@@ -361,8 +362,12 @@
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
+
+ // INVALID_BUFFER_SLOT is used to identify BufferStateLayers. Default to 0
+ // for BufferQueueLayers
+ int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
(*outputLayer->editState().hwc)
- .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
+ .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer);
auto acquireFence = mConsumer->getCurrentFence();
auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5efa4e0..64dfdc3 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -585,10 +585,10 @@
const State& s(getDrawingState());
- // obtain slot
- uint32_t hwcSlot = 0;
+ uint32_t hwcSlot;
sp<GraphicBuffer> buffer;
- hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
+ hwcInfo.hwcBufferCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, s.buffer, &hwcSlot,
+ &buffer);
auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
if (error != HWC2::Error::None) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 02d7890..97bdc8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -45,7 +45,7 @@
//
// outBuffer is set to buffer when buffer is not in the HWC cache;
// otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer);
protected:
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 024ed45..0c47eb5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -18,6 +18,7 @@
#include <cstdint>
+#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -78,6 +79,9 @@
// The color transform to apply
android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+ // The color transform matrix to apply, corresponding with colorTransform.
+ mat4 colorTransformMat;
+
// Current active color mode
ui::ColorMode colorMode{ui::ColorMode::NATIVE};
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 8613210..a941e09 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -48,12 +48,20 @@
return std::distance(std::begin(mBuffers), iter);
}
-void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- bool cached = getSlot(buffer, outSlot);
+ // if this slot corresponds to a BufferStateLayer, generate the slot
+ if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
+ getSlot(buffer, outSlot);
+ } else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+ *outSlot = 0;
+ } else {
+ *outSlot = slot;
+ }
auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
- if (cached) {
+ wp<GraphicBuffer> weakCopy(buffer);
+ if (currentBuffer == weakCopy) {
// already cached in HWC, skip sending the buffer
*outBuffer = nullptr;
currentCounter = getCounter();
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index d22bdaf..2893031 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -98,6 +98,7 @@
}
mState.colorTransform = newColorTransform;
+ mState.colorTransformMat = transform;
dirtyEntireOutput();
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index ac04cb3..b261493 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -24,9 +24,9 @@
class TestableHwcBufferCache : public impl::HwcBufferCache {
public:
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+ HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
}
bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
return HwcBufferCache::getSlot(buffer, outSlot);
@@ -38,64 +38,88 @@
public:
~HwcBufferCacheTest() override = default;
- TestableHwcBufferCache mCache;
+ void testSlot(const int inSlot, const uint32_t expectedSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // Setting a slot to use nullptr lookslike works, but note that
+ // the output values make it look like no new buffer is being set....
+ mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+ }
+
+ impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
};
-TEST_F(HwcBufferCacheTest, testSlot) {
+TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
+ testSlot(0, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
+ testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
+ testSlot(-123, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheGeneratesSlotForInvalidBufferSlot) {
uint32_t outSlot;
sp<GraphicBuffer> outBuffer;
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer2, outBuffer);
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, sp<GraphicBuffer>(), &outSlot,
+ &outBuffer);
EXPECT_EQ(2, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
-}
-TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
- int slot;
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
- mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
- EXPECT_EQ(buf, outBuffer);
- EXPECT_EQ(i, outSlot);
- }
-
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(0, slot);
-
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(0, outSlot);
+ // note that sending mBuffer1 with explicit slot 1 will overwrite mBuffer2
+ // and also cause mBuffer1 to be stored in two places
+ mCache.getHwcBuffer(1, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(1, slot);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(3, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
}
} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index c80925e..0067b50 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -288,16 +288,21 @@
}
Rect getSourceCrop() const override {
- // use the (projected) logical display viewport by default
+ // use the projected display viewport by default.
if (mSourceCrop.isEmpty()) {
return mDevice->getScissor();
}
- const int orientation = mDevice->getInstallOrientation();
- if (orientation == DisplayState::eOrientationDefault) {
- return mSourceCrop;
- }
+ // Recompute the device transformation for the source crop.
+ ui::Transform rotation;
+ ui::Transform translatePhysical;
+ ui::Transform translateLogical;
+ ui::Transform scale;
+ const Rect& viewport = mDevice->getViewport();
+ const Rect& scissor = mDevice->getScissor();
+ const Rect& frame = mDevice->getFrame();
+ const int orientation = mDevice->getInstallOrientation();
// Install orientation is transparent to the callers. Apply it now.
uint32_t flags = 0x00;
switch (orientation) {
@@ -310,10 +315,17 @@
case DisplayState::eOrientation270:
flags = ui::Transform::ROT_270;
break;
+ default:
+ break;
}
- ui::Transform tr;
- tr.set(flags, getWidth(), getHeight());
- return tr.transform(mSourceCrop);
+ rotation.set(flags, getWidth(), getHeight());
+ translateLogical.set(-viewport.left, -viewport.top);
+ translatePhysical.set(scissor.left, scissor.top);
+ scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
+ frame.getHeight() / float(viewport.getHeight()));
+ const ui::Transform finalTransform =
+ rotation * translatePhysical * scale * translateLogical;
+ return finalTransform.transform(mSourceCrop);
}
private:
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 775fb80..7370b0c 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -111,7 +111,7 @@
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -137,7 +137,7 @@
mCurrentFence = item.mFence;
outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
outDataspace = static_cast<Dataspace>(item.mDataSpace);
status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
if (result != NO_ERROR) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 62073b6..12a94a7 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -26,7 +26,6 @@
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
-#include <ui/Region.h>
#include <android/configuration.h>
@@ -826,6 +825,11 @@
Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer,
const sp<Fence>& acquireFence)
{
+ if (buffer == nullptr && mBufferSlot == slot) {
+ return Error::None;
+ }
+ mBufferSlot = slot;
+
int32_t fenceFd = acquireFence->dup();
auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer,
fenceFd);
@@ -834,6 +838,12 @@
Error Layer::setSurfaceDamage(const Region& damage)
{
+ if (damage.isRect() && mDamageRegion.isRect() &&
+ (damage.getBounds() == mDamageRegion.getBounds())) {
+ return Error::None;
+ }
+ mDamageRegion = damage;
+
// We encode default full-screen damage as INVALID_RECT upstream, but as 0
// rects for HWC
Hwc2::Error intError = Hwc2::Error::NONE;
@@ -988,6 +998,12 @@
Error Layer::setVisibleRegion(const Region& region)
{
+ if (region.isRect() && mVisibleRegion.isRect() &&
+ (region.getBounds() == mVisibleRegion.getBounds())) {
+ return Error::None;
+ }
+ mVisibleRegion = region;
+
size_t rectCount = 0;
auto rectArray = region.getArray(&rectCount);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 4209e45..e0a5ef1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -27,6 +27,7 @@
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
+#include <ui/Region.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
@@ -42,8 +43,6 @@
class Fence;
class FloatRect;
class GraphicBuffer;
- class Rect;
- class Region;
namespace Hwc2 {
class Composer;
}
@@ -438,9 +437,15 @@
hwc2_display_t mDisplayId;
hwc2_layer_t mId;
+
+ // Cached HWC2 data, to ensure the same commands aren't sent to the HWC
+ // multiple times.
+ android::Region mVisibleRegion = android::Region::INVALID_REGION;
+ android::Region mDamageRegion = android::Region::INVALID_REGION;
android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN;
android::HdrMetadata mHdrMetadata;
android::mat4 mColorMatrix;
+ uint32_t mBufferSlot;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 613dc77..4e0e4df 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -224,7 +224,7 @@
if (fbBuffer != nullptr) {
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
+ mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 898d37e..7965245 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1788,13 +1788,9 @@
}
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
- return getRoundedCornerStateInternal(mSourceBounds);
-}
-
-Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect bounds) const {
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ RoundedCornerState parentState = p->getRoundedCornerState();
if (parentState.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
@@ -1808,9 +1804,7 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0
- ? RoundedCornerState(bounds.intersect(getCrop(getDrawingState()).toFloatRect()), radius)
- : RoundedCornerState();
+ return radius > 0 ? RoundedCornerState(getBounds(), radius) : RoundedCornerState();
}
void Layer::commitChildList() {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 83ff3b6..8348ce1 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -908,8 +908,6 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
-
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
new file mode 100644
index 0000000..e70bfe4
--- /dev/null
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "RefreshRateOverlay.h"
+#include "Client.h"
+#include "Layer.h"
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
+ : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+ createLayer();
+}
+
+bool RefreshRateOverlay::createLayer() {
+ const status_t ret =
+ mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
+ LayerMetadata(), &mIBinder, &mGbp, &mLayer);
+ if (ret) {
+ ALOGE("failed to color layer");
+ return false;
+ }
+
+ mLayer = mClient->getLayerUser(mIBinder);
+ mLayer->setCrop_legacy(Rect(0, 0, 200, 100), true);
+ mLayer->setLayer(INT32_MAX - 2);
+
+ return true;
+}
+
+void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
+ const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
+ mLayer->setColor(color);
+ mFlinger.setTransactionFlags(eTransactionMask);
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
new file mode 100644
index 0000000..ce29bc3
--- /dev/null
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "SurfaceFlinger.h"
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
+class RefreshRateOverlay {
+public:
+ RefreshRateOverlay(SurfaceFlinger& flinger);
+
+ void changeRefreshRate(RefreshRateType type);
+
+private:
+ bool createLayer();
+
+ SurfaceFlinger& mFlinger;
+ sp<Client> mClient;
+ sp<Layer> mLayer;
+ sp<IBinder> mIBinder;
+ sp<IGraphicBufferProducer> mGbp;
+
+ const half3 RED = half3(1.0f, 0.0f, 0.0f);
+ const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 6599f9e..f72aef1 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -211,13 +211,14 @@
const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
const nsecs_t phaseCorrection = mPhase + listener.mPhase;
- const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
- if (predictedLastEventTime >= now) {
- // Make sure that the last event time does not exceed the current time.
- // If it would, then back the last event time by a period.
- listener.mLastEventTime = predictedLastEventTime - mPeriod;
- } else {
- listener.mLastEventTime = predictedLastEventTime;
+ listener.mLastEventTime = predictedReference + phaseCorrection;
+ // If we're very close in time to the predicted last event time,
+ // then we need to back up the last event time so that we can
+ // attempt to fire an event immediately.
+ //
+ // Otherwise, keep the last event time that we predicted.
+ if (isShorterThanPeriod(now - listener.mLastEventTime)) {
+ listener.mLastEventTime -= mPeriod;
}
} else {
listener.mLastEventTime = now + mPhase - mWakeupLatency;
@@ -314,7 +315,7 @@
// Sanity check that the duration is close enough in length to a period without
// falling into double-rate vsyncs.
- bool isCloseToPeriod(nsecs_t duration) {
+ bool isShorterThanPeriod(nsecs_t duration) {
// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
return duration < (3 * mPeriod) / 5;
}
@@ -330,9 +331,8 @@
nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
if (t < now) {
- if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
+ if (isShorterThanPeriod(now - eventListener.mLastCallbackTime)) {
eventListener.mLastEventTime = t;
- eventListener.mLastCallbackTime = now;
ALOGV("[%s] [%s] Skipping event due to model error", mName,
eventListener.mName);
continue;
@@ -391,7 +391,7 @@
// Check that it's been slightly more than half a period since the last
// event so that we don't accidentally fall into double-rate vsyncs
- if (isCloseToPeriod(t - listener.mLastEventTime)) {
+ if (isShorterThanPeriod(t - listener.mLastEventTime)) {
t += mPeriod;
ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 78bf7c5..a760079 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -97,7 +97,7 @@
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(uint32_t displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
event.config.configId = configId;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index cbcc031..1aa6ade 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -57,16 +57,23 @@
}
~RefreshRateConfigs() = default;
- const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+ const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() {
return mRefreshRates;
}
- const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
+ std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) {
+ const auto& refreshRate = mRefreshRates.find(type);
+ if (refreshRate != mRefreshRates.end()) {
+ return refreshRate->second;
+ }
+ return nullptr;
+ }
private:
void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
// This is the rate that HWC encapsulates right now when the device is in DOZE mode.
mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+ std::make_shared<RefreshRate>(
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}));
if (configs.size() < 1) {
ALOGE("Device does not have valid configs. Config size is 0.");
@@ -90,9 +97,10 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::DEFAULT,
- RefreshRate{configIdToVsyncPeriod[0].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[0].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
if (configs.size() < 2) {
@@ -105,13 +113,14 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
- RefreshRate{configIdToVsyncPeriod[1].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[1].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
}
- std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
+ std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index d4ae330..ff63faf 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -85,10 +85,13 @@
std::unordered_map<std::string, int64_t> totalTime;
for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
int64_t totalTimeForConfig = 0;
- if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
- totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+ if (!config) {
+ continue;
}
- totalTime[config.name] = totalTimeForConfig;
+ if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
+ totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
+ }
+ totalTime[config->name] = totalTimeForConfig;
}
return totalTime;
}
@@ -124,8 +127,11 @@
mConfigModesTotalTime[mode] += timeElapsedMs;
for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
- if (config.configId == mode) {
- mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+ if (!config) {
+ continue;
+ }
+ if (config->configId == mode) {
+ mTimeStats->recordRefreshRate(config->fps, timeElapsed);
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 63a1e4f..33455b9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,6 +75,7 @@
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
+#include "BufferStateLayerCache.h"
#include "Client.h"
#include "ColorLayer.h"
#include "Colorizer.h"
@@ -84,6 +85,7 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
+#include "RefreshRateOverlay.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceInterceptor.h"
@@ -94,12 +96,14 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "Effects/Daltonizer.h"
+#include "RegionSamplingThread.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/InjectVSyncSource.h"
#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
#include "TimeStats/TimeStats.h"
@@ -128,8 +132,6 @@
using ui::Hdr;
using ui::RenderIntent;
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-
namespace {
#pragma clang diagnostic push
@@ -267,48 +269,17 @@
}
}
-SurfaceFlingerBE::SurfaceFlingerBE()
- : mHwcServiceName(getHwcServiceName()),
- mFrameBuckets(),
- mTotalTime(0),
- mLastSwapTime(0),
- mComposerSequenceId(0) {
-}
+SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {}
-SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory,
- SurfaceFlinger::SkipInitializationTag)
- : BnSurfaceComposer(),
- mFactory(factory),
- mTransactionPending(false),
- mAnimTransactionPending(false),
- mLayersRemoved(false),
- mLayersAdded(false),
- mBootTime(systemTime()),
- mPhaseOffsets{getFactory().createPhaseOffsets()},
- mVisibleRegionsDirty(false),
- mGeometryInvalid(false),
- mAnimCompositionPending(false),
- mBootStage(BootStage::BOOTLOADER),
- mDebugRegion(0),
- mDebugDisableHWC(0),
- mDebugDisableTransformHint(0),
- mDebugEnableProtectedContent(false),
- mDebugInTransaction(0),
- mLastTransactionTime(0),
- mForceFullDamage(false),
- mTracing(*this),
- mTimeStats(factory.createTimeStats()),
- mRefreshStartTime(0),
- mHasPoweredOff(false),
- mNumLayers(0),
- mVrFlingerRequestsDisplay(false),
- mMainThreadId(std::this_thread::get_id()),
- mCompositionEngine{getFactory().createCompositionEngine()} {
- mSetInputWindowsListener = new SetInputWindowsListener(this);
-}
+SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
+ : mFactory(factory),
+ mPhaseOffsets(mFactory.createPhaseOffsets()),
+ mInterceptor(mFactory.createSurfaceInterceptor(this)),
+ mTimeStats(mFactory.createTimeStats()),
+ mEventQueue(mFactory.createMessageQueue()),
+ mCompositionEngine(mFactory.createCompositionEngine()) {}
-SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory)
- : SurfaceFlinger(factory, SkipInitialization) {
+SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
hasSyncFramework = running_without_sync_framework(true);
@@ -427,9 +398,7 @@
mEventQueue->init(this);
}
-SurfaceFlinger::~SurfaceFlinger()
-{
-}
+SurfaceFlinger::~SurfaceFlinger() = default;
void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
{
@@ -592,10 +561,11 @@
const auto displayId = getInternalDisplayIdLocked();
LOG_ALWAYS_FATAL_IF(!displayId);
- const auto performanceRefreshRate =
+ const auto& performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ if (performanceRefreshRate &&
+ isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
} else {
setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
@@ -666,6 +636,9 @@
renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0);
renderEngineFeature |= (useContextPriority ?
renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0);
+ renderEngineFeature |=
+ (enable_protected_contents(false) ? renderengine::RenderEngine::ENABLE_PROTECTED_CONTEXT
+ : 0);
// TODO(b/77156734): We need to stop casting and use HAL types when possible.
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
@@ -941,24 +914,18 @@
return display->getActiveConfig();
}
-void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
- Scheduler::ConfigEvent event) {
+void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
ATRACE_CALL();
// Lock is acquired by setRefreshRateTo.
- const auto display = getDisplayDeviceLocked(displayToken);
+ const auto display = getDisplayDeviceLocked(info.displayToken);
if (!display) {
- ALOGE("Attempt to set active config %d for invalid display token %p", mode,
- displayToken.get());
+ ALOGE("Attempt to set active config %d for invalid display token %p", info.configId,
+ info.displayToken.get());
return;
}
if (display->isVirtual()) {
- ALOGW("Attempt to set active config %d for virtual display", mode);
- return;
- }
- int currentDisplayPowerMode = display->getPowerMode();
- if (currentDisplayPowerMode != HWC_POWER_MODE_NORMAL) {
- // Don't change active config when in AoD.
+ ALOGW("Attempt to set active config %d for virtual display", info.configId);
return;
}
@@ -966,8 +933,9 @@
// config twice. However event generation config might have changed so we need to update it
// accordingly
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- const Scheduler::ConfigEvent desiredConfig = mDesiredActiveConfig.event | event;
- mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, desiredConfig};
+ const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
+ mDesiredActiveConfig = info;
+ mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
if (!mDesiredActiveConfigChanged) {
// This is the first time we set the desired
@@ -978,6 +946,10 @@
}
mDesiredActiveConfigChanged = true;
ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+ }
}
status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
@@ -1040,6 +1012,7 @@
// on both cases there is nothing left to do
std::lock_guard<std::mutex> lock(mActiveConfigLock);
mScheduler->pauseVsyncCallback(mAppConnectionHandle, false);
+ mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
mDesiredActiveConfigChanged = false;
ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
return false;
@@ -1471,19 +1444,27 @@
LOG_ALWAYS_FATAL_IF(!displayId);
const auto displayToken = getInternalDisplayTokenLocked();
- auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
- const auto display = getDisplayDeviceLocked(displayToken);
- if (desiredConfigId == display->getActiveConfig()) {
+ const auto& refreshRateConfig = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate);
+ if (!refreshRateConfig) {
+ ALOGV("Skipping refresh rate change request for unsupported rate.");
return;
}
+ const int desiredConfigId = refreshRateConfig->configId;
+
if (!isConfigAllowed(*displayId, desiredConfigId)) {
ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
return;
}
mPhaseOffsets->setRefreshRateType(refreshRate);
- setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (desiredConfigId == display->getActiveConfig()) {
+ return;
+ }
+
+ setDesiredActiveConfig({refreshRate, desiredConfigId, getInternalDisplayTokenLocked(), event});
}
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1696,22 +1677,22 @@
ATRACE_CALL();
uint32_t transactionFlags = peekTransactionFlags();
- // Apply any ready transactions in the queues if there are still transactions that have not been
- // applied, wake up during the next vsync period and check again
- bool transactionNeeded = false;
- if (!flushTransactionQueues()) {
- transactionNeeded = true;
+ bool flushedATransaction = flushTransactionQueues();
+
+ bool runHandleTransaction = transactionFlags &&
+ ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+
+ if (runHandleTransaction) {
+ handleTransaction(eTransactionMask);
+ } else {
+ getTransactionFlags(eTransactionFlushNeeded);
}
- if (transactionFlags) {
- handleTransaction(transactionFlags);
+ if (transactionFlushNeeded()) {
+ setTransactionFlags(eTransactionFlushNeeded);
}
- if (transactionNeeded) {
- setTransactionFlags(eTransactionNeeded);
- }
-
- return transactionFlags;
+ return runHandleTransaction;
}
void SurfaceFlinger::handleMessageRefresh() {
@@ -2409,8 +2390,6 @@
const auto& displayState = display->getState();
const auto displayId = display->getId();
- mPostFramebufferTime = systemTime();
-
if (displayState.isEnabled) {
if (displayId) {
getHwComposer().presentAndGetReleaseFences(*displayId);
@@ -2473,8 +2452,7 @@
State drawingState(mDrawingState);
Mutex::Autolock _l(mStateLock);
- const nsecs_t now = systemTime();
- mDebugInTransaction = now;
+ mDebugInTransaction = systemTime();
// Here we're guaranteed that some transaction flags are set
// so we can call handleTransactionLocked() unconditionally.
@@ -2486,7 +2464,6 @@
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
- mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
invalidateHwcGeometry();
// here the transaction has been committed
@@ -2511,6 +2488,9 @@
state.displayName = info->name;
mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
mInterceptor->saveDisplayCreation(state);
+ // TODO(b/123715322): Removes the per-display state that was added to the scheduler.
+ mRefreshRateConfigs[info->id] = std::make_shared<scheduler::RefreshRateConfigs>(
+ getHwComposer().getConfigs(info->id));
}
} else {
ALOGV("Removing display %s", to_string(info->id).c_str());
@@ -2522,6 +2502,7 @@
mCurrentState.displays.removeItemsAt(index);
}
mPhysicalDisplayTokens.erase(info->id);
+ mRefreshRateConfigs.erase(info->id);
}
processDisplayChangesLocked();
@@ -2766,7 +2747,7 @@
* (perform the transaction for each of them if needed)
*/
- if (transactionFlags & eTraversalNeeded) {
+ if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2779,6 +2760,7 @@
mInputInfoChanged = true;
}
});
+ mTraversalNeededMainThread = false;
}
/*
@@ -3267,15 +3249,13 @@
const auto& displayState = display->getState();
const auto displayId = display->getId();
auto& renderEngine = getRenderEngine();
- const bool supportProtectedContent =
- mDebugEnableProtectedContent && renderEngine.supportsProtectedContent();
+ const bool supportProtectedContent = renderEngine.supportsProtectedContent();
const Region bounds(displayState.bounds);
const DisplayRenderArea renderArea(displayDevice);
const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
ATRACE_INT("hasClientComposition", hasClientComposition);
- mat4 colorMatrix;
bool applyColorMatrix = false;
renderengine::DisplaySettings clientCompositionDisplay;
@@ -3314,6 +3294,7 @@
clientCompositionDisplay.clip = displayState.scissor;
const ui::Transform& displayTransform = displayState.transform;
clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
+ clientCompositionDisplay.orientation = displayState.orientation;
const auto* profile = display->getDisplayColorProfile();
Dataspace outputDataspace = Dataspace::UNKNOWN;
@@ -3333,7 +3314,7 @@
// Compute the global color transform matrix.
applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
if (applyColorMatrix) {
- clientCompositionDisplay.colorTransform = colorMatrix;
+ clientCompositionDisplay.colorTransform = displayState.colorTransformMat;
}
}
@@ -3424,7 +3405,8 @@
}
}
renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
- buf->getNativeBuffer(), std::move(fd), readyFence);
+ buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
+ readyFence);
if (expensiveRenderingExpected && displayId) {
mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
}
@@ -3504,26 +3486,34 @@
bool SurfaceFlinger::flushTransactionQueues() {
Mutex::Autolock _l(mStateLock);
+ bool flushedATransaction = false;
+
auto it = mTransactionQueues.begin();
while (it != mTransactionQueues.end()) {
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
const auto&
- [states, displays, flags, desiredPresentTime, uncacheBuffer, postTime,
- privileged] = transactionQueue.front();
+ [states, displays, flags, desiredPresentTime, uncacheBuffer, listenerCallbacks,
+ postTime, privileged] = transactionQueue.front();
if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+ setTransactionFlags(eTransactionFlushNeeded);
break;
}
applyTransactionState(states, displays, flags, mPendingInputWindowCommands,
- desiredPresentTime, uncacheBuffer, postTime, privileged,
- /*isMainThread*/ true);
+ desiredPresentTime, uncacheBuffer, listenerCallbacks, postTime,
+ privileged, /*isMainThread*/ true);
transactionQueue.pop();
+ flushedATransaction = true;
}
it = (transactionQueue.empty()) ? mTransactionQueues.erase(it) : std::next(it, 1);
}
- return mTransactionQueues.empty();
+ return flushedATransaction;
+}
+
+bool SurfaceFlinger::transactionFlushNeeded() {
+ return !mTransactionQueues.empty();
}
bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
@@ -3575,7 +3565,8 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime,
- const cached_buffer_t& uncacheBuffer) {
+ const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) {
ATRACE_CALL();
const int64_t postTime = systemTime();
@@ -3592,13 +3583,14 @@
if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
- uncacheBuffer, postTime, privileged);
- setTransactionFlags(eTransactionNeeded);
+ uncacheBuffer, listenerCallbacks, postTime,
+ privileged);
+ setTransactionFlags(eTransactionFlushNeeded);
return;
}
applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
- uncacheBuffer, postTime, privileged);
+ uncacheBuffer, listenerCallbacks, postTime, privileged);
}
void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
@@ -3606,6 +3598,7 @@
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime,
const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
const int64_t postTime, bool privileged,
bool isMainThread) {
uint32_t transactionFlags = 0;
@@ -3630,10 +3623,22 @@
transactionFlags |= setDisplayStateLocked(display);
}
+ // In case the client has sent a Transaction that should receive callbacks but without any
+ // SurfaceControls that should be included in the callback, send the listener and callbackIds
+ // to the callback thread so it can send an empty callback
+ if (!listenerCallbacks.empty()) {
+ mTransactionCompletedThread.run();
+ }
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ mTransactionCompletedThread.addCallback(listener, callbackIds);
+ }
+
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged);
+ clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
+ postTime, privileged);
}
+
// If the state doesn't require a traversal and there are callbacks, send them now
if (!(clientStateFlags & eTraversalNeeded)) {
mTransactionCompletedThread.sendCallbacks();
@@ -3655,6 +3660,13 @@
transactionFlags = eTransactionNeeded;
}
+ // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit
+ // so we don't have to wake up again next frame to preform an uneeded traversal.
+ if (isMainThread && (transactionFlags & eTraversalNeeded)) {
+ transactionFlags = transactionFlags & (~eTraversalNeeded);
+ mTraversalNeededMainThread = true;
+ }
+
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
@@ -3753,9 +3765,10 @@
return true;
}
-uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState,
- int64_t desiredPresentTime, int64_t postTime,
- bool privileged) {
+uint32_t SurfaceFlinger::setClientStateLocked(
+ const ComposerState& composerState, int64_t desiredPresentTime,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
+ bool privileged) {
const layer_state_t& s = composerState.state;
sp<Client> client(static_cast<Client*>(composerState.client.get()));
@@ -3981,9 +3994,8 @@
}
}
std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
- mTransactionCompletedThread.run();
- for (const auto& [listener, callbackIds] : s.listenerCallbacks) {
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
@@ -4244,7 +4256,7 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {});
+ setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
@@ -5008,9 +5020,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1033 are currently used for backdoors. The code
+ // Numbers from 1000 to 1034 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1033) {
+ if (code >= 1000 && code <= 1034) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5067,13 +5079,13 @@
}
case 1008: // toggle use of hw composer
n = data.readInt32();
- mDebugDisableHWC = n ? 1 : 0;
+ mDebugDisableHWC = n != 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
case 1009: // toggle use of transform hint
n = data.readInt32();
- mDebugDisableTransformHint = n ? 1 : 0;
+ mDebugDisableTransformHint = n != 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -5155,7 +5167,7 @@
}
case 1017: {
n = data.readInt32();
- mForceFullDamage = static_cast<bool>(n);
+ mForceFullDamage = n != 0;
return NO_ERROR;
}
case 1018: { // Modify Choreographer's phase offset
@@ -5313,11 +5325,6 @@
}
return NO_ERROR;
}
- case 1032: {
- n = data.readInt32();
- mDebugEnableProtectedContent = n;
- return NO_ERROR;
- }
// Set trace flags
case 1033: {
n = data.readUint32();
@@ -5326,6 +5333,18 @@
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
+ case 1034: {
+ // TODO(b/129297325): expose this via developer menu option
+ n = data.readInt32();
+ if (n && !mRefreshRateOverlay) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
+ mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+ } else if (!n) {
+ mRefreshRateOverlay.reset();
+ }
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5623,8 +5642,9 @@
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
- const auto sourceCrop = renderArea.getSourceCrop();
const auto rotation = renderArea.getRotationFlags();
+ const auto transform = renderArea.getTransform();
+ const auto sourceCrop = renderArea.getSourceCrop();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<renderengine::LayerSettings> clientCompositionLayers;
@@ -5632,31 +5652,34 @@
// assume that bounds are never offset, and that they are the same as the
// buffer bounds.
clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
- ui::Transform transform = renderArea.getTransform();
+ clientCompositionDisplay.clip = sourceCrop;
clientCompositionDisplay.globalTransform = transform.asMatrix4();
+
+ // Now take into account the rotation flag. We append a transform that
+ // rotates the layer stack about the origin, then translate by buffer
+ // boundaries to be in the right quadrant.
mat4 rotMatrix;
- // Displacement for repositioning the clipping rectangle after rotating it
- // with the rotation hint.
int displacementX = 0;
int displacementY = 0;
float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
switch (rotation) {
case ui::Transform::ROT_90:
rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
- displacementX = reqWidth;
+ displacementX = renderArea.getBounds().getHeight();
break;
case ui::Transform::ROT_180:
rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
- displacementX = reqWidth;
- displacementY = reqHeight;
+ displacementY = renderArea.getBounds().getWidth();
+ displacementX = renderArea.getBounds().getHeight();
break;
case ui::Transform::ROT_270:
rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
- displacementY = reqHeight;
+ displacementY = renderArea.getBounds().getWidth();
break;
default:
break;
}
+
// We need to transform the clipping window into the right spot.
// First, rotate the clipping rectangle by the rotation hint to get the
// right orientation
@@ -5672,15 +5695,14 @@
// Now reposition the clipping rectangle with the displacement vector
// computed above.
const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
-
clientCompositionDisplay.clip =
Rect(newClipLeft + displacementX, newClipTop + displacementY,
newClipRight + displacementX, newClipBottom + displacementY);
- // We need to perform the same transformation in layer space, so propagate
- // it to the global transform.
mat4 clipTransform = displacementMat * rotMatrix;
- clientCompositionDisplay.globalTransform *= clipTransform;
+ clientCompositionDisplay.globalTransform =
+ clipTransform * clientCompositionDisplay.globalTransform;
+
clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
@@ -5710,7 +5732,7 @@
base::unique_fd drawFence;
getRenderEngine().useProtectedContext(false);
getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
- std::move(bufferFence), &drawFence);
+ /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
*outSyncFd = drawFence.release();
}
@@ -5793,29 +5815,14 @@
mAllowedConfigs[*displayId] = std::move(allowedConfigs);
}
- // make sure that the current config is still allowed
- int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
- if (!isConfigAllowed(*displayId, currentConfigIndex)) {
- for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
- if (isConfigAllowed(*displayId, config.configId)) {
- // TODO: we switch to the first allowed config. In the future
- // we may want to enhance this logic to pick a similar config
- // to the current one
- ALOGV("Old config is not allowed - switching to config %d", config.configId);
- setDesiredActiveConfig(displayToken, config.configId,
- Scheduler::ConfigEvent::Changed);
- break;
- }
- }
- }
-
- // If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
- // there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
- if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
- const auto performanceRefreshRate =
- mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
- setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
+ // Set the highest allowed config by iterating backwards on available refresh rates
+ const auto& refreshRates = mRefreshRateConfigs[*displayId]->getRefreshRates();
+ for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+ if (iter->second && isConfigAllowed(*displayId, iter->second->configId)) {
+ ALOGV("switching to config %d", iter->second->configId);
+ setDesiredActiveConfig({iter->first, iter->second->configId, displayToken,
+ Scheduler::ConfigEvent::Changed});
+ break;
}
}
}
@@ -5885,14 +5892,11 @@
return NO_ERROR;
}
-// ----------------------------------------------------------------------------
-
-void SetInputWindowsListener::onSetInputWindowsFinished() {
+void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
mFlinger->setInputWindowsFinished();
}
-}; // namespace android
-
+} // namespace android
#if defined(__gl_h_)
#error "don't include gl/gl.h in this file"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 26d0cd1..5cd0f21 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_SURFACE_FLINGER_H
-#define ANDROID_SURFACE_FLINGER_H
+#pragma once
#include <sys/types.h>
@@ -48,27 +47,18 @@
#include <utils/threads.h>
#include "AllowedDisplayConfigs.h"
-#include "Barrier.h"
-#include "BufferStateLayerCache.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "Effects/Daltonizer.h"
#include "FrameTracker.h"
#include "LayerStats.h"
#include "LayerVector.h"
-#include "RegionSamplingThread.h"
-#include "Scheduler/DispSync.h"
-#include "Scheduler/EventThread.h"
-#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncModulator.h"
#include "SurfaceFlingerFactory.h"
-#include "SurfaceInterceptor.h"
#include "SurfaceTracing.h"
#include "TransactionCompletedThread.h"
@@ -90,53 +80,37 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Client;
-class ColorLayer;
-class DisplayEventConnection;
-class EventControlThread;
class EventThread;
-class IGraphicBufferConsumer;
+class HWComposer;
class IGraphicBufferProducer;
class IInputFlinger;
class InjectVSyncSource;
class Layer;
-class Surface;
-class SurfaceFlingerBE;
+class MessageBase;
+class RefreshRateOverlay;
+class RegionSamplingThread;
class TimeStats;
-class VSyncSource;
namespace compositionengine {
class DisplaySurface;
} // namespace compositionengine
-namespace impl {
-class EventThread;
-} // namespace impl
-
namespace renderengine {
class RenderEngine;
-}
-
-typedef std::function<void(const LayerVector::Visitor&)> TraverseLayersFunction;
+} // namespace renderengine
namespace dvr {
class VrFlinger;
} // namespace dvr
-namespace surfaceflinger {
-class NativeWindowSurface;
-} // namespace surfaceflinger
-
-// ---------------------------------------------------------------------------
-
enum {
- eTransactionNeeded = 0x01,
- eTraversalNeeded = 0x02,
+ eTransactionNeeded = 0x01,
+ eTraversalNeeded = 0x02,
eDisplayTransactionNeeded = 0x04,
eDisplayLayerStackChanged = 0x08,
- eTransactionMask = 0x0f,
+ eTransactionFlushNeeded = 0x10,
+ eTransactionMask = 0x1f,
};
enum class DisplayColorSetting : int32_t {
@@ -161,35 +135,28 @@
// Only accessed from the main thread.
struct CompositePresentTime {
- nsecs_t composite { -1 };
- std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE };
+ nsecs_t composite = -1;
+ std::shared_ptr<FenceTime> display = FenceTime::NO_FENCE;
};
std::queue<CompositePresentTime> mCompositePresentTimes;
static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
- nsecs_t mFrameBuckets[NUM_BUCKETS];
- nsecs_t mTotalTime;
- std::atomic<nsecs_t> mLastSwapTime;
+ nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
+ nsecs_t mTotalTime = 0;
+ std::atomic<nsecs_t> mLastSwapTime = 0;
// Double- vs. triple-buffering stats
struct BufferingStats {
- BufferingStats()
- : numSegments(0),
- totalTime(0),
- twoBufferTime(0),
- doubleBufferedTime(0),
- tripleBufferedTime(0) {}
-
- size_t numSegments;
- nsecs_t totalTime;
+ size_t numSegments = 0;
+ nsecs_t totalTime = 0;
// "Two buffer" means that a third buffer was never used, whereas
// "double-buffered" means that on average the segment only used two
// buffers (though it may have used a third for some part of the
// segment)
- nsecs_t twoBufferTime;
- nsecs_t doubleBufferedTime;
- nsecs_t tripleBufferedTime;
+ nsecs_t twoBufferTime = 0;
+ nsecs_t doubleBufferedTime = 0;
+ nsecs_t tripleBufferedTime = 0;
};
mutable Mutex mBufferingStatsMutex;
std::unordered_map<std::string, BufferingStats> mBufferingStats;
@@ -197,16 +164,7 @@
// The composer sequence id is a monotonically increasing integer that we
// use to differentiate callbacks from different hardware composer
// instances. Each hardware composer instance gets a different sequence id.
- int32_t mComposerSequenceId;
-};
-
-class SetInputWindowsListener : public BnSetInputWindowsListener {
-public:
- SetInputWindowsListener(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger) {}
- void onSetInputWindowsFinished() override;
-
-private:
- const sp<SurfaceFlinger> mFlinger;
+ int32_t mComposerSequenceId = 0;
};
class SurfaceFlinger : public BnSurfaceComposer,
@@ -355,17 +313,14 @@
return mTransactionCompletedThread;
}
- void setInputWindowsFinished();
-
private:
- friend class Client;
- friend class DisplayEventConnection;
- friend class impl::EventThread;
- friend class Layer;
friend class BufferLayer;
friend class BufferQueueLayer;
friend class BufferStateLayer;
+ friend class Client;
+ friend class Layer;
friend class MonitoredProducer;
+ friend class RefreshRateOverlay;
friend class RegionSamplingThread;
friend class SurfaceTracing;
@@ -431,8 +386,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime,
- const cached_buffer_t& uncacheBuffer) override;
+ int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -528,11 +483,32 @@
void signalLayerUpdate();
void signalRefresh();
+ using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
+ struct ActiveConfigInfo {
+ RefreshRateType type;
+ int configId;
+ sp<IBinder> displayToken;
+ Scheduler::ConfigEvent event;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ if (type != other.type) {
+ return true;
+ }
+ if (configId != other.configId) {
+ return true;
+ }
+ if (displayToken != other.displayToken) {
+ return true;
+ }
+ return (event != other.event);
+ }
+ };
+
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
- void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
- Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+ void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
// Once HWC has returned the present fence, this sets the active config and a new refresh
// rate in SF. It also triggers HWC vsync.
void setActiveConfigInternal() REQUIRES(mStateLock);
@@ -564,6 +540,7 @@
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
void executeInputWindowCommands();
+ void setInputWindowsFinished();
void updateCursorAsync();
/* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -579,9 +556,14 @@
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime,
- const cached_buffer_t& uncacheBuffer, const int64_t postTime,
- bool privileged, bool isMainThread = false) REQUIRES(mStateLock);
+ const cached_buffer_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ const int64_t postTime, bool privileged, bool isMainThread = false)
+ REQUIRES(mStateLock);
+ // Returns true if at least one transaction was flushed
bool flushTransactionQueues();
+ // Returns true if there is at least one transaction that needs to be flushed
+ bool transactionFlushNeeded();
uint32_t getTransactionFlags(uint32_t flags);
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
@@ -593,6 +575,7 @@
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states);
uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
int64_t postTime, bool privileged) REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -647,6 +630,8 @@
void startBootAnim();
+ using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
int* outSyncFd);
@@ -815,8 +800,7 @@
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType,
- Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+ void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
bool isConfigAllowed(const DisplayId& displayId, int32_t config);
@@ -933,11 +917,12 @@
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
- std::atomic<int32_t> mTransactionFlags{0};
+ std::atomic<int32_t> mTransactionFlags = 0;
Condition mTransactionCV;
- bool mTransactionPending;
- bool mAnimTransactionPending;
- SortedVector< sp<Layer> > mLayersPendingRemoval;
+ bool mTransactionPending = false;
+ bool mAnimTransactionPending = false;
+ SortedVector<sp<Layer>> mLayersPendingRemoval;
+ bool mTraversalNeededMainThread = false;
// guards access to the mDrawing state if tracing is enabled.
mutable std::mutex mDrawingStateLock;
@@ -952,31 +937,30 @@
size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
// protected by mStateLock (but we could use another lock)
- bool mLayersRemoved;
- bool mLayersAdded;
+ bool mLayersRemoved = false;
+ bool mLayersAdded = false;
- std::atomic<bool> mRepaintEverything{false};
+ std::atomic<bool> mRepaintEverything = false;
// constant members (no synchronization needed for access)
- nsecs_t mBootTime;
- bool mGpuToCpuSupported;
+ const nsecs_t mBootTime = systemTime();
+ bool mGpuToCpuSupported = false;
std::unique_ptr<EventThread> mInjectorEventThread;
std::unique_ptr<InjectVSyncSource> mVSyncInjector;
- std::unique_ptr<EventControlThread> mEventControlThread;
// Calculates correct offsets.
VSyncModulator mVsyncModulator;
// Keeps track of all available phase offsets for different refresh types.
- std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+ const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
// Can only accessed from the main thread, these members
// don't need synchronization
State mDrawingState{LayerVector::StateSet::Drawing};
- bool mVisibleRegionsDirty;
+ bool mVisibleRegionsDirty = false;
// Set during transaction commit stage to track if the input info for a layer has changed.
- bool mInputInfoChanged{false};
- bool mGeometryInvalid;
- bool mAnimCompositionPending;
+ bool mInputInfoChanged = false;
+ bool mGeometryInvalid = false;
+ bool mAnimCompositionPending = false;
std::vector<sp<Layer>> mLayersWithQueuedFrames;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
@@ -993,7 +977,7 @@
BOOTANIMATION,
FINISHED,
};
- BootStage mBootStage;
+ BootStage mBootStage = BootStage::BOOTLOADER;
struct HotplugEvent {
hwc2_display_t hwcDisplayId;
@@ -1008,26 +992,22 @@
std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
// don't use a lock for these, we don't care
- int mDebugRegion;
- int mDebugDisableHWC;
- int mDebugDisableTransformHint;
- bool mDebugEnableProtectedContent;
- volatile nsecs_t mDebugInSwapBuffers;
- volatile nsecs_t mDebugInTransaction;
- nsecs_t mLastTransactionTime;
- nsecs_t mPostFramebufferTime;
- bool mForceFullDamage;
+ int mDebugRegion = 0;
+ bool mDebugDisableHWC = false;
+ bool mDebugDisableTransformHint = false;
+ volatile nsecs_t mDebugInTransaction = 0;
+ bool mForceFullDamage = false;
bool mPropagateBackpressure = true;
- std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
- SurfaceTracing mTracing;
+ std::unique_ptr<SurfaceInterceptor> mInterceptor;
+ SurfaceTracing mTracing{*this};
bool mTracingEnabled = false;
bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
LayerStats mLayerStats;
- std::shared_ptr<TimeStats> mTimeStats;
+ const std::shared_ptr<TimeStats> mTimeStats;
bool mUseHwcVirtualDisplays = false;
- std::atomic<uint32_t> mFrameMissedCount{0};
- std::atomic<uint32_t> mHwcFrameMissedCount{0};
- std::atomic<uint32_t> mGpuFrameMissedCount{0};
+ std::atomic<uint32_t> mFrameMissedCount = 0;
+ std::atomic<uint32_t> mHwcFrameMissedCount = 0;
+ std::atomic<uint32_t> mGpuFrameMissedCount = 0;
TransactionCompletedThread mTransactionCompletedThread;
@@ -1035,16 +1015,16 @@
bool mLayerTripleBufferingDisabled = false;
// these are thread safe
- mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()};
+ std::unique_ptr<MessageQueue> mEventQueue;
FrameTracker mAnimFrameTracker;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
Vector<Layer const *> mDestroyedLayers;
- nsecs_t mRefreshStartTime;
+ nsecs_t mRefreshStartTime = 0;
- std::atomic<bool> mRefreshPending{false};
+ std::atomic<bool> mRefreshPending = false;
// We maintain a pool of pre-generated texture names to hand out to avoid
// layer creation needing to run on the main thread (which it would
@@ -1062,12 +1042,14 @@
TransactionState(const Vector<ComposerState>& composerStates,
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer,
- int64_t postTime, bool privileged)
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
+ bool privileged)
: states(composerStates),
displays(displayStates),
flags(transactionFlags),
desiredPresentTime(desiredPresentTime),
buffer(uncacheBuffer),
+ callback(listenerCallbacks),
postTime(postTime),
privileged(privileged) {}
@@ -1076,6 +1058,7 @@
uint32_t flags;
const int64_t desiredPresentTime;
cached_buffer_t buffer;
+ std::vector<ListenerCallbacks> callback;
const int64_t postTime;
bool privileged;
};
@@ -1085,21 +1068,21 @@
* Feature prototyping
*/
- bool mInjectVSyncs;
+ bool mInjectVSyncs = false;
// Static screen stats
- bool mHasPoweredOff;
+ bool mHasPoweredOff = false;
- size_t mNumLayers;
+ size_t mNumLayers = 0;
// Verify that transaction is being called by an approved process:
// either AID_GRAPHICS or AID_SYSTEM.
status_t CheckTransactCodeCredentials(uint32_t code);
std::unique_ptr<dvr::VrFlinger> mVrFlinger;
- std::atomic<bool> mVrFlingerRequestsDisplay;
+ std::atomic<bool> mVrFlingerRequestsDisplay = false;
static bool useVrFlinger;
- std::thread::id mMainThreadId;
+ std::thread::id mMainThreadId = std::this_thread::get_id();
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED;
@@ -1134,18 +1117,6 @@
std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs
GUARDED_BY(mAllowedConfigsLock);
- struct ActiveConfigInfo {
- int configId;
- sp<IBinder> displayToken;
- Scheduler::ConfigEvent event;
-
- bool operator!=(const ActiveConfigInfo& other) const {
- if (configId != other.configId) {
- return true;
- }
- return (displayToken != other.displayToken);
- }
- };
std::mutex mActiveConfigLock;
// This bit is set once we start setting the config. We read from this bit during the
// process. If at the end, this bit is different than mDesiredActiveConfig, we restart
@@ -1158,21 +1129,30 @@
bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
bool mCheckPendingFence = false;
- /* ------------------------------------------------------------------------ */
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
+ ui::DisplayPrimaries mInternalDisplayPrimaries;
sp<IInputFlinger> mInputFlinger;
-
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
// Should only be accessed by the main thread.
InputWindowCommands mInputWindowCommands;
- ui::DisplayPrimaries mInternalDisplayPrimaries;
- sp<SetInputWindowsListener> mSetInputWindowsListener;
+ struct SetInputWindowsListener : BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
+ : mFlinger(std::move(flinger)) {}
+
+ void onSetInputWindowsFinished() override;
+
+ const sp<SurfaceFlinger> mFlinger;
+ };
+
+ const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
+
bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
Hwc2::impl::PowerAdvisor mPowerAdvisor;
-};
-}; // namespace android
-#endif // ANDROID_SURFACE_FLINGER_H
+ std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index e130511..3522429 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -1,5 +1,18 @@
-
-#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+/*
+ * 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 <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
@@ -225,6 +238,14 @@
return defaultValue;
}
+bool enable_protected_contents(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::enable_protected_contents();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
#define DISPLAY_PRIMARY_SIZE 3
constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 6f90117..1864290 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -1,10 +1,25 @@
+/*
+ * 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.
+ */
#ifndef SURFACEFLINGERPROPERTIES_H_
#define SURFACEFLINGERPROPERTIES_H_
+#include <SurfaceFlingerProperties.sysprop.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/graphics/common/1.2/types.h>
-#include <sysprop/SurfaceFlingerProperties.sysprop.h>
#include <ui/ConfigStoreTypes.h>
#include <cstdint>
@@ -59,6 +74,8 @@
bool use_smart_90_for_video(bool defaultValue);
+bool enable_protected_contents(bool defaultValue);
+
android::ui::DisplayPrimaries getDisplayNativePrimaries();
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 4be2ee9..4773307 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -41,7 +41,7 @@
*/
class SurfaceTracing {
public:
- SurfaceTracing(SurfaceFlinger& flinger);
+ explicit SurfaceTracing(SurfaceFlinger& flinger);
void enable();
bool disable();
status_t writeToFile();
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index d2b7fe0..6b2b583 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -29,6 +29,19 @@
namespace android {
+// Returns 0 if they are equal
+// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
+// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
+//
+// See CallbackIdsHash for a explaniation of why this works
+static int compareCallbackIds(const std::vector<CallbackId>& c1,
+ const std::vector<CallbackId>& c2) {
+ if (c1.empty()) {
+ return !c2.empty();
+ }
+ return c1.front() - c2.front();
+}
+
TransactionCompletedThread::~TransactionCompletedThread() {
std::lock_guard lockThread(mThreadMutex);
@@ -44,8 +57,8 @@
{
std::lock_guard lock(mMutex);
- for (const auto& [listener, listenerStats] : mListenerStats) {
- listener->unlinkToDeath(mDeathRecipient);
+ for (const auto& [listener, transactionStats] : mCompletedTransactions) {
+ IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
}
}
}
@@ -62,64 +75,128 @@
mThread = std::thread(&TransactionCompletedThread::threadMain, this);
}
-void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
+status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds) {
std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add callback because the callback thread isn't running");
+ return BAD_VALUE;
+ }
- sp<IBinder> listener = IInterface::asBinder(handle->listener);
- const auto& callbackIds = handle->callbackIds;
+ if (mCompletedTransactions.count(listener) == 0) {
+ status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+ return err;
+ }
+ }
- mPendingTransactions[listener][callbackIds]++;
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
+ return NO_ERROR;
}
-void TransactionCompletedThread::addPresentedCallbackHandles(
+status_t TransactionCompletedThread::registerPendingCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot register callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to register a pending callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ ALOGE("cannot find transaction stats");
+ return err;
+ }
+
+ mPendingTransactions[handle->listener][handle->callbackIds]++;
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addPresentedCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles) {
std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add presented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
for (const auto& handle : handles) {
- auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
- auto& pendingCallbacks = listener->second;
- auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+ auto listener = mPendingTransactions.find(handle->listener);
+ if (listener != mPendingTransactions.end()) {
+ auto& pendingCallbacks = listener->second;
+ auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
- if (pendingCallback != pendingCallbacks.end()) {
- auto& pendingCount = pendingCallback->second;
+ if (pendingCallback != pendingCallbacks.end()) {
+ auto& pendingCount = pendingCallback->second;
- // Decrease the pending count for this listener
- if (--pendingCount == 0) {
- pendingCallbacks.erase(pendingCallback);
+ // Decrease the pending count for this listener
+ if (--pendingCount == 0) {
+ pendingCallbacks.erase(pendingCallback);
+ }
+ } else {
+ ALOGW("there are more latched callbacks than there were registered callbacks");
}
} else {
- ALOGE("there are more latched callbacks than there were registered callbacks");
+ ALOGW("cannot find listener in mPendingTransactions");
}
- addCallbackHandle(handle);
+ status_t err = addCallbackHandle(handle);
+ if (err != NO_ERROR) {
+ ALOGE("could not add callback handle");
+ return err;
+ }
}
+
+ return NO_ERROR;
}
-void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
+status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+ const sp<CallbackHandle>& handle) {
std::lock_guard lock(mMutex);
- addCallbackHandle(handle);
+ if (!mRunning) {
+ ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ return addCallbackHandle(handle);
}
-void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
- const sp<IBinder> listener = IInterface::asBinder(handle->listener);
+status_t TransactionCompletedThread::findTransactionStats(
+ const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
- // If we don't already have a reference to this listener, linkToDeath so we get a notification
- // if it dies.
- if (mListenerStats.count(listener) == 0) {
- status_t error = listener->linkToDeath(mDeathRecipient);
- if (error != NO_ERROR) {
- ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error);
- return;
+ // Search back to front because the most recent transactions are at the back of the deque
+ auto itr = transactionStatsDeque.rbegin();
+ for (; itr != transactionStatsDeque.rend(); itr++) {
+ if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+ *outTransactionStats = &(*itr);
+ return NO_ERROR;
}
}
- auto& listenerStats = mListenerStats[listener];
- listenerStats.listener = handle->listener;
+ ALOGE("could not find transaction stats");
+ return BAD_VALUE;
+}
- auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
- transactionStats.latchTime = handle->latchTime;
- transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
- handle->previousReleaseFence);
+status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to add a presnted callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transactionStats->latchTime = handle->latchTime;
+ transactionStats->surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+ handle->previousReleaseFence);
+ return NO_ERROR;
}
void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
@@ -141,40 +218,46 @@
mConditionVariable.wait(mMutex);
// For each listener
- auto it = mListenerStats.begin();
- while (it != mListenerStats.end()) {
- auto& [listener, listenerStats] = *it;
+ auto completedTransactionsItr = mCompletedTransactions.begin();
+ while (completedTransactionsItr != mCompletedTransactions.end()) {
+ auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
// For each transaction
- bool sendCallback = true;
- for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- // If we are still waiting on the callback handles for this transaction, skip it
- if (mPendingTransactions[listener].count(callbackIds) != 0) {
- sendCallback = false;
+ auto transactionStatsItr = transactionStatsDeque.begin();
+ while (transactionStatsItr != transactionStatsDeque.end()) {
+ auto& transactionStats = *transactionStatsItr;
+
+ // If we are still waiting on the callback handles for this transaction, stop
+ // here because all transaction callbacks for the same listener must come in order
+ if (mPendingTransactions[listener].count(transactionStats.callbackIds) != 0) {
break;
}
// If the transaction has been latched
if (transactionStats.latchTime >= 0) {
if (!mPresentFence) {
- sendCallback = false;
break;
}
transactionStats.presentFence = mPresentFence;
}
+
+ // Remove the transaction from completed to the callback
+ listenerStats.transactionStats.push_back(std::move(transactionStats));
+ transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
}
- // If the listener has no pending transactions and all latched transactions have been
- // presented
- if (sendCallback) {
+ // If the listener has completed transactions
+ if (!listenerStats.transactionStats.empty()) {
// If the listener is still alive
- if (listener->isBinderAlive()) {
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
// Send callback
listenerStats.listener->onTransactionCompleted(listenerStats);
- listener->unlinkToDeath(mDeathRecipient);
+ IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
}
- it = mListenerStats.erase(it);
+ completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
} else {
- it++;
+ completedTransactionsItr++;
}
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index f49306d..21e2678 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -30,6 +30,18 @@
namespace android {
+struct CallbackIdsHash {
+ // CallbackId vectors have several properties that let us get away with this simple hash.
+ // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+ // empty we can still hash 0.
+ // 2) CallbackId vectors for the same listener either are identical or contain none of the
+ // same members. It is sufficient to just check the first CallbackId in the vectors. If
+ // they match, they are the same. If they do not match, they are not the same.
+ std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
+ return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+ }
+};
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
@@ -51,18 +63,24 @@
void run();
+ // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
+ // to be included in the callback. This functions should be call before attempting to add any
+ // callback handles.
+ status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
+ const std::vector<CallbackId>& callbackIds);
+
// Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
// that needs to be latched and presented this frame. This function should be called once the
// layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
// a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
// presented.
- void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+ status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
- void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+ status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
- void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+ status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
void addPresentFence(const sp<Fence>& presentFence);
@@ -71,7 +89,11 @@
private:
void threadMain();
- void addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
+ status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds,
+ TransactionStats** outTransactionStats) REQUIRES(mMutex);
+
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
class ThreadDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -84,9 +106,10 @@
};
sp<ThreadDeathRecipient> mDeathRecipient;
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& strongPointer) const {
- return std::hash<IBinder*>{}(strongPointer.get());
+ struct ITransactionCompletedListenerHash {
+ std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
+ return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
+ : nullptr);
}
};
@@ -99,12 +122,13 @@
std::condition_variable_any mConditionVariable;
std::unordered_map<
- sp<IBinder /*listener*/>,
+ sp<ITransactionCompletedListener>,
std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
- IBinderHash>
+ ITransactionCompletedListenerHash>
mPendingTransactions GUARDED_BY(mMutex);
- std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
- GUARDED_BY(mMutex);
+ std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
+ ITransactionCompletedListenerHash>
+ mCompletedTransactions GUARDED_BY(mMutex);
bool mRunning GUARDED_BY(mMutex) = false;
bool mKeepRunning GUARDED_BY(mMutex) = true;
diff --git a/services/surfaceflinger/sysprop/Android.bp b/services/surfaceflinger/sysprop/Android.bp
new file mode 100644
index 0000000..7721d7d2
--- /dev/null
+++ b/services/surfaceflinger/sysprop/Android.bp
@@ -0,0 +1,6 @@
+sysprop_library {
+ name: "SurfaceFlingerProperties",
+ srcs: ["*.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index fe6dc93..830c03e 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -307,3 +307,11 @@
access: Readonly
prop_name: "ro.surface_flinger.use_smart_90_for_video"
}
+
+prop {
+ api_name: "enable_protected_contents"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.protected_contents"
+}
diff --git a/services/surfaceflinger/sysprop/api/current.txt b/services/surfaceflinger/sysprop/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/removed.txt b/services/surfaceflinger/sysprop/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/system-removed.txt b/services/surfaceflinger/sysprop/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/test-current.txt b/services/surfaceflinger/sysprop/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/test-removed.txt b/services/surfaceflinger/sysprop/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 25ce4ac..12b41fd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -47,6 +47,7 @@
"LayerMetadataTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "RefreshRateConfigsTest.cpp",
"RefreshRateStatsTest.cpp",
"TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ea2818d..ef3dfef 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -175,7 +175,6 @@
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::DispSync* mPrimaryDispSync = new mock::DispSync();
- renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -312,8 +311,8 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly(
[](const renderengine::DisplaySettings& displaySettings,
- const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
- ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*) -> status_t {
+ const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -351,8 +350,8 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly(
[](const renderengine::DisplaySettings& displaySettings,
- const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
- ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*) -> status_t {
+ const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -581,7 +580,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- ANativeWindowBuffer*, base::unique_fd&&,
+ ANativeWindowBuffer*, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -624,7 +623,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- ANativeWindowBuffer*, base::unique_fd&&,
+ ANativeWindowBuffer*, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -694,7 +693,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- ANativeWindowBuffer*, base::unique_fd&&,
+ ANativeWindowBuffer*, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 406ec81..249c78f 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -38,6 +38,7 @@
constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL;
class MockVSyncSource : public VSyncSource {
public:
@@ -470,5 +471,10 @@
expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
}
+TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
+ mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
new file mode 100644
index 0000000..b218ad6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "DisplayHardware/HWC2.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+class RefreshRateConfigsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_60 = 0;
+ static constexpr int CONFIG_ID_90 = 1;
+ static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr int64_t VSYNC_90 = 11111111;
+
+ RefreshRateConfigsTest();
+ ~RefreshRateConfigsTest();
+
+ void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
+ ASSERT_EQ(left.configId, right.configId);
+ ASSERT_EQ(left.name, right.name);
+ ASSERT_EQ(left.fps, right.fps);
+ }
+};
+
+RefreshRateConfigsTest::RefreshRateConfigsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ RefreshRateConfigs configs(displayConfigs);
+
+ // We always store a configuration for screen off.
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(1, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
+
+ RefreshRate expectedConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedConfig, *powerSavingRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedConfig, *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(1, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(2, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(2, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ displayConfigs.push_back(config90.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(3, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_NE(rates.end(), performanceRate);
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+ RefreshRate expectedPerformanceConfig = RefreshRate{CONFIG_ID_90, "90fps", 90};
+ assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ assertRatesEqual(expectedPerformanceConfig,
+ *configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 46fd964..81235ba 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -31,6 +31,7 @@
#include "FakePhaseOffsets.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
+#include "Scheduler/MessageQueue.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
@@ -276,8 +277,9 @@
auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); }
auto captureScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer,
- bool useIdentityTransform, bool forSystem, int* outSyncFd) {
+ SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ bool forSystem, int* outSyncFd) {
return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
useIdentityTransform, forSystem, outSyncFd);
}
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 4e24a64..afb3004 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -44,9 +44,6 @@
static_libs: [
"libbufferhub",
],
-
- // TODO(b/117568153): Temporarily opt out using libcrt.
- no_libcrt: true,
}
cc_binary {
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 206c8eb..71a120a 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -76,6 +76,7 @@
"libhardware",
"libsync",
"libbase",
+ "libdl_android",
"libhidlbase",
"libhidltransport",
"liblog",