Merge "GraphicsEnv: refactor to unify the debuggable logic"
diff --git a/Android.bp b/Android.bp
index bf4cf5d..9829c7f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7,6 +7,7 @@
}
subdirs = [
+ "adbd_auth",
"cmds/*",
"headers",
"libs/*",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5469d0c..61e22a4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -154,6 +154,7 @@
#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat"
#define WLUTIL "/vendor/xbin/wlutil"
#define WMTRACE_DATA_DIR "/data/misc/wmtrace"
+#define OTA_METADATA_DIR "/metadata/ota"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -1546,6 +1547,7 @@
add_mountinfo();
DumpIpTablesAsRoot();
DumpDynamicPartitionInfo();
+ ds.AddDir(OTA_METADATA_DIR, true);
// Capture any IPSec policies in play. No keys are exposed here.
RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build());
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index cf4e0b5..b9395ba 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -54,6 +54,7 @@
MOCK_METHOD4(addService, status_t(const String16&, const sp<IBinder>&, bool, int));
MOCK_METHOD1(listServices, Vector<String16>(int));
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
+ MOCK_METHOD1(isDeclared, bool(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 838d11d..768d900 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -713,6 +713,7 @@
static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 5;
class RunProfman : public ExecVHelper {
public:
@@ -720,7 +721,9 @@
const unique_fd& reference_profile_fd,
const std::vector<unique_fd>& apk_fds,
const std::vector<std::string>& dex_locations,
- bool copy_and_update) {
+ bool copy_and_update,
+ bool for_snapshot,
+ bool for_boot_image) {
// TODO(calin): Assume for now we run in the bg compile job (which is in
// most of the invocation). With the current data flow, is not very easy or
@@ -752,6 +755,14 @@
AddArg("--copy-and-update-profile-key");
}
+ if (for_snapshot) {
+ AddArg("--force-merge");
+ }
+
+ if (for_boot_image) {
+ AddArg("--boot-image-merge");
+ }
+
// Do not add after dex2oat_flags, they should override others for debugging.
PrepareArgs(profman_bin);
}
@@ -759,12 +770,16 @@
void SetupMerge(const std::vector<unique_fd>& profiles_fd,
const unique_fd& reference_profile_fd,
const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(),
- const std::vector<std::string>& dex_locations = std::vector<std::string>()) {
+ const std::vector<std::string>& dex_locations = std::vector<std::string>(),
+ bool for_snapshot = false,
+ bool for_boot_image = false) {
SetupArgs(profiles_fd,
reference_profile_fd,
apk_fds,
dex_locations,
- /*copy_and_update=*/false);
+ /*copy_and_update=*/ false,
+ for_snapshot,
+ for_boot_image);
}
void SetupCopyAndUpdate(unique_fd&& profile_fd,
@@ -781,7 +796,9 @@
reference_profile_fd_,
apk_fds_,
dex_locations,
- /*copy_and_update=*/true);
+ /*copy_and_update=*/true,
+ /*for_snapshot*/false,
+ /*for_boot_image*/false);
}
void SetupDump(const std::vector<unique_fd>& profiles_fd,
@@ -795,7 +812,9 @@
reference_profile_fd,
apk_fds,
dex_locations,
- /*copy_and_update=*/false);
+ /*copy_and_update=*/false,
+ /*for_snapshot*/false,
+ /*for_boot_image*/false);
}
void Exec() {
@@ -872,7 +891,7 @@
break;
default:
// Unknown return code or error. Unlink profiles.
- LOG(WARNING) << "Unknown error code while processing profiles for location "
+ LOG(WARNING) << "Unexpected error code while processing profiles for location "
<< location << ": " << return_code;
need_to_compile = false;
should_clear_current_profiles = true;
@@ -2741,7 +2760,7 @@
}
RunProfman args;
- args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations);
+ args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, /*for_snapshot=*/true);
pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
@@ -2756,6 +2775,13 @@
return false;
}
+ // Verify that profman finished successfully.
+ int profman_code = WEXITSTATUS(return_code);
+ if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) {
+ LOG(WARNING) << "profman error for " << package_name << ":" << profile_name
+ << ":" << profman_code;
+ return false;
+ }
return true;
}
@@ -2818,19 +2844,29 @@
// We do this to avoid opening a huge a amount of files.
static constexpr size_t kAggregationBatchSize = 10;
- std::vector<unique_fd> profiles_fd;
for (size_t i = 0; i < profiles.size(); ) {
+ std::vector<unique_fd> profiles_fd;
for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
if (fd.get() >= 0) {
profiles_fd.push_back(std::move(fd));
}
}
+
+ // We aggregate (read & write) into the same fd multiple times in a row.
+ // We need to reset the cursor every time to ensure we read the whole file every time.
+ if (TEMP_FAILURE_RETRY(lseek(snapshot_fd, 0, SEEK_SET)) == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Cannot reset position for snapshot profile";
+ return false;
+ }
+
RunProfman args;
args.SetupMerge(profiles_fd,
snapshot_fd,
apk_fds,
- dex_locations);
+ dex_locations,
+ /*for_snapshot=*/true,
+ /*for_boot_image=*/true);
pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
@@ -2843,12 +2879,21 @@
/* parent */
int return_code = wait_child(pid);
+
if (!WIFEXITED(return_code)) {
PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
return false;
}
- return true;
+
+ // Verify that profman finished successfully.
+ int profman_code = WEXITSTATUS(return_code);
+ if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) {
+ LOG(WARNING) << "profman error for " << package_name << ":" << profile_name
+ << ":" << profman_code;
+ return false;
+ }
}
+
return true;
}
diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh
index 1075688..0e6d7b9 100644
--- a/cmds/installd/migrate_legacy_obb_data.sh
+++ b/cmds/installd/migrate_legacy_obb_data.sh
@@ -15,17 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-rm -rf /sdcard/Android/obb/test_probe
-mkdir -p /sdcard/Android/obb/
-touch /sdcard/Android/obb/test_probe
+rm -rf /data/media/Android/obb/test_probe
+mkdir -p /data/media/Android/obb/
+touch /data/media/Android/obb/test_probe
if ! test -f /data/media/0/Android/obb/test_probe ; then
log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating"
- rm -rf /sdcard/Android/obb/test_probe
+ rm -rf /data/media/Android/obb/test_probe
exit 0
fi
# Delete the test file, and remove the obb folder if it is empty
-rm -rf /sdcard/Android/obb/test_probe
+rm -rf /data/media/Android/obb/test_probe
rmdir /data/media/obb
if ! test -d /data/media/obb ; then
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 0212bc5..69fefa1 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -897,7 +897,9 @@
std::string expected_profile_content = snap_profile_ + ".expected";
run_cmd("rm -f " + expected_profile_content);
run_cmd("touch " + expected_profile_content);
- run_cmd("profman --profile-file=" + cur_profile_ +
+ // We force merging when creating the expected profile to make sure
+ // that the random profiles do not affect the output.
+ run_cmd("profman --force-merge --profile-file=" + cur_profile_ +
" --profile-file=" + ref_profile_ +
" --reference-profile-file=" + expected_profile_content +
" --apk=" + apk_path_);
@@ -1130,16 +1132,60 @@
class BootProfileTest : public ProfileTest {
public:
- virtual void setup() {
+ std::vector<const std::string> extra_apps_;
+ std::vector<int64_t> extra_ce_data_inodes_;
+
+ virtual void SetUp() {
+
ProfileTest::SetUp();
intial_android_profiles_dir = android_profiles_dir;
+ // Generate profiles for some extra apps.
+ // When merging boot profile we split profiles into small groups to avoid
+ // opening a lot of file descriptors at the same time.
+ // (Currently the group size for aggregation is 10)
+ //
+ // To stress test that works fine, create profile for more apps.
+ createAppProfilesForBootMerge(21);
}
virtual void TearDown() {
android_profiles_dir = intial_android_profiles_dir;
+ deleteAppProfilesForBootMerge();
ProfileTest::TearDown();
}
+ void createAppProfilesForBootMerge(size_t number_of_profiles) {
+ for (size_t i = 0; i < number_of_profiles; i++) {
+ int64_t ce_data_inode;
+ std::string package_name = "dummy_test_pkg" + std::to_string(i);
+ LOG(INFO) << package_name;
+ ASSERT_BINDER_SUCCESS(service_->createAppData(
+ volume_uuid_,
+ package_name,
+ kTestUserId,
+ kAppDataFlags,
+ kTestAppUid,
+ se_info_,
+ kOSdkVersion,
+ &ce_data_inode));
+ extra_apps_.push_back(package_name);
+ extra_ce_data_inodes_.push_back(ce_data_inode);
+ std::string profile = create_current_profile_path(
+ kTestUserId, package_name, kPrimaryProfile, /*is_secondary_dex*/ false);
+ SetupProfile(profile, kTestAppUid, kTestAppGid, 0600, 1);
+ }
+ }
+
+ void deleteAppProfilesForBootMerge() {
+ if (kDebug) {
+ return;
+ }
+ for (size_t i = 0; i < extra_apps_.size(); i++) {
+ service_->destroyAppData(
+ volume_uuid_, extra_apps_[i], kTestUserId, kAppDataFlags, extra_ce_data_inodes_[i]);
+ }
+ }
+
void UpdateAndroidProfilesDir(const std::string& profile_dir) {
android_profiles_dir = profile_dir;
// We need to create the reference profile directory in the new profile dir.
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 934646d..861401c 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -34,11 +34,7 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
-static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
- if (!Stability::requiresVintfDeclaration(binder)) {
- return true;
- }
-
+static bool isVintfDeclared(const std::string& name) {
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
if (firstSlash == std::string::npos || lastDot == std::string::npos) {
@@ -54,7 +50,7 @@
vintf::VintfObject::GetDeviceHalManifest(),
vintf::VintfObject::GetFrameworkHalManifest()
}) {
- if (manifest->hasAidlInstance(package, iface, instance)) {
+ if (manifest != nullptr && manifest->hasAidlInstance(package, iface, instance)) {
return true;
}
}
@@ -62,6 +58,14 @@
<< " in the VINTF manifest.";
return false;
}
+
+static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
+ if (!Stability::requiresVintfDeclaration(binder)) {
+ return true;
+ }
+
+ return isVintfDeclared(name);
+}
#endif // !VENDORSERVICEMANAGER
ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}
@@ -270,6 +274,21 @@
return Status::ok();
}
+Status ServiceManager::isDeclared(const std::string& name, bool* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ if (!mAccess->canFind(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ *outReturn = false;
+
+#ifndef VENDORSERVICEMANAGER
+ *outReturn = isVintfDeclared(name);
+#endif
+ return Status::ok();
+}
+
void ServiceManager::removeCallback(const wp<IBinder>& who,
CallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 006e519..7dcdaa4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -40,6 +40,7 @@
const sp<IServiceCallback>& callback) override;
binder::Status unregisterForNotifications(const std::string& name,
const sp<IServiceCallback>& callback) override;
+ binder::Status isDeclared(const std::string& name, bool* outReturn) override;
void binderDied(const wp<IBinder>& who) override;
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
index c87ee56..8cc9d36 100644
--- a/headers/media_plugin/media/cas/CasAPI.h
+++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -56,6 +56,11 @@
size_t size,
const CasSessionId *sessionId);
+typedef void (*CasPluginStatusCallback)(
+ void *appData,
+ int32_t event,
+ int32_t arg);
+
struct CasFactory {
CasFactory() {}
virtual ~CasFactory() {}
@@ -91,6 +96,10 @@
CasPlugin() {}
virtual ~CasPlugin() {}
+ // Provide a callback to report plugin status
+ virtual status_t setStatusCallback(
+ CasPluginStatusCallback callback) = 0;
+
// Provide the CA private data from a CA_descriptor in the conditional
// access table to a CasPlugin.
virtual status_t setPrivateData(
@@ -100,6 +109,11 @@
// streams.
virtual status_t openSession(CasSessionId *sessionId) = 0;
+ // Open a session with intend and mode for descrambling a program, or one
+ // or more elementary streams.
+ virtual status_t openSession(uint32_t intent, uint32_t mode,
+ CasSessionId *sessionId) = 0;
+
// Close a previously opened session.
virtual status_t closeSession(const CasSessionId &sessionId) = 0;
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index b6edaa9..81ee5fb 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -90,6 +90,7 @@
OMX_VIDEO_CodingHEVC, /**< ITU H.265/HEVC */
OMX_VIDEO_CodingDolbyVision,/**< Dolby Vision */
OMX_VIDEO_CodingImageHEIC, /**< HEIF image encoded with HEVC */
+ OMX_VIDEO_CodingAV1, /**< AV1 */
OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_VIDEO_CodingMax = 0x7FFFFFFF
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 1b589bc..0d97e8c 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -59,6 +59,8 @@
/**
* Get the AChoreographer instance for the current thread. This must be called
* on an ALooper thread.
+ *
+ * Available since API level 24.
*/
AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
@@ -82,6 +84,8 @@
/**
* Power a callback to be run on the next frame. The data pointer provided will
* be passed to the callback function when it's called.
+ *
+ * Available since API level 29.
*/
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
@@ -90,6 +94,8 @@
* Post a callback to be run on the frame following the specified delay. The
* data pointer provided will be passed to the callback function when it's
* called.
+ *
+ * Available since API level 29.
*/
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
diff --git a/include/android/configuration.h b/include/android/configuration.h
index ef6c5a2..3310722 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -675,50 +675,52 @@
*/
void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
-#if __ANDROID_API__ >= 13
/**
* Return the current configuration screen width in dp units, or
* ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set.
*/
-int32_t AConfiguration_getScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13);
+int32_t AConfiguration_getScreenWidthDp(AConfiguration* config);
/**
* Set the configuration's current screen width in dp units.
*/
-void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13);
+void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value);
/**
* Return the current configuration screen height in dp units, or
* ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set.
*/
-int32_t AConfiguration_getScreenHeightDp(AConfiguration* config) __INTRODUCED_IN(13);
+int32_t AConfiguration_getScreenHeightDp(AConfiguration* config);
/**
* Set the configuration's current screen width in dp units.
*/
-void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13);
+void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value);
/**
* Return the configuration's smallest screen width in dp units, or
* ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY if not set.
*/
-int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13);
+int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config);
/**
* Set the configuration's smallest screen width in dp units.
*/
-void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13);
-#endif /* __ANDROID_API__ >= 13 */
+void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value);
#if __ANDROID_API__ >= 17
/**
* Return the configuration's layout direction, or
* ACONFIGURATION_LAYOUTDIR_ANY if not set.
+ *
+ * Available since API level 17.
*/
int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_IN(17);
/**
* Set the configuration's layout direction.
+ *
+ * Available since API level 17.
*/
void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17);
#endif /* __ANDROID_API__ >= 17 */
diff --git a/include/android/font.h b/include/android/font.h
index 8001ee1..1618096 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -96,6 +96,8 @@
/**
* Close an AFont.
*
+ * Available since API level 29.
+ *
* \param font a font returned by ASystemFontIterator_next or AFontMatchert_match.
* Do nothing if NULL is passed.
*/
@@ -116,6 +118,8 @@
* 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.
*
+ * Available since API level 29.
+ *
* \param font a font object. Passing NULL is not allowed.
* \return a string of the font file path.
*/
@@ -184,6 +188,8 @@
*
* For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
*
+ * Available since API level 29.
+ *
* \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.
*/
@@ -192,6 +198,8 @@
/**
* Return true if the current font is italic, otherwise returns false.
*
+ * Available since API level 29.
+ *
* \param font a font object. Passing NULL is not allowed.
* \return true if italic, otherwise false.
*/
@@ -204,6 +212,8 @@
*
* Note that the returned pointer is valid until AFont_close() is called.
*
+ * Available since API level 29.
+ *
* \param font a font object. Passing NULL is not allowed.
* \return a IETF BCP47 compliant language tag or nullptr if not available.
*/
@@ -216,6 +226,8 @@
* 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.
*
+ * Available since API level 29.
+ *
* \param font a font object. Passing NULL is not allowed.
* \return a font collection index.
*/
@@ -247,6 +259,8 @@
*
* For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
*
+ * Available since API level 29.
+ *
* \param font a font object. Passing NULL is not allowed.
* \return a number of font variation settings.
*/
@@ -258,6 +272,8 @@
*
* See AFont_getAxisCount for more details.
*
+ * Available since API level 29.
+ *
* \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.
@@ -271,6 +287,8 @@
*
* See AFont_getAxisCount for more details.
*
+ * Available since API level 29.
+ *
* \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.
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 0b8f892..d4bd892 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -130,13 +130,17 @@
*/
/**
- * Creates a new AFontMatcher object
+ * Creates a new AFontMatcher object.
+ *
+ * Available since API level 29.
*/
AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29);
/**
* Destroy the matcher object.
*
+ * Available since API level 29.
+ *
* \param matcher a matcher object. Passing NULL is not allowed.
*/
void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29);
@@ -147,6 +151,8 @@
* If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
* with non-italic style.
*
+ * Available since API level 29.
+ *
* \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.
@@ -161,6 +167,8 @@
*
* If this function is not called, the matcher performs with empty locale list.
*
+ * Available since API level 29.
+ *
* \param matcher a matcher object. Passing NULL is not allowed.
* \param languageTags a null character terminated comma separated IETF BCP47 compliant language
* tags.
@@ -174,6 +182,8 @@
*
* If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}.
*
+ * Available since API level 29.
+ *
* \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.
@@ -190,6 +200,8 @@
* Even if no font can render the given text, this function will return a non-null result for
* drawing Tofu character.
*
+ * Available since API level 29.
+ *
* \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.
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
index aedf369..293e5ac 100644
--- a/include/android/hardware_buffer_jni.h
+++ b/include/android/hardware_buffer_jni.h
@@ -42,6 +42,8 @@
* that is returned. To keep the AHardwareBuffer live after the Java
* HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire()
* to acquire an additional reference.
+ *
+ * Available since API level 26.
*/
AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
jobject hardwareBufferObj) __INTRODUCED_IN(26);
@@ -49,6 +51,8 @@
/**
* Return a new Java HardwareBuffer object that wraps the passed native
* AHardwareBuffer object.
+ *
+ * Available since API level 26.
*/
jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26);
diff --git a/include/android/input.h b/include/android/input.h
index cfade6c..ce439c6 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -986,10 +986,8 @@
*/
int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
-#if __ANDROID_API__ >= 14
/** Get the button state of all buttons that are pressed. */
-int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event) __INTRODUCED_IN(14);
-#endif
+int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
/**
* Get a bitfield indicating which edges, if any, were touched by this motion event.
@@ -1054,14 +1052,12 @@
*/
int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
-#if __ANDROID_API__ >= 14
/**
* Get the tool type of a pointer for the given pointer index.
* The tool type indicates the type of tool used to make contact such as a
* finger or stylus, if known.
*/
-int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index) __INTRODUCED_IN(14);
-#endif
+int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index);
/**
* Get the original raw X coordinate of this event.
@@ -1151,11 +1147,9 @@
*/
float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
-#if __ANDROID_API__ >= 13
/** Get the value of the request axis for the given pointer index. */
float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
- int32_t axis, size_t pointer_index) __INTRODUCED_IN(13);
-#endif
+ int32_t axis, size_t pointer_index);
/**
* Get the number of historical points in this event. These are movements that
@@ -1286,14 +1280,12 @@
float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
-#if __ANDROID_API__ >= 13
/**
* Get the historical value of the request axis for the given pointer index
* that occurred between this event and the previous motion event.
*/
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
- int32_t axis, size_t pointer_index, size_t history_index) __INTRODUCED_IN(13);
-#endif
+ int32_t axis, size_t pointer_index, size_t history_index);
struct AInputQueue;
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index d31d1f1..59b1deb 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -69,6 +69,7 @@
*
* This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket))
*
+ * Available since API level 23.
*/
int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23);
@@ -86,6 +87,7 @@
*
* This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
*
+ * Available since API level 23.
*/
int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23);
@@ -103,6 +105,7 @@
*
* This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String))
*
+ * Available since API level 23.
*/
int android_getaddrinfofornetwork(net_handle_t network,
const char *node, const char *service,
@@ -144,6 +147,8 @@
*
* Returns a file descriptor to watch for read events, or a negative
* POSIX error code (see errno.h) if an immediate error occurs.
+ *
+ * Available since API level 29.
*/
int android_res_nquery(net_handle_t network,
const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29);
@@ -155,6 +160,8 @@
*
* Returns a file descriptor to watch for read events, or a negative
* POSIX error code (see errno.h) if an immediate error occurs.
+ *
+ * Available since API level 29.
*/
int android_res_nsend(net_handle_t network,
const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29);
@@ -163,6 +170,8 @@
* Read a result for the query associated with the |fd| descriptor.
* Closes |fd| before returning.
*
+ * Available since 29.
+ *
* Returns:
* < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set.
* >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain)
@@ -173,6 +182,8 @@
/**
* Attempts to cancel the in-progress query associated with the |nsend_fd|
* descriptor.
+ *
+ * Available since API level 29.
*/
void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29);
diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h
index 0c196b9..3a77ffe 100644
--- a/include/android/native_window_jni.h
+++ b/include/android/native_window_jni.h
@@ -51,6 +51,8 @@
* the ANativeWindow; maintains it through general Java object's life cycle;
* and will automatically release the reference when the Java object gets garbage
* collected.
+ *
+ * Available since API level 26.
*/
jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) __INTRODUCED_IN(26);
#endif
diff --git a/include/android/sensor.h b/include/android/sensor.h
index e9d5c16..3ebe79f 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -564,6 +564,7 @@
*
* ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
*
+ * Available since API level 26.
*/
ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) __INTRODUCED_IN(26);
#endif
@@ -583,6 +584,8 @@
/**
* Returns the default sensor with the given type and wakeUp properties or NULL if no sensor
* of this type and wakeUp properties exists.
+ *
+ * Available since API level 21.
*/
ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) __INTRODUCED_IN(21);
#endif
@@ -609,6 +612,8 @@
* Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used
* for configuring sensor direct report.
*
+ * Available since API level 26.
+ *
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
* \param fd file descriptor representing a shared memory created by
@@ -627,6 +632,8 @@
* Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used
* for configuring sensor direct report.
*
+ * Available since API level 26.
+ *
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
* \param buffer {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}.
@@ -646,6 +653,8 @@
* The buffer used for creating direct channel does not get destroyed with
* {@link ASensorManager_destroy} and has to be close or released separately.
*
+ * Available since API level 26.
+ *
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
* \param channelId channel id (a positive integer) returned from
@@ -678,6 +687,8 @@
*
* ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
*
+ * Available since API level 26.
+ *
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
* \param sensor a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate
@@ -780,7 +791,7 @@
*/
ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
/**
* Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
* the given {@link ASensorEventQueue}.
@@ -796,13 +807,15 @@
* {@link AAdditionalInfoEvent#type}, as new values may be defined in the future
* and may delivered to the client.
*
+ * Available since API level 29.
+ *
* \param queue {@link ASensorEventQueue} to configure
* \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events,
* false to stop receiving events
* \return 0 on success or a negative error code on failure
*/
-int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable);
-#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable) __INTRODUCED_IN(29);
+#endif /* __ANDROID_API__ >= 29 */
/*****************************************************************************/
@@ -837,26 +850,36 @@
/**
* Returns the maximum size of batches for this sensor. Batches will often be
* smaller, as the hardware fifo might be used for other sensors.
+ *
+ * Available since API level 21.
*/
int ASensor_getFifoMaxEventCount(ASensor const* sensor) __INTRODUCED_IN(21);
/**
* Returns the hardware batch fifo size reserved to this sensor.
+ *
+ * Available since API level 21.
*/
int ASensor_getFifoReservedEventCount(ASensor const* sensor) __INTRODUCED_IN(21);
/**
* Returns this sensor's string type.
+ *
+ * Available since API level 21.
*/
const char* ASensor_getStringType(ASensor const* sensor) __INTRODUCED_IN(21);
/**
* Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants.
+ *
+ * Available since API level 21.
*/
int ASensor_getReportingMode(ASensor const* sensor) __INTRODUCED_IN(21);
/**
* Returns true if this is a wake up sensor, false otherwise.
+ *
+ * Available since API level 21.
*/
bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21);
#endif /* __ANDROID_API__ >= 21 */
@@ -865,6 +888,8 @@
/**
* Test if sensor supports a certain type of direct channel.
*
+ * Available since API level 26.
+ *
* \param sensor a {@link ASensor} to denote the sensor to be checked.
* \param channelType Channel type constant, either
* {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
@@ -874,7 +899,9 @@
bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType) __INTRODUCED_IN(26);
/**
- * Get the highest direct rate level that a sensor support.
+ * Get the highest direct rate level that a sensor supports.
+ *
+ * Available since API level 26.
*
* \param sensor a {@link ASensor} to denote the sensor to be checked.
*
@@ -885,7 +912,7 @@
int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26);
#endif /* __ANDROID_API__ >= 26 */
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
/**
* Returns the sensor's handle.
*
@@ -899,9 +926,11 @@
* It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
* the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
* between the values.
+ *
+ * Available since API level 29.
*/
-int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__);
-#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */
+int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(29);
+#endif /* __ANDROID_API__ >= 29 */
#ifdef __cplusplus
};
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 7f5177b..6efa4f7 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -21,7 +21,7 @@
/**
* @file sharedmem.h
- * @brief Shared memory buffers that can be shared across process.
+ * @brief Shared memory buffers that can be shared between processes.
*/
#ifndef ANDROID_SHARED_MEMORY_H
@@ -61,11 +61,15 @@
*
* Use close() to release the shared memory region.
*
+ * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to
+ * another process. File descriptors may also be sent to other processes over a Unix domain
+ * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
+ *
* Available since API level 26.
*
* \param name an optional name.
* \param size size of the shared memory region
- * \return file descriptor that denotes the shared memory; error code on failure.
+ * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
*/
int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
@@ -109,7 +113,7 @@
* \param fd file descriptor of the shared memory region.
* \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
* updated access. Note access can only be removed, but not added back.
- * \return 0 for success, error code on failure.
+ * \return 0 for success, -1 and sets errno on failure.
*/
int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26);
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index abb8368..31abb66 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -46,7 +46,7 @@
*/
typedef struct ASurfaceControl ASurfaceControl;
-/*
+/**
* Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
* |debug_name| is a debug name associated with this surface. It can be used to
* identify this surface in the SurfaceFlinger's layer tree. It must not be
@@ -54,10 +54,17 @@
*
* The caller takes ownership of the ASurfaceControl returned and must release it
* using ASurfaceControl_release below.
+ *
+ * Available since API level 29.
*/
ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
__INTRODUCED_IN(29);
+/**
+ * See ASurfaceControl_createFromWindow.
+ *
+ * Available since API level 29.
+ */
ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name)
__INTRODUCED_IN(29);
@@ -65,6 +72,8 @@
* Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
* has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
* as their parent remains on display.
+ *
+ * Available since API level 29.
*/
void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
@@ -79,11 +88,15 @@
/**
* The caller takes ownership of the transaction and must release it using
* ASurfaceControl_delete below.
+ *
+ * Available since API level 29.
*/
ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
/**
* Destroys the |transaction| object.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
@@ -93,6 +106,8 @@
* Note that the transaction is guaranteed to be applied atomically. The
* transactions which are applied on the same thread are also guaranteed to be
* applied in order.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
@@ -116,6 +131,8 @@
*
* THREADING
* The transaction completed callback can be invoked on any thread.
+ *
+ * Available since API level 29.
*/
typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
__INTRODUCED_IN(29);
@@ -123,6 +140,8 @@
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
* latched by the framework, it is presented at the following hardware vsync.
+ *
+ * Available since API level 29.
*/
int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
__INTRODUCED_IN(29);
@@ -131,6 +150,8 @@
* Returns a sync fence that signals when the transaction has been presented.
* The recipient of the callback takes ownership of the fence and is responsible for closing
* it. If a device does not support present fences, a -1 will be returned.
+ *
+ * Available since API level 29.
*/
int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
__INTRODUCED_IN(29);
@@ -141,6 +162,8 @@
* When the client is done using the array, it must release it by calling
* ASurfaceTransactionStats_releaseASurfaceControls.
*
+ * Available since API level 29.
+ *
* |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
*/
void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
@@ -150,6 +173,8 @@
/**
* Releases the array of ASurfaceControls that were returned by
* ASurfaceTransactionStats_getASurfaceControls.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
__INTRODUCED_IN(29);
@@ -158,6 +183,8 @@
* Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
* acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until
* it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
+ *
+ * Available since API level 29.
*/
int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
ASurfaceControl* surface_control)
@@ -180,6 +207,8 @@
*
* The client must ensure that all pending refs on a buffer are released before attempting to reuse
* this buffer, otherwise synchronization errors may occur.
+ *
+ * Available since API level 29.
*/
int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
ASurfaceTransactionStats* surface_transaction_stats,
@@ -190,6 +219,8 @@
* Sets the callback that will be invoked when the updates from this transaction
* are presented. For details on the callback semantics and data, see the
* comments on the ASurfaceTransaction_OnComplete declaration above.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
@@ -199,6 +230,8 @@
* Any children of the* reparented |surface_control| will remain children of the |surface_control|.
*
* The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, ASurfaceControl* new_parent)
@@ -213,6 +246,8 @@
* Updates the visibility of |surface_control|. If show is set to
* ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
* be hidden.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, int8_t visibility)
@@ -224,6 +259,8 @@
* the same z order is undefined.
*
* Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, int32_t z_order)
@@ -236,6 +273,8 @@
*
* The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
* for closing it.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, AHardwareBuffer* buffer,
@@ -246,6 +285,8 @@
* ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
* and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
* will be the dataspace and alpha set for the background color layer.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float r, float g, float b,
@@ -264,6 +305,8 @@
* |transform| the transform applied after the source rect is applied to the buffer. This parameter
* should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
* enum.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, const ARect& source,
@@ -281,6 +324,8 @@
* Updates whether the content for the buffer associated with this surface is
* completely opaque. If true, every pixel of content inside the buffer must be
* opaque or visual errors can occur.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control,
@@ -290,6 +335,8 @@
/**
* Updates the region for the content on this surface updated in this
* transaction. If unspecified, the complete surface is assumed to be damaged.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, const ARect rects[],
@@ -304,6 +351,8 @@
*
* If an earlier transaction has a desired present time of x, and a later transaction has a desired
* present time that is before x, the later transaction will not preempt the earlier transaction.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
int64_t desiredPresentTime) __INTRODUCED_IN(29);
@@ -312,6 +361,8 @@
* Sets the alpha for the buffer. It uses a premultiplied blending.
*
* The |alpha| must be between 0.0 and 1.0.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float alpha)
@@ -321,6 +372,8 @@
* Sets the data space of the surface_control's buffers.
*
* If no data space is set, the surface control defaults to ADATASPACE_SRGB.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, ADataSpace data_space)
@@ -331,6 +384,8 @@
*
* When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
* the surface's buffer.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control,
@@ -342,6 +397,8 @@
*
* When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
* the surface's buffer.
+ *
+ * Available since API level 29.
*/
void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control,
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index 540d23a..dde7eaa 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -65,6 +65,9 @@
* Release the reference to the native ASurfaceTexture acquired with
* ASurfaceTexture_fromSurfaceTexture().
* Failing to do so will result in leaked memory and graphic resources.
+ *
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
*/
void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28);
@@ -73,6 +76,8 @@
* Returns a reference to an ANativeWindow (i.e. the Producer) for this SurfaceTexture.
* This is equivalent to Java's: Surface sur = new Surface(surfaceTexture);
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
* @return A reference to an ANativeWindow. This reference MUST BE released when no longer needed
* using ANativeWindow_release(). Failing to do so will result in leaked resources. nullptr is
@@ -90,6 +95,8 @@
* contexts. Note, however, that the image contents are only accessible from one OpenGL ES
* context at a time.
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
* \param texName The name of the OpenGL ES texture that will be created. This texture name
* must be unusued in the OpenGL ES context that is current on the calling thread.
@@ -108,6 +115,8 @@
* contexts. Note, however, that the image contents are only accessible from one OpenGL ES
* context at a time.
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
* \return 0 on success, negative posix error code otherwise (see <errno.h>)
*/
@@ -118,6 +127,8 @@
* called while the OpenGL ES context that owns the texture is current on the calling thread.
* It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
* \return 0 on success, negative posix error code otherwise (see <errno.h>)
*/
@@ -135,6 +146,8 @@
* The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via
* the glLoadMatrixf or glUniformMatrix4fv functions.
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
* \param mtx the array into which the 4x4 matrix will be stored. The array must have exactly
* 16 elements.
@@ -156,6 +169,8 @@
* For EGL/Vulkan producers, this timestamp is the desired present time set with the
* EGL_ANDROID_presentation_time or VK_GOOGLE_display_timing extensions
*
+ * Available since API level 28.
+ *
* \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture()
*/
int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) __INTRODUCED_IN(28);
diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h
index b0e1edd..2266d54 100644
--- a/include/android/surface_texture_jni.h
+++ b/include/android/surface_texture_jni.h
@@ -32,6 +32,8 @@
__BEGIN_DECLS
+#if __ANDROID_API__ >= 28
+
/**
* Get a reference to the native ASurfaceTexture from the corresponding java object.
*
@@ -40,13 +42,17 @@
* properly once the Java object gets finalized.
* However, this will not result in program termination.
*
+ * Available since API level 28.
+ *
* \param env JNI environment
* \param surfacetexture Instance of Java SurfaceTexture object
* \return native ASurfaceTexture reference or nullptr if the java object is not a SurfaceTexture.
* The returned reference MUST BE released when it's no longer needed using
* ASurfaceTexture_release().
*/
-ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture);
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) __INTRODUCED_IN(28);
+
+#endif
__END_DECLS
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
index f0485a1..6fd7d2c 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -102,6 +102,8 @@
*
* Use ASystemFont_close() to close the iterator.
*
+ * Available since API level 29.
+ *
* \return a pointer for a newly allocated iterator, nullptr on failure.
*/
ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29);
@@ -109,6 +111,8 @@
/**
* Close an opened system font iterator, freeing any related resources.
*
+ * Available since API level 29.
+ *
* \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
*/
void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29);
@@ -116,6 +120,8 @@
/**
* Move to the next system font.
*
+ * Available since API level 29.
+ *
* \param iterator an iterator for the system fonts. Passing NULL is not allowed.
* \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.
diff --git a/include/android/trace.h b/include/android/trace.h
index bb7ff28..d59690a 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -74,7 +74,7 @@
#endif /* __ANDROID_API__ >= 23 */
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
/**
* Writes a trace message to indicate that a given section of code has
@@ -83,6 +83,8 @@
* asynchronous events do not need to be nested. The name and cookie used to
* begin an event must be used to end it.
*
+ * Available since API level 29.
+ *
* \param sectionName The method name to appear in the trace.
* \param cookie Unique identifier for distinguishing simultaneous events
*/
@@ -93,6 +95,8 @@
* Must be called exactly once for each call to {@link ATrace_beginAsyncSection}
* using the same name and cookie.
*
+ * Available since API level 29.
+ *
* \param methodName The method name to appear in the trace.
* \param cookie Unique identifier for distinguishing simultaneous events
*/
@@ -101,6 +105,8 @@
/**
* Writes trace message to indicate the value of a given counter.
*
+ * Available since API level 29.
+ *
* \param counterName The counter name to appear in the trace.
* \param counterValue The counter value.
*/
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 1822e4a..94d90ad 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -167,11 +167,15 @@
virtual ~InputChannel();
public:
- static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd);
+ static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
+ sp<IBinder> token);
- /* Creates a pair of input channels.
+ /**
+ * Create a pair of input channels.
+ * The two returned input channels are equivalent, and are labeled as "server" and "client"
+ * for convenience. The two input channels share the same token.
*
- * Returns OK on success.
+ * Return OK on success.
*/
static status_t openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
@@ -179,46 +183,57 @@
inline std::string getName() const { return mName; }
inline int getFd() const { return mFd.get(); }
- /* Sends a message to the other endpoint.
+ /* Send a message to the other endpoint.
*
* If the channel is full then the message is guaranteed not to have been sent at all.
* Try again after the consumer has sent a finished signal indicating that it has
* consumed some of the pending messages from the channel.
*
- * Returns OK on success.
- * Returns WOULD_BLOCK if the channel is full.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Return OK on success.
+ * Return WOULD_BLOCK if the channel is full.
+ * Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
status_t sendMessage(const InputMessage* msg);
- /* Receives a message sent by the other endpoint.
+ /* Receive a message sent by the other endpoint.
*
* If there is no message present, try again after poll() indicates that the fd
* is readable.
*
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no message present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Return OK on success.
+ * Return WOULD_BLOCK if there is no message present.
+ * Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
status_t receiveMessage(InputMessage* msg);
- /* Returns a new object that has a duplicate of this channel's fd. */
+ /* Return a new object that has a duplicate of this channel's fd. */
sp<InputChannel> dup() const;
status_t write(Parcel& out) const;
static sp<InputChannel> read(const Parcel& from);
- sp<IBinder> getToken() const;
- void setToken(const sp<IBinder>& token);
+ /**
+ * The connection token is used to identify the input connection, i.e.
+ * the pair of input channels that were created simultaneously. Input channels
+ * are always created in pairs, and the token can be used to find the server-side
+ * input channel from the client-side input channel, and vice versa.
+ *
+ * Do not use connection token to check equality of a specific input channel object
+ * to another, because two different (client and server) input channels will share the
+ * same connection token.
+ *
+ * Return the token that identifies this connection.
+ */
+ sp<IBinder> getConnectionToken() const;
private:
- InputChannel(const std::string& name, android::base::unique_fd fd);
+ InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
std::string mName;
android::base::unique_fd mFd;
- sp<IBinder> mToken = nullptr;
+ sp<IBinder> mToken;
};
/*
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
new file mode 100644
index 0000000..9cf0143
--- /dev/null
+++ b/libs/adbd_auth/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+cc_library {
+ name: "libadbd_auth",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+ srcs: ["adbd_auth.cpp"],
+ export_include_dirs: ["include"],
+
+ version_script: "libadbd_auth.map.txt",
+ stubs: {
+ symbol_file: "libadbd_auth.map.txt",
+ },
+
+ host_supported: true,
+ recovery_available: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+}
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
new file mode 100644
index 0000000..6479109
--- /dev/null
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -0,0 +1,443 @@
+/*
+ * 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 ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION
+
+#include "include/adbd_auth.h"
+
+#include <inttypes.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/uio.h>
+
+#include <chrono>
+#include <deque>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+using android::base::unique_fd;
+
+struct AdbdAuthPacketAuthenticated {
+ std::string public_key;
+};
+
+struct AdbdAuthPacketDisconnected {
+ std::string public_key;
+};
+
+struct AdbdAuthPacketRequestAuthorization {
+ std::string public_key;
+};
+
+using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected,
+ AdbdAuthPacketRequestAuthorization>;
+
+struct AdbdAuthContext {
+ static constexpr uint64_t kEpollConstSocket = 0;
+ static constexpr uint64_t kEpollConstEventFd = 1;
+ static constexpr uint64_t kEpollConstFramework = 2;
+
+public:
+ explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) {
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+ if (epoll_fd_ == -1) {
+ PLOG(FATAL) << "failed to create epoll fd";
+ }
+
+ event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
+ sock_fd_.reset(android_get_control_socket("adbd"));
+ if (sock_fd_ == -1) {
+ PLOG(ERROR) << "failed to get adbd authentication socket";
+ } else {
+ if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
+ PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+ }
+
+ if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
+ PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+ }
+
+ if (listen(sock_fd_.get(), 4) != 0) {
+ PLOG(FATAL) << "failed to listen on adbd authentication socket";
+ }
+ }
+ }
+
+ AdbdAuthContext(const AdbdAuthContext& copy) = delete;
+ AdbdAuthContext(AdbdAuthContext&& move) = delete;
+ AdbdAuthContext& operator=(const AdbdAuthContext& copy) = delete;
+ AdbdAuthContext& operator=(AdbdAuthContext&& move) = delete;
+
+ uint64_t NextId() { return next_id_++; }
+
+ void DispatchPendingPrompt() REQUIRES(mutex_) {
+ if (dispatched_prompt_) {
+ LOG(INFO) << "adbd_auth: prompt currently pending, skipping";
+ return;
+ }
+
+ if (pending_prompts_.empty()) {
+ LOG(INFO) << "adbd_auth: no prompts to send";
+ return;
+ }
+
+ LOG(INFO) << "adbd_auth: prompting user for adb authentication";
+ auto [id, public_key, arg] = std::move(pending_prompts_.front());
+ pending_prompts_.pop_front();
+
+ this->output_queue_.emplace_back(
+ AdbdAuthPacketRequestAuthorization{.public_key = public_key});
+
+ Interrupt();
+ dispatched_prompt_ = std::make_tuple(id, public_key, arg);
+ }
+
+ void UpdateFrameworkWritable() REQUIRES(mutex_) {
+ // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we get notified
+ // at the same time as a framework connection, but that's unlikely and this doesn't need to
+ // be fast anyway.
+ if (framework_fd_ != -1) {
+ struct epoll_event event;
+ event.events = EPOLLIN;
+ if (!output_queue_.empty()) {
+ LOG(INFO) << "marking framework writable";
+ event.events |= EPOLLOUT;
+ }
+ event.data.u64 = kEpollConstFramework;
+ CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), &event));
+ }
+ }
+
+ void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
+ LOG(INFO) << "received new framework fd " << new_fd.get()
+ << " (current = " << framework_fd_.get() << ")";
+
+ // If we already had a framework fd, clean up after ourselves.
+ if (framework_fd_ != -1) {
+ output_queue_.clear();
+ dispatched_prompt_.reset();
+ CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), nullptr));
+ framework_fd_.reset();
+ }
+
+ if (new_fd != -1) {
+ struct epoll_event event;
+ event.events = EPOLLIN;
+ if (!output_queue_.empty()) {
+ LOG(INFO) << "marking framework writable";
+ event.events |= EPOLLOUT;
+ }
+ event.data.u64 = kEpollConstFramework;
+ CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event));
+ framework_fd_ = std::move(new_fd);
+ }
+ }
+
+ void HandlePacket(std::string_view packet) REQUIRES(mutex_) {
+ LOG(INFO) << "received packet: " << packet;
+
+ if (packet.length() < 2) {
+ LOG(ERROR) << "received packet of invalid length";
+ ReplaceFrameworkFd(unique_fd());
+ }
+
+ if (packet[0] == 'O' && packet[1] == 'K') {
+ CHECK(this->dispatched_prompt_.has_value());
+ auto& [id, key, arg] = *this->dispatched_prompt_;
+ keys_.emplace(id, std::move(key));
+
+ this->callbacks_.key_authorized(arg, id);
+ this->dispatched_prompt_ = std::nullopt;
+ } else if (packet[0] == 'N' && packet[1] == 'O') {
+ CHECK_EQ(2UL, packet.length());
+ // TODO: Do we want a callback if the key is denied?
+ this->dispatched_prompt_ = std::nullopt;
+ DispatchPendingPrompt();
+ } else {
+ LOG(ERROR) << "unhandled packet: " << packet;
+ ReplaceFrameworkFd(unique_fd());
+ }
+ }
+
+ bool SendPacket() REQUIRES(mutex_) {
+ if (output_queue_.empty()) {
+ return false;
+ }
+
+ CHECK_NE(-1, framework_fd_.get());
+
+ auto& packet = output_queue_.front();
+ struct iovec iovs[2];
+ if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) {
+ iovs[0].iov_base = const_cast<char*>("CK");
+ iovs[0].iov_len = 2;
+ iovs[1].iov_base = p->public_key.data();
+ iovs[1].iov_len = p->public_key.size();
+ } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) {
+ iovs[0].iov_base = const_cast<char*>("DC");
+ iovs[0].iov_len = 2;
+ iovs[1].iov_base = p->public_key.data();
+ iovs[1].iov_len = p->public_key.size();
+ } else if (auto* p = std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) {
+ iovs[0].iov_base = const_cast<char*>("PK");
+ iovs[0].iov_len = 2;
+ iovs[1].iov_base = p->public_key.data();
+ iovs[1].iov_len = p->public_key.size();
+ } else {
+ LOG(FATAL) << "unhandled packet type?";
+ }
+
+ output_queue_.pop_front();
+
+ ssize_t rc = writev(framework_fd_.get(), iovs, 2);
+ if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ PLOG(ERROR) << "failed to write to framework fd";
+ ReplaceFrameworkFd(unique_fd());
+ return false;
+ }
+
+ return true;
+ }
+
+ void Run() {
+ if (sock_fd_ == -1) {
+ LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+ } else {
+ struct epoll_event event;
+ event.events = EPOLLIN;
+ event.data.u64 = kEpollConstSocket;
+ CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event));
+ }
+
+ {
+ struct epoll_event event;
+ event.events = EPOLLIN;
+ event.data.u64 = kEpollConstEventFd;
+ CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
+ }
+
+ while (true) {
+ struct epoll_event events[3];
+ int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
+ if (rc == -1) {
+ PLOG(FATAL) << "epoll_wait failed";
+ } else if (rc == 0) {
+ LOG(FATAL) << "epoll_wait returned 0";
+ }
+
+ bool restart = false;
+ for (int i = 0; i < rc; ++i) {
+ if (restart) {
+ break;
+ }
+
+ struct epoll_event& event = events[i];
+ switch (event.data.u64) {
+ case kEpollConstSocket: {
+ unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
+ SOCK_CLOEXEC | SOCK_NONBLOCK));
+ if (new_framework_fd == -1) {
+ PLOG(FATAL) << "failed to accept framework fd";
+ }
+
+ LOG(INFO) << "adbd_auth: received a new framework connection";
+ std::lock_guard<std::mutex> lock(mutex_);
+ ReplaceFrameworkFd(std::move(new_framework_fd));
+
+ // Stop iterating over events: one of the later ones might be the old
+ // framework fd.
+ restart = false;
+ break;
+ }
+
+ case kEpollConstEventFd: {
+ // We were woken up to write something.
+ uint64_t dummy;
+ int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
+ if (rc != 8) {
+ PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+ }
+
+ std::lock_guard<std::mutex> lock(mutex_);
+ UpdateFrameworkWritable();
+ break;
+ }
+
+ case kEpollConstFramework: {
+ char buf[4096];
+ if (event.events & EPOLLIN) {
+ int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
+ if (rc == -1) {
+ LOG(FATAL) << "failed to read from framework fd";
+ } else if (rc == 0) {
+ LOG(INFO) << "hit EOF on framework fd";
+ std::lock_guard<std::mutex> lock(mutex_);
+ ReplaceFrameworkFd(unique_fd());
+ } else {
+ std::lock_guard<std::mutex> lock(mutex_);
+ HandlePacket(std::string_view(buf, rc));
+ }
+ }
+
+ if (event.events & EPOLLOUT) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ while (SendPacket()) {
+ continue;
+ }
+ UpdateFrameworkWritable();
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"};
+ void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) {
+ for (const auto& path : key_paths) {
+ if (access(path, R_OK) == 0) {
+ LOG(INFO) << "Loading keys from " << path;
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(ERROR) << "Couldn't read " << path;
+ continue;
+ }
+ for (const auto& line : android::base::Split(content, "\n")) {
+ if (!callback(line.data(), line.size(), arg)) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ uint64_t PromptUser(std::string_view public_key, void* arg) EXCLUDES(mutex_) {
+ uint64_t id = NextId();
+
+ std::lock_guard<std::mutex> lock(mutex_);
+ pending_prompts_.emplace_back(id, public_key, arg);
+ DispatchPendingPrompt();
+ return id;
+ }
+
+ uint64_t NotifyAuthenticated(std::string_view public_key) EXCLUDES(mutex_) {
+ uint64_t id = NextId();
+ std::lock_guard<std::mutex> lock(mutex_);
+ keys_.emplace(id, public_key);
+ output_queue_.emplace_back(
+ AdbdAuthPacketDisconnected{.public_key = std::string(public_key)});
+ return id;
+ }
+
+ void NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = keys_.find(id);
+ if (it == keys_.end()) {
+ LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+ return;
+ }
+ output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
+ keys_.erase(it);
+ }
+
+ // Interrupt the worker thread to do some work.
+ void Interrupt() {
+ uint64_t value = 1;
+ ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
+ if (rc == -1) {
+ PLOG(FATAL) << "write to eventfd failed";
+ } else if (rc != sizeof(value)) {
+ LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+ }
+ }
+
+ unique_fd epoll_fd_;
+ unique_fd event_fd_;
+ unique_fd sock_fd_;
+ unique_fd framework_fd_;
+
+ std::atomic<uint64_t> next_id_;
+ AdbdAuthCallbacksV1 callbacks_;
+
+ std::mutex mutex_;
+ std::unordered_map<uint64_t, std::string> keys_ GUARDED_BY(mutex_);
+
+ // We keep two separate queues: one to handle backpressure from the socket (output_queue_)
+ // and one to make sure we only dispatch one authrequest at a time (pending_prompts_).
+ std::deque<AdbdAuthPacket> output_queue_;
+
+ std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_);
+ std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_);
+};
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) {
+ if (callbacks->version != 1) {
+ LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
+ return nullptr;
+ }
+
+ return new AdbdAuthContext(&callbacks->callbacks.v1);
+}
+
+void adbd_auth_delete(AdbdAuthContext* ctx) {
+ delete ctx;
+}
+
+void adbd_auth_run(AdbdAuthContext* ctx) {
+ return ctx->Run();
+}
+
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+ bool (*callback)(const char* public_key, size_t len, void* arg),
+ void* arg) {
+ ctx->IteratePublicKeys(callback, arg);
+}
+
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) {
+ return ctx->NotifyAuthenticated(std::string_view(public_key, len));
+}
+
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) {
+ return ctx->NotifyDisconnected(id);
+}
+
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
+ void* arg) {
+ ctx->PromptUser(std::string_view(public_key, len), arg);
+}
+
+bool adbd_auth_supports_feature(AdbdAuthFeature) {
+ return false;
+}
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
new file mode 100644
index 0000000..b7c1cb8
--- /dev/null
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/*
+ * 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.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+extern "C" {
+
+struct AdbdAuthCallbacksV1 {
+ // Callback for a successful user authorization.
+ void (*key_authorized)(void* arg, uint64_t id);
+};
+
+struct AdbdAuthCallbacks {
+ uint32_t version;
+ union {
+ AdbdAuthCallbacksV1 v1;
+ } callbacks;
+};
+
+struct AdbdAuthContext;
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks);
+void adbd_auth_delete(AdbdAuthContext* ctx);
+
+void adbd_auth_run(AdbdAuthContext* ctx);
+
+// Iterate through the list of authorized public keys.
+// Return false from the callback to stop iteration.
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+ bool (*callback)(const char* public_key, size_t len, void* arg),
+ void* arg);
+
+// Let system_server know that a key has been successfully used for authentication.
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len);
+
+// Let system_server know that a connection has been closed.
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id);
+
+// Prompt the user to authorize a public key.
+// When this happens, a callback will be run on the auth thread with the result.
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg);
+
+enum AdbdAuthFeature {
+};
+
+bool adbd_auth_supports_feature(AdbdAuthFeature f);
+
+}
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
new file mode 100644
index 0000000..d01233c
--- /dev/null
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -0,0 +1,13 @@
+LIBADBD_AUTH {
+ global:
+ adbd_auth_new; # apex
+ adbd_auth_delete; # apex
+ adbd_auth_run; # apex
+ adbd_auth_get_public_keys; #apex
+ adbd_auth_notify_auth; # apex
+ adbd_auth_notify_disconnect; # apex
+ adbd_auth_prompt_user; # apex
+ adbd_auth_supports_feature; # apex
+ local:
+ *;
+};
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index ad8287c..2518b14 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -13,13 +13,18 @@
// limitations under the License.
ndk_headers {
- name: "libarect_headers",
+ name: "libarect_headers_for_ndk",
from: "include/android",
to: "android",
srcs: ["include/android/*.h"],
license: "NOTICE",
}
+cc_library_headers {
+ name: "libarect_headers",
+ export_include_dirs: ["include"],
+}
+
cc_library_static {
name: "libarect",
host_supported: true,
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 60f047f..4f0b7d3 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -115,23 +115,19 @@
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
- return noteOp(op, uid, callingPackage, String16(), String16());
+ return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+ String16("Legacy AppOpsManager.noteOp call"));
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const String16& featureId, const String16& message) {
+ const std::unique_ptr<String16>& featureId, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->noteOperation(op, uid, callingPackage)
+ ? service->noteOperation(op, uid, callingPackage, featureId)
: APP_OPS_MANAGER_UNAVAILABLE_MODE;
if (mode == AppOpsManager::MODE_ALLOWED) {
- if (message.size() == 0) {
- markAppOpNoted(uid, callingPackage, op, featureId,
- String16("noteOp from native code"));
- } else {
- markAppOpNoted(uid, callingPackage, op, featureId, message);
- }
+ markAppOpNoted(uid, callingPackage, op, featureId, message);
}
return mode;
@@ -139,32 +135,34 @@
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault) {
- return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, String16(), String16());
+ return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+ String16("Legacy AppOpsManager.startOpNoThrow call"));
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const String16& featureId, const String16& message) {
+ bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getToken(service), op, uid, callingPackage,
- startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
if (mode == AppOpsManager::MODE_ALLOWED) {
- if (message.size() == 0) {
- markAppOpNoted(uid, callingPackage, op, featureId,
- String16("startOp from native code"));
- } else {
- markAppOpNoted(uid, callingPackage, op, featureId, message);
- }
+ markAppOpNoted(uid, callingPackage, op, featureId, message);
}
return mode;
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+ const std::unique_ptr<String16>& callingFeatureId) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getToken(service), op, uid, callingPackage);
+ service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
}
}
@@ -207,7 +205,7 @@
}
void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
- const String16& featureId, const String16& message) {
+ const std::unique_ptr<String16>& featureId, const String16& message) {
// check it the appops needs to be collected and cache result
if (appOpsToNote[opCode] == 0) {
if (shouldCollectNotes(opCode)) {
@@ -221,11 +219,11 @@
return;
}
- noteAsyncOp(String16(), uid, packageName, opCode, featureId, message);
+ noteAsyncOp(std::unique_ptr<String16>(), uid, packageName, opCode, featureId, message);
}
-void AppOpsManager::noteAsyncOp(const String16& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const String16& featureId,
+void AppOpsManager::noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+ const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
const String16& message) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 9760e13..b85a5f2 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -46,12 +46,14 @@
return reply.readInt32();
}
- virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::unique_ptr<String16>& featureId) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(featureId);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -59,13 +61,15 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, bool startIfModeDefault) {
+ const String16& packageName, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(featureId);
data.writeInt32(startIfModeDefault ? 1 : 0);
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
@@ -74,13 +78,14 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) {
+ const String16& packageName, const std::unique_ptr<String16>& featureId) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
+ data.writeString16(featureId);
remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
}
@@ -144,37 +149,16 @@
remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
}
- virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const String16& featureId,
+ virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+ const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-
- // Convert empty callingPackage into null string
- if (callingPackageName.size() != 0) {
- data.writeString16(callingPackageName);
- } else {
- data.writeString16(nullptr, 0);
- }
-
+ data.writeString16(callingPackageName);
data.writeInt32(uid);
-
- // Convert empty packageName into null string
- if (packageName.size() != 0) {
- data.writeString16(packageName);
- } else {
- data.writeString16(nullptr, 0);
- }
-
+ data.writeString16(packageName);
data.writeInt32(opCode);
-
- // Convert empty featureId into null string
- if (featureId.size() != 0) {
- data.writeString16(featureId);
- } else {
- data.writeString16(nullptr, 0);
- }
-
+ data.writeString16(featureId);
data.writeString16(message);
remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply);
}
@@ -217,7 +201,9 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- int32_t res = noteOperation(code, uid, packageName);
+ std::unique_ptr<String16> featureId;
+ data.readString16(&featureId);
+ int32_t res = noteOperation(code, uid, packageName, featureId);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -228,8 +214,11 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
+ std::unique_ptr<String16> featureId;
+ data.readString16(&featureId);
bool startIfModeDefault = data.readInt32() == 1;
- int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault);
+ int32_t res = startOperation(token, code, uid, packageName, featureId,
+ startIfModeDefault);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -240,7 +229,9 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- finishOperation(token, code, uid, packageName);
+ std::unique_ptr<String16> featureId;
+ data.readString16(&featureId);
+ finishOperation(token, code, uid, packageName, featureId);
reply->writeNoException();
return NO_ERROR;
} break;
@@ -296,11 +287,13 @@
} break;
case NOTE_ASYNC_OP_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
- String16 callingPackageName = data.readString16();
+ std::unique_ptr<String16> callingPackageName;
+ data.readString16(&callingPackageName);
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
int32_t opCode = data.readInt32();
- String16 featureId = data.readString16();
+ std::unique_ptr<String16> featureId;
+ data.readString16(&featureId);
String16 message = data.readString16();
noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId, message);
reply->writeNoException();
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a30df14..4f47db1 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -72,6 +72,7 @@
bool allowIsolated, int dumpsysPriority) override;
Vector<String16> listServices(int dumpsysPriority) override;
sp<IBinder> waitForService(const String16& name16) override;
+ bool isDeclared(const String16& name) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -321,4 +322,12 @@
}
}
+bool ServiceManagerShim::isDeclared(const String16& name) {
+ bool declared;
+ if (!mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared).isOk()) {
+ return false;
+ }
+ return declared;
+}
+
} // namespace android
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 038e6bf..b21af96 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -56,13 +56,15 @@
remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability)
{
Parcel data, reply;
data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
data.writeInt32((int32_t) uid);
data.writeInt32(procState);
data.writeInt64(procStateSeq);
+ data.writeInt32(capability);
remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -104,7 +106,8 @@
uid_t uid = data.readInt32();
int32_t procState = data.readInt32();
int64_t procStateSeq = data.readInt64();
- onUidStateChanged(uid, procState, procStateSeq);
+ int32_t capability = data.readInt32();
+ onUidStateChanged(uid, procState, procStateSeq, capability);
return NO_ERROR;
} break;
default:
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7219b47..a9f8ae6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1449,41 +1449,38 @@
return err;
}
-status_t Parcel::readByteVectorInternal(int8_t* data, size_t size) const {
- if (size_t(size) > dataAvail()) {
- return BAD_VALUE;
- }
- return read(data, size);
-}
-
status_t Parcel::readByteVector(std::vector<int8_t>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
- return readByteVectorInternal(val->data(), val->size());
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ return readByteVectorInternal(val, size);
}
status_t Parcel::readByteVector(std::vector<uint8_t>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
- return readByteVectorInternal(reinterpret_cast<int8_t*>(val->data()), val->size());
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ return readByteVectorInternal(val, size);
}
status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
if (val->get() == nullptr) {
- // resizeOutVector does not create the out vector if size is < 0.
+ // reserveOutVector does not create the out vector if size is < 0.
// This occurs when writing a null byte vector.
return OK;
}
- return readByteVectorInternal((*val)->data(), (*val)->size());
+ return readByteVectorInternal(val->get(), size);
}
status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
if (val->get() == nullptr) {
- // resizeOutVector does not create the out vector if size is < 0.
+ // reserveOutVector does not create the out vector if size is < 0.
// This occurs when writing a null byte vector.
return OK;
}
- return readByteVectorInternal(reinterpret_cast<int8_t*>((*val)->data()), (*val)->size());
+ return readByteVectorInternal(val->get(), size);
}
status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 471b63f..8c7ebba 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -89,4 +89,11 @@
* Unregisters all requests for notifications for a specific callback.
*/
void unregisterForNotifications(@utf8InCpp String name, IServiceCallback callback);
+
+ /**
+ * Returns whether a given interface is declared on the device, even if it
+ * is not started yet. For instance, this could be a service declared in the VINTF
+ * manifest.
+ */
+ boolean isDeclared(@utf8InCpp String name);
}
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
index a9d2b75..521d36f 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/fuzzer/Android.bp
@@ -2,6 +2,11 @@
name: "binder_parcel_fuzzer",
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
+
srcs: [
"binder.cpp",
"binder_ndk.cpp",
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 2744ce1..22a0179 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -134,21 +134,26 @@
// const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const String16& featureId, const String16& message);
+ const std::unique_ptr<String16>& featureId, const String16& message);
// @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
// const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const String16& featureId, const String16& message);
+ bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ const String16& message);
+ // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+ void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+ const std::unique_ptr<String16>& featureId);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
int32_t permissionToOpCode(const String16& permission);
void setCameraAudioRestriction(int32_t mode);
- void noteAsyncOp(const String16& callingPackageName, int32_t uid, const String16& packageName,
- int32_t opCode, const String16& featureId, const String16& message);
+ void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+ const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
+ const String16& message);
private:
Mutex mLock;
@@ -156,7 +161,7 @@
sp<IAppOpsService> getService();
void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
- const String16& featureId, const String16& message);
+ const std::unique_ptr<String16>& featureId, const String16& message);
bool shouldCollectNotes(int32_t opCode);
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index ad34bc5..15ba005 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -35,11 +35,13 @@
DECLARE_META_INTERFACE(AppOpsService)
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
- virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::unique_ptr<String16>& featureId) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, bool startIfModeDefault) = 0;
+ const String16& packageName, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName) = 0;
+ const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
@@ -48,8 +50,8 @@
virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
const String16& packageName) = 0;
virtual void setCameraAudioRestriction(int32_t mode) = 0;
- virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const String16& featureId,
+ virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
+ const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
const String16& message) = 0;
virtual bool shouldCollectNotes(int32_t opCode) = 0;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index a675513..2c43263 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -88,6 +88,14 @@
* Returns nullptr only for permission problem or fatal error.
*/
virtual sp<IBinder> waitForService(const String16& name) = 0;
+
+ /**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * If this returns true, waitForService should always be able to return the
+ * service.
+ */
+ virtual bool isDeclared(const String16& name) = 0;
};
sp<IServiceManager> defaultServiceManager();
@@ -99,6 +107,34 @@
}
template<typename INTERFACE>
+sp<INTERFACE> waitForDeclaredService(const String16& name) {
+ const sp<IServiceManager> sm = defaultServiceManager();
+ if (!sm->isDeclared(name)) return nullptr;
+ return interface_cast<INTERFACE>(sm->waitForService(name));
+}
+
+template <typename INTERFACE>
+sp<INTERFACE> checkDeclaredService(const String16& name) {
+ const sp<IServiceManager> sm = defaultServiceManager();
+ if (!sm->isDeclared(name)) return nullptr;
+ return interface_cast<INTERFACE>(sm->checkService(name));
+}
+
+template<typename INTERFACE>
+sp<INTERFACE> waitForVintfService(
+ const String16& instance = String16("default")) {
+ return waitForDeclaredService<INTERFACE>(
+ INTERFACE::descriptor + String16("/") + instance);
+}
+
+template<typename INTERFACE>
+sp<INTERFACE> checkVintfService(
+ const String16& instance = String16("default")) {
+ return checkDeclaredService<INTERFACE>(
+ INTERFACE::descriptor + String16("/") + instance);
+}
+
+template<typename INTERFACE>
status_t getService(const String16& name, sp<INTERFACE>* outService)
{
const sp<IServiceManager> sm = defaultServiceManager();
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 09e50a9..d070390 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,7 +34,8 @@
virtual void onUidGone(uid_t uid, bool disabled) = 0;
virtual void onUidActive(uid_t uid) = 0;
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
- virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0;
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 0f8abab..d4bb85b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -356,6 +356,11 @@
status_t resizeOutVector(std::vector<T>* val) const;
template<typename T>
status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+ template<typename T>
+ status_t reserveOutVector(std::vector<T>* val, size_t* size) const;
+ template<typename T>
+ status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val,
+ size_t* size) const;
// Like Parcel.java's readExceptionCode(). Reads the first int32
// off of a Parcel's header, returning 0 or the negative error
@@ -475,7 +480,8 @@
status_t readEnum(T* pArg) const;
status_t writeByteVectorInternal(const int8_t* data, size_t size);
- status_t readByteVectorInternal(int8_t* data, size_t size) const;
+ template<typename T>
+ status_t readByteVectorInternal(std::vector<T>* val, size_t size) const;
template<typename T, typename U>
status_t unsafeReadTypedVector(std::vector<T>* val,
@@ -720,6 +726,42 @@
}
template<typename T>
+status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const {
+ int32_t read_size;
+ status_t err = readInt32(&read_size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (read_size < 0) {
+ return UNEXPECTED_NULL;
+ }
+ *size = static_cast<size_t>(read_size);
+ val->reserve(*size);
+ return OK;
+}
+
+template<typename T>
+status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
+ size_t* size) const {
+ int32_t read_size;
+ status_t err = readInt32(&read_size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (read_size >= 0) {
+ *size = static_cast<size_t>(read_size);
+ val->reset(new std::vector<T>());
+ (*val)->reserve(*size);
+ } else {
+ val->reset();
+ }
+
+ return OK;
+}
+
+template<typename T>
status_t Parcel::readStrongBinder(sp<T>* val) const {
sp<IBinder> tmp;
status_t ret = readStrongBinder(&tmp);
@@ -988,20 +1030,33 @@
return readInt64(reinterpret_cast<int64_t *>(pArg));
}
+template<typename T>
+inline status_t Parcel::readByteVectorInternal(std::vector<T>* val, size_t size) const {
+ // readByteVectorInternal expects a vector that has been reserved (but not
+ // resized) to have the provided size.
+ const T* data = reinterpret_cast<const T*>(readInplace(size));
+ if (!data) return BAD_VALUE;
+ val->clear();
+ val->insert(val->begin(), data, data+size);
+ return NO_ERROR;
+}
+
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::vector<T>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
- return readByteVectorInternal(reinterpret_cast<int8_t*>(val->data()), val->size());
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ return readByteVectorInternal(val, size);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
- if (status_t status = resizeOutVector(val); status != OK) return status;
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
if (val->get() == nullptr) {
- // resizeOutVector does not create the out vector if size is < 0.
+ // reserveOutVector does not create the out vector if size is < 0.
// This occurs when writing a null Enum vector.
return OK;
}
- return readByteVectorInternal(reinterpret_cast<int8_t*>((*val)->data()), (*val)->size());
+ return readByteVectorInternal(val->get(), size);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::vector<T>* val) const {
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 662e56e..4635ad8 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -42,6 +42,24 @@
android::status_t writeToParcel(android::Parcel* parcel) const override;
android::status_t readFromParcel(const android::Parcel* parcel) override;
+ inline bool operator!=(const ParcelFileDescriptor& rhs) const {
+ return mFd != rhs.mFd;
+ }
+ inline bool operator<(const ParcelFileDescriptor& rhs) const {
+ return mFd < rhs.mFd;
+ }
+ inline bool operator<=(const ParcelFileDescriptor& rhs) const {
+ return mFd <= rhs.mFd;
+ }
+ inline bool operator==(const ParcelFileDescriptor& rhs) const {
+ return mFd == rhs.mFd;
+ }
+ inline bool operator>(const ParcelFileDescriptor& rhs) const {
+ return mFd > rhs.mFd;
+ }
+ inline bool operator>=(const ParcelFileDescriptor& rhs) const {
+ return mFd >= rhs.mFd;
+ }
private:
android::base::unique_fd mFd;
};
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 22344b6..fa07d04 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -22,6 +22,8 @@
cflags: [
"-D__INTRODUCED_IN(n)=",
"-D__assert(a,b,c)=",
+ // We want all the APIs to be available on the host.
+ "-D__ANDROID_API__=10000",
],
},
},
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index dc3c8d2..946ccb7 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -21,7 +21,7 @@
/**
* @file binder_auto_utils.h
- * @brief These objects provide a more C++-like thin interface to the .
+ * @brief These objects provide a more C++-like thin interface to the binder.
*/
#pragma once
@@ -159,13 +159,17 @@
*/
T* getR() { return &mT; }
- // copy-constructing, or move/copy assignment is disallowed
+ // copy-constructing/assignment is disallowed
ScopedAResource(const ScopedAResource&) = delete;
ScopedAResource& operator=(const ScopedAResource&) = delete;
- ScopedAResource& operator=(ScopedAResource&&) = delete;
- // move-constructing is okay
+ // move-constructing/assignment is okay
ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; }
+ ScopedAResource& operator=(ScopedAResource&& other) {
+ set(other.mT);
+ other.mT = DEFAULT;
+ return *this;
+ }
private:
T mT;
@@ -197,6 +201,7 @@
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
ScopedAStatus(ScopedAStatus&&) = default;
+ ScopedAStatus& operator=(ScopedAStatus&&) = default;
/**
* See AStatus_isOk.
@@ -219,9 +224,31 @@
binder_status_t getStatus() const { return AStatus_getStatus(get()); }
/**
- * Convenience method for okay status.
+ * See AStatus_getMessage
+ */
+ const char* getMessage() const { return AStatus_getMessage(get()); }
+
+ /**
+ * Convenience methods for creating scoped statuses.
*/
static ScopedAStatus ok() { return ScopedAStatus(AStatus_newOk()); }
+ static ScopedAStatus fromExceptionCode(binder_exception_t exception) {
+ return ScopedAStatus(AStatus_fromExceptionCode(exception));
+ }
+ static ScopedAStatus fromExceptionCodeWithMessage(binder_exception_t exception,
+ const char* message) {
+ return ScopedAStatus(AStatus_fromExceptionCodeWithMessage(exception, message));
+ }
+ static ScopedAStatus fromServiceSpecificError(int32_t serviceSpecific) {
+ return ScopedAStatus(AStatus_fromServiceSpecificError(serviceSpecific));
+ }
+ static ScopedAStatus fromServiceSpecificErrorWithMessage(int32_t serviceSpecific,
+ const char* message) {
+ return ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(serviceSpecific, message));
+ }
+ static ScopedAStatus fromStatus(binder_status_t status) {
+ return ScopedAStatus(AStatus_fromStatus(status));
+ }
};
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 160739b..4d5c044 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -34,7 +34,7 @@
#include <android/binder_status.h>
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
// Also see TF_* in kernel's binder.h
typedef uint32_t binder_flags_t;
@@ -165,6 +165,8 @@
*
* None of these parameters can be null.
*
+ * Available since API level 29.
+ *
* \param interfaceDescriptor this is a unique identifier for the class. This is used internally for
* sanity checks on transactions.
* \param onCreate see AIBinder_Class_onCreate.
@@ -199,6 +201,8 @@
* If this isn't set, nothing will be dumped when dump is called (for instance with
* android.os.Binder#dump). Must be called before any instance of the class is created.
*
+ * Available since API level 29.
+ *
* \param dump function to call when an instance of this binder class is being dumped.
*/
void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
@@ -220,6 +224,8 @@
* these two objects are actually equal using the AIBinder pointer alone (which they should be able
* to do). Also see the suggested memory ownership model suggested above.
*
+ * Available since API level 29.
+ *
* \param clazz the type of the object to be created.
* \param args the args to pass to AIBinder_onCreate for that class.
*
@@ -231,6 +237,8 @@
/**
* If this is hosted in a process other than the current one.
*
+ * Available since API level 29.
+ *
* \param binder the binder being queried.
*
* \return true if the AIBinder represents an object in another process.
@@ -244,6 +252,8 @@
* updated as the result of a transaction made using AIBinder_transact, but it will also be updated
* based on the results of bookkeeping or other transactions made internally.
*
+ * Available since API level 29.
+ *
* \param binder the binder being queried.
*
* \return true if the binder is alive.
@@ -255,6 +265,8 @@
* return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a
* sanity check.
*
+ * Available since API level 29.
+ *
* \param binder the binder being queried.
*
* \return STATUS_OK if the ping succeeds.
@@ -264,7 +276,9 @@
/**
* Built-in transaction for all binder objects. This dumps information about a given binder.
*
- * See also AIBinder_Class_setOnDump, AIBinder_onDump
+ * See also AIBinder_Class_setOnDump, AIBinder_onDump.
+ *
+ * Available since API level 29.
*
* \param binder the binder to dump information about
* \param fd where information should be dumped to
@@ -287,6 +301,8 @@
*
* If binder is local, this will return STATUS_INVALID_OPERATION.
*
+ * Available since API level 29.
+ *
* \param binder the binder object you want to receive death notifications from.
* \param recipient the callback that will receive notifications when/if the binder dies.
* \param cookie the value that will be passed to the death recipient on death.
@@ -306,6 +322,8 @@
* If the binder dies, it will automatically unlink. If the binder is deleted, it will be
* automatically unlinked.
*
+ * Available since API level 29.
+ *
* \param binder the binder object to remove a previously linked death recipient from.
* \param recipient the callback to remove.
* \param cookie the cookie used to link to death.
@@ -322,9 +340,11 @@
* This can be used with higher-level system services to determine the caller's identity and check
* permissions.
*
+ * Available since API level 29.
+ *
* \return calling uid or the current process's UID if this thread isn't processing a transaction.
*/
-uid_t AIBinder_getCallingUid();
+uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29);
/**
* This returns the calling PID assuming that this thread is called from a thread that is processing
@@ -335,14 +355,18 @@
* calling process dies and is replaced with another process with elevated permissions and the same
* PID.
*
+ * Available since API level 29.
+ *
* \return calling pid or the current process's PID if this thread isn't processing a transaction.
* If the transaction being processed is a oneway transaction, then this method will return 0.
*/
-pid_t AIBinder_getCallingPid();
+pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29);
/**
* This can only be called if a strong reference to this object already exists in process.
*
+ * Available since API level 29.
+ *
* \param binder the binder object to add a refcount to.
*/
void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29);
@@ -350,6 +374,8 @@
/**
* This will delete the object and call onDestroy once the refcount reaches zero.
*
+ * Available since API level 29.
+ *
* \param binder the binder object to remove a refcount from.
*/
void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29);
@@ -357,6 +383,8 @@
/**
* For debugging only!
*
+ * Available since API level 29.
+ *
* \param binder the binder object to retrieve the refcount of.
*
* \return the number of strong-refs on this binder in this process. If binder is null, this will be
@@ -373,6 +401,8 @@
* This returns true if the class association succeeds. If it fails, no change is made to the
* binder object.
*
+ * Available since API level 29.
+ *
* \param binder the object to attach the class to.
* \param clazz the clazz to attach to binder.
*
@@ -383,6 +413,8 @@
/**
* Returns the class that this binder was constructed with or associated with.
*
+ * Available since API level 29.
+ *
* \param binder the object that is being queried.
*
* \return the class that this binder is associated with. If this binder wasn't created with
@@ -394,6 +426,8 @@
* Value returned by onCreate for a local binder. For stateless classes (if onCreate returns
* null), this also returns null. For a remote binder, this will always return null.
*
+ * Available since API level 29.
+ *
* \param binder the object that is being queried.
*
* \return the userdata returned from AIBinder_onCreate when this object was created. This may be
@@ -422,6 +456,8 @@
* AIBinder_transact. Alternatively, if there is an error while filling out the parcel, it can be
* deleted with AParcel_delete.
*
+ * Available since API level 29.
+ *
* \param binder the binder object to start a transaction on.
* \param in out parameter for input data to the transaction.
*
@@ -442,6 +478,8 @@
* This does not affect the ownership of binder. The out parcel's ownership is passed to the caller
* and must be released with AParcel_delete when finished reading.
*
+ * Available since API level 29.
+ *
* \param binder the binder object to transact on.
* \param code the implementation-specific code representing which transaction should be taken.
* \param in the implementation-specific input data to this transaction.
@@ -459,6 +497,8 @@
* This does not take any ownership of the input binder, but it can be used to retrieve it if
* something else in some process still holds a reference to it.
*
+ * Available since API level 29.
+ *
* \param binder object to create a weak pointer to.
*
* \return object representing a weak pointer to binder (or null if binder is null).
@@ -469,6 +509,8 @@
/**
* Deletes the weak reference. This will have no impact on the lifetime of the binder.
*
+ * Available since API level 29.
+ *
* \param weakBinder object created with AIBinder_Weak_new.
*/
void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29);
@@ -477,6 +519,8 @@
* If promotion succeeds, result will have one strong refcount added to it. Otherwise, this returns
* null.
*
+ * Available since API level 29.
+ *
* \param weakBinder weak pointer to attempt retrieving the original object from.
*
* \return an AIBinder object with one refcount given to the caller or null.
@@ -487,6 +531,8 @@
/**
* This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath.
*
+ * Available since API level 29.
+ *
* \param cookie the cookie passed to AIBinder_linkToDeath.
*/
typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29);
@@ -494,6 +540,8 @@
/**
* Creates a new binder death recipient. This can be attached to multiple different binder objects.
*
+ * Available since API level 29.
+ *
* \param onBinderDied the callback to call when this death recipient is invoked.
*
* \return the newly constructed object (or null if onBinderDied is null).
@@ -505,19 +553,23 @@
* Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before
* calling this as these will all be automatically unlinked.
*
+ * Available since API level 29.
+ *
* \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new).
*/
void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29);
-#endif //__ANDROID_API__ >= __ANDROID_API_Q__
+#endif //__ANDROID_API__ >= 29
-#if __ANDROID_API__ >= __ANDROID_API_R__
+#if __ANDROID_API__ >= 30
/**
* Gets the extension registered with AIBinder_setExtension.
*
* See AIBinder_setExtension.
*
+ * Available since API level 30.
+ *
* \param binder the object to get the extension of.
* \param outExt the returned extension object. Will be null if there is no extension set or
* non-null with one strong ref count.
@@ -570,6 +622,8 @@
* // if bar is null, then there is no extension or a different
* // type of extension
*
+ * Available since API level 30.
+ *
* \param binder the object to get the extension on. Must be local.
* \param ext the extension to set (binder will hold a strong reference to this)
*
@@ -578,7 +632,7 @@
*/
binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) __INTRODUCED_IN(30);
-#endif //__ANDROID_API__ >= __ANDROID_API_R__
+#endif //__ANDROID_API__ >= 30
__END_DECLS
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index 124f36c..be3029c 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -31,7 +31,7 @@
#include <jni.h>
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
/**
* Converts an android.os.IBinder object into an AIBinder* object.
@@ -40,6 +40,8 @@
* AIBinder object, the original object is returned. The returned object has one refcount
* associated with it, and so this should be accompanied with an AIBinder_decStrong call.
*
+ * Available since API level 29.
+ *
* \param env Java environment.
* \param binder android.os.IBinder java object.
*
@@ -55,6 +57,8 @@
* If either env or the binder is null, null is returned. If this binder object was originally an
* IBinder object, the original java object will be returned.
*
+ * Available since API level 29.
+ *
* \param env Java environment.
* \param binder the object to convert.
*
@@ -63,7 +67,7 @@
__attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder)
__INTRODUCED_IN(29);
-#endif //__ANDROID_API__ >= __ANDROID_API_Q__
+#endif //__ANDROID_API__ >= 29
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 8c41707..86b75b8 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -35,7 +35,7 @@
typedef struct AIBinder AIBinder;
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
/**
* This object represents a package of data that can be sent between processes. When transacting, an
@@ -49,6 +49,8 @@
/**
* Cleans up a parcel.
*
+ * Available since API level 29.
+ *
* \param parcel A parcel returned by AIBinder_prepareTransaction or AIBinder_transact when a
* transaction is being aborted.
*/
@@ -57,6 +59,8 @@
/**
* Sets the position within the parcel.
*
+ * Available since API level 29.
+ *
* \param parcel The parcel of which to set the position.
* \param position Position of the parcel to set. This must be a value returned by
* AParcel_getDataPosition. Positions are constant for a given parcel between processes.
@@ -69,6 +73,8 @@
/**
* Gets the current position within the parcel.
*
+ * Available since API level 29.
+ *
* \param parcel The parcel of which to get the position.
*
* \return The size of the parcel. This will always be greater than 0. The values returned by this
@@ -389,6 +395,8 @@
* Writes an AIBinder to the next location in a non-null parcel. Can be null. This does not take any
* refcounts of ownership of the binder from the client.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param binder the value to write to the parcel.
*
@@ -400,6 +408,8 @@
* Reads an AIBinder from the next location in a non-null parcel. One strong ref-count of ownership
* is passed to the caller of this function.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param binder the out parameter for what is read from the parcel. This may be null.
*
@@ -414,12 +424,14 @@
*
* This corresponds to the SDK's android.os.ParcelFileDescriptor.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param fd the value to write to the parcel (-1 to represent a null ParcelFileDescriptor).
*
* \return STATUS_OK on successful write.
*/
-binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd);
+binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) __INTRODUCED_IN(29);
/**
* Reads an int from the next location in a non-null parcel.
@@ -428,13 +440,16 @@
*
* This corresponds to the SDK's android.os.ParcelFileDescriptor.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param fd the out parameter for what is read from the parcel (or -1 to represent a null
* ParcelFileDescriptor)
*
* \return STATUS_OK on successful write.
*/
-binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd);
+binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd)
+ __INTRODUCED_IN(29);
/**
* Writes an AStatus object to the next location in a non-null parcel.
@@ -445,6 +460,8 @@
* this happens or if writing the status object itself fails, the return value from this function
* should be propagated to the client, and AParcel_readStatusHeader shouldn't be called.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param status the value to write to the parcel.
*
@@ -457,6 +474,8 @@
* Reads an AStatus from the next location in a non-null parcel. Ownership is passed to the caller
* of this function.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param status the out parameter for what is read from the parcel.
*
@@ -470,6 +489,8 @@
*
* If length is -1, and string is nullptr, this will write a 'null' string to the parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param string the null-terminated string to write to the parcel, at least of size 'length'.
* \param length the length of the string to be written.
@@ -487,6 +508,8 @@
* the output buffer from this read. If there is a 'null' string on the binder buffer, the allocator
* will be called with length -1.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param stringData some external representation of a string.
* \param allocator allocator that will be called once the size of the string is known.
@@ -504,6 +527,8 @@
* returned from this function will be used to fill out the data from the parcel. If length is -1,
* this will write a 'null' string array to the binder buffer.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData some external representation of an array.
* \param length the length of the array to be written.
@@ -526,6 +551,8 @@
* the contents of the string that is read. If the string array being read is 'null', this will
* instead just pass -1 to AParcel_stringArrayAllocator.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called with arrayData once the size of the output
@@ -543,6 +570,8 @@
/**
* Writes an array of parcelables (user-defined types) to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -562,6 +591,8 @@
* length is greater than zero, elementReader will be called for every index to read the
* corresponding parcelable.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -578,6 +609,8 @@
/**
* Writes int32_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -588,6 +621,8 @@
/**
* Writes uint32_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -598,6 +633,8 @@
/**
* Writes int64_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -608,6 +645,8 @@
/**
* Writes uint64_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -618,6 +657,8 @@
/**
* Writes float value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -628,6 +669,8 @@
/**
* Writes double value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -638,6 +681,8 @@
/**
* Writes bool value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -648,6 +693,8 @@
/**
* Writes char16_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -658,6 +705,8 @@
/**
* Writes int8_t value to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param value the value to write to the parcel.
*
@@ -668,6 +717,8 @@
/**
* Reads into int32_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -678,6 +729,8 @@
/**
* Reads into uint32_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -688,6 +741,8 @@
/**
* Reads into int64_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -698,6 +753,8 @@
/**
* Reads into uint64_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -708,6 +765,8 @@
/**
* Reads into float value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -718,6 +777,8 @@
/**
* Reads into double value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -728,6 +789,8 @@
/**
* Reads into bool value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -738,6 +801,8 @@
/**
* Reads into char16_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -748,6 +813,8 @@
/**
* Reads into int8_t value from the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param value the value to read from the parcel.
*
@@ -758,6 +825,8 @@
/**
* Writes an array of int32_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -770,6 +839,8 @@
/**
* Writes an array of uint32_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -782,6 +853,8 @@
/**
* Writes an array of int64_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -794,6 +867,8 @@
/**
* Writes an array of uint64_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -806,6 +881,8 @@
/**
* Writes an array of float to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -818,6 +895,8 @@
/**
* Writes an array of double to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -833,6 +912,8 @@
* getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying
* values to write to the parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData some external representation of an array.
* \param length the length of arrayData (or -1 if this represents a null array).
@@ -846,6 +927,8 @@
/**
* Writes an array of char16_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -858,6 +941,8 @@
/**
* Writes an array of int8_t to the next location in a non-null parcel.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to write to.
* \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).
* \param length the length of arrayData or -1 if this represents a null array.
@@ -874,6 +959,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -890,6 +977,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -906,6 +995,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -922,6 +1013,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -938,6 +1031,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -954,6 +1049,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -969,6 +1066,8 @@
* First, allocator will be called with the length of the array. Then, for every i in [0, length),
* setter(arrayData, i, x) will be called where x is the value at the associated index.
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -988,6 +1087,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -1004,6 +1105,8 @@
* length is greater than zero, the buffer returned by the allocator will be filled with the
* corresponding data
*
+ * Available since API level 29.
+ *
* \param parcel the parcel to read from.
* \param arrayData some external representation of an array.
* \param allocator the callback that will be called to allocate the array.
@@ -1015,7 +1118,7 @@
// @END-PRIMITIVE-READ-WRITE
-#endif //__ANDROID_API__ >= __ANDROID_API_Q__
+#endif //__ANDROID_API__ >= 29
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 2671b9b..78d70f8 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -30,7 +30,7 @@
#include <sys/cdefs.h>
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_Q__
+#if __ANDROID_API__ >= 29
enum {
STATUS_OK = 0,
@@ -105,6 +105,8 @@
/**
* New status which is considered a success.
*
+ * Available since API level 29.
+ *
* \return a newly constructed status object that the caller owns.
*/
__attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29);
@@ -112,6 +114,8 @@
/**
* New status with exception code.
*
+ * Available since API level 29.
+ *
* \param exception the code that this status should represent. If this is EX_NONE, then this
* constructs an non-error status object.
*
@@ -123,6 +127,8 @@
/**
* New status with exception code and message.
*
+ * Available since API level 29.
+ *
* \param exception the code that this status should represent. If this is EX_NONE, then this
* constructs an non-error status object.
* \param message the error message to associate with this status object.
@@ -137,6 +143,8 @@
*
* This is considered to be EX_TRANSACTION_FAILED with extra information.
*
+ * Available since API level 29.
+ *
* \param serviceSpecific an implementation defined error code.
*
* \return a newly constructed status object that the caller owns.
@@ -149,6 +157,8 @@
*
* This is considered to be EX_TRANSACTION_FAILED with extra information.
*
+ * Available since API level 29.
+ *
* \param serviceSpecific an implementation defined error code.
* \param message the error message to associate with this status object.
*
@@ -162,6 +172,8 @@
* is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning
* an AStatus instance.
*
+ * Available since API level 29.
+ *
* \param a low-level error to associate with this status object.
*
* \return a newly constructed status object that the caller owns.
@@ -173,6 +185,8 @@
* Whether this object represents a successful transaction. If this function returns true, then
* AStatus_getExceptionCode will return EX_NONE.
*
+ * Available since API level 29.
+ *
* \param status the status being queried.
*
* \return whether the status represents a successful transaction. For more details, see below.
@@ -182,6 +196,8 @@
/**
* The exception that this status object represents.
*
+ * Available since API level 29.
+ *
* \param status the status being queried.
*
* \return the exception code that this object represents.
@@ -194,6 +210,8 @@
* 0, the status object may still represent a different exception or status. To find out if this
* transaction as a whole is okay, use AStatus_isOk instead.
*
+ * Available since API level 29.
+ *
* \param status the status being queried.
*
* \return the service-specific error code if the exception code is EX_SERVICE_SPECIFIC or 0.
@@ -206,6 +224,8 @@
* object may represent a different exception or a service specific error. To find out if this
* transaction as a whole is okay, use AStatus_isOk instead.
*
+ * Available since API level 29.
+ *
* \param status the status being queried.
*
* \return the status code if the exception code is EX_TRANSACTION_FAILED or 0.
@@ -218,6 +238,8 @@
*
* The returned string has the lifetime of the status object passed into this function.
*
+ * Available since API level 29.
+ *
* \param status the status being queried.
*
* \return the message associated with this error.
@@ -227,11 +249,13 @@
/**
* Deletes memory associated with the status instance.
*
+ * Available since API level 29.
+ *
* \param status the status to delete, returned from AStatus_newOk or one of the AStatus_from* APIs.
*/
void AStatus_delete(AStatus* status) __INTRODUCED_IN(29);
-#endif //__ANDROID_API__ >= __ANDROID_API_Q__
+#endif //__ANDROID_API__ >= 29
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d4d5387..d59d6e4 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -89,12 +89,12 @@
AStatus_getStatus;
AStatus_isOk;
AStatus_newOk;
- ABinderProcess_joinThreadPool; # apex vndk
- ABinderProcess_setThreadPoolMaxThreadCount; # apex vndk
- ABinderProcess_startThreadPool; # apex vndk
- AServiceManager_addService; # apex vndk
- AServiceManager_checkService; # apex vndk
- AServiceManager_getService; # apex vndk
+ ABinderProcess_joinThreadPool; # apex llndk
+ ABinderProcess_setThreadPoolMaxThreadCount; # apex llndk
+ ABinderProcess_startThreadPool; # apex llndk
+ AServiceManager_addService; # apex llndk
+ AServiceManager_checkService; # apex llndk
+ AServiceManager_getService; # apex llndk
local:
*;
};
@@ -105,8 +105,8 @@
AIBinder_setExtension;
AIBinder_markSystemStability; # apex
- AIBinder_markVendorStability; # vndk
- AIBinder_markVintfStability; # apex vndk
+ AIBinder_markVendorStability; # llndk
+ AIBinder_markVintfStability; # apex llndk
local:
*;
};
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index ae2276e..f18e118 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -50,7 +50,7 @@
if (length < -1) return STATUS_BAD_VALUE;
if (!isNullArray && length < 0) {
- LOG(ERROR) << __func__ << ": null array must be used with length == -1.";
+ LOG(ERROR) << __func__ << ": non-null array but length is " << length;
return STATUS_BAD_VALUE;
}
if (isNullArray && length > 0) {
diff --git a/libs/binder/ndk/scripts/format.sh b/libs/binder/ndk/scripts/format.sh
deleted file mode 100755
index 698d291..0000000
--- a/libs/binder/ndk/scripts/format.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 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.
-
-set -e
-
-echo "Formatting code"
-
-bpfmt -w $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -name "Android.bp")
-clang-format -i $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -\( -name "*.cpp" -o -name "*.h" -\))
diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh
deleted file mode 100755
index 1eba892..0000000
--- a/libs/binder/ndk/update.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 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.
-
-
-set -ex
-
-# This script makes sure that the source code is in sync with the various scripts
-./scripts/gen_parcel_helper.py
-./scripts/format.sh
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 635ea69..5a7f9a9 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -119,12 +119,7 @@
srcs: ["binderSafeInterfaceTest.cpp"],
cppflags: [
- "-Weverything",
- "-Wno-c++98-compat",
- "-Wno-c++98-compat-pedantic",
- "-Wno-global-constructors",
- "-Wno-padded",
- "-Wno-weak-vtables",
+ "-Wextra",
],
cpp_std: "experimental",
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 0bee56c..1f2779a 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -134,18 +134,15 @@
TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
sp<IBinder> vintfServer = BadStableBinder::vintf();
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("."), vintfServer));
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("/"), vintfServer));
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("/."), vintfServer));
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("a.d.IFoo"), vintfServer));
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("foo"), vintfServer));
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
- android::defaultServiceManager()->addService(String16("a.d.IFoo/foo"), vintfServer));
+ for (const char* instance8 : {
+ ".", "/", "/.", "a.d.IFoo", "foo", "a.d.IFoo/foo"
+ }) {
+ String16 instance (instance8);
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ android::defaultServiceManager()->addService(String16("."), vintfServer)) << instance8;
+ EXPECT_FALSE(android::defaultServiceManager()->isDeclared(instance)) << instance8;
+ }
}
TEST(BinderStability, CantCallVendorBinderInSystemContext) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index e6d442d..4f605e0 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -17,8 +17,16 @@
export_include_dirs: ["include"],
// we must build this module to get the required header as that is generated
- export_shared_lib_headers: [ "android.hidl.token@1.0-utils" ],
- shared_libs: [ "android.hidl.token@1.0-utils" ],
+ export_shared_lib_headers: [
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ ],
+ shared_libs: [
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ ],
}
cc_library_shared {
@@ -59,6 +67,9 @@
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
"view/Surface.cpp",
+ "surfacetexture/SurfaceTexture.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/EGLConsumer.cpp",
],
shared_libs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 9a50175..3c31d74 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -33,6 +33,7 @@
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+ mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -41,6 +42,7 @@
mWidth = width;
mHeight = height;
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
}
static void transactionCallbackThunk(void* context, nsecs_t latchTime,
@@ -63,6 +65,7 @@
? stats[0].previousReleaseFence
: Fence::NO_FENCE);
mNextCallbackBufferItem = BufferItem();
+ mBufferItemConsumer->setTransformHint(stats[0].transformHint);
}
mDequeueWaitCV.notify_all();
decStrong((void*)transactionCallbackThunk);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 09c74de..a307d04 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -512,6 +512,12 @@
mCore->mSharedBufferSlot = found;
mSlots[found].mBufferState.mShared = true;
}
+
+ if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
+ if (mCore->mConsumerListener != nullptr) {
+ mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
+ }
+ }
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -528,6 +534,10 @@
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+ if (mCore->mConsumerListener != nullptr) {
+ mCore->mConsumerListener->onFrameDequeued(
+ mSlots[*outSlot].mGraphicBuffer->getId());
+ }
}
mCore->mIsAllocating = false;
@@ -580,11 +590,6 @@
}
addAndGetFrameTimestamps(nullptr, outTimestamps);
- { // Autolock scope
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
- }
-
return returnFlags;
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index b98e48b..621cf59 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -49,25 +49,28 @@
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
std::move(metadata),
- handle, gbp);
+ handle, gbp,
+ outTransformHint);
}
status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) override {
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) override {
return callRemote<decltype(
&ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
name, width, height, format,
flags, parent,
- std::move(metadata), handle,
- gbp);
+ std::move(metadata), handle, gbp,
+ outTransformHint);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e004e95..e392bc5 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -406,6 +406,12 @@
what |= eMetadataChanged;
metadata.merge(other.metadata);
}
+
+ if (other.what & eShadowRadiusChanged) {
+ what |= eShadowRadiusChanged;
+ shadowRadius = other.shadowRadius;
+ }
+
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e9079ef..a538e14 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -224,6 +224,8 @@
surfaceStats.acquireTime,
surfaceStats.previousReleaseFence,
surfaceStats.transformHint);
+ surfaceControls[surfaceStats.surfaceControl]->setTransformHint(
+ surfaceStats.transformHint);
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -1335,6 +1337,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShadowRadius(
+ const sp<SurfaceControl>& sc, float shadowRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eShadowRadiusChanged;
+ s->shadowRadius = shadowRadius;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1451,16 +1465,19 @@
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
SurfaceControl* parent,
- LayerMetadata metadata) {
+ LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
+ createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+ outTransformHint);
return s;
}
sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
uint32_t h, PixelFormat format,
uint32_t flags, Surface* parent,
- LayerMetadata metadata) {
+ LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1469,11 +1486,15 @@
sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
sp<IGraphicBufferProducer> gbp;
+ uint32_t transformHint = 0;
err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
- std::move(metadata), &handle, &gbp);
+ std::move(metadata), &handle, &gbp, &transformHint);
+ if (outTransformHint) {
+ *outTransformHint = transformHint;
+ }
ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, gbp, true /* owned */);
+ return new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
}
}
return nullptr;
@@ -1482,8 +1503,8 @@
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, uint32_t flags,
- SurfaceControl* parent,
- LayerMetadata metadata) {
+ SurfaceControl* parent, LayerMetadata metadata,
+ uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1496,11 +1517,15 @@
parentHandle = parent->getHandle();
}
+ uint32_t transformHint = 0;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
- &handle, &gbp);
+ &handle, &gbp, &transformHint);
+ if (outTransformHint) {
+ *outTransformHint = transformHint;
+ }
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
+ *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
}
}
return err;
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 071314f..6292388 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -45,20 +45,21 @@
// SurfaceControl
// ============================================================================
-SurfaceControl::SurfaceControl(
- const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp,
- bool owned)
- : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), mOwned(owned)
-{
-}
+SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbp, bool owned,
+ uint32_t transform)
+ : mClient(client),
+ mHandle(handle),
+ mGraphicBufferProducer(gbp),
+ mOwned(owned),
+ mTransformHint(transform) {}
SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
mClient = other->mClient;
mHandle = other->mHandle;
mGraphicBufferProducer = other->mGraphicBufferProducer;
mOwned = false;
+ mTransformHint = other->mTransformHint;
}
SurfaceControl::~SurfaceControl()
@@ -171,11 +172,22 @@
return mClient;
}
+uint32_t SurfaceControl::getTransformHint() const {
+ Mutex::Autolock _l(mLock);
+ return mTransformHint;
+}
+
+void SurfaceControl::setTransformHint(uint32_t hint) {
+ Mutex::Autolock _l(mLock);
+ mTransformHint = hint;
+}
+
void SurfaceControl::writeToParcel(Parcel* parcel)
{
parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
parcel->writeStrongBinder(mHandle);
parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+ parcel->writeUint32(mTransformHint);
}
sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
@@ -189,10 +201,12 @@
sp<IBinder> gbp;
parcel->readNullableStrongBinder(&gbp);
+ uint32_t transformHint = parcel->readUint32();
// We aren't the original owner of the surface.
return new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), interface_cast<IGraphicBufferProducer>(gbp), false /* owned */);
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+ false /* owned */, transformHint);
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 06be1b3..f2bae98 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -30,6 +30,7 @@
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/PhysicalDisplayId.h>
#include <ui/PixelFormat.h>
#include <utils/Errors.h>
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 5fe7ca5..2b65d2f 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -56,7 +56,7 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) = 0;
+ sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -65,7 +65,8 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) = 0;
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a49ed52..c2b5119 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -72,7 +72,7 @@
eCropChanged_legacy = 0x00000100,
eDeferTransaction_legacy = 0x00000200,
eOverrideScalingModeChanged = 0x00000400,
- // AVAILABLE 0x00000800,
+ eShadowRadiusChanged = 0x00000800,
eReparentChildren = 0x00001000,
eDetachChildren = 0x00002000,
eRelativeLayerChanged = 0x00004000,
@@ -126,7 +126,8 @@
colorTransform(mat4()),
bgColorAlpha(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
- colorSpaceAgnostic(false) {
+ colorSpaceAgnostic(false),
+ shadowRadius(0.0f) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -204,6 +205,9 @@
bool colorSpaceAgnostic;
std::vector<ListenerCallbacks> listeners;
+
+ // Draws a shadow around the surface.
+ float shadowRadius;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 08f4e9e..37387ac 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -226,18 +226,18 @@
PixelFormat format, // pixel-format desired
uint32_t flags = 0, // usage flags
SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
status_t createSurfaceChecked(const String8& name, // name of the surface
uint32_t w, // width in pixel
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
//! Create a surface
sp<SurfaceControl> createWithSurfaceParent(const String8& name, // name of the surface
@@ -246,8 +246,8 @@
PixelFormat format, // pixel-format desired
uint32_t flags = 0, // usage flags
Surface* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata() // metadata
- );
+ LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t* outTransformHint = nullptr);
// Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl
// which is a parent of the root of the mirrored hierarchy.
@@ -474,6 +474,7 @@
Transaction& setGeometry(const sp<SurfaceControl>& sc,
const Rect& source, const Rect& dst, int transform);
+ Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index ae4a146..7bc7c68 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -82,10 +82,14 @@
sp<SurfaceComposerClient> getClient() const;
+ uint32_t getTransformHint() const;
+
+ void setTransformHint(uint32_t hint);
+
explicit SurfaceControl(const sp<SurfaceControl>& other);
SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp, bool owned);
+ const sp<IGraphicBufferProducer>& gbp, bool owned, uint32_t transformHint = 0);
private:
// can't be copied
@@ -106,6 +110,7 @@
mutable Mutex mLock;
mutable sp<Surface> mSurfaceData;
bool mOwned;
+ uint32_t mTransformHint;
};
}; // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/EGLConsumer.h b/libs/gui/include/gui/surfacetexture/EGLConsumer.h
new file mode 100644
index 0000000..444722b
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/EGLConsumer.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <gui/BufferQueueDefs.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+ EGLConsumer();
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage(SurfaceTexture& st);
+
+ /*
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage(SurfaceTexture& st);
+
+ /**
+ * detachFromContext detaches the EGLConsumer from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a EGLConsumer from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a EGLConsumer has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the EGLConsumer is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext(SurfaceTexture& st);
+
+ /**
+ * attachToContext attaches a EGLConsumer that is currently in the
+ * 'detached' state to the current OpenGL ES context. A EGLConsumer is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * EGLConsumer that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+ /**
+ * onAbandonLocked amends the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ void onAbandonLocked();
+
+protected:
+ struct PendingRelease {
+ PendingRelease()
+ : isPending(false),
+ currentTexture(-1),
+ graphicBuffer(),
+ display(nullptr),
+ fence(nullptr) {}
+
+ bool isPending;
+ int currentTexture;
+ sp<GraphicBuffer> graphicBuffer;
+ EGLDisplay display;
+ EGLSyncKHR fence;
+ };
+
+ /**
+ * This releases the buffer in the slot referenced by mCurrentTexture,
+ * then updates state to refer to the BufferItem, which must be a
+ * newly-acquired buffer. If pendingRelease is not null, the parameters
+ * which would have been passed to releaseBufferLocked upon the successful
+ * 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,
+ SurfaceTexture& st);
+
+ /**
+ * Binds mTexName and the current buffer to mTexTarget. Uses
+ * mCurrentTexture if it's set, mCurrentTextureImage if not. If the
+ * bind succeeds, this calls doGLFenceWait.
+ */
+ status_t bindTextureImageLocked(SurfaceTexture& st);
+
+ /**
+ * Gets the current EGLDisplay and EGLContext values, and compares them
+ * to mEglDisplay and mEglContext. If the fields have been previously
+ * set, the values must match; if not, the fields are set to the current
+ * values.
+ * The contextCheck argument is used to ensure that a GL context is
+ * properly set; when set to false, the check is not performed.
+ */
+ status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+ /**
+ * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+ * is primarily just one image per slot, but there is also special cases:
+ * - For releaseTexImage, we use a debug image (mReleasedTexImage)
+ * - After freeBuffer, we must still keep the current image/buffer
+ * Reference counting EGLImages lets us handle all these cases easily while
+ * also only creating new EGLImages from buffers when required.
+ */
+ class EglImage : public LightRefBase<EglImage> {
+ public:
+ EglImage(sp<GraphicBuffer> graphicBuffer);
+
+ /**
+ * createIfNeeded creates an EGLImage if required (we haven't created
+ * one yet, or the EGLDisplay or crop-rect has changed).
+ */
+ status_t createIfNeeded(EGLDisplay display, bool forceCreate = false);
+
+ /**
+ * This calls glEGLImageTargetTexture2DOES to bind the image to the
+ * texture in the specified texture target.
+ */
+ void bindToTextureTarget(uint32_t texTarget);
+
+ const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+ const native_handle* graphicBufferHandle() {
+ return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+ }
+
+ private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<EglImage>;
+ virtual ~EglImage();
+
+ // createImage creates a new EGLImage from a GraphicBuffer.
+ EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+ // Disallow copying
+ EglImage(const EglImage& rhs);
+ void operator=(const EglImage& rhs);
+
+ // mGraphicBuffer is the buffer that was used to create this image.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mEglImage is the EGLImage created from mGraphicBuffer.
+ EGLImageKHR mEglImage;
+
+ // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+ EGLDisplay mEglDisplay;
+
+ // mCropRect is the crop rectangle passed to EGL when mEglImage
+ // was created.
+ Rect mCropRect;
+ };
+
+ /**
+ * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+ * stream to ensure that it is safe for future OpenGL ES commands to
+ * access the current texture buffer.
+ */
+ status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+ /**
+ * syncForReleaseLocked performs the synchronization needed to release the
+ * current slot from an OpenGL ES context. If needed it will set the
+ * current slot's fence to guard against a producer accessing the buffer
+ * before the outstanding accesses have completed.
+ */
+ status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st);
+
+ /**
+ * returns a graphic buffer used when the texture image has been released
+ */
+ static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+ /**
+ * The default consumer usage flags that EGLConsumer always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the EGLConsumer user. In particular, EGLConsumer will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentTextureImage is the EglImage/buffer of the current texture. It's
+ * possible that this buffer is not associated with any buffer slot, so we
+ * must track it separately in order to support the getCurrentBuffer method.
+ */
+ sp<EglImage> mCurrentTextureImage;
+
+ /**
+ * EGLSlot contains the information and object references that
+ * EGLConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct EglSlot {
+ EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ /**
+ * mEglImage is the EGLImage created from mGraphicBuffer.
+ */
+ sp<EglImage> mEglImage;
+
+ /**
+ * mFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued. It is initialized
+ * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+ * on a compile-time option) set to a new sync object in updateTexImage.
+ */
+ EGLSyncKHR mEglFence;
+ };
+
+ /**
+ * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+ * associated. It is intialized to EGL_NO_DISPLAY and gets set to the
+ * current display when updateTexImage is called for the first time and when
+ * attachToContext is called.
+ */
+ EGLDisplay mEglDisplay;
+
+ /**
+ * mEglContext is the OpenGL ES context with which this EGLConsumer is
+ * currently associated. It is initialized to EGL_NO_CONTEXT and gets set
+ * to the current GL context when updateTexImage is called for the first
+ * time and when attachToContext is called.
+ */
+ EGLContext mEglContext;
+
+ /**
+ * mEGLSlots stores the buffers that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+ /**
+ * protects static initialization
+ */
+ static Mutex sStaticInitLock;
+
+ /**
+ * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ * mode and releaseTexImage() has been called
+ */
+ static sp<GraphicBuffer> sReleasedTexImageBuffer;
+ sp<EglImage> mReleasedTexImage;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/ImageConsumer.h b/libs/gui/include/gui/surfacetexture/ImageConsumer.h
new file mode 100644
index 0000000..35ae3d2
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/ImageConsumer.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+
+namespace android {
+
+class SurfaceTexture;
+class DequeueBufferCallbacks;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+ typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* fencePassThroughHandle);
+
+ typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+ sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ bool* outQueueEmpty, SurfaceTexture& cb,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+private:
+ /**
+ * ImageSlot contains the information and object references that
+ * ImageConsumer maintains about a BufferQueue buffer slot.
+ */
+ class ImageSlot {
+ public:
+ ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ inline EGLSyncKHR& eglFence() { return mEglFence; }
+
+ private:
+ /**
+ * mEglFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued.
+ */
+ EGLSyncKHR mEglFence;
+ };
+
+ /**
+ * ImageConsumer stores the SkImages that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+} /* namespace android */
diff --git a/libs/gui/include/gui/surfacetexture/SurfaceTexture.h b/libs/gui/include/gui/surfacetexture/SurfaceTexture.h
new file mode 100644
index 0000000..6eaa84e
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,472 @@
+/*
+ * 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 <android/hardware_buffer.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated. If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueBuffer, which either pulls a new buffer or returns the
+ * last cached buffer if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+ /**
+ * Callback function needed by dequeueBuffer. It creates a fence,
+ * that is signalled, when the previous buffer is no longer in use by HWUI
+ * and can be used by written by the producer.
+ */
+ typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* passThroughHandle);
+
+ /**
+ * Callback function needed by dequeueBuffer. It waits for the new buffer
+ * fence to signal, before issuing any draw commands.
+ */
+ typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* passThroughHandle);
+
+ enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+ typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+ /**
+ * SurfaceTexture constructs a new SurfaceTexture object. If the constructor
+ * with the tex parameter is used, tex indicates the name of the OpenGL ES
+ * texture to which images are to be streamed. texTarget specifies the
+ * OpenGL ES texture target to which the texture will be bound in
+ * updateTexImage. useFenceSync specifies whether fences should be used to
+ * synchronize access to buffers if that behavior is enabled at
+ * compile-time.
+ *
+ * A SurfaceTexture may be detached from one OpenGL ES context and then
+ * attached to a different context using the detachFromContext and
+ * attachToContext methods, respectively. The intention of these methods is
+ * purely to allow a SurfaceTexture to be transferred from one consumer
+ * context to another. If such a transfer is not needed there is no
+ * requirement that either of these methods be called.
+ *
+ * If the constructor with the tex parameter is used, the SurfaceTexture is
+ * created in a state where it is considered attached to an OpenGL ES
+ * context for the purposes of the attachToContext and detachFromContext
+ * methods. However, despite being considered "attached" to a context, the
+ * specific OpenGL ES context doesn't get latched until the first call to
+ * updateTexImage. After that point, all calls to updateTexImage must be
+ * made with the same OpenGL ES context current.
+ *
+ * If the constructor without the tex parameter is used, the SurfaceTexture
+ * is created in a detached state, and attachToContext must be called before
+ * calls to updateTexImage.
+ */
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage();
+
+ /**
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage();
+
+ /**
+ * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+ * associated with the texture image set by the most recent call to
+ * updateTexImage.
+ *
+ * This transform matrix maps 2D homogeneous texture coordinates of the form
+ * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+ * coordinate that should be used to sample that location from the texture.
+ * Sampling the texture outside of the range of this transform is undefined.
+ *
+ * This transform is necessary to compensate for transforms that the stream
+ * content producer may implicitly apply to the content. By forcing users of
+ * a SurfaceTexture to apply this transform we avoid performing an extra
+ * copy of the data that would be needed to hide the transform from the
+ * user.
+ *
+ * The matrix is stored in column-major order so that it may be passed
+ * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+ * functions.
+ */
+ void getTransformMatrix(float mtx[16]);
+
+ /**
+ * Computes the transform matrix documented by getTransformMatrix
+ * from the BufferItem sub parts.
+ */
+ static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform, bool filtering);
+
+ /**
+ * Scale the crop down horizontally or vertically such that it has the
+ * same aspect ratio as the buffer does.
+ */
+ static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+
+ /**
+ * getTimestamp retrieves the timestamp associated with the texture image
+ * set by the most recent call to updateTexImage.
+ *
+ * The timestamp is in nanoseconds, and is monotonically increasing. Its
+ * other semantics (zero point, etc) are source-dependent and should be
+ * documented by the source.
+ */
+ int64_t getTimestamp();
+
+ /**
+ * getDataSpace retrieves the DataSpace associated with the texture image
+ * set by the most recent call to updateTexImage.
+ */
+ android_dataspace getCurrentDataSpace();
+
+ /**
+ * getFrameNumber retrieves the frame number associated with the texture
+ * image set by the most recent call to updateTexImage.
+ *
+ * The frame number is an incrementing counter set to 0 at the creation of
+ * the BufferQueue associated with this consumer.
+ */
+ uint64_t getFrameNumber();
+
+ /**
+ * setDefaultBufferSize is used to set the size of buffers returned by
+ * requestBuffers when a with and height of zero is requested.
+ * A call to setDefaultBufferSize() may trigger requestBuffers() to
+ * be called from the client.
+ * The width and height parameters must be no greater than the minimum of
+ * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+ * An error due to invalid dimensions might not be reported until
+ * updateTexImage() is called.
+ */
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+ /**
+ * setFilteringEnabled sets whether the transform matrix should be computed
+ * for use with bilinear filtering.
+ */
+ void setFilteringEnabled(bool enabled);
+
+ /**
+ * getCurrentTextureTarget returns the texture target of the current
+ * texture as returned by updateTexImage().
+ */
+ uint32_t getCurrentTextureTarget() const;
+
+ /**
+ * getCurrentCrop returns the cropping rectangle of the current buffer.
+ */
+ Rect getCurrentCrop() const;
+
+ /**
+ * getCurrentTransform returns the transform of the current buffer.
+ */
+ uint32_t getCurrentTransform() const;
+
+ /**
+ * getCurrentScalingMode returns the scaling mode of the current buffer.
+ */
+ uint32_t getCurrentScalingMode() const;
+
+ /**
+ * getCurrentFence returns the fence indicating when the current buffer is
+ * ready to be read from.
+ */
+ sp<Fence> getCurrentFence() const;
+
+ /**
+ * getCurrentFence returns the FenceTime indicating when the current
+ * buffer is ready to be read from.
+ */
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+ /**
+ * setConsumerUsageBits overrides the ConsumerBase method to OR
+ * DEFAULT_USAGE_FLAGS to usage.
+ */
+ status_t setConsumerUsageBits(uint64_t usage);
+
+ /**
+ * detachFromContext detaches the SurfaceTexture from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a SurfaceTexture has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the SurfaceTexture is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext();
+
+ /**
+ * attachToContext attaches a SurfaceTexture that is currently in the
+ * 'detached' state to the current OpenGL ES context. A SurfaceTexture is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * SurfaceTexture that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex);
+
+ sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outQueueEmpty,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+ /**
+ * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
+ * 'detached' state to a consumer context (usually HWUI RenderThread).
+ */
+ void takeConsumerOwnership();
+
+ /**
+ * releaseConsumerOwnership detaches a SurfaceTexture from a consumer
+ * context (usually HWUI RenderThread).
+ */
+ void releaseConsumerOwnership();
+
+protected:
+ /**
+ * abandonLocked overrides the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ virtual void abandonLocked();
+
+ /**
+ * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+ * specific info in addition to the ConsumerBase behavior.
+ */
+ virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+ /**
+ * acquireBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber = 0) override;
+
+ /**
+ * releaseBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) override;
+
+ /**
+ * freeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ *
+ * This method must be called with mMutex locked.
+ */
+ virtual void freeBufferLocked(int slotIndex);
+
+ /**
+ * computeCurrentTransformMatrixLocked computes the transform matrix for the
+ * current texture. It uses mCurrentTransform and the current GraphicBuffer
+ * to compute this matrix and stores it in mCurrentTransformMatrix.
+ * mCurrentTextureImage must not be NULL.
+ */
+ void computeCurrentTransformMatrixLocked();
+
+ /**
+ * The default consumer usage flags that SurfaceTexture always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the SurfaceTexture user. In particular, SurfaceTexture will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentCrop is the crop rectangle that applies to the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ Rect mCurrentCrop;
+
+ /**
+ * mCurrentTransform is the transform identifier for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ uint32_t mCurrentTransform;
+
+ /**
+ * mCurrentScalingMode is the scaling mode for the current texture. It gets
+ * set each time updateTexImage is called.
+ */
+ uint32_t mCurrentScalingMode;
+
+ /**
+ * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+ */
+ sp<Fence> mCurrentFence;
+
+ /**
+ * The FenceTime wrapper around mCurrentFence.
+ */
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+ /**
+ * mCurrentTransformMatrix is the transform matrix for the current texture.
+ * It gets computed by computeTransformMatrix each time updateTexImage is
+ * called.
+ */
+ float mCurrentTransformMatrix[16];
+
+ /**
+ * mCurrentTimestamp is the timestamp for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ int64_t mCurrentTimestamp;
+
+ /**
+ * mCurrentDataSpace is the dataspace for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ android_dataspace mCurrentDataSpace;
+
+ /**
+ * mCurrentFrameNumber is the frame counter for the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ uint64_t mCurrentFrameNumber;
+
+ uint32_t mDefaultWidth, mDefaultHeight;
+
+ /**
+ * mFilteringEnabled indicates whether the transform matrix is computed for
+ * use with bilinear filtering. It defaults to true and is changed by
+ * setFilteringEnabled().
+ */
+ bool mFilteringEnabled;
+
+ /**
+ * mTexName is the name of the OpenGL texture to which streamed images will
+ * be bound when updateTexImage is called. It is set at construction time
+ * and can be changed with a call to attachToContext.
+ */
+ uint32_t mTexName;
+
+ /**
+ * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+ * extension should be used to prevent buffers from being dequeued before
+ * it's safe for them to be written. It gets set at construction time and
+ * never changes.
+ */
+ const bool mUseFenceSync;
+
+ /**
+ * mTexTarget is the GL texture target with which the GL texture object is
+ * associated. It is set in the constructor and never changed. It is
+ * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+ * Browser. In that case it is set to GL_TEXTURE_2D to allow
+ * glCopyTexSubImage to read from the texture. This is a hack to work
+ * around a GL driver limitation on the number of FBO attachments, which the
+ * browser's tile cache exceeds.
+ */
+ const uint32_t mTexTarget;
+
+ /**
+ * mCurrentTexture is the buffer slot index of the buffer that is currently
+ * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+ * indicating that no buffer slot is currently bound to the texture. Note,
+ * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ * that no buffer is bound to the texture. A call to setBufferCount will
+ * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+ */
+ int mCurrentTexture;
+
+ enum class OpMode { detached, attachedToConsumer, attachedToGL };
+ /**
+ * mOpMode indicates whether the SurfaceTexture is currently attached to
+ * an OpenGL ES context or the consumer context. For legacy reasons, this
+ * is initialized to, "attachedToGL" indicating that the SurfaceTexture is
+ * considered to be attached to whatever GL context is current at the time
+ * of the first updateTexImage call.
+ * It is set to "detached" by detachFromContext, and then set to
+ * "attachedToGL" again by attachToContext.
+ * takeConsumerOwnership/releaseConsumerOwnership are used to attach/detach
+ * from a consumer context - usually HWUI RenderThread.
+ */
+ OpMode mOpMode;
+
+ /**
+ * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+ */
+ EGLConsumer mEGLConsumer;
+
+ /**
+ * mImageConsumer has SurfaceTexture logic used when attached to a consumer
+ * context (usually HWUI RenderThread).
+ */
+ ImageConsumer mImageConsumer;
+
+ friend class ImageConsumer;
+ friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
new file mode 100644
index 0000000..72f98ad
--- /dev/null
+++ b/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
@@ -0,0 +1,91 @@
+/*
+ * 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 _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+#define _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <system/graphics.h>
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+
+// This file provides a facade API on top of SurfaceTexture, which avoids using
+// C++ types. This is still a C++ unstable API though. Ideally features here
+// will be exposed via public NDK API and this file will be deleted.
+
+struct ASurfaceTexture {
+ android::sp<android::SurfaceTexture> consumer;
+ android::sp<android::IGraphicBufferProducer> producer;
+};
+
+namespace android {
+
+/**
+ * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
+ * current texture.
+ */
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_takeConsumerOwnership attaches an ASurfaceTexture that is
+ * currently in the 'detached' state to a consumer context.
+ */
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_releaseConsumerOwnership detaches a SurfaceTexture from
+ * a consumer context.
+ */
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It creates a
+ * fence that is signalled when the previous buffer is no longer in use by the
+ * consumer (usually HWUI RenderThread) and can be written to by the producer.
+ */
+typedef int (*ASurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* fencePassThroughHandle);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It waits for the
+ * new buffer fence to signal before issuing any draw commands.
+ */
+typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ */
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_create creates an ASurfaceTexture, which is
+ * a simple struct containing a SurfaceTexture and an IGraphicBufferProducer.
+ */
+ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
+ sp<IGraphicBufferProducer> producer);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/gui/surfacetexture/EGLConsumer.cpp b/libs/gui/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 0000000..1f0f52c
--- /dev/null
+++ b/libs/gui/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <gui/surfacetexture/EGLConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+#include <inttypes.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+ uint32_t width, height;
+ char const* bits;
+} kDebugData = {15, 12,
+ "_______________"
+ "_______________"
+ "_____XX_XX_____"
+ "__X_X_____X_X__"
+ "__X_XXXXXXX_X__"
+ "__XXXXXXXXXXX__"
+ "___XX_XXX_XX___"
+ "____XXXXXXX____"
+ "_____X___X_____"
+ "____X_____X____"
+ "_______________"
+ "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+ bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+ bool atEnd = (cropExtLen + 1) < extsLen &&
+ !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+ bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+ return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+ // Only compute whether the extension is present once the first time this
+ // function is called.
+ static bool hasIt = hasEglProtectedContentImpl();
+ return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ EGC_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(st.mTexTarget, st.mTexName);
+ err = NO_ERROR;
+ } else {
+ EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = updateAndReleaseLocked(item, nullptr, st);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(st.mTexTarget, st.mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = NO_ERROR;
+
+ // if we're detached, no need to validate EGL's state -- we won't use it.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ err = checkAndUpdateEglStateLocked(st, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ int buf = st.mCurrentTexture;
+ if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+ EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+ // if we're detached, we just use the fence that was created in
+ // detachFromContext() so... basically, nothing more to do here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // Do whatever sync ops we need to do before releasing the slot.
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+ return err;
+ }
+ }
+
+ err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ if (err < NO_ERROR) {
+ EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ if (mReleasedTexImage == nullptr) {
+ mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ }
+
+ st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ mCurrentTextureImage = mReleasedTexImage;
+ st.mCurrentCrop.makeInvalid();
+ st.mCurrentTransform = 0;
+ st.mCurrentTimestamp = 0;
+ st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+ st.mCurrentFence = Fence::NO_FENCE;
+ st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+ // detached, don't touch the texture (and we may not even have an
+ // EGLDisplay here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // This binds a dummy buffer (mReleasedTexImage).
+ status_t result = bindTextureImageLocked(st);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+ Mutex::Autolock _l(sStaticInitLock);
+ if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
+ // The first time, create the debug texture in case the application
+ // continues to use it.
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+ GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ "[EGLConsumer debug texture]");
+ uint32_t* bits;
+ buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+ uint32_t stride = buffer->getStride();
+ uint32_t height = buffer->getHeight();
+ memset(bits, 0, stride * height * 4);
+ for (uint32_t y = 0; y < kDebugData.height; y++) {
+ for (uint32_t x = 0; x < kDebugData.width; x++) {
+ bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+ : 0xFFFFFFFF;
+ }
+ bits += stride;
+ }
+ buffer->unlock();
+ sReleasedTexImageBuffer = buffer;
+ }
+ return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior EglImage created is using a stale buffer. This
+ // replaces any old EglImage with a new one (using the new buffer).
+ int slot = item->mSlot;
+ if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+ mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st) {
+ status_t err = NO_ERROR;
+
+ int slot = item.mSlot;
+
+ if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+ EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL "
+ "ES context");
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return INVALID_OPERATION;
+ }
+
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
+
+ // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+ // if nessessary, for the gralloc buffer currently in the slot in
+ // ConsumerBase.
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired).
+ err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+ slot);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return UNKNOWN_ERROR;
+ }
+
+ // Do whatever sync ops we need to do before releasing the old slot.
+ if (slot != st.mCurrentTexture) {
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ // As we are still under lock since acquireBuffer, it is safe to
+ // release by slot.
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ return err;
+ }
+ }
+
+ EGC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+ mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle()
+ : nullptr,
+ slot, st.mSlots[slot].mGraphicBuffer->handle);
+
+ // 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.
+ sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+ // release old buffer
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (pendingRelease == nullptr) {
+ status_t status =
+ st.releaseBufferLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+ mEglSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+ status);
+ err = status;
+ // keep going, with error raised [?]
+ }
+ } else {
+ pendingRelease->currentTexture = st.mCurrentTexture;
+ pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+ pendingRelease->display = mEglDisplay;
+ pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ pendingRelease->isPending = true;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ st.mCurrentTexture = slot;
+ mCurrentTextureImage = nextTextureImage;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+
+ st.computeCurrentTransformMatrixLocked();
+
+ return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(st.mTexTarget, st.mTexName);
+ if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+ EGC_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+
+ status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+ // In the rare case that the display is terminated and then initialized
+ // again, we can't detect that the display changed (it didn't), but the
+ // image is invalid. In this case, repeat the exact same steps while
+ // forcing the creation of a new image.
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ glBindTexture(st.mTexTarget, st.mTexName);
+ status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+ if (result != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (!contextCheck) {
+ // if this is the first time we're called, mEglDisplay/mEglContext have
+ // never been set, so don't error out (below).
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ mEglDisplay = dpy;
+ }
+ if (mEglContext == EGL_NO_CONTEXT) {
+ mEglContext = ctx;
+ }
+ }
+
+ if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+ EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+ EGC_LOGE("detachFromContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+ status_t err = syncForReleaseLocked(dpy, st);
+ if (err != OK) {
+ return err;
+ }
+
+ glDeleteTextures(1, &st.mTexName);
+ }
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+
+ return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+ // Initialize mCurrentTextureImage if there is a current buffer from past
+ // attached state.
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ if (!mEglSlots[slot].mEglImage.get()) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+ mCurrentTextureImage = mEglSlots[slot].mEglImage;
+ }
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("attachToContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("attachToContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ // We need to bind the texture regardless of whether there's a current
+ // buffer.
+ glBindTexture(st.mTexTarget, GLuint(tex));
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ st.mTexName = tex;
+ st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+ if (mCurrentTextureImage != nullptr) {
+ // This may wait for a buffer a second time. This is likely required if
+ // this is a different context, since otherwise the wait could be skipped
+ // by bouncing through another context. For the same context the extra
+ // wait is redundant.
+ status_t err = bindTextureImageLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+ EGC_LOGV("syncForReleaseLocked");
+
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+ eglDestroySyncKHR(dpy, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ EGC_LOGE("syncForReleaseLocked: error dup'ing native fence "
+ "fd: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), fence);
+ if (err != OK) {
+ EGC_LOGE("syncForReleaseLocked: error adding release fence: "
+ "%s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ if (fence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ EGC_LOGE("syncForReleaseLocked: error waiting for previous "
+ "fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ EGC_LOGE("syncForReleaseLocked: timeout waiting for previous "
+ "fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(dpy, fence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (fence == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ mEglSlots[st.mCurrentTexture].mEglFence = fence;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (st.mCurrentFence->isValid()) {
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = st.mCurrentFence->dup();
+ if (fenceFd == -1) {
+ EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(dpy, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(dpy, sync);
+ if (eglErr != EGL_SUCCESS) {
+ EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+ if (err != NO_ERROR) {
+ EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+ mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+ mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+ : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+ if (mEglImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("~EglImage: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) {
+ // If there's an image and it's no longer valid, destroy it.
+ bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+ bool displayInvalid = mEglDisplay != eglDisplay;
+ if (haveImage && (displayInvalid || forceCreation)) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ mEglImage = EGL_NO_IMAGE_KHR;
+ mEglDisplay = EGL_NO_DISPLAY;
+ }
+
+ // If there's no image, create one.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = eglDisplay;
+ mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+ }
+
+ // Fail if we can't create a valid image.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = EGL_NO_DISPLAY;
+ const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+ 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 UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+ glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& graphicBuffer) {
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+ const bool createProtectedImage =
+ (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent();
+ EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR,
+ EGL_TRUE,
+ createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ createProtectedImage ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ eglInitialize(dpy, nullptr, nullptr);
+ EGLImageKHR image =
+ eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ EGLint error = eglGetError();
+ ALOGE("error creating EGLImage: %#x", error);
+ eglTerminate(dpy);
+ }
+ return image;
+}
+
+} // namespace android
diff --git a/libs/gui/surfacetexture/ImageConsumer.cpp b/libs/gui/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 0000000..4bc4a7b
--- /dev/null
+++ b/libs/gui/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <gui/BufferQueue.h>
+#include <gui/surfacetexture/ImageConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+ mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
+}
+
+sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ bool* outQueueEmpty, SurfaceTexture& st,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle) {
+ BufferItem item;
+ status_t err;
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != OK) {
+ if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+ IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ } else {
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ *outQueueEmpty = true;
+ *outDataspace = st.mCurrentDataSpace;
+ *outSlotid = slot;
+ return st.mSlots[slot].mGraphicBuffer;
+ }
+ }
+ return nullptr;
+ }
+
+ int slot = item.mSlot;
+ if (item.mFence->isValid()) {
+ // Wait on the producer fence for the buffer to be ready.
+ err = fenceWait(item.mFence->get(), fencePassThroughHandle);
+ if (err != OK) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Release old buffer.
+ if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+ // If needed, set the released slot's fence to guard against a producer
+ // accessing the buffer before the outstanding accesses have completed.
+ int releaseFenceId = -1;
+ EGLDisplay display = EGL_NO_DISPLAY;
+ err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display,
+ &releaseFenceId, fencePassThroughHandle);
+ if (OK != err) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+
+ if (releaseFenceId != -1) {
+ sp<Fence> releaseFence(new Fence(releaseFenceId));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ st.mSlots[st.mCurrentTexture].mGraphicBuffer,
+ releaseFence);
+ if (err != OK) {
+ IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Finally release the old buffer.
+ status_t status =
+ st.releaseBufferLocked(st.mCurrentTexture,
+ st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+ mImageSlots[st.mCurrentTexture].eglFence());
+ if (status < NO_ERROR) {
+ IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+ err = status;
+ // Keep going, with error raised.
+ }
+ }
+
+ // Update the state.
+ st.mCurrentTexture = slot;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+ st.computeCurrentTransformMatrixLocked();
+
+ *outQueueEmpty = false;
+ *outDataspace = item.mDataSpace;
+ *outSlotid = slot;
+ return st.mSlots[slot].mGraphicBuffer;
+}
+
+} /* namespace android */
diff --git a/libs/gui/surfacetexture/SurfaceTexture.cpp b/libs/gui/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 0000000..25e5618
--- /dev/null
+++ b/libs/gui/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,528 @@
+/*
+ * 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 <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <gui/surfacetexture/ImageConsumer.h>
+#include <gui/surfacetexture/SurfaceTexture.h>
+#include <gui/surfacetexture/surface_texture_platform.h>
+#include <math/mat4.h>
+#include <system/window.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+ uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::attachedToGL) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
+ bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::detached) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+ ATRACE_CALL();
+ SFT_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+ // releaseTexImage can be invoked even when not attached to a GL context.
+ ATRACE_CALL();
+ SFT_LOGV("releaseTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) {
+ status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ switch (mOpMode) {
+ case OpMode::attachedToConsumer:
+ break;
+ case OpMode::attachedToGL:
+ mEGLConsumer.onAcquireBufferLocked(item, *this);
+ break;
+ case OpMode::detached:
+ break;
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) {
+ // release the buffer if it hasn't already been discarded by the
+ // BufferQueue. This can happen, for example, when the producer of this
+ // buffer has reallocated the original buffer slot after this buffer
+ // was acquired.
+ status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
+ // We could be releasing an EGL/Vulkan buffer, even if not currently
+ // attached to a GL context.
+ mImageConsumer.onReleaseBufferLocked(buf);
+ mEGLConsumer.onReleaseBufferLocked(buf);
+ return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+ ATRACE_CALL();
+ SFT_LOGV("detachFromContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::attachedToGL) {
+ SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mEGLConsumer.detachFromContext(*this);
+ if (err == OK) {
+ mOpMode = OpMode::detached;
+ }
+
+ return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+ ATRACE_CALL();
+ SFT_LOGV("attachToContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::detached) {
+ SFT_LOGE("attachToContext: SurfaceTexture is already attached to a "
+ "context");
+ return INVALID_OPERATION;
+ }
+
+ return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::takeConsumerOwnership() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("attachToView: abandoned SurfaceTexture");
+ return;
+ }
+ if (mOpMode == OpMode::detached) {
+ mOpMode = OpMode::attachedToConsumer;
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible EGLConsumer texture cache
+ mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+ mEGLConsumer.onAbandonLocked();
+ }
+ } else {
+ SFT_LOGE("attachToView: already attached");
+ }
+}
+
+void SurfaceTexture::releaseConsumerOwnership() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+ return;
+ }
+
+ if (mOpMode == OpMode::attachedToConsumer) {
+ mOpMode = OpMode::detached;
+ } else {
+ SFT_LOGE("detachFromView: not attached to View");
+ }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+ return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+ Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ return;
+ }
+ bool needsRecompute = mFilteringEnabled != enabled;
+ mFilteringEnabled = enabled;
+
+ if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ SFT_LOGD("setFilteringEnabled called with no current item");
+ }
+
+ if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ computeCurrentTransformMatrixLocked();
+ }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+ SFT_LOGV("computeCurrentTransformMatrixLocked");
+ sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+ ? nullptr
+ : mSlots[mCurrentTexture].mGraphicBuffer;
+ if (buf == nullptr) {
+ SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+ }
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+ mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform,
+ bool filtering) {
+ // Transform matrices
+ static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+ mat4 xform;
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ xform *= mtxFlipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ xform *= mtxFlipV;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ xform *= mtxRot90;
+ }
+
+ if (!cropRect.isEmpty() && buf.get()) {
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+ }
+
+ mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+ xform = crop * xform;
+ }
+
+ // SurfaceFlinger expects the top of its window textures to be at a Y
+ // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // want to expose this to applications, however, so we must add an
+ // additional vertical flip to the transform after all the other transforms.
+ xform = mtxFlipV * xform;
+
+ memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+ Rect outCrop = crop;
+
+ uint32_t newWidth = static_cast<uint32_t>(crop.width());
+ uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+ if (newWidth * bufferHeight > newHeight * bufferWidth) {
+ newWidth = newHeight * bufferWidth / bufferHeight;
+ ALOGV("too wide: newWidth = %d", newWidth);
+ } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+ newHeight = newWidth * bufferHeight / bufferWidth;
+ ALOGV("too tall: newHeight = %d", newHeight);
+ }
+
+ uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+ uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+ // The crop is too wide
+ if (newWidth < currentWidth) {
+ uint32_t dw = currentWidth - newWidth;
+ auto halfdw = dw / 2;
+ outCrop.left += halfdw;
+ // Not halfdw because it would subtract 1 too few when dw is odd
+ outCrop.right -= (dw - halfdw);
+ // The crop is too tall
+ } else if (newHeight < currentHeight) {
+ uint32_t dh = currentHeight - newHeight;
+ auto halfdh = dh / 2;
+ outCrop.top += halfdh;
+ // Not halfdh because it would subtract 1 too few when dh is odd
+ outCrop.bottom -= (dh - halfdh);
+ }
+
+ ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+ outCrop.bottom);
+
+ return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+ SFT_LOGV("getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+ SFT_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+ SFT_LOGV("getFrameNumber");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+ Mutex::Autolock lock(mMutex);
+ return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+ ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+ : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+ SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ if (slotIndex == mCurrentTexture) {
+ mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ }
+ // The slotIndex buffer could have EGL cache, but there is no way to tell
+ // for sure. Buffers can be freed after SurfaceTexture has detached from GL
+ // context or View.
+ mEGLConsumer.onFreeBufferLocked(slotIndex);
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+ SFT_LOGV("abandonLocked");
+ mEGLConsumer.onAbandonLocked();
+ ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+ return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
+ "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+ prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
+ mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+ mCurrentTransform);
+
+ ConsumerBase::dumpLocked(result, prefix);
+}
+
+sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outQueueEmpty,
+ SurfaceTexture_createReleaseFence createFence,
+ SurfaceTexture_fenceWait fenceWait,
+ void* fencePassThroughHandle) {
+ Mutex::Autolock _l(mMutex);
+ sp<GraphicBuffer> buffer;
+
+ if (mAbandoned) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+ return buffer;
+ }
+
+ if (mOpMode != OpMode::attachedToConsumer) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+ return buffer;
+ }
+
+ buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
+ createFence, fenceWait, fencePassThroughHandle);
+ memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+ return buffer;
+}
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+ return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle) {
+ sp<GraphicBuffer> buffer;
+ *outNewContent = false;
+ bool queueEmpty;
+ do {
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+ &queueEmpty, createFence, fenceWait, handle);
+ if (!queueEmpty) {
+ *outNewContent = true;
+ }
+ } while (buffer.get() && (!queueEmpty));
+ return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+}
+
+ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
+ sp<IGraphicBufferProducer> producer) {
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = consumer;
+ ast->producer = producer;
+ return ast;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index db1ac24..ff22913 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -26,6 +26,7 @@
#include <ui/DisplayInfo.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
#include <gtest/gtest.h>
@@ -201,6 +202,7 @@
igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
&qbOutput));
ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3));
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
int slot;
sp<Fence> fence;
@@ -222,6 +224,7 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
adapter.waitForCallback();
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 03b9cd7..8d36ba7 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -69,7 +69,6 @@
mSurfaceControl = sc;
InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
- mServerChannel->setToken(new BBinder());
mInputFlinger = getInputFlinger();
mInputFlinger->registerInputChannel(mServerChannel);
@@ -165,7 +164,7 @@
}
void populateInputInfo(int width, int height) {
- mInputInfo.token = mServerChannel->getToken();
+ mInputInfo.token = mServerChannel->getConnectionToken();
mInputInfo.name = "Test info";
mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index c4f7fe0..a5dd3c0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -11,7 +11,7 @@
#define DEBUG_CHANNEL_MESSAGES 0
// Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 0
+static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
// Log debug messages about transport actions
#define DEBUG_TRANSPORT_ACTIONS 0
@@ -225,28 +225,28 @@
// --- InputChannel ---
-sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd) {
+sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
+ sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
- return new InputChannel(name, std::move(fd));
+ return new InputChannel(name, std::move(fd), token);
}
-InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd)
- : mName(name), mFd(std::move(fd)) {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel constructed: name='%s', fd=%d",
- mName.c_str(), fd);
-#endif
+InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
+ : mName(name), mFd(std::move(fd)), mToken(token) {
+ if (DEBUG_CHANNEL_LIFECYCLE) {
+ ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ }
}
InputChannel::~InputChannel() {
-#if DEBUG_CHANNEL_LIFECYCLE
- ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
-#endif
+ if (DEBUG_CHANNEL_LIFECYCLE) {
+ ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ }
}
status_t InputChannel::openInputChannelPair(const std::string& name,
@@ -267,13 +267,15 @@
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
+ sp<IBinder> token = new BBinder();
+
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
- outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd));
+ outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
- outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd));
+ outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
@@ -369,7 +371,7 @@
getName().c_str());
return nullptr;
}
- return InputChannel::create(mName, std::move(newFd));
+ return InputChannel::create(mName, std::move(newFd), mToken);
}
status_t InputChannel::write(Parcel& out) const {
@@ -396,24 +398,13 @@
return nullptr;
}
- sp<InputChannel> channel = InputChannel::create(name, std::move(rawFd));
- if (channel != nullptr) {
- channel->setToken(token);
- }
- return channel;
+ return InputChannel::create(name, std::move(rawFd), token);
}
-sp<IBinder> InputChannel::getToken() const {
+sp<IBinder> InputChannel::getConnectionToken() const {
return mToken;
}
-void InputChannel::setToken(const sp<IBinder>& token) {
- if (mToken != nullptr) {
- ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str());
- }
- mToken = token;
-}
-
// --- InputPublisher ---
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 7c331e1..ada275d 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -46,7 +46,8 @@
android::base::unique_fd sendFd(pipe.sendFd);
- sp<InputChannel> inputChannel = InputChannel::create("channel name", std::move(sendFd));
+ sp<InputChannel> inputChannel =
+ InputChannel::create("channel name", std::move(sendFd), new BBinder());
EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
EXPECT_STREQ("channel name", inputChannel->getName().c_str())
@@ -59,13 +60,11 @@
TEST_F(InputChannelTest, SetAndGetToken) {
Pipe pipe;
- sp<InputChannel> channel =
- InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd));
- EXPECT_EQ(channel->getToken(), nullptr);
-
sp<IBinder> token = new BBinder();
- channel->setToken(token);
- EXPECT_EQ(token, channel->getToken());
+ sp<InputChannel> channel =
+ InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
+
+ EXPECT_EQ(token, channel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
new file mode 100644
index 0000000..6665635
--- /dev/null
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <apex/display.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
+
+#include <algorithm>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+ /**
+ * The width in pixels of the display configuration.
+ */
+ int32_t width{0};
+
+ /**
+ * The height in pixels of the display configuration.
+ */
+
+ int32_t height{0};
+
+ /**
+ * The display density.
+ */
+ float density{0};
+
+ /**
+ * The refresh rate of the display configuration, in frames per second.
+ */
+ float fps{0.0};
+
+ /**
+ * The vsync offset at which surfaceflinger runs, in nanoseconds.
+ */
+ int64_t sfOffset{0};
+
+ /**
+ * The vsync offset at which applications run, in nanoseconds.
+ */
+ int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+ /**
+ * A physical display ID, unique to this display.
+ */
+ PhysicalDisplayId id;
+
+ /**
+ * The type of the display, i.e. whether it is an internal or external
+ * display.
+ */
+ ADisplayType type;
+
+ /**
+ * Number of supported configs
+ */
+ size_t numConfigs;
+
+ /**
+ * Set of supported configs by this display.
+ */
+ DisplayConfigImpl* configs;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+#define CHECK_NOT_NULL(name) \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+namespace {
+sp<IBinder> getToken(ADisplay* display) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
+}
+
+int64_t computeSfOffset(const DisplayInfo& info) {
+ // This should probably be part of the config instead of extrapolated from
+ // the presentation deadline and fudged here, but the way the math works out
+ // here we do get the right offset.
+ return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
+}
+} // namespace
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+ const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ const size_t size = ids.size();
+ if (size == 0) {
+ return NO_INIT;
+ }
+
+ std::vector<DisplayConfigImpl> configsPerDisplay[size];
+ int numConfigs = 0;
+ for (int i = 0; i < size; ++i) {
+ const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
+ Vector<DisplayInfo> configs;
+ const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ if (status != OK) {
+ return status;
+ }
+ if (configs.empty()) {
+ return NO_INIT;
+ }
+
+ numConfigs += configs.size();
+ configsPerDisplay[i].reserve(configs.size());
+ for (int j = 0; j < configs.size(); ++j) {
+ const DisplayInfo config = configs[j];
+ configsPerDisplay[i].emplace_back(
+ DisplayConfigImpl{static_cast<int32_t>(config.w),
+ static_cast<int32_t>(config.h), config.density, config.fps,
+ computeSfOffset(config), config.appVsyncOffset});
+ }
+ }
+
+ const std::optional<PhysicalDisplayId> internalId =
+ SurfaceComposerClient::getInternalDisplayId();
+
+ // Here we allocate all our required memory in one block. The layout is as
+ // follows:
+ // ------------------------------------------------------------
+ // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
+ // ------------------------------------------------------------
+ //
+ // The caller will be given a DisplayImpl** which points to the beginning of
+ // the block of DisplayImpl pointers.
+ // Each DisplayImpl* points to a DisplayImpl in the second block.
+ // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
+ // contiguous block of DisplayConfigImpls specific to that display.
+ DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+ malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
+ sizeof(DisplayConfigImpl) * numConfigs));
+ DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
+ DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
+
+ for (size_t i = 0; i < size; ++i) {
+ const PhysicalDisplayId id = ids[i];
+ const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
+ : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+ const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+ memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
+
+ displayData[i] = DisplayImpl{id, type, configs.size(), configData};
+ impls[i] = displayData + i;
+ // Advance the configData pointer so that future configs are written to
+ // the correct display.
+ configData += configs.size();
+ }
+
+ *outDisplays = reinterpret_cast<ADisplay**>(impls);
+ return size;
+}
+
+void ADisplay_release(ADisplay** displays) {
+ if (displays == nullptr) {
+ return;
+ }
+ free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ float maxFps = 0.0;
+ for (int i = 0; i < impl->numConfigs; ++i) {
+ maxFps = std::max(maxFps, impl->configs[i].fps);
+ }
+ return maxFps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+ CHECK_NOT_NULL(display);
+
+ return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+ CHECK_NOT_NULL(display);
+
+ sp<IBinder> token = getToken(display);
+ const int index = SurfaceComposerClient::getActiveConfig(token);
+ if (index < 0) {
+ return index;
+ }
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
+ return OK;
+}
+
+float ADisplayConfig_getDensity(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->density;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+ CHECK_NOT_NULL(config);
+
+ return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
new file mode 100644
index 0000000..66ebdfd
--- /dev/null
+++ b/libs/nativedisplay/Android.bp
@@ -0,0 +1,50 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+ name: "libnativedisplay_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libnativedisplay",
+ export_include_dirs: [
+ "include",
+ ],
+
+ clang: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ srcs: [
+ "ADisplay.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libnativedisplay_headers",
+ ],
+
+}
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
new file mode 100644
index 0000000..7af452a
--- /dev/null
+++ b/libs/nativedisplay/include/apex/display.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native display
+ */
+typedef struct ADisplay ADisplay;
+
+/**
+ * Enum describing the possible types of a display
+ */
+enum ADisplayType {
+ /**
+ * A display that is the internal, or "primary" display for a device.
+ */
+ DISPLAY_TYPE_INTERNAL = 0,
+
+ /**
+ * A display that is externally connected for a device.
+ */
+ DISPLAY_TYPE_EXTERNAL = 1,
+};
+
+/**
+ * Opaque handle for display metadata
+ */
+typedef struct ADisplayConfig ADisplayConfig;
+
+/**
+ * Acquires a list of display handles. Memory is allocated for the list and is
+ * owned by the caller. The caller is responsible for freeing this memory by
+ * calling ADisplayList_release.
+ *
+ * Returns the size of the returned list on success.
+ * Returns -errno on error.
+ */
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);
+
+/**
+ * Releases a list of display handles created by
+ * ADisplayList_acquirePhysicalDisplays.
+ */
+void ADisplay_release(ADisplay** displays);
+
+/**
+ * Queries the maximum supported fps for the given display.
+ */
+float ADisplay_getMaxSupportedFps(ADisplay* display);
+
+/**
+ * Queries the display's type.
+ */
+ADisplayType ADisplay_getDisplayType(ADisplay* display);
+
+/**
+ * Gets the current display configuration for the given display.
+ *
+ * Memory is *not* allocated for the caller. As such, the returned output
+ * configuration's lifetime will not be longer than the ADisplay* passed to this
+ * function - if ADisplay_release is called destroying the ADisplay object then
+ * it is invalid to access the ADisplayConfig returned here.
+ *
+ * Note that the current display configuration can change. Listening to updates
+ * to the current display configuration should be done via Choreographer. If
+ * such an update is observed, then this method should be recalled to get the
+ * new current configuration.
+ *
+ * Returns OK on success, -errno on failure.
+ */
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
+
+/**
+ * Queries the density for a given display configuration.
+ */
+float ADisplayConfig_getDensity(ADisplayConfig* config);
+
+/**
+ * Queries the width in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
+
+/**
+ * Queries the height in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config);
+
+/**
+ * Queries the display refresh rate for a given display configuration.
+ */
+float ADisplayConfig_getFps(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which the system compositor is scheduled to
+ * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
+ * this returns S in nanoseconds.
+ */
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which applications are scheduled to run. If a
+ * vsync occurs at time T, and applications run at time T + S, then this returns
+ * S in nanoseconds.
+ */
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
+
+__END_DECLS
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index da959e3..ae5e47b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -342,6 +342,8 @@
* not compatible with its usage flags, the results are undefined and
* may include program termination.
*
+ * Available since API level 26.
+ *
* \return 0 on success, or an error number of the allocation fails for
* any reason. The returned buffer has a reference count of 1.
*/
@@ -352,18 +354,24 @@
*
* This prevents the object from being deleted until the last reference
* is removed.
+ *
+ * Available since API level 26.
*/
void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
/**
* Remove a reference that was previously acquired with
* AHardwareBuffer_acquire() or AHardwareBuffer_allocate().
+ *
+ * Available since API level 26.
*/
void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
/**
* Return a description of the AHardwareBuffer in the passed
* AHardwareBuffer_Desc struct.
+ *
+ * Available since API level 26.
*/
void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26);
@@ -413,6 +421,8 @@
* simultaneously, and the contents of the buffer behave like shared
* memory.
*
+ * Available since API level 26.
+ *
* \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
* are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
* has more than one layer. Error number if the lock fails for any other
@@ -441,6 +451,8 @@
*
* See the AHardwareBuffer_lock documentation for all other locking semantics.
*
+ * Available since API level 29.
+ *
* \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
* are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
* has more than one layer. Error number if the lock fails for any other
@@ -462,6 +474,8 @@
* completed before the function returned and no further operations are
* necessary.
*
+ * Available since API level 26.
+ *
* \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
* the unlock fails for any reason.
*/
@@ -470,6 +484,8 @@
/**
* Send the AHardwareBuffer to an AF_UNIX socket.
*
+ * Available since API level 26.
+ *
* \return 0 on success, -EINVAL if \a buffer is NULL, or an error
* number if the operation fails for any reason.
*/
@@ -478,6 +494,8 @@
/**
* Receive an AHardwareBuffer from an AF_UNIX socket.
*
+ * Available since API level 26.
+ *
* \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
* number if the operation fails for any reason.
*/
@@ -501,6 +519,8 @@
* some implementations have implementation-defined limits on texture
* size and layer count.
*
+ * Available since API level 29.
+ *
* \return 1 if the format and usage flag combination is allocatable,
* 0 otherwise.
*/
@@ -514,6 +534,8 @@
* of the locked buffer. If the bytes per pixel or bytes per stride are unknown
* or variable, or if the underlying mapper implementation does not support returning
* additional information, then this call will fail with INVALID_OPERATION
+ *
+ * Available since API level 29.
*/
int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
int32_t fence, const ARect* rect, void** outVirtualAddress,
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 6730596..3e436e3 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -189,6 +189,8 @@
/**
* Set a transform that will be applied to future buffers posted to the window.
*
+ * Available since API level 26.
+ *
* \param transform combination of {@link ANativeWindowTransform} flags
* \return 0 for success, or -EINVAL if \p transform is invalid
*/
@@ -208,6 +210,8 @@
* measurement data instead of color images. The default dataSpace is 0,
* ADATASPACE_UNKNOWN, unless it has been overridden by the producer.
*
+ * Available since API level 28.
+ *
* \param dataSpace data space of all buffers queued after this call.
* \return 0 for success, -EINVAL if window is invalid or the dataspace is not
* supported.
@@ -216,6 +220,9 @@
/**
* Get the dataspace of the buffers in window.
+ *
+ * Available since API level 28.
+ *
* \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if
* dataspace is unknown, or -EINVAL if window is invalid.
*/
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index b741faa..daf1dcc 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,9 +2,9 @@
global:
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
- AHardwareBuffer_createFromHandle; # vndk
+ AHardwareBuffer_createFromHandle; # llndk
AHardwareBuffer_describe;
- AHardwareBuffer_getNativeHandle; # vndk
+ AHardwareBuffer_getNativeHandle; # llndk
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
AHardwareBuffer_lockAndGetInfo; # introduced=29
@@ -13,12 +13,12 @@
AHardwareBuffer_release;
AHardwareBuffer_sendHandleToUnixSocket;
AHardwareBuffer_unlock;
- ANativeWindowBuffer_getHardwareBuffer; # vndk
- ANativeWindow_OemStorageGet; # vndk
- ANativeWindow_OemStorageSet; # vndk
+ ANativeWindowBuffer_getHardwareBuffer; # llndk
+ ANativeWindow_OemStorageGet; # llndk
+ ANativeWindow_OemStorageSet; # llndk
ANativeWindow_acquire;
- ANativeWindow_cancelBuffer; # vndk
- ANativeWindow_dequeueBuffer; # vndk
+ ANativeWindow_cancelBuffer; # llndk
+ ANativeWindow_dequeueBuffer; # llndk
ANativeWindow_getBuffersDataSpace; # introduced=28
ANativeWindow_getFormat;
ANativeWindow_getHeight;
@@ -27,23 +27,23 @@
ANativeWindow_getLastQueueDuration; # apex # introduced=30
ANativeWindow_getWidth;
ANativeWindow_lock;
- ANativeWindow_query; # vndk
- ANativeWindow_queryf; # vndk
- ANativeWindow_queueBuffer; # vndk
+ ANativeWindow_query; # llndk
+ ANativeWindow_queryf; # llndk
+ ANativeWindow_queueBuffer; # llndk
ANativeWindow_release;
- ANativeWindow_setAutoPrerotation; # vndk
- ANativeWindow_setAutoRefresh; # vndk
- ANativeWindow_setBufferCount; # vndk
+ ANativeWindow_setAutoPrerotation; # llndk
+ ANativeWindow_setAutoRefresh; # llndk
+ ANativeWindow_setBufferCount; # llndk
ANativeWindow_setBuffersDataSpace; # introduced=28
- ANativeWindow_setBuffersDimensions; # vndk
- ANativeWindow_setBuffersFormat; # vndk
+ ANativeWindow_setBuffersDimensions; # llndk
+ ANativeWindow_setBuffersFormat; # llndk
ANativeWindow_setBuffersGeometry;
- ANativeWindow_setBuffersTimestamp; # vndk
+ ANativeWindow_setBuffersTimestamp; # llndk
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
- ANativeWindow_setSharedBufferMode; # vndk
- ANativeWindow_setSwapInterval; # vndk
- ANativeWindow_setUsage; # vndk
+ ANativeWindow_setSharedBufferMode; # llndk
+ ANativeWindow_setSwapInterval; # llndk
+ ANativeWindow_setUsage; # llndk
ANativeWindow_unlockAndPost;
local:
*;
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 940ff5a..e8154a6 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,22 +21,7 @@
"-Werror",
],
cppflags: [
- "-Weverything",
-
- // The static constructors and destructors in this library have not been noted to
- // introduce significant overheads
- "-Wno-exit-time-destructors",
- "-Wno-global-constructors",
-
- // We only care about compiling as C++14
- "-Wno-c++98-compat-pedantic",
-
- // android/sensors.h uses nested anonymous unions and anonymous structs
- "-Wno-nested-anon-types",
- "-Wno-gnu-anonymous-struct",
-
- // Don't warn about struct padding
- "-Wno-padded",
+ "-Wextra",
],
srcs: [
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 47137f1..afa6a2b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -26,32 +26,18 @@
"-Werror",
],
cppflags: [
- "-Weverything",
-
- // The static constructors and destructors in this library have not been noted to
- // introduce significant overheads
- "-Wno-exit-time-destructors",
- "-Wno-global-constructors",
-
- // We only care about compiling as C++14
- "-Wno-c++98-compat-pedantic",
-
- // We are aware of the risks inherent in comparing floats for equality
- "-Wno-float-equal",
-
- // We use four-character constants for the GraphicBuffer header, and don't care
- // that they're non-portable as long as they're consistent within one execution
- "-Wno-four-char-constants",
-
- // Don't warn about struct padding
- "-Wno-padded",
-
- "-Wno-switch-enum",
- "-Wno-format-pedantic",
+ "-Wextra",
],
sanitize: {
integer_overflow: true,
+ misc_undefined: ["bounds"],
+ diag: {
+ misc_undefined: ["bounds"],
+ no_recover: [
+ "bounds",
+ ],
+ },
},
srcs: [
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 9c600a8..fcc2547 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -144,7 +144,8 @@
// if stride has no meaning or is too large,
// approximate size with the input width instead
- if (std::numeric_limits<size_t>::max() / height / (*stride) < static_cast<size_t>(bpp)) {
+ if ((*stride) != 0 &&
+ std::numeric_limits<size_t>::max() / height / (*stride) < static_cast<size_t>(bpp)) {
bufSize = static_cast<size_t>(width) * height * bpp;
} else {
bufSize = static_cast<size_t>((*stride)) * height * bpp;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 5dc56c8..d7411ea 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,19 +16,12 @@
#pragma once
-#include <cinttypes>
-#include <cstdint>
-
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
namespace android {
-using PhysicalDisplayId = uint64_t;
-
// android::ui::* in this header file will alias different types as
// the HIDL interface is updated.
namespace ui {
diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/PhysicalDisplayId.h
new file mode 100644
index 0000000..1a345ac
--- /dev/null
+++ b/libs/ui/include/ui/PhysicalDisplayId.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <cinttypes>
+#include <cstdint>
+
+#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
+
+namespace android {
+
+using PhysicalDisplayId = uint64_t;
+
+constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
+ return static_cast<uint8_t>(displayId);
+}
+
+} // namespace android
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
new file mode 120000
index 0000000..6e3fb1e
--- /dev/null
+++ b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
@@ -0,0 +1 @@
+../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index 4bbc549..efca083 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -76,7 +76,21 @@
ASSERT_EQ(kTestWidth, stride);
}
-TEST_F(GraphicBufferAllocatorTest, AllocateBadStride) {
+TEST_F(GraphicBufferAllocatorTest, AllocateZeroStride) {
+ android::PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ uint32_t expectedStride = 0;
+
+ mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride);
+ uint32_t stride = 0;
+ buffer_handle_t handle;
+ // a divide by zero would cause a crash
+ status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage,
+ &handle, &stride, 0, "GraphicBufferAllocatorTest");
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_EQ(expectedStride, stride);
+}
+
+TEST_F(GraphicBufferAllocatorTest, AllocateLargeStride) {
uint32_t height = std::numeric_limits<uint32_t>::max();
uint32_t bpp = 4;
android::PixelFormat format = PIXEL_FORMAT_RGBA_8888;
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 6d202ae..2fcee7b 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -29,11 +29,9 @@
sharedLibraries = [
"libbase",
"libcutils",
- "libhardware",
"liblog",
"libui",
"libutils",
- "libnativewindow",
"libpdx_default_transport",
]
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 1176abf..1ce9c99 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -39,7 +39,6 @@
"libcutils",
"liblog",
"libutils",
- "libcrypto",
"libselinux",
],
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index d1b4a4e..1fc7927 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -61,7 +61,7 @@
using nsecs_t = int64_t;
-struct extention_map_t {
+struct extension_map_t {
const char* name;
__eglMustCastToProperFunctionPointerType address;
};
@@ -154,7 +154,7 @@
* (keep in sync with gExtensionString above)
*
*/
-static const extention_map_t sExtensionMap[] = {
+static const extension_map_t sExtensionMap[] = {
// EGL_KHR_lock_surface
{ "eglLockSurfaceKHR",
(__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
@@ -257,13 +257,14 @@
!strcmp((procname), "eglAwakenProcessIMG"))
// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
+static std::unordered_map<std::string, int> sGLExtensionSlotMap;
-static int sGLExtentionSlot = 0;
+static int sGLExtensionSlot = 0;
static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
static void(*findProcAddress(const char* name,
- const extention_map_t* map, size_t n))() {
+ const extension_map_t* map, size_t n))() {
for (uint32_t i=0 ; i<n ; i++) {
if (!strcmp(name, map[i].name)) {
return map[i].address;
@@ -1223,7 +1224,7 @@
addr = findBuiltinWrapper(procname);
if (addr) return addr;
- // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+ // this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap
pthread_mutex_lock(&sExtensionMapMutex);
/*
@@ -1244,51 +1245,69 @@
*/
const std::string name(procname);
-
- auto& extentionMap = sGLExtentionMap;
- auto pos = extentionMap.find(name);
- addr = (pos != extentionMap.end()) ? pos->second : nullptr;
- const int slot = sGLExtentionSlot;
-
- ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
- "no more slots for eglGetProcAddress(\"%s\")",
- procname);
-
+ auto& extensionMap = sGLExtensionMap;
+ auto& extensionSlotMap = sGLExtensionSlotMap;
egl_connection_t* const cnx = &gEGLImpl;
LayerLoader& layer_loader(LayerLoader::getInstance());
- if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+ // See if we've already looked up this extension
+ auto pos = extensionMap.find(name);
+ addr = (pos != extensionMap.end()) ? pos->second : nullptr;
- if (cnx->dso && cnx->egl.eglGetProcAddress) {
+ if (!addr) {
+ // This is the first time we've looked this function up
+ // Ensure we have room to track it
+ const int slot = sGLExtensionSlot;
+ if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
- // Extensions are independent of the bound context
- addr = cnx->egl.eglGetProcAddress(procname);
- if (addr) {
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
- // purposefully track the bottom of the stack in extensionMap
- extentionMap[name] = addr;
+ // Extensions are independent of the bound context
+ addr = cnx->egl.eglGetProcAddress(procname);
+ if (addr) {
- // Apply layers
- addr = layer_loader.ApplyLayers(procname, addr);
+ // purposefully track the bottom of the stack in extensionMap
+ extensionMap[name] = addr;
- // Track the top most entry point
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
- addr = gExtensionForwarders[slot];
- sGLExtentionSlot++;
+ // Apply layers
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point return the extension forwarder
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+
+ // Remember the slot for this extension
+ extensionSlotMap[name] = slot;
+
+ // Increment the global extension index
+ sGLExtensionSlot++;
+ }
}
+ } else {
+ // The extension forwarder has a fixed number of slots
+ ALOGE("no more slots for eglGetProcAddress(\"%s\")", procname);
}
- } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+ } else {
+ // We tracked an address, so we've seen this func before
+ // Look up the slot for this extension
+ auto slot_pos = extensionSlotMap.find(name);
+ int ext_slot = (slot_pos != extensionSlotMap.end()) ? slot_pos->second : -1;
+ if (ext_slot < 0) {
+ // Something has gone wrong, this should not happen
+ ALOGE("No extension slot found for %s", procname);
+ return nullptr;
+ }
- // We've seen this func before, but we tracked the bottom, so re-apply layers
- // More layers might have been enabled
+ // We tracked the bottom of the stack, so re-apply layers since
+ // more layers might have been enabled
addr = layer_loader.ApplyLayers(procname, addr);
- // Track the top most entry point
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
- addr = gExtensionForwarders[slot];
+ // Track the top most entry point and return the extension forwarder
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+ addr = gExtensionForwarders[ext_slot];
}
pthread_mutex_unlock(&sExtensionMapMutex);
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
index b2d7957..0c14e01 100644
--- a/opengl/libs/libEGL.map.txt
+++ b/opengl/libs/libEGL.map.txt
@@ -28,7 +28,7 @@
eglDestroySurface;
eglDestroySync; # introduced=29
eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
- eglDupNativeFenceFDANDROID; # vndk
+ eglDupNativeFenceFDANDROID; # llndk
eglGetConfigAttrib;
eglGetConfigs;
eglGetCurrentContext;
@@ -54,7 +54,7 @@
eglQueryStreamTimeKHR; # introduced=23
eglQueryStreamu64KHR; # introduced=23
eglQueryString;
- eglQueryStringImplementationANDROID; # vndk
+ eglQueryStringImplementationANDROID; # llndk
eglQuerySurface;
eglReleaseTexImage;
eglReleaseThread;
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e7640dd..1043390 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,7 +46,6 @@
}
void InputManager::initialize() {
- mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
@@ -57,9 +56,9 @@
return result;
}
- result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ result = mReader->start();
if (result) {
- ALOGE("Could not start InputReader thread due to error %d.", result);
+ ALOGE("Could not start InputReader due to error %d.", result);
mDispatcherThread->requestExit();
return result;
@@ -69,9 +68,9 @@
}
status_t InputManager::stop() {
- status_t result = mReaderThread->requestExitAndWait();
+ status_t result = mReader->stop();
if (result) {
- ALOGW("Could not stop InputReader thread due to error %d.", result);
+ ALOGW("Could not stop InputReader due to error %d.", result);
}
result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 40f66d8..2a7ed0f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager uses two threads.
+ * The input manager has two components.
*
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- * applies policy, and posts messages to a queue managed by the DispatcherThread.
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ * policy, and posts messages to a queue managed by the InputDispatcherThread.
* 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
* queue and asynchronously dispatches them to applications.
*
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state. Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcherThread class do not share any
+ * internal state. Moreover, all communication is done one way from the InputReader
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
* InputDispatchPolicy, however.
*
@@ -102,7 +102,6 @@
private:
sp<InputReaderInterface> mReader;
- sp<InputReaderThread> mReaderThread;
sp<InputClassifierInterface> mClassifier;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0422d83..2d6f2c1 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,20 +33,6 @@
namespace android {
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
-}
-
// --- InputReaderConfiguration ---
std::string InputReaderConfiguration::changesToString(uint32_t changes) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d8cf8dd..58a5b3c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "InputDispatcher"
#define ATRACE_TAG ATRACE_TAG_INPUT
-#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
// Log detailed debug messages about each inbound event notification to the dispatcher.
#define DEBUG_INBOUND_EVENT_DETAILS 0
@@ -363,7 +363,7 @@
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
- pokeUserActivityLocked(mPendingEvent);
+ pokeUserActivityLocked(*mPendingEvent);
}
// Get ready to dispatch the event.
@@ -404,14 +404,14 @@
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(typedEntry)) {
+ if (isAppSwitchKeyEvent(*typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
@@ -426,7 +426,7 @@
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
@@ -443,7 +443,7 @@
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
- dropInboundEventLocked(mPendingEvent, dropReason);
+ dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
@@ -462,16 +462,16 @@
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
if (isAppSwitchKeyEvent(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+ if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
- } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+ mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
@@ -591,7 +591,7 @@
}
}
-void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
+void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
@@ -626,15 +626,15 @@
}
}
- switch (entry->type) {
+ switch (entry.type) {
case EventEntry::TYPE_KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
@@ -651,10 +651,10 @@
keyCode == AKEYCODE_APP_SWITCH;
}
-bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
- return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) &&
- (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) &&
- (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) {
+ return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) &&
+ (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) &&
+ (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
}
bool InputDispatcher::isAppSwitchPendingLocked() {
@@ -673,8 +673,8 @@
#endif
}
-bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) {
- return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+ return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
bool InputDispatcher::haveCommandsLocked() const {
@@ -837,7 +837,7 @@
entry->dispatchInProgress = true;
- logOutboundKeyDetails("dispatchKey - ", entry);
+ logOutboundKeyDetails("dispatchKey - ", *entry);
}
// Handle case where the policy asked us to try again later last time.
@@ -858,7 +858,7 @@
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
+ getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
if (focusedWindowHandle != nullptr) {
commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
}
@@ -887,7 +887,7 @@
// Identify targets.
std::vector<InputTarget> inputTargets;
int32_t injectionResult =
- findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+ findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
@@ -898,21 +898,21 @@
}
// Add monitor channels from event's or focused display.
- addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+ addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
-void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
"policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
"metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
- prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
- entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount, entry->downTime);
+ prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
+ entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
+ entry.repeatCount, entry.downTime);
#endif
}
@@ -923,7 +923,7 @@
if (!entry->dispatchInProgress) {
entry->dispatchInProgress = true;
- logOutboundMotionDetails("dispatchMotion - ", entry);
+ logOutboundMotionDetails("dispatchMotion - ", *entry);
}
// Clean up if dropping the event.
@@ -944,12 +944,12 @@
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult =
- findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime,
+ findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
&conflictingPointerActions);
} else {
// Non touch event. (eg. trackball)
injectionResult =
- findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+ findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
@@ -968,7 +968,7 @@
}
// Add monitor channels from event's or focused display.
- addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+ addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
if (isPointerEvent) {
ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
@@ -996,33 +996,32 @@
return true;
}
-void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
+void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
", policyFlags=0x%x, "
"action=0x%x, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
- entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState,
- entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
- entry->downTime);
+ prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
+ entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState,
+ entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime);
- for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
"x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
"orientation=%f",
- i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType,
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
#endif
}
@@ -1036,10 +1035,11 @@
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
- pokeUserActivityLocked(eventEntry);
+ pokeUserActivityLocked(*eventEntry);
for (const InputTarget& inputTarget : inputTargets) {
- sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel);
+ sp<Connection> connection =
+ getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
} else {
@@ -1053,7 +1053,7 @@
}
int32_t InputDispatcher::handleTargetsNotReadyLocked(
- nsecs_t currentTime, const EventEntry* entry,
+ nsecs_t currentTime, const EventEntry& entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
if (applicationHandle == nullptr && windowHandle == nullptr) {
@@ -1103,7 +1103,7 @@
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
- onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime,
+ onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
@@ -1127,7 +1127,7 @@
}
void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
- nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {
+ nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
if (newTimeout > 0) {
// Extend the timeout.
mInputTargetWaitTimeoutTime = now() + newTimeout;
@@ -1136,13 +1136,9 @@
mInputTargetWaitTimeoutExpired = true;
// Input state will not be realistic. Mark it out of sync.
- sp<Connection> connection = getConnectionLocked(inputChannel);
+ sp<Connection> connection = getConnectionLocked(inputConnectionToken);
if (connection != nullptr) {
- sp<IBinder> token = connection->inputChannel->getToken();
-
- if (token != nullptr) {
- removeWindowByTokenLocked(token);
- }
+ removeWindowByTokenLocked(inputConnectionToken);
if (connection->status == Connection::STATUS_NORMAL) {
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
@@ -1175,21 +1171,21 @@
* then it should be dispatched to that display. Otherwise, the event goes to the focused display.
* Focused display is the display that the user most recently interacted with.
*/
-int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
+int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
int32_t displayId;
- switch (entry->type) {
+ switch (entry.type) {
case EventEntry::TYPE_KEY: {
- const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
- displayId = typedEntry->displayId;
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ displayId = keyEntry.displayId;
break;
}
case EventEntry::TYPE_MOTION: {
- const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
- displayId = typedEntry->displayId;
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+ displayId = motionEntry.displayId;
break;
}
default: {
- ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+ ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry.type);
return ADISPLAY_ID_NONE;
}
}
@@ -1197,7 +1193,7 @@
}
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry* entry,
+ const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
int32_t injectionResult;
@@ -1230,7 +1226,7 @@
}
// Check permissions.
- if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) {
+ if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
@@ -1264,7 +1260,7 @@
}
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry* entry,
+ const MotionEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
@@ -1277,8 +1273,8 @@
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
- int32_t displayId = entry->displayId;
- int32_t action = entry->action;
+ int32_t displayId = entry.displayId;
+ int32_t action = entry.action;
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
@@ -1298,14 +1294,14 @@
bool isSplit = mTempTouchState.split;
bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
- (mTempTouchState.deviceId != entry->deviceId ||
- mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId);
+ (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
+ mTempTouchState.displayId != displayId);
bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
- const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE;
+ const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE;
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
@@ -1323,8 +1319,8 @@
}
mTempTouchState.reset();
mTempTouchState.down = down;
- mTempTouchState.deviceId = entry->deviceId;
- mTempTouchState.source = entry->source;
+ mTempTouchState.deviceId = entry.deviceId;
+ mTempTouchState.source = entry.source;
mTempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
@@ -1348,11 +1344,11 @@
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
- x = int32_t(entry->xCursorPosition);
- y = int32_t(entry->yCursorPosition);
+ x = int32_t(entry.xCursorPosition);
+ y = int32_t(entry.yCursorPosition);
} else {
- x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
- y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
+ y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
sp<InputWindowHandle> newTouchedWindowHandle =
@@ -1410,7 +1406,7 @@
// Update the temporary touch state.
BitSet32 pointerIds;
if (isSplit) {
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
pointerIds.markBit(pointerId);
}
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
@@ -1432,10 +1428,10 @@
}
// Check whether touches should slip outside of the current foreground window.
- if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 &&
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
mTempTouchState.isSlippery()) {
- int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> oldTouchedWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
@@ -1469,7 +1465,7 @@
BitSet32 pointerIds;
if (isSplit) {
- pointerIds.markBit(entry->pointerProperties[0].id);
+ pointerIds.markBit(entry.pointerProperties[0].id);
}
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
@@ -1507,7 +1503,7 @@
for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
- if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) {
+ if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
@@ -1613,7 +1609,7 @@
Failed:
// Check injection permission once and for all.
if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(nullptr, entry->injectionState)) {
+ if (checkInjectionPermission(nullptr, entry.injectionState)) {
injectionPermission = INJECTION_PERMISSION_GRANTED;
} else {
injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1642,8 +1638,8 @@
mTempTouchState.reset();
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mTempTouchState.deviceId = entry->deviceId;
- mTempTouchState.source = entry->source;
+ mTempTouchState.deviceId = entry.deviceId;
+ mTempTouchState.source = entry.source;
mTempTouchState.displayId = displayId;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
@@ -1662,7 +1658,7 @@
// One pointer went up.
if (isSplit) {
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (size_t i = 0; i < mTempTouchState.windows.size();) {
TouchedWindow& touchedWindow = mTempTouchState.windows[i];
@@ -1822,15 +1818,14 @@
std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
- const EventEntry* eventEntry, const char* targetType) {
+ const EventEntry& eventEntry, const char* targetType) {
// If the window is paused then keep waiting.
if (windowHandle->getInfo()->paused) {
return StringPrintf("Waiting because the %s window is paused.", targetType);
}
// If the window's connection is not registered then keep waiting.
- sp<Connection> connection =
- getConnectionLocked(getInputChannelLocked(windowHandle->getToken()));
+ sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
if (connection == nullptr) {
return StringPrintf("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the "
@@ -1854,7 +1849,7 @@
}
// Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry->type == EventEntry::TYPE_KEY) {
+ if (eventEntry.type == EventEntry::TYPE_KEY) {
// If the event is a key event, then we must wait for all previous events to
// complete before delivering it because previous events may have the
// side-effect of transferring focus to a different window and we want to
@@ -1926,7 +1921,7 @@
}
}
-void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
+void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
int32_t displayId = getTargetDisplayId(eventEntry);
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
@@ -1941,21 +1936,21 @@
}
int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
- switch (eventEntry->type) {
+ switch (eventEntry.type) {
case EventEntry::TYPE_MOTION: {
- const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
return;
}
- if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
+ if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
eventType = USER_ACTIVITY_EVENT_TOUCH;
}
break;
}
case EventEntry::TYPE_KEY: {
- const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
- if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
}
eventType = USER_ACTIVITY_EVENT_BUTTON;
@@ -1965,7 +1960,7 @@
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
- commandEntry->eventTime = eventEntry->eventTime;
+ commandEntry->eventTime = eventEntry.eventTime;
commandEntry->userActivityEventType = eventType;
postCommandLocked(std::move(commandEntry));
}
@@ -2003,8 +1998,8 @@
if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
- MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
- if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+ const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
MotionEntry* splitMotionEntry =
splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
if (!splitMotionEntry) {
@@ -2013,7 +2008,7 @@
if (DEBUG_FOCUS) {
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
- logOutboundMotionDetails(" ", splitMotionEntry);
+ logOutboundMotionDetails(" ", *splitMotionEntry);
}
enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
splitMotionEntry->release();
@@ -2086,9 +2081,9 @@
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- dispatchEntry->resolvedAction = keyEntry->action;
- dispatchEntry->resolvedFlags = keyEntry->flags;
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ dispatchEntry->resolvedAction = keyEntry.action;
+ dispatchEntry->resolvedFlags = keyEntry.flags;
if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
@@ -2103,7 +2098,7 @@
}
case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2115,11 +2110,11 @@
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
- dispatchEntry->resolvedAction = motionEntry->action;
+ dispatchEntry->resolvedAction = motionEntry.action;
}
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
- !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source,
- motionEntry->displayId)) {
+ !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
"event",
@@ -2128,7 +2123,7 @@
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
- dispatchEntry->resolvedFlags = motionEntry->flags;
+ dispatchEntry->resolvedFlags = motionEntry.flags;
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
@@ -2147,8 +2142,8 @@
return; // skip the inconsistent event
}
- dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction,
- inputTarget->inputChannel->getToken());
+ dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
+ inputTarget->inputChannel->getConnectionToken());
break;
}
@@ -2478,7 +2473,7 @@
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
const sp<InputChannel>& channel, const CancelationOptions& options) {
- sp<Connection> connection = getConnectionLocked(channel);
+ sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
if (connection == nullptr) {
return;
}
@@ -2494,8 +2489,8 @@
nsecs_t currentTime = now();
- std::vector<EventEntry*> cancelationEvents;
- connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options);
+ std::vector<EventEntry*> cancelationEvents =
+ connection->inputState.synthesizeCancelationEvents(currentTime, options);
if (!cancelationEvents.empty()) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
@@ -2509,17 +2504,18 @@
switch (cancelationEventEntry->type) {
case EventEntry::TYPE_KEY:
logOutboundKeyDetails("cancel - ",
- static_cast<KeyEntry*>(cancelationEventEntry));
+ static_cast<const KeyEntry&>(*cancelationEventEntry));
break;
case EventEntry::TYPE_MOTION:
logOutboundMotionDetails("cancel - ",
- static_cast<MotionEntry*>(cancelationEventEntry));
+ static_cast<const MotionEntry&>(
+ *cancelationEventEntry));
break;
}
InputTarget target;
sp<InputWindowHandle> windowHandle =
- getWindowHandleLocked(connection->inputChannel->getToken());
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.xOffset = -windowInfo->frameLeft;
@@ -2545,7 +2541,7 @@
}
}
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry,
+MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
@@ -2553,19 +2549,19 @@
PointerProperties splitPointerProperties[MAX_POINTERS];
PointerCoords splitPointerCoords[MAX_POINTERS];
- uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+ uint32_t originalPointerCount = originalMotionEntry.pointerCount;
uint32_t splitPointerCount = 0;
for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
originalPointerIndex++) {
const PointerProperties& pointerProperties =
- originalMotionEntry->pointerProperties[originalPointerIndex];
+ originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
if (pointerIds.hasBit(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
splitPointerCoords[splitPointerCount].copyFrom(
- originalMotionEntry->pointerCoords[originalPointerIndex]);
+ originalMotionEntry.pointerCoords[originalPointerIndex]);
splitPointerCount += 1;
}
}
@@ -2583,13 +2579,13 @@
return nullptr;
}
- int32_t action = originalMotionEntry->action;
+ int32_t action = originalMotionEntry.action;
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
const PointerProperties& pointerProperties =
- originalMotionEntry->pointerProperties[originalPointerIndex];
+ originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
if (pointerIds.hasBit(pointerId)) {
if (pointerIds.count() == 1) {
@@ -2613,19 +2609,19 @@
}
MotionEntry* splitMotionEntry =
- new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime,
- originalMotionEntry->deviceId, originalMotionEntry->source,
- originalMotionEntry->displayId, originalMotionEntry->policyFlags,
- action, originalMotionEntry->actionButton, originalMotionEntry->flags,
- originalMotionEntry->metaState, originalMotionEntry->buttonState,
- originalMotionEntry->classification, originalMotionEntry->edgeFlags,
- originalMotionEntry->xPrecision, originalMotionEntry->yPrecision,
- originalMotionEntry->xCursorPosition,
- originalMotionEntry->yCursorPosition, originalMotionEntry->downTime,
+ new MotionEntry(originalMotionEntry.sequenceNum, originalMotionEntry.eventTime,
+ originalMotionEntry.deviceId, originalMotionEntry.source,
+ originalMotionEntry.displayId, originalMotionEntry.policyFlags, action,
+ originalMotionEntry.actionButton, originalMotionEntry.flags,
+ originalMotionEntry.metaState, originalMotionEntry.buttonState,
+ originalMotionEntry.classification, originalMotionEntry.edgeFlags,
+ originalMotionEntry.xPrecision, originalMotionEntry.yPrecision,
+ originalMotionEntry.xCursorPosition,
+ originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
- if (originalMotionEntry->injectionState) {
- splitMotionEntry->injectionState = originalMotionEntry->injectionState;
+ if (originalMotionEntry.injectionState) {
+ splitMotionEntry->injectionState = originalMotionEntry.injectionState;
splitMotionEntry->injectionState->refCount += 1;
}
@@ -3222,7 +3218,7 @@
!(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
!(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
if (canReceiveInput && !noInputChannel) {
- ALOGE("Window handle %s has no registered input channel",
+ ALOGV("Window handle %s has no registered input channel",
handle->getName().c_str());
}
continue;
@@ -3571,10 +3567,8 @@
return false;
}
- sp<InputChannel> fromChannel = getInputChannelLocked(fromToken);
- sp<InputChannel> toChannel = getInputChannelLocked(toToken);
- sp<Connection> fromConnection = getConnectionLocked(fromChannel);
- sp<Connection> toConnection = getConnectionLocked(toChannel);
+ sp<Connection> fromConnection = getConnectionLocked(fromToken);
+ sp<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
CancelationOptions
@@ -3873,7 +3867,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> existingConnection = getConnectionLocked(inputChannel);
+ sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
if (existingConnection != nullptr) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().c_str());
@@ -3884,7 +3878,7 @@
int fd = inputChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+ mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
@@ -3904,7 +3898,7 @@
return BAD_VALUE;
}
- if (inputChannel->getToken() == nullptr) {
+ if (inputChannel->getConnectionToken() == nullptr) {
ALOGW("Attempted to register input monitor without an identifying token.");
return BAD_VALUE;
}
@@ -3913,7 +3907,7 @@
const int fd = inputChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+ mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
@@ -3948,16 +3942,16 @@
status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel);
+ sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
if (connection == nullptr) {
ALOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().c_str());
return BAD_VALUE;
}
- const bool removed = removeByValue(mConnectionsByFd, connection);
+ [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
ALOG_ASSERT(removed);
- mInputChannelsByToken.erase(inputChannel->getToken());
+ mInputChannelsByToken.erase(inputChannel->getConnectionToken());
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
@@ -4017,7 +4011,7 @@
TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
std::optional<int32_t> foundDeviceId;
for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
- if (touchedMonitor.monitor.inputChannel->getToken() == token) {
+ if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
foundDeviceId = state.deviceId;
}
}
@@ -4048,7 +4042,7 @@
for (const auto& it : mGestureMonitorsByDisplay) {
const std::vector<Monitor>& monitors = it.second;
for (const Monitor& monitor : monitors) {
- if (monitor.inputChannel->getToken() == token) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
return it.first;
}
}
@@ -4056,14 +4050,14 @@
return std::nullopt;
}
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<InputChannel>& inputChannel) {
- if (inputChannel == nullptr) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+ if (inputConnectionToken == nullptr) {
return nullptr;
}
for (const auto& pair : mConnectionsByFd) {
- sp<Connection> connection = pair.second;
- if (connection->inputChannel->getToken() == inputChannel->getToken()) {
+ const sp<Connection>& connection = pair.second;
+ if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
return connection;
}
}
@@ -4156,7 +4150,7 @@
if (connection->status != Connection::STATUS_ZOMBIE) {
mLock.unlock();
- mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken());
+ mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
mLock.lock();
}
@@ -4171,31 +4165,29 @@
}
void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
+ sp<IBinder> token =
+ commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
nsecs_t newTimeout =
- mPolicy->notifyANR(commandEntry->inputApplicationHandle,
- commandEntry->inputChannel ? commandEntry->inputChannel->getToken()
- : nullptr,
- commandEntry->reason);
+ mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason);
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
- KeyEvent event;
- initializeKeyEvent(&event, entry);
+ KeyEvent event = createKeyEvent(*entry);
mLock.unlock();
android::base::Timer t;
sp<IBinder> token = commandEntry->inputChannel != nullptr
- ? commandEntry->inputChannel->getToken()
+ ? commandEntry->inputChannel->getConnectionToken()
: nullptr;
nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4309,13 +4301,12 @@
keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
keyEntry->policyFlags);
#endif
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
+ KeyEvent event = createKeyEvent(*keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
mLock.unlock();
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+ mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
keyEntry->policyFlags, &event);
mLock.lock();
@@ -4352,13 +4343,13 @@
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
#endif
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
+ KeyEvent event = createKeyEvent(*keyEntry);
mLock.unlock();
- bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
- keyEntry->policyFlags, &event);
+ bool fallback =
+ mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+ &event, keyEntry->policyFlags, &event);
mLock.lock();
@@ -4470,13 +4461,15 @@
mLock.lock();
}
-void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
- event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
- entry->downTime, entry->eventTime);
+KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
+ KeyEvent event;
+ event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags,
+ entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
+ entry.downTime, entry.eventTime);
+ return event;
}
-void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
int32_t injectionResult,
nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9d22e2c..d21b0a1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -144,7 +144,7 @@
bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
// Cleans up input state when dropping an inbound event.
- void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
+ void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
@@ -153,12 +153,12 @@
bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
- bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+ bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
bool isAppSwitchPendingLocked() REQUIRES(mLock);
void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
// Stale event latency optimization.
- static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
+ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
@@ -183,7 +183,7 @@
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
- sp<Connection> getConnectionLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) 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<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -293,8 +293,8 @@
void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
- void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
+ void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
+ void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
// Keeping track of ANR timeouts.
enum InputTargetWaitCause {
@@ -313,7 +313,7 @@
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
// Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason)
@@ -322,16 +322,16 @@
void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel)
+ const sp<IBinder>& inputConnectionToken)
REQUIRES(mLock);
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
void resetANRTimeoutsLocked() REQUIRES(mLock);
- int32_t getTargetDisplayId(const EventEntry* entry);
- int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t getTargetDisplayId(const EventEntry& entry);
+ int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) REQUIRES(mLock);
- int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+ int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) REQUIRES(mLock);
@@ -350,7 +350,7 @@
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
- void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
+ void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
@@ -361,7 +361,7 @@
std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle,
- const EventEntry* eventEntry,
+ const EventEntry& eventEntry,
const char* targetType) REQUIRES(mLock);
// Manage the dispatch cycle for a single connection.
@@ -405,7 +405,7 @@
REQUIRES(mLock);
// Splitting motion events across windows.
- MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+ MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -450,7 +450,7 @@
DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+ KeyEvent createKeyEvent(const KeyEntry& entry);
void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
// Statistics gathering.
@@ -458,7 +458,7 @@
LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
void reportTouchEventForStatistics(const MotionEntry& entry);
- void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+ void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
void traceInboundQueueLengthLocked() REQUIRES(mLock);
void traceOutboundQueueLength(const sp<Connection>& connection);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index c60700e..c43e304 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -36,12 +36,12 @@
return false;
}
-bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
switch (action) {
case AKEY_EVENT_ACTION_UP: {
- if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+ if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
for (size_t i = 0; i < mFallbackKeys.size();) {
- if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+ if (mFallbackKeys.valueAt(i) == entry.keyCode) {
mFallbackKeys.removeItemsAt(i);
} else {
i += 1;
@@ -65,7 +65,7 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
"keyCode=%d, scanCode=%d",
- entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+ entry.deviceId, entry.source, entry.keyCode, entry.scanCode);
#endif
return false;
*/
@@ -86,7 +86,7 @@
}
}
-bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
@@ -99,7 +99,7 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
"displayId=%" PRId32 ", actionMasked=%d",
- entry->deviceId, entry->source, entry->displayId, actionMasked);
+ entry.deviceId, entry.source, entry.displayId, actionMasked);
#endif
return false;
}
@@ -116,7 +116,7 @@
case AMOTION_EVENT_ACTION_POINTER_UP:
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_MOVE: {
- if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+ if (entry.source & AINPUT_SOURCE_CLASS_NAVIGATION) {
// Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
// to generate cancellation events for these since they're based in relative rather
// than absolute units.
@@ -125,20 +125,20 @@
ssize_t index = findMotionMemento(entry, false /*hovering*/);
- if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+ if (entry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
// Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
// joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
// Any other value and we need to track the motion so we can send cancellation
// events for anything generating fallback events (e.g. DPad keys for joystick
// movements).
if (index >= 0) {
- if (entry->pointerCoords[0].isEmpty()) {
+ if (entry.pointerCoords[0].isEmpty()) {
mMotionMementos.erase(mMotionMementos.begin() + index);
} else {
MotionMemento& memento = mMotionMementos[index];
memento.setPointers(entry);
}
- } else if (!entry->pointerCoords[0].isEmpty()) {
+ } else if (!entry.pointerCoords[0].isEmpty()) {
addMotionMemento(entry, flags, false /*hovering*/);
}
@@ -153,7 +153,7 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion pointer up/down or move event: "
"deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
- entry->deviceId, entry->source, entry->displayId, actionMasked);
+ entry.deviceId, entry.source, entry.displayId, actionMasked);
#endif
return false;
}
@@ -167,7 +167,7 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
"displayId=%" PRId32,
- entry->deviceId, entry->source, entry->displayId);
+ entry.deviceId, entry.source, entry.displayId);
#endif
return false;
}
@@ -187,79 +187,79 @@
}
}
-ssize_t InputState::findKeyMemento(const KeyEntry* entry) const {
+ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos[i];
- if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
- memento.displayId == entry->displayId && memento.keyCode == entry->keyCode &&
- memento.scanCode == entry->scanCode) {
+ if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+ memento.displayId == entry.displayId && memento.keyCode == entry.keyCode &&
+ memento.scanCode == entry.scanCode) {
return i;
}
}
return -1;
}
-ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const {
+ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
for (size_t i = 0; i < mMotionMementos.size(); i++) {
const MotionMemento& memento = mMotionMementos[i];
- if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
- memento.displayId == entry->displayId && memento.hovering == hovering) {
+ if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+ memento.displayId == entry.displayId && memento.hovering == hovering) {
return i;
}
}
return -1;
}
-void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+void InputState::addKeyMemento(const KeyEntry& entry, int32_t flags) {
KeyMemento memento;
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.displayId = entry->displayId;
- memento.keyCode = entry->keyCode;
- memento.scanCode = entry->scanCode;
- memento.metaState = entry->metaState;
+ memento.deviceId = entry.deviceId;
+ memento.source = entry.source;
+ memento.displayId = entry.displayId;
+ memento.keyCode = entry.keyCode;
+ memento.scanCode = entry.scanCode;
+ memento.metaState = entry.metaState;
memento.flags = flags;
- memento.downTime = entry->downTime;
- memento.policyFlags = entry->policyFlags;
+ memento.downTime = entry.downTime;
+ memento.policyFlags = entry.policyFlags;
mKeyMementos.push_back(memento);
}
-void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) {
+void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering) {
MotionMemento memento;
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.displayId = entry->displayId;
+ memento.deviceId = entry.deviceId;
+ memento.source = entry.source;
+ memento.displayId = entry.displayId;
memento.flags = flags;
- memento.xPrecision = entry->xPrecision;
- memento.yPrecision = entry->yPrecision;
- memento.xCursorPosition = entry->xCursorPosition;
- memento.yCursorPosition = entry->yCursorPosition;
- memento.downTime = entry->downTime;
+ memento.xPrecision = entry.xPrecision;
+ memento.yPrecision = entry.yPrecision;
+ memento.xCursorPosition = entry.xCursorPosition;
+ memento.yCursorPosition = entry.yCursorPosition;
+ memento.downTime = entry.downTime;
memento.setPointers(entry);
memento.hovering = hovering;
- memento.policyFlags = entry->policyFlags;
+ memento.policyFlags = entry.policyFlags;
mMotionMementos.push_back(memento);
}
-void InputState::MotionMemento::setPointers(const MotionEntry* entry) {
- pointerCount = entry->pointerCount;
- for (uint32_t i = 0; i < entry->pointerCount; i++) {
- pointerProperties[i].copyFrom(entry->pointerProperties[i]);
- pointerCoords[i].copyFrom(entry->pointerCoords[i]);
+void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
+ pointerCount = entry.pointerCount;
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ pointerProperties[i].copyFrom(entry.pointerProperties[i]);
+ pointerCoords[i].copyFrom(entry.pointerCoords[i]);
}
}
-void InputState::synthesizeCancelationEvents(nsecs_t currentTime,
- std::vector<EventEntry*>& outEvents,
- const CancelationOptions& options) {
+std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+ nsecs_t currentTime, const CancelationOptions& options) {
+ std::vector<EventEntry*> events;
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
- outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
- memento.deviceId, memento.source, memento.displayId,
- memento.policyFlags, AKEY_EVENT_ACTION_UP,
- memento.flags | AKEY_EVENT_FLAG_CANCELED,
- memento.keyCode, memento.scanCode, memento.metaState,
- 0, memento.downTime));
+ events.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId,
+ memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
+ memento.scanCode, memento.metaState, 0 /*repeatCount*/,
+ memento.downTime));
}
}
@@ -267,18 +267,19 @@
if (shouldCancelMotion(memento, options)) {
const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL;
- outEvents.push_back(
- new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags, action,
- 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime, memento.pointerCount,
- memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
- 0 /*yOffset*/));
+ events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId,
+ memento.policyFlags, action, 0 /*actionButton*/,
+ memento.flags, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ memento.pointerCount, memento.pointerProperties,
+ memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
}
}
+ return events;
}
void InputState::clear() {
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 47e9b36..a93f486 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -41,16 +41,16 @@
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
+ bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags);
// Records tracking information for a motion event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
+ bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
// Synthesizes cancelation events for the current state and resets the tracked state.
- void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents,
- const CancelationOptions& options);
+ std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
+ const CancelationOptions& options);
// Clears the current state.
void clear();
@@ -100,18 +100,18 @@
bool hovering;
uint32_t policyFlags;
- void setPointers(const MotionEntry* entry);
+ void setPointers(const MotionEntry& entry);
};
std::vector<KeyMemento> mKeyMementos;
std::vector<MotionMemento> mMotionMementos;
KeyedVector<int32_t, int32_t> mFallbackKeys;
- ssize_t findKeyMemento(const KeyEntry* entry) const;
- ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+ ssize_t findKeyMemento(const KeyEntry& entry) const;
+ ssize_t findMotionMemento(const MotionEntry& entry, bool hovering) const;
- void addKeyMemento(const KeyEntry* entry, int32_t flags);
- void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+ void addKeyMemento(const KeyEntry& entry, int32_t flags);
+ void addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering);
static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5d576b9..56c0a73 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
#include "PointerControllerInterface.h"
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <stddef.h>
@@ -44,7 +44,16 @@
namespace android {
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
class InputReaderInterface : public virtual RefBase {
protected:
InputReaderInterface() { }
@@ -56,18 +65,17 @@
* This method may be called on any thread (usually by the input manager). */
virtual void dump(std::string& dump) = 0;
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+ /* Called by the heartbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
/* Returns true if the input device is enabled. */
virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
- /* Runs a single iteration of the processing loop.
- * Nominally reads and processes one incoming message from the EventHub.
- *
- * This method should be called on the input reader thread.
- */
- virtual void loopOnce() = 0;
+ /* Makes the reader start processing events from the kernel. */
+ virtual status_t start() = 0;
+
+ /* Makes the reader stop processing any more events. */
+ virtual status_t stop() = 0;
/* Gets information about all input devices.
*
@@ -104,17 +112,7 @@
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
};
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(const sp<InputReaderInterface>& reader);
- virtual ~InputReaderThread();
-
-private:
- sp<InputReaderInterface> mReader;
-
- virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
/*
* Input reader configuration.
@@ -285,6 +283,8 @@
std::vector<DisplayViewport> mDisplays;
};
+// --- TouchAffineTransformation ---
+
struct TouchAffineTransformation {
float x_scale;
float x_ymix;
@@ -307,6 +307,8 @@
void applyTo(float& x, float& y) const;
};
+// --- InputReaderPolicyInterface ---
+
/*
* Input reader policy interface.
*
@@ -316,8 +318,8 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
*/
class InputReaderPolicyInterface : public virtual RefBase {
protected:
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e57604c..05f0db1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,16 +38,38 @@
#include <unistd.h>
#include <log/log.h>
+#include <utils/Errors.h>
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
-
+#include <utils/Thread.h>
using android::base::StringPrintf;
namespace android {
+// --- InputReader::InputReaderThread ---
+
+/* Thread that reads raw events from the event hub and processes them, endlessly. */
+class InputReader::InputReaderThread : public Thread {
+public:
+ explicit InputReaderThread(InputReader* reader)
+ : Thread(/* canCallJava */ true), mReader(reader) {}
+
+ ~InputReaderThread() {}
+
+private:
+ InputReader* mReader;
+
+ bool threadLoop() override {
+ mReader->loopOnce();
+ return true;
+ }
+};
+
+// --- InputReader ---
+
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
@@ -61,6 +83,7 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
+ mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -71,11 +94,33 @@
}
InputReader::~InputReader() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ delete devicePair.second;
}
}
+status_t InputReader::start() {
+ if (mThread->isRunning()) {
+ return ALREADY_EXISTS;
+ }
+ return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputReader::stop() {
+ if (!mThread->isRunning()) {
+ return OK;
+ }
+ if (gettid() == mThread->getTid()) {
+ ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ return INVALID_OPERATION;
+ }
+ // Directly calling requestExitAndWait() causes the thread to not exit
+ // if mEventHub is waiting for a long timeout.
+ mThread->requestExit();
+ mEventHub->wake();
+ return mThread->requestExitAndWait();
+}
+
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
@@ -179,8 +224,7 @@
}
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
+ if (mDevices.find(deviceId) != mDevices.end()) {
ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
return;
}
@@ -201,7 +245,7 @@
device->getSources());
}
- mDevices.add(deviceId, device);
+ mDevices.insert({deviceId, device});
bumpGenerationLocked();
if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -210,15 +254,14 @@
}
void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
- InputDevice* device = nullptr;
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt == mDevices.end()) {
ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
return;
}
- device = mDevices.valueAt(deviceIndex);
- mDevices.removeItemsAt(deviceIndex, 1);
+ InputDevice* device = deviceIt->second;
+ mDevices.erase(deviceIt);
bumpGenerationLocked();
if (device->isIgnored()) {
@@ -315,13 +358,13 @@
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
size_t count) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt == mDevices.end()) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ InputDevice* device = deviceIt->second;
if (device->isIgnored()) {
// ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
@@ -331,8 +374,8 @@
}
void InputReader::timeoutExpiredLocked(nsecs_t when) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
if (!device->isIgnored()) {
device->timeoutExpired(when);
}
@@ -360,8 +403,8 @@
if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
mEventHub->requestReopenDevices();
} else {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
device->configure(now, &mConfig, changes);
}
}
@@ -371,8 +414,8 @@
void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
mGlobalMetaState |= device->getMetaState();
}
}
@@ -386,8 +429,8 @@
}
void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
@@ -397,8 +440,8 @@
}
void InputReader::dispatchExternalStylusState(const StylusState& state) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
device->updateExternalStylusState(state);
}
}
@@ -421,8 +464,8 @@
}
void InputReader::fadePointerLocked() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
device->fadePointer();
}
}
@@ -446,9 +489,8 @@
void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
outInputDevices.clear();
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
if (!device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
@@ -479,17 +521,16 @@
GetStateFunc getStateFunc) {
int32_t result = AKEY_STATE_UNKNOWN;
if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt != mDevices.end()) {
+ InputDevice* device = deviceIt->second;
if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
result = (device->*getStateFunc)(sourceMask, code);
}
}
} else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
// If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
// value. Otherwise, return AKEY_STATE_UP as long as one device reports it.
@@ -506,13 +547,13 @@
}
void InputReader::toggleCapsLockState(int32_t deviceId) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt == mDevices.end()) {
ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
return;
}
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ InputDevice* device = deviceIt->second;
if (device->isIgnored()) {
return;
}
@@ -533,17 +574,16 @@
uint8_t* outFlags) {
bool result = false;
if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt != mDevices.end()) {
+ InputDevice* device = deviceIt->second;
if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
}
}
} else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
+ for (auto& devicePair : mDevices) {
+ InputDevice* device = devicePair.second;
if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
}
@@ -568,10 +608,9 @@
void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
ssize_t repeat, int32_t token) {
AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt != mDevices.end()) {
+ InputDevice* device = deviceIt->second;
device->vibrate(pattern, patternSize, repeat, token);
}
}
@@ -579,9 +618,9 @@
void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
AutoMutex _l(mLock);
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt != mDevices.end()) {
+ InputDevice* device = deviceIt->second;
device->cancelVibrate(token);
}
}
@@ -589,9 +628,9 @@
bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
AutoMutex _l(mLock);
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt != mDevices.end()) {
+ InputDevice* device = deviceIt->second;
return device->isEnabled();
}
ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
@@ -601,13 +640,13 @@
bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
AutoMutex _l(mLock);
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
+ auto deviceIt = mDevices.find(deviceId);
+ if (deviceIt == mDevices.end()) {
ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
return false;
}
- InputDevice* device = mDevices.valueAt(deviceIndex);
+ InputDevice* device = deviceIt->second;
if (!device->isEnabled()) {
ALOGW("Ignoring disabled device %s", device->getName().c_str());
return false;
@@ -635,8 +674,9 @@
dump += "Input Reader State:\n";
- for (size_t i = 0; i < mDevices.size(); i++) {
- mDevices.valueAt(i)->dump(dump);
+ for (const auto& devicePair : mDevices) {
+ InputDevice* const device = devicePair.second;
+ device->dump(dump);
}
dump += INDENT "Configuration:\n";
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 7b4321e..0a4e808 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -23,9 +23,9 @@
#include "InputReaderContext.h"
#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -38,12 +38,12 @@
* that it sends to the input listener. Some functions of the input reader, such as early
* event filtering in low power states, are controlled by a separate policy object.
*
- * The InputReader owns a collection of InputMappers. Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
* components running on arbitrary threads. To keep things manageable, the InputReader
* uses a single Mutex to guard its state. The Mutex may be held while calling into the
* EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
*/
class InputReader : public InputReaderInterface {
public:
@@ -55,7 +55,8 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
- virtual void loopOnce() override;
+ virtual status_t start() override;
+ virtual status_t stop() override;
virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
@@ -111,6 +112,9 @@
friend class ContextImpl;
private:
+ class InputReaderThread;
+ sp<InputReaderThread> mThread;
+
Mutex mLock;
Condition mReaderIsAliveCondition;
@@ -131,7 +135,11 @@
static const int EVENT_BUFFER_SIZE = 256;
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
- KeyedVector<int32_t, InputDevice*> mDevices;
+ std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
+
+ // With each iteration of the loop, InputReader reads and processes one incoming message from
+ // the EventHub.
+ void loopOnce();
// low-level input event decoding and device management
void processEventsLocked(const RawEvent* rawEvents, size_t count);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index c4f8626..ada2266 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -15,6 +15,7 @@
"-Werror",
"-Wextra",
"-Wno-unused-parameter",
+ "-Wthread-safety",
],
shared_libs: [
"android.hardware.input.classifier@1.0",
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7d69854..b706a74 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -470,7 +470,6 @@
const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
FakeInputReceiver(dispatcher, name, displayId),
mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
- mServerChannel->setToken(new BBinder());
mDispatcher->registerInputChannel(mServerChannel);
inputApplicationHandle->updateInfo();
@@ -478,7 +477,7 @@
}
virtual bool updateInfo() {
- mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
+ mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr;
mInfo.name = mName;
mInfo.layoutParamsFlags = mLayoutParamFlags;
mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
@@ -859,7 +858,6 @@
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false)
: FakeInputReceiver(dispatcher, name, displayId) {
- mServerChannel->setToken(new BBinder());
mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
}
};
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 028fb6f..8d4ab6a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -25,13 +25,18 @@
#include <TestInputListener.h>
#include <TouchInputMapper.h>
+#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <inttypes.h>
#include <math.h>
-
namespace android {
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
// An arbitrary time value.
static const nsecs_t ARBITRARY_TIME = 1234;
@@ -164,9 +169,13 @@
// --- FakeInputReaderPolicy ---
class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+ std::mutex mLock;
+ std::condition_variable mDevicesChangedCondition;
+
InputReaderConfiguration mConfig;
KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
- std::vector<InputDeviceInfo> mInputDevices;
+ std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+ bool mInputDevicesChanged GUARDED_BY(mLock){false};
std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
@@ -177,6 +186,20 @@
FakeInputReaderPolicy() {
}
+ void assertInputDevicesChanged() {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool devicesChanged =
+ mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
+ if (!devicesChanged) {
+ FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+ }
+ mInputDevicesChanged = false;
+ }
+
virtual void clearViewports() {
mViewports.clear();
mConfig.setDisplayViewports(mViewports);
@@ -291,7 +314,10 @@
}
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+ std::scoped_lock<std::mutex> lock(mLock);
mInputDevices = inputDevices;
+ mInputDevicesChanged = true;
+ mDevicesChangedCondition.notify_all();
}
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
@@ -342,9 +368,12 @@
}
};
+ std::mutex mLock;
+ std::condition_variable mEventsCondition;
+
KeyedVector<int32_t, Device*> mDevices;
std::vector<std::string> mExcludedDevices;
- List<RawEvent> mEvents;
+ List<RawEvent> mEvents GUARDED_BY(mLock);
std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
public:
@@ -496,6 +525,7 @@
void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
int32_t code, int32_t value) {
+ std::scoped_lock<std::mutex> lock(mLock);
RawEvent event;
event.when = when;
event.deviceId = deviceId;
@@ -515,8 +545,14 @@
}
void assertQueueIsEmpty() {
- ASSERT_EQ(size_t(0), mEvents.size())
- << "Expected the event queue to be empty (fully consumed).";
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool queueIsEmpty =
+ mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+ [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+ if (!queueIsEmpty) {
+ FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+ }
}
private:
@@ -619,12 +655,14 @@
}
virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+ std::scoped_lock<std::mutex> lock(mLock);
if (mEvents.empty()) {
return 0;
}
*buffer = *mEvents.begin();
mEvents.erase(mEvents.begin());
+ mEventsCondition.notify_all();
return 1;
}
@@ -877,11 +915,13 @@
KeyedVector<int32_t, int32_t> mScanCodeStates;
KeyedVector<int32_t, int32_t> mSwitchStates;
std::vector<int32_t> mSupportedKeyCodes;
- RawEvent mLastEvent;
- bool mConfigureWasCalled;
- bool mResetWasCalled;
- bool mProcessWasCalled;
+ std::mutex mLock;
+ std::condition_variable mStateChangedCondition;
+ bool mConfigureWasCalled GUARDED_BY(mLock);
+ bool mResetWasCalled GUARDED_BY(mLock);
+ bool mProcessWasCalled GUARDED_BY(mLock);
+ RawEvent mLastEvent GUARDED_BY(mLock);
std::optional<DisplayViewport> mViewport;
public:
@@ -903,20 +943,41 @@
}
void assertConfigureWasCalled() {
- ASSERT_TRUE(mConfigureWasCalled)
- << "Expected configure() to have been called.";
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool configureCalled =
+ mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mConfigureWasCalled;
+ });
+ if (!configureCalled) {
+ FAIL() << "Expected configure() to have been called.";
+ }
mConfigureWasCalled = false;
}
void assertResetWasCalled() {
- ASSERT_TRUE(mResetWasCalled)
- << "Expected reset() to have been called.";
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool resetCalled =
+ mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mResetWasCalled;
+ });
+ if (!resetCalled) {
+ FAIL() << "Expected reset() to have been called.";
+ }
mResetWasCalled = false;
}
void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
- ASSERT_TRUE(mProcessWasCalled)
- << "Expected process() to have been called.";
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool processCalled =
+ mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mProcessWasCalled;
+ });
+ if (!processCalled) {
+ FAIL() << "Expected process() to have been called.";
+ }
if (outLastEvent) {
*outLastEvent = mLastEvent;
}
@@ -953,6 +1014,7 @@
}
virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+ std::scoped_lock<std::mutex> lock(mLock);
mConfigureWasCalled = true;
// Find the associated viewport if exist.
@@ -960,15 +1022,21 @@
if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mViewport = config->getDisplayViewportByPort(*displayPort);
}
+
+ mStateChangedCondition.notify_all();
}
virtual void reset(nsecs_t) {
+ std::scoped_lock<std::mutex> lock(mLock);
mResetWasCalled = true;
+ mStateChangedCondition.notify_all();
}
virtual void process(const RawEvent* rawEvent) {
+ std::scoped_lock<std::mutex> lock(mLock);
mLastEvent = *rawEvent;
mProcessWasCalled = true;
+ mStateChangedCondition.notify_all();
}
virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
@@ -1033,23 +1101,22 @@
}
}
- void setNextDevice(InputDevice* device) {
- mNextDevice = device;
- }
+ void setNextDevice(InputDevice* device) { mNextDevice = device; }
InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
- uint32_t classes, const std::string& location = "") {
+ uint32_t classes, const std::string& location = "") {
InputDeviceIdentifier identifier;
identifier.name = name;
identifier.location = location;
int32_t generation = deviceId + 1;
return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
- classes);
+ classes);
}
protected:
virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes) {
+ const InputDeviceIdentifier& identifier,
+ uint32_t classes) {
if (mNextDevice) {
InputDevice* device = mNextDevice;
mNextDevice = nullptr;
@@ -1066,12 +1133,8 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() {
- mFakePolicy = new FakeInputReaderPolicy();
- }
- virtual void TearDown() {
- mFakePolicy.clear();
- }
+ virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+ virtual void TearDown() override { mFakePolicy.clear(); }
};
/**
@@ -1254,18 +1317,20 @@
sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<InstrumentedInputReader> mReader;
+ std::unique_ptr<InstrumentedInputReader> mReader;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
+ ASSERT_EQ(OK, mReader->start());
}
- virtual void TearDown() {
- mReader.clear();
+ virtual void TearDown() override {
+ ASSERT_EQ(OK, mReader->stop());
mFakeListener.clear();
mFakePolicy.clear();
@@ -1279,23 +1344,18 @@
mFakeEventHub->addConfigurationMap(deviceId, configuration);
}
mFakeEventHub->finishDeviceScan();
- mReader->loopOnce();
- mReader->loopOnce();
- mFakeEventHub->assertQueueIsEmpty();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
}
void disableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->addDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
void enableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->removeDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
- }
-
- void configureDevice(uint32_t changes, InputDevice* device) {
- device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1316,10 +1376,8 @@
ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
0, nullptr)); // no classes so device will be ignored
-
std::vector<InputDeviceInfo> inputDevices;
mReader->getInputDevices(inputDevices);
-
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(1, inputDevices[0].getId());
ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1345,34 +1403,28 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, "fake", deviceClass, nullptr);
+ ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
disableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), false);
disableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasNotCalled();
- mFakeListener->assertNotifyConfigurationChangedWasNotCalled();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
ASSERT_EQ(device->isEnabled(), false);
enableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
}
@@ -1494,7 +1546,7 @@
ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
}
-TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
+TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) {
addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
NotifyConfigurationChangedArgs args;
@@ -1503,13 +1555,12 @@
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
}
-TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
+TEST_F(InputReaderTest, ForwardsRawEventsToMappers) {
FakeInputMapper* mapper = nullptr;
ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
- mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
RawEvent event;
@@ -1529,27 +1580,24 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, "fake", deviceClass, nullptr);
+ ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
uint32_t prevSequenceNum = resetArgs.sequenceNum;
disableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
enableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
disableDevice(deviceId, device);
- mReader->loopOnce();
- mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
}
@@ -1563,7 +1611,6 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, "fake", deviceClass, nullptr);
const uint8_t hdmi1 = 1;
@@ -1571,12 +1618,20 @@
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
// Add default and second display.
+ mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- mReader->loopOnce();
+
+ // Add the device, and make sure all of the callbacks are triggered.
+ // The device is added after the input port associations are processed since
+ // we do not yet support dynamic device-to-display associations.
+ ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
// Device should only dispatch to the specified display.
ASSERT_EQ(deviceId, device->getId());
@@ -1585,6 +1640,8 @@
// Can't dispatch event from a disabled device.
disableDevice(deviceId, device);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
@@ -1607,7 +1664,7 @@
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1621,7 +1678,7 @@
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
@@ -1845,7 +1902,7 @@
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1859,7 +1916,7 @@
mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
mFakeListener.clear();
@@ -2522,7 +2579,7 @@
sp<FakePointerController> mFakePointerController;
- virtual void SetUp() {
+ virtual void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 3ee33f1..06b05ac 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -19,6 +19,15 @@
#include "TestInputListener.h"
+namespace {
+
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;
+
+} // namespace
+
namespace android {
// --- TestInputListener ---
@@ -29,87 +38,117 @@
void TestInputListener::assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs) {
- ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
- }
- mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
+ "Expected notifyConfigurationChanged() "
+ "to have been called."));
}
void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
- ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to not have been called.";
+ ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
+ "notifyConfigurationChanged() should not be called."));
}
void TestInputListener::assertNotifyDeviceResetWasCalled(
NotifyDeviceResetArgs* outEventArgs) {
- ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
- }
- mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<
+ NotifyDeviceResetArgs>(outEventArgs,
+ "Expected notifyDeviceReset() to have been called."));
}
void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
- ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to not have been called.";
+ ASSERT_NO_FATAL_FAILURE(
+ assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called."));
}
void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
- ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyKeyArgsQueue.begin();
- }
- mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
}
void TestInputListener::assertNotifyKeyWasNotCalled() {
- ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+ ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
- ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyMotionArgsQueue.begin();
- }
- mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifyMotionArgs>(outEventArgs,
+ "Expected notifyMotion() to have been called."));
}
void TestInputListener::assertNotifyMotionWasNotCalled() {
- ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to not have been called.";
+ ASSERT_NO_FATAL_FAILURE(
+ assertNotCalled<NotifySwitchArgs>("notifySwitch() should not be called."));
}
void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
- ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
- << "Expected notifySwitch() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifySwitchArgsQueue.begin();
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifySwitchArgs>(outEventArgs,
+ "Expected notifySwitch() to have been called."));
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ if (queue.empty()) {
+ const bool eventReceived =
+ mCondition.wait_for(lock, WAIT_TIMEOUT,
+ [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ if (!eventReceived) {
+ FAIL() << "Timed out waiting for event: " << message.c_str();
+ }
}
- mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+ if (outEventArgs) {
+ *outEventArgs = *queue.begin();
+ }
+ queue.erase(queue.begin());
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertNotCalled(std::string message) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
+ if (eventReceived) {
+ FAIL() << "Unexpected event: " << message.c_str();
+ }
+}
+
+template <class NotifyArgsType>
+void TestInputListener::notify(const NotifyArgsType* args) {
+ std::scoped_lock<std::mutex> lock(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ queue.push_back(*args);
+ mCondition.notify_all();
}
void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- mNotifyConfigurationChangedArgsQueue.push_back(*args);
+ notify<NotifyConfigurationChangedArgs>(args);
}
void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mNotifyDeviceResetArgsQueue.push_back(*args);
+ notify<NotifyDeviceResetArgs>(args);
}
void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
- mNotifyKeyArgsQueue.push_back(*args);
+ notify<NotifyKeyArgs>(args);
}
void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
- mNotifyMotionArgsQueue.push_back(*args);
+ notify<NotifyMotionArgs>(args);
}
void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
- mNotifySwitchArgsQueue.push_back(*args);
- }
-
+ notify<NotifySwitchArgs>(args);
+}
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 085d343..945e2ea 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -17,6 +17,7 @@
#ifndef _UI_TEST_INPUT_LISTENER_H
#define _UI_TEST_INPUT_LISTENER_H
+#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include "InputListener.h"
@@ -25,13 +26,6 @@
// --- TestInputListener ---
class TestInputListener : public InputListenerInterface {
-private:
- std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
- std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
- std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
- std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
- std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
protected:
virtual ~TestInputListener();
@@ -58,15 +52,34 @@
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
private:
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+ template <class NotifyArgsType>
+ void assertCalled(NotifyArgsType* outEventArgs, std::string message);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+ template <class NotifyArgsType>
+ void assertNotCalled(std::string message);
- virtual void notifyKey(const NotifyKeyArgs* args);
+ template <class NotifyArgsType>
+ void notify(const NotifyArgsType* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
- virtual void notifySwitch(const NotifySwitchArgs* args);
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+
+ std::mutex mLock;
+ std::condition_variable mCondition;
+
+ std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+ std::vector<NotifyDeviceResetArgs>, //
+ std::vector<NotifyKeyArgs>, //
+ std::vector<NotifyMotionArgs>, //
+ std::vector<NotifySwitchArgs>> //
+ mQueues GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 060b5eb..fa23da0 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -180,7 +180,7 @@
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
- int64_t procStateSeq __unused) {}
+ int64_t procStateSeq __unused, int32_t capability __unused) {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e5d23d0..f529a44 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,6 +165,7 @@
"Scheduler/PhaseOffsets.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
+ "Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncModulator.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 603a9f7..a25709c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -107,11 +107,8 @@
}
bool BufferLayer::isVisible() const {
- bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
+ return !isHiddenByPolicy() && getAlpha() > 0.0f &&
(mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
- mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible);
-
- return visible;
}
bool BufferLayer::isFixedSize() const {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 8cc1831..dc61e49 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -374,13 +374,26 @@
void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
const int32_t layerID = getSequence();
+ mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
- systemTime(), FrameTracer::FrameEvent::DEQUEUE, 3000);
+ systemTime(), FrameTracer::FrameEvent::DEQUEUE);
+}
+
+void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
+ const int32_t layerID = getSequence();
+ mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+ systemTime(), FrameTracer::FrameEvent::DETACH);
+}
+
+void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
+ const int32_t layerID = getSequence();
+ mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+ systemTime(), FrameTracer::FrameEvent::CANCEL);
}
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
const int32_t layerID = getSequence();
- mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
mFlinger->mFrameTracer->traceTimestamp(layerID, item.mGraphicBuffer->getId(), item.mFrameNumber,
systemTime(), FrameTracer::FrameEvent::QUEUE);
@@ -389,8 +402,8 @@
{ // Autolock scope
if (mFlinger->mUseSmart90ForVideo) {
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
- item.mHdrMetadata.validTypes != 0);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ item.mHdrMetadata.validTypes != 0);
}
Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 95e0b10..1b1fccd 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -94,6 +94,8 @@
void onFrameReplaced(const BufferItem& item) override;
void onSidebandStreamChanged() override;
void onFrameDequeued(const uint64_t bufferId) override;
+ void onFrameDetached(const uint64_t bufferId) override;
+ void onFrameCancelled(const uint64_t bufferId) override;
// -----------------------------------------------------------------------
public:
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 85d2253..5768edd 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -249,8 +249,8 @@
if (mFlinger->mUseSmart90ForVideo) {
const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime;
- mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
- mCurrentState.hdrMetadata.validTypes != 0);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ mCurrentState.hdrMetadata.validTypes != 0);
}
return true;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 289bb17..8e6a70c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -63,6 +63,7 @@
}
Rect getCrop(const Layer::State& s) const;
+ uint32_t getTransformHint() const { return mTransformHint; }
bool setTransform(uint32_t transform) override;
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index c7ed9b0..f331364 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -75,17 +75,18 @@
status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parentHandle,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) {
+ sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
// We rely on createLayer to check permissions.
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- parentHandle);
+ parentHandle, nullptr, outTransformHint);
}
status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) {
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint) {
if (mFlinger->authenticateSurfaceTexture(parent) == false) {
ALOGE("failed to authenticate surface texture");
// The extra parent layer check below before returning is to help with debugging
@@ -103,7 +104,7 @@
}
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- nullptr, layer);
+ nullptr, layer, outTransformHint);
}
status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 7d7cef8..e9063e5 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -54,13 +54,15 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp);
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint = nullptr);
virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp);
+ sp<IGraphicBufferProducer>* gbp,
+ uint32_t* outTransformHint = nullptr);
status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 721e953..ce0222c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -326,6 +326,9 @@
writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), outputIndependentState);
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+
+ // Always set the layer color after setting the composition type.
+ writeSolidColorStateToHWC(hwcLayer.get(), outputIndependentState);
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(
@@ -435,7 +438,7 @@
// Content-specific per-frame state
switch (outputIndependentState.compositionType) {
case Hwc2::IComposerClient::Composition::SOLID_COLOR:
- writeSolidColorStateToHWC(hwcLayer, outputIndependentState);
+ // For compatibility, should be written AFTER the composition type.
break;
case Hwc2::IComposerClient::Composition::SIDEBAND:
writeSidebandStateToHWC(hwcLayer, outputIndependentState);
@@ -453,6 +456,10 @@
void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
+ if (outputIndependentState.compositionType != Hwc2::IComposerClient::Composition::SOLID_COLOR) {
+ return;
+ }
+
hwc_color_t color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index a338784..88acd04 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -33,6 +33,7 @@
namespace {
using testing::_;
+using testing::InSequence;
using testing::Return;
using testing::ReturnRef;
using testing::StrictMock;
@@ -769,8 +770,13 @@
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
- expectSetColorCall();
+
+ // Setting the composition type should happen before setting the color. We
+ // check this in this test only by setting up an testing::InSeqeuence
+ // instance before setting up the two expectations.
+ InSequence s;
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+ expectSetColorCall();
mOutputLayer.writeStateToHWC(false);
}
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index b48d471..9b7bab1 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -34,6 +34,7 @@
bool isCreatedFromMainThread() const override { return true; }
protected:
+ bool canDrawShadows() const override { return false; }
sp<Layer> createClone() override;
};
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index acddc42..aef1c75 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -95,6 +95,7 @@
// assume NO_RESOURCES when Status::isOk returns false
constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = V2_4::Error::NO_RESOURCES;
template<typename T, typename U>
T unwrapRet(Return<T>& ret, const U& default_val)
@@ -177,7 +178,7 @@
if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError == Error::NONE) {
+ if (tmpError == V2_4::Error::NONE) {
mClient = tmpClient;
mClient_2_2 = tmpClient;
mClient_2_3 = tmpClient;
@@ -1172,12 +1173,12 @@
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ V2_4::Error error = kDefaultError_2_4;
if (mClient_2_4) {
mClient_2_4->getDisplayCapabilities_2_4(display,
[&](const auto& tmpError, const auto& tmpCaps) {
error = tmpError;
- if (error != Error::NONE) {
+ if (error != V2_4::Error::NONE) {
return;
}
*outCapabilities = tmpCaps;
@@ -1185,8 +1186,8 @@
} else {
mClient_2_3
->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
- error = tmpError;
- if (error != Error::NONE) {
+ error = static_cast<V2_4::Error>(tmpError);
+ if (error != V2_4::Error::NONE) {
return;
}
@@ -1196,7 +1197,7 @@
});
}
- return error;
+ return static_cast<Error>(error);
}
Error Composer::getDisplayConnectionType(Display display,
@@ -1205,17 +1206,17 @@
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ V2_4::Error error = kDefaultError_2_4;
mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
error = tmpError;
- if (error != Error::NONE) {
+ if (error != V2_4::Error::NONE) {
return;
}
*outType = tmpType;
});
- return error;
+ return static_cast<V2_1::Error>(error);
}
CommandReader::~CommandReader()
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index d63cd79..22b268a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -23,7 +23,7 @@
#include <string_view>
#include <vector>
-#include <ui/GraphicTypes.h>
+#include <ui/PhysicalDisplayId.h>
namespace android {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 37caab2..d5a9ae1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -118,10 +118,9 @@
mFrameEventHistory.initializeCompositorTiming(compositorTiming);
mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
- mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
- mFlinger->onLayerCreated();
+ mFlinger->onLayerCreated(this);
}
Layer::~Layer() {
@@ -340,7 +339,8 @@
return bufferScaleTransform.inverse().transform(mBounds);
}
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
+ float parentShadowRadius) {
const State& s(getDrawingState());
// Calculate effective layer transform
@@ -363,11 +363,23 @@
mBounds = bounds;
mScreenBounds = mEffectiveTransform.transform(mBounds);
+ // Use the layer's own shadow radius if set. Otherwise get the radius from
+ // parent.
+ if (s.shadowRadius > 0.f) {
+ mEffectiveShadowRadius = s.shadowRadius;
+ } else {
+ mEffectiveShadowRadius = parentShadowRadius;
+ }
+
+ // Shadow radius is passed down to only one layer so if the layer can draw shadows,
+ // don't pass it to its children.
+ const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
+
// Add any buffer scaling to the layer's children.
ui::Transform bufferScaleTransform = getBufferScaleTransform();
for (const sp<Layer>& child : mDrawingChildren) {
child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
- getTransformWithScale(bufferScaleTransform));
+ getTransformWithScale(bufferScaleTransform), childShadowRadius);
}
}
@@ -467,11 +479,13 @@
compositionState.hasProtectedContent = isProtected();
const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool drawsShadows = mEffectiveShadowRadius != 0.f;
+
compositionState.isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
// Force client composition for special cases known only to the front-end.
- if (isHdrY410() || usesRoundedCorners) {
+ if (isHdrY410() || usesRoundedCorners || drawsShadows) {
compositionState.forceClientComposition = true;
}
}
@@ -1117,6 +1131,18 @@
return p->getLayerStack();
}
+bool Layer::setShadowRadius(float shadowRadius) {
+ if (mCurrentState.shadowRadius == shadowRadius) {
+ return false;
+ }
+
+ mCurrentState.sequence++;
+ mCurrentState.shadowRadius = shadowRadius;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
ATRACE_CALL();
mCurrentState.barrierLayer_legacy = barrierLayer;
@@ -1411,8 +1437,8 @@
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
child->computeBounds(newParent->mBounds,
- newParent->getTransformWithScale(
- newParent->getBufferScaleTransform()));
+ newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+ newParent->mEffectiveShadowRadius);
}
}
@@ -1541,8 +1567,10 @@
mCurrentParent = layer;
}
-int32_t Layer::getZ() const {
- return mDrawingState.z;
+int32_t Layer::getZ(LayerVector::StateSet stateSet) const {
+ const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+ const State& state = useDrawing ? mDrawingState : mCurrentState;
+ return state.z;
}
bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
@@ -1602,7 +1630,7 @@
continue;
}
- if (relative->getZ() >= 0) {
+ if (relative->getZ(stateSet) >= 0) {
break;
}
relative->traverseInZOrder(stateSet, visitor);
@@ -1636,7 +1664,7 @@
continue;
}
- if (relative->getZ() < 0) {
+ if (relative->getZ(stateSet) < 0) {
break;
}
relative->traverseInReverseZOrder(stateSet, visitor);
@@ -1694,7 +1722,7 @@
size_t i = 0;
for (; i < list.size(); i++) {
const auto& relative = list[i];
- if (relative->getZ() >= 0) {
+ if (relative->getZ(stateSet) >= 0) {
break;
}
relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ad81920..fb72391 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -216,11 +216,14 @@
std::deque<sp<CallbackHandle>> callbackHandles;
bool colorSpaceAgnostic;
nsecs_t desiredPresentTime = -1;
+ float shadowRadius;
};
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
+ int getWindowType() const { return mWindowType; }
+
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
@@ -327,6 +330,7 @@
};
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
+ bool setShadowRadius(float shadowRadius);
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
@@ -360,7 +364,7 @@
FloatRect getBounds() const;
// Compute bounds for the layer and cache the results.
- void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+ void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
// Returns the buffer scale transform if a scaling mode is set.
ui::Transform getBufferScaleTransform() const;
@@ -670,7 +674,7 @@
// Copy the current list of children to the drawing state. Called by
// SurfaceFlinger to complete a transaction.
void commitChildList();
- int32_t getZ() const;
+ int32_t getZ(LayerVector::StateSet stateSet) const;
virtual void pushPendingState();
/**
@@ -886,9 +890,6 @@
// Window types from WindowManager.LayoutParams
const int mWindowType;
- // This is populated if the layer is registered with Scheduler for tracking purposes.
- std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
-
private:
/**
* Returns an unsorted vector of all layers that are part of this tree.
@@ -943,6 +944,14 @@
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
// and relatives, this layer will update as well.
wp<Layer> mClonedFrom;
+
+ // The inherited shadow radius after taking into account the layer hierarchy. This is the
+ // final shadow radius for this layer. If a shadow is specified for a layer, then effective
+ // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
+ float mEffectiveShadowRadius;
+
+ // Returns true if the layer can draw shadows on its border.
+ virtual bool canDrawShadows() const { return true; }
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 30027de..a484373 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -268,16 +268,6 @@
mDescriptors.erase(who);
}
-namespace {
-// Using Rec. 709 primaries
-inline float getLuma(float r, float g, float b) {
- constexpr auto rec709_red_primary = 0.2126f;
- constexpr auto rec709_green_primary = 0.7152f;
- constexpr auto rec709_blue_primary = 0.0722f;
- return rec709_red_primary * r + rec709_green_primary * g + rec709_blue_primary * b;
-}
-} // anonymous namespace
-
float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
uint32_t orientation, const Rect& sample_area) {
if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
@@ -298,30 +288,23 @@
std::swap(area.left, area.right);
}
- std::array<int32_t, 256> brightnessBuckets = {};
- const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2;
+ const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+ uint32_t accumulatedLuma = 0;
+ // Calculates luma with approximation of Rec. 709 primaries
for (int32_t row = area.top; row < area.bottom; ++row) {
const uint32_t* rowBase = data + row * stride;
for (int32_t column = area.left; column < area.right; ++column) {
uint32_t pixel = rowBase[column];
- const float r = pixel & 0xFF;
- const float g = (pixel >> 8) & 0xFF;
- const float b = (pixel >> 16) & 0xFF;
- const uint8_t luma = std::round(getLuma(r, g, b));
- ++brightnessBuckets[luma];
- if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
+ const uint32_t r = pixel & 0xFF;
+ const uint32_t g = (pixel >> 8) & 0xFF;
+ const uint32_t b = (pixel >> 16) & 0xFF;
+ const uint32_t luma = (r * 7 + b * 2 + g * 23) >> 5;
+ accumulatedLuma += luma;
}
}
- int32_t accumulated = 0;
- size_t bucket = 0;
- for (; bucket < brightnessBuckets.size(); bucket++) {
- accumulated += brightnessBuckets[bucket];
- if (accumulated > majoritySampleNum) break;
- }
-
- return bucket / 255.0f;
+ return accumulatedLuma / (255.0f * pixelCount);
}
std::vector<float> RegionSamplingThread::sampleBuffer(
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5473db6..8b71728 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,183 +14,150 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "LayerHistory"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "LayerHistory.h"
-#include <cinttypes>
-#include <cstdint>
-#include <limits>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "LayerInfo.h"
#include "SchedulerUtils.h"
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
-std::atomic<int64_t> LayerHistory::sNextId = 0;
+namespace {
-LayerHistory::LayerHistory() {
+bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+ return layer.isVisible() && (info.isHDR() || info.getLastUpdatedTime() >= threshold);
+}
+
+bool traceEnabled() {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.layer_history_trace", value, "0");
- mTraceEnabled = static_cast<bool>(atoi(value));
+ return atoi(value);
}
+void trace(const wp<Layer>& weak, int fps) {
+ const auto layer = weak.promote();
+ if (!layer) return;
+
+ const auto& name = layer->getName();
+ const auto tag = "LFPS " + name;
+ ATRACE_INT(tag.c_str(), fps);
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+
+} // namespace
+
+LayerHistory::LayerHistory() : mTraceEnabled(traceEnabled()) {}
LayerHistory::~LayerHistory() = default;
-std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
- float minRefreshRate,
- float maxRefreshRate) {
- const int64_t id = sNextId++;
-
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate) {
+ auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
std::lock_guard lock(mLock);
- mInactiveLayerInfos.emplace(id,
- std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
- return std::make_unique<LayerHistory::LayerHandle>(*this, id);
+ mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::destroyLayer(const int64_t id) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, bool isHDR, nsecs_t now) {
std::lock_guard lock(mLock);
- auto it = mActiveLayerInfos.find(id);
- if (it != mActiveLayerInfos.end()) {
- mActiveLayerInfos.erase(it);
- }
- it = mInactiveLayerInfos.find(id);
- if (it != mInactiveLayerInfos.end()) {
- mInactiveLayerInfos.erase(it);
+ const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+ [layer](const auto& pair) { return pair.first == layer; });
+ LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+ const auto& info = it->second;
+ info->setLastPresentTime(presentTime, now);
+ info->setIsHDR(isHDR);
+
+ // Activate layer if inactive.
+ if (const auto end = activeLayers().end(); it >= end) {
+ std::iter_swap(it, end);
+ mActiveLayersEnd++;
}
}
-void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
- bool isHdr) {
- std::shared_ptr<LayerInfo> layerInfo;
- {
- std::lock_guard lock(mLock);
- auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mInactiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- mInactiveLayerInfos.erase(layerInfoIterator);
- mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
- } else {
- layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mActiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- } else {
- ALOGW("Inserting information about layer that is not registered: %" PRId64,
- layerHandle->mId);
- return;
- }
- }
- }
- layerInfo->setLastPresentTime(presentTime);
- layerInfo->setHDRContent(isHdr);
-}
-
-void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
- std::shared_ptr<LayerInfo> layerInfo;
- {
- std::lock_guard lock(mLock);
- auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mInactiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- if (visible) {
- mInactiveLayerInfos.erase(layerInfoIterator);
- mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
- }
- } else {
- layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mActiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- } else {
- ALOGW("Inserting information about layer that is not registered: %" PRId64,
- layerHandle->mId);
- return;
- }
- }
- }
- layerInfo->setVisibility(visible);
-}
-
-std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
+LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
+ float maxRefreshRate = 0;
bool isHDR = false;
- float newRefreshRate = 0.f;
+
std::lock_guard lock(mLock);
- removeIrrelevantLayers();
+ partitionLayers(now);
- // Iterate through all layers that have been recently updated, and find the max refresh rate.
- for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
- const bool recent = layerInfo->isRecentlyActive();
+ // Find the maximum refresh rate among recently active layers.
+ for (const auto& [layer, info] : activeLayers()) {
+ const bool recent = info->isRecentlyActive(now);
if (recent || CC_UNLIKELY(mTraceEnabled)) {
- const float refreshRate = layerInfo->getDesiredRefreshRate();
- if (recent && refreshRate > newRefreshRate) {
- newRefreshRate = refreshRate;
+ const float refreshRate = info->getRefreshRate(now);
+ if (recent && refreshRate > maxRefreshRate) {
+ maxRefreshRate = refreshRate;
}
if (CC_UNLIKELY(mTraceEnabled)) {
- std::string name = "LFPS " + layerInfo->getName();
- const float rate = std::round(refreshRate);
- ATRACE_INT(name.c_str(), rate);
- ALOGD("%s: %f", name.c_str(), rate);
+ trace(layer, std::round(refreshRate));
}
}
- isHDR |= layerInfo->getHDRContent();
+ isHDR |= info->isHDR();
}
if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+ ALOGD("%s: maxRefreshRate=%.2f, isHDR=%d", __FUNCTION__, maxRefreshRate, isHDR);
}
- return {newRefreshRate, isHDR};
+ return {maxRefreshRate, isHDR};
}
-void LayerHistory::removeIrrelevantLayers() {
- const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
- // Iterator pointing to first element in map
- auto it = mActiveLayerInfos.begin();
- while (it != mActiveLayerInfos.end()) {
- // If last updated was before the obsolete time, remove it.
- // Keep HDR layer around as long as they are visible.
- if (!it->second->isVisible() ||
- (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
- // erase() function returns the iterator of the next
- // to last deleted element.
- if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("Layer %s obsolete", it->second->getName().c_str());
- // Make sure to update systrace to indicate that the layer was erased.
- std::string layerName = "LFPS " + it->second->getName();
- ATRACE_INT(layerName.c_str(), 0);
- }
- auto id = it->first;
- auto layerInfo = it->second;
- layerInfo->clearHistory();
- mInactiveLayerInfos.insert({id, layerInfo});
- it = mActiveLayerInfos.erase(it);
+void LayerHistory::partitionLayers(nsecs_t now) {
+ const nsecs_t threshold = getActiveLayerThreshold(now);
+
+ // Collect expired and inactive layers after active layers.
+ size_t i = 0;
+ while (i < mActiveLayersEnd) {
+ auto& [weak, info] = mLayerInfos[i];
+ if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+ i++;
+ continue;
+ }
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(weak, 0);
+ }
+
+ info->clearHistory();
+ std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+ }
+
+ // Collect expired layers after inactive layers.
+ size_t end = mLayerInfos.size();
+ while (i < end) {
+ if (mLayerInfos[i].first.promote()) {
+ i++;
} else {
- ++it;
+ std::swap(mLayerInfos[i], mLayerInfos[--end]);
}
}
+
+ mLayerInfos.erase(mLayerInfos.begin() + end, mLayerInfos.end());
}
-void LayerHistory::clearHistory() {
+void LayerHistory::clear() {
std::lock_guard lock(mLock);
- auto it = mActiveLayerInfos.begin();
- while (it != mActiveLayerInfos.end()) {
- auto id = it->first;
- auto layerInfo = it->second;
- layerInfo->clearHistory();
- mInactiveLayerInfos.insert({id, layerInfo});
- it = mActiveLayerInfos.erase(it);
+ for (const auto& [layer, info] : activeLayers()) {
+ info->clearHistory();
}
+
+ mActiveLayersEnd = 0;
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5598cc1..15ac8ca 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -16,76 +16,78 @@
#pragma once
-#include <array>
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
+#include <android-base/thread_annotations.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include "LayerInfo.h"
-#include "SchedulerUtils.h"
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
namespace android {
+
+class Layer;
+
namespace scheduler {
-/*
- * This class represents information about layers that are considered current. We keep an
- * unordered map between layer name and LayerInfo.
- */
+class LayerInfo;
+
+// Records per-layer history of scheduling-related information (primarily present time),
+// heuristically categorizes layers as active or inactive, and summarizes stats about
+// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
class LayerHistory {
public:
- // Handle for each layer we keep track of.
- class LayerHandle {
- public:
- LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
- ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
-
- const int64_t mId;
-
- private:
- LayerHistory& mLayerHistory;
- };
-
LayerHistory();
~LayerHistory();
- // When the layer is first created, register it.
- std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
- float maxRefreshRate);
+ // Layers are unregistered when the weak reference expires.
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
- // Method for inserting layers and their requested present time into the unordered map.
- void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
- // Method for setting layer visibility
- void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+ // Marks the layer as active, and records the given state to its history.
+ void record(Layer*, nsecs_t presentTime, bool isHDR, nsecs_t now);
- // Returns the desired refresh rate, which is a max refresh rate of all the current
- // layers. See go/content-fps-detection-in-scheduler for more information.
- std::pair<float, bool> getDesiredRefreshRateAndHDR();
+ struct Summary {
+ float maxRefreshRate; // Maximum refresh rate among recently active layers.
+ bool isHDR; // True if any recently active layer has HDR content.
+ };
- // Clears all layer history.
- void clearHistory();
+ // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+ Summary summarize(nsecs_t now);
- // Removes the handle and the object from the map.
- void destroyLayer(const int64_t id);
+ void clear();
private:
- // Removes the layers that have been idle for a given amount of time from mLayerInfos.
- void removeIrrelevantLayers() REQUIRES(mLock);
+ friend class LayerHistoryTest;
- // Information about currently active layers.
- std::mutex mLock;
- std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
- std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
+ using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+ using LayerInfos = std::vector<LayerPair>;
- // Each layer has it's own ID. This variable keeps track of the count.
- static std::atomic<int64_t> sNextId;
+ struct ActiveLayers {
+ LayerInfos& infos;
+ const size_t index;
- // Flag whether to log layer FPS in systrace
- bool mTraceEnabled = false;
+ auto begin() { return infos.begin(); }
+ auto end() { return begin() + index; }
+ };
+
+ ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+ // Iterates over layers in a single pass, swapping pairs such that active layers precede
+ // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+ // truncating after inactive layers.
+ void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+ mutable std::mutex mLock;
+
+ // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+ // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+ LayerInfos mLayerInfos GUARDED_BY(mLock);
+ size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+ // Whether to emit systrace output and debug logs.
+ const bool mTraceEnabled;
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 723d71f..f3b0d56 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -16,27 +16,17 @@
#include "LayerInfo.h"
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
+#include <algorithm>
+#include <utility>
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
-LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
- : mName(name),
- mMinRefreshDuration(1e9f / maxRefreshRate),
- mLowActivityRefreshDuration(1e9f / minRefreshRate),
- mRefreshRateHistory(mMinRefreshDuration) {}
+LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
+ : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
-LayerInfo::~LayerInfo() = default;
-
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
- std::lock_guard lock(mLock);
-
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
// Buffers can come with a present time far in the future. That keeps them relevant.
- mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+ mLastUpdatedTime = std::max(lastPresentTime, now);
mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
if (mLastPresentTime == 0) {
@@ -45,14 +35,13 @@
return;
}
- const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+ const nsecs_t period = lastPresentTime - mLastPresentTime;
mLastPresentTime = lastPresentTime;
// Ignore time diff that are too high - those are stale values
- if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
- const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
- const int fps = 1e9f / refreshDuration;
+ if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+
+ const float fps = std::min(1e9f / period, mHighRefreshRate);
mRefreshRateHistory.insertRefreshRate(fps);
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 17afdda..b86709f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,27 +16,37 @@
#pragma once
-#include <cinttypes>
-#include <cstdint>
-#include <deque>
-#include <mutex>
-#include <numeric>
-#include <string>
-
-#include <log/log.h>
-
-#include <utils/Mutex.h>
#include <utils/Timers.h>
+#include <chrono>
+#include <deque>
+
#include "SchedulerUtils.h"
namespace android {
+
+class Layer;
+
namespace scheduler {
-/*
- * This class represents information about individial layers.
- */
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+ return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
class LayerInfo {
+ // Layer is considered frequent if the earliest value in the window of most recent present times
+ // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+ // favor of a low refresh rate.
+ static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+ static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
/**
* Struct that keeps the information about the refresh rate for last
* HISTORY_SIZE frames. This is used to better determine the refresh rate
@@ -44,9 +54,9 @@
*/
class RefreshRateHistory {
public:
- explicit RefreshRateHistory(nsecs_t minRefreshDuration)
- : mMinRefreshDuration(minRefreshDuration) {}
- void insertRefreshRate(int refreshRate) {
+ explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
+
+ void insertRefreshRate(float refreshRate) {
mElements.push_back(refreshRate);
if (mElements.size() > HISTORY_SIZE) {
mElements.pop_front();
@@ -54,19 +64,16 @@
}
float getRefreshRateAvg() const {
- if (mElements.empty()) {
- return 1e9f / mMinRefreshDuration;
- }
-
- return scheduler::calculate_mean(mElements);
+ return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
}
void clearHistory() { mElements.clear(); }
private:
- std::deque<nsecs_t> mElements;
+ const float mHighRefreshRate;
+
static constexpr size_t HISTORY_SIZE = 30;
- const nsecs_t mMinRefreshDuration;
+ std::deque<float> mElements;
};
/**
@@ -76,6 +83,8 @@
*/
class PresentTimeHistory {
public:
+ static constexpr size_t HISTORY_SIZE = 90;
+
void insertPresentTime(nsecs_t presentTime) {
mElements.push_back(presentTime);
if (mElements.size() > HISTORY_SIZE) {
@@ -83,60 +92,45 @@
}
}
- // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
- // certain threshold: TIME_EPSILON_NS.
- bool isRelevant() const {
+ // Returns whether the earliest present time is within the active threshold.
+ bool isRecentlyActive(nsecs_t now) const {
if (mElements.size() < 2) {
return false;
}
// The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
- if (mElements.size() != HISTORY_SIZE &&
- mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+ if (mElements.size() < HISTORY_SIZE &&
+ mElements.back() - mElements.front() < HISTORY_TIME.count()) {
return false;
}
- // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
- const int64_t obsoleteEpsilon =
- systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
- if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
- return false;
- }
-
- return true;
+ return mElements.back() >= getActiveLayerThreshold(now);
}
- bool isLowActivityLayer() const {
- // We want to make sure that we received more than two frames from the layer
- // in order to check low activity.
- if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
+ bool isFrequent(nsecs_t now) const {
+ // Assume layer is infrequent if too few present times have been recorded.
+ if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
return false;
}
- const int64_t obsoleteEpsilon =
- systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
- // Check the frame before last to determine whether there is low activity.
- // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
- // infrequent updates.
- if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
- obsoleteEpsilon) {
- return true;
- }
-
- return false;
+ // Layer is frequent if the earliest value in the window of most recent present times is
+ // within threshold.
+ const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
+ const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ return *it >= threshold;
}
void clearHistory() { mElements.clear(); }
private:
std::deque<nsecs_t> mElements;
- static constexpr size_t HISTORY_SIZE = 90;
static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
+ friend class LayerHistoryTest;
+
public:
- LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
- ~LayerInfo();
+ LayerInfo(float lowRefreshRate, float highRefreshRate);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
@@ -144,71 +138,37 @@
// Records the last requested oresent time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
- void setHDRContent(bool isHdr) {
- std::lock_guard lock(mLock);
- mIsHDR = isHdr;
- }
+ bool isHDR() const { return mIsHDR; }
+ void setIsHDR(bool isHDR) { mIsHDR = isHDR; }
- void setVisibility(bool visible) {
- std::lock_guard lock(mLock);
- mIsVisible = visible;
- }
+ bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
+ bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
- // Checks the present time history to see whether the layer is relevant.
- bool isRecentlyActive() const {
- std::lock_guard lock(mLock);
- return mPresentTimeHistory.isRelevant();
- }
-
- // Calculate the average refresh rate.
- float getDesiredRefreshRate() const {
- std::lock_guard lock(mLock);
-
- if (mPresentTimeHistory.isLowActivityLayer()) {
- return 1e9f / mLowActivityRefreshDuration;
- }
- return mRefreshRateHistory.getRefreshRateAvg();
- }
-
- bool getHDRContent() {
- std::lock_guard lock(mLock);
- return mIsHDR;
- }
-
- bool isVisible() {
- std::lock_guard lock(mLock);
- return mIsVisible;
+ float getRefreshRate(nsecs_t now) const {
+ return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
}
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- nsecs_t getLastUpdatedTime() {
- std::lock_guard lock(mLock);
- return mLastUpdatedTime;
- }
-
- std::string getName() const { return mName; }
+ nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
void clearHistory() {
- std::lock_guard lock(mLock);
mRefreshRateHistory.clearHistory();
mPresentTimeHistory.clearHistory();
}
private:
- const std::string mName;
- const nsecs_t mMinRefreshDuration;
- const nsecs_t mLowActivityRefreshDuration;
- mutable std::mutex mLock;
- nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
- nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
- RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
- PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
- bool mIsHDR GUARDED_BY(mLock) = false;
- bool mIsVisible GUARDED_BY(mLock) = false;
+ const float mLowRefreshRate;
+ const float mHighRefreshRate;
+
+ nsecs_t mLastUpdatedTime = 0;
+ nsecs_t mLastPresentTime = 0;
+ RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
+ PresentTimeHistory mPresentTimeHistory;
+ bool mIsHDR = false;
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d60e101..71b3500 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,13 +20,6 @@
#include "Scheduler.h"
-#include <algorithm>
-#include <cinttypes>
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <numeric>
-
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
@@ -37,6 +30,14 @@
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <numeric>
+
+#include "../Layer.h"
#include "DispSync.h"
#include "DispSyncSource.h"
#include "EventControlThread.h"
@@ -325,37 +326,27 @@
return mPrimaryDispSync->expectedPresentTime();
}
-std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
- std::string const& name, int windowType) {
+void Scheduler::registerLayer(Layer* layer) {
uint32_t defaultFps, performanceFps;
if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- performanceFps =
- mRefreshRateConfigs
- .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE)
- .fps;
+ const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE;
+ performanceFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
} else {
defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
performanceFps = defaultFps;
}
- return mLayerHistory.createLayer(name, defaultFps, performanceFps);
+ mLayerHistory.registerLayer(layer, defaultFps, performanceFps);
}
-void Scheduler::addLayerPresentTimeAndHDR(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
- nsecs_t presentTime, bool isHDR) {
- mLayerHistory.insert(layerHandle, presentTime, isHDR);
-}
-
-void Scheduler::setLayerVisibility(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
- mLayerHistory.setVisibility(layerHandle, visible);
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
+ mLayerHistory.record(layer, presentTime, isHDR, systemTime());
}
void Scheduler::updateFpsBasedOnContent() {
- auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
+ auto [refreshRate, isHDR] = mLayerHistory.summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
RefreshRateType newRefreshRateType;
{
@@ -402,7 +393,7 @@
// Touch event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clearHistory();
+ mLayerHistory.clear();
}
void Scheduler::setDisplayPowerState(bool normal) {
@@ -417,7 +408,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clearHistory();
+ mLayerHistory.clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a5971fe..c983475 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -100,17 +100,11 @@
void addPresentFence(const std::shared_ptr<FenceTime>&);
void setIgnorePresentFences(bool ignore);
nsecs_t getDispSyncExpectedPresentTime();
- // Registers the layer in the scheduler, and returns the handle for future references.
- std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
- int windowType);
- // Stores present time for a layer.
- void addLayerPresentTimeAndHDR(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
- nsecs_t presentTime, bool isHDR);
- // Stores visibility for a layer.
- void setLayerVisibility(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
+ // Layers are registered on creation, and unregistered when the weak reference expires.
+ void registerLayer(Layer*);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
+
// Updates FPS based on the most content presented.
void updateFpsBasedOnContent();
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 3b7567c..d301b99 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -16,7 +16,6 @@
#pragma once
-#include <chrono>
#include <cinttypes>
#include <numeric>
#include <unordered_map>
@@ -38,21 +37,6 @@
return lhs.id == rhs.id;
}
-using namespace std::chrono_literals;
-
-// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. It is also used to determine how long the layer stays relevant.
-// This time period captures infrequent updates when playing YouTube video with static image,
-// or waiting idle in messaging app, when cursor is blinking.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
-
-// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
-// LOW_ACTIVITY_EPSILON_NS apart.
-// This is helping SF to vote for lower refresh rates when there is not activity
-// in screen.
-static constexpr int LOW_ACTIVITY_BUFFERS = 2;
-static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
-
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
template <typename T>
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
new file mode 100644
index 0000000..699cd50
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -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.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+#include <functional>
+
+namespace android::scheduler {
+
+/*
+ * TimeKeeper is the interface for a single-shot timer primitive.
+ */
+class TimeKeeper {
+public:
+ virtual ~TimeKeeper();
+
+ /*
+ * Arms callback to fired in time nanoseconds.
+ * There is only one timer, and subsequent calls will reset the callback function and the time.
+ */
+ virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+
+ /*
+ * Cancels an existing pending callback
+ */
+ virtual void alarmCancel() = 0;
+
+ /*
+ * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+ */
+ virtual nsecs_t now() const = 0;
+
+protected:
+ TimeKeeper(TimeKeeper const&) = delete;
+ TimeKeeper& operator=(TimeKeeper const&) = delete;
+ TimeKeeper() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
new file mode 100644
index 0000000..4a4bef8
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -0,0 +1,137 @@
+/*
+ * 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 <utils/Timers.h>
+#include <functional>
+#include <string>
+
+#include "StrongTyping.h"
+
+namespace android::scheduler {
+class TimeKeeper;
+class VSyncTracker;
+
+enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class CancelResult { Cancelled, TooLate, Error };
+
+/*
+ * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
+ */
+class VSyncDispatch {
+public:
+ using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
+
+ virtual ~VSyncDispatch();
+
+ /*
+ * Registers a callback that will be called at designated points on the vsync timeline.
+ * The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
+ * The token returned must be cleaned up via unregisterCallback.
+ *
+ * \param [in] callbackFn A function to schedule for callback. The resources needed to invoke
+ * callbackFn must have lifetimes encompassing the lifetime of the
+ * CallbackToken returned.
+ * \param [in] callbackName A human-readable, unique name to identify the callback.
+ * \return A token that can be used to schedule, reschedule, or cancel the
+ * invocation of callbackFn.
+ *
+ */
+ virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) = 0;
+
+ /*
+ * Unregisters a callback.
+ *
+ * \param [in] token The callback to unregister.
+ *
+ */
+ virtual void unregisterCallback(CallbackToken token) = 0;
+
+ /*
+ * Schedules the registered callback to be dispatched.
+ *
+ * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+ *
+ * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+ * parameter.
+ * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
+ * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ *
+ * If (workDuration - earliestVsync) is in the past, or if a callback has already been
+ * dispatched for the predictedVsync, an error will be returned.
+ *
+ * It is valid to reschedule a callback to a different time.
+ *
+ * \param [in] token The callback to schedule.
+ * \param [in] workDuration The time before the actual vsync time to invoke the callback
+ * associated with token.
+ * \param [in] earliestVsync The targeted display time. This will be snapped to the closest
+ * predicted vsync time after earliestVsync.
+ * \return A ScheduleResult::Scheduled if callback was scheduled.
+ * A ScheduleResult::ReScheduled if callback was rescheduled.
+ * A ScheduleResult::CannotSchedule
+ * if (workDuration - earliestVsync) is in the past, or
+ * if a callback was dispatched for the predictedVsync already.
+ * A ScheduleResult::Error if there was another error.
+ */
+ virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) = 0;
+
+ /* Cancels a scheduled callback, if possible.
+ *
+ * \param [in] token The callback to cancel.
+ * \return A CancelResult::TooLate if the callback was already dispatched.
+ * A CancelResult::Cancelled if the callback was successfully cancelled.
+ * A CancelResult::Error if there was an pre-condition violation.
+ */
+ virtual CancelResult cancel(CallbackToken token) = 0;
+
+protected:
+ VSyncDispatch() = default;
+ VSyncDispatch(VSyncDispatch const&) = delete;
+ VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+};
+
+/*
+ * Helper class to operate on registered callbacks. It is up to user of the class to ensure
+ * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
+ */
+class VSyncCallbackRegistration {
+public:
+ VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn,
+ std::string const& callbackName);
+ VSyncCallbackRegistration(VSyncCallbackRegistration&&);
+ VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
+ ~VSyncCallbackRegistration();
+
+ // See documentation for VSyncDispatch::schedule.
+ ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+
+ // See documentation for VSyncDispatch::cancel.
+ CancelResult cancel();
+
+private:
+ VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
+ VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
+
+ std::reference_wrapper<VSyncDispatch> mDispatch;
+ VSyncDispatch::CallbackToken mToken;
+ bool mValidToken;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
new file mode 100644
index 0000000..7922484
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+#include <vector>
+
+#include "TimeKeeper.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+VSyncDispatch::~VSyncDispatch() = default;
+VSyncTracker::~VSyncTracker() = default;
+TimeKeeper::~TimeKeeper() = default;
+
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
+ std::function<void(nsecs_t)> const& cb)
+ : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
+ return mLastDispatchTime;
+}
+
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
+ return mName;
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
+ if (!mArmedInfo) {
+ return {};
+ }
+ return {mArmedInfo->mActualWakeupTime};
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ VSyncTracker& tracker, nsecs_t now) {
+ mWorkDuration = workDuration;
+ mEarliestVsync = earliestVsync;
+ arm(tracker, now);
+ return mArmedInfo->mActualWakeupTime;
+}
+
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
+ if (!mArmedInfo) {
+ return;
+ }
+ arm(tracker, now);
+}
+
+void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
+ auto const nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
+ mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+}
+
+void VSyncDispatchTimerQueueEntry::disarm() {
+ mArmedInfo.reset();
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
+ mLastDispatchTime = mArmedInfo->mActualVsyncTime;
+ disarm();
+ return *mLastDispatchTime;
+}
+
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
+ {
+ std::lock_guard<std::mutex> lk(mRunningMutex);
+ mRunning = true;
+ }
+
+ mCallback(t);
+
+ std::lock_guard<std::mutex> lk(mRunningMutex);
+ mRunning = false;
+ mCv.notify_all();
+}
+
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
+ std::unique_lock<std::mutex> lk(mRunningMutex);
+ mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
+}
+
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+ VSyncTracker& tracker, nsecs_t timerSlack)
+ : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ cancelTimer();
+}
+
+void VSyncDispatchTimerQueue::cancelTimer() {
+ mIntendedWakeupTime = kInvalidTime;
+ mTimeKeeper->alarmCancel();
+}
+
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+ mIntendedWakeupTime = targetTime;
+ mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+ targetTime - now);
+}
+
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
+ rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+}
+
+void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
+ nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+ std::optional<nsecs_t> min;
+ for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+ auto& callback = it->second;
+ if (!callback->wakeupTime()) {
+ continue;
+ }
+
+ if (it != skipUpdateIt) {
+ callback->update(mTracker, now);
+ }
+ auto const wakeupTime = *callback->wakeupTime();
+ if (!min || (min && *min > wakeupTime)) {
+ min = wakeupTime;
+ }
+ }
+
+ if (min && (min < mIntendedWakeupTime)) {
+ setTimer(*min, now);
+ } else {
+ cancelTimer();
+ }
+}
+
+void VSyncDispatchTimerQueue::timerCallback() {
+ struct Invocation {
+ std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
+ nsecs_t timestamp;
+ };
+ std::vector<Invocation> invocations;
+ {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+ auto& callback = it->second;
+ auto const wakeupTime = callback->wakeupTime();
+ if (!wakeupTime) {
+ continue;
+ }
+
+ if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+ callback->executing();
+ invocations.emplace_back(
+ Invocation{callback, *callback->lastExecutedVsyncTarget()});
+ }
+ }
+
+ mIntendedWakeupTime = kInvalidTime;
+ rearmTimer(mTimeKeeper->now());
+ }
+
+ for (auto const& invocation : invocations) {
+ invocation.callback->callback(invocation.timestamp);
+ }
+}
+
+VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
+ std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ return CallbackToken{
+ mCallbacks
+ .emplace(++mCallbackToken,
+ std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+ callbackFn))
+ .first->first};
+}
+
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+ std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
+ {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ auto it = mCallbacks.find(token);
+ if (it != mCallbacks.end()) {
+ entry = it->second;
+ mCallbacks.erase(it);
+ }
+ }
+
+ if (entry) {
+ entry->ensureNotRunning();
+ }
+}
+
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) {
+ auto result = ScheduleResult::Error;
+ {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+ auto it = mCallbacks.find(token);
+ if (it == mCallbacks.end()) {
+ return result;
+ }
+ auto& callback = it->second;
+ result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
+
+ auto const now = mTimeKeeper->now();
+ auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
+
+ if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
+ return ScheduleResult::CannotSchedule;
+ }
+
+ if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+ rearmTimerSkippingUpdateFor(now, it);
+ }
+ }
+
+ return result;
+}
+
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+ auto it = mCallbacks.find(token);
+ if (it == mCallbacks.end()) {
+ return CancelResult::Error;
+ }
+ auto& callback = it->second;
+
+ if (callback->wakeupTime()) {
+ callback->disarm();
+ mIntendedWakeupTime = kInvalidTime;
+ rearmTimer(mTimeKeeper->now());
+ return CancelResult::Cancelled;
+ }
+ return CancelResult::TooLate;
+}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+ std::function<void(nsecs_t)> const& callbackFn,
+ std::string const& callbackName)
+ : mDispatch(dispatch),
+ mToken(dispatch.registerCallback(callbackFn, callbackName)),
+ mValidToken(true) {}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
+ : mDispatch(other.mDispatch),
+ mToken(std::move(other.mToken)),
+ mValidToken(std::move(other.mValidToken)) {
+ other.mValidToken = false;
+}
+
+VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+ mDispatch = std::move(other.mDispatch);
+ mToken = std::move(other.mToken);
+ mValidToken = std::move(other.mValidToken);
+ other.mValidToken = false;
+ return *this;
+}
+
+VSyncCallbackRegistration::~VSyncCallbackRegistration() {
+ if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+}
+
+ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+ if (!mValidToken) return ScheduleResult::Error;
+ return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+}
+
+CancelResult VSyncCallbackRegistration::cancel() {
+ if (!mValidToken) return CancelResult::Error;
+ return mDispatch.get().cancel(mToken);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
new file mode 100644
index 0000000..f058099
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+
+// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
+// VSyncDispatchTimerQueue hoisted to public for unit testing.
+class VSyncDispatchTimerQueueEntry {
+public:
+ // This is the state of the entry. There are 3 states, armed, running, disarmed.
+ // Valid transition: disarmed -> armed ( when scheduled )
+ // Valid transition: armed -> running -> disarmed ( when timer is called)
+ // Valid transition: armed -> disarmed ( when cancelled )
+ VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+ std::string_view name() const;
+
+ // Start: functions that are not threadsafe.
+ // Return the last vsync time this callback was invoked.
+ std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+ // This moves the state from disarmed->armed and will calculate the wakeupTime.
+ nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ nsecs_t now);
+ // This will update armed entries with the latest vsync information. Entry remains armed.
+ void update(VSyncTracker& tracker, nsecs_t now);
+
+ // This will return empty if not armed, or the next calculated wakeup time if armed.
+ // It will not update the wakeupTime.
+ std::optional<nsecs_t> wakeupTime() const;
+
+ // This moves state from armed->disarmed.
+ void disarm();
+
+ // This moves the state from armed->running.
+ // Store the timestamp that this was intended for as the last called timestamp.
+ nsecs_t executing();
+ // End: functions that are not threadsafe.
+
+ // Invoke the callback with the timestamp, moving the state from running->disarmed.
+ void callback(nsecs_t timestamp);
+ // Block calling thread while the callback is executing.
+ void ensureNotRunning();
+
+private:
+ void arm(VSyncTracker& tracker, nsecs_t now);
+ std::string const mName;
+ std::function<void(nsecs_t)> const mCallback;
+
+ nsecs_t mWorkDuration;
+ nsecs_t mEarliestVsync;
+
+ struct ArmingInfo {
+ nsecs_t mActualWakeupTime;
+ nsecs_t mActualVsyncTime;
+ };
+ std::optional<ArmingInfo> mArmedInfo;
+ std::optional<nsecs_t> mLastDispatchTime;
+
+ std::mutex mRunningMutex;
+ std::condition_variable mCv;
+ bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+/*
+ * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
+ * using a single timer queue.
+ */
+class VSyncDispatchTimerQueue : public VSyncDispatch {
+public:
+ explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+ nsecs_t timerSlack);
+ ~VSyncDispatchTimerQueue();
+
+ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) final;
+ void unregisterCallback(CallbackToken token) final;
+ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+ CancelResult cancel(CallbackToken token) final;
+
+private:
+ VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
+ VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+
+ using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+
+ void timerCallback();
+ void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+ void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+ void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+ REQUIRES(mMutex);
+ void cancelTimer() REQUIRES(mMutex);
+
+ static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+ std::unique_ptr<TimeKeeper> const mTimeKeeper;
+ VSyncTracker& mTracker;
+ nsecs_t const mTimerSlack;
+
+ std::mutex mutable mMutex;
+ size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+ CallbackMap mCallbacks GUARDED_BY(mMutex);
+ nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
new file mode 100644
index 0000000..97b9620
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <utils/Timers.h>
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+/*
+ * VSyncTracker is an interface for providing estimates on future Vsync signal times based on
+ * historical vsync timing data.
+ */
+class VSyncTracker {
+public:
+ virtual ~VSyncTracker();
+
+ /*
+ * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence)
+ * to the model.
+ *
+ * \param [in] timestamp The timestamp when the vsync signal was.
+ */
+ virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+
+ /*
+ * Access the next anticipated vsync time such that the anticipated time >= timePoint.
+ * This will always give the best accurate at the time of calling; multiple
+ * calls with the same timePoint might give differing values if the internal model
+ * is updated.
+ *
+ * \param [in] timePoint The point in time after which to estimate a vsync event.
+ * \return A prediction of the timestamp of a vsync event.
+ */
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+
+protected:
+ VSyncTracker(VSyncTracker const&) = delete;
+ VSyncTracker& operator=(VSyncTracker const&) = delete;
+ VSyncTracker() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 057669b..76fd51f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2011,7 +2011,8 @@
continue;
}
- layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+ layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform(),
+ 0.f /* shadowRadius */);
}
}
}
@@ -3335,6 +3336,9 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eShadowRadiusChanged) {
+ if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -3432,8 +3436,8 @@
uint32_t h, PixelFormat format, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle,
- const sp<Layer>& parentLayer) {
+ const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
+ uint32_t* outTransformHint) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -3470,7 +3474,7 @@
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), handle, &layer);
+ std::move(metadata), handle, outTransformHint, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceColor:
// check if buffer size is set for color layer.
@@ -3585,11 +3589,14 @@
status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
uint32_t w, uint32_t h, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<Layer>* outLayer) {
+ uint32_t* outTransformHint, sp<Layer>* outLayer) {
LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.displayDevice = getDefaultDisplayDevice();
args.textureName = getNewTexture();
sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
+ if (outTransformHint) {
+ *outTransformHint = layer->getTransformHint();
+ }
*handle = layer->getHandle();
*outLayer = layer;
@@ -4438,18 +4445,7 @@
case SET_DISPLAY_BRIGHTNESS: {
return OK;
}
- case CAPTURE_LAYERS: {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- // allow media to capture layer for video thumbnails
- if ((uid != AID_GRAPHICS && uid != AID_MEDIA) &&
- !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
- ALOGE("Permission Denial: can't capture layer pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- return OK;
- }
+ case CAPTURE_LAYERS:
case CAPTURE_SCREEN:
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
@@ -5010,7 +5006,8 @@
const Rect& drawingBounds)
: oldParent(oldParent), newParent(newParent) {
// Compute and cache the bounds for the new parent layer.
- newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+ 0.f /* shadowRadius */);
oldParent->setChildrenDrawingParent(newParent);
}
~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
@@ -5505,6 +5502,16 @@
return nullptr;
}
+void SurfaceFlinger::onLayerCreated(Layer* layer) {
+ mNumLayers++;
+ mScheduler->registerLayer(layer);
+}
+
+void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+ mNumLayers--;
+ mOffscreenLayers.erase(layer);
+}
+
void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b719245..e7ad295 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -310,11 +310,8 @@
bool authenticateSurfaceTextureLocked(
const sp<IGraphicBufferProducer>& bufferProducer) const;
- inline void onLayerCreated() { mNumLayers++; }
- inline void onLayerDestroyed(Layer* layer) {
- mNumLayers--;
- mOffscreenLayers.erase(layer);
- }
+ void onLayerCreated(Layer*);
+ void onLayerDestroyed(Layer*);
TransactionCompletedThread& getTransactionCompletedThread() {
return mTransactionCompletedThread;
@@ -605,7 +602,8 @@
status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags, LayerMetadata metadata,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr);
+ const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+ uint32_t* outTransformHint = nullptr);
status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
uint32_t h, uint32_t flags, LayerMetadata metadata,
@@ -614,7 +612,8 @@
status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ sp<IBinder>* outHandle, uint32_t* outTransformHint,
+ sp<Layer>* outLayer);
status_t createColorLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index a48f553..999e82d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -15,9 +15,9 @@
*/
#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
#include <thread>
#include "TransactionTestHarnesses.h"
-
namespace android {
using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -188,6 +188,15 @@
getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
+TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
+ uint32_t transformHint = ui::Transform::orientation_flags::ROT_INVALID;
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr, &transformHint));
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, transformHint);
+}
+
void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 7edddb6..f7a6d96 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -53,9 +53,10 @@
virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
- auto layer =
- createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
+ uint32_t flags = 0, SurfaceControl* parent = nullptr,
+ uint32_t* outTransformHint = nullptr) {
+ auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags,
+ parent, outTransformHint);
Transaction t;
t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
@@ -72,15 +73,18 @@
virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
const char* name, uint32_t width, uint32_t height,
PixelFormat format, uint32_t flags,
- SurfaceControl* parent = nullptr) {
- auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+ SurfaceControl* parent = nullptr,
+ uint32_t* outTransformHint = nullptr) {
+ auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
+ LayerMetadata(), outTransformHint);
EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
return layer;
}
virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
- return createLayer(mClient, name, width, height, flags, parent);
+ uint32_t flags = 0, SurfaceControl* parent = nullptr,
+ uint32_t* outTransformHint = nullptr) {
+ return createLayer(mClient, name, width, height, flags, parent, outTransformHint);
}
sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 8fdcde4..5612bb2 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -114,12 +114,14 @@
LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ uint32_t flags = 0, SurfaceControl* parent = nullptr,
+ uint32_t* outTransformHint = nullptr) {
// if the flags already have a layer type specified, return an error
if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
return nullptr;
}
- return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
+ return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
+ outTransformHint);
}
void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f85da20..78114a1 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -54,6 +54,7 @@
"FrameTracerTest.cpp",
"TransactionApplicationTest.cpp",
"StrongTypingTest.cpp",
+ "VSyncDispatchTimerQueueTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 60da70f..143a7a0 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -803,7 +803,7 @@
layerDrawingState.active.h = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
- layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
+ layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
return layer;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8e7440c..9a962bc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,140 +1,243 @@
#undef LOG_TAG
-#define LOG_TAG "LayerHistoryUnittests"
+#define LOG_TAG "LayerHistoryTest"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
-#include <mutex>
-#include <thread>
-
#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
using testing::_;
using testing::Return;
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
class LayerHistoryTest : public testing::Test {
-public:
- LayerHistoryTest();
- ~LayerHistoryTest() override;
-
protected:
- std::unique_ptr<LayerHistory> mLayerHistory;
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
- static constexpr float MIN_REFRESH_RATE = 30.f;
- static constexpr float MAX_REFRESH_RATE = 90.f;
- static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
- static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+ static constexpr float LO_FPS = 30.f;
+ static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
- void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
- mLayerHistory->setVisibility(testLayer, true);
- for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- }
- };
-};
+ static constexpr float HI_FPS = 90.f;
+ static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
-LayerHistoryTest::LayerHistoryTest() {
- mLayerHistory = std::make_unique<LayerHistory>();
-}
-LayerHistoryTest::~LayerHistoryTest() {}
+ LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
-namespace {
-TEST_F(LayerHistoryTest, oneLayer) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
- for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+
+ size_t layerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mLayerInfos.size(); }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+ size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+ [now](const auto& pair) { return pair.second->isFrequent(now); });
}
- // Add a few more. This time we should get MAX refresh rate as the layer
- // becomes relevant
- static constexpr auto A_FEW = 10;
- for (auto i = 0u; i < A_FEW; i++) {
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+ RefreshRateConfigs mConfigs{true,
+ {RefreshRateConfigs::InputConfig{0, LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{1, HI_FPS_PERIOD}},
+ 0};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
+ TestableSurfaceFlinger mFlinger;
+
+ const nsecs_t mTime = systemTime();
+};
+
+namespace {
+
+TEST_F(LayerHistoryTest, oneLayer) {
+ const auto layer = createLayer();
+ constexpr bool isHDR = false;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // 0 FPS is returned if no layers are active.
+ EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(0, activeLayerCount());
+
+ // 0 FPS is returned if active layers have insufficient history.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ history().record(layer.get(), 0, isHDR, mTime);
+ EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ }
+
+ // High FPS is returned once enough history has been recorded.
+ for (int i = 0; i < 10; i++) {
+ history().record(layer.get(), 0, isHDR, mTime);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
}
}
TEST_F(LayerHistoryTest, oneHDRLayer) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
+ const auto layer = createLayer();
+ constexpr bool isHDR = true;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
- EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- EXPECT_EQ(true, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
- mLayerHistory->setVisibility(testLayer, false);
- EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- EXPECT_EQ(false, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+ history().record(layer.get(), 0, isHDR, mTime);
+ auto summary = history().summarize(mTime);
+ EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
+ EXPECT_TRUE(summary.isHDR);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+ summary = history().summarize(mTime);
+ EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
+ EXPECT_FALSE(summary.isHDR);
+ EXPECT_EQ(0, activeLayerCount());
}
TEST_F(LayerHistoryTest, explicitTimestamp) {
- std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(test30FpsLayer, true);
+ const auto layer = createLayer();
+ constexpr bool isHDR = false;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- nsecs_t startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
}
TEST_F(LayerHistoryTest, multipleLayers) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
- std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(test30FpsLayer, true);
- std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
- mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer2, true);
+ auto layer1 = createLayer();
+ auto layer2 = createLayer();
+ auto layer3 = createLayer();
+ constexpr bool isHDR = false;
- nsecs_t startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+
+ nsecs_t time = mTime;
+
+ EXPECT_EQ(3, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer1 is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer1.get(), time, isHDR, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer2 is frequent and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ // layer1 is still active but infrequent.
+ history().record(layer1.get(), time, isHDR, time);
+
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer1 is no longer active.
+ // layer2 is frequent and has low refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // This frame is only around for 9 occurrences, so it doesn't throw
- // anything off.
- for (int i = 0; i < 9; i++) {
- mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 has high refresh rate but not enough history.
+ constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ if (i % RATIO == 0) {
+ history().record(layer2.get(), time, isHDR, time);
+ }
+
+ history().record(layer3.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // After 1200 ms frames become obsolete.
- std::this_thread::sleep_for(std::chrono::milliseconds(1500));
- mLayerHistory->insert(test30FpsLayer,
- startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
- EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ history().record(layer3.get(), time, isHDR, time);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ layer1.clear();
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, layerCount());
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 becomes inactive.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 expires.
+ layer2.clear();
+ EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer3 becomes active and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer3.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ layer3.clear();
+ EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(0, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
}
} // namespace
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index 160f041..d8de804 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -69,16 +69,16 @@
n++;
return pixel;
});
+
EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
- testing::FloatNear(0.083f, 0.01f));
+ testing::FloatNear(0.16f, 0.01f));
}
TEST_F(RegionSamplingTest, bimodal_tiebreaker) {
std::generate(buffer.begin(), buffer.end(),
[n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; });
- // presently there's no tiebreaking strategy in place, accept either of the means
EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
- testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f)));
+ testing::FloatEq(0.5f));
}
TEST_F(RegionSamplingTest, bounds_checking) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ae72467..ae6aa89 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -48,6 +48,7 @@
auto& mutableEventControlThread() { return mEventControlThread; }
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+ auto& mutableLayerHistory() { return mLayerHistory; }
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c7664fb..94fc5f7 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -22,6 +22,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/mock/DisplaySurface.h>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -173,6 +174,7 @@
class TestableSurfaceFlinger {
public:
+ SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
// Extend this as needed for accessing SurfaceFlinger private (and public)
@@ -207,13 +209,15 @@
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
+ resetScheduler(mScheduler);
- mFlinger->mScheduler.reset(mScheduler);
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
mFlinger->mPhaseOffsets->getCurrentOffsets());
}
+ void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
mFactory.mCreateBufferQueue = f;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
new file mode 100644
index 0000000..82950b5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -0,0 +1,681 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+ MockVSyncTracker(nsecs_t period) : mPeriod{period} {
+ ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+ .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+ }
+
+ MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+
+ nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+ if (timePoint % mPeriod == 0) {
+ return timePoint;
+ }
+ return (timePoint - (timePoint % mPeriod) + mPeriod);
+ }
+
+protected:
+ nsecs_t const mPeriod;
+};
+
+class ControllableClock : public TimeKeeper {
+public:
+ ControllableClock() {
+ ON_CALL(*this, alarmIn(_, _))
+ .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+ ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
+ }
+
+ MOCK_CONST_METHOD0(now, nsecs_t());
+ MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+ MOCK_METHOD0(alarmCancel, void());
+
+ void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+ mCallback = callback;
+ mNextCallbackTime = time + mCurrentTime;
+ }
+
+ nsecs_t fakeTime() const { return mCurrentTime; }
+
+ void advanceToNextCallback() {
+ mCurrentTime = mNextCallbackTime;
+ if (mCallback) {
+ mCallback();
+ }
+ }
+
+ void advanceBy(nsecs_t advancement) {
+ mCurrentTime += advancement;
+ if (mCurrentTime >= mNextCallbackTime && mCallback) {
+ mCallback();
+ }
+ };
+
+private:
+ std::function<void()> mCallback;
+ nsecs_t mNextCallbackTime = 0;
+ nsecs_t mCurrentTime = 0;
+};
+
+class CountingCallback {
+public:
+ CountingCallback(VSyncDispatch& dispatch)
+ : mDispatch(dispatch),
+ mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
+ std::placeholders::_1),
+ "test")) {}
+ ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+
+ operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+ void counter(nsecs_t time) { mCalls.push_back(time); }
+
+ VSyncDispatch& mDispatch;
+ VSyncDispatch::CallbackToken mToken;
+ std::vector<nsecs_t> mCalls;
+};
+
+class PausingCallback {
+public:
+ PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
+ : mDispatch(dispatch),
+ mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
+ std::placeholders::_1),
+ "test")),
+ mRegistered(true),
+ mPauseAmount(pauseAmount) {}
+ ~PausingCallback() { unregister(); }
+
+ operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+ void pause(nsecs_t) {
+ std::unique_lock<std::mutex> lk(mMutex);
+ mPause = true;
+ mCv.notify_all();
+
+ mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+
+ mResourcePresent = (mResource.lock() != nullptr);
+ }
+
+ bool waitForPause() {
+ std::unique_lock<std::mutex> lk(mMutex);
+ auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+ return waiting;
+ }
+
+ void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; }
+
+ bool resourcePresent() { return mResourcePresent; }
+
+ void unpause() {
+ std::unique_lock<std::mutex> lk(mMutex);
+ mPause = false;
+ mCv.notify_all();
+ }
+
+ void unregister() {
+ if (mRegistered) {
+ mDispatch.unregisterCallback(mToken);
+ mRegistered = false;
+ }
+ }
+
+ VSyncDispatch& mDispatch;
+ VSyncDispatch::CallbackToken mToken;
+ bool mRegistered = true;
+
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ bool mPause = false;
+ std::weak_ptr<void> mResource;
+ bool mResourcePresent = false;
+ std::chrono::milliseconds const mPauseAmount;
+};
+
+class VSyncDispatchTimerQueueTest : public testing::Test {
+protected:
+ std::unique_ptr<TimeKeeper> createTimeKeeper() {
+ class TimeKeeperWrapper : public TimeKeeper {
+ public:
+ TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
+ void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
+ mControllableClock.alarmIn(callback, time);
+ }
+ void alarmCancel() final { mControllableClock.alarmCancel(); }
+ nsecs_t now() const final { return mControllableClock.now(); }
+
+ private:
+ TimeKeeper& mControllableClock;
+ };
+ return std::make_unique<TimeKeeperWrapper>(mMockClock);
+ }
+
+ ~VSyncDispatchTimerQueueTest() {
+ // destructor of dispatch will cancelAlarm(). Ignore final cancel in common test.
+ Mock::VerifyAndClearExpectations(&mMockClock);
+ }
+
+ void advanceToNextCallback() { mMockClock.advanceToNextCallback(); }
+
+ NiceMock<ControllableClock> mMockClock;
+ static nsecs_t constexpr mDispatchGroupThreshold = 5;
+ nsecs_t const mPeriod = 1000;
+ NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+};
+
+TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmCancel());
+ {
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
+ mDispatchGroupThreshold};
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+ }
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
+ auto intended = mPeriod - 230;
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+ EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+
+ CountingCallback cb(mDispatch);
+ mDispatch.schedule(cb, 100, mPeriod);
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(1150));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) {
+ auto const now = 234;
+ mMockClock.advanceBy(234);
+ auto const workDuration = 10 * mPeriod;
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+ .WillOnce(Return(mPeriod * 11));
+ EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmCancel());
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmCancel());
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(950);
+ EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmCancel());
+
+ PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
+ EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+ std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+ EXPECT_TRUE(cb.waitForPause());
+ EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+ cb.unpause();
+ pausingThread.join();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmCancel());
+
+ auto resource = std::make_shared<int>(110);
+
+ PausingCallback cb(mDispatch, 50ms);
+ cb.stashResource(resource);
+ EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+ std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+ EXPECT_TRUE(cb.waitForPause());
+
+ cb.unregister();
+ resource.reset();
+
+ cb.unpause();
+ pausingThread.join();
+
+ EXPECT_TRUE(cb.resourcePresent());
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .Times(4)
+ .WillOnce(Return(1055))
+ .WillOnce(Return(1063))
+ .WillOnce(Return(1063))
+ .WillOnce(Return(1075));
+
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 100, mPeriod);
+ mDispatch.schedule(cb1, 250, mPeriod);
+
+ advanceToNextCallback();
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb0.mCalls[0], Eq(1075));
+ ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb1.mCalls[0], Eq(1063));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(4)
+ .WillOnce(Return(10000))
+ .WillOnce(Return(1000))
+ .WillOnce(Return(10000))
+ .WillOnce(Return(10000));
+
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 100, mPeriod * 10);
+ mDispatch.schedule(cb1, 250, mPeriod);
+ mDispatch.cancel(cb1);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 400, 1000);
+ mDispatch.schedule(cb1, 200, 1000);
+ mDispatch.schedule(cb1, 300, 1000);
+ advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 400, 1000);
+ mDispatch.schedule(cb1, 200, 1000);
+ mDispatch.schedule(cb1, 500, 1000);
+ advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+
+ auto offset = 400;
+ auto closeOffset = offset + mDispatchGroupThreshold - 1;
+ auto notCloseOffset = offset + 2 * mDispatchGroupThreshold;
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 400, 1000);
+ mDispatch.schedule(cb1, 200, 1000);
+ mDispatch.schedule(cb1, closeOffset, 1000);
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod));
+ ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
+
+ mDispatch.schedule(cb0, 400, 2000);
+ mDispatch.schedule(cb1, notCloseOffset, 2000);
+ advanceToNextCallback();
+ ASSERT_THAT(cb1.mCalls.size(), Eq(2));
+ EXPECT_THAT(cb1.mCalls[1], Eq(2000));
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb0.mCalls.size(), Eq(2));
+ EXPECT_THAT(cb0.mCalls[1], Eq(2000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmIn(_, 800));
+ EXPECT_CALL(mMockClock, alarmIn(_, 100));
+ EXPECT_CALL(mMockClock, alarmCancel());
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 100, 1000);
+ mDispatch.schedule(cb1, 200, 1000);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(3)
+ .WillOnce(Return(950))
+ .WillOnce(Return(1975))
+ .WillOnce(Return(2950));
+
+ CountingCallback cb(mDispatch);
+ mDispatch.schedule(cb, 100, 920);
+
+ mMockClock.advanceBy(850);
+ EXPECT_THAT(cb.mCalls.size(), Eq(1));
+
+ mDispatch.schedule(cb, 100, 1900);
+ mMockClock.advanceBy(900);
+ EXPECT_THAT(cb.mCalls.size(), Eq(1));
+ mMockClock.advanceBy(125);
+ EXPECT_THAT(cb.mCalls.size(), Eq(2));
+
+ mDispatch.schedule(cb, 100, 2900);
+ mMockClock.advanceBy(975);
+ EXPECT_THAT(cb.mCalls.size(), Eq(3));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+
+ VSyncDispatch::CallbackToken tmp;
+ tmp = mDispatch.registerCallback([&](auto) { mDispatch.schedule(tmp, 100, 2000); }, "o.o");
+
+ mDispatch.schedule(tmp, 100, 1000);
+ advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
+ VSyncDispatch::CallbackToken tmp;
+ tmp = mDispatch.registerCallback(
+ [&](auto) {
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+ },
+ "oo");
+
+ mDispatch.schedule(tmp, 999, 1000);
+ advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+
+ CountingCallback cb(mDispatch);
+ mDispatch.schedule(cb, 0, 1000);
+
+ mMockClock.advanceBy(750);
+ mDispatch.schedule(cb, 50, 1000);
+
+ advanceToNextCallback();
+ mDispatch.schedule(cb, 50, 2000);
+
+ mMockClock.advanceBy(800);
+ mDispatch.schedule(cb, 100, 2000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+
+ mDispatch.schedule(cb0, 500, 1000);
+ mDispatch.schedule(cb1, 100, 1000);
+
+ advanceToNextCallback();
+ mDispatch.schedule(cb0, 200, 2000);
+ mDispatch.schedule(cb1, 150, 1000);
+
+ advanceToNextCallback();
+ advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ CountingCallback cb1(mDispatch);
+ mDispatch.schedule(cb0, 500, 1000);
+ mDispatch.schedule(cb1, 500, 20000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+
+ CountingCallback cb0(mDispatch);
+ mDispatch.schedule(cb0, 500, 1000);
+ mDispatch.cancel(cb0);
+ mDispatch.schedule(cb0, 100, 1000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
+ VSyncDispatch::CallbackToken token(100);
+ EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+ EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
+ CountingCallback cb0(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+ VSyncCallbackRegistration cb(
+ mDispatch, [](auto) {}, "");
+ VSyncCallbackRegistration cb1(std::move(cb));
+ cb.schedule(100, 1000);
+ cb.cancel();
+
+ cb1.schedule(500, 1000);
+ cb1.cancel();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+ VSyncCallbackRegistration cb(
+ mDispatch, [](auto) {}, "");
+ VSyncCallbackRegistration cb1(
+ mDispatch, [](auto) {}, "");
+ cb1 = std::move(cb);
+ cb.schedule(100, 1000);
+ cb.cancel();
+
+ cb1.schedule(500, 1000);
+ cb1.cancel();
+}
+
+class VSyncDispatchTimerQueueEntryTest : public testing::Test {
+protected:
+ nsecs_t const mPeriod = 1000;
+ NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+};
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
+ std::string name("basicname");
+ VSyncDispatchTimerQueueEntry entry(name, [](auto) {});
+ EXPECT_THAT(entry.name(), Eq(name));
+ EXPECT_FALSE(entry.lastExecutedVsyncTarget());
+ EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+
+ EXPECT_FALSE(entry.wakeupTime());
+ auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ auto const queried = entry.wakeupTime();
+ ASSERT_TRUE(queried);
+ EXPECT_THAT(*queried, Eq(wakeup));
+ EXPECT_THAT(*queried, Eq(900));
+
+ entry.disarm();
+ EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) {
+ auto const duration = 500;
+ auto const now = 8750;
+
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+ .Times(1)
+ .WillOnce(Return(10000));
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+
+ EXPECT_FALSE(entry.wakeupTime());
+ auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
+ auto const queried = entry.wakeupTime();
+ ASSERT_TRUE(queried);
+ EXPECT_THAT(*queried, Eq(wakeup));
+ EXPECT_THAT(*queried, Eq(9500));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
+ auto callCount = 0;
+ auto calledTime = 0;
+ VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
+ callCount++;
+ calledTime = time;
+ });
+
+ auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(wakeup, Eq(900));
+
+ entry.callback(entry.executing());
+
+ EXPECT_THAT(callCount, Eq(1));
+ EXPECT_THAT(calledTime, Eq(mPeriod));
+ EXPECT_FALSE(entry.wakeupTime());
+ auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+ ASSERT_TRUE(lastCalledTarget);
+ EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(2)
+ .WillOnce(Return(1000))
+ .WillOnce(Return(1020));
+
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+
+ EXPECT_FALSE(entry.wakeupTime());
+ entry.update(mStubTracker, 0);
+ EXPECT_FALSE(entry.wakeupTime());
+
+ auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(wakeup, Eq(900));
+
+ entry.update(mStubTracker, 0);
+ auto const queried = entry.wakeupTime();
+ ASSERT_TRUE(queried);
+ EXPECT_THAT(*queried, Eq(920));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ entry.update(mStubTracker, 0);
+
+ auto const queried = entry.wakeupTime();
+ ASSERT_TRUE(queried);
+ EXPECT_THAT(*queried, Eq(wakeup));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
new file mode 100644
index 0000000..f375e23
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "Layer.h"
+
+namespace android::mock {
+
+class MockLayer : public Layer {
+public:
+ explicit MockLayer(SurfaceFlinger* flinger)
+ : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+
+ MOCK_CONST_METHOD0(getType, const char*());
+ MOCK_CONST_METHOD0(isVisible, bool());
+ MOCK_METHOD0(createClone, sp<Layer>());
+};
+
+} // namespace android::mock
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5686891..018f200 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -41,12 +41,13 @@
"-DVK_NO_PROTOTYPES",
"-fvisibility=hidden",
"-fstrict-aliasing",
- "-Weverything",
+ "-Wextra",
"-Werror",
"-Wno-padded",
+ "-Wno-sign-compare",
"-Wno-switch-enum",
- "-Wno-undef",
- "-Wno-format-pedantic",
+ "-Wno-unused-variable",
+ "-Wno-unused-function",
// Have clang emit complete debug_info.
"-fstandalone-debug",
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index a1207d3..7e988a1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -145,7 +145,7 @@
}
void GetLayersFromSettings() {
- // These will only be available if conditions are met in GraphicsEnvironemnt
+ // These will only be available if conditions are met in GraphicsEnvironment
// gpu_debug_layers = layer1:layer2:layerN
const std::string layers = android::GraphicsEnv::getInstance().getDebugLayers();
if (!layers.empty()) {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index c1994f9..90a73e2 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -151,15 +151,12 @@
Hal Hal::hal_;
void* LoadLibrary(const android_dlextinfo& dlextinfo,
- const char* subname,
- int subname_len) {
+ const std::string_view subname) {
ATRACE_CALL();
- const char kLibFormat[] = "vulkan.%*s.so";
- char* name = static_cast<char*>(
- alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len)));
- sprintf(name, kLibFormat, subname_len, subname);
- return android_dlopen_ext(name, RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ std::stringstream ss;
+ ss << "vulkan." << subname << ".so";
+ return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
}
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
@@ -179,8 +176,9 @@
char prop[PROPERTY_VALUE_MAX];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
int prop_len = property_get(key, prop, nullptr);
- if (prop_len > 0) {
- so = LoadLibrary(dlextinfo, prop, prop_len);
+ if (prop_len > 0 && prop_len <= UINT_MAX) {
+ std::string_view lib_name(prop, static_cast<unsigned int>(prop_len));
+ so = LoadLibrary(dlextinfo, lib_name);
if (so)
break;
}
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 3ebfd7b..a14fed2 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -95,9 +95,7 @@
bool EnumerateLayers(size_t library_idx,
std::vector<Layer>& instance_layers) const;
- void* GetGPA(const Layer& layer,
- const char* gpa_name,
- size_t gpa_name_len) const;
+ void* GetGPA(const Layer& layer, const std::string_view gpa_name) const;
const std::string GetFilename() { return filename_; }
@@ -220,9 +218,8 @@
}
// get layer properties
- VkLayerProperties* properties = static_cast<VkLayerProperties*>(alloca(
- (num_instance_layers + num_device_layers) * sizeof(VkLayerProperties)));
- result = enumerate_instance_layers(&num_instance_layers, properties);
+ std::vector<VkLayerProperties> properties(num_instance_layers + num_device_layers);
+ result = enumerate_instance_layers(&num_instance_layers, properties.data());
if (result != VK_SUCCESS) {
ALOGE("vkEnumerateInstanceLayerProperties failed for library '%s': %d",
path_.c_str(), result);
@@ -230,7 +227,7 @@
}
if (num_device_layers > 0) {
result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers,
- properties + num_instance_layers);
+ &properties[num_instance_layers]);
if (result != VK_SUCCESS) {
ALOGE(
"vkEnumerateDeviceLayerProperties failed for library '%s': %d",
@@ -315,21 +312,11 @@
return true;
}
-void* LayerLibrary::GetGPA(const Layer& layer,
- const char* gpa_name,
- size_t gpa_name_len) const {
- void* gpa;
- size_t layer_name_len =
- std::max(size_t{2}, strlen(layer.properties.layerName));
- char* name = static_cast<char*>(alloca(layer_name_len + gpa_name_len + 1));
- strcpy(name, layer.properties.layerName);
- strcpy(name + layer_name_len, gpa_name);
- if (!(gpa = GetTrampoline(name))) {
- strcpy(name, "vk");
- strcpy(name + 2, gpa_name);
- gpa = GetTrampoline(name);
- }
- return gpa;
+void* LayerLibrary::GetGPA(const Layer& layer, const std::string_view gpa_name) const {
+ std::string layer_name { layer.properties.layerName };
+ if (void* gpa = GetTrampoline((layer_name.append(gpa_name).c_str())))
+ return gpa;
+ return GetTrampoline((std::string {"vk"}.append(gpa_name)).c_str());
}
// ----------------------------------------------------------------------------
@@ -464,10 +451,9 @@
}
void* GetLayerGetProcAddr(const Layer& layer,
- const char* gpa_name,
- size_t gpa_name_len) {
+ const std::string_view gpa_name) {
const LayerLibrary& library = g_layer_libraries[layer.library_idx];
- return library.GetGPA(layer, gpa_name, gpa_name_len);
+ return library.GetGPA(layer, gpa_name);
}
} // anonymous namespace
@@ -549,13 +535,13 @@
PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const {
return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>(
- GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr", 19))
+ GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr"))
: nullptr;
}
PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const {
return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>(
- GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr", 17))
+ GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr"))
: nullptr;
}
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
index dedf419..ba02504 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -23,18 +23,11 @@
"-fvisibility=hidden",
"-fstrict-aliasing",
"-DLOG_TAG=\"vknulldrv\"",
- "-Weverything",
+ "-Wextra",
"-Werror",
- "-Wno-padded",
- "-Wno-undef",
- "-Wno-zero-length-array",
"-DLOG_NDEBUG=0",
],
- cppflags: [
- "-Wno-c++98-compat-pedantic",
- "-Wno-c99-extensions",
- ],
srcs: [
"null_driver.cpp",