Merge "Fix AIBinder_setMinSchedulerPolicy failed to set sched policy" into main
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index bb3faaf..6686717 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
package android.view;
-@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h" rust_type "nativewindow::Surface";
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 0ce7711..9695e07 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -95,7 +95,7 @@
flags = O_RDWR;
checkRead = checkWrite = true;
} else {
- mErrorLog << "Invalid mode requested: " << mode.c_str() << endl;
+ mErrorLog << "Invalid mode requested: " << mode << endl;
return -EINVAL;
}
int fd = open(fullPath.c_str(), flags, S_IRWXU|S_IRWXG);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 522442f..326f927 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -198,6 +198,7 @@
static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
static const std::string ANR_DIR = "/data/anr/";
static const std::string ANR_FILE_PREFIX = "anr_";
+static const std::string ANR_TRACE_FILE_PREFIX = "trace_";
static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
@@ -1179,6 +1180,10 @@
} else {
printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
}
+
+ // Add Java anr traces (such as generated by the Finalizer Watchdog).
+ AddDumps(ds.anr_trace_data_.begin(), ds.anr_trace_data_.end(), "JAVA ANR TRACES",
+ true /* add_to_zip */);
}
static void AddAnrTraceFiles() {
@@ -1905,6 +1910,7 @@
if (!PropertiesHelper::IsDryRun()) {
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX);
ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
+ ds.anr_trace_data_ = GetDumpFds(ANR_DIR, ANR_TRACE_FILE_PREFIX);
ds.shutdown_checkpoints_ = GetDumpFds(
SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX);
}
@@ -3046,6 +3052,7 @@
}
tombstone_data_.clear();
anr_data_.clear();
+ anr_trace_data_.clear();
shutdown_checkpoints_.clear();
// Instead of shutdown the pool, we delete temporary files directly since
@@ -3341,6 +3348,7 @@
tombstone_data_.clear();
anr_data_.clear();
+ anr_trace_data_.clear();
shutdown_checkpoints_.clear();
return (consent_callback_ != nullptr &&
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8a31c31..596aa76 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -526,6 +526,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // List of open Java traces files in the anr directory.
+ std::vector<DumpData> anr_trace_data_;
+
// List of open shutdown checkpoint files.
std::vector<DumpData> shutdown_checkpoints_;
diff --git a/cmds/flatland/Android.bp b/cmds/flatland/Android.bp
new file mode 100644
index 0000000..39a0d75
--- /dev/null
+++ b/cmds/flatland/Android.bp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_native_license",
+ ],
+}
+
+cc_benchmark {
+ name: "flatland",
+ auto_gen_config: false,
+ srcs: [
+ "Composers.cpp",
+ "GLHelper.cpp",
+ "Renderers.cpp",
+ "Main.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ stem: "flatland",
+ },
+ lib64: {
+ stem: "flatland64",
+ },
+ },
+ shared_libs: [
+ "libEGL",
+ "libGLESv2",
+ "libcutils",
+ "libgui",
+ "libui",
+ "libutils",
+ ],
+}
diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk
deleted file mode 100644
index 754a99c..0000000
--- a/cmds/flatland/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-local_target_dir := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- Composers.cpp \
- GLHelper.cpp \
- Renderers.cpp \
- Main.cpp \
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE:= flatland
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(local_target_dir)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := flatland
-LOCAL_MODULE_STEM_64 := flatland64
-LOCAL_SHARED_LIBRARIES := \
- libEGL \
- libGLESv2 \
- libcutils \
- libgui \
- libui \
- libutils \
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index dfbb886..e100eac 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -74,7 +74,7 @@
}
template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
+using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>;
template <typename I>
class HalWrapper {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index cad7787..71a8740 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -234,12 +234,12 @@
return ok();
}
-binder::Status checkUidInAppRange(int32_t appUid) {
- if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) {
+binder::Status checkArgumentAppId(int32_t appId) {
+ if (FIRST_APPLICATION_UID <= appId && appId <= LAST_APPLICATION_UID) {
return ok();
}
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
- StringPrintf("UID %d is outside of the range", appUid));
+ StringPrintf("appId %d is outside of the range", appId));
}
#define ENFORCE_UID(uid) { \
@@ -302,12 +302,12 @@
} \
}
-#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \
- { \
- binder::Status status = checkUidInAppRange((uid)); \
- if (!status.isOk()) { \
- return status; \
- } \
+#define CHECK_ARGUMENT_APP_ID(appId) \
+ { \
+ binder::Status status = checkArgumentAppId((appId)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
}
#ifdef GRANULAR_LOCKS
@@ -411,7 +411,7 @@
} // namespace
binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate(
- const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) {
+ const ParcelFileDescriptor& authFd, int32_t uid) {
int open_flags = fcntl(authFd.get(), F_GETFL);
if (open_flags < 0) {
return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed");
@@ -426,9 +426,8 @@
return exception(binder::Status::EX_SECURITY, "Not a regular file");
}
// Don't accept a file owned by a different app.
- uid_t uid = multiuser_get_uid(userId, appUid);
- if (this->mStatFromAuthFd.st_uid != uid) {
- return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid");
+ if (this->mStatFromAuthFd.st_uid != (uid_t)uid) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by uid");
}
return ok();
}
@@ -3986,7 +3985,7 @@
// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable,
// return null.
//
-// appUid and userId are passed for additional ownership check, such that one app can not be
+// app process uid is passed for additional ownership check, such that one app can not be
// authenticated for another app's file. These parameters are assumed trusted for this purpose of
// consistency check.
//
@@ -3994,13 +3993,13 @@
// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the
// server allows the server to hold the only reference (as long as the client app doesn't).
binder::Status InstalldNativeService::createFsveritySetupAuthToken(
- const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId,
+ const ParcelFileDescriptor& authFd, int32_t uid,
sp<IFsveritySetupAuthToken>* _aidl_return) {
- CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid);
- ENFORCE_VALID_USER(userId);
+ CHECK_ARGUMENT_APP_ID(multiuser_get_app_id(uid));
+ ENFORCE_VALID_USER(multiuser_get_user_id(uid));
auto token = sp<FsveritySetupAuthToken>::make();
- binder::Status status = token->authenticate(authFd, appUid, userId);
+ binder::Status status = token->authenticate(authFd, uid);
if (!status.isOk()) {
return status;
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 1ec092d..88caba7 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,8 +44,7 @@
public:
FsveritySetupAuthToken() : mStatFromAuthFd() {}
- binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid,
- int32_t userId);
+ binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t uid);
bool isSameStat(const struct stat& st) const;
private:
@@ -213,7 +212,7 @@
int32_t* _aidl_return);
binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd,
- int32_t appUid, int32_t userId,
+ int32_t uid,
android::sp<IFsveritySetupAuthToken>* _aidl_return);
binder::Status enableFsverity(const android::sp<IFsveritySetupAuthToken>& authToken,
const std::string& filePath, const std::string& packageName,
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 643b2c2..e9fb85b 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -1,11 +1,10 @@
set noparent
-calin@google.com
jsharkey@android.com
maco@google.com
mast@google.com
+jiakaiz@google.com
narayan@google.com
ngeoffray@google.com
rpl@google.com
-toddke@google.com
patb@google.com
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 8893e38..120d61d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -145,8 +145,7 @@
//
// We don't necessarily need a method here, so it's left blank intentionally.
}
- IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid,
- int userId);
+ IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int uid);
int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath,
@utf8InCpp String packageName);
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 4bc92af..f2b578a 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -548,8 +548,7 @@
unique_fd ufd(open(path.c_str(), open_mode));
EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno);
ParcelFileDescriptor rfd(std::move(ufd));
- return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId,
- _aidl_return);
+ return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, _aidl_return);
}
};
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 805e8dc..d97ba89 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -16,83 +16,44 @@
#pragma once
-#include <condition_variable>
#include <chrono>
-#include <functional>
-#include <mutex>
-#include <thread>
+#include <future>
#include <hidl/Status.h>
+#include <utils/Errors.h>
namespace android {
namespace lshal {
-class BackgroundTaskState {
-public:
- explicit BackgroundTaskState(std::function<void(void)> &&func)
- : mFunc(std::forward<decltype(func)>(func)) {}
- void notify() {
- std::unique_lock<std::mutex> lock(mMutex);
- mFinished = true;
- lock.unlock();
- mCondVar.notify_all();
- }
- template<class C, class D>
- bool wait(std::chrono::time_point<C, D> end) {
- std::unique_lock<std::mutex> lock(mMutex);
- mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
- return mFinished;
- }
- void operator()() {
- mFunc();
- }
-private:
- std::mutex mMutex;
- std::condition_variable mCondVar;
- bool mFinished = false;
- std::function<void(void)> mFunc;
-};
-
-void *callAndNotify(void *data) {
- BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
- state();
- state.notify();
- return nullptr;
-}
-
-template<class R, class P>
-bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
- auto now = std::chrono::system_clock::now();
- BackgroundTaskState state{std::forward<decltype(func)>(func)};
- pthread_t thread;
- if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
- std::cerr << "FATAL: could not create background thread." << std::endl;
- return false;
- }
- bool success = state.wait(now + delay);
- if (!success) {
- pthread_kill(thread, SIGINT);
- }
- pthread_join(thread, nullptr);
- return success;
-}
-
+// Call function on interfaceObject and wait for result until the given timeout has reached.
+// Callback functions pass to timeoutIPC() may be executed after the this function
+// has returned, especially if deadline has been reached. Hence, care must be taken when passing
+// data between the background thread and the main thread. See b/311143089.
template<class R, class P, class Function, class I, class... Args>
-typename std::result_of<Function(I *, Args...)>::type
+typename std::invoke_result<Function, I *, Args...>::type
timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
Args &&... args) {
using ::android::hardware::Status;
- typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
- auto boundFunc = std::bind(std::forward<Function>(func),
- interfaceObject.get(), std::forward<Args>(args)...);
- bool success = timeout(wait, [&ret, &boundFunc] {
- ret = std::move(boundFunc());
- });
- if (!success) {
+
+ // Execute on a background thread but do not defer execution.
+ auto future =
+ std::async(std::launch::async, func, interfaceObject, std::forward<Args>(args)...);
+ auto status = future.wait_for(wait);
+ if (status == std::future_status::ready) {
+ return future.get();
+ }
+
+ // This future belongs to a background thread that we no longer care about.
+ // Putting this in the global list avoids std::future::~future() that may wait for the
+ // result to come back.
+ // This leaks memory, but lshal is a debugging tool, so this is fine.
+ static std::vector<decltype(future)> gDeadPool{};
+ gDeadPool.emplace_back(std::move(future));
+
+ if (status == std::future_status::timeout) {
return Status::fromStatusT(TIMED_OUT);
}
- return ret;
+ return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status");
}
-
-} // namespace lshal
-} // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
index 366c938..bd5fa32 100644
--- a/cmds/lshal/main.cpp
+++ b/cmds/lshal/main.cpp
@@ -18,5 +18,6 @@
int main(int argc, char **argv) {
using namespace ::android::lshal;
- return Lshal{}.main(Arg{argc, argv});
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(Lshal{}.main(Arg{argc, argv}));
}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index cba7c4b..c24f827 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <chrono>
+#include <future>
+#include <mutex>
+#include "android/hidl/base/1.0/IBase.h"
#define LOG_TAG "Lshal"
#include <android-base/logging.h>
@@ -36,6 +40,8 @@
using namespace testing;
+using std::chrono_literals::operator""ms;
+
using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -934,12 +940,9 @@
return hardware::Void();
}));
EXPECT_CALL(*serviceManager, get(_, _))
- .WillRepeatedly(
- Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
- int id = getIdFromInstanceName(instance);
- if (id > inheritanceLevel) return nullptr;
- return sp<IBase>(service);
- }));
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return sp<IBase>(service);
+ }));
const std::string expected = "[fake description 0]\n"
"Interface\n"
@@ -957,6 +960,110 @@
EXPECT_EQ("", err.str());
}
+// In SlowService, everything goes slooooooow. Each IPC call will wait for
+// the specified time before calling the callback function or returning.
+class SlowService : public IBase {
+public:
+ explicit SlowService(std::chrono::milliseconds wait) : mWait(wait) {}
+ android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ cb(getInterfaceName(1));
+ storeHistory("interfaceDescriptor");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1));
+ ret.push_back(IBase::descriptor);
+ cb(ret);
+ storeHistory("interfaceChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_hash> ret;
+ ret.push_back(getHashFromId(0));
+ ret.push_back(getHashFromId(0xff));
+ cb(ret);
+ storeHistory("getHashChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> debug(const hidl_handle&,
+ const hidl_vec<hidl_string>&) override {
+ std::this_thread::sleep_for(mWait);
+ storeHistory("debug");
+ return Void();
+ }
+
+ template <class R, class P, class Pred>
+ bool waitForHistory(std::chrono::duration<R, P> wait, Pred predicate) {
+ std::unique_lock<std::mutex> lock(mLock);
+ return mCv.wait_for(lock, wait, [&]() { return predicate(mCallHistory); });
+ }
+
+private:
+ void storeHistory(std::string hist) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCallHistory.emplace_back(std::move(hist));
+ }
+ mCv.notify_all();
+ }
+
+ const std::chrono::milliseconds mWait;
+ std::mutex mLock;
+ std::condition_variable mCv;
+ // List of functions that have finished being called on this interface.
+ std::vector<std::string> mCallHistory;
+};
+
+class TimeoutTest : public ListTest {
+public:
+ void setMockServiceManager(sp<IBase> service) {
+ EXPECT_CALL(*serviceManager, list(_))
+ .WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1) + "/default");
+ cb(ret);
+ return hardware::Void();
+ }));
+ EXPECT_CALL(*serviceManager, get(_, _))
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return service;
+ }));
+ }
+};
+
+TEST_F(TimeoutTest, BackgroundThreadIsKept) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 200ms;
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_THAT(err.str(), HasSubstr("Skipping \"a.h.foo1@1.0::IFoo/default\""));
+ EXPECT_TRUE(service->waitForHistory(serviceIpcTimeout * 5, [](const auto& hist) {
+ return hist.size() == 1 && hist[0] == "interfaceChain";
+ })) << "The background thread should continue after the main thread moves on, but it is killed";
+}
+
+TEST_F(TimeoutTest, BackgroundThreadDoesNotBlockMainThread) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 2000ms;
+ auto start = std::chrono::system_clock::now();
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_LE(std::chrono::system_clock::now(), start + 5 * lshalIpcTimeout)
+ << "The main thread should not be blocked by the background task";
+}
+
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {
@@ -1079,5 +1186,6 @@
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
- return RUN_ALL_TESTS();
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(RUN_ALL_TESTS());
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 77989d1..f2f0a0f 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -150,7 +150,7 @@
std::optional<std::string> updatableViaApex;
forEachManifest([&](const ManifestWithDescription& mwd) {
- mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
if (manifestInstance.format() != vintf::HalFormat::AIDL) return true;
if (manifestInstance.package() != aname.package) return true;
if (manifestInstance.interface() != aname.iface) return true;
@@ -158,8 +158,7 @@
updatableViaApex = manifestInstance.updatableViaApex();
return false; // break (libvintf uses opposite convention)
});
- if (updatableViaApex.has_value()) return true; // break (found match)
- return false; // continue
+ return !cont;
});
return updatableViaApex;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index ae56cb0..07908ba 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -40,15 +40,12 @@
public:
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
sp<BinderCallback> cb = sp<BinderCallback>::make();
+ cb->mLooper = looper;
- int binder_fd = -1;
- IPCThreadState::self()->setupPolling(&binder_fd);
- LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
+ IPCThreadState::self()->setupPolling(&cb->mBinderFd);
+ LOG_ALWAYS_FATAL_IF(cb->mBinderFd < 0, "Failed to setupPolling: %d", cb->mBinderFd);
- int ret = looper->addFd(binder_fd,
- Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT,
- cb,
+ int ret = looper->addFd(cb->mBinderFd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, cb,
nullptr /*data*/);
LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
@@ -59,13 +56,26 @@
IPCThreadState::self()->handlePolledCommands();
return 1; // Continue receiving callbacks.
}
+
+ void repoll() {
+ if (!mLooper->repoll(mBinderFd)) {
+ ALOGE("Failed to repoll binder FD.");
+ }
+ }
+
+private:
+ sp<Looper> mLooper;
+ int mBinderFd = -1;
};
// LooperCallback for IClientCallback
class ClientCallbackCallback : public LooperCallback {
public:
- static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
+ static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper,
+ const sp<ServiceManager>& manager,
+ sp<BinderCallback> binderCallback) {
sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
+ cb->mBinderCallback = binderCallback;
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -102,12 +112,15 @@
}
mManager->handleClientCallbacks();
+ mBinderCallback->repoll(); // b/316829336
+
return 1; // Continue receiving callbacks.
}
private:
friend sp<ClientCallbackCallback>;
ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
sp<ServiceManager> mManager;
+ sp<BinderCallback> mBinderCallback;
};
int main(int argc, char** argv) {
@@ -139,8 +152,8 @@
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
- BinderCallback::setupTo(looper);
- ClientCallbackCallback::setupTo(looper, manager);
+ sp<BinderCallback> binderCallback = BinderCallback::setupTo(looper);
+ ClientCallbackCallback::setupTo(looper, manager, binderCallback);
#ifndef VENDORSERVICEMANAGER
if (!SetProperty("servicemanager.ready", "true")) {
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 4f92b3a..6c450ab 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -11,5 +11,5 @@
onrestart class_restart --only-enabled main
onrestart class_restart --only-enabled hal
onrestart class_restart --only-enabled early_hal
- task_profiles ServiceCapacityLow
+ task_profiles ProcessCapacityHigh
shutdown critical
diff --git a/include/android/input.h b/include/android/input.h
index 9a0eb4d..16d86af 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -54,16 +54,12 @@
#include <stdint.h>
#include <sys/types.h>
#include <android/keycodes.h>
-
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <jni.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
diff --git a/include/android/looper.h b/include/android/looper.h
index 4fe142a..e50730d 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -26,10 +26,18 @@
#ifndef ANDROID_LOOPER_H
#define ANDROID_LOOPER_H
+#include <sys/cdefs.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+// This file may also be built on glibc or on Windows/MacOS libc's, so
+// deprecated definitions are provided.
+#if !defined(__REMOVED_IN)
+#define __REMOVED_IN(__api_level) __attribute__((__deprecated__))
+#endif
+
struct ALooper;
/**
* ALooper
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 16c5dde..0cc7834 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -29,6 +29,8 @@
#ifndef ANDROID_SENSOR_H
#define ANDROID_SENSOR_H
+#include <sys/cdefs.h>
+
/******************************************************************
*
* IMPORTANT NOTICE:
@@ -45,11 +47,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <stdbool.h>
@@ -57,6 +54,8 @@
#include <math.h>
#include <stdint.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// and deprecated definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
diff --git a/include/input/Input.h b/include/input/Input.h
index 527a477..88d1c11 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -284,6 +284,16 @@
// the touch firmware or driver. Causes touch events from the same device to be canceled.
POLICY_FLAG_GESTURE = 0x00000008,
+ // Indicates that key usage mapping represents a fallback mapping.
+ // Fallback mappings cannot be used to definitively determine whether a device
+ // supports a key code. For example, a HID device can report a key press
+ // as a HID usage code if it is not mapped to any linux key code in the kernel.
+ // However, we cannot know which HID usage codes that device supports from
+ // userspace through the evdev. We can use fallback mappings to convert HID
+ // usage codes to Android key codes without needing to know if a device can
+ // actually report the usage code.
+ POLICY_FLAG_FALLBACK_USAGE_MAPPING = 0x00000010,
+
POLICY_FLAG_RAW_MASK = 0x0000ffff,
#ifdef __linux__
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index b582f17..d6fe9fa 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <optional>
#include <ostream>
#include <string>
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index f178027..864ff50 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -31,7 +31,11 @@
*/
class PersistableBundle {
public:
- PersistableBundle() noexcept : mPBundle(APersistableBundle_new()) {}
+ PersistableBundle() noexcept {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ mPBundle = APersistableBundle_new();
+ }
+ }
// takes ownership of the APersistableBundle*
PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {}
// takes ownership of the APersistableBundle*
@@ -57,7 +61,7 @@
if (__builtin_available(android __ANDROID_API_V__, *)) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
@@ -68,7 +72,7 @@
if (__builtin_available(android __ANDROID_API_V__, *)) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
@@ -327,20 +331,32 @@
}
bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) {
- return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
- vec);
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
}
bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
- return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
- vec);
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
}
bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
- return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
- vec);
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
}
bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
- return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(),
- vec);
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
+ key.c_str(), vec);
+ }
+ return false;
}
// Takes ownership of and frees the char** and its elements.
@@ -361,15 +377,17 @@
}
bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) {
- int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
- &stringAllocator, nullptr);
- if (bytes > 0) {
- char** strings = (char**)malloc(bytes);
- if (strings) {
- bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes,
- &stringAllocator, nullptr);
- *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes);
- return true;
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
+ &stringAllocator, nullptr);
+ if (bytes > 0) {
+ char** strings = (char**)malloc(bytes);
+ if (strings) {
+ bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings,
+ bytes, &stringAllocator, nullptr);
+ *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes);
+ return true;
+ }
}
}
return false;
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index eff8104..98c0cb2 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#pragma once
+
#include <android/binder_parcel.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -31,14 +36,30 @@
struct APersistableBundle;
typedef struct APersistableBundle APersistableBundle;
+enum {
+ /**
+ * This can be returned from functions that need to distinguish between an empty
+ * value and a non-existent key.
+ */
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND = -1,
+
+ /**
+ * This can be returned from functions that take a APersistableBundle_stringAllocator.
+ * This means the allocator has failed and returned a nullptr.
+ */
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED = -2,
+};
+
/**
* This is a user supplied allocator that allocates a buffer for the
- * APersistableBundle APIs to fill in with a string.
+ * APersistableBundle APIs to fill in with a UTF-8 string.
+ * The caller that supplies this function is responsible for freeing the
+ * returned data.
*
* \param the required size in bytes for the allocated buffer
- * \param void* _Nullable context if needed by the callback
+ * \param context pointer if needed by the callback
*
- * \return allocated buffer of sizeBytes. Null if allocation failed.
+ * \return allocated buffer of sizeBytes for a UTF-8 string. Null if allocation failed.
*/
typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes,
void* _Nullable context);
@@ -54,10 +75,12 @@
/**
* Create a new APersistableBundle based off an existing APersistableBundle.
+ * This is a deep copy, so the new APersistableBundle has its own values from
+ * copying the original underlying PersistableBundle.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to duplicate
+ * \param pBundle to duplicate
*
* \return Pointer to a new APersistableBundle
*/
@@ -68,11 +91,11 @@
* Delete an APersistableBundle. This must always be called when finished using
* the object.
*
- * \param bundle to delete
+ * \param pBundle to delete. No-op if null.
*
* Available since API level __ANDROID_API_V__.
*/
-void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle)
+void APersistableBundle_delete(APersistableBundle* _Nullable pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -80,8 +103,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param lhs bundle to compare agains the other param
- * \param rhs bundle to compare agains the other param
+ * \param lhs bundle to compare against the other param
+ * \param rhs bundle to compare against the other param
*
* \return true when equal, false when not
*/
@@ -134,11 +157,11 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to get the size of (number of mappings)
+ * \param pBundle to get the size of (number of mappings)
*
* \return number of mappings in the object
*/
-int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle)
+int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -146,8 +169,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping to erase
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8 to erase
*
* \return number of entries erased. Either 0 or 1.
*/
@@ -158,8 +181,8 @@
* Put a boolean associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
* \param value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
@@ -171,9 +194,9 @@
* Put an int32_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -184,9 +207,9 @@
* Put an int64_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -197,9 +220,9 @@
* Put a double associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -209,10 +232,11 @@
/**
* Put a string associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -222,11 +246,12 @@
/**
* Put a boolean vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -237,11 +262,12 @@
/**
* Put an int32_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -252,11 +278,12 @@
/**
* Put an int64_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -267,11 +294,12 @@
/**
* Put a double vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -282,11 +310,12 @@
/**
* Put a string vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -298,10 +327,11 @@
/**
* Put an APersistableBundle associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is deep-copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -315,9 +345,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -330,9 +360,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -344,9 +374,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -359,9 +389,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -371,17 +401,19 @@
/**
* Get a string associated with the provided key.
+ * The caller is responsible for freeing the returned data.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to in UTF-8
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
- * \return size of string associated with the provided key on success
- * 0 if no string exists for the provided key
- * -1 if the provided allocator fails and returns false
+ * \return size of string in bytes associated with the provided key on success
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, char* _Nullable* _Nonnull val,
@@ -393,7 +425,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -401,13 +433,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, bool* _Nullable buffer,
@@ -419,7 +452,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -427,13 +460,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int32_t* _Nullable buffer,
@@ -444,7 +478,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -452,13 +486,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int64_t* _Nullable buffer,
@@ -470,7 +505,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -478,13 +513,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, double* _Nullable buffer,
@@ -496,9 +532,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -506,17 +543,18 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the string pointers to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
* 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
@@ -531,9 +569,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to an APersistableBundle pointer to write to point to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to an APersistableBundle pointer to write to point to
* a new copy of the stored APersistableBundle. The caller takes ownership of
* the new APersistableBundle and must be deleted with
* APersistableBundle_delete.
@@ -550,9 +588,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -560,16 +599,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -583,9 +621,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -593,16 +632,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -614,9 +652,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -624,16 +663,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -645,9 +683,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -655,16 +694,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -678,9 +716,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -688,16 +727,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -711,9 +749,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -721,16 +760,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -744,9 +782,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -754,16 +793,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -777,9 +815,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -787,16 +826,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -810,9 +848,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -820,16 +859,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
*/
int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -843,9 +880,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -853,15 +891,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
* false
*/
int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle,
@@ -876,9 +913,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data in bytes.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -886,16 +924,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getPersistableBundleKeys(
const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp
index 404611c..9b6877d 100644
--- a/libs/binder/ndk/persistable_bundle.cpp
+++ b/libs/binder/ndk/persistable_bundle.cpp
@@ -76,7 +76,7 @@
return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel));
}
-int32_t APersistableBundle_size(APersistableBundle* pBundle) {
+int32_t APersistableBundle_size(const APersistableBundle* pBundle) {
size_t size = pBundle->mPBundle.size();
LOG_ALWAYS_FATAL_IF(size > INT32_MAX,
"The APersistableBundle has gotten too large! There will be an overflow in "
@@ -167,40 +167,42 @@
void* context) {
android::String16 outVal;
bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal);
- if (ret) {
- android::String8 tmp8(outVal);
- *val = stringAllocator(tmp8.bytes() + 1, context);
- if (*val) {
- strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
- return tmp8.bytes();
- } else {
- return -1;
- }
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
+ android::String8 tmp8(outVal);
+ *val = stringAllocator(tmp8.bytes() + 1, context);
+ if (*val) {
+ strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
+ return tmp8.bytes();
+ } else {
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
- return 0;
}
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key,
bool* buffer, int32_t bufferSizeBytes) {
std::vector<bool> newVec;
- pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<bool>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key,
int32_t* buffer, int32_t bufferSizeBytes) {
std::vector<int32_t> newVec;
- pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int32_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key,
int64_t* buffer, int32_t bufferSizeBytes) {
std::vector<int64_t> newVec;
- pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int64_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key,
double* buffer, int32_t bufferSizeBytes) {
std::vector<double> newVec;
- pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<double>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key,
@@ -208,7 +210,8 @@
APersistableBundle_stringAllocator stringAllocator,
void* context) {
std::vector<android::String16> newVec;
- pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getStringsInternal<std::vector<android::String16>>(newVec, vec, bufferSizeBytes,
stringAllocator, context);
}
diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h
index 279c66f..bee10fd 100644
--- a/libs/binder/ndk/persistable_bundle_internal.h
+++ b/libs/binder/ndk/persistable_bundle_internal.h
@@ -61,7 +61,7 @@
int32_t numAvailable = bufferSizeBytes / sizeof(char*);
int32_t numFill = numAvailable < num ? numAvailable : num;
if (!stringAllocator) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
if (numFill > 0 && buffer) {
@@ -70,7 +70,7 @@
android::String8 tmp8 = android::String8(val);
buffer[i] = stringAllocator(tmp8.bytes() + 1, context);
if (buffer[i] == nullptr) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1);
i++;
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index 73eb863..ca3d5e6 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -27,7 +27,7 @@
#error libbinder_ndk should only be built in a system context
#endif
-#ifdef __ANDROID_VENDOR__
+#if defined(__ANDROID_VENDOR__) && !defined(__TRUSTY__)
#error libbinder_ndk should only be built in a system context
#endif
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7f9348d..16049f2 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -136,8 +136,8 @@
pub use crate::native::Binder;
pub use crate::parcel::{
BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
- ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
- NULL_PARCELABLE_FLAG,
+ ParcelableMetadata, Serialize, SerializeArray, SerializeOption, UnstructuredParcelable,
+ NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
};
pub use crate::proxy::{AssociateClass, Proxy};
}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index f9f135d..3bfc425 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -34,7 +34,7 @@
pub use self::file_descriptor::ParcelFileDescriptor;
pub use self::parcelable::{
Deserialize, DeserializeArray, DeserializeOption, Parcelable, Serialize, SerializeArray,
- SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
+ SerializeOption, UnstructuredParcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
};
pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata};
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 9008a3c..33dfe19 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -27,7 +27,7 @@
use std::ptr;
use std::slice;
-/// Super-trait for Binder parcelables.
+/// Super-trait for structured Binder parcelables, i.e. those generated from AIDL.
///
/// This trait is equivalent `android::Parcelable` in C++,
/// and defines a common interface that all parcelables need
@@ -50,6 +50,35 @@
fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>;
}
+/// Super-trait for unstructured Binder parcelables, i.e. those implemented manually.
+///
+/// These differ from structured parcelables in that they may not have a reasonable default value
+/// and so aren't required to implement `Default`.
+pub trait UnstructuredParcelable: Sized {
+ /// Internal serialization function for parcelables.
+ ///
+ /// This method is mainly for internal use. `Serialize::serialize` and its variants are
+ /// generally preferred over calling this function, since the former also prepend a header.
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
+
+ /// Internal deserialization function for parcelables.
+ ///
+ /// This method is mainly for internal use. `Deserialize::deserialize` and its variants are
+ /// generally preferred over calling this function, since the former also parse the additional
+ /// header.
+ fn from_parcel(parcel: &BorrowedParcel<'_>) -> Result<Self>;
+
+ /// Internal deserialization function for parcelables.
+ ///
+ /// This method is mainly for internal use. `Deserialize::deserialize_from` and its variants are
+ /// generally preferred over calling this function, since the former also parse the additional
+ /// header.
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+ *self = Self::from_parcel(parcel)?;
+ Ok(())
+ }
+}
+
/// A struct whose instances can be written to a [`crate::parcel::Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Serialize {
@@ -1002,6 +1031,125 @@
};
}
+/// Implements `Serialize` trait and friends for an unstructured parcelable.
+///
+/// The target type must implement the `UnstructuredParcelable` trait.
+#[macro_export]
+macro_rules! impl_serialize_for_unstructured_parcelable {
+ ($parcelable:ident) => {
+ $crate::impl_serialize_for_unstructured_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_serialize_for_unstructured_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param),* > $crate::binder_impl::Serialize for $parcelable < $($param),* > {
+ fn serialize(
+ &self,
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+
+ impl < $($param),* > $crate::binder_impl::SerializeArray for $parcelable < $($param),* > {}
+
+ impl < $($param),* > $crate::binder_impl::SerializeOption for $parcelable < $($param),* > {
+ fn serialize_option(
+ this: Option<&Self>,
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ if let Some(this) = this {
+ use $crate::binder_impl::UnstructuredParcelable;
+ parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
+ this.write_to_parcel(parcel)
+ } else {
+ parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
+ }
+ }
+ }
+ };
+}
+
+/// Implement `Deserialize` trait and friends for an unstructured parcelable
+///
+/// The target type must implement the `UnstructuredParcelable` trait.
+#[macro_export]
+macro_rules! impl_deserialize_for_unstructured_parcelable {
+ ($parcelable:ident) => {
+ $crate::impl_deserialize_for_unstructured_parcelable!($parcelable < >);
+ };
+ ($parcelable:ident < $( $param:ident ),* , >) => {
+ $crate::impl_deserialize_for_unstructured_parcelable!($parcelable < $($param),* >);
+ };
+ ($parcelable:ident < $( $param:ident ),* > ) => {
+ impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > {
+ type UninitType = Option<Self>;
+ fn uninit() -> Self::UninitType { None }
+ fn from_init(value: Self) -> Self::UninitType { Some(value) }
+ fn deserialize(
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Self, $crate::StatusCode> {
+ $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
+ }
+ fn deserialize_from(
+ &mut self,
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ let status: i32 = parcel.read()?;
+ if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
+ Err($crate::StatusCode::UNEXPECTED_NULL)
+ } else {
+ use $crate::binder_impl::UnstructuredParcelable;
+ self.read_from_parcel(parcel)
+ }
+ }
+ }
+
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeArray for $parcelable < $($param),* > {}
+
+ impl < $($param: Default),* > $crate::binder_impl::DeserializeOption for $parcelable < $($param),* > {
+ fn deserialize_option(
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
+ let present: i32 = parcel.read()?;
+ match present {
+ $crate::binder_impl::NULL_PARCELABLE_FLAG => Ok(None),
+ $crate::binder_impl::NON_NULL_PARCELABLE_FLAG => {
+ use $crate::binder_impl::UnstructuredParcelable;
+ Ok(Some(Self::from_parcel(parcel)?))
+ }
+ _ => Err(StatusCode::BAD_VALUE),
+ }
+ }
+ fn deserialize_option_from(
+ this: &mut Option<Self>,
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ let present: i32 = parcel.read()?;
+ match present {
+ $crate::binder_impl::NULL_PARCELABLE_FLAG => {
+ *this = None;
+ Ok(())
+ }
+ $crate::binder_impl::NON_NULL_PARCELABLE_FLAG => {
+ use $crate::binder_impl::UnstructuredParcelable;
+ if let Some(this) = this {
+ this.read_from_parcel(parcel)?;
+ } else {
+ *this = Some(Self::from_parcel(parcel)?);
+ }
+ Ok(())
+ }
+ _ => Err(StatusCode::BAD_VALUE),
+ }
+ }
+ }
+ };
+}
+
impl<T: Serialize> Serialize for Box<T> {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(&**self, parcel)
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 0ea4a3f..10912c7 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -49,6 +49,63 @@
}
};
+static uint64_t warn_latency = std::numeric_limits<uint64_t>::max();
+
+struct ProcResults {
+ vector<uint64_t> data;
+
+ ProcResults(size_t capacity) { data.reserve(capacity); }
+
+ void add_time(uint64_t time) { data.push_back(time); }
+ void combine_with(const ProcResults& append) {
+ data.insert(data.end(), append.data.begin(), append.data.end());
+ }
+ uint64_t worst() {
+ return *max_element(data.begin(), data.end());
+ }
+ void dump() {
+ if (data.size() == 0) {
+ // This avoids index-out-of-bounds below.
+ cout << "error: no data\n" << endl;
+ return;
+ }
+
+ size_t num_long_transactions = 0;
+ for (uint64_t elem : data) {
+ if (elem > warn_latency) {
+ num_long_transactions += 1;
+ }
+ }
+
+ if (num_long_transactions > 0) {
+ cout << (double)num_long_transactions / data.size() << "% of transactions took longer "
+ "than estimated max latency. Consider setting -m to be higher than "
+ << worst() / 1000 << " microseconds" << endl;
+ }
+
+ sort(data.begin(), data.end());
+
+ uint64_t total_time = 0;
+ for (uint64_t elem : data) {
+ total_time += elem;
+ }
+
+ double best = (double)data[0] / 1.0E6;
+ double worst = (double)data.back() / 1.0E6;
+ double average = (double)total_time / data.size() / 1.0E6;
+ cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl;
+
+ double percentile_50 = data[(50 * data.size()) / 100] / 1.0E6;
+ double percentile_90 = data[(90 * data.size()) / 100] / 1.0E6;
+ double percentile_95 = data[(95 * data.size()) / 100] / 1.0E6;
+ double percentile_99 = data[(99 * data.size()) / 100] / 1.0E6;
+ cout << "50%: " << percentile_50 << " ";
+ cout << "90%: " << percentile_90 << " ";
+ cout << "95%: " << percentile_95 << " ";
+ cout << "99%: " << percentile_99 << endl;
+ }
+};
+
class Pipe {
int m_readFd;
int m_writeFd;
@@ -79,13 +136,37 @@
int error = read(m_readFd, &val, sizeof(val));
ASSERT_TRUE(error >= 0);
}
- template <typename T> void send(const T& v) {
- int error = write(m_writeFd, &v, sizeof(T));
+ void send(const ProcResults& v) {
+ size_t num_elems = v.data.size();
+
+ int error = write(m_writeFd, &num_elems, sizeof(size_t));
ASSERT_TRUE(error >= 0);
+
+ char* to_write = (char*)v.data.data();
+ size_t num_bytes = sizeof(uint64_t) * num_elems;
+
+ while (num_bytes > 0) {
+ int ret = write(m_writeFd, to_write, num_bytes);
+ ASSERT_TRUE(ret >= 0);
+ num_bytes -= ret;
+ to_write += ret;
+ }
}
- template <typename T> void recv(T& v) {
- int error = read(m_readFd, &v, sizeof(T));
+ void recv(ProcResults& v) {
+ size_t num_elems = 0;
+ int error = read(m_readFd, &num_elems, sizeof(size_t));
ASSERT_TRUE(error >= 0);
+
+ v.data.resize(num_elems);
+ char* read_to = (char*)v.data.data();
+ size_t num_bytes = sizeof(uint64_t) * num_elems;
+
+ while (num_bytes > 0) {
+ int ret = read(m_readFd, read_to, num_bytes);
+ ASSERT_TRUE(ret >= 0);
+ num_bytes -= ret;
+ read_to += ret;
+ }
}
static tuple<Pipe, Pipe> createPipePair() {
int a[2];
@@ -100,74 +181,6 @@
}
};
-static const uint32_t num_buckets = 128;
-static uint64_t max_time_bucket = 50ull * 1000000;
-static uint64_t time_per_bucket = max_time_bucket / num_buckets;
-
-struct ProcResults {
- uint64_t m_worst = 0;
- uint32_t m_buckets[num_buckets] = {0};
- uint64_t m_transactions = 0;
- uint64_t m_long_transactions = 0;
- uint64_t m_total_time = 0;
- uint64_t m_best = max_time_bucket;
-
- void add_time(uint64_t time) {
- if (time > max_time_bucket) {
- m_long_transactions++;
- }
- m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1;
- m_best = min(time, m_best);
- m_worst = max(time, m_worst);
- m_transactions += 1;
- m_total_time += time;
- }
- static ProcResults combine(const ProcResults& a, const ProcResults& b) {
- ProcResults ret;
- for (int i = 0; i < num_buckets; i++) {
- ret.m_buckets[i] = a.m_buckets[i] + b.m_buckets[i];
- }
- ret.m_worst = max(a.m_worst, b.m_worst);
- ret.m_best = min(a.m_best, b.m_best);
- ret.m_transactions = a.m_transactions + b.m_transactions;
- ret.m_long_transactions = a.m_long_transactions + b.m_long_transactions;
- ret.m_total_time = a.m_total_time + b.m_total_time;
- return ret;
- }
- void dump() {
- if (m_long_transactions > 0) {
- cout << (double)m_long_transactions / m_transactions << "% of transactions took longer "
- "than estimated max latency. Consider setting -m to be higher than "
- << m_worst / 1000 << " microseconds" << endl;
- }
-
- double best = (double)m_best / 1.0E6;
- double worst = (double)m_worst / 1.0E6;
- double average = (double)m_total_time / m_transactions / 1.0E6;
- cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl;
-
- uint64_t cur_total = 0;
- float time_per_bucket_ms = time_per_bucket / 1.0E6;
- for (int i = 0; i < num_buckets; i++) {
- float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms;
- if ((cur_total < 0.5f * m_transactions) && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) {
- cout << "50%: " << cur_time << " ";
- }
- if ((cur_total < 0.9f * m_transactions) && (cur_total + m_buckets[i] >= 0.9f * m_transactions)) {
- cout << "90%: " << cur_time << " ";
- }
- if ((cur_total < 0.95f * m_transactions) && (cur_total + m_buckets[i] >= 0.95f * m_transactions)) {
- cout << "95%: " << cur_time << " ";
- }
- if ((cur_total < 0.99f * m_transactions) && (cur_total + m_buckets[i] >= 0.99f * m_transactions)) {
- cout << "99%: " << cur_time << " ";
- }
- cur_total += m_buckets[i];
- }
- cout << endl;
- }
-};
-
String16 generateServiceName(int num)
{
char num_str[32];
@@ -204,34 +217,37 @@
for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- workers.push_back(serviceMgr->getService(generateServiceName(i)));
-#pragma clang diagnostic pop
+ workers.push_back(serviceMgr->waitForService(generateServiceName(i)));
}
- // Run the benchmark if client
- ProcResults results;
+ p.signal();
+ p.wait();
+
+ ProcResults results(iterations);
chrono::time_point<chrono::high_resolution_clock> start, end;
- for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
- Parcel data, reply;
- int target = cs_pair ? num % server_count : rand() % workers.size();
- int sz = payload_size;
- while (sz >= sizeof(uint32_t)) {
- data.writeInt32(0);
- sz -= sizeof(uint32_t);
- }
- start = chrono::high_resolution_clock::now();
- status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
- end = chrono::high_resolution_clock::now();
+ // Skip the benchmark if server of a cs_pair.
+ if (!(cs_pair && num < server_count)) {
+ for (int i = 0; i < iterations; i++) {
+ Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
- uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
- results.add_time(cur_time);
+ while (sz >= sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
+ start = chrono::high_resolution_clock::now();
+ status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
+ end = chrono::high_resolution_clock::now();
- if (ret != NO_ERROR) {
- cout << "thread " << num << " failed " << ret << "i : " << i << endl;
- exit(EXIT_FAILURE);
+ uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
+ results.add_time(cur_time);
+
+ if (ret != NO_ERROR) {
+ cout << "thread " << num << " failed " << ret << "i : " << i << endl;
+ exit(EXIT_FAILURE);
+ }
}
}
@@ -289,8 +305,15 @@
pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
+ // All workers have now been spawned and added themselves to service
+ // manager. Signal each worker to obtain a handle to the server workers from
+ // servicemanager.
+ signal_all(pipes);
+ // Wait for each worker to finish obtaining a handle to all server workers
+ // from servicemanager.
+ wait_all(pipes);
- // Run the workers and wait for completion.
+ // Run the benchmark and wait for completion.
chrono::time_point<chrono::high_resolution_clock> start, end;
cout << "waiting for workers to complete" << endl;
start = chrono::high_resolution_clock::now();
@@ -305,11 +328,10 @@
// Collect all results from the workers.
cout << "collecting results" << endl;
signal_all(pipes);
- ProcResults tot_results;
+ ProcResults tot_results(0), tmp_results(0);
for (int i = 0; i < workers; i++) {
- ProcResults tmp_results;
pipes[i].recv(tmp_results);
- tot_results = ProcResults::combine(tot_results, tmp_results);
+ tot_results.combine_with(tmp_results);
}
// Kill all the workers.
@@ -323,13 +345,11 @@
}
}
if (training_round) {
- // sets max_time_bucket to 2 * m_worst from the training round.
- // Also needs to adjust time_per_bucket accordingly.
- max_time_bucket = 2 * tot_results.m_worst;
- time_per_bucket = max_time_bucket / num_buckets;
- cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl;
+ // Sets warn_latency to 2 * worst from the training round.
+ warn_latency = 2 * tot_results.worst();
+ cout << "Max latency during training: " << tot_results.worst() / 1.0E6 << "ms" << endl;
} else {
- tot_results.dump();
+ tot_results.dump();
}
}
@@ -340,8 +360,7 @@
int payload_size = 0;
bool cs_pair = false;
bool training_round = false;
- (void)argc;
- (void)argv;
+ int max_time_us;
// Parse arguments.
for (int i = 1; i < argc; i++) {
@@ -351,46 +370,65 @@
cout << "\t-m N : Specify expected max latency in microseconds." << endl;
cout << "\t-p : Split workers into client/server pairs." << endl;
cout << "\t-s N : Specify payload size." << endl;
- cout << "\t-t N : Run training round." << endl;
+ cout << "\t-t : Run training round." << endl;
cout << "\t-w N : Specify total number of workers." << endl;
return 0;
}
if (string(argv[i]) == "-w") {
+ if (i + 1 == argc) {
+ cout << "-w requires an argument\n" << endl;
+ exit(EXIT_FAILURE);
+ }
workers = atoi(argv[i+1]);
i++;
continue;
}
if (string(argv[i]) == "-i") {
+ if (i + 1 == argc) {
+ cout << "-i requires an argument\n" << endl;
+ exit(EXIT_FAILURE);
+ }
iterations = atoi(argv[i+1]);
i++;
continue;
}
if (string(argv[i]) == "-s") {
+ if (i + 1 == argc) {
+ cout << "-s requires an argument\n" << endl;
+ exit(EXIT_FAILURE);
+ }
payload_size = atoi(argv[i+1]);
i++;
+ continue;
}
if (string(argv[i]) == "-p") {
// client/server pairs instead of spreading
// requests to all workers. If true, half
// the workers become clients and half servers
cs_pair = true;
+ continue;
}
if (string(argv[i]) == "-t") {
// Run one training round before actually collecting data
// to get an approximation of max latency.
training_round = true;
+ continue;
}
if (string(argv[i]) == "-m") {
+ if (i + 1 == argc) {
+ cout << "-m requires an argument\n" << endl;
+ exit(EXIT_FAILURE);
+ }
// Caller specified the max latency in microseconds.
// No need to run training round in this case.
- if (atoi(argv[i+1]) > 0) {
- max_time_bucket = strtoull(argv[i+1], (char **)nullptr, 10) * 1000;
- time_per_bucket = max_time_bucket / num_buckets;
- i++;
- } else {
+ max_time_us = atoi(argv[i+1]);
+ if (max_time_us <= 0) {
cout << "Max latency -m must be positive." << endl;
exit(EXIT_FAILURE);
}
+ warn_latency = max_time_us * 1000ull;
+ i++;
+ continue;
}
}
diff --git a/libs/binder/tests/format.h b/libs/binder/tests/format.h
index b5440a4..c588de7 100644
--- a/libs/binder/tests/format.h
+++ b/libs/binder/tests/format.h
@@ -18,7 +18,7 @@
// ETA for this blocker is 2023-10-27~2023-11-10.
// Also, remember to remove fmtlib's format.cc from trusty makefiles.
-#if __has_include(<format>)
+#if __has_include(<format>) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
#include <format>
#else
#include <fmt/format.h>
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index 6de7eb5..d343f14 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -30,6 +30,9 @@
external/rust/crates/downcast-rs \
trusty/user/base/lib/trusty-sys \
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
# Trusty does not have `ProcessState`, so there are a few
# doc links in `IBinder` that are still broken.
MODULE_RUSTFLAGS += \
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 5eb3308..f4cf11e 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -89,6 +89,9 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
+ "android.hardware.audio.core.IConfig",
+ "android.hardware.audio.core.IModule",
+ "android.hardware.audio.effect.IFactory",
"android.hardware.automotive.audiocontrol.IAudioControl",
"android.hardware.automotive.can.ICanController",
"android.hardware.automotive.evs.IEvsEnumerator",
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 022dfad..a67ed29 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -221,7 +221,7 @@
"liblog",
"libPlatformProperties",
"libtinyxml2",
- "libvintf",
+ "libz", // needed by libkernelconfigs
],
ldflags: [
@@ -238,6 +238,7 @@
"inputconstants-cpp",
"libui-types",
"libtflite_static",
+ "libkernelconfigs",
],
whole_static_libs: [
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index bade686..b25e06c 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -429,7 +429,8 @@
DEFINE_FLAG(VIRTUAL), \
DEFINE_FLAG(FUNCTION), \
DEFINE_FLAG(GESTURE), \
- DEFINE_FLAG(WAKE)
+ DEFINE_FLAG(WAKE), \
+ DEFINE_FLAG(FALLBACK_USAGE_MAPPING)
// clang-format on
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index ddc9ea4..ab8c341 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -27,8 +27,7 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
#if defined(__ANDROID__)
-#include <vintf/RuntimeInfo.h>
-#include <vintf/VintfObject.h>
+#include <vintf/KernelConfigs.h>
#endif
#include <cstdlib>
@@ -98,12 +97,10 @@
bool kernelConfigsArePresent(const std::set<std::string>& configs) {
#if defined(__ANDROID__)
- std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
- android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
- LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched");
+ std::map<std::string, std::string> kernelConfigs;
+ const status_t result = android::kernelconfigs::LoadKernelConfigs(&kernelConfigs);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Kernel configs could not be fetched");
- const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs();
for (const std::string& requiredConfig : configs) {
const auto configIt = kernelConfigs.find(requiredConfig);
if (configIt == kernelConfigs.end()) {
@@ -250,7 +247,7 @@
std::vector<int32_t> KeyLayoutMap::findUsageCodesForKey(int32_t keyCode) const {
std::vector<int32_t> usageCodes;
for (const auto& [usageCode, key] : mKeysByUsageCode) {
- if (keyCode == key.keyCode) {
+ if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)) {
usageCodes.push_back(usageCode);
}
}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e7224ff..3fc7d9d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,10 @@
"libgmock",
"libgui_window_info_static",
"libinput",
+ "libkernelconfigs",
"libtflite_static",
"libui-types",
+ "libz", // needed by libkernelconfigs
],
cflags: [
"-Wall",
@@ -61,7 +63,6 @@
"libPlatformProperties",
"libtinyxml2",
"libutils",
- "libvintf",
],
data: [
"data/*",
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index ee961f0..5c44366 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -147,6 +147,41 @@
ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
}
+TEST(InputDeviceKeyLayoutTest, HidUsageCodesFallbackMapping) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/hid_fallback_mapping.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_TRUE(ret.ok()) << "Unable to load KeyLayout at " << klPath;
+ const std::shared_ptr<KeyLayoutMap>& keyLayoutMap = *ret;
+
+ static constexpr std::array<int32_t, 5> hidUsageCodesWithoutFallback = {0x0c0067, 0x0c0070,
+ 0x0c006F, 0x0c0079,
+ 0x0c007A};
+ for (int32_t hidUsageCode : hidUsageCodesWithoutFallback) {
+ int32_t outKeyCode;
+ uint32_t outFlags;
+ keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
+ ASSERT_FALSE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
+ << "HID usage code should not be marked as fallback";
+ std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
+ ASSERT_NE(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
+ << "Fallback usage code should be mapped to key";
+ }
+
+ static constexpr std::array<int32_t, 6> hidUsageCodesWithFallback = {0x0c007C, 0x0c0173,
+ 0x0c019C, 0x0c01A2,
+ 0x0d0044, 0x0d005a};
+ for (int32_t hidUsageCode : hidUsageCodesWithFallback) {
+ int32_t outKeyCode;
+ uint32_t outFlags;
+ keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
+ ASSERT_TRUE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
+ << "HID usage code should be marked as fallback";
+ std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
+ ASSERT_EQ(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
+ << "Fallback usage code should not be mapped to key";
+ }
+}
+
TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
#if !defined(__ANDROID__)
GTEST_SKIP() << "Can't check kernel configs on host";
diff --git a/libs/input/tests/data/hid_fallback_mapping.kl b/libs/input/tests/data/hid_fallback_mapping.kl
new file mode 100644
index 0000000..b4ca9ef
--- /dev/null
+++ b/libs/input/tests/data/hid_fallback_mapping.kl
@@ -0,0 +1,32 @@
+# Copyright 2023 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.
+
+#
+# test key layout file for InputDeviceKeyMapTest#HidUsageCodesUseFallbackMapping
+#
+
+# Keys defined by HID usages without fallback mapping flag
+key usage 0x0c0067 WINDOW
+key usage 0x0c006F BRIGHTNESS_UP
+key usage 0x0c0070 BRIGHTNESS_DOWN
+key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP
+key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN
+
+# Keys defined by HID usages with fallback mapping flag
+key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
+key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING
+key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING
\ No newline at end of file
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index eab21fb..9f8ae86 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -29,6 +29,7 @@
#define ANDROID_DATA_SPACE_H
#include <inttypes.h>
+#include <stdint.h>
#include <sys/cdefs.h>
@@ -37,7 +38,7 @@
/**
* ADataSpace.
*/
-enum ADataSpace {
+enum ADataSpace : int32_t {
/**
* Default-assumption data space, when not explicitly specified.
*
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
index 78f7590..ef3d280 100644
--- a/libs/nativewindow/include/android/native_window_aidl.h
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -100,7 +100,7 @@
if (__builtin_available(android __ANDROID_API_U__, *)) {
return ANativeWindow_readFromParcel(parcel, &mWindow);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
@@ -111,7 +111,7 @@
if (__builtin_available(android __ANDROID_API_U__, *)) {
return ANativeWindow_writeToParcel(mWindow, parcel);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 6f86c4a..22ad834 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,13 +16,13 @@
extern crate nativewindow_bindgen as ffi;
+pub mod surface;
+
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
use binder::{
- binder_impl::{
- BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize,
- SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
- },
+ binder_impl::{BorrowedParcel, UnstructuredParcelable},
+ impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
unstable_api::{status_result, AsNative},
StatusCode,
};
@@ -102,20 +102,35 @@
}
}
- /// Adopts the raw pointer and wraps it in a Rust AHardwareBuffer.
- ///
- /// # Errors
- ///
- /// Will panic if buffer_ptr is null.
+ /// Adopts the given raw pointer and wraps it in a Rust HardwareBuffer.
///
/// # Safety
///
- /// This function adopts the pointer but does NOT increment the refcount on the buffer. If the
- /// caller uses the pointer after the created object is dropped it will cause a memory leak.
+ /// This function takes ownership of the pointer and does NOT increment the refcount on the
+ /// buffer. If the caller uses the pointer after the created object is dropped it will cause
+ /// undefined behaviour. If the caller wants to continue using the pointer after calling this
+ /// then use [`clone_from_raw`](Self::clone_from_raw) instead.
pub unsafe fn from_raw(buffer_ptr: NonNull<AHardwareBuffer>) -> Self {
Self(buffer_ptr)
}
+ /// Creates a new Rust HardwareBuffer to wrap the given AHardwareBuffer without taking ownership
+ /// of it.
+ ///
+ /// Unlike [`from_raw`](Self::from_raw) this method will increment the refcount on the buffer.
+ /// This means that the caller can continue to use the raw buffer it passed in, and must call
+ /// [`AHardwareBuffer_release`](ffi::AHardwareBuffer_release) when it is finished with it to
+ /// avoid a memory leak.
+ ///
+ /// # Safety
+ ///
+ /// The buffer pointer must point to a valid `AHardwareBuffer`.
+ pub unsafe fn clone_from_raw(buffer: NonNull<AHardwareBuffer>) -> Self {
+ // SAFETY: The caller guarantees that the AHardwareBuffer pointer is valid.
+ unsafe { ffi::AHardwareBuffer_acquire(buffer.as_ptr()) };
+ Self(buffer)
+ }
+
/// Get the internal |AHardwareBuffer| pointer without decrementing the refcount. This can
/// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
@@ -197,7 +212,7 @@
}
impl Debug for HardwareBuffer {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("HardwareBuffer").field("id", &self.id()).finish()
}
}
@@ -210,81 +225,40 @@
}
}
-impl Serialize for HardwareBuffer {
- fn serialize(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
- SerializeOption::serialize_option(Some(self), parcel)
+impl UnstructuredParcelable for HardwareBuffer {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ let status =
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
+ unsafe { AHardwareBuffer_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ }
+
+ fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `AHardwareBuffer_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `AHardwareBuffer` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { AHardwareBuffer_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Self(
+ NonNull::new(buffer).expect(
+ "AHardwareBuffer_readFromParcel returned success but didn't allocate buffer",
+ ),
+ ))
}
}
-impl SerializeOption for HardwareBuffer {
- fn serialize_option(
- this: Option<&Self>,
- parcel: &mut BorrowedParcel,
- ) -> Result<(), StatusCode> {
- if let Some(this) = this {
- parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
-
- let status =
- // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
- // because it must have been allocated by `AHardwareBuffer_allocate`,
- // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
- // released it.
- unsafe { AHardwareBuffer_writeToParcel(this.0.as_ptr(), parcel.as_native_mut()) };
- status_result(status)
- } else {
- parcel.write(&NULL_PARCELABLE_FLAG)
- }
- }
-}
-
-impl Deserialize for HardwareBuffer {
- type UninitType = Option<Self>;
-
- fn uninit() -> Option<Self> {
- None
- }
-
- fn from_init(value: Self) -> Option<Self> {
- Some(value)
- }
-
- fn deserialize(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
- DeserializeOption::deserialize_option(parcel)
- .transpose()
- .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
- }
-}
-
-impl DeserializeOption for HardwareBuffer {
- fn deserialize_option(parcel: &BorrowedParcel) -> Result<Option<Self>, StatusCode> {
- let present: i32 = parcel.read()?;
- match present {
- NULL_PARCELABLE_FLAG => Ok(None),
- NON_NULL_PARCELABLE_FLAG => {
- let mut buffer = null_mut();
-
- let status =
- // SAFETY: Both pointers must be valid because they are obtained from references.
- // `AHardwareBuffer_readFromParcel` doesn't store them or do anything else special
- // with them. If it returns success then it will have allocated a new
- // `AHardwareBuffer` and incremented the reference count, so we can use it until we
- // release it.
- unsafe { AHardwareBuffer_readFromParcel(parcel.as_native(), &mut buffer) };
-
- status_result(status)?;
-
- Ok(Some(Self(NonNull::new(buffer).expect(
- "AHardwareBuffer_readFromParcel returned success but didn't allocate buffer",
- ))))
- }
- _ => Err(StatusCode::BAD_VALUE),
- }
- }
-}
-
-impl SerializeArray for HardwareBuffer {}
-
-impl DeserializeArray for HardwareBuffer {}
+impl_deserialize_for_unstructured_parcelable!(HardwareBuffer);
+impl_serialize_for_unstructured_parcelable!(HardwareBuffer);
// SAFETY: The underlying *AHardwareBuffers can be moved between threads.
unsafe impl Send for HardwareBuffer {}
diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs
new file mode 100644
index 0000000..25fea80
--- /dev/null
+++ b/libs/nativewindow/rust/src/surface.rs
@@ -0,0 +1,143 @@
+// Copyright (C) 2024 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.
+
+//! Rust wrapper for `ANativeWindow` and related types.
+
+use binder::{
+ binder_impl::{BorrowedParcel, UnstructuredParcelable},
+ impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
+ unstable_api::{status_result, AsNative},
+ StatusCode,
+};
+use nativewindow_bindgen::{
+ AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getFormat,
+ ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_readFromParcel,
+ ANativeWindow_release, ANativeWindow_writeToParcel,
+};
+use std::error::Error;
+use std::fmt::{self, Debug, Display, Formatter};
+use std::ptr::{null_mut, NonNull};
+
+/// Wrapper around an opaque C `ANativeWindow`.
+#[derive(PartialEq, Eq)]
+pub struct Surface(NonNull<ANativeWindow>);
+
+impl Surface {
+ /// Returns the current width in pixels of the window surface.
+ pub fn width(&self) -> Result<u32, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let width = unsafe { ANativeWindow_getWidth(self.0.as_ptr()) };
+ width.try_into().map_err(|_| ErrorCode(width))
+ }
+
+ /// Returns the current height in pixels of the window surface.
+ pub fn height(&self) -> Result<u32, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let height = unsafe { ANativeWindow_getHeight(self.0.as_ptr()) };
+ height.try_into().map_err(|_| ErrorCode(height))
+ }
+
+ /// Returns the current pixel format of the window surface.
+ pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let format = unsafe { ANativeWindow_getFormat(self.0.as_ptr()) };
+ format.try_into().map_err(|_| ErrorCode(format))
+ }
+}
+
+impl Drop for Surface {
+ fn drop(&mut self) {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_release(self.0.as_ptr()) }
+ }
+}
+
+impl Debug for Surface {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("Surface")
+ .field("width", &self.width())
+ .field("height", &self.height())
+ .field("format", &self.format())
+ .finish()
+ }
+}
+
+impl Clone for Surface {
+ fn clone(&self) -> Self {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_acquire(self.0.as_ptr()) };
+ Self(self.0)
+ }
+}
+
+impl UnstructuredParcelable for Surface {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ let status =
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ }
+
+ fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `ANativeWindow_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `ANativeWindow` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { ANativeWindow_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Self(
+ NonNull::new(buffer)
+ .expect("ANativeWindow_readFromParcel returned success but didn't allocate buffer"),
+ ))
+ }
+}
+
+impl_deserialize_for_unstructured_parcelable!(Surface);
+impl_serialize_for_unstructured_parcelable!(Surface);
+
+// SAFETY: The underlying *ANativeWindow can be moved between threads.
+unsafe impl Send for Surface {}
+
+// SAFETY: The underlying *ANativeWindow can be used from multiple threads concurrently.
+unsafe impl Sync for Surface {}
+
+/// An error code returned by methods on [`Surface`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct ErrorCode(i32);
+
+impl Error for ErrorCode {}
+
+impl Display for ErrorCode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Error {}", self.0)
+ }
+}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index 4525a42..5689f7d 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -19,3 +19,4 @@
#include <android/hardware_buffer_aidl.h>
#include <android/hdr_metadata.h>
#include <android/native_window.h>
+#include <android/native_window_aidl.h>
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d71e55f..a733fd0 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1022,7 +1022,7 @@
.fakeOutputDataspace = fakeDataspace}));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
- static constexpr float kDimmingThreshold = 0.2f;
+ static constexpr float kDimmingThreshold = 0.9f;
// ...or we're rendering an HDR layer down to an 8-bit target
// Most HDR standards require at least 10-bits of color depth for source content, so we
// can just extract the transfer function rather than dig into precise gralloc layout.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index dc7c75a..84b6fa9 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -84,6 +84,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -96,15 +98,11 @@
shared_libs: [
"libgui",
"libinput",
- "libstatspull",
- "libstatssocket",
],
},
host: {
static_libs: [
"libinput",
- "libstatspull",
- "libstatssocket",
],
include_dirs: [
"bionic/libc/kernel/android/uapi/",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 492551e..7fa33f6 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -64,6 +64,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -75,15 +77,11 @@
shared_libs: [
"libgui",
"libinput",
- "libstatspull",
- "libstatssocket",
],
},
host: {
static_libs: [
"libinput",
- "libstatspull",
- "libstatssocket",
],
},
},
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ccb8773..1ae36e7 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -82,6 +82,7 @@
"liblog",
"libPlatformProperties",
"libstatslog",
+ "libstatspull",
"libutils",
],
static_libs: [
@@ -98,14 +99,12 @@
android: {
shared_libs: [
"libinput",
- "libstatspull",
],
},
host: {
static_libs: [
"libinput",
"libbinder",
- "libstatspull",
],
},
},
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 9e0e7b5..bf2d568 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3403,7 +3403,6 @@
static const mat4 kDefaultColorTransformMat;
static const Region kDebugRegion;
- static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs;
static const HdrCapabilities kHdrCapabilities;
StrictMock<mock::CompositionEngine> mCompositionEngine;
@@ -3426,7 +3425,6 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
-const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index 2c71a2e..99062f0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -179,23 +179,35 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 1);
+ ASSERT_EQ(packets.size(), 2);
- const auto& packet = packets[0];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- ASSERT_TRUE(buffer_event.has_duration_ns());
- EXPECT_EQ(buffer_event.duration_ns(), duration);
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+ ASSERT_TRUE(buffer_event1.has_duration_ns());
+ EXPECT_EQ(buffer_event1.duration_ns(), duration);
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), 0);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
}
@@ -219,7 +231,18 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ const auto& frame_event = packet.graphics_frame_event();
+ ASSERT_TRUE(frame_event.has_buffer_event());
+ const auto& buffer_event = frame_event.buffer_event();
+ ASSERT_TRUE(buffer_event.has_type());
+ EXPECT_EQ(buffer_event.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
{
@@ -235,22 +258,56 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
+ ASSERT_EQ(packets.size(), 3);
- const auto& packet = packets[1];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- EXPECT_FALSE(buffer_event.has_duration_ns());
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event1.has_duration_ns());
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), timestamp);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_buffer_id());
+ EXPECT_EQ(buffer_event2.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event2.has_frame_number());
+ EXPECT_EQ(buffer_event2.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event2.has_duration_ns());
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ const auto& frame_event3 = packet3.graphics_frame_event();
+ ASSERT_TRUE(frame_event3.has_buffer_event());
+ const auto& buffer_event3 = frame_event3.buffer_event();
+ ASSERT_TRUE(buffer_event3.has_buffer_id());
+ EXPECT_EQ(buffer_event3.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event3.has_frame_number());
+ EXPECT_EQ(buffer_event3.frame_number(), 0);
+ ASSERT_TRUE(buffer_event3.has_type());
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
+ EXPECT_FALSE(buffer_event3.has_duration_ns());
}
}
@@ -285,7 +342,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -293,6 +350,8 @@
ASSERT_TRUE(packet1.has_graphics_frame_event());
ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet1.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -300,6 +359,17 @@
ASSERT_TRUE(packet2.has_graphics_frame_event());
ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet2.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet3.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
@@ -322,7 +392,15 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ ASSERT_TRUE(packet.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
@@ -357,7 +435,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -367,6 +445,7 @@
ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event1.duration_ns(), duration);
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -376,6 +455,17 @@
ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event2.duration_ns(), duration);
+ EXPECT_EQ(buffer_event2.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ const auto& buffer_event3 = packet3.graphics_frame_event().buffer_event();
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
} // namespace