Merge changes from topic "gles_layers_updates"
* changes:
Initial readme doc for GLES layers
Add initial draft of EGL_ANDROID_GLES_layers.txt
More verbose debugging for GLES layers
Advertise EGL_ANDROID_GLES_layers extension
Update entrypoint names for GLES layers
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1fbd2b3..12de2d2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2735,8 +2735,8 @@
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
- consent_callback_.get());
+ calling_uid, calling_package, String16(), String16(),
+ 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 92fac66..c730ab9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -171,7 +171,7 @@
}
binder::Status checkArgumentPackageName(const std::string& packageName) {
- if (is_valid_package_name(packageName.c_str())) {
+ if (is_valid_package_name(packageName)) {
return ok();
} else {
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 20142aa..a5cc0df 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1564,7 +1564,7 @@
if (vdex_fd >= 0) {
AddArg(vdex_fd_arg);
}
- AddArg(zip_fd_arg.c_str());
+ AddArg(zip_fd_arg);
if (profile_was_updated) {
AddArg(assume_profile_changed);
}
@@ -1572,9 +1572,9 @@
AddArg(downgrade_flag);
}
if (class_loader_context != nullptr) {
- AddArg(class_loader_context_arg.c_str());
+ AddArg(class_loader_context_arg);
if (!class_loader_context_fds.empty()) {
- AddArg(class_loader_context_fds_arg.c_str());
+ AddArg(class_loader_context_fds_arg);
}
}
@@ -2259,7 +2259,7 @@
drop_capabilities(uid);
const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
_exit(kReconcileSecondaryDexValidationError);
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 1ed49a0..aa79fdc 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -15,6 +15,7 @@
"libinstalld",
"liblog",
],
+ test_config: "installd_utils_test.xml",
}
cc_test {
@@ -39,6 +40,7 @@
"liblog",
"liblogwrap",
],
+ test_config: "installd_cache_test.xml",
}
cc_test {
@@ -63,6 +65,7 @@
"liblog",
"liblogwrap",
],
+ test_config: "installd_service_test.xml",
}
cc_test {
@@ -87,6 +90,7 @@
"liblog",
"liblogwrap",
],
+ test_config: "installd_dexopt_test.xml",
}
cc_test {
diff --git a/cmds/installd/tests/installd_cache_test.xml b/cmds/installd/tests/installd_cache_test.xml
new file mode 100644
index 0000000..97af514
--- /dev/null
+++ b/cmds/installd/tests/installd_cache_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_cache_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_cache_test->/data/local/tmp/installd_cache_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_cache_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 36d5a60..fa2b0d9 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -936,8 +936,7 @@
protected:
void TransitionToSystemServer() {
ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid));
- int32_t res = selinux_android_setcontext(
- kSystemUid, true, se_info_.c_str(), "system_server");
+ int32_t res = selinux_android_setcon("u:r:system_server:s0");
ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno);
}
diff --git a/cmds/installd/tests/installd_dexopt_test.xml b/cmds/installd/tests/installd_dexopt_test.xml
new file mode 100644
index 0000000..24526cc
--- /dev/null
+++ b/cmds/installd/tests/installd_dexopt_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_dexopt_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_dexopt_test->/data/local/tmp/installd_dexopt_test" />
+ </target_preparer>
+
+ <!-- The test runs as root to prepare the temporary directory, make selinux adjustments
+ and so on. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_dexopt_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_service_test.xml b/cmds/installd/tests/installd_service_test.xml
new file mode 100644
index 0000000..b838f4f
--- /dev/null
+++ b/cmds/installd/tests/installd_service_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_service_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_service_test->/data/local/tmp/installd_service_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_service_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_utils_test.xml b/cmds/installd/tests/installd_utils_test.xml
new file mode 100644
index 0000000..92aba50
--- /dev/null
+++ b/cmds/installd/tests/installd_utils_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_utils_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_utils_test->/data/local/tmp/installd_utils_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access (rollback paths). -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_utils_test" />
+ </test>
+</configuration>
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 71a8f20..7f04611 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -50,19 +50,19 @@
class InputApplicationHandle : public RefBase {
public:
inline const InputApplicationInfo* getInfo() const {
- return mInfo;
+ return &mInfo;
}
inline std::string getName() const {
- return mInfo ? mInfo->name : "<invalid>";
+ return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
- return mInfo ? mInfo->token : nullptr;
+ return mInfo.token;
}
/**
@@ -75,18 +75,11 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
- InputApplicationInfo* mInfo;
+ InputApplicationInfo mInfo;
};
} // namespace android
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
new file mode 100644
index 0000000..b200314
--- /dev/null
+++ b/libs/android_runtime_lazy/Android.bp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// libandroid_runtime_lazy is a shim library.
+// This provides very limited small set of APIs from libandroid_runtime.
+//
+// By depending on this instead of libandroid_runtime,
+// a library can be loaded without paying the cost of libandroid_runtime
+// which is quite huge. The cost will be paid when libandroid_runtime is actually used.
+//
+// For Partial-source PDK build, there is a constraint that
+// frameworks/native modules should not depend on frameworks/base.
+// This library can be used to cut down the dependency between them.
+// (e.g. libbinder_ndk)
+//
+// Some libraries which serve as LL-NDK and NDK as well may depend on this
+// instead of libandroid_runtime. When they are used by a vendor process,
+// depending on libandroid_runtime is meaningless. In this case,
+// they can depend on libandroid_runtime_lazy.
+cc_library {
+ name: "libandroid_runtime_lazy",
+ vendor_available: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ srcs: [
+ "android_runtime_lazy.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ required: [
+ "libandroid_runtime",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "jni_headers",
+ "libbinder_headers",
+ ],
+}
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
new file mode 100644
index 0000000..98d8e8a
--- /dev/null
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "ANDROID_RUNTIME_LAZY"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
+
+#include <dlfcn.h>
+#include <mutex>
+
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+std::once_flag loadFlag;
+
+typedef JNIEnv* (*getJNIEnv_t)();
+typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
+typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
+
+getJNIEnv_t _getJNIEnv;
+ibinderForJavaObject_t _ibinderForJavaObject;
+javaObjectForIBinder_t _javaObjectForIBinder;
+
+void load() {
+ std::call_once(loadFlag, []() {
+ void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
+ if (handle == nullptr) {
+ ALOGE("Could not open libandroid_runtime.");
+ return;
+ }
+
+ _getJNIEnv = reinterpret_cast<getJNIEnv_t>(
+ dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv"));
+ if (_getJNIEnv == nullptr) {
+ ALOGW("Could not find getJNIEnv.");
+ // no return
+ }
+
+ _ibinderForJavaObject = reinterpret_cast<ibinderForJavaObject_t>(
+ dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (_ibinderForJavaObject == nullptr) {
+ ALOGW("Could not find ibinderForJavaObject.");
+ // no return
+ }
+
+ _javaObjectForIBinder = reinterpret_cast<javaObjectForIBinder_t>(
+ dlsym(handle,
+ "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
+ if (_javaObjectForIBinder == nullptr) {
+ ALOGW("Could not find javaObjectForIBinder.");
+ // no return
+ }
+ });
+}
+
+} // namespace
+
+// exports delegate functions
+
+JNIEnv* AndroidRuntime::getJNIEnv() {
+ load();
+ if (_getJNIEnv == nullptr) {
+ return nullptr;
+ }
+ return _getJNIEnv();
+}
+
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
+ load();
+ if (_ibinderForJavaObject == nullptr) {
+ return nullptr;
+ }
+ return _ibinderForJavaObject(env, obj);
+}
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
+ load();
+ if (_javaObjectForIBinder == nullptr) {
+ return nullptr;
+ }
+ return _javaObjectForIBinder(env, val);
+}
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
new file mode 100644
index 0000000..85231fa
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "jni.h"
+
+namespace android {
+
+// Intentionally use the same name with AndroidRuntime in frameworks/base/core/jni/
+// to make the client use this in the same way with the original class.
+class AndroidRuntime {
+public:
+ static JNIEnv* getJNIEnv();
+};
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_util_Binder.h b/libs/android_runtime_lazy/include/android_util_Binder.h
new file mode 100644
index 0000000..e47390e
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_util_Binder.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+#include "jni.h"
+
+namespace android {
+
+// The name of this file is same with the file in frameworks/base/core/jni/
+// This is intentional to make the client use these exported functions
+// in the same way with the original.
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val);
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a96c9a0..21bef2e 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,17 +39,12 @@
],
shared_libs: [
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
],
- required: [
- // libbinder_ndk may be used by Java and non-Java things. When lower-level things use it,
- // they shouldn't have to take on the cost of loading libandroid_runtime.
- "libandroid_runtime",
- ],
-
header_libs: [
"jni_headers",
],
diff --git a/libs/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp
index 4a31080..d931785 100644
--- a/libs/binder/ndk/ibinder_jni.cpp
+++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -17,69 +17,19 @@
#include <android/binder_ibinder_jni.h>
#include "ibinder_internal.h"
-#include <android-base/logging.h>
-#include <binder/IBinder.h>
-
-#include <mutex>
-
-#include <dlfcn.h>
+#include <android_util_Binder.h>
using ::android::IBinder;
+using ::android::ibinderForJavaObject;
+using ::android::javaObjectForIBinder;
using ::android::sp;
-struct LazyAndroidRuntime {
- typedef sp<IBinder> (*FromJava)(JNIEnv* env, jobject obj);
- typedef jobject (*ToJava)(JNIEnv* env, const sp<IBinder>& val);
-
- static FromJava ibinderForJavaObject;
- static ToJava javaObjectForIBinder;
-
- static void load() {
- std::call_once(mLoadFlag, []() {
- void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
- if (handle == nullptr) {
- LOG(WARNING) << "Could not open libandroid_runtime.";
- return;
- }
-
- ibinderForJavaObject = reinterpret_cast<FromJava>(
- dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
- if (ibinderForJavaObject == nullptr) {
- LOG(WARNING) << "Could not find ibinderForJavaObject.";
- // no return
- }
-
- javaObjectForIBinder = reinterpret_cast<ToJava>(dlsym(
- handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
- if (javaObjectForIBinder == nullptr) {
- LOG(WARNING) << "Could not find javaObjectForIBinder.";
- // no return
- }
- });
- }
-
- private:
- static std::once_flag mLoadFlag;
-
- LazyAndroidRuntime(){};
-};
-
-LazyAndroidRuntime::FromJava LazyAndroidRuntime::ibinderForJavaObject = nullptr;
-LazyAndroidRuntime::ToJava LazyAndroidRuntime::javaObjectForIBinder = nullptr;
-std::once_flag LazyAndroidRuntime::mLoadFlag;
-
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
if (binder == nullptr) {
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::ibinderForJavaObject == nullptr) {
- return nullptr;
- }
-
- sp<IBinder> ibinder = (LazyAndroidRuntime::ibinderForJavaObject)(env, binder);
-
+ sp<IBinder> ibinder = ibinderForJavaObject(env, binder);
sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder);
AIBinder_incStrong(cbinder.get());
@@ -91,10 +41,5 @@
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::javaObjectForIBinder == nullptr) {
- return nullptr;
- }
-
- return (LazyAndroidRuntime::javaObjectForIBinder)(env, binder->getBinder());
+ return javaObjectForIBinder(env, binder->getBinder());
}
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index b29b6e7..8cd4e03 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -41,7 +41,7 @@
name: "test_libbinder_ndk_test_defaults",
defaults: ["test_libbinder_ndk_defaults"],
shared_libs: [
- "libandroid_runtime",
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 247dc8d..bc63d31 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -68,7 +68,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& commands,
- int64_t desiredPresentTime) {
+ int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -86,6 +87,8 @@
data.writeStrongBinder(applyToken);
commands.write(data);
data.writeInt64(desiredPresentTime);
+ data.writeStrongBinder(uncacheBuffer.token);
+ data.writeUint64(uncacheBuffer.cacheId);
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -970,8 +973,13 @@
inputWindowCommands.read(data);
int64_t desiredPresentTime = data.readInt64();
+
+ cached_buffer_t uncachedBuffer;
+ uncachedBuffer.token = data.readStrongBinder();
+ uncachedBuffer.cacheId = data.readUint64();
+
setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime);
+ desiredPresentTime, uncachedBuffer);
return NO_ERROR;
}
case BOOT_FINISHED: {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 84ba644..f6ca9e8 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -95,7 +95,7 @@
}
output.writeStrongBinder(cachedBuffer.token);
- output.writeInt32(cachedBuffer.bufferId);
+ output.writeUint64(cachedBuffer.cacheId);
output.writeParcelable(metadata);
output.writeFloat(bgColorAlpha);
@@ -173,7 +173,7 @@
}
cachedBuffer.token = input.readStrongBinder();
- cachedBuffer.bufferId = input.readInt32();
+ cachedBuffer.cacheId = input.readUint64();
input.readParcelable(&metadata);
bgColorAlpha = input.readFloat();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b0e8275..39cd62f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -233,6 +233,8 @@
// ---------------------------------------------------------------------------
+void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId);
+
class BufferCache : public Singleton<BufferCache> {
public:
BufferCache() : token(new BBinder()) {}
@@ -241,77 +243,57 @@
return IInterface::asBinder(TransactionCompletedListener::getIInstance());
}
- int32_t getId(const sp<GraphicBuffer>& buffer) {
+ status_t getCacheId(const sp<GraphicBuffer>& buffer, uint64_t* cacheId) {
std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mBuffers.find(buffer);
+ auto itr = mBuffers.find(buffer->getId());
if (itr == mBuffers.end()) {
- return -1;
+ return BAD_VALUE;
}
- itr->second.counter = getCounter();
- return itr->second.id;
+ itr->second = getCounter();
+ *cacheId = buffer->getId();
+ return NO_ERROR;
}
- int32_t cache(const sp<GraphicBuffer>& buffer) {
+ uint64_t cache(const sp<GraphicBuffer>& buffer) {
std::lock_guard<std::mutex> lock(mMutex);
- int32_t bufferId = getNextAvailableId();
+ if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) {
+ evictLeastRecentlyUsedBuffer();
+ }
- mBuffers[buffer].id = bufferId;
- mBuffers[buffer].counter = getCounter();
- return bufferId;
+ buffer->addDeathCallback(bufferCacheCallback, nullptr);
+
+ mBuffers[buffer->getId()] = getCounter();
+ return buffer->getId();
+ }
+
+ void uncache(uint64_t cacheId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ uncacheLocked(cacheId);
+ }
+
+ void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) {
+ mBuffers.erase(cacheId);
+ SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
}
private:
- int32_t evictDestroyedBuffer() REQUIRES(mMutex) {
+ void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
auto itr = mBuffers.begin();
- while (itr != mBuffers.end()) {
- auto& buffer = itr->first;
- if (buffer == nullptr || buffer.promote() == nullptr) {
- int32_t bufferId = itr->second.id;
- mBuffers.erase(itr);
- return bufferId;
- }
- itr++;
- }
- return -1;
- }
-
- int32_t evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
- if (mBuffers.size() < 0) {
- return -1;
- }
- auto itr = mBuffers.begin();
- uint64_t minCounter = itr->second.counter;
+ uint64_t minCounter = itr->second;
auto minBuffer = itr;
itr++;
while (itr != mBuffers.end()) {
- uint64_t counter = itr->second.counter;
+ uint64_t counter = itr->second;
if (counter < minCounter) {
minCounter = counter;
minBuffer = itr;
}
itr++;
}
- int32_t minBufferId = minBuffer->second.id;
- mBuffers.erase(minBuffer);
- return minBufferId;
- }
-
- int32_t getNextAvailableId() REQUIRES(mMutex) {
- static int32_t id = 0;
- if (id + 1 < BUFFER_CACHE_MAX_SIZE) {
- return id++;
- }
-
- // There are no more valid cache ids. To set additional buffers, evict existing buffers
- // and reuse their cache ids.
- int32_t bufferId = evictDestroyedBuffer();
- if (bufferId > 0) {
- return bufferId;
- }
- return evictLeastRecentlyUsedBuffer();
+ uncacheLocked(minBuffer->first);
}
uint64_t getCounter() REQUIRES(mMutex) {
@@ -319,18 +301,8 @@
return counter++;
}
- struct Metadata {
- // The cache id of a buffer that can be set to ISurfaceComposer. When ISurfaceComposer
- // recieves this id, it can retrieve the buffer from its cache. Caching GraphicBuffers
- // is important because sending them across processes is expensive.
- int32_t id = 0;
- // When a buffer is set, a counter is incremented and stored in the cache's metadata.
- // When an buffer must be evicted, the entry with the lowest counter value is chosen.
- uint64_t counter = 0;
- };
-
std::mutex mMutex;
- std::map<wp<GraphicBuffer>, Metadata> mBuffers GUARDED_BY(mMutex);
+ std::map<uint64_t /*Cache id*/, uint64_t /*counter*/> mBuffers GUARDED_BY(mMutex);
// Used by ISurfaceComposer to identify which process is sending the cached buffer.
sp<IBinder> token;
@@ -338,6 +310,11 @@
ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
+void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId) {
+ // GraphicBuffer id's are used as the cache ids.
+ BufferCache::getInstance().uncache(graphicBufferId);
+}
+
// ---------------------------------------------------------------------------
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
@@ -385,6 +362,9 @@
mInputWindowCommands.merge(other.mInputWindowCommands);
other.mInputWindowCommands.clear();
+ mContainsBuffer = other.mContainsBuffer;
+ other.mContainsBuffer = false;
+
return *this;
}
@@ -401,7 +381,50 @@
s.state.parentHandleForChild = nullptr;
composerStates.add(s);
- sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
+ sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1, {});
+}
+
+void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+ cached_buffer_t uncacheBuffer;
+ uncacheBuffer.token = BufferCache::getInstance().getToken();
+ uncacheBuffer.cacheId = cacheId;
+
+ sf->setTransactionState({}, {}, 0, nullptr, {}, -1, uncacheBuffer);
+}
+
+void SurfaceComposerClient::Transaction::cacheBuffers() {
+ if (!mContainsBuffer) {
+ return;
+ }
+
+ size_t count = 0;
+ for (auto& [sc, cs] : mComposerStates) {
+ layer_state_t* s = getLayerState(sc);
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ continue;
+ }
+
+ uint64_t cacheId = 0;
+ status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
+ if (ret == NO_ERROR) {
+ s->what &= ~static_cast<uint32_t>(layer_state_t::eBufferChanged);
+ s->buffer = nullptr;
+ } else {
+ cacheId = BufferCache::getInstance().cache(s->buffer);
+ }
+ s->what |= layer_state_t::eCachedBufferChanged;
+ s->cachedBuffer.token = BufferCache::getInstance().getToken();
+ s->cachedBuffer.cacheId = cacheId;
+
+ // If we have more buffers than the size of the cache, we should stop caching so we don't
+ // evict other buffers in this transaction
+ count++;
+ if (count >= BUFFER_CACHE_MAX_SIZE) {
+ break;
+ }
+ }
}
status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
@@ -437,6 +460,8 @@
}
mListenerCallbacks.clear();
+ cacheBuffers();
+
Vector<ComposerState> composerStates;
Vector<DisplayState> displayStates;
uint32_t flags = 0;
@@ -468,7 +493,8 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
- mDesiredPresentTime);
+ mDesiredPresentTime,
+ {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
@@ -882,20 +908,12 @@
mStatus = BAD_INDEX;
return *this;
}
-
- int32_t bufferId = BufferCache::getInstance().getId(buffer);
- if (bufferId < 0) {
- bufferId = BufferCache::getInstance().cache(buffer);
-
- s->what |= layer_state_t::eBufferChanged;
- s->buffer = buffer;
- }
-
- s->what |= layer_state_t::eCachedBufferChanged;
- s->cachedBuffer.token = BufferCache::getInstance().getToken();
- s->cachedBuffer.bufferId = bufferId;
+ s->what |= layer_state_t::eBufferChanged;
+ s->buffer = buffer;
registerSurfaceControlForCallback(sc);
+
+ mContainsBuffer = true;
return *this;
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8f54fee..0ef5b39 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -40,6 +40,7 @@
namespace android {
// ----------------------------------------------------------------------------
+struct cached_buffer_t;
struct ComposerState;
struct DisplayState;
struct DisplayInfo;
@@ -131,7 +132,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime) = 0;
+ int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 35e795c..77bf8f1 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -41,6 +41,11 @@
class Parcel;
class ISurfaceComposerClient;
+struct cached_buffer_t {
+ sp<IBinder> token = nullptr;
+ uint64_t cacheId;
+};
+
/*
* Used to communicate layer information between SurfaceFlinger and its clients.
*/
@@ -133,10 +138,6 @@
float dtdy{0};
float dsdy{0};
};
- struct cached_buffer_t {
- sp<IBinder> token = nullptr;
- int32_t bufferId = -1;
- };
sp<IBinder> surface;
uint64_t what;
float x;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 39d6d13..593a5e7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -165,6 +165,12 @@
static void doDropReferenceTransaction(const sp<IBinder>& handle,
const sp<ISurfaceComposerClient>& client);
+ /**
+ * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
+ * in order with other transactions that use buffers.
+ */
+ static void doUncacheBufferTransaction(uint64_t cacheId);
+
// Queries whether a given display is wide color display.
static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
@@ -279,6 +285,9 @@
bool mAnimation = false;
bool mEarlyWakeup = false;
+ // Indicates that the Transaction contains a buffer that should be cached
+ bool mContainsBuffer = false;
+
// mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
// to be presented. When it is not possible to present at exactly that time, it will be
// presented after the time has passed.
@@ -297,6 +306,7 @@
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
DisplayState& getDisplayState(const sp<IBinder>& token);
+ void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
public:
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 06fe86c..94b669d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -560,7 +560,8 @@
const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/) override {}
+ int64_t /*desiredPresentTime*/,
+ const cached_buffer_t& /*cachedBuffer*/) override {}
void bootFinished() override {}
bool authenticateSurfaceTexture(
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
index 45eab00..63411b9 100644
--- a/libs/incidentcompanion/Android.bp
+++ b/libs/incidentcompanion/Android.bp
@@ -35,8 +35,12 @@
},
srcs: [
":incidentcompanion_aidl",
+ "src/IncidentManager.cpp",
],
- export_include_dirs: ["binder"],
+ export_include_dirs: [
+ "binder",
+ "include",
+ ],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
index 6bf98d2..98c2814 100644
--- a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IIncidentAuthListener;
+import android.os.IncidentManager;
/**
* Helper service for incidentd and dumpstated to provide user feedback
@@ -35,6 +36,10 @@
* returns via the callback whether the application should be trusted. It is up
* to the caller to actually implement the restriction to take or not take
* the incident or bug report.
+ * @param receiverClass The class that will be the eventual broacast receiver for the
+ * INCIDENT_REPORT_READY message. Used as part of the id in incidentd.
+ * @param reportId The incident report ID. Incidentd should call with this parameter, but
+ * everyone else should pass null or empty string.
* @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
* a dialog will be shown as a notification.
* @param callback Interface to receive results. The results may not come back for
@@ -44,6 +49,7 @@
* to send their report.
*/
oneway void authorizeReport(int callingUid, String callingPackage,
+ String receiverClass, String reportId,
int flags, IIncidentAuthListener callback);
/**
@@ -52,6 +58,11 @@
oneway void cancelAuthorization(IIncidentAuthListener callback);
/**
+ * Send the report ready broadcast on behalf of incidentd.
+ */
+ oneway void sendReportReadyBroadcast(String pkg, String cls);
+
+ /**
* Return the list of pending approvals.
*/
List<String> getPendingReports();
@@ -69,4 +80,26 @@
* @param uri the report.
*/
void denyReport(String uri);
+
+ /**
+ * List the incident reports for the given ComponentName. The receiver
+ * must be for a package inside the caller.
+ */
+ List<String> getIncidentReportList(String pkg, String cls);
+
+ /**
+ * Get the IncidentReport object.
+ */
+ IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with this incident report and it can be deleted.
+ */
+ void deleteIncidentReports(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with all incident reports from this package.
+ * Especially useful for testing.
+ */
+ void deleteAllIncidentReports(String pkg);
}
diff --git a/libs/incidentcompanion/binder/android/os/IncidentManager.aidl b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
new file mode 100644
index 0000000..d17823e
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h";
+
diff --git a/libs/incidentcompanion/include/android/os/IncidentManager.h b/libs/incidentcompanion/include/android/os/IncidentManager.h
new file mode 100644
index 0000000..07b6d82
--- /dev/null
+++ b/libs/incidentcompanion/include/android/os/IncidentManager.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+class IncidentManager : public virtual RefBase {
+public:
+ class IncidentReport : public Parcelable {
+ public:
+ IncidentReport();
+ virtual ~IncidentReport();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ void setTimestampNs(int64_t val) { mTimestampNs = val; }
+ int64_t getTimestampNs() const { return mTimestampNs; }
+ int64_t getTimestampMs() const { return mTimestampNs / 1000000; }
+
+ void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; }
+ // This was accidentally published as a long in the java api.
+ int64_t getPrivacyPolicy() const { return mPrivacyPolicy; }
+ // Dups the fd, so you retain ownership of the original one. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ status_t setFileDescriptor(int fd);
+
+ // Does not dup the fd, so ownership is passed to this object. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ void takeFileDescriptor(int fd);
+
+ // Returns the fd, which you don't own. Call dup if you need a copy.
+ int getFileDescriptor() const { return mFileDescriptor; }
+
+ private:
+ int64_t mTimestampNs;
+ int32_t mPrivacyPolicy;
+ int mFileDescriptor;
+ };
+
+
+private:
+ // Not implemented for now.
+ IncidentManager();
+ virtual ~IncidentManager();
+};
+}
+}
+
diff --git a/libs/incidentcompanion/src/IncidentManager.cpp b/libs/incidentcompanion/src/IncidentManager.cpp
new file mode 100644
index 0000000..f7c8a5e
--- /dev/null
+++ b/libs/incidentcompanion/src/IncidentManager.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/os/IncidentManager.h>
+
+namespace android {
+namespace os {
+
+// ============================================================
+IncidentManager::IncidentReport::IncidentReport()
+ :mTimestampNs(0),
+ mPrivacyPolicy(0),
+ mFileDescriptor(-1) {
+}
+
+IncidentManager::IncidentReport::~IncidentReport() {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+}
+
+status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const {
+ status_t err;
+
+ err = out->writeInt64(mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+
+ err = out->writeInt32(mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (mFileDescriptor >= 0) {
+ err = out->writeInt32(1);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeDupParcelFileDescriptor(mFileDescriptor);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ } else {
+ err = out->writeInt32(0);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) {
+ status_t err;
+ int32_t hasField;
+
+ err = in->readInt64(&mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&hasField);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (hasField) {
+ int fd = in->readParcelFileDescriptor();
+ if (fd >= 0) {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ return NO_ERROR;
+}
+
+void IncidentManager::IncidentReport::takeFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = fd;
+ }
+}
+
+// ============================================================
+IncidentManager::~IncidentManager() {
+}
+
+}
+}
+
+
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index 7936f50..1d9f8a7 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -24,19 +24,10 @@
// --- InputApplicationHandle ---
-InputApplicationHandle::InputApplicationHandle() :
- mInfo(nullptr) {
+InputApplicationHandle::InputApplicationHandle() {
}
InputApplicationHandle::~InputApplicationHandle() {
- delete mInfo;
-}
-
-void InputApplicationHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = nullptr;
- }
}
InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index bf80481..9bd3095 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -96,7 +96,12 @@
int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
int32_t fence, const ARect* rect, void** outVirtualAddress,
int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
- if (!buffer) return BAD_VALUE;
+ if (outBytesPerPixel) *outBytesPerPixel = -1;
+ if (outBytesPerStride) *outBytesPerStride = -1;
+
+ if (!buffer) {
+ return BAD_VALUE;
+ }
if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
@@ -127,15 +132,19 @@
} else {
bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
}
- int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, outBytesPerPixel, outBytesPerStride);
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride);
// if hardware returns -1 for bytes per pixel or bytes per stride, we fail
// and unlock the buffer
- if (*outBytesPerPixel == -1 || *outBytesPerStride == -1) {
+ if (bytesPerPixel == -1 || bytesPerStride == -1) {
gbuffer->unlock();
return INVALID_OPERATION;
}
+ if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel;
+ if (outBytesPerStride) *outBytesPerStride = bytesPerStride;
return result;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index f651309..1980f50 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -595,11 +595,7 @@
}
void GLESRenderEngine::setScissor(const Rect& region) {
- // Invert y-coordinate to map to GL-space.
- int32_t canvasHeight = mFboHeight;
- int32_t glBottom = canvasHeight - region.bottom;
-
- glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+ glScissor(region.left, region.top, region.getWidth(), region.getHeight());
glEnable(GL_SCISSOR_TEST);
}
@@ -719,6 +715,36 @@
return cropWin;
}
+void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
+ const LayerSettings& layer, const Mesh& mesh) {
+ // We separate the layer into 3 parts essentially, such that we only turn on blending for the
+ // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
+ FloatRect bounds = layer.geometry.roundedCornersCrop;
+ const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1],
+ rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]);
+ const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+
+ const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
+ setScissor(topRect);
+ drawMesh(mesh);
+ const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
+ setScissor(bottomRect);
+ drawMesh(mesh);
+
+ // The middle part of the layer can turn off blending.
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ disableScissor();
+}
+
status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
ATRACE_CALL();
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
@@ -738,8 +764,6 @@
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
- mFboHeight = glFramebuffer->getBufferHeight();
-
uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
@@ -750,7 +774,6 @@
void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
ATRACE_CALL();
- mFboHeight = 0;
// back to main framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -917,7 +940,14 @@
}
setSourceDataSpace(layer.sourceDataspace);
- drawMesh(mesh);
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
// Cleanup if there's a buffer source
if (layer.source.buffer.buffer != nullptr) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index efb6ef0..8c8f308 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -69,8 +69,6 @@
void clearWithColor(float red, float green, float blue, float alpha) override;
void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) override;
- void setScissor(const Rect& region) override;
- void disableScissor() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
@@ -141,6 +139,8 @@
Protection protection);
static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
+ void setScissor(const Rect& region);
+ void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
// A data space is considered HDR data space if it has BT2020 color space
@@ -156,6 +156,13 @@
// coordinates for the mesh.
FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+ // We do a special handling for rounded corners when it's possible to turn off blending
+ // for the majority of the layer. The rounded corners needs to turn on blending such that
+ // we can set the alpha value correctly, however, only the corners need this, and since
+ // blending is an expensive operation, we want to turn off blending when it's not necessary.
+ void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
+ const Mesh& mesh);
+
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
@@ -185,7 +192,6 @@
bool mInProtectedContext = false;
// If set to true, then enables tracing flush() and finish() to systrace.
bool mTraceGpuCompletion = false;
- int32_t mFboHeight = 0;
// Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
// the last recently used buffer should be kicked out.
uint32_t mFramebufferImageCacheSize = 0;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 49bdd2a..cd1182c 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -102,14 +102,21 @@
// Prime for sRGB->P3 conversion
if (useColorManagement) {
Key shaderKey;
- shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK |
- Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK,
- Key::BLEND_PREMULT | Key::TEXTURE_EXT | Key::OUTPUT_TRANSFORM_MATRIX_ON |
- Key::INPUT_TF_SRGB | Key::OUTPUT_TF_SRGB);
- for (int i = 0; i < 4; i++) {
+ shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK |
+ Key::OUTPUT_TF_MASK,
+ Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB |
+ Key::OUTPUT_TF_SRGB);
+ for (int i = 0; i < 16; i++) {
shaderKey.set(Key::OPACITY_MASK,
(i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
+
+ // Cache rounded corners
+ shaderKey.set(Key::ROUNDED_CORNERS_MASK,
+ (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
+
+ // Cache texture off option for window transition
+ shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF);
if (cache.count(shaderKey) == 0) {
cache.emplace(shaderKey, generateProgram(shaderKey));
shaderCount++;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ab34274..b211551 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -103,9 +103,6 @@
virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) = 0;
-
- virtual void setScissor(const Rect& region) = 0;
- virtual void disableScissor() = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index ddf7420..479c7ac 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -48,8 +48,6 @@
bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
MOCK_METHOD4(clearWithColor, void(float, float, float, float));
MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
- MOCK_METHOD1(setScissor, void(const Rect&));
- MOCK_METHOD0(disableScissor, void());
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index a66dbc8..3fc6a2d 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -133,6 +133,9 @@
if (handle) {
free_handle();
}
+ for (auto& [callback, context] : mDeathCallbacks) {
+ callback(context, mId);
+ }
}
void GraphicBuffer::free_handle()
@@ -191,6 +194,7 @@
if (inFormat != format) return true;
if (inLayerCount != layerCount) return true;
if ((usage & inUsage) != inUsage) return true;
+ if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true;
return false;
}
@@ -371,14 +375,29 @@
}
size_t GraphicBuffer::getFlattenedSize() const {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return 48;
+ }
+#endif
return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return 0;
+ }
+#endif
return static_cast<size_t>(handle ? mTransportNumFds : 0);
}
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return flattenBufferHubBuffer(buffer, size);
+ }
+#endif
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
if (size < sizeNeeded) return NO_MEMORY;
@@ -405,7 +424,7 @@
buf[11] = int32_t(mTransportNumInts);
memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int));
memcpy(buf + 13, handle->data + handle->numFds,
- static_cast<size_t>(mTransportNumInts) * sizeof(int));
+ static_cast<size_t>(mTransportNumInts) * sizeof(int));
}
buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
@@ -414,14 +433,13 @@
fds += mTransportNumFds;
count -= static_cast<size_t>(mTransportNumFds);
}
-
return NO_ERROR;
}
-status_t GraphicBuffer::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count) {
- if (size < 12 * sizeof(int)) {
- android_errorWriteLog(0x534e4554, "114223584");
+status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count) {
+ // Check if size is not smaller than buf[0] is supposed to take.
+ if (size < sizeof(int)) {
return NO_MEMORY;
}
@@ -436,10 +454,21 @@
} else if (buf[0] == 'GBFR') {
// old version, when usage bits were 32-bits
flattenWordCount = 12;
+ } else if (buf[0] == 'BHBB') { // BufferHub backed buffer.
+#ifndef LIBUI_IN_VNDK
+ return unflattenBufferHubBuffer(buffer, size);
+#else
+ return BAD_TYPE;
+#endif
} else {
return BAD_TYPE;
}
+ if (size < 12 * sizeof(int)) {
+ android_errorWriteLog(0x534e4554, "114223584");
+ return NO_MEMORY;
+ }
+
const size_t numFds = static_cast<size_t>(buf[10]);
const size_t numInts = static_cast<size_t>(buf[11]);
@@ -480,8 +509,8 @@
} else {
usage = uint64_t(usage_deprecated);
}
- native_handle* h = native_handle_create(
- static_cast<int>(numFds), static_cast<int>(numInts));
+ native_handle* h =
+ native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
if (!h) {
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
@@ -530,11 +559,78 @@
size -= sizeNeeded;
fds += numFds;
count -= numFds;
+ return NO_ERROR;
+}
+
+void GraphicBuffer::addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context) {
+ mDeathCallbacks.emplace_back(deathCallback, context);
+}
+
+#ifndef LIBUI_IN_VNDK
+status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const {
+ sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate();
+ if (tokenHandle == nullptr || tokenHandle->handle() == nullptr ||
+ tokenHandle->handle()->numFds != 0) {
+ return BAD_VALUE;
+ }
+
+ // Size needed for one label, one number of ints inside the token, one generation number and
+ // the token itself.
+ int numIntsInToken = tokenHandle->handle()->numInts;
+ const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
+ if (size < sizeNeeded) {
+ ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
+ static_cast<int>(sizeNeeded), static_cast<int>(size));
+ return NO_MEMORY;
+ }
+ size -= sizeNeeded;
+
+ int* buf = static_cast<int*>(buffer);
+ buf[0] = 'BHBB';
+ buf[1] = numIntsInToken;
+ memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int));
+ buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber);
return NO_ERROR;
}
-#ifndef LIBUI_IN_VNDK
+status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) {
+ const int* buf = static_cast<const int*>(buffer);
+ int numIntsInToken = buf[1];
+ // Size needed for one label, one number of ints inside the token, one generation number and
+ // the token itself.
+ const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
+ if (size < sizeNeeded) {
+ ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
+ static_cast<int>(sizeNeeded), static_cast<int>(size));
+ return NO_MEMORY;
+ }
+ size -= sizeNeeded;
+ native_handle_t* importToken = native_handle_create(/*numFds=*/0, /*numInts=*/numIntsInToken);
+ memcpy(importToken->data, buf + 2, static_cast<size_t>(buf[1]) * sizeof(int));
+ sp<NativeHandle> importTokenHandle = NativeHandle::create(importToken, /*ownHandle=*/true);
+ std::unique_ptr<BufferHubBuffer> bufferHubBuffer = BufferHubBuffer::import(importTokenHandle);
+ if (bufferHubBuffer == nullptr || bufferHubBuffer.get() == nullptr) {
+ return BAD_VALUE;
+ }
+ // Reconstruct this GraphicBuffer object using the new BufferHubBuffer object.
+ if (handle) {
+ free_handle();
+ }
+ mId = 0;
+ mGenerationNumber = static_cast<uint32_t>(buf[2 + numIntsInToken]);
+ mInitCheck =
+ initWithHandle(bufferHubBuffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+ bufferHubBuffer->desc().width, bufferHubBuffer->desc().height,
+ static_cast<PixelFormat>(bufferHubBuffer->desc().format),
+ bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage,
+ bufferHubBuffer->desc().stride);
+ mBufferId = bufferHubBuffer->id();
+ mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
+
+ return NO_ERROR;
+}
+
bool GraphicBuffer::isBufferHubBuffer() const {
return mBufferHubBuffer != nullptr;
}
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 1c88777..c195342 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -21,6 +21,8 @@
#include <sys/types.h>
#include <string>
+#include <utility>
+#include <vector>
#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
@@ -42,6 +44,8 @@
class GraphicBufferMapper;
+using GraphicBufferDeathCallback = std::function<void(void* /*context*/, uint64_t bufferId)>;
+
// ===========================================================================
// GraphicBuffer
// ===========================================================================
@@ -219,6 +223,8 @@
return mBufferMapper.getMapperVersion();
}
+ void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
+
#ifndef LIBUI_IN_VNDK
// Returns whether this GraphicBuffer is backed by BufferHubBuffer.
bool isBufferHubBuffer() const;
@@ -280,7 +286,32 @@
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
uint32_t mGenerationNumber;
+ // Send a callback when a GraphicBuffer dies.
+ //
+ // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When
+ // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes
+ // problems when trying to implicitcly cache across process boundaries. Ideally, both sides
+ // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer
+ // would be destroyed. Unfortunately, when SurfaceFlinger has only a wp<> reference to the
+ // GraphicBuffer, it immediately goes out of scope in the SurfaceFlinger process. SurfaceFlinger
+ // must hold onto a sp<> to the buffer. When the GraphicBuffer goes out of scope in the app's
+ // process, the client side cache will get this callback. It erases the buffer from its cache
+ // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
+ std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
+ mDeathCallbacks;
+
#ifndef LIBUI_IN_VNDK
+ // Flatten this GraphicBuffer object if backed by BufferHubBuffer.
+ status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const;
+
+ // Unflatten into BufferHubBuffer backed GraphicBuffer.
+ // Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a
+ // GraphicBuffer backed by BufferHubBuffer_1 flatten in process/thread A, transport the token
+ // to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the
+ // token. Race condition occurs between the invalidation of the token in bufferhub process and
+ // process/thread B trying to unflatten and import the buffer with that token.
+ status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size);
+
// Stores a BufferHubBuffer that handles buffer signaling, identification.
std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
#endif // LIBUI_IN_VNDK
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index c767ce0..a7c248c 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -74,4 +74,49 @@
EXPECT_EQ(gb->getBufferId(), b1_id);
}
+TEST_F(GraphicBufferTest, flattenAndUnflatten) {
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ kTestUsage, /*userMetadataSize=*/0);
+ ASSERT_NE(b1, nullptr);
+ sp<GraphicBuffer> gb1(new GraphicBuffer(std::move(b1)));
+ gb1->setGenerationNumber(42);
+
+ size_t flattenedSize = gb1->getFlattenedSize();
+ EXPECT_EQ(flattenedSize, 48);
+ size_t fdCount = gb1->getFdCount();
+ EXPECT_EQ(fdCount, 0);
+
+ int data[flattenedSize];
+ int fds[0];
+
+ // Make copies of needed items since flatten modifies them.
+ size_t flattenedSizeCopy = flattenedSize;
+ size_t fdCountCopy = fdCount;
+ void* dataStart = data;
+ int* fdsStart = fds;
+ status_t err = gb1->flatten(dataStart, flattenedSizeCopy, fdsStart, fdCountCopy);
+ ASSERT_EQ(err, NO_ERROR);
+ EXPECT_EQ(flattenedSizeCopy, 0);
+ EXPECT_EQ(fdCountCopy, 0);
+
+ size_t unflattenSize = flattenedSize;
+ size_t unflattenFdCount = fdCount;
+ const void* unflattenData = static_cast<const void*>(dataStart);
+ const int* unflattenFdData = static_cast<const int*>(fdsStart);
+
+ GraphicBuffer* gb2 = new GraphicBuffer();
+ err = gb2->unflatten(unflattenData, unflattenSize, unflattenFdData, unflattenFdCount);
+ ASSERT_EQ(err, NO_ERROR);
+ EXPECT_TRUE(gb2->isBufferHubBuffer());
+
+ EXPECT_EQ(gb2->getWidth(), kTestWidth);
+ EXPECT_EQ(gb2->getHeight(), kTestHeight);
+ EXPECT_EQ(static_cast<uint32_t>(gb2->getPixelFormat()), kTestFormat);
+ EXPECT_EQ(gb2->getUsage(), kTestUsage);
+ EXPECT_EQ(gb2->getLayerCount(), kTestLayerCount);
+ EXPECT_EQ(gb1->getBufferId(), gb2->getBufferId());
+ EXPECT_EQ(gb2->getGenerationNumber(), 42);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 1317620..0d5bc15 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1958,17 +1958,17 @@
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1977,7 +1977,7 @@
}
}
-void InputDispatcher::enqueueDispatchEntry(
+void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
@@ -2054,6 +2054,10 @@
delete dispatchEntry;
return; // skip the inconsistent event
}
+
+ dispatchPointerDownOutsideFocusIfNecessary(motionEntry->source,
+ dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
+
break;
}
}
@@ -2066,6 +2070,32 @@
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
traceOutboundQueueLength(connection);
+
+}
+
+void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) {
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ if (source != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+ return;
+ }
+
+ sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
+ if (inputWindowHandle == nullptr) {
+ return;
+ }
+
+ int32_t displayId = inputWindowHandle->getInfo()->displayId;
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+ bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
+
+ if (!hasFocusChanged) {
+ return;
+ }
+
+ // Dispatch onPointerDownOutsideFocus to the policy.
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2385,7 +2415,7 @@
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
+ enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
&target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
@@ -3221,13 +3251,11 @@
if (oldFocusedApplicationHandle != inputApplicationHandle) {
if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
}
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 4d2c216..3735a0b 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -1136,8 +1136,9 @@
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntry(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
+ REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
@@ -1147,6 +1148,9 @@
void drainDispatchQueue(Queue<DispatchEntry>* queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
+ // The action sent should only be of type AMOTION_EVENT_*
+ void dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) REQUIRES(mLock);
void synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) REQUIRES(mLock);
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 0cc37a0..a45b8a5 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -4314,7 +4314,8 @@
nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
android::util::stats_write(android::util::TOUCH_EVENT_REPORTED,
- mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev());
+ mStatistics.min, mStatistics.max,
+ mStatistics.mean(), mStatistics.stdev(), mStatistics.count);
mStatistics.reset(now);
}
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d63ff8c..745fac0 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -340,10 +340,7 @@
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() {
- if (!mInfo) {
- mInfo = new InputApplicationInfo();
- }
- mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
return true;
}
};
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 189ae36..a6ed75f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -359,7 +359,7 @@
for (const auto & s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
- if (info.batchParams.isEmpty()) continue;
+ if (info.numActiveClients() == 0) continue;
result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
@@ -730,6 +730,15 @@
return mDisabledClients.indexOf(ident) >= 0;
}
+bool SensorDevice::isSensorActive(int handle) const {
+ Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ return false;
+ }
+ return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
+}
+
void SensorDevice::enableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 2a69654..71b918f 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -120,6 +120,8 @@
return mReconnecting;
}
+ bool isSensorActive(int handle) const;
+
// Dumpable
virtual std::string dump() const;
private:
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index b66cbcf..c4cfdc6 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -78,7 +78,14 @@
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tOperating Mode: %s\n",mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
+ result.appendFormat("\tOperating Mode: ");
+ if (!mService->isWhiteListedPackage(getPackageName())) {
+ result.append("RESTRICTED\n");
+ } else if (mDataInjectionMode) {
+ result.append("DATA_INJECTION\n");
+ } else {
+ result.append("NORMAL\n");
+ }
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
"max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3fbd61e..e3dfde4 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -441,12 +441,15 @@
}
result.append("Active sensors:\n");
+ SensorDevice& dev = SensorDevice::getInstance();
for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
int handle = mActiveSensors.keyAt(i);
- result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
- getSensorName(handle).string(),
- handle,
- mActiveSensors.valueAt(i)->getNumConnections());
+ if (dev.isSensorActive(handle)) {
+ result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ }
}
result.appendFormat("Socket Buffer size = %zd events\n",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e4179ee..d3b36fe 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -361,8 +361,12 @@
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
+
+ // INVALID_BUFFER_SLOT is used to identify BufferStateLayers. Default to 0
+ // for BufferQueueLayers
+ int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
(*outputLayer->editState().hwc)
- .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
+ .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer);
auto acquireFence = mConsumer->getCurrentFence();
auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5efa4e0..64dfdc3 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -585,10 +585,10 @@
const State& s(getDrawingState());
- // obtain slot
- uint32_t hwcSlot = 0;
+ uint32_t hwcSlot;
sp<GraphicBuffer> buffer;
- hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
+ hwcInfo.hwcBufferCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, s.buffer, &hwcSlot,
+ &buffer);
auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
if (error != HWC2::Error::None) {
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
index cb02d16..51ca45c 100644
--- a/services/surfaceflinger/BufferStateLayerCache.cpp
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -23,21 +23,14 @@
#include "BufferStateLayerCache.h"
-#define VALID_CACHE_ID(id) ((id) >= 0 || (id) < (BUFFER_CACHE_MAX_SIZE))
-
namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(BufferStateLayerCache);
BufferStateLayerCache::BufferStateLayerCache() : mDeathRecipient(new CacheDeathRecipient) {}
-void BufferStateLayerCache::add(sp<IBinder> processToken, int32_t id,
+void BufferStateLayerCache::add(const sp<IBinder>& processToken, uint64_t id,
const sp<GraphicBuffer>& buffer) {
- if (!VALID_CACHE_ID(id)) {
- ALOGE("failed to cache buffer: invalid buffer id");
- return;
- }
-
if (!processToken) {
ALOGE("failed to cache buffer: invalid process token");
return;
@@ -61,15 +54,33 @@
}
auto& processBuffers = mBuffers[processToken];
+
+ if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
+ ALOGE("failed to cache buffer: cache is full");
+ return;
+ }
+
processBuffers[id] = buffer;
}
-sp<GraphicBuffer> BufferStateLayerCache::get(sp<IBinder> processToken, int32_t id) {
- if (!VALID_CACHE_ID(id)) {
- ALOGE("failed to get buffer: invalid buffer id");
- return nullptr;
+void BufferStateLayerCache::erase(const sp<IBinder>& processToken, uint64_t id) {
+ if (!processToken) {
+ ALOGE("failed to uncache buffer: invalid process token");
+ return;
}
+ std::lock_guard lock(mMutex);
+
+ if (mBuffers.find(processToken) == mBuffers.end()) {
+ ALOGE("failed to uncache buffer: process token not found");
+ return;
+ }
+
+ auto& processBuffers = mBuffers[processToken];
+ processBuffers.erase(id);
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, uint64_t id) {
if (!processToken) {
ALOGE("failed to cache buffer: invalid process token");
return nullptr;
@@ -82,8 +93,8 @@
return nullptr;
}
- if (id >= itr->second.size()) {
- ALOGE("failed to get buffer: id outside the bounds of the cache");
+ if (itr->second.find(id) == itr->second.end()) {
+ ALOGE("failed to get buffer: buffer not found");
return nullptr;
}
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
index ede3feb..415c09c 100644
--- a/services/surfaceflinger/BufferStateLayerCache.h
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -34,14 +34,16 @@
public:
BufferStateLayerCache();
- void add(sp<IBinder> processToken, int32_t id, const sp<GraphicBuffer>& buffer);
- sp<GraphicBuffer> get(sp<IBinder> processToken, int32_t id);
+ void add(const sp<IBinder>& processToken, uint64_t id, const sp<GraphicBuffer>& buffer);
+ void erase(const sp<IBinder>& processToken, uint64_t id);
+
+ sp<GraphicBuffer> get(const sp<IBinder>& processToken, uint64_t id);
void removeProcess(const wp<IBinder>& processToken);
private:
std::mutex mMutex;
- std::map<wp<IBinder> /*caching process*/, std::array<sp<GraphicBuffer>, BUFFER_CACHE_MAX_SIZE>>
+ std::map<wp<IBinder> /*caching process*/, std::map<uint64_t /*Cache id*/, sp<GraphicBuffer>>>
mBuffers GUARDED_BY(mMutex);
class CacheDeathRecipient : public IBinder::DeathRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 02d7890..97bdc8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -45,7 +45,7 @@
//
// outBuffer is set to buffer when buffer is not in the HWC cache;
// otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer);
protected:
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 8613210..a941e09 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -48,12 +48,20 @@
return std::distance(std::begin(mBuffers), iter);
}
-void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- bool cached = getSlot(buffer, outSlot);
+ // if this slot corresponds to a BufferStateLayer, generate the slot
+ if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
+ getSlot(buffer, outSlot);
+ } else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+ *outSlot = 0;
+ } else {
+ *outSlot = slot;
+ }
auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
- if (cached) {
+ wp<GraphicBuffer> weakCopy(buffer);
+ if (currentBuffer == weakCopy) {
// already cached in HWC, skip sending the buffer
*outBuffer = nullptr;
currentCounter = getCounter();
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index ac04cb3..b261493 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -24,9 +24,9 @@
class TestableHwcBufferCache : public impl::HwcBufferCache {
public:
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+ HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
}
bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
return HwcBufferCache::getSlot(buffer, outSlot);
@@ -38,64 +38,88 @@
public:
~HwcBufferCacheTest() override = default;
- TestableHwcBufferCache mCache;
+ void testSlot(const int inSlot, const uint32_t expectedSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // Setting a slot to use nullptr lookslike works, but note that
+ // the output values make it look like no new buffer is being set....
+ mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+ }
+
+ impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
};
-TEST_F(HwcBufferCacheTest, testSlot) {
+TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
+ testSlot(0, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
+ testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
+ testSlot(-123, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheGeneratesSlotForInvalidBufferSlot) {
uint32_t outSlot;
sp<GraphicBuffer> outBuffer;
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer2, outBuffer);
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, sp<GraphicBuffer>(), &outSlot,
+ &outBuffer);
EXPECT_EQ(2, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
-}
-TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
- int slot;
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
- mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
- EXPECT_EQ(buf, outBuffer);
- EXPECT_EQ(i, outSlot);
- }
-
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(0, slot);
-
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(0, outSlot);
+ // note that sending mBuffer1 with explicit slot 1 will overwrite mBuffer2
+ // and also cause mBuffer1 to be stored in two places
+ mCache.getHwcBuffer(1, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(1, slot);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(3, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
}
} // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 7927fa9..ad08a92 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -42,4 +42,32 @@
void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
const Rect&, int32_t, const ui::Dataspace) {}
+Layer::RoundedCornerState ContainerLayer::getRoundedCornerStateInternal(
+ const FloatRect bounds) const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ if (parentState.radius > 0) {
+ ui::Transform t = getActiveTransform(getDrawingState());
+ t = t.inverse();
+ parentState.cropRect = t.transform(parentState.cropRect);
+ // The rounded corners shader only accepts 1 corner radius for performance reasons,
+ // but a transform matrix can define horizontal and vertical scales.
+ // Let's take the average between both of them and pass into the shader, practically we
+ // never do this type of transformation on windows anyway.
+ parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+ return parentState;
+ }
+ }
+ const float radius = getDrawingState().cornerRadius;
+ if (radius > 0) {
+ const Rect crop = getCrop(getDrawingState());
+ if (!crop.isEmpty()) {
+ return RoundedCornerState(bounds.intersect(crop.toFloatRect()), radius);
+ }
+ return RoundedCornerState(bounds, radius);
+ }
+ return RoundedCornerState();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 7222a3e..cd8e722 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -40,6 +40,7 @@
bool isCreatedFromMainThread() const override { return true; }
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+ Layer::RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const override;
protected:
bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 775fb80..7370b0c 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -111,7 +111,7 @@
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -137,7 +137,7 @@
mCurrentFence = item.mFence;
outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
outDataspace = static_cast<Dataspace>(item.mDataSpace);
status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
if (result != NO_ERROR) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 613dc77..4e0e4df 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -224,7 +224,7 @@
if (fbBuffer != nullptr) {
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
+ mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 898d37e..73f27e4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1791,10 +1791,10 @@
return getRoundedCornerStateInternal(mSourceBounds);
}
-Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect bounds) const {
+Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect) const {
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ RoundedCornerState parentState = p->getRoundedCornerStateInternal(mSourceBounds);
if (parentState.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
@@ -1809,7 +1809,8 @@
}
const float radius = getDrawingState().cornerRadius;
return radius > 0
- ? RoundedCornerState(bounds.intersect(getCrop(getDrawingState()).toFloatRect()), radius)
+ ? RoundedCornerState(mSourceBounds.intersect(getCrop(getDrawingState()).toFloatRect()),
+ radius)
: RoundedCornerState();
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 83ff3b6..b9dc7ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -452,13 +452,13 @@
virtual void setPostTime(nsecs_t /*postTime*/) {}
virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {}
+ virtual RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
protected:
virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform, Region& clearRegion,
const bool supportProtectedContent,
renderengine::LayerSettings& layer);
-
public:
/*
* compositionengine::LayerFE overrides
@@ -908,8 +908,6 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
-
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 6599f9e..a2a6bd8 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -211,13 +211,14 @@
const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
const nsecs_t phaseCorrection = mPhase + listener.mPhase;
- const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
- if (predictedLastEventTime >= now) {
- // Make sure that the last event time does not exceed the current time.
- // If it would, then back the last event time by a period.
- listener.mLastEventTime = predictedLastEventTime - mPeriod;
- } else {
- listener.mLastEventTime = predictedLastEventTime;
+ listener.mLastEventTime = predictedReference + phaseCorrection;
+ // If we're very close in time to the predicted last event time,
+ // then we need to back up the last event time so that we can
+ // attempt to fire an event immediately.
+ //
+ // Otherwise, keep the last event time that we predicted.
+ if (isShorterThanPeriod(now - listener.mLastEventTime)) {
+ listener.mLastEventTime -= mPeriod;
}
} else {
listener.mLastEventTime = now + mPhase - mWakeupLatency;
@@ -314,7 +315,7 @@
// Sanity check that the duration is close enough in length to a period without
// falling into double-rate vsyncs.
- bool isCloseToPeriod(nsecs_t duration) {
+ bool isShorterThanPeriod(nsecs_t duration) {
// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
return duration < (3 * mPeriod) / 5;
}
@@ -330,7 +331,7 @@
nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
if (t < now) {
- if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
+ if (isShorterThanPeriod(now - eventListener.mLastCallbackTime)) {
eventListener.mLastEventTime = t;
eventListener.mLastCallbackTime = now;
ALOGV("[%s] [%s] Skipping event due to model error", mName,
@@ -391,7 +392,7 @@
// Check that it's been slightly more than half a period since the last
// event so that we don't accidentally fall into double-rate vsyncs
- if (isCloseToPeriod(t - listener.mLastEventTime)) {
+ if (isShorterThanPeriod(t - listener.mLastEventTime)) {
t += mPeriod;
ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index cbcc031..9e95f95 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -57,16 +57,23 @@
}
~RefreshRateConfigs() = default;
- const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+ const std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() {
return mRefreshRates;
}
- const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
+ std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) {
+ const auto& refreshRate = mRefreshRates.find(type);
+ if (refreshRate != mRefreshRates.end()) {
+ return refreshRate->second;
+ }
+ return nullptr;
+ }
private:
void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
// This is the rate that HWC encapsulates right now when the device is in DOZE mode.
mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+ std::make_shared<RefreshRate>(
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}));
if (configs.size() < 1) {
ALOGE("Device does not have valid configs. Config size is 0.");
@@ -90,9 +97,10 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::DEFAULT,
- RefreshRate{configIdToVsyncPeriod[0].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[0].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
if (configs.size() < 2) {
@@ -105,13 +113,14 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
- RefreshRate{configIdToVsyncPeriod[1].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[1].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
}
- std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
+ std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 2491081..ff63faf 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -85,10 +85,13 @@
std::unordered_map<std::string, int64_t> totalTime;
for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
int64_t totalTimeForConfig = 0;
- if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
- totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+ if (!config) {
+ continue;
}
- totalTime[config.name] = totalTimeForConfig;
+ if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
+ totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
+ }
+ totalTime[config->name] = totalTimeForConfig;
}
return totalTime;
}
@@ -124,8 +127,11 @@
mConfigModesTotalTime[mode] += timeElapsedMs;
for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
- if (config.configId == mode) {
- mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+ if (!config) {
+ continue;
+ }
+ if (config->configId == mode) {
+ mTimeStats->recordRefreshRate(config->fps, timeElapsed);
}
}
}
@@ -147,7 +153,7 @@
// Aggregate refresh rate statistics for telemetry.
std::shared_ptr<TimeStats> mTimeStats;
- int64_t mCurrentConfigMode = 0;
+ int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f6251b8..945f6fa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -592,10 +592,11 @@
const auto displayId = getInternalDisplayIdLocked();
LOG_ALWAYS_FATAL_IF(!displayId);
- const auto performanceRefreshRate =
+ const auto& performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ if (performanceRefreshRate &&
+ isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
} else {
setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
@@ -738,6 +739,7 @@
mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()],
mTimeStats);
+ mRefreshRateStats->setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
ALOGV("Done initializing");
}
@@ -1470,17 +1472,24 @@
LOG_ALWAYS_FATAL_IF(!displayId);
const auto displayToken = getInternalDisplayTokenLocked();
- auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
- const auto display = getDisplayDeviceLocked(displayToken);
- if (desiredConfigId == display->getActiveConfig()) {
+ const auto& refreshRateConfig = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate);
+ if (!refreshRateConfig) {
+ ALOGV("Skipping refresh rate change request for unsupported rate.");
return;
}
+ const int desiredConfigId = refreshRateConfig->configId;
+
if (!isConfigAllowed(*displayId, desiredConfigId)) {
ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
return;
}
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (desiredConfigId == display->getActiveConfig()) {
+ return;
+ }
+
mPhaseOffsets->setRefreshRateType(refreshRate);
setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
}
@@ -3508,13 +3517,15 @@
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
- const auto& [states, displays, flags, desiredPresentTime, postTime, privileged] =
- transactionQueue.front();
+ const auto&
+ [states, displays, flags, desiredPresentTime, uncacheBuffer, postTime,
+ privileged] = transactionQueue.front();
if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
break;
}
applyTransactionState(states, displays, flags, mPendingInputWindowCommands,
- desiredPresentTime, postTime, privileged, /*isMainThread*/ true);
+ desiredPresentTime, uncacheBuffer, postTime, privileged,
+ /*isMainThread*/ true);
transactionQueue.pop();
}
@@ -3571,7 +3582,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime) {
+ int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer) {
ATRACE_CALL();
const int64_t postTime = systemTime();
@@ -3588,20 +3600,22 @@
if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
- postTime, privileged);
+ uncacheBuffer, postTime, privileged);
setTransactionFlags(eTransactionNeeded);
return;
}
applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
- postTime, privileged);
+ uncacheBuffer, postTime, privileged);
}
void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime, const int64_t postTime,
- bool privileged, bool isMainThread) {
+ const int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer,
+ const int64_t postTime, bool privileged,
+ bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -3636,6 +3650,10 @@
transactionFlags |= addInputWindowCommands(inputWindowCommands);
+ if (uncacheBuffer.token) {
+ BufferStateLayerCache::getInstance().erase(uncacheBuffer.token, uncacheBuffer.cacheId);
+ }
+
// If a synchronous transaction is explicitly requested without any changes, force a transaction
// anyway. This can be used as a flush mechanism for previous async transactions.
// Empty animation transaction can be used to simulate back-pressure, so also force a
@@ -3977,16 +3995,20 @@
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
-
- if (what & layer_state_t::eBufferChanged) {
- // Add the new buffer to the cache. This should always come before eCachedBufferChanged.
- BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.bufferId,
+ bool bufferChanged = what & layer_state_t::eBufferChanged;
+ bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
+ sp<GraphicBuffer> buffer;
+ if (bufferChanged && cacheIdChanged) {
+ BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.cacheId,
s.buffer);
+ buffer = s.buffer;
+ } else if (cacheIdChanged) {
+ buffer = BufferStateLayerCache::getInstance().get(s.cachedBuffer.token,
+ s.cachedBuffer.cacheId);
+ } else if (bufferChanged) {
+ buffer = s.buffer;
}
- if (what & layer_state_t::eCachedBufferChanged) {
- sp<GraphicBuffer> buffer =
- BufferStateLayerCache::getInstance().get(s.cachedBuffer.token,
- s.cachedBuffer.bufferId);
+ if (buffer) {
if (layer->setBuffer(buffer)) {
flags |= eTraversalNeeded;
layer->setPostTime(postTime);
@@ -4230,7 +4252,7 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1);
+ setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {});
setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
@@ -5783,12 +5805,12 @@
int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
if (!isConfigAllowed(*displayId, currentConfigIndex)) {
for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
- if (isConfigAllowed(*displayId, config.configId)) {
+ if (config && isConfigAllowed(*displayId, config->configId)) {
// TODO: we switch to the first allowed config. In the future
// we may want to enhance this logic to pick a similar config
// to the current one
- ALOGV("Old config is not allowed - switching to config %d", config.configId);
- setDesiredActiveConfig(displayToken, config.configId,
+ ALOGV("Old config is not allowed - switching to config %d", config->configId);
+ setDesiredActiveConfig(displayToken, config->configId,
Scheduler::ConfigEvent::Changed);
break;
}
@@ -5798,9 +5820,10 @@
// If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
// there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
- const auto performanceRefreshRate =
+ const auto& performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ if (performanceRefreshRate &&
+ isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3bf7c21..26d0cd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -431,7 +431,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime) override;
+ int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -577,7 +578,8 @@
void applyTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime, const int64_t postTime,
+ const int64_t desiredPresentTime,
+ const cached_buffer_t& uncacheBuffer, const int64_t postTime,
bool privileged, bool isMainThread = false) REQUIRES(mStateLock);
bool flushTransactionQueues();
uint32_t getTransactionFlags(uint32_t flags);
@@ -1059,11 +1061,13 @@
struct TransactionState {
TransactionState(const Vector<ComposerState>& composerStates,
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- int64_t desiredPresentTime, int64_t postTime, bool privileged)
+ int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer,
+ int64_t postTime, bool privileged)
: states(composerStates),
displays(displayStates),
flags(transactionFlags),
desiredPresentTime(desiredPresentTime),
+ buffer(uncacheBuffer),
postTime(postTime),
privileged(privileged) {}
@@ -1071,6 +1075,7 @@
Vector<DisplayState> displays;
uint32_t flags;
const int64_t desiredPresentTime;
+ cached_buffer_t buffer;
const int64_t postTime;
bool privileged;
};
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 25ce4ac..12b41fd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -47,6 +47,7 @@
"LayerMetadataTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "RefreshRateConfigsTest.cpp",
"RefreshRateStatsTest.cpp",
"TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
new file mode 100644
index 0000000..b218ad6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "DisplayHardware/HWC2.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+class RefreshRateConfigsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_60 = 0;
+ static constexpr int CONFIG_ID_90 = 1;
+ static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr int64_t VSYNC_90 = 11111111;
+
+ RefreshRateConfigsTest();
+ ~RefreshRateConfigsTest();
+
+ void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
+ ASSERT_EQ(left.configId, right.configId);
+ ASSERT_EQ(left.name, right.name);
+ ASSERT_EQ(left.fps, right.fps);
+ }
+};
+
+RefreshRateConfigsTest::RefreshRateConfigsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ RefreshRateConfigs configs(displayConfigs);
+
+ // We always store a configuration for screen off.
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(1, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
+
+ RefreshRate expectedConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedConfig, *powerSavingRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedConfig, *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(1, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(2, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(2, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ displayConfigs.push_back(config90.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(3, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_NE(rates.end(), performanceRate);
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+ RefreshRate expectedPerformanceConfig = RefreshRate{CONFIG_ID_90, "90fps", 90};
+ assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ assertRatesEqual(expectedPerformanceConfig,
+ *configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index b7e55f4..10f5af8 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -109,6 +109,7 @@
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(0, times["90fps"]);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -172,6 +173,7 @@
EXPECT_EQ(sixty, times["60fps"]);
EXPECT_EQ(ninety, times["90fps"]);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));