Merge "binderClearBufTest: remove unnecessary flush"
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index bfcc058..ba25a5a 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -39,8 +39,13 @@
std::string calling_package;
};
-static binder::Status exception(uint32_t code, const std::string& msg) {
- MYLOGE("%s (%d) ", msg.c_str(), code);
+static binder::Status exception(uint32_t code, const std::string& msg,
+ const std::string& extra_msg = "") {
+ if (extra_msg.empty()) {
+ MYLOGE("%s (%d) ", msg.c_str(), code);
+ } else {
+ MYLOGE("%s %s (%d) ", msg.c_str(), extra_msg.c_str(), code);
+ }
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
@@ -60,7 +65,7 @@
} // namespace
-DumpstateService::DumpstateService() : ds_(nullptr) {
+DumpstateService::DumpstateService() : ds_(nullptr), calling_uid_(-1), calling_package_() {
}
char const* DumpstateService::getServiceName() {
@@ -131,6 +136,10 @@
ds_->SetOptions(std::move(options));
ds_->listener_ = listener;
+ // Track caller info for cancellation purposes.
+ calling_uid_ = calling_uid;
+ calling_package_ = calling_package;
+
DumpstateInfo* ds_info = new DumpstateInfo();
ds_info->ds = ds_;
ds_info->calling_uid = calling_uid;
@@ -149,8 +158,20 @@
return binder::Status::ok();
}
-binder::Status DumpstateService::cancelBugreport() {
+binder::Status DumpstateService::cancelBugreport(int32_t calling_uid,
+ const std::string& calling_package) {
std::lock_guard<std::mutex> lock(lock_);
+ if (calling_uid != calling_uid_ || calling_package != calling_package_) {
+ // Note: we use a SecurityException to prevent BugreportManagerServiceImpl from killing the
+ // report in progress (from another caller).
+ return exception(
+ binder::Status::EX_SECURITY,
+ StringPrintf("Cancellation requested by %d/%s does not match report in "
+ "progress",
+ calling_uid, calling_package.c_str()),
+ // Sharing the owner of the BR is a (minor) leak, so leave it out of the app's exception
+ StringPrintf("started by %d/%s", calling_uid_, calling_package_.c_str()));
+ }
ds_->Cancel();
return binder::Status::ok();
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index ac8d3ac..3ec8471 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -44,8 +44,7 @@
const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) override;
- // No-op
- binder::Status cancelBugreport();
+ binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
private:
// Dumpstate object which contains all the bugreporting logic.
@@ -53,6 +52,8 @@
// one bugreport.
// This service does not own this object.
Dumpstate* ds_;
+ int32_t calling_uid_;
+ std::string calling_package_;
std::mutex lock_;
};
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index ba008bb..0793f0b 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
import android.os.IDumpstateListener;
/**
- * Binder interface for the currently running dumpstate process.
- * {@hide}
- */
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
interface IDumpstate {
// NOTE: If you add to or change these modes, please also change the corresponding enums
@@ -49,10 +49,10 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
- /*
+ /**
* Starts a bugreport in the background.
*
- *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling
+ * <p>Shows the user a dialog to get consent for sharing the bugreport with the calling
* application. If they deny {@link IDumpstateListener#onError} will be called. If they
* consent and bugreport generation is successful artifacts will be copied to the given fds and
* {@link IDumpstateListener#onFinished} will be called. If there
@@ -71,8 +71,15 @@
int bugreportMode, IDumpstateListener listener,
boolean isScreenshotRequested);
- /*
+ /**
* Cancels the bugreport currently in progress.
+ *
+ * <p>The caller must match the original caller of {@link #startBugreport} in order for the
+ * report to actually be cancelled. A {@link SecurityException} is reported if a mismatch is
+ * detected.
+ *
+ * @param callingUid UID of the original application that requested the cancellation.
+ * @param callingPackage package of the original application that requested the cancellation.
*/
- void cancelBugreport();
+ void cancelBugreport(int callingUid, @utf8InCpp String callingPackage);
}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 85e6969..50c1624 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -21,8 +21,6 @@
*
* <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called.
*
- * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler
- * to handle.
*
* {@hide}
*/
@@ -54,10 +52,8 @@
/**
* Called on an error condition with one of the error codes listed above.
- * This is not an asynchronous method since it can race with dumpstate exiting, thus triggering
- * death recipient.
*/
- void onError(int errorCode);
+ oneway void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index b1bc6dc..627dfe6 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -45,10 +45,6 @@
IPCThreadState::self()->setupPolling(&binder_fd);
LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
- // Flush after setupPolling(), to make sure the binder driver
- // knows about this thread handling commands.
- IPCThreadState::self()->flushCommands();
-
int ret = looper->addFd(binder_fd,
Looper::POLL_CALLBACK,
Looper::EVENT_INPUT,
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6dff93a..5c34069 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -502,12 +502,14 @@
void IPCThreadState::blockUntilThreadAvailable()
{
pthread_mutex_lock(&mProcess->mThreadCountLock);
+ mProcess->mWaitingForThreads++;
while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) {
ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n",
static_cast<unsigned long>(mProcess->mExecutingThreadsCount),
static_cast<unsigned long>(mProcess->mMaxThreads));
pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock);
}
+ mProcess->mWaitingForThreads--;
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
@@ -547,7 +549,12 @@
}
mProcess->mStarvationStartTimeMs = 0;
}
- pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
+
+ // Cond broadcast can be expensive, so don't send it every time a binder
+ // call is processed. b/168806193
+ if (mProcess->mWaitingForThreads > 0) {
+ pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
+ }
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
@@ -644,6 +651,7 @@
}
mOut.writeInt32(BC_ENTER_LOOPER);
+ flushCommands();
*fd = mProcess->mDriverFD;
return 0;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index b5e4dfe..c38249e 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -399,6 +399,7 @@
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
+ , mWaitingForThreads(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mThreadPoolStarted(false)
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 00a14f4..2e90142 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -65,6 +65,9 @@
},
{
"name": "binderRustNdkInteropTest"
+ },
+ {
+ "name": "rustBinderSerializationTest"
}
]
}
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index bab6469..2405ab6 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -107,11 +107,14 @@
int mDriverFD;
void* mVMStart;
- // Protects thread count variable below.
+ // Protects thread count and wait variables below.
pthread_mutex_t mThreadCountLock;
+ // Broadcast whenever mWaitingForThreads > 0
pthread_cond_t mThreadCountDecrement;
// Number of binder threads current executing a command.
size_t mExecutingThreadsCount;
+ // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
+ size_t mWaitingForThreads;
// Maximum number for binder threads allowed for this process.
size_t mMaxThreads;
// Time when thread pool was emptied
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 53871f2..0ad400b 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -259,10 +259,24 @@
const char* getMessage() const { return AStatus_getMessage(get()); }
std::string getDescription() const {
- const char* cStr = AStatus_getDescription(get());
- std::string ret = cStr;
- AStatus_deleteDescription(cStr);
- return ret;
+ if (__builtin_available(android 30, *)) {
+ const char* cStr = AStatus_getDescription(get());
+ std::string ret = cStr;
+ AStatus_deleteDescription(cStr);
+ return ret;
+ }
+ binder_exception_t exception = getExceptionCode();
+ std::string desc = std::to_string(exception);
+ if (exception == EX_SERVICE_SPECIFIC) {
+ desc += " (" + std::to_string(getServiceSpecificError()) + ")";
+ } else if (exception == EX_TRANSACTION_FAILED) {
+ desc += " (" + std::to_string(getStatus()) + ")";
+ }
+ if (const char* msg = getMessage(); msg != nullptr) {
+ desc += ": ";
+ desc += msg;
+ }
+ return desc;
}
/**
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index a1102e2..05eb64b 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -247,7 +247,7 @@
// ourselves. The defaults are harmless.
AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
#ifdef HAS_BINDER_SHELL_COMMAND
- if (AIBinder_Class_setHandleShellCommand != nullptr) {
+ if (__builtin_available(android 30, *)) {
AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
}
#endif
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 6636a41..2277148 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -51,14 +51,27 @@
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
- RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
- RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
- AParcel_getDataSize(this->mParcel.get())));
+ if (__builtin_available(android 31, *)) {
+ int32_t size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
+ } else {
+ return STATUS_INVALID_OPERATION;
+ }
+ if (__builtin_available(android 31, *)) {
+ int32_t size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
+ } else {
+ return STATUS_INVALID_OPERATION;
+ }
return STATUS_OK;
}
binder_status_t readFromParcel(const AParcel* parcel) {
- AParcel_reset(mParcel.get());
+ if (__builtin_available(android 31, *)) {
+ AParcel_reset(mParcel.get());
+ } else {
+ return STATUS_INVALID_OPERATION;
+ }
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
int32_t dataSize;
@@ -74,7 +87,11 @@
return STATUS_BAD_VALUE;
}
- status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+ if (__builtin_available(android 31, *)) {
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+ } else {
+ status = STATUS_INVALID_OPERATION;
+ }
if (status != STATUS_OK) {
return status;
}
@@ -86,7 +103,11 @@
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
- AParcel_reset(mParcel.get());
+ if (__builtin_available(android 31, *)) {
+ AParcel_reset(mParcel.get());
+ } else {
+ return STATUS_INVALID_OPERATION;
+ }
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
p.writeToParcel(mParcel.get());
return STATUS_OK;
@@ -96,9 +117,13 @@
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
- if (AParcel_getDataSize(mParcel.get()) == 0) {
- *ret = std::nullopt;
- return STATUS_OK;
+ if (__builtin_available(android 31, *)) {
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ *ret = std::nullopt;
+ return STATUS_OK;
+ }
+ } else {
+ return STATUS_INVALID_OPERATION;
}
std::string parcelableDescInParcel;
binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
@@ -115,7 +140,11 @@
return STATUS_OK;
}
- void reset() { AParcel_reset(mParcel.get()); }
+ void reset() {
+ if (__builtin_available(android 31, *)) {
+ AParcel_reset(mParcel.get());
+ }
+ }
inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 0ca3a07..bb70588 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -660,13 +660,15 @@
/**
* Whether AIBinder is less than another.
*
- * This provides a per-process-unique total ordering of binders determined by
- * an underlying allocation address where a null AIBinder* is considered to be
- * ordered before all other binders.
+ * This provides a per-process-unique total ordering of binders where a null
+ * AIBinder* object is considered to be before all other binder objects.
+ * For instance, two binders refer to the same object in a local or remote
+ * process when both AIBinder_lt(a, b) and AIBinder(b, a) are false. This API
+ * might be used to insert and lookup binders in binary search trees.
*
* AIBinder* pointers themselves actually also create a per-process-unique total
* ordering. However, this ordering is inconsistent with AIBinder_Weak_lt for
- * remote binders.
+ * remote binders. So, in general, this function should be preferred.
*
* Available since API level 31.
*
@@ -698,14 +700,21 @@
* the same as AIBinder_lt. Similarly, a null AIBinder_Weak* is considered to be
* ordered before all other weak references.
*
- * If you have many AIBinder_Weak* objects which are all references to distinct
- * binder objects which happen to have the same underlying address (as ordered
- * by AIBinder_lt), these AIBinder_Weak* objects will retain the same order with
- * respect to all other AIBinder_Weak* pointers with different underlying
- * addresses and are also guaranteed to have a per-process-unique ordering. That
- * is, even though multiple AIBinder* instances may happen to be allocated at
- * the same underlying address, this function will still correctly distinguish
- * that these are weak pointers to different binder objects.
+ * This function correctly distinguishes binders even if one is deallocated. So,
+ * for instance, an AIBinder_Weak* entry representing a deleted binder will
+ * never compare as equal to an AIBinder_Weak* entry which represents a
+ * different allocation of a binder, even if the two binders were originally
+ * allocated at the same address. That is:
+ *
+ * AIBinder* a = ...; // imagine this has address 0x8
+ * AIBinder_Weak* bWeak = AIBinder_Weak_new(a);
+ * AIBinder_decStrong(a); // a may be deleted, if this is the last reference
+ * AIBinder* b = ...; // imagine this has address 0x8 (same address as b)
+ * AIBinder_Weak* bWeak = AIBinder_Weak_new(b);
+ *
+ * Then when a/b are compared with other binders, their order will be preserved,
+ * and it will either be the case that AIBinder_Weak_lt(aWeak, bWeak) OR
+ * AIBinder_Weak_lt(bWeak, aWeak), but not both.
*
* Unlike AIBinder*, the AIBinder_Weak* addresses themselves have nothing to do
* with the underlying binder.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 93c3f32..ab67017 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1120,7 +1120,6 @@
// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= 29
-#if __ANDROID_API__ >= 31
/**
* Reset the parcel to the initial status.
*
@@ -1166,7 +1165,6 @@
* \return A parcel which is not related to any IBinder objects.
*/
AParcel* AParcel_create() __INTRODUCED_IN(31);
-#endif //__ANDROID_API__ >= 31
__END_DECLS
/** @} */
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index ed3b9ec..42c1e0a 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -613,7 +613,7 @@
impl $crate::parcel::Serialize for dyn $interface + '_
where
- $interface: $crate::Interface
+ dyn $interface: $crate::Interface
{
fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
let binder = $crate::Interface::as_binder(self);
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 17af099..e9e74c0 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -70,6 +70,20 @@
ptr.as_mut().map(|p| Self(p))
}
+ /// Extract a raw `AIBinder` pointer from this wrapper.
+ ///
+ /// This method should _only_ be used for testing. Do not try to use the NDK
+ /// interface directly for anything else.
+ ///
+ /// # Safety
+ ///
+ /// The resulting pointer is valid only as long as the SpIBinder is alive.
+ /// The SpIBinder object retains ownership of the AIBinder and the caller
+ /// should not attempt to free the returned pointer.
+ pub unsafe fn as_raw(&self) -> *mut sys::AIBinder {
+ self.0
+ }
+
/// Return true if this binder object is hosted in a different process than
/// the current one.
pub fn is_remote(&self) -> bool {
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 5ae9c53..8810b5d 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -79,3 +79,50 @@
"IBinderRustNdkInteropTest-rust",
],
}
+
+cc_test {
+ name: "rustBinderSerializationTest",
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libutils",
+ "libbase",
+ ],
+ static_libs: [
+ "libbinder_rs_serialization_test"
+ ],
+ srcs: [
+ "serialization.cpp",
+ ],
+ auto_gen_config: true,
+ test_suites: ["general-tests"],
+}
+
+rust_bindgen {
+ name: "libbinder_rs_serialization_bindgen",
+ crate_name: "binder_rs_serialization_bindgen",
+ wrapper_src: "serialization.hpp",
+ source_stem: "bindings",
+ cpp_std: "gnu++17",
+ bindgen_flags: [
+ "--whitelist-type", "Transaction",
+ "--whitelist-var", "TESTDATA_.*",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libc++",
+ ],
+}
+
+rust_ffi_static {
+ name: "libbinder_rs_serialization_test",
+ crate_name: "binder_rs_serialization_test",
+ srcs: [
+ "serialization.rs",
+ ":libbinder_rs_serialization_bindgen",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+}
diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp
new file mode 100644
index 0000000..ec780f2
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2020 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/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include "android-base/file.h"
+#include "serialization.hpp"
+
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <optional>
+
+using namespace std;
+using namespace android;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
+
+// defined in Rust
+extern "C" AIBinder *rust_service();
+
+
+const int8_t TESTDATA_I8[4] = {-128, 0, 117, 127};
+const uint8_t TESTDATA_U8[4] = {0, 42, 117, 255};
+const char16_t TESTDATA_CHARS[4] = {0, 42, 117, numeric_limits<char16_t>::max()};
+const int32_t TESTDATA_I32[4] = {numeric_limits<int32_t>::min(), 0, 117, numeric_limits<int32_t>::max()};
+const int64_t TESTDATA_I64[4] = {numeric_limits<int64_t>::min(), 0, 117, numeric_limits<int64_t>::max()};
+const uint64_t TESTDATA_U64[4] = {0, 42, 117, numeric_limits<uint64_t>::max()};
+const float TESTDATA_FLOAT[4] = {
+ numeric_limits<float>::quiet_NaN(),
+ -numeric_limits<float>::infinity(),
+ 117.0,
+ numeric_limits<float>::infinity(),
+};
+const double TESTDATA_DOUBLE[4] = {
+ numeric_limits<double>::quiet_NaN(),
+ -numeric_limits<double>::infinity(),
+ 117.0,
+ numeric_limits<double>::infinity(),
+};
+const bool TESTDATA_BOOL[4] = {true, false, false, true};
+const char* const TESTDATA_STRS[4] = {"", nullptr, "test", ""};
+
+static ::testing::Environment* gEnvironment;
+
+class SerializationEnvironment : public ::testing::Environment {
+public:
+ void SetUp() override {
+ m_server = AIBinder_toPlatformBinder(rust_service());
+ }
+
+ sp<IBinder> getServer(void) { return m_server; }
+
+private:
+ sp<IBinder> m_server;
+};
+
+
+class SerializationTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ ASSERT_NE(gEnvironment, nullptr);
+ m_server = static_cast<SerializationEnvironment *>(gEnvironment)->getServer();
+ }
+
+ sp<IBinder> m_server;
+};
+
+
+TEST_F(SerializationTest, SerializeBool) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<bool> bools(begin(TESTDATA_BOOL), end(TESTDATA_BOOL));
+ ASSERT_EQ(data.writeBool(true), OK);
+ ASSERT_EQ(data.writeBool(false), OK);
+ ASSERT_EQ(data.writeBoolVector(bools), OK);
+ ASSERT_EQ(data.writeBoolVector(nullopt), OK);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_BOOL, data, &reply), OK);
+
+ vector<bool> read_bools;
+ optional<vector<bool>> maybe_bools;
+ ASSERT_EQ(reply.readBool(), true);
+ ASSERT_EQ(reply.readBool(), false);
+ ASSERT_EQ(reply.readBoolVector(&read_bools), OK);
+ ASSERT_EQ(read_bools, bools);
+ ASSERT_EQ(reply.readBoolVector(&maybe_bools), OK);
+ ASSERT_EQ(maybe_bools, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeByte) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int8_t> i8s(begin(TESTDATA_I8), end(TESTDATA_I8));
+ vector<uint8_t> u8s(begin(TESTDATA_U8), end(TESTDATA_U8));
+ data.writeByte(0);
+ data.writeByte(1);
+ data.writeByte(numeric_limits<int8_t>::max());
+ data.writeByteVector(i8s);
+ data.writeByteVector(u8s);
+ data.writeByteVector(optional<vector<int8_t>>({}));
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_BYTE, data, &reply), OK);
+
+ vector<int8_t> read_i8s;
+ vector<uint8_t> read_u8s;
+ optional<vector<int8_t>> maybe_i8s;
+ ASSERT_EQ(reply.readByte(), 0);
+ ASSERT_EQ(reply.readByte(), 1);
+ ASSERT_EQ(reply.readByte(), numeric_limits<int8_t>::max());
+ ASSERT_EQ(reply.readByteVector(&read_i8s), OK);
+ ASSERT_EQ(read_i8s, i8s);
+ ASSERT_EQ(reply.readByteVector(&read_u8s), OK);
+ ASSERT_EQ(read_u8s, u8s);
+ ASSERT_EQ(reply.readByteVector(&maybe_i8s), OK);
+ ASSERT_EQ(maybe_i8s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeU16) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<char16_t> chars(begin(TESTDATA_CHARS), end(TESTDATA_CHARS));
+ data.writeChar(0);
+ data.writeChar(1);
+ data.writeChar(numeric_limits<char16_t>::max());
+ data.writeCharVector(chars);
+ data.writeCharVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_U16, data, &reply), OK);
+
+ vector<char16_t> read_chars;
+ optional<vector<char16_t>> maybe_chars;
+ ASSERT_EQ(reply.readChar(), 0);
+ ASSERT_EQ(reply.readChar(), 1);
+ ASSERT_EQ(reply.readChar(), numeric_limits<char16_t>::max());
+ ASSERT_EQ(reply.readCharVector(&read_chars), OK);
+ ASSERT_EQ(read_chars, chars);
+ ASSERT_EQ(reply.readCharVector(&maybe_chars), OK);
+ ASSERT_EQ(maybe_chars, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeI32) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int32_t> i32s(begin(TESTDATA_I32), end(TESTDATA_I32));
+ data.writeInt32(0);
+ data.writeInt32(1);
+ data.writeInt32(numeric_limits<int32_t>::max());
+ data.writeInt32Vector(i32s);
+ data.writeInt32Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_I32, data, &reply), OK);
+
+ vector<int32_t> read_i32s;
+ optional<vector<int32_t>> maybe_i32s;
+ ASSERT_EQ(reply.readInt32(), 0);
+ ASSERT_EQ(reply.readInt32(), 1);
+ ASSERT_EQ(reply.readInt32(), numeric_limits<int32_t>::max());
+ ASSERT_EQ(reply.readInt32Vector(&read_i32s), OK);
+ ASSERT_EQ(read_i32s, i32s);
+ ASSERT_EQ(reply.readInt32Vector(&maybe_i32s), OK);
+ ASSERT_EQ(maybe_i32s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeI64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int64_t> i64s(begin(TESTDATA_I64), end(TESTDATA_I64));
+ data.writeInt64(0);
+ data.writeInt64(1);
+ data.writeInt64(numeric_limits<int64_t>::max());
+ data.writeInt64Vector(i64s);
+ data.writeInt64Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_I64, data, &reply), OK);
+
+ vector<int64_t> read_i64s;
+ optional<vector<int64_t>> maybe_i64s;
+ ASSERT_EQ(reply.readInt64(), 0);
+ ASSERT_EQ(reply.readInt64(), 1);
+ ASSERT_EQ(reply.readInt64(), numeric_limits<int64_t>::max());
+ ASSERT_EQ(reply.readInt64Vector(&read_i64s), OK);
+ ASSERT_EQ(read_i64s, i64s);
+ ASSERT_EQ(reply.readInt64Vector(&maybe_i64s), OK);
+ ASSERT_EQ(maybe_i64s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeU64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<uint64_t> u64s(begin(TESTDATA_U64), end(TESTDATA_U64));
+ data.writeUint64(0);
+ data.writeUint64(1);
+ data.writeUint64(numeric_limits<uint64_t>::max());
+ data.writeUint64Vector(u64s);
+ data.writeUint64Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_U64, data, &reply), OK);
+
+ vector<uint64_t> read_u64s;
+ optional<vector<uint64_t>> maybe_u64s;
+ ASSERT_EQ(reply.readUint64(), 0);
+ ASSERT_EQ(reply.readUint64(), 1);
+ ASSERT_EQ(reply.readUint64(), numeric_limits<uint64_t>::max());
+ ASSERT_EQ(reply.readUint64Vector(&read_u64s), OK);
+ ASSERT_EQ(read_u64s, u64s);
+ ASSERT_EQ(reply.readUint64Vector(&maybe_u64s), OK);
+ ASSERT_EQ(maybe_u64s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeF32) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<float> floats(begin(TESTDATA_FLOAT), end(TESTDATA_FLOAT));
+ data.writeFloat(0);
+ data.writeFloatVector(floats);
+ data.writeFloatVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_F32, data, &reply), OK);
+
+ vector<float> read_floats;
+ optional<vector<float>> maybe_floats;
+ ASSERT_EQ(reply.readFloat(), 0);
+ ASSERT_EQ(reply.readFloatVector(&read_floats), OK);
+ ASSERT_TRUE(isnan(read_floats[0]));
+ ASSERT_EQ(read_floats[1], floats[1]);
+ ASSERT_EQ(read_floats[2], floats[2]);
+ ASSERT_EQ(read_floats[3], floats[3]);
+ ASSERT_EQ(reply.readFloatVector(&maybe_floats), OK);
+ ASSERT_EQ(maybe_floats, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeF64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<double> doubles(begin(TESTDATA_DOUBLE), end(TESTDATA_DOUBLE));
+ data.writeDouble(0);
+ data.writeDoubleVector(doubles);
+ data.writeDoubleVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_F64, data, &reply), OK);
+
+ vector<double> read_doubles;
+ optional<vector<double>> maybe_doubles;
+ ASSERT_EQ(reply.readDouble(), 0);
+ ASSERT_EQ(reply.readDoubleVector(&read_doubles), OK);
+ ASSERT_TRUE(isnan(read_doubles[0]));
+ ASSERT_EQ(read_doubles[1], doubles[1]);
+ ASSERT_EQ(read_doubles[2], doubles[2]);
+ ASSERT_EQ(read_doubles[3], doubles[3]);
+ ASSERT_EQ(reply.readDoubleVector(&maybe_doubles), OK);
+ ASSERT_EQ(maybe_doubles, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeString) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<optional<String16>> strings;
+ for (auto I = begin(TESTDATA_STRS), E = end(TESTDATA_STRS); I != E; ++I) {
+ if (*I == nullptr) {
+ strings.push_back(optional<String16>());
+ } else {
+ strings.emplace_back(*I);
+ }
+ }
+ data.writeUtf8AsUtf16(string("testing"));
+ data.writeString16(nullopt);
+ data.writeString16Vector(strings);
+ data.writeString16Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_STRING, data, &reply), OK);
+
+ optional<String16> maybe_string;
+ optional<vector<optional<String16>>> read_strings;
+ ASSERT_EQ(reply.readString16(), String16("testing"));
+ ASSERT_EQ(reply.readString16(&maybe_string), OK);
+ ASSERT_EQ(maybe_string, nullopt);
+ ASSERT_EQ(reply.readString16Vector(&read_strings), OK);
+ ASSERT_EQ(read_strings, strings);
+ ASSERT_EQ(reply.readString16Vector(&read_strings), OK);
+ ASSERT_EQ(read_strings, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeFileDescriptor) {
+ unique_fd out_file, in_file;
+ ASSERT_TRUE(base::Pipe(&out_file, &in_file));
+
+ vector<ParcelFileDescriptor> file_descriptors;
+ file_descriptors.push_back(ParcelFileDescriptor(std::move(out_file)));
+ file_descriptors.push_back(ParcelFileDescriptor(std::move(in_file)));
+
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ data.writeParcelable(file_descriptors[0]);
+ data.writeParcelable(file_descriptors[1]);
+ data.writeParcelableVector(file_descriptors);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_FILE_DESCRIPTOR, data, &reply), OK);
+
+ ParcelFileDescriptor returned_fd1, returned_fd2;
+ vector<ParcelFileDescriptor> returned_file_descriptors;
+ ASSERT_EQ(reply.readParcelable(&returned_fd1), OK);
+ ASSERT_EQ(reply.readParcelable(&returned_fd2), OK);
+ ASSERT_EQ(reply.readParcelableVector(&returned_file_descriptors), OK);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+
+ base::WriteStringToFd("Testing", returned_fd2.get());
+ base::WriteStringToFd("File", returned_file_descriptors[1].get());
+ base::WriteStringToFd("Descriptors", file_descriptors[1].get());
+
+ string expected = "TestingFileDescriptors";
+ vector<char> buf(expected.length());
+ base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size());
+ ASSERT_EQ(expected, string(buf.data()));
+}
+
+TEST_F(SerializationTest, SerializeIBinder) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ data.writeStrongBinder(m_server);
+ data.writeStrongBinder(nullptr);
+ data.writeStrongBinderVector({m_server, nullptr});
+ data.writeStrongBinderVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_IBINDER, data, &reply), OK);
+
+ optional<vector<sp<IBinder>>> binders;
+ ASSERT_TRUE(reply.readStrongBinder());
+ ASSERT_FALSE(reply.readStrongBinder());
+ ASSERT_EQ(reply.readStrongBinderVector(&binders), OK);
+ ASSERT_EQ(binders->size(), 2);
+ ASSERT_TRUE((*binders)[0]);
+ ASSERT_FALSE((*binders)[1]);
+ ASSERT_EQ(reply.readStrongBinderVector(&binders), OK);
+ ASSERT_FALSE(binders);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeStatus) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ binder::Status::ok().writeToParcel(&data);
+ binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER, "a status message")
+ .writeToParcel(&data);
+ binder::Status::fromServiceSpecificError(42, "a service-specific error").writeToParcel(&data);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_STATUS, data, &reply), OK);
+
+ binder::Status status;
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_TRUE(status.isOk());
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_EQ(status.exceptionCode(), binder::Status::EX_NULL_POINTER);
+ ASSERT_EQ(status.exceptionMessage(), "a status message");
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_EQ(status.serviceSpecificErrorCode(), 42);
+ ASSERT_EQ(status.exceptionMessage(), "a service-specific error");
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+// Test that failures from Rust properly propagate to C++
+TEST_F(SerializationTest, SerializeRustFail) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+ ASSERT_EQ(m_server->transact(TEST_FAIL, data, nullptr), FAILED_TRANSACTION);
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnvironment = AddGlobalTestEnvironment(new SerializationEnvironment());
+ ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/rust/tests/serialization.hpp b/libs/binder/rust/tests/serialization.hpp
new file mode 100644
index 0000000..0041608
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.hpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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>
+
+using namespace android;
+
+enum Transaction {
+ TEST_BOOL = IBinder::FIRST_CALL_TRANSACTION,
+ TEST_BYTE,
+ TEST_U16,
+ TEST_I32,
+ TEST_I64,
+ TEST_U64,
+ TEST_F32,
+ TEST_F64,
+ TEST_STRING,
+ TEST_FILE_DESCRIPTOR,
+ TEST_IBINDER,
+ TEST_STATUS,
+ TEST_FAIL,
+};
+
+extern const int8_t TESTDATA_I8[4];
+extern const uint8_t TESTDATA_U8[4];
+extern const char16_t TESTDATA_CHARS[4];
+extern const int32_t TESTDATA_I32[4];
+extern const int64_t TESTDATA_I64[4];
+extern const uint64_t TESTDATA_U64[4];
+extern const float TESTDATA_FLOAT[4];
+extern const double TESTDATA_DOUBLE[4];
+extern const bool TESTDATA_BOOL[4];
+extern const char* const TESTDATA_STRS[4];
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
new file mode 100644
index 0000000..2ae13f4
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.rs
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+//! Included as a module in the binder crate internal tests for internal API
+//! access.
+
+use binder::declare_binder_interface;
+use binder::{
+ Binder, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+ StatusCode, TransactionCode,
+};
+use binder::parcel::ParcelFileDescriptor;
+
+use std::ffi::{c_void, CStr, CString};
+use std::panic::{self, AssertUnwindSafe};
+use std::sync::Once;
+
+#[allow(
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused,
+ improper_ctypes,
+ missing_docs,
+ clippy::all
+)]
+mod bindings {
+ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
+
+static SERVICE_ONCE: Once = Once::new();
+static mut SERVICE: Option<SpIBinder> = None;
+
+/// Start binder service and return a raw AIBinder pointer to it.
+///
+/// Safe to call multiple times, only creates the service once.
+#[no_mangle]
+pub extern "C" fn rust_service() -> *mut c_void {
+ unsafe {
+ SERVICE_ONCE.call_once(|| {
+ SERVICE = Some(BnReadParcelTest::new_binder(()).as_binder());
+ });
+ SERVICE.as_ref().unwrap().as_raw().cast()
+ }
+}
+
+/// Empty interface just to use the declare_binder_interface macro
+pub trait ReadParcelTest: Interface {}
+
+declare_binder_interface! {
+ ReadParcelTest["read_parcel_test"] {
+ native: BnReadParcelTest(on_transact),
+ proxy: BpReadParcelTest,
+ }
+}
+
+impl ReadParcelTest for Binder<BnReadParcelTest> {}
+
+impl ReadParcelTest for BpReadParcelTest {}
+
+impl ReadParcelTest for () {}
+
+fn on_transact(
+ _service: &dyn ReadParcelTest,
+ code: TransactionCode,
+ parcel: &Parcel,
+ reply: &mut Parcel,
+) -> Result<()> {
+ panic::catch_unwind(AssertUnwindSafe(|| transact_inner(code, parcel, reply))).unwrap_or_else(
+ |e| {
+ eprintln!("Failure in Rust: {:?}", e.downcast_ref::<String>());
+ Err(StatusCode::FAILED_TRANSACTION)
+ },
+ )
+}
+
+#[allow(clippy::float_cmp)]
+fn transact_inner(code: TransactionCode, parcel: &Parcel, reply: &mut Parcel) -> Result<()> {
+ match code {
+ bindings::Transaction_TEST_BOOL => {
+ assert_eq!(parcel.read::<bool>()?, true);
+ assert_eq!(parcel.read::<bool>()?, false);
+ assert_eq!(parcel.read::<Vec<bool>>()?, unsafe {
+ bindings::TESTDATA_BOOL
+ });
+ assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None);
+
+ reply.write(&true)?;
+ reply.write(&false)?;
+ reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?;
+ reply.write(&(None as Option<Vec<bool>>))?;
+ }
+ bindings::Transaction_TEST_BYTE => {
+ assert_eq!(parcel.read::<i8>()?, 0);
+ assert_eq!(parcel.read::<i8>()?, 1);
+ assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
+ assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 });
+ assert_eq!(parcel.read::<Option<Vec<i8>>>()?, None);
+
+ reply.write(&0i8)?;
+ reply.write(&1i8)?;
+ reply.write(&i8::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
+ reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?;
+ reply.write(&(None as Option<Vec<i8>>))?;
+ }
+ bindings::Transaction_TEST_U16 => {
+ assert_eq!(parcel.read::<u16>()?, 0);
+ assert_eq!(parcel.read::<u16>()?, 1);
+ assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ assert_eq!(parcel.read::<Vec<u16>>()?, unsafe {
+ bindings::TESTDATA_CHARS
+ });
+ assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
+
+ reply.write(&0u16)?;
+ reply.write(&1u16)?;
+ reply.write(&u16::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
+ reply.write(&(None as Option<Vec<u16>>))?;
+ }
+ bindings::Transaction_TEST_I32 => {
+ assert_eq!(parcel.read::<i32>()?, 0);
+ assert_eq!(parcel.read::<i32>()?, 1);
+ assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ assert_eq!(parcel.read::<Vec<i32>>()?, unsafe {
+ bindings::TESTDATA_I32
+ });
+ assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
+
+ reply.write(&0i32)?;
+ reply.write(&1i32)?;
+ reply.write(&i32::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
+ reply.write(&(None as Option<Vec<i32>>))?;
+ }
+ bindings::Transaction_TEST_I64 => {
+ assert_eq!(parcel.read::<i64>()?, 0);
+ assert_eq!(parcel.read::<i64>()?, 1);
+ assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ assert_eq!(parcel.read::<Vec<i64>>()?, unsafe {
+ bindings::TESTDATA_I64
+ });
+ assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
+
+ reply.write(&0i64)?;
+ reply.write(&1i64)?;
+ reply.write(&i64::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
+ reply.write(&(None as Option<Vec<i64>>))?;
+ }
+ bindings::Transaction_TEST_U64 => {
+ assert_eq!(parcel.read::<u64>()?, 0);
+ assert_eq!(parcel.read::<u64>()?, 1);
+ assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ assert_eq!(parcel.read::<Vec<u64>>()?, unsafe {
+ bindings::TESTDATA_U64
+ });
+ assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
+
+ reply.write(&0u64)?;
+ reply.write(&1u64)?;
+ reply.write(&u64::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
+ reply.write(&(None as Option<Vec<u64>>))?;
+ }
+ bindings::Transaction_TEST_F32 => {
+ assert_eq!(parcel.read::<f32>()?, 0f32);
+ let floats = parcel.read::<Vec<f32>>()?;
+ assert!(floats[0].is_nan());
+ assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]);
+ assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None);
+
+ reply.write(&0f32)?;
+ reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?;
+ reply.write(&(None as Option<Vec<f32>>))?;
+ }
+ bindings::Transaction_TEST_F64 => {
+ assert_eq!(parcel.read::<f64>()?, 0f64);
+ let doubles = parcel.read::<Vec<f64>>()?;
+ assert!(doubles[0].is_nan());
+ assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]);
+ assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None);
+
+ reply.write(&0f64)?;
+ reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?;
+ reply.write(&(None as Option<Vec<f64>>))?;
+ }
+ bindings::Transaction_TEST_STRING => {
+ let s: Option<String> = parcel.read()?;
+ assert_eq!(s.as_deref(), Some("testing"));
+ let s: Option<String> = parcel.read()?;
+ assert_eq!(s, None);
+ let s: Option<Vec<Option<String>>> = parcel.read()?;
+ for (s, expected) in s
+ .unwrap()
+ .iter()
+ .zip(unsafe { bindings::TESTDATA_STRS }.iter())
+ {
+ let expected = unsafe {
+ expected
+ .as_ref()
+ .and_then(|e| CStr::from_ptr(e).to_str().ok())
+ };
+ assert_eq!(s.as_deref(), expected);
+ }
+ let s: Option<Vec<Option<String>>> = parcel.read()?;
+ assert_eq!(s, None);
+
+ let strings: Vec<Option<String>> = unsafe {
+ bindings::TESTDATA_STRS
+ .iter()
+ .map(|s| {
+ s.as_ref().map(|s| {
+ CStr::from_ptr(s)
+ .to_str()
+ .expect("String was not UTF-8")
+ .to_owned()
+ })
+ })
+ .collect()
+ };
+
+ reply.write("testing")?;
+ reply.write(&(None as Option<String>))?;
+ reply.write(&strings)?;
+ reply.write(&(None as Option<Vec<String>>))?;
+ }
+ bindings::Transaction_TEST_FILE_DESCRIPTOR => {
+ let file1 = parcel.read::<ParcelFileDescriptor>()?;
+ let file2 = parcel.read::<ParcelFileDescriptor>()?;
+ let files = parcel.read::<Vec<Option<ParcelFileDescriptor>>>()?;
+
+ reply.write(&file1)?;
+ reply.write(&file2)?;
+ reply.write(&files)?;
+ }
+ bindings::Transaction_TEST_IBINDER => {
+ assert!(parcel.read::<Option<SpIBinder>>()?.is_some());
+ assert!(parcel.read::<Option<SpIBinder>>()?.is_none());
+ let ibinders = parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.unwrap();
+ assert_eq!(ibinders.len(), 2);
+ assert!(ibinders[0].is_some());
+ assert!(ibinders[1].is_none());
+ assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none());
+
+ let service = unsafe {
+ SERVICE
+ .as_ref()
+ .expect("Global binder service not initialized")
+ .clone()
+ };
+ reply.write(&service)?;
+ reply.write(&(None as Option<&SpIBinder>))?;
+ reply.write(&[Some(&service), None][..])?;
+ reply.write(&(None as Option<Vec<Option<&SpIBinder>>>))?;
+ }
+ bindings::Transaction_TEST_STATUS => {
+ let status: Status = parcel.read()?;
+ assert!(status.is_ok());
+ let status: Status = parcel.read()?;
+ assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER);
+ assert_eq!(
+ status.get_description(),
+ "Status(-4, EX_NULL_POINTER): 'a status message'"
+ );
+ let status: Status = parcel.read()?;
+ assert_eq!(status.service_specific_error(), 42);
+ assert_eq!(
+ status.get_description(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '42: a service-specific error'"
+ );
+
+ reply.write(&Status::ok())?;
+ reply.write(&Status::new_exception(
+ ExceptionCode::NULL_POINTER,
+ Some(&CString::new("a status message").unwrap()),
+ ))?;
+ reply.write(&Status::new_service_specific_error(
+ 42,
+ Some(&CString::new("a service-specific error").unwrap()),
+ ))?;
+ }
+ bindings::Transaction_TEST_FAIL => {
+ panic!("Testing expected failure");
+ }
+ _ => return Err(StatusCode::UNKNOWN_TRANSACTION),
+ }
+
+ assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ Ok(())
+}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 940a26b..9606daa 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -20,6 +20,7 @@
},
srcs: [
"GpuMemTest.cpp",
+ "GpuMemTracerTest.cpp",
"GpuStatsTest.cpp",
],
shared_libs: [
@@ -29,14 +30,19 @@
"libcutils",
"libgfxstats",
"libgpumem",
+ "libgpumemtracer",
"libgraphicsenv",
"liblog",
+ "libprotobuf-cpp-lite",
+ "libprotoutil",
"libstatslog",
"libstatspull",
"libutils",
],
static_libs: [
"libgmock",
+ "libperfetto_client_experimental",
+ "perfetto_trace_protos",
],
require_root: true,
}
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
new file mode 100644
index 0000000..d76f039
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2020 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 "gpuservice_unittest"
+
+#include <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <perfetto/trace/trace.pb.h>
+#include <tracing/GpuMemTracer.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint32_t TEST_GLOBAL_PID = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint32_t TEST_GLOBAL_GPU_ID = 0;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint32_t TEST_PROC_PID_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint32_t TEST_PROC_1_GPU_ID = 0;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint32_t TEST_PROC_PID_2 = 2;
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_PROC_2_GPU_ID = 1;
+
+class GpuMemTracerTest : public testing::Test {
+public:
+ GpuMemTracerTest() {
+ 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());
+ }
+
+ ~GpuMemTracerTest() {
+ 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());
+ }
+
+ void SetUp() override {
+ bpf::setrlimitForTest();
+
+ mGpuMem = std::make_shared<GpuMem>();
+ mGpuMemTracer = std::make_unique<GpuMemTracer>();
+ mGpuMemTracer->initializeForTest(mGpuMem);
+ mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+
+ errno = 0;
+ mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+
+ EXPECT_EQ(0, errno);
+ EXPECT_LE(0, mTestMap.getMap().get());
+ EXPECT_TRUE(mTestMap.isValid());
+ }
+
+ int getTracerThreadCount() { return mGpuMemTracer->tracerThreadCount; }
+
+ std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsBlocking(
+ perfetto::TracingSession* tracingSession) {
+ std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+ perfetto::protos::Trace trace;
+ trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
+
+ std::vector<perfetto::protos::TracePacket> packets;
+ for (const auto& packet : trace.packet()) {
+ if (!packet.has_gpu_mem_total_event()) {
+ continue;
+ }
+ packets.emplace_back(packet);
+ }
+ return packets;
+ }
+
+ std::shared_ptr<GpuMem> mGpuMem;
+ TestableGpuMem mTestableGpuMem;
+ std::unique_ptr<GpuMemTracer> mGpuMemTracer;
+ bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+static constexpr uint64_t getSizeForPid(uint32_t pid) {
+ switch (pid) {
+ case TEST_GLOBAL_PID:
+ return TEST_GLOBAL_VAL;
+ case TEST_PROC_PID_1:
+ return TEST_PROC_VAL_1;
+ case TEST_PROC_PID_2:
+ return TEST_PROC_VAL_2;
+ }
+ return 0;
+}
+
+static constexpr uint32_t getGpuIdForPid(uint32_t pid) {
+ switch (pid) {
+ case TEST_GLOBAL_PID:
+ return TEST_GLOBAL_GPU_ID;
+ case TEST_PROC_PID_1:
+ return TEST_PROC_1_GPU_ID;
+ case TEST_PROC_PID_2:
+ return TEST_PROC_2_GPU_ID;
+ }
+ return 0;
+}
+
+TEST_F(GpuMemTracerTest, traceInitialCountersAfterGpuMemInitialize) {
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+ mTestableGpuMem.setInitialized();
+
+ // Only 1 tracer thread should be existing for test.
+ EXPECT_EQ(getTracerThreadCount(), 1);
+ auto tracingSession = mGpuMemTracer->getTracingSessionForTest();
+
+ tracingSession->StartBlocking();
+ // Sleep for a short time to let the tracer thread finish its work
+ sleep(1);
+ tracingSession->StopBlocking();
+
+ // The test tracer thread should have finished its execution by now.
+ EXPECT_EQ(getTracerThreadCount(), 0);
+
+ auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 3);
+
+ const auto& packet0 = packets[0];
+ ASSERT_TRUE(packet0.has_timestamp());
+ ASSERT_TRUE(packet0.has_gpu_mem_total_event());
+ const auto& gpuMemEvent0 = packet0.gpu_mem_total_event();
+ ASSERT_TRUE(gpuMemEvent0.has_pid());
+ const auto& pid0 = gpuMemEvent0.pid();
+ ASSERT_TRUE(gpuMemEvent0.has_size());
+ EXPECT_EQ(gpuMemEvent0.size(), getSizeForPid(pid0));
+ ASSERT_TRUE(gpuMemEvent0.has_gpu_id());
+ EXPECT_EQ(gpuMemEvent0.gpu_id(), getGpuIdForPid(pid0));
+
+ const auto& packet1 = packets[1];
+ ASSERT_TRUE(packet1.has_timestamp());
+ ASSERT_TRUE(packet1.has_gpu_mem_total_event());
+ const auto& gpuMemEvent1 = packet1.gpu_mem_total_event();
+ ASSERT_TRUE(gpuMemEvent1.has_pid());
+ const auto& pid1 = gpuMemEvent1.pid();
+ ASSERT_TRUE(gpuMemEvent1.has_size());
+ EXPECT_EQ(gpuMemEvent1.size(), getSizeForPid(pid1));
+ ASSERT_TRUE(gpuMemEvent1.has_gpu_id());
+ EXPECT_EQ(gpuMemEvent1.gpu_id(), getGpuIdForPid(pid1));
+
+ const auto& packet2 = packets[2];
+ ASSERT_TRUE(packet2.has_timestamp());
+ ASSERT_TRUE(packet2.has_gpu_mem_total_event());
+ const auto& gpuMemEvent2 = packet2.gpu_mem_total_event();
+ ASSERT_TRUE(gpuMemEvent2.has_pid());
+ const auto& pid2 = gpuMemEvent2.pid();
+ ASSERT_TRUE(gpuMemEvent2.has_size());
+ EXPECT_EQ(gpuMemEvent2.size(), getSizeForPid(pid2));
+ ASSERT_TRUE(gpuMemEvent2.has_gpu_id());
+ EXPECT_EQ(gpuMemEvent2.gpu_id(), getGpuIdForPid(pid2));
+}
+
+TEST_F(GpuMemTracerTest, noTracingWithoutGpuMemInitialize) {
+ // Only 1 tracer thread should be existing for test.
+ EXPECT_EQ(getTracerThreadCount(), 1);
+
+ auto tracingSession = mGpuMemTracer->getTracingSessionForTest();
+
+ tracingSession->StartBlocking();
+ // Sleep for a short time to let the tracer thread finish its work
+ sleep(1);
+ tracingSession->StopBlocking();
+
+ // The test tracer thread should have finished its execution by now.
+ EXPECT_EQ(getTracerThreadCount(), 0);
+
+ auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0);
+}
+} // namespace android
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 6366e1d..44a30ea 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -44,9 +44,35 @@
args.backends = perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
registerDataSource();
- std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this, true);
pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
tracerThread.detach();
+ tracerThreadCount++;
+}
+
+void GpuMemTracer::initializeForTest(std::shared_ptr<GpuMem> gpuMem) {
+ mGpuMem = gpuMem;
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kInProcessBackend;
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this, false);
+ pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThreadForTest");
+ tracerThread.detach();
+ tracerThreadCount++;
+}
+
+// Each tracing session can be used for a single block of Start -> Stop.
+std::unique_ptr<perfetto::TracingSession> GpuMemTracer::getTracingSessionForTest() {
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name(GpuMemTracer::kGpuMemDataSource);
+
+ auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+ tracingSession->Setup(cfg);
+ return tracingSession;
}
void GpuMemTracer::registerDataSource() {
@@ -55,8 +81,8 @@
GpuMemDataSource::Register(dsd);
}
-void GpuMemTracer::threadLoop() {
- while (true) {
+void GpuMemTracer::threadLoop(bool infiniteLoop) {
+ do {
{
std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
while (!sTraceStarted) {
@@ -68,7 +94,11 @@
std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
sTraceStarted = false;
}
- }
+ } while (infiniteLoop);
+
+ // Thread loop is exiting. Reduce the tracerThreadCount to reflect the number of active threads
+ // in the wait loop.
+ tracerThreadCount--;
}
void GpuMemTracer::traceInitialCounters() {
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
index 40deb4c..ae871f1 100644
--- a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -20,6 +20,10 @@
#include <mutex>
+namespace perfetto::protos {
+class TracePacket;
+}
+
namespace android {
class GpuMem;
@@ -45,16 +49,37 @@
// perfetto::kInProcessBackend in tests.
void registerDataSource();
+ // TODO(b/175904796): Refactor gpuservice lib to include perfetto lib and move the test
+ // functions into the unittests.
+ // Functions only used for testing with in-process backend. These functions require the static
+ // perfetto lib to be linked. If the tests have a perfetto linked, while libgpumemtracer.so also
+ // has one linked, they will both use different static states maintained in perfetto. Since the
+ // static perfetto states are not shared, tracing sessions created in the unit test are not
+ // recognized by GpuMemTracer. As a result, we cannot use any of the perfetto functions from
+ // this class, which defeats the purpose of the unit test. To solve this, we restrict all
+ // tracing functionality to this class, while the unit test validates the data.
+ // Sets up the perfetto in-process backend and calls into registerDataSource.
+ void initializeForTest(std::shared_ptr<GpuMem>);
+ // Creates a tracing session with in process backend, for testing.
+ std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
+ // Read and filter the gpu memory packets from the created trace.
+ std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsForTestBlocking(
+ perfetto::TracingSession* tracingSession);
+
static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
static std::condition_variable sCondition;
static std::mutex sTraceMutex;
static bool sTraceStarted;
private:
- void traceInitialCounters();
- void threadLoop();
+ // Friend class for testing
+ friend class GpuMemTracerTest;
+ void threadLoop(bool infiniteLoop);
+ void traceInitialCounters();
std::shared_ptr<GpuMem> mGpuMem;
+ // Count of how many tracer threads are currently active. Useful for testing.
+ std::atomic<int32_t> tracerThreadCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index bdd04db..2734ee9 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -20,7 +20,6 @@
"-DEGL_EGLEXT_PROTOTYPES",
],
shared_libs: [
- "android.frameworks.vr.composer@2.0",
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
"android.hardware.configstore@1.1",
@@ -58,21 +57,12 @@
"libutils",
"libSurfaceFlingerProp",
],
- // VrComposer is not used when building surfaceflinger for vendors
- target: {
- vendor: {
- exclude_shared_libs: [
- "android.frameworks.vr.composer@2.0",
- ],
- },
- },
static_libs: [
"libcompositionengine",
"libperfetto_client_experimental",
"librenderengine",
"libserviceutils",
"libtrace_proto",
- "libvrflinger",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -198,17 +188,6 @@
// can be easily replaced.
"SurfaceFlingerFactory.cpp",
],
- cflags: [
- "-DUSE_VR_COMPOSER=1",
- ],
- // VrComposer is not used when building surfaceflinger for vendors
- target: {
- vendor: {
- cflags: [
- "-DUSE_VR_COMPOSER=0",
- ],
- },
- },
logtags: ["EventLog/EventLogTags.logtags"],
}
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f0b0200..df26a3d 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -176,7 +176,14 @@
if (!holes.isEmpty()) {
targetSettings.clearRegion.orSelf(holes);
}
- return std::nullopt;
+
+ if (mSidebandStream != nullptr) {
+ // For surfaceview of tv sideband, there is no activeBuffer
+ // in bufferqueue, we need return LayerSettings.
+ return result;
+ } else {
+ return std::nullopt;
+ }
}
bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
(isSecure() && !targetSettings.isSecure);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b37ca33..4863297 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -6,7 +6,6 @@
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
],
shared_libs: [
- "android.frameworks.vr.composer@2.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index b4ed92f..77400eb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -130,16 +130,6 @@
Rect geomContentCrop;
Rect geomCrop;
- /*
- * Extra metadata
- */
-
- // The type for this layer
- int type{0};
-
- // The appId for this layer
- int appId{0};
-
GenericLayerMetadataMap metadata;
/*
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 02e3a45..1338538 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -75,10 +75,6 @@
dumpVal(out, "alpha", alpha);
dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
- out.append("\n ");
- dumpVal(out, "type", type);
- dumpVal(out, "appId", appId);
-
if (!metadata.empty()) {
out.append("\n metadata {");
for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1faf775..3aed7f5 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -402,13 +402,6 @@
outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
- static_cast<uint32_t>(outputIndependentState.appId));
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-
for (const auto& [name, entry] : outputIndependentState.metadata) {
if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
error != hal::Error::NONE) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index d21b97e..87911cc 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -66,7 +66,6 @@
MOCK_METHOD1(setTransform, Error(hal::Transform));
MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
MOCK_METHOD1(setZOrder, Error(uint32_t));
- MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
MOCK_METHOD3(setLayerGenericMetadata,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 020f93a..c32ef11 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -676,8 +676,6 @@
static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
static_cast<Hwc2::IComposerClient::BlendMode>(41);
static constexpr float kAlpha = 51.f;
- static constexpr uint32_t kType = 61u;
- static constexpr uint32_t kAppId = 62u;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
@@ -711,8 +709,6 @@
mLayerFEState.blendMode = kBlendMode;
mLayerFEState.alpha = kAlpha;
- mLayerFEState.type = kType;
- mLayerFEState.appId = kAppId;
mLayerFEState.colorTransform = kColorTransform;
mLayerFEState.color = kColor;
mLayerFEState.surfaceDamage = kSurfaceDamage;
@@ -746,7 +742,6 @@
EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
}
void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index a3f1b52..1bf43da 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -117,63 +117,7 @@
namespace impl {
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
- : CommandWriterBase(initialMaxSize) {}
-
-Composer::CommandWriter::~CommandWriter()
-{
-}
-
-void Composer::CommandWriter::setLayerInfo(uint32_t type, uint32_t appId)
-{
- constexpr uint16_t kSetLayerInfoLength = 2;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_LAYER_INFO),
- kSetLayerInfoLength);
- write(type);
- write(appId);
- endCommand();
-}
-
-void Composer::CommandWriter::setClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- constexpr uint16_t kSetClientTargetMetadataLength = 7;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA),
- kSetClientTargetMetadataLength);
- writeBufferMetadata(metadata);
- endCommand();
-}
-
-void Composer::CommandWriter::setLayerBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- constexpr uint16_t kSetLayerBufferMetadataLength = 7;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA),
- kSetLayerBufferMetadataLength);
- writeBufferMetadata(metadata);
- endCommand();
-}
-
-void Composer::CommandWriter::writeBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- write(metadata.width);
- write(metadata.height);
- write(metadata.stride);
- write(metadata.layerCount);
- writeSigned(static_cast<int32_t>(metadata.format));
- write64(metadata.usage);
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
-Composer::Composer(const std::string& serviceName)
- : mWriter(kWriterInitialSize),
- mIsUsingVrComposer(serviceName == std::string("vr"))
-{
+Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
mComposer = V2_1::IComposer::getService(serviceName);
if (mComposer == nullptr) {
@@ -215,15 +159,6 @@
if (mClient == nullptr) {
LOG_ALWAYS_FATAL("failed to create composer client");
}
-
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer) {
- sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient);
- if (vrClient == nullptr) {
- LOG_ALWAYS_FATAL("failed to create vr composer client");
- }
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
}
Composer::~Composer() = default;
@@ -262,10 +197,6 @@
}
}
-bool Composer::isRemote() {
- return mClient->isRemote();
-}
-
void Composer::resetCommands() {
mWriter.reset();
}
@@ -587,20 +518,6 @@
{
mWriter.selectDisplay(display);
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer && target.get()) {
- IVrComposerClient::BufferMetadata metadata = {
- .width = target->getWidth(),
- .height = target->getHeight(),
- .stride = target->getStride(),
- .layerCount = target->getLayerCount(),
- .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()),
- .usage = target->getUsage(),
- };
- mWriter.setClientTargetMetadata(metadata);
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
const native_handle_t* handle = nullptr;
if (target.get()) {
handle = target->getNativeBuffer()->handle;
@@ -720,20 +637,6 @@
mWriter.selectDisplay(display);
mWriter.selectLayer(layer);
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer && buffer.get()) {
- IVrComposerClient::BufferMetadata metadata = {
- .width = buffer->getWidth(),
- .height = buffer->getHeight(),
- .stride = buffer->getStride(),
- .layerCount = buffer->getLayerCount(),
- .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()),
- .usage = buffer->getUsage(),
- };
- mWriter.setLayerBufferMetadata(metadata);
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
const native_handle_t* handle = nullptr;
if (buffer.get()) {
handle = buffer->getNativeBuffer()->handle;
@@ -850,27 +753,6 @@
return Error::NONE;
}
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId)
-{
- if (mIsUsingVrComposer) {
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerInfo(type, appId);
- }
- return Error::NONE;
-}
-#else
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) {
- if (mIsUsingVrComposer) {
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- }
- return Error::NONE;
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
Error Composer::execute()
{
// prepare input command queue
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 00ef782..5b66809 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -27,9 +27,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
@@ -47,10 +44,6 @@
namespace Hwc2 {
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-using frameworks::vr::composer::V2_0::IVrComposerClient;
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
namespace types = hardware::graphics::common;
namespace V2_1 = hardware::graphics::composer::V2_1;
@@ -91,11 +84,6 @@
virtual void registerCallback(const sp<IComposerCallback>& callback) = 0;
- // Returns true if the connected composer service is running in a remote
- // process, false otherwise. This will return false if the service is
- // configured in passthrough mode, for example.
- virtual bool isRemote() = 0;
-
// Reset all pending commands in the command buffer. Useful if you want to
// skip a frame but have already queued some commands.
virtual void resetCommands() = 0;
@@ -104,7 +92,6 @@
virtual Error executeCommands() = 0;
virtual uint32_t getMaxVirtualDisplayCount() = 0;
- virtual bool isUsingVrComposer() const = 0;
virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) = 0;
virtual Error destroyVirtualDisplay(Display display) = 0;
@@ -188,7 +175,6 @@
virtual Error setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) = 0;
virtual Error setLayerZOrder(Display display, Layer layer, uint32_t z) = 0;
- virtual Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) = 0;
// Composer HAL 2.2
virtual Error setLayerPerFrameMetadata(
@@ -344,11 +330,6 @@
void registerCallback(const sp<IComposerCallback>& callback) override;
- // Returns true if the connected composer service is running in a remote
- // process, false otherwise. This will return false if the service is
- // configured in passthrough mode, for example.
- bool isRemote() override;
-
// Reset all pending commands in the command buffer. Useful if you want to
// skip a frame but have already queued some commands.
void resetCommands() override;
@@ -357,7 +338,6 @@
Error executeCommands() override;
uint32_t getMaxVirtualDisplayCount() override;
- bool isUsingVrComposer() const override { return mIsUsingVrComposer; }
Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) override;
Error destroyVirtualDisplay(Display display) override;
@@ -436,7 +416,6 @@
Error setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) override;
Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
- Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) override;
// Composer HAL 2.2
Error setLayerPerFrameMetadata(
@@ -490,29 +469,11 @@
IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
private:
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- class CommandWriter : public CommandWriterBase {
- public:
- explicit CommandWriter(uint32_t initialMaxSize);
- ~CommandWriter() override;
-
- void setLayerInfo(uint32_t type, uint32_t appId);
- void setClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
- void setLayerBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
-
- private:
- void writeBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
- };
-#else
class CommandWriter : public CommandWriterBase {
public:
explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
~CommandWriter() override {}
};
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
// Many public functions above simply write a command into the command
// queue to batch the calls. validateDisplay and presentDisplay will call
@@ -531,10 +492,6 @@
64 * 1024 / sizeof(uint32_t) - 16;
CommandWriter mWriter;
CommandReader mReader;
-
- // When true, the we attach to the vr_hwcomposer service instead of the
- // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr.
- const bool mIsUsingVrComposer;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 08559bd..e6bff04 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -977,12 +977,6 @@
return static_cast<Error>(intError);
}
-Error Layer::setInfo(uint32_t type, uint32_t appId)
-{
- auto intError = mComposer.setLayerInfo(mDisplayId, mId, type, appId);
- return static_cast<Error>(intError);
-}
-
// Composer HAL 2.3
Error Layer::setColorTransform(const android::mat4& matrix) {
if (matrix == mColorMatrix) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 6819ff4..8bd6ea8 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -380,7 +380,6 @@
[[clang::warn_unused_result]] virtual hal::Error setVisibleRegion(
const android::Region& region) = 0;
[[clang::warn_unused_result]] virtual hal::Error setZOrder(uint32_t z) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setInfo(uint32_t type, uint32_t appId) = 0;
// Composer HAL 2.3
[[clang::warn_unused_result]] virtual hal::Error setColorTransform(
@@ -422,7 +421,6 @@
hal::Error setTransform(hal::Transform transform) override;
hal::Error setVisibleRegion(const android::Region& region) override;
hal::Error setZOrder(uint32_t z) override;
- hal::Error setInfo(uint32_t type, uint32_t appId) override;
// Composer HAL 2.3
hal::Error setColorTransform(const android::mat4& matrix) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7a2f0f3..7db9359 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -815,10 +815,6 @@
});
}
-bool HWComposer::isUsingVrComposer() const {
- return getComposer()->isUsingVrComposer();
-}
-
status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
@@ -883,11 +879,6 @@
bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
bool hasDisplayIdentificationData) const {
- if (isUsingVrComposer() && mInternalHwcDisplayId) {
- ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
- return true;
- }
-
if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
hwcDisplayId);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index c355ebd..e05e41a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -195,8 +195,6 @@
virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
ui::RenderIntent renderIntent) = 0;
- virtual bool isUsingVrComposer() const = 0;
-
// Composer 2.4
virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
@@ -336,8 +334,6 @@
status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
ui::RenderIntent renderIntent) override;
- bool isUsingVrComposer() const override;
-
// Composer 2.4
DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 55af849..3282a5a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -492,9 +492,6 @@
compositionState->geomUsesSourceCrop = usesSourceCrop();
compositionState->isSecure = isSecure();
- compositionState->type = type;
- compositionState->appId = appId;
-
compositionState->metadata.clear();
const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
for (const auto& [key, mandatory] : supportedMetadata) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 02b5324..261722d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -46,7 +46,6 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <dlfcn.h>
-#include <dvr/vr_flinger.h>
#include <errno.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
@@ -267,7 +266,6 @@
bool SurfaceFlinger::useHwcForRgbToYuv;
uint64_t SurfaceFlinger::maxVirtualDisplaySize;
bool SurfaceFlinger::hasSyncFramework;
-bool SurfaceFlinger::useVrFlinger;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
@@ -332,9 +330,6 @@
maxVirtualDisplaySize = max_virtual_display_dimension(0);
- // Vr flinger is only enabled on Daydream ready devices.
- useVrFlinger = use_vr_flinger(false);
-
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
@@ -609,10 +604,6 @@
mInputFlinger = interface_cast<IInputFlinger>(input);
}
- if (mVrFlinger) {
- mVrFlinger->OnBootFinished();
- }
-
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
@@ -688,9 +679,6 @@
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
mCompositionEngine->setTimeStats(mTimeStats);
-
- LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
- "Starting with vr flinger active is not currently supported.");
mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
// Process any initial hotplug and resulting display changes.
@@ -700,30 +688,6 @@
LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
"Internal display is disconnected.");
- if (useVrFlinger) {
- auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
- // This callback is called from the vr flinger dispatch thread. We
- // need to call signalTransaction(), which requires holding
- // mStateLock when we're not on the main thread. Acquiring
- // mStateLock from the vr flinger dispatch thread might trigger a
- // deadlock in surface flinger (see b/66916578), so post a message
- // to be handled on the main thread instead.
- static_cast<void>(schedule([=] {
- ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
- mVrFlingerRequestsDisplay = requestDisplay;
- signalTransaction();
- }));
- };
- mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
- getHwComposer()
- .fromPhysicalDisplayId(*display->getId())
- .value_or(0),
- vrFlingerRequestDisplayCallback);
- if (!mVrFlinger) {
- ALOGE("Failed to start vrflinger");
- }
- }
-
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -1700,98 +1664,6 @@
}
}
-void SurfaceFlinger::resetDisplayState() {
- mScheduler->disableHardwareVsync(true);
- // Clear the drawing state so that the logic inside of
- // handleTransactionLocked will fire. It will determine the delta between
- // mCurrentState and mDrawingState and re-apply all changes when we make the
- // transition.
- mDrawingState.displays.clear();
- mDisplays.clear();
-}
-
-void SurfaceFlinger::updateVrFlinger() {
- ATRACE_CALL();
- if (!mVrFlinger)
- return;
- bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
- if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) {
- return;
- }
-
- if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) {
- ALOGE("Vr flinger is only supported for remote hardware composer"
- " service connections. Ignoring request to transition to vr"
- " flinger.");
- mVrFlingerRequestsDisplay = false;
- return;
- }
-
- Mutex::Autolock _l(mStateLock);
-
- sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
- LOG_ALWAYS_FATAL_IF(!display);
-
- const hal::PowerMode currentDisplayPowerMode = display->getPowerMode();
-
- // Clear out all the output layers from the composition engine for all
- // displays before destroying the hardware composer interface. This ensures
- // any HWC layers are destroyed through that interface before it becomes
- // invalid.
- for (const auto& [token, displayDevice] : mDisplays) {
- displayDevice->getCompositionDisplay()->clearOutputLayers();
- }
-
- // This DisplayDevice will no longer be relevant once resetDisplayState() is
- // called below. Clear the reference now so we don't accidentally use it
- // later.
- display.clear();
-
- if (!vrFlingerRequestsDisplay) {
- mVrFlinger->SeizeDisplayOwnership();
- }
-
- resetDisplayState();
- // Delete the current instance before creating the new one
- mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
- mCompositionEngine->setHwComposer(getFactory().createHWComposer(
- vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
- mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
-
- LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
- "Switched to non-remote hardware composer");
-
- if (vrFlingerRequestsDisplay) {
- mVrFlinger->GrantDisplayOwnership();
- }
-
- mVisibleRegionsDirty = true;
- invalidateHwcGeometry();
-
- // Re-enable default display.
- display = getDefaultDisplayDeviceLocked();
- LOG_ALWAYS_FATAL_IF(!display);
- setPowerModeInternal(display, currentDisplayPowerMode);
-
- // Reset the timing values to account for the period of the swapped in HWC
- const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
- mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
- // The present fences returned from vr_hwc are not an accurate
- // representation of vsync times.
- mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework);
-
- // Use phase of 0 since phase is not known.
- // Use latency of 0, which will snap to the ideal latency.
- DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
- setCompositorTimingSnapped(stats, 0);
-
- mScheduler->resyncToHardwareVsync(false, vsyncPeriod);
-
- mRepaintEverything = true;
- setTransactionFlags(eDisplayTransactionNeeded);
-}
-
sp<Fence> SurfaceFlinger::previousFrameFence() {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
@@ -1960,11 +1832,6 @@
}
}
- // Now that we're going to make it to the handleMessageTransaction()
- // call below it's safe to call updateVrFlinger(), which will
- // potentially trigger a display handoff.
- updateVrFlinger();
-
if (mTracingEnabledChanged) {
mTracingEnabled = mTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2611,7 +2478,7 @@
builder.setIsSecure(state.isSecure);
builder.setLayerStackId(state.layerStack);
builder.setPowerAdvisor(&mPowerAdvisor);
- builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+ builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
builder.setName(state.displayName);
const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -4832,15 +4699,6 @@
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.dump(result);
- /*
- * Dump VrFlinger state if in use.
- */
- if (mVrFlingerRequestsDisplay && mVrFlinger) {
- result.append("VrFlinger state:\n");
- result.append(mVrFlinger->Dump());
- result.append("\n");
- }
-
result.append(mTimeStats->miniDump());
result.append("\n");
}
@@ -5223,11 +5081,8 @@
}
return NO_ERROR;
}
- // Is VrFlinger active?
- case 1028: {
- Mutex::Autolock _l(mStateLock);
- reply->writeBool(getHwComposer().isUsingVrComposer());
- return NO_ERROR;
+ case 1028: { // Unused.
+ return NAME_NOT_FOUND;
}
// Set buffer size for SF tracing (value in KB)
case 1029: {
@@ -6206,9 +6061,6 @@
// on the work to remove the table in that bug rather than adding more to
// it.
static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
- // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
- // supported, and exposed via the
- // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
{"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
{"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
};
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c727574..61bd020 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -109,10 +109,6 @@
class RenderEngine;
} // namespace renderengine
-namespace dvr {
-class VrFlinger;
-} // namespace dvr
-
enum {
eTransactionNeeded = 0x01,
eTraversalNeeded = 0x02,
@@ -798,8 +794,7 @@
// The following thread safety rules apply when accessing mHwc, either
// directly or via getHwComposer():
//
- // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
- // only when switching into and out of vr. Recreating mHwc must only be
+ // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be
// done on the main thread.
//
// 2. When accessing mHwc on the main thread, it's not necessary to acquire
@@ -983,14 +978,6 @@
void onFrameRateFlexibilityTokenReleased();
- /* ------------------------------------------------------------------------
- * VrFlinger
- */
- void resetDisplayState() REQUIRES(mStateLock);
-
- // Check to see if we should handoff to vr flinger.
- void updateVrFlinger();
-
void updateColorMatrixLocked();
/* ------------------------------------------------------------------------
@@ -1175,9 +1162,6 @@
// to mWindowManager or mInputFlinger
std::atomic<bool> mBootFinished = false;
- std::unique_ptr<dvr::VrFlinger> mVrFlinger;
- std::atomic<bool> mVrFlingerRequestsDisplay = false;
- static bool useVrFlinger;
std::thread::id mMainThreadId = std::this_thread::get_id();
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32d722e..faa619e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -577,8 +577,6 @@
.Times(1);
// TODO: Coverage of other values
EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
- // TODO: Coverage of other values
- EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
// These expectations retire on saturation as the code path these
// expectations are for appears to make an extra call to them.
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 06bdcdc..0323778 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1298,55 +1298,6 @@
}
/* ------------------------------------------------------------------------
- * SurfaceFlinger::resetDisplayState
- */
-
-TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) {
- using Case = NonHwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // vsync is enabled and available
- mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true;
- mFlinger.scheduler()->mutableHWVsyncAvailable() = true;
-
- // A display exists
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call disable vsyncs
- EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
-
- // The call ends any display resyncs
- EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.resetDisplayState();
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // vsyncs should be off and not available.
- EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled());
- EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable());
-
- // The display should have been removed from the display map.
- EXPECT_FALSE(hasDisplayDevice(existing.token()));
-
- // The display should still exist in the current state
- EXPECT_TRUE(hasCurrentDisplayState(existing.token()));
-
- // The display should have been removed from the drawing state
- EXPECT_FALSE(hasDrawingDisplayState(existing.token()));
-}
-
-/* ------------------------------------------------------------------------
* DisplayDevice::GetBestColorMode
*/
class GetBestColorModeTest : public DisplayTransactionTest {
@@ -2019,8 +1970,6 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillOnce(Return(false));
-
setupCommonCallExpectationsForConnectProcessing<Case>();
// --------------------------------------------------------------------
@@ -2082,7 +2031,6 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
.Times(0);
@@ -2139,20 +2087,9 @@
SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
Return(Error::NONE)));
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfExternalForVrComposer) {
- // Inject a primary display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(true));
-
- ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
}
@@ -2177,8 +2114,6 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
setupCommonCallExpectationsForConnectProcessing<Case>();
setupCommonCallExpectationsForDisconnectProcessing<Case>();
@@ -2225,8 +2160,6 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
setupCommonCallExpectationsForConnectProcessing<Case>();
setupCommonCallExpectationsForDisconnectProcessing<Case>();
@@ -2385,11 +2318,6 @@
mFlinger.mutableCurrentState().displays.removeItem(existing.token());
// --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
- // --------------------------------------------------------------------
// Invocation
mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cbf264d..cd2a482 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -289,8 +289,6 @@
return mFlinger->destroyDisplay(displayToken);
}
- auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
-
auto setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index c2c5072..cd9b87a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -52,11 +52,9 @@
MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
- MOCK_METHOD0(isRemote, bool());
MOCK_METHOD0(resetCommands, void());
MOCK_METHOD0(executeCommands, Error());
MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
- MOCK_CONST_METHOD0(isUsingVrComposer, bool());
MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
MOCK_METHOD1(acceptDisplayChanges, Error(Display));
@@ -110,7 +108,6 @@
MOCK_METHOD3(setLayerVisibleRegion,
Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t));
- MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
MOCK_METHOD4(getDisplayedContentSamplingAttributes,