Merge "Use apexd to set up chroot apex dir" into sc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a8c0500..922367f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,8 +8,7 @@
                include/input/
                include/powermanager/
                libs/binder/fuzzer/
-               libs/binder/ndk/
-               libs/binder/tests/fuzzers/
+               libs/binder/
                libs/binderthreadstate/
                libs/graphicsenv/
                libs/gui/
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index e15ac3f..c2c8a72 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -64,8 +64,8 @@
     if (shutdown_ || threads_.empty()) {
         return;
     }
-    while (!tasks_.empty()) tasks_.pop();
     futures_map_.clear();
+    while (!tasks_.empty()) tasks_.pop();
 
     shutdown_ = true;
     condition_variable_.notify_all();
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index b2518ad..2c573e4 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1762,6 +1762,27 @@
     EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
 }
 
+TEST_F(DumpPoolTest, Shutdown_withoutCrash) {
+    bool run_1 = false;
+    auto dump_func_1 = [&]() {
+        run_1 = true;
+    };
+    auto dump_func = []() {
+        sleep(1);
+    };
+
+    dump_pool_->start(/* thread_counts = */1);
+    dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+    dump_pool_->enqueueTask(/* task_name = */"2", dump_func);
+    dump_pool_->enqueueTask(/* task_name = */"3", dump_func);
+    dump_pool_->enqueueTask(/* task_name = */"4", dump_func);
+    dump_pool_->waitForTask("1", "", out_fd_.get());
+    dump_pool_->shutdown();
+
+    EXPECT_TRUE(run_1);
+    EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
 class TaskQueueTest : public DumpstateBaseTest {
 public:
     void SetUp() {
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 1ebdc47..ec3bc61 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -49,13 +49,20 @@
         "vibrator/CommandAlwaysOnDisable.cpp",
         "vibrator/CommandAlwaysOnEnable.cpp",
         "vibrator/CommandCompose.cpp",
+        "vibrator/CommandComposePwle.cpp",
+        "vibrator/CommandGetBandwidthAmplitudeMap.cpp",
         "vibrator/CommandGetCapabilities.cpp",
         "vibrator/CommandGetCompositionDelayMax.cpp",
         "vibrator/CommandGetCompositionSizeMax.cpp",
+        "vibrator/CommandGetFrequencyMinimum.cpp",
+        "vibrator/CommandGetFrequencyResolution.cpp",
         "vibrator/CommandGetPrimitiveDuration.cpp",
+        "vibrator/CommandGetPwleCompositionSizeMax.cpp",
+        "vibrator/CommandGetPwlePrimitiveDurationMax.cpp",
         "vibrator/CommandGetQFactor.cpp",
         "vibrator/CommandGetResonantFrequency.cpp",
         "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+        "vibrator/CommandGetSupportedBraking.cpp",
         "vibrator/CommandGetSupportedEffects.cpp",
         "vibrator/CommandGetSupportedPrimitives.cpp",
         "vibrator/CommandOff.cpp",
diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp
new file mode 100644
index 0000000..b8308ce
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <charconv>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::ActivePwle;
+using aidl::Braking;
+using aidl::BrakingPwle;
+using aidl::PrimitivePwle;
+
+class CommandComposePwle : public Command {
+    std::string getDescription() const override { return "Compose PWLE vibration."; }
+
+    std::string getUsageSummary() const override {
+        return "[options] a <active pwle params> b <braking pwle params> ...";
+    }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+            {"-b", {"Block for duration of vibration."}},
+            {"a <startAmplitude> <startFrequency> <endAmplitude> <endFrequency> <duration>",
+             {"Enter the active PWLE segment parameters"}},
+            {"b <brakingMethod> <duration>", {"Enter the braking PWLE segment parameters"}},
+            {"...", {"May repeat multiple times."}},
+        };
+        return details;
+    }
+
+    int getIntFromString(std::string input, int *output) {
+        int rc = 0;
+        int value;
+        const auto res = std::from_chars(input.data(), input.data() + input.size(), value);
+        if (res.ec == std::errc::invalid_argument) {
+            std::cerr << "Invalid int argument: " << input << std::endl;
+            rc = (int)std::errc::invalid_argument;
+        } else if (res.ec == std::errc::result_out_of_range) {
+            std::cerr << "Result out of range: " << input << std::endl;
+            rc = (int)std::errc::result_out_of_range;
+        }
+        *output = value;
+        return rc;
+    }
+
+    float getFloatFromString(std::string_view input, float *output) {
+        int rc = 0;
+        errno = 0;
+        // from_chars doesn't support conversion to float so we need to first
+        // convert the string_view to string and use the C-string for strtof
+        float value = strtof(std::string(input).c_str(), NULL);
+
+        if (input == "0.0" || input == "0") {
+            return rc;
+        }
+
+        if (value <= 0.0) {
+            std::cerr << "Invalid float argument: " << input << std::endl;
+            rc = EINVAL;
+        } else if (errno == ERANGE) {
+            std::cerr << "Result out of range: " << input << std::endl;
+            rc = errno;
+        } else {
+            *output = value;
+        }
+        return rc;
+    }
+
+    Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
+        if (args.empty()) {
+            std::cerr << "Missing arguments! Please see usage" << std::endl;
+            return USAGE;
+        }
+        while (!args.empty()) {
+            PrimitivePwle pwle;
+            auto nextArg = args.pop();
+
+            if (*nextArg == "a") {
+                auto startAmplitude = args.pop();
+                float startAmp;
+                if (getFloatFromString(*startAmplitude, &startAmp))
+                    return USAGE;
+
+                auto startFrequency = args.pop();
+                float startFreq;
+                if (getFloatFromString(*startFrequency, &startFreq))
+                    return USAGE;
+
+                auto endAmplitude = args.pop();
+                float endAmp;
+                if (getFloatFromString(*endAmplitude, &endAmp))
+                    return USAGE;
+
+                auto endFrequency = args.pop();
+                float endFreq;
+                if (getFloatFromString(*endFrequency, &endFreq))
+                    return USAGE;
+
+                auto duration = args.pop();
+                int dur;
+                if (getIntFromString(*duration, &dur))
+                    return USAGE;
+
+                ActivePwle active = {startAmp, startFreq, endAmp, endFreq, dur};
+                pwle = active;
+            } else if (*nextArg == "b") {
+                auto brakingMethod = args.pop();
+                Braking brakingMeth;
+                if (getIntFromString(*brakingMethod, (int *)&brakingMeth))
+                    return USAGE;
+
+                auto duration = args.pop();
+                int dur;
+                if (getIntFromString(*duration, &dur))
+                    return USAGE;
+
+                BrakingPwle braking = {brakingMeth, dur};
+                pwle = braking;
+            } else {
+                std::cerr << "Invalid arguments! Please see usage" << std::endl;
+                return USAGE;
+            }
+            mCompositePwle.emplace_back(std::move(pwle));
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto hal = getHal<aidl::IVibrator>();
+
+        if (!hal) {
+            return UNAVAILABLE;
+        }
+
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
+
+        std::shared_ptr<VibratorCallback> callback;
+
+        if (mBlocking) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback);
+
+        if (status.isOk() && callback) {
+            callback->waitForComplete();
+        }
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
+    }
+
+    bool mBlocking;
+    std::vector<PrimitivePwle> mCompositePwle;
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandComposePwle>("composePwle");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
new file mode 100644
index 0000000..aa01a11
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetBandwidthAmplitudeMap : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator bandwidth amplitude map.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        std::vector<float> bandwidthAmplitude;
+        float frequencyMinimumHz;
+        float frequencyResolutionHz;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status =
+                hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude);
+            statusStr = status.getDescription();
+            ret = (status.isOk() ? OK : ERROR);
+
+            status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
+            ret = (status.isOk() ? OK : ERROR);
+
+            status =
+                hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
+            ret = (status.isOk() ? OK : ERROR);
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Bandwidth Amplitude Map: " << std::endl;
+        float frequency = frequencyMinimumHz;
+        for (auto &e : bandwidthAmplitude) {
+            std::cout << frequency << ":" << e << std::endl;
+            frequency += frequencyResolutionHz;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetBandwidthAmplitudeMap>(
+        "getBandwidthAmplitudeMap");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
new file mode 100644
index 0000000..504c648
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetFrequencyMinimum : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator minimum frequency in Hz.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        float frequencyMinimumHz;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyMinimum>("getFrequencyMinimum");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
new file mode 100644
index 0000000..de35838
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetFrequencyResolution : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator frequency resolution in Hz.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        float frequencyResolutionHz;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status =
+                hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyResolution>(
+        "getFrequencyResolution");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
new file mode 100644
index 0000000..b2c3551
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleCompositionSizeMax : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator PWLE composition size max.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        int32_t maxSize;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Max Size: " << maxSize << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetPwleCompositionSizeMax>(
+        "getPwleCompositionSizeMax");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
new file mode 100644
index 0000000..9081973
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwlePrimitiveDurationMax : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator PWLE primitive duration size max in milliseconds.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        int32_t maxDurationMs;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetPwlePrimitiveDurationMax>(
+        "getPwlePrimitiveDurationMax");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
new file mode 100644
index 0000000..b326e07
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Braking;
+
+class CommandGetSupportedBraking : public Command {
+    std::string getDescription() const override { return "List of supported braking mechanisms."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        std::vector<Braking> braking;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Braking Mechanisms:" << std::endl;
+        for (auto &e : braking) {
+            std::cout << "  " << toString(e) << std::endl;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command =
+    CommandRegistry<CommandVibrator>::Register<CommandGetSupportedBraking>("getSupportedBraking");
+
+}  // namespace vibrator
+}  // namespace idlcli
+}  // namespace android
diff --git a/headers/media_plugin/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h
index 9ff934e..4b69130 100644
--- a/headers/media_plugin/media/openmax/OMX_Core.h
+++ b/headers/media_plugin/media/openmax/OMX_Core.h
@@ -556,6 +556,12 @@
      */
     OMX_EventConfigUpdate,
 
+    /**
+     * Event fired by a tunneled decoder when the first frame is decoded and
+     * ready to be rendered.
+     */
+    OMX_EventOnFirstTunnelFrameReady,
+
     OMX_EventMax = 0x7FFFFFFF
 } OMX_EVENTTYPE;
 
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index a427db7..07bd749 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -103,6 +103,7 @@
     OMX_IndexParamConsumerUsageBits,                /**< reference: OMX_PARAM_U32TYPE */
     OMX_IndexConfigLatency,                         /**< reference: OMX_PARAM_U32TYPE */
     OMX_IndexConfigLowLatency,                      /**< reference: OMX_CONFIG_BOOLEANTYPE */
+    OMX_IndexConfigAndroidTunnelPeek,               /**< reference: OMX_CONFIG_BOOLEANTYPE */
     OMX_IndexExtOtherEndUnused,
 
     /* Time configurations */
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 424299d..509ee0e 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -83,7 +83,7 @@
  *
  * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
  *
- * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
+ * This is the equivalent of: [android.net.ConnectivityManager#bindProcessToNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#bindProcessToNetwork(android.net.Network))
  *
  * Available since API level 23.
  */
@@ -91,6 +91,41 @@
 
 
 /**
+ * Gets the |network| bound to the current process, as per android_setprocnetwork.
+ *
+ * This is the equivalent of: [android.net.ConnectivityManager#getBoundNetworkForProcess()](https://developer.android.com/reference/android/net/ConnectivityManager.html#getBoundNetworkForProcess(android.net.Network))
+ * Returns 0 on success, or -1 setting errno to EINVAL if a null pointer is
+ * passed in.
+ *
+ *
+ * Available since API level 31.
+ */
+int android_getprocnetwork(net_handle_t *network) __INTRODUCED_IN(31);
+
+/**
+ * Binds domain name resolutions performed by this process to |network|.
+ * android_setprocnetwork takes precedence over this setting.
+ *
+ * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
+ * On success 0 is returned. On error -1 is returned, and errno is set.
+ *
+ * Available since API level 31.
+ */
+int android_setprocdns(net_handle_t network) __INTRODUCED_IN(31);
+
+/**
+ * Gets the |network| to which domain name resolutions are bound on the
+ * current process.
+ *
+ * Returns 0 on success, or -1 setting errno to EINVAL if a null pointer is
+ * passed in.
+ *
+ * Available since API level 31.
+ */
+int android_getprocdns(net_handle_t *network) __INTRODUCED_IN(31);
+
+
+/**
  * Perform hostname resolution via the DNS servers associated with |network|.
  *
  * All arguments (apart from |network|) are used identically as those passed
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index c17f822..eb9c528 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -325,6 +325,38 @@
                                      const ARect& destination, int32_t transform)
                                      __INTRODUCED_IN(29);
 
+/**
+ * \param source The sub-rect within the buffer's content to be rendered inside the surface's area
+ * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
+ * and height must be > 0.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setSourceRect(ASurfaceTransaction* transaction,
+                                       ASurfaceControl* surface_control, const ARect& source)
+                                       __INTRODUCED_IN(31);
+
+/**
+ * \param destination Specifies the rect in the parent's space where this surface will be drawn. The
+ * post source rect bounds are scaled to fit the destination rect. The surface's destination rect is
+ * clipped by the bounds of its parent. The destination rect's width and height must be > 0.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction,
+                                     ASurfaceControl* surface_control, const ARect& destination)
+                                     __INTRODUCED_IN(31);
+
+/**
+ * \param transform The transform applied after the source rect is applied to the buffer. This
+ * parameter should be set to 0 for no transform. To specify a transfrom use the
+ * NATIVE_WINDOW_TRANSFORM_* enum.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setTransform(ASurfaceTransaction* transaction,
+                                      ASurfaceControl* surface_control, int32_t transform)
+                                      __INTRODUCED_IN(31);
 
 /**
  * Parameter for ASurfaceTransaction_setBufferTransparency().
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index a17e482..274c3ae 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -121,11 +121,16 @@
         "ParcelFileDescriptor.cpp",
         "PersistableBundle.cpp",
         "ProcessState.cpp",
+        "RpcAddress.cpp",
+        "RpcConnection.cpp",
+        "RpcServer.cpp",
+        "RpcState.cpp",
         "Static.cpp",
         "Stability.cpp",
         "Status.cpp",
         "TextOutput.cpp",
         "Utils.cpp",
+        ":packagemanager_aidl",
         ":libbinder_aidl",
         ":activity_manager_procstate_aidl",
     ],
@@ -228,9 +233,6 @@
 filegroup {
     name: "libbinder_aidl",
     srcs: [
-        "aidl/android/content/pm/IPackageChangeObserver.aidl",
-        "aidl/android/content/pm/IPackageManagerNative.aidl",
-        "aidl/android/content/pm/PackageChangeEvent.aidl",
         "aidl/android/os/IClientCallback.aidl",
         "aidl/android/os/IServiceCallback.aidl",
         "aidl/android/os/IServiceManager.aidl",
@@ -239,6 +241,16 @@
     path: "aidl",
 }
 
+filegroup {
+    name: "packagemanager_aidl",
+    srcs: [
+        "aidl/android/content/pm/IPackageChangeObserver.aidl",
+        "aidl/android/content/pm/IPackageManagerNative.aidl",
+        "aidl/android/content/pm/PackageChangeEvent.aidl",
+    ],
+    path: "aidl",
+}
+
 aidl_interface {
     name: "libbinder_aidl_test_stub",
     unstable: true,
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index ddda024..825a821 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,6 +21,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
+#include <binder/RpcConnection.h>
 #include <binder/Stability.h>
 #include <cutils/compiler.h>
 #include <utils/Log.h>
@@ -133,25 +134,56 @@
         }
         sTrackingMap[trackedUid]++;
     }
-    return new BpBinder(handle, trackedUid);
+    return new BpBinder(BinderHandle{handle}, trackedUid);
 }
 
-BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
-    : mStability(0)
-    , mHandle(handle)
-    , mAlive(1)
-    , mObitsSent(0)
-    , mObituaries(nullptr)
-    , mTrackedUid(trackedUid)
-{
-    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
+BpBinder* BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
+    LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
 
+    // These are not currently tracked, since there is no UID or other
+    // identifier to track them with. However, if similar functionality is
+    // needed, connection objects keep track of all BpBinder objects on a
+    // per-connection basis.
+
+    return new BpBinder(SocketHandle{connection, address});
+}
+
+BpBinder::BpBinder(Handle&& handle)
+      : mStability(0),
+        mHandle(handle),
+        mAlive(true),
+        mObitsSent(false),
+        mObituaries(nullptr),
+        mTrackedUid(-1) {
     extendObjectLifetime(OBJECT_LIFETIME_WEAK);
-    IPCThreadState::self()->incWeakHandle(handle, this);
 }
 
-int32_t BpBinder::handle() const {
-    return mHandle;
+BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
+    mTrackedUid = trackedUid;
+
+    ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle());
+
+    IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
+}
+
+BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
+    LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+}
+
+bool BpBinder::isRpcBinder() const {
+    return std::holds_alternative<SocketHandle>(mHandle);
+}
+
+const RpcAddress& BpBinder::rpcAddress() const {
+    return std::get<SocketHandle>(mHandle).address;
+}
+
+const sp<RpcConnection>& BpBinder::rpcConnection() const {
+    return std::get<SocketHandle>(mHandle).connection;
+}
+
+int32_t BpBinder::binderHandle() const {
+    return std::get<BinderHandle>(mHandle).handle;
 }
 
 bool BpBinder::isDescriptorCached() const {
@@ -190,9 +222,10 @@
 
 status_t BpBinder::pingBinder()
 {
-    Parcel send;
+    Parcel data;
+    data.markForBinder(this);
     Parcel reply;
-    return transact(PING_TRANSACTION, send, &reply);
+    return transact(PING_TRANSACTION, data, &reply);
 }
 
 status_t BpBinder::dump(int fd, const Vector<String16>& args)
@@ -236,8 +269,13 @@
             }
         }
 
-        status_t status = IPCThreadState::self()->transact(
-            mHandle, code, data, reply, flags);
+        status_t status;
+        if (CC_UNLIKELY(isRpcBinder())) {
+            status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+        } else {
+            status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
+        }
+
         if (status == DEAD_OBJECT) mAlive = 0;
 
         return status;
@@ -250,6 +288,8 @@
 status_t BpBinder::linkToDeath(
     const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
 {
+    if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+
     Obituary ob;
     ob.recipient = recipient;
     ob.cookie = cookie;
@@ -267,10 +307,10 @@
                 if (!mObituaries) {
                     return NO_MEMORY;
                 }
-                ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
+                ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle());
                 getWeakRefs()->incWeak(this);
                 IPCThreadState* self = IPCThreadState::self();
-                self->requestDeathNotification(mHandle, this);
+                self->requestDeathNotification(binderHandle(), this);
                 self->flushCommands();
             }
             ssize_t res = mObituaries->add(ob);
@@ -286,6 +326,8 @@
     const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
     wp<DeathRecipient>* outRecipient)
 {
+    if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+
     AutoMutex _l(mLock);
 
     if (mObitsSent) {
@@ -303,9 +345,9 @@
             }
             mObituaries->removeAt(i);
             if (mObituaries->size() == 0) {
-                ALOGV("Clearing death notification: %p handle %d\n", this, mHandle);
+                ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle());
                 IPCThreadState* self = IPCThreadState::self();
-                self->clearDeathNotification(mHandle, this);
+                self->clearDeathNotification(binderHandle(), this);
                 self->flushCommands();
                 delete mObituaries;
                 mObituaries = nullptr;
@@ -319,8 +361,10 @@
 
 void BpBinder::sendObituary()
 {
-    ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
-        this, mHandle, mObitsSent ? "true" : "false");
+    LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder.");
+
+    ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(),
+          mObitsSent ? "true" : "false");
 
     mAlive = 0;
     if (mObitsSent) return;
@@ -328,9 +372,9 @@
     mLock.lock();
     Vector<Obituary>* obits = mObituaries;
     if(obits != nullptr) {
-        ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
+        ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle());
         IPCThreadState* self = IPCThreadState::self();
-        self->clearDeathNotification(mHandle, this);
+        self->clearDeathNotification(binderHandle(), this);
         self->flushCommands();
         mObituaries = nullptr;
     }
@@ -388,7 +432,9 @@
 
 BpBinder::~BpBinder()
 {
-    ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
+
+    if (CC_UNLIKELY(isRpcBinder())) return;
 
     IPCThreadState* ipc = IPCThreadState::self();
 
@@ -396,7 +442,8 @@
         AutoMutex _l(sTrackingLock);
         uint32_t trackedValue = sTrackingMap[mTrackedUid];
         if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) {
-            ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, mHandle);
+            ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this,
+                  binderHandle());
         } else {
             if (CC_UNLIKELY(
                 (trackedValue & LIMIT_REACHED_MASK) &&
@@ -413,26 +460,31 @@
     }
 
     if (ipc) {
-        ipc->expungeHandle(mHandle, this);
-        ipc->decWeakHandle(mHandle);
+        ipc->expungeHandle(binderHandle(), this);
+        ipc->decWeakHandle(binderHandle());
     }
 }
 
 void BpBinder::onFirstRef()
 {
-    ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
+    if (CC_UNLIKELY(isRpcBinder())) return;
     IPCThreadState* ipc = IPCThreadState::self();
-    if (ipc) ipc->incStrongHandle(mHandle, this);
+    if (ipc) ipc->incStrongHandle(binderHandle(), this);
 }
 
 void BpBinder::onLastStrongRef(const void* /*id*/)
 {
-    ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
+    if (CC_UNLIKELY(isRpcBinder())) {
+        (void)rpcConnection()->sendDecStrong(rpcAddress());
+        return;
+    }
     IF_ALOGV() {
         printRefs();
     }
     IPCThreadState* ipc = IPCThreadState::self();
-    if (ipc) ipc->decStrongHandle(mHandle);
+    if (ipc) ipc->decStrongHandle(binderHandle());
 
     mLock.lock();
     Vector<Obituary>* obits = mObituaries;
@@ -442,7 +494,7 @@
                   mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
         }
 
-        if (ipc) ipc->clearDeathNotification(mHandle, this);
+        if (ipc) ipc->clearDeathNotification(binderHandle(), this);
         mObituaries = nullptr;
     }
     mLock.unlock();
@@ -457,9 +509,12 @@
 
 bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/)
 {
-    ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
+    // RPC binder doesn't currently support inc from weak binders
+    if (CC_UNLIKELY(isRpcBinder())) return false;
+
+    ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle());
     IPCThreadState* ipc = IPCThreadState::self();
-    return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false;
+    return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false;
 }
 
 uint32_t BpBinder::getBinderProxyCount(uint32_t uid)
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index e4ac4b4..8676955 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -26,6 +26,22 @@
 
 namespace android {
 
+std::string hexString(const void* bytes, size_t len) {
+    if (bytes == nullptr) return "<null>";
+
+    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+    const char chars[] = "0123456789abcdef";
+    std::string result;
+    result.resize(len * 2);
+
+    for (size_t i = 0; i < len; i++) {
+        result[2 * i] = chars[bytes8[i] >> 4];
+        result[2 * i + 1] = chars[bytes8[i] & 0xf];
+    }
+
+    return result;
+}
+
 // ---------------------------------------------------------------------
 
 static const char indentStr[] =
diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h
index ac71e00..7ca087e 100644
--- a/libs/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -17,13 +17,13 @@
 #pragma once
 
 #include <stdint.h>
-#include <sys/cdefs.h>
 #include <sys/types.h>
+#include <string>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
-__BEGIN_DECLS
+std::string hexString(const void* data, size_t size);
 
 const char* stringForIndent(int32_t indentLevel);
 
@@ -37,10 +37,7 @@
     size_t alignment=0, bool cArrayStyle=false,
     debugPrintFunc func = nullptr, void* cookie = nullptr);
 
-
-ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
-
-__END_DECLS
+extern "C" ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
 
 // ---------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 79a11d2..406bd54 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -689,6 +689,8 @@
                                   uint32_t code, const Parcel& data,
                                   Parcel* reply, uint32_t flags)
 {
+    LOG_ALWAYS_FATAL_IF(data.isForRpc(), "Parcel constructed for RPC, but being used with binder.");
+
     status_t err;
 
     flags |= TF_ACCEPT_FDS;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 3773760..7fedba2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -41,13 +41,15 @@
 #include <binder/TextOutput.h>
 
 #include <cutils/ashmem.h>
+#include <cutils/compiler.h>
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
-#include <utils/misc.h>
-#include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/misc.h>
 
 #include <private/binder/binder_module.h>
+#include "RpcState.h"
 #include "Static.h"
 #include "Utils.h"
 
@@ -191,6 +193,22 @@
 
 status_t Parcel::flattenBinder(const sp<IBinder>& binder)
 {
+    if (isForRpc()) {
+        if (binder) {
+            status_t status = writeInt32(1); // non-null
+            if (status != OK) return status;
+            RpcAddress address = RpcAddress::zero();
+            status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+            if (status != OK) return status;
+            status = address.writeToParcel(this);
+            if (status != OK) return status;
+        } else {
+            status_t status = writeInt32(0); // null
+            if (status != OK) return status;
+        }
+        return finishFlattenBinder(binder);
+    }
+
     flat_binder_object obj;
     obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
 
@@ -205,8 +223,13 @@
             BpBinder *proxy = binder->remoteBinder();
             if (proxy == nullptr) {
                 ALOGE("null proxy");
+            } else {
+                if (proxy->isRpcBinder()) {
+                    ALOGE("Sending a socket binder over RPC is prohibited");
+                    return INVALID_OPERATION;
+                }
             }
-            const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
+            const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0;
             obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
             obj.handle = handle;
@@ -245,6 +268,26 @@
 
 status_t Parcel::unflattenBinder(sp<IBinder>* out) const
 {
+    if (isForRpc()) {
+        LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
+                            "RpcConnection required to read from remote parcel");
+
+        int32_t isNull;
+        status_t status = readInt32(&isNull);
+        if (status != OK) return status;
+
+        sp<IBinder> binder;
+
+        if (isNull & 1) {
+            auto addr = RpcAddress::zero();
+            status_t status = addr.readFromParcel(*this);
+            if (status != OK) return status;
+            binder = mConnection->state()->onBinderEntering(mConnection, addr);
+        }
+
+        return finishUnflattenBinder(binder, out);
+    }
+
     const flat_binder_object* flat = readObject(false);
 
     if (flat) {
@@ -511,6 +554,21 @@
     mDeallocZero = true;
 }
 
+void Parcel::markForBinder(const sp<IBinder>& binder) {
+    if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
+        markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+    }
+}
+
+void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+    LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
+    mConnection = connection;
+}
+
+bool Parcel::isForRpc() const {
+    return mConnection != nullptr;
+}
+
 void Parcel::updateWorkSourceRequestHeaderPosition() const {
     // Only update the request headers once. We only want to point
     // to the first headers read/written.
@@ -533,12 +591,14 @@
 }
 
 status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
-    const IPCThreadState* threadState = IPCThreadState::self();
-    writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
-    updateWorkSourceRequestHeaderPosition();
-    writeInt32(threadState->shouldPropagateWorkSource() ?
-            threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
-    writeInt32(kHeader);
+    if (CC_LIKELY(!isForRpc())) {
+        const IPCThreadState* threadState = IPCThreadState::self();
+        writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+        updateWorkSourceRequestHeaderPosition();
+        writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
+                                                            : IPCThreadState::kUnsetWorkSource);
+        writeInt32(kHeader);
+    }
 
     // currently the interface identification token is just its name as a string
     return writeString16(str, len);
@@ -585,31 +645,34 @@
                               size_t len,
                               IPCThreadState* threadState) const
 {
-    // StrictModePolicy.
-    int32_t strictPolicy = readInt32();
-    if (threadState == nullptr) {
-        threadState = IPCThreadState::self();
+    if (CC_LIKELY(!isForRpc())) {
+        // StrictModePolicy.
+        int32_t strictPolicy = readInt32();
+        if (threadState == nullptr) {
+            threadState = IPCThreadState::self();
+        }
+        if ((threadState->getLastTransactionBinderFlags() & IBinder::FLAG_ONEWAY) != 0) {
+            // For one-way calls, the callee is running entirely
+            // disconnected from the caller, so disable StrictMode entirely.
+            // Not only does disk/network usage not impact the caller, but
+            // there's no way to communicate back violations anyway.
+            threadState->setStrictModePolicy(0);
+        } else {
+            threadState->setStrictModePolicy(strictPolicy);
+        }
+        // WorkSource.
+        updateWorkSourceRequestHeaderPosition();
+        int32_t workSource = readInt32();
+        threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+        // vendor header
+        int32_t header = readInt32();
+        if (header != kHeader) {
+            ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader,
+                  header);
+            return false;
+        }
     }
-    if ((threadState->getLastTransactionBinderFlags() &
-         IBinder::FLAG_ONEWAY) != 0) {
-      // For one-way calls, the callee is running entirely
-      // disconnected from the caller, so disable StrictMode entirely.
-      // Not only does disk/network usage not impact the caller, but
-      // there's no way to commuicate back any violations anyway.
-      threadState->setStrictModePolicy(0);
-    } else {
-      threadState->setStrictModePolicy(strictPolicy);
-    }
-    // WorkSource.
-    updateWorkSourceRequestHeaderPosition();
-    int32_t workSource = readInt32();
-    threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
-    // vendor header
-    int32_t header = readInt32();
-    if (header != kHeader) {
-        ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header);
-        return false;
-    }
+
     // Interface descriptor.
     size_t parcel_interface_len;
     const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len);
@@ -1070,6 +1133,11 @@
 
 status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
 {
+    if (isForRpc()) {
+        ALOGE("Cannot write file descriptor to remote binder.");
+        return BAD_TYPE;
+    }
+
     flat_binder_object obj;
     obj.hdr.type = BINDER_TYPE_FD;
     obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
@@ -2413,6 +2481,7 @@
     mDataPos = 0;
     ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
     ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
+    mConnection = nullptr;
     mObjects = nullptr;
     mObjectsSize = 0;
     mObjectsCapacity = 0;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index edadcd5..abb792e 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -205,10 +205,12 @@
 //
 // Returns -1 in case of failure, otherwise the strong reference count.
 ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
+    if (binder->isRpcBinder()) return -1;
+
     binder_node_info_for_ref info;
     memset(&info, 0, sizeof(binder_node_info_for_ref));
 
-    info.handle = binder->getPrivateAccessorForHandle().handle();
+    info.handle = binder->getPrivateAccessorForId().binderHandle();
 
     status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
 
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
new file mode 100644
index 0000000..5c32320
--- /dev/null
+++ b/libs/binder/RpcAddress.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/RpcAddress.h>
+
+#include <binder/Parcel.h>
+
+#include "Debug.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+namespace android {
+
+RpcAddress RpcAddress::zero() {
+    return RpcAddress();
+}
+
+bool RpcAddress::isZero() const {
+    RpcWireAddress ZERO{0};
+    return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0;
+}
+
+static void ReadRandomBytes(uint8_t* buf, size_t len) {
+    int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+    if (fd == -1) {
+        ALOGE("%s: cannot read /dev/urandom", __func__);
+        return;
+    }
+
+    size_t n;
+    while ((n = TEMP_FAILURE_RETRY(read(fd, buf, len))) > 0) {
+        len -= n;
+        buf += n;
+    }
+    if (len > 0) {
+        ALOGW("%s: there are %d bytes skipped", __func__, (int)len);
+    }
+    close(fd);
+}
+
+RpcAddress RpcAddress::unique() {
+    RpcAddress ret;
+    ReadRandomBytes((uint8_t*)ret.mRawAddr.get(), sizeof(RpcWireAddress));
+    LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str());
+    return ret;
+}
+
+RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) {
+    RpcAddress addr;
+    memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress));
+    return addr;
+}
+
+const RpcWireAddress& RpcAddress::viewRawEmbedded() const {
+    return *mRawAddr.get();
+}
+
+bool RpcAddress::operator<(const RpcAddress& rhs) const {
+    return std::memcmp(mRawAddr.get(), rhs.mRawAddr.get(), sizeof(RpcWireAddress)) < 0;
+}
+
+std::string RpcAddress::toString() const {
+    return hexString(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+status_t RpcAddress::writeToParcel(Parcel* parcel) const {
+    return parcel->write(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+status_t RpcAddress::readFromParcel(const Parcel& parcel) {
+    return parcel.read(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+RpcAddress::~RpcAddress() {}
+RpcAddress::RpcAddress() : mRawAddr(std::make_shared<RpcWireAddress>()) {}
+
+} // namespace android
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
new file mode 100644
index 0000000..83a1618
--- /dev/null
+++ b/libs/binder/RpcConnection.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcConnection"
+
+#include <binder/RpcConnection.h>
+
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#if defined(__GLIBC__)
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcConnection::RpcConnection() {
+    LOG_RPC_DETAIL("RpcConnection created %p", this);
+
+    mState = std::make_unique<RpcState>();
+}
+RpcConnection::~RpcConnection() {
+    LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
+}
+
+sp<RpcConnection> RpcConnection::make() {
+    return new RpcConnection;
+}
+
+bool RpcConnection::setupUnixDomainServer(const char* path) {
+    LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
+
+    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
+        return false;
+    }
+
+    struct sockaddr_un addr = {
+            .sun_family = AF_UNIX,
+    };
+
+    unsigned int pathLen = strlen(path) + 1;
+    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
+    memcpy(addr.sun_path, path, pathLen);
+
+    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
+        ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+        ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
+        return false;
+    }
+
+    mServer = std::move(serverFd);
+    return true;
+}
+
+bool RpcConnection::addUnixDomainClient(const char* path) {
+    LOG_RPC_DETAIL("Connecting on path: %s", path);
+
+    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
+        return false;
+    }
+
+    struct sockaddr_un addr = {
+            .sun_family = AF_UNIX,
+    };
+
+    unsigned int pathLen = strlen(path) + 1;
+    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
+    memcpy(addr.sun_path, path, pathLen);
+
+    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
+        ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
+        return false;
+    }
+
+    LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
+
+    addClient(std::move(serverFd));
+    return true;
+}
+
+sp<IBinder> RpcConnection::getRootObject() {
+    ExclusiveSocket socket(this, SocketUse::CLIENT);
+    return state()->getRootObject(socket.fd(), this);
+}
+
+status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+                                 Parcel* reply, uint32_t flags) {
+    ExclusiveSocket socket(this,
+                           (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
+                                                          : SocketUse::CLIENT);
+    return state()->transact(socket.fd(), address, code, data, this, reply, flags);
+}
+
+status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
+    ExclusiveSocket socket(this, SocketUse::CLIENT_REFCOUNT);
+    return state()->sendDecStrong(socket.fd(), address);
+}
+
+void RpcConnection::join() {
+    // establish a connection
+    {
+        struct sockaddr_un clientSa;
+        socklen_t clientSaLen = sizeof(clientSa);
+
+        unique_fd clientFd(TEMP_FAILURE_RETRY(
+                accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
+        if (clientFd < 0) {
+            // If this log becomes confusing, should save more state from setupUnixDomainServer
+            // in order to output here.
+            ALOGE("Could not accept4 socket: %s", strerror(errno));
+            return;
+        }
+
+        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+        addServer(std::move(clientFd));
+    }
+
+    // We may not use the connection we just established (two threads might
+    // establish connections for each other), but for now, just use one
+    // server/socket connection.
+    ExclusiveSocket socket(this, SocketUse::SERVER);
+
+    while (true) {
+        status_t error = state()->getAndExecuteCommand(socket.fd(), this);
+
+        if (error != OK) {
+            ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
+            return;
+        }
+    }
+}
+
+void RpcConnection::setForServer(const wp<RpcServer>& server) {
+    mForServer = server;
+}
+
+wp<RpcServer> RpcConnection::server() {
+    return mForServer;
+}
+
+void RpcConnection::addClient(base::unique_fd&& fd) {
+    std::lock_guard<std::mutex> _l(mSocketMutex);
+    sp<ConnectionSocket> connection = new ConnectionSocket();
+    connection->fd = std::move(fd);
+    mClients.push_back(connection);
+}
+
+void RpcConnection::addServer(base::unique_fd&& fd) {
+    std::lock_guard<std::mutex> _l(mSocketMutex);
+    sp<ConnectionSocket> connection = new ConnectionSocket();
+    connection->fd = std::move(fd);
+    mServers.push_back(connection);
+}
+
+RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
+      : mConnection(connection) {
+    pid_t tid = gettid();
+    std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
+
+    mConnection->mWaitingThreads++;
+    while (true) {
+        sp<ConnectionSocket> exclusive;
+        sp<ConnectionSocket> available;
+
+        // CHECK FOR DEDICATED CLIENT SOCKET
+        //
+        // A server/looper should always use a dedicated connection.
+        if (use != SocketUse::SERVER) {
+            findSocket(tid, &exclusive, &available, mConnection->mClients,
+                       mConnection->mClientsOffset);
+
+            // WARNING: this assumes a server cannot request its client to send
+            // a transaction, as mServers is excluded below.
+            //
+            // Imagine we have more than one thread in play, and a single thread
+            // sends a synchronous, then an asynchronous command. Imagine the
+            // asynchronous command is sent on the first client socket. Then, if
+            // we naively send a synchronous command to that same socket, the
+            // thread on the far side might be busy processing the asynchronous
+            // command. So, we move to considering the second available thread
+            // for subsequent calls.
+            if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+                mConnection->mClientsOffset =
+                        (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
+            }
+        }
+
+        // USE SERVING SOCKET (to start serving or for nested transaction)
+        //
+        // asynchronous calls cannot be nested
+        if (use != SocketUse::CLIENT_ASYNC) {
+            // servers should start serving on an available thread only
+            // otherwise, this should only be a nested call
+            bool useAvailable = use == SocketUse::SERVER;
+
+            findSocket(tid, &exclusive, (useAvailable ? &available : nullptr),
+                       mConnection->mServers, 0 /* index hint */);
+        }
+
+        // if our thread is already using a connection, prioritize using that
+        if (exclusive != nullptr) {
+            mSocket = exclusive;
+            mReentrant = true;
+            break;
+        } else if (available != nullptr) {
+            mSocket = available;
+            mSocket->exclusiveTid = tid;
+            break;
+        }
+
+        LOG_ALWAYS_FATAL_IF(use == SocketUse::SERVER, "Must create connection to join one.");
+
+        // in regular binder, this would usually be a deadlock :)
+        LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
+                            "Not a client of any connection. You must create a connection to an "
+                            "RPC server to make any non-nested (e.g. oneway or on another thread) "
+                            "calls.");
+
+        LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
+                       mConnection->mClients.size(), mConnection->mServers.size());
+        mConnection->mSocketCv.wait(_l);
+    }
+    mConnection->mWaitingThreads--;
+}
+
+void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
+                                                sp<ConnectionSocket>* available,
+                                                std::vector<sp<ConnectionSocket>>& sockets,
+                                                size_t socketsIndexHint) {
+    LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+                        "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+    if (*exclusive != nullptr) return; // consistent with break below
+
+    for (size_t i = 0; i < sockets.size(); i++) {
+        sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+        // take first available connection (intuition = caching)
+        if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+            *available = socket;
+            continue;
+        }
+
+        // though, prefer to take connection which is already inuse by this thread
+        // (nested transactions)
+        if (exclusive && socket->exclusiveTid == tid) {
+            *exclusive = socket;
+            break; // consistent with return above
+        }
+    }
+}
+
+RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
+    // reentrant use of a connection means something less deep in the call stack
+    // is using this fd, and it retains the right to it. So, we don't give up
+    // exclusive ownership, and no thread is freed.
+    if (!mReentrant) {
+        std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
+        mSocket->exclusiveTid = std::nullopt;
+        if (mConnection->mWaitingThreads > 0) {
+            _l.unlock();
+            mConnection->mSocketCv.notify_one();
+        }
+    }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
new file mode 100644
index 0000000..df07916
--- /dev/null
+++ b/libs/binder/RpcServer.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcServer"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <vector>
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <log/log.h>
+#include "RpcState.h"
+
+#include "RpcWireFormat.h"
+
+namespace android {
+
+using base::unique_fd;
+
+RpcServer::RpcServer() {}
+RpcServer::~RpcServer() {}
+
+sp<RpcServer> RpcServer::make() {
+    return new RpcServer;
+}
+
+void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
+    mAgreedExperimental = true;
+}
+
+sp<RpcConnection> RpcServer::addClientConnection() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+    auto connection = RpcConnection::make();
+    connection->setForServer(this);
+    mConnections.push_back(connection);
+    return connection;
+}
+
+void RpcServer::setRootObject(const sp<IBinder>& binder) {
+    LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
+    mRootObject = binder;
+}
+
+sp<IBinder> RpcServer::getRootObject() {
+    return mRootObject;
+}
+
+} // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
new file mode 100644
index 0000000..64e842e
--- /dev/null
+++ b/libs/binder/RpcState.cpp
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcState"
+
+#include "RpcState.h"
+
+#include <binder/BpBinder.h>
+#include <binder/RpcServer.h>
+
+#include "Debug.h"
+#include "RpcWireFormat.h"
+
+#include <inttypes.h>
+
+namespace android {
+
+RpcState::RpcState() {}
+RpcState::~RpcState() {}
+
+status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+                                   RpcAddress* outAddress) {
+    bool isRemote = binder->remoteBinder();
+    bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
+
+    if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+        // We need to be able to send instructions over the socket for how to
+        // connect to a different server, and we also need to let the host
+        // process know that this is happening.
+        ALOGE("Canot send binder from unrelated binder RPC connection.");
+        return INVALID_OPERATION;
+    }
+
+    if (isRemote && !isRpc) {
+        // Without additional work, this would have the effect of using this
+        // process to proxy calls from the socket over to the other process, and
+        // it would make those calls look like they come from us (not over the
+        // sockets). In order to make this work transparently like binder, we
+        // would instead need to send instructions over the socket for how to
+        // connect to the host process, and we also need to let the host process
+        // know this was happening.
+        ALOGE("Cannot send binder proxy %p over sockets", binder.get());
+        return INVALID_OPERATION;
+    }
+
+    std::lock_guard<std::mutex> _l(mNodeMutex);
+
+    // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map
+    // in RpcState
+    for (auto& [addr, node] : mNodeForAddress) {
+        if (binder == node.binder) {
+            if (isRpc) {
+                const RpcAddress& actualAddr =
+                        binder->remoteBinder()->getPrivateAccessorForId().rpcAddress();
+                // TODO(b/182939933): this is only checking integrity of data structure
+                // a different data structure doesn't need this
+                LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch");
+                LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch");
+            }
+            node.timesSent++;
+            node.sentRef = binder; // might already be set
+            *outAddress = addr;
+            return OK;
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(isRpc, "RPC binder must have known address at this point");
+
+    auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::unique(),
+                                                    BinderNode{
+                                                            .binder = binder,
+                                                            .timesSent = 1,
+                                                            .sentRef = binder,
+                                                    }});
+    // TODO(b/182939933): better organization could avoid needing this log
+    LOG_ALWAYS_FATAL_IF(!inserted);
+
+    *outAddress = it->first;
+    return OK;
+}
+
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
+                                       const RpcAddress& address) {
+    std::unique_lock<std::mutex> _l(mNodeMutex);
+
+    if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
+        sp<IBinder> binder = it->second.binder.promote();
+
+        // implicitly have strong RPC refcount, since we received this binder
+        it->second.timesRecd++;
+
+        _l.unlock();
+
+        // We have timesRecd RPC refcounts, but we only need to hold on to one
+        // when we keep the object. All additional dec strongs are sent
+        // immediately, we wait to send the last one in BpBinder::onLastDecStrong.
+        (void)connection->sendDecStrong(address);
+
+        return binder;
+    }
+
+    auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
+    LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
+
+    // Currently, all binders are assumed to be part of the same connection (no
+    // device global binders in the RPC world).
+    sp<IBinder> binder = BpBinder::create(connection, it->first);
+    it->second.binder = binder;
+    it->second.timesRecd = 1;
+    return binder;
+}
+
+size_t RpcState::countBinders() {
+    std::lock_guard<std::mutex> _l(mNodeMutex);
+    return mNodeForAddress.size();
+}
+
+void RpcState::dump() {
+    std::lock_guard<std::mutex> _l(mNodeMutex);
+    ALOGE("DUMP OF RpcState %p", this);
+    ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size());
+    for (const auto& [address, node] : mNodeForAddress) {
+        sp<IBinder> binder = node.binder.promote();
+
+        const char* desc;
+        if (binder) {
+            if (binder->remoteBinder()) {
+                if (binder->remoteBinder()->isRpcBinder()) {
+                    desc = "(rpc binder proxy)";
+                } else {
+                    desc = "(binder proxy)";
+                }
+            } else {
+                desc = "(local binder)";
+            }
+        } else {
+            desc = "(null)";
+        }
+
+        ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s",
+              node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(),
+              desc);
+    }
+    ALOGE("END DUMP OF RpcState");
+}
+
+void RpcState::terminate() {
+    if (SHOULD_LOG_RPC_DETAIL) {
+        ALOGE("RpcState::terminate()");
+        dump();
+    }
+
+    // if the destructor of a binder object makes another RPC call, then calling
+    // decStrong could deadlock. So, we must hold onto these binders until
+    // mNodeMutex is no longer taken.
+    std::vector<sp<IBinder>> tempHoldBinder;
+
+    {
+        std::lock_guard<std::mutex> _l(mNodeMutex);
+        mTerminated = true;
+        for (auto& [address, node] : mNodeForAddress) {
+            sp<IBinder> binder = node.binder.promote();
+            LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get());
+
+            if (node.sentRef != nullptr) {
+                tempHoldBinder.push_back(node.sentRef);
+            }
+        }
+
+        mNodeForAddress.clear();
+    }
+}
+
+bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
+    LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+
+    if (size > std::numeric_limits<ssize_t>::max()) {
+        ALOGE("Cannot send %s at size %zu (too big)", what, size);
+        terminate();
+        return false;
+    }
+
+    ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, 0));
+
+    if (sent < 0 || sent != static_cast<ssize_t>(size)) {
+        ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size,
+              fd.get(), strerror(errno));
+
+        terminate();
+        return false;
+    }
+
+    return true;
+}
+
+bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size) {
+    if (size > std::numeric_limits<ssize_t>::max()) {
+        ALOGE("Cannot rec %s at size %zu (too big)", what, size);
+        terminate();
+        return false;
+    }
+
+    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL));
+
+    if (recd < 0 || recd != static_cast<ssize_t>(size)) {
+        terminate();
+
+        if (recd == 0 && errno == 0) {
+            LOG_RPC_DETAIL("No more data when trying to read %s on fd %d", what, fd.get());
+            return false;
+        }
+
+        ALOGE("Failed to read %s (received %zd of %zu bytes) on fd %d, error: %s", what, recd, size,
+              fd.get(), strerror(errno));
+        return false;
+    } else {
+        LOG_RPC_DETAIL("Received %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+    }
+
+    return true;
+}
+
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
+                                    const sp<RpcConnection>& connection) {
+    Parcel data;
+    data.markForRpc(connection);
+    Parcel reply;
+
+    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
+                               connection, &reply, 0);
+    if (status != OK) {
+        ALOGE("Error getting root object: %s", statusToString(status).c_str());
+        return nullptr;
+    }
+
+    return reply.readStrongBinder();
+}
+
+status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
+                            const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+                            uint32_t flags) {
+    uint64_t asyncNumber = 0;
+
+    if (!address.isZero()) {
+        std::lock_guard<std::mutex> _l(mNodeMutex);
+        if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
+        auto it = mNodeForAddress.find(address);
+        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s",
+                            address.toString().c_str());
+
+        if (flags & IBinder::FLAG_ONEWAY) {
+            asyncNumber = it->second.asyncNumber++;
+        }
+    }
+
+    if (!data.isForRpc()) {
+        ALOGE("Refusing to send RPC with parcel not crafted for RPC");
+        return BAD_TYPE;
+    }
+
+    if (data.objectsCount() != 0) {
+        ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data);
+        return BAD_TYPE;
+    }
+
+    RpcWireTransaction transaction{
+            .address = address.viewRawEmbedded(),
+            .code = code,
+            .flags = flags,
+            .asyncNumber = asyncNumber,
+    };
+
+    std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+    memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
+    memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
+
+    if (transactionData.size() > std::numeric_limits<uint32_t>::max()) {
+        ALOGE("Transaction size too big %zu", transactionData.size());
+        return BAD_VALUE;
+    }
+
+    RpcWireHeader command{
+            .command = RPC_COMMAND_TRANSACT,
+            .bodySize = static_cast<uint32_t>(transactionData.size()),
+    };
+
+    if (!rpcSend(fd, "transact header", &command, sizeof(command))) {
+        return DEAD_OBJECT;
+    }
+    if (!rpcSend(fd, "command body", transactionData.data(), transactionData.size())) {
+        return DEAD_OBJECT;
+    }
+
+    if (flags & IBinder::FLAG_ONEWAY) {
+        return OK; // do not wait for result
+    }
+
+    LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
+
+    return waitForReply(fd, connection, reply);
+}
+
+static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize,
+                         const binder_size_t* objects, size_t objectsCount) {
+    (void)p;
+    delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
+    (void)dataSize;
+    LOG_ALWAYS_FATAL_IF(objects != nullptr);
+    LOG_ALWAYS_FATAL_IF(objectsCount, 0);
+}
+
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+                                Parcel* reply) {
+    RpcWireHeader command;
+    while (true) {
+        if (!rpcRec(fd, "command header", &command, sizeof(command))) {
+            return DEAD_OBJECT;
+        }
+
+        if (command.command == RPC_COMMAND_REPLY) break;
+
+        status_t status = processServerCommand(fd, connection, command);
+        if (status != OK) return status;
+    }
+
+    uint8_t* data = new uint8_t[command.bodySize];
+
+    if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+        return DEAD_OBJECT;
+    }
+
+    if (command.bodySize < sizeof(RpcWireReply)) {
+        ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!",
+              sizeof(RpcWireReply), command.bodySize);
+        terminate();
+        return BAD_VALUE;
+    }
+    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+    if (rpcReply->status != OK) return rpcReply->status;
+
+    reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
+                               nullptr, 0, cleanup_data);
+
+    reply->markForRpc(connection);
+
+    return OK;
+}
+
+status_t RpcState::sendDecStrong(const base::unique_fd& fd, const RpcAddress& addr) {
+    {
+        std::lock_guard<std::mutex> _l(mNodeMutex);
+        if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
+        auto it = mNodeForAddress.find(addr);
+        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s",
+                            addr.toString().c_str());
+        LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s",
+                            addr.toString().c_str());
+
+        it->second.timesRecd--;
+        if (it->second.timesRecd == 0 && it->second.timesSent == 0) {
+            mNodeForAddress.erase(it);
+        }
+    }
+
+    RpcWireHeader cmd = {
+            .command = RPC_COMMAND_DEC_STRONG,
+            .bodySize = sizeof(RpcWireAddress),
+    };
+    if (!rpcSend(fd, "dec ref header", &cmd, sizeof(cmd))) return DEAD_OBJECT;
+    if (!rpcSend(fd, "dec ref body", &addr.viewRawEmbedded(), sizeof(RpcWireAddress)))
+        return DEAD_OBJECT;
+    return OK;
+}
+
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
+                                        const sp<RpcConnection>& connection) {
+    LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
+
+    RpcWireHeader command;
+    if (!rpcRec(fd, "command header", &command, sizeof(command))) {
+        return DEAD_OBJECT;
+    }
+
+    return processServerCommand(fd, connection, command);
+}
+
+status_t RpcState::processServerCommand(const base::unique_fd& fd,
+                                        const sp<RpcConnection>& connection,
+                                        const RpcWireHeader& command) {
+    switch (command.command) {
+        case RPC_COMMAND_TRANSACT:
+            return processTransact(fd, connection, command);
+        case RPC_COMMAND_DEC_STRONG:
+            return processDecStrong(fd, command);
+    }
+
+    // We should always know the version of the opposing side, and since the
+    // RPC-binder-level wire protocol is not self synchronizing, we have no way
+    // to understand where the current command ends and the next one begins. We
+    // also can't consider it a fatal error because this would allow any client
+    // to kill us, so ending the connection for misbehaving client.
+    ALOGE("Unknown RPC command %d - terminating connection", command.command);
+    terminate();
+    return DEAD_OBJECT;
+}
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+                                   const RpcWireHeader& command) {
+    LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
+
+    std::vector<uint8_t> transactionData(command.bodySize);
+    if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
+        return DEAD_OBJECT;
+    }
+
+    return processTransactInternal(fd, connection, std::move(transactionData));
+}
+
+status_t RpcState::processTransactInternal(const base::unique_fd& fd,
+                                           const sp<RpcConnection>& connection,
+                                           std::vector<uint8_t>&& transactionData) {
+    if (transactionData.size() < sizeof(RpcWireTransaction)) {
+        ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
+              sizeof(RpcWireTransaction), transactionData.size());
+        terminate();
+        return BAD_VALUE;
+    }
+    RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data());
+
+    // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress,
+    // maybe add an RpcAddress 'view' if the type remains 'heavy'
+    auto addr = RpcAddress::fromRawEmbedded(&transaction->address);
+
+    status_t replyStatus = OK;
+    sp<IBinder> target;
+    if (!addr.isZero()) {
+        std::lock_guard<std::mutex> _l(mNodeMutex);
+
+        auto it = mNodeForAddress.find(addr);
+        if (it == mNodeForAddress.end()) {
+            ALOGE("Unknown binder address %s.", addr.toString().c_str());
+            dump();
+            replyStatus = BAD_VALUE;
+        } else {
+            target = it->second.binder.promote();
+            if (target == nullptr) {
+                // This can happen if the binder is remote in this process, and
+                // another thread has called the last decStrong on this binder.
+                // However, for local binders, it indicates a misbehaving client
+                // (any binder which is being transacted on should be holding a
+                // strong ref count), so in either case, terminating the
+                // connection.
+                ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
+                      addr.toString().c_str());
+                terminate();
+                replyStatus = BAD_VALUE;
+            } else if (target->localBinder() == nullptr) {
+                ALOGE("Transactions can only go to local binders, not address %s. Terminating!",
+                      addr.toString().c_str());
+                terminate();
+                replyStatus = BAD_VALUE;
+            } else if (transaction->flags & IBinder::FLAG_ONEWAY) {
+                if (transaction->asyncNumber != it->second.asyncNumber) {
+                    // we need to process some other asynchronous transaction
+                    // first
+                    // TODO(b/183140903): limit enqueues/detect overfill for bad client
+                    // TODO(b/183140903): detect when an object is deleted when it still has
+                    //        pending async transactions
+                    it->second.asyncTodo.push(BinderNode::AsyncTodo{
+                            .data = std::move(transactionData),
+                            .asyncNumber = transaction->asyncNumber,
+                    });
+                    LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s", transaction->asyncNumber,
+                                   addr.toString().c_str());
+                    return OK;
+                }
+            }
+        }
+    }
+
+    Parcel data;
+    data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data));
+    data.markForRpc(connection);
+
+    Parcel reply;
+    reply.markForRpc(connection);
+
+    if (replyStatus == OK) {
+        if (target) {
+            replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
+        } else {
+            LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+            // special case for 'zero' address (special server commands)
+            switch (transaction->code) {
+                case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+                    sp<IBinder> root;
+                    sp<RpcServer> server = connection->server().promote();
+                    if (server) {
+                        root = server->getRootObject();
+                    } else {
+                        ALOGE("Root object requested, but no server attached.");
+                    }
+
+                    replyStatus = reply.writeStrongBinder(root);
+                    break;
+                }
+                default: {
+                    replyStatus = UNKNOWN_TRANSACTION;
+                }
+            }
+        }
+    }
+
+    if (transaction->flags & IBinder::FLAG_ONEWAY) {
+        if (replyStatus != OK) {
+            ALOGW("Oneway call failed with error: %d", replyStatus);
+        }
+
+        LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber,
+                       addr.toString().c_str());
+
+        // Check to see if there is another asynchronous transaction to process.
+        // This behavior differs from binder behavior, since in the binder
+        // driver, asynchronous transactions will be processed after existing
+        // pending binder transactions on the queue. The downside of this is
+        // that asynchronous transactions can be drowned out by synchronous
+        // transactions. However, we have no easy way to queue these
+        // transactions after the synchronous transactions we may want to read
+        // from the wire. So, in socket binder here, we have the opposite
+        // downside: asynchronous transactions may drown out synchronous
+        // transactions.
+        {
+            std::unique_lock<std::mutex> _l(mNodeMutex);
+            auto it = mNodeForAddress.find(addr);
+            // last refcount dropped after this transaction happened
+            if (it == mNodeForAddress.end()) return OK;
+
+            // note - only updated now, instead of later, so that other threads
+            // will queue any later transactions
+
+            // TODO(b/183140903): support > 2**64 async transactions
+            //     (we can do this by allowing asyncNumber to wrap, since we
+            //     don't expect more than 2**64 simultaneous transactions)
+            it->second.asyncNumber++;
+
+            if (it->second.asyncTodo.size() == 0) return OK;
+            if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+                LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s",
+                               it->second.asyncNumber, addr.toString().c_str());
+
+                // justification for const_cast (consider avoiding priority_queue):
+                // - AsyncTodo operator< doesn't depend on 'data' object
+                // - gotta go fast
+                std::vector<uint8_t> data = std::move(
+                        const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
+                it->second.asyncTodo.pop();
+                _l.unlock();
+                return processTransactInternal(fd, connection, std::move(data));
+            }
+        }
+        return OK;
+    }
+
+    RpcWireReply rpcReply{
+            .status = replyStatus,
+    };
+
+    std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+    memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
+    memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
+
+    if (replyData.size() > std::numeric_limits<uint32_t>::max()) {
+        ALOGE("Reply size too big %zu", transactionData.size());
+        terminate();
+        return BAD_VALUE;
+    }
+
+    RpcWireHeader cmdReply{
+            .command = RPC_COMMAND_REPLY,
+            .bodySize = static_cast<uint32_t>(replyData.size()),
+    };
+
+    if (!rpcSend(fd, "reply header", &cmdReply, sizeof(RpcWireHeader))) {
+        return DEAD_OBJECT;
+    }
+    if (!rpcSend(fd, "reply body", replyData.data(), replyData.size())) {
+        return DEAD_OBJECT;
+    }
+    return OK;
+}
+
+status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
+    LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
+
+    std::vector<uint8_t> commandData(command.bodySize);
+    if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
+        return DEAD_OBJECT;
+    }
+
+    if (command.bodySize < sizeof(RpcWireAddress)) {
+        ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
+              sizeof(RpcWireAddress), command.bodySize);
+        terminate();
+        return BAD_VALUE;
+    }
+    RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data());
+
+    // TODO(b/182939933): heap allocation just for lookup
+    auto addr = RpcAddress::fromRawEmbedded(address);
+    std::unique_lock<std::mutex> _l(mNodeMutex);
+    auto it = mNodeForAddress.find(addr);
+    if (it == mNodeForAddress.end()) {
+        ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
+        dump();
+        return OK;
+    }
+
+    sp<IBinder> target = it->second.binder.promote();
+    if (target == nullptr) {
+        ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!",
+              addr.toString().c_str());
+        terminate();
+        return BAD_VALUE;
+    }
+
+    if (it->second.timesSent == 0) {
+        ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str());
+        return OK;
+    }
+
+    LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s",
+                        addr.toString().c_str());
+
+    sp<IBinder> tempHold;
+
+    it->second.timesSent--;
+    if (it->second.timesSent == 0) {
+        tempHold = it->second.sentRef;
+        it->second.sentRef = nullptr;
+
+        if (it->second.timesRecd == 0) {
+            mNodeForAddress.erase(it);
+        }
+    }
+
+    _l.unlock();
+    tempHold = nullptr; // destructor may make binder calls on this connection
+
+    return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
new file mode 100644
index 0000000..f4f5151
--- /dev/null
+++ b/libs/binder/RpcState.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcConnection.h>
+
+#include <map>
+#include <queue>
+
+namespace android {
+
+struct RpcWireHeader;
+
+/**
+ * Log a lot more information about RPC calls, when debugging issues. Usually,
+ * you would want to enable this in only one process. If repeated issues require
+ * a specific subset of logs to debug, this could be broken up like
+ * IPCThreadState's.
+ */
+#define SHOULD_LOG_RPC_DETAIL false
+
+#if SHOULD_LOG_RPC_DETAIL
+#define LOG_RPC_DETAIL(...) ALOGI(__VA_ARGS__)
+#else
+#define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
+#endif
+
+/**
+ * Abstracts away management of ref counts and the wire format from
+ * RpcConnection
+ */
+class RpcState {
+public:
+    RpcState();
+    ~RpcState();
+
+    sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+
+    [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
+                                    uint32_t code, const Parcel& data,
+                                    const sp<RpcConnection>& connection, Parcel* reply,
+                                    uint32_t flags);
+    [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
+    [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
+                                                const sp<RpcConnection>& connection);
+
+    /**
+     * Called by Parcel for outgoing binders. This implies one refcount of
+     * ownership to the outgoing binder.
+     */
+    [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
+                                           const sp<IBinder>& binder, RpcAddress* outAddress);
+
+    /**
+     * Called by Parcel for incoming binders. This either returns the refcount
+     * to the process, if this process already has one, or it takes ownership of
+     * that refcount
+     */
+    sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+
+    size_t countBinders();
+    void dump();
+
+private:
+    /**
+     * Called when reading or writing data to a connection fails to clean up
+     * data associated with the connection in order to cleanup binders.
+     * Specifically, we have a strong dependency cycle, since BpBinder is
+     * OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
+     *
+     *     BpBinder -> RpcConnection -> RpcState
+     *      ^-----------------------------/
+     *
+     * In the success case, eventually all refcounts should be propagated over
+     * the connection, though this could also be called to eagerly cleanup
+     * the connection.
+     *
+     * WARNING: RpcState is responsible for calling this when the connection is
+     * no longer recoverable.
+     */
+    void terminate();
+
+    [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
+                               size_t size);
+    [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
+
+    [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
+                                        const sp<RpcConnection>& connection, Parcel* reply);
+    [[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
+                                                const sp<RpcConnection>& connection,
+                                                const RpcWireHeader& command);
+    [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
+                                           const sp<RpcConnection>& connection,
+                                           const RpcWireHeader& command);
+    [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
+                                                   const sp<RpcConnection>& connection,
+                                                   std::vector<uint8_t>&& transactionData);
+    [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
+                                            const RpcWireHeader& command);
+
+    struct BinderNode {
+        // Two cases:
+        // A - local binder we are serving
+        // B - remote binder, we are sending transactions to
+        wp<IBinder> binder;
+
+        // if timesSent > 0, this will be equal to binder.promote()
+        sp<IBinder> sentRef;
+
+        // Number of times we've sent this binder out of process, which
+        // translates to an implicit strong count. A client must send RPC binder
+        // socket's dec ref for each time it is sent out of process in order to
+        // deallocate it. Note, a proxy binder we are holding onto might be
+        // sent (this is important when the only remaining refcount of this
+        // binder is the one associated with a transaction sending it back to
+        // its server)
+        size_t timesSent = 0;
+
+        // Number of times we've received this binder, each time corresponds to
+        // a reference we hold over the wire (not a local incStrong/decStrong)
+        size_t timesRecd = 0;
+
+        // transaction ID, for async transactions
+        uint64_t asyncNumber = 0;
+
+        //
+        // CASE A - local binder we are serving
+        //
+
+        // async transaction queue, _only_ for local binder
+        struct AsyncTodo {
+            std::vector<uint8_t> data; // most convenient format, to move it here
+            uint64_t asyncNumber = 0;
+
+            bool operator<(const AsyncTodo& o) const {
+                return asyncNumber > /* !!! */ o.asyncNumber;
+            }
+        };
+        std::priority_queue<AsyncTodo> asyncTodo;
+
+        //
+        // CASE B - remote binder, we are sending transactions to
+        //
+
+        // (no additional data specific to remote binders)
+    };
+
+    std::mutex mNodeMutex;
+    bool mTerminated = false;
+    // binders known by both sides of a connection
+    std::map<RpcAddress, BinderNode> mNodeForAddress;
+};
+
+} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
new file mode 100644
index 0000000..60ec6c9
--- /dev/null
+++ b/libs/binder/RpcWireFormat.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace android {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wpadded"
+
+enum : uint32_t {
+    /**
+     * follows is RpcWireTransaction, if flags != oneway, reply w/ RPC_COMMAND_REPLY expected
+     */
+    RPC_COMMAND_TRANSACT = 0,
+    /**
+     * follows is RpcWireReply
+     */
+    RPC_COMMAND_REPLY,
+    /**
+     * follows is RpcWireAddress
+     *
+     * note - this in the protocol directly instead of as a 'special
+     * transaction' in order to keep it as lightweight as possible (we don't
+     * want to create a 'Parcel' object for every decref)
+     */
+    RPC_COMMAND_DEC_STRONG,
+};
+
+/**
+ * These commands are used when the address in an RpcWireTransaction is zero'd
+ * out (no address). This allows the transact/reply flow to be used for
+ * additional server commands, without making the protocol for
+ * transactions/replies more complicated.
+ */
+enum : uint32_t {
+    RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+};
+
+// serialization is like:
+// |RpcWireHeader|struct desginated by 'command'| (over and over again)
+
+struct RpcWireHeader {
+    uint32_t command; // RPC_COMMAND_*
+    uint32_t bodySize;
+
+    uint32_t reserved[2];
+};
+
+struct RpcWireAddress {
+    uint8_t address[32];
+};
+
+struct RpcWireTransaction {
+    RpcWireAddress address;
+    uint32_t code;
+    uint32_t flags;
+
+    uint64_t asyncNumber;
+
+    uint32_t reserved[4];
+
+    uint8_t data[0];
+};
+
+struct RpcWireReply {
+    int32_t status; // transact return
+    uint8_t data[0];
+};
+
+#pragma clang diagnostic pop
+
+} // namespace android
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index b56e09f..c3f1ba7 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -38,6 +38,18 @@
     };
 }
 
+void Stability::forceDowngradeCompilationUnit(const sp<IBinder>& binder) {
+    // Downgrading a remote binder would require also copying the version from
+    // the binder sent here. In practice though, we don't need to downgrade the
+    // stability of a remote binder, since this would as an effect only restrict
+    // what we can do to it.
+    LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
+
+    auto stability = Category::currentFromLevel(getLocalLevel());
+    status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
+    LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
+}
+
 std::string Stability::Category::debugString() {
     return levelString(level) + " wire protocol version "
         + std::to_string(version);
@@ -45,13 +57,13 @@
 
 void Stability::markCompilationUnit(IBinder* binder) {
     auto stability = Category::currentFromLevel(getLocalLevel());
-    status_t result = setRepr(binder, stability.repr(), true /*log*/);
+    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
 void Stability::markVintf(IBinder* binder) {
     auto stability = Category::currentFromLevel(Level::VINTF);
-    status_t result = setRepr(binder, stability.repr(), true /*log*/);
+    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
@@ -62,7 +74,7 @@
 
 void Stability::markVndk(IBinder* binder) {
     auto stability = Category::currentFromLevel(Level::VENDOR);
-    status_t result = setRepr(binder, stability.repr(), true /*log*/);
+    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
@@ -72,7 +84,7 @@
 
 void Stability::tryMarkCompilationUnit(IBinder* binder) {
     auto stability = Category::currentFromLevel(getLocalLevel());
-    (void) setRepr(binder, stability.repr(), false /*log*/);
+    (void) setRepr(binder, stability.repr(), REPR_NONE);
 }
 
 Stability::Level Stability::getLocalLevel() {
@@ -88,7 +100,10 @@
 #endif
 }
 
-status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) {
+status_t Stability::setRepr(IBinder* binder, int32_t representation, uint32_t flags) {
+    bool log = flags & REPR_LOG;
+    bool allowDowngrade = flags & REPR_ALLOW_DOWNGRADE;
+
     auto current = getCategory(binder);
     auto setting = Category::fromRepr(representation);
 
@@ -123,7 +138,11 @@
         return BAD_TYPE;
     }
 
-    if (current.repr() != 0 && current != setting) {
+    if (current == setting) return OK;
+
+    bool hasAlreadyBeenSet = current.repr() != 0;
+    bool isAllowedDowngrade = allowDowngrade && check(current, setting.level);
+    if (hasAlreadyBeenSet && !isAllowedDowngrade) {
         if (log) {
             ALOGE("Interface being set with %s but it is already marked as %s",
                   setting.debugString().c_str(),
@@ -132,7 +151,11 @@
         return BAD_TYPE;
     }
 
-    if (current == setting) return OK;
+    if (isAllowedDowngrade) {
+        ALOGI("Interface set with %s downgraded to %s stability",
+              current.debugString().c_str(),
+              setting.debugString().c_str());
+    }
 
     BBinder* local = binder->localBinder();
     if (local != nullptr) {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1fbaa13..b58d919 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -25,6 +25,9 @@
       "name": "binderLibTest"
     },
     {
+      "name": "binderRpcTest"
+    },
+    {
       "name": "binderStabilityTest"
     },
     {
@@ -40,6 +43,9 @@
       "name": "aidl_integration_test"
     },
     {
+      "name": "memunreachable_binder_test"
+    },
+    {
       "name": "libbinderthreadstateutils_test"
     },
     {
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 0386d66..eac1bb2 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -142,7 +142,8 @@
         OP_FINE_LOCATION_SOURCE = 108,
         OP_COARSE_LOCATION_SOURCE = 109,
         OP_MANAGE_MEDIA = 110,
-        _NUM_OP = 111
+        OP_BLUETOOTH_CONNECT = 111,
+        _NUM_OP = 112
     };
 
     AppOpsManager();
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 22300ac..8ab7893 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,15 +17,19 @@
 #pragma once
 
 #include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
 #include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
 
 #include <unordered_map>
+#include <variant>
 
 // ---------------------------------------------------------------------------
 namespace android {
 
+class RpcConnection;
+class RpcState;
 namespace internal {
 class Stability;
 }
@@ -37,6 +41,14 @@
 {
 public:
     static BpBinder*    create(int32_t handle);
+    static BpBinder* create(const sp<RpcConnection>& connection, const RpcAddress& address);
+
+    /**
+     * Return value:
+     * true - this is associated with a socket RpcConnection
+     * false - (usual) binder over e.g. /dev/binder
+     */
+    bool isRpcBinder() const;
 
     virtual const String16&    getInterfaceDescriptor() const;
     virtual bool        isBinderAlive() const;
@@ -108,33 +120,56 @@
         KeyedVector<const void*, entry_t> mObjects;
     };
 
-    class PrivateAccessorForHandle {
+    class PrivateAccessorForId {
     private:
-        friend BpBinder;
-        friend ::android::Parcel;
-        friend ::android::ProcessState;
-        explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
-        int32_t handle() const { return mBinder->handle(); }
+        friend class BpBinder;
+        friend class ::android::Parcel;
+        friend class ::android::ProcessState;
+        friend class ::android::RpcState;
+        explicit PrivateAccessorForId(const BpBinder* binder) : mBinder(binder) {}
+
+        // valid if !isRpcBinder
+        int32_t binderHandle() const { return mBinder->binderHandle(); }
+
+        // valid if isRpcBinder
+        const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
+        const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+
         const BpBinder* mBinder;
     };
-    const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
-        return PrivateAccessorForHandle(this);
+    const PrivateAccessorForId getPrivateAccessorForId() const {
+        return PrivateAccessorForId(this);
     }
 
 private:
-    friend PrivateAccessorForHandle;
+    friend PrivateAccessorForId;
 
-    int32_t             handle() const;
-                        BpBinder(int32_t handle,int32_t trackedUid);
+    struct BinderHandle {
+        int32_t handle;
+    };
+    struct SocketHandle {
+        sp<RpcConnection> connection;
+        RpcAddress address;
+    };
+    using Handle = std::variant<BinderHandle, SocketHandle>;
+
+    int32_t binderHandle() const;
+    const RpcAddress& rpcAddress() const;
+    const sp<RpcConnection>& rpcConnection() const;
+
+    explicit BpBinder(Handle&& handle);
+    BpBinder(BinderHandle&& handle, int32_t trackedUid);
+    explicit BpBinder(SocketHandle&& handle);
+
     virtual             ~BpBinder();
     virtual void        onFirstRef();
     virtual void        onLastStrongRef(const void* id);
     virtual bool        onIncStrongAttempted(uint32_t flags, const void* id);
 
     friend ::android::internal::Stability;
-            int32_t             mStability;
 
-    const   int32_t             mHandle;
+    int32_t mStability;
+    Handle mHandle;
 
     struct Obituary {
         wp<DeathRecipient> recipient;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 7b298f5..9578372 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,11 +50,14 @@
 class IBinder;
 class IPCThreadState;
 class ProcessState;
+class RpcConnection;
 class String8;
 class TextOutput;
 
 class Parcel {
     friend class IPCThreadState;
+    friend class RpcState;
+
 public:
     class ReadableBlob;
     class WritableBlob;
@@ -92,7 +95,21 @@
     // In order to verify this, heap dumps should be used.
     void                markSensitive() const;
 
-    // Writes the RPC header.
+    // For a 'data' Parcel, this should mark the Parcel as being prepared for a
+    // transaction on this specific binder object. Based on this, the format of
+    // the wire binder protocol may change (data is written differently when it
+    // is for an RPC transaction).
+    void markForBinder(const sp<IBinder>& binder);
+
+    // Whenever possible, markForBinder should be preferred. This method is
+    // called automatically on reply Parcels for RPC transactions.
+    void markForRpc(const sp<RpcConnection>& connection);
+
+    // Whether this Parcel is written for RPC transactions (after calls to
+    // markForBinder or markForRpc).
+    bool isForRpc() const;
+
+    // Writes the IPC/RPC header.
     status_t            writeInterfaceToken(const String16& interface);
     status_t            writeInterfaceToken(const char16_t* str, size_t len);
 
@@ -1106,6 +1123,7 @@
     mutable bool        mObjectsSorted;
 
     mutable bool        mRequestHeaderPresent;
+
     mutable size_t      mWorkSourceRequestHeaderPosition;
 
     mutable bool        mFdsKnown;
@@ -1118,8 +1136,7 @@
 
     release_func        mOwner;
 
-    // TODO(167966510): reserved for binder/version/stability
-    void*               mReserved = reinterpret_cast<void*>(0xAAAAAAAA);
+    sp<RpcConnection> mConnection;
 
     class Blob {
     public:
diff --git a/libs/binder/include/binder/RpcAddress.h b/libs/binder/include/binder/RpcAddress.h
new file mode 100644
index 0000000..5a3f3a6
--- /dev/null
+++ b/libs/binder/include/binder/RpcAddress.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include <utils/Errors.h>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+struct RpcWireAddress;
+
+/**
+ * This class represents an identifier of a binder object.
+ *
+ * The purpose of this class it to hide the ABI of an RpcWireAddress, and
+ * potentially allow us to change the size of it in the future (RpcWireAddress
+ * is PIMPL, essentially - although the type that is used here is not exposed).
+ */
+class RpcAddress {
+public:
+    /**
+     * The zero address is used for special RPC transactions, but it might also
+     * be used in conjunction with readFromParcel.
+     */
+    static RpcAddress zero();
+
+    bool isZero() const;
+
+    /**
+     * Create a new address which is unique
+     */
+    static RpcAddress unique();
+
+    /**
+     * Creates a new address as a copy of an embedded object.
+     */
+    static RpcAddress fromRawEmbedded(const RpcWireAddress* raw);
+    const RpcWireAddress& viewRawEmbedded() const;
+
+    bool operator<(const RpcAddress& rhs) const;
+    std::string toString() const;
+
+    status_t writeToParcel(Parcel* parcel) const;
+    status_t readFromParcel(const Parcel& parcel);
+
+    ~RpcAddress();
+
+private:
+    RpcAddress();
+
+    std::shared_ptr<RpcWireAddress> mRawAddr;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
new file mode 100644
index 0000000..65c5232
--- /dev/null
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <optional>
+#include <vector>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+class RpcServer;
+class RpcState;
+
+/**
+ * This represents a multi-threaded/multi-socket connection between a client
+ * and a server.
+ */
+class RpcConnection final : public virtual RefBase {
+public:
+    static sp<RpcConnection> make();
+
+    /**
+     * This represents a connection for responses, e.g.:
+     *
+     *     process A serves binder a
+     *     process B opens a connection to process A
+     *     process B makes binder b and sends it to A
+     *     A uses this 'back connection' to send things back to B
+     *
+     * This should be called once, and then a call should be made to join per
+     * connection thread.
+     */
+    [[nodiscard]] bool setupUnixDomainServer(const char* path);
+
+    /**
+     * This should be called once per thread, matching 'join' in the remote
+     * process.
+     */
+    [[nodiscard]] bool addUnixDomainClient(const char* path);
+
+    /**
+     * Query the other side of the connection for the root object hosted by that
+     * process's RpcServer (if one exists)
+     */
+    sp<IBinder> getRootObject();
+
+    [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+                                    Parcel* reply, uint32_t flags);
+    [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+    /**
+     * Adds a server thread accepting connections. Must be called after
+     * setup*Server.
+     */
+    void join();
+
+    ~RpcConnection();
+
+    void setForServer(const wp<RpcServer>& server);
+    wp<RpcServer> server();
+
+    // internal only
+    const std::unique_ptr<RpcState>& state() { return mState; }
+
+private:
+    RpcConnection();
+
+    void addServer(base::unique_fd&& fd);
+    void addClient(base::unique_fd&& fd);
+
+    struct ConnectionSocket : public RefBase {
+        base::unique_fd fd;
+
+        // whether this or another thread is currently using this fd to make
+        // or receive transactions.
+        std::optional<pid_t> exclusiveTid;
+    };
+
+    enum class SocketUse {
+        CLIENT,
+        CLIENT_ASYNC,
+        CLIENT_REFCOUNT,
+        SERVER,
+    };
+
+    // RAII object for connection socket
+    class ExclusiveSocket {
+    public:
+        explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
+        ~ExclusiveSocket();
+        const base::unique_fd& fd() { return mSocket->fd; }
+
+    private:
+        static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
+                               sp<ConnectionSocket>* available,
+                               std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
+
+        sp<RpcConnection> mConnection; // avoid deallocation
+        sp<ConnectionSocket> mSocket;
+
+        // whether this is being used for a nested transaction (being on the same
+        // thread guarantees we won't write in the middle of a message, the way
+        // the wire protocol is constructed guarantees this is safe).
+        bool mReentrant = false;
+    };
+
+    // On the other side of a connection, for each of mClients here, there should
+    // be one of mServers on the other side (and vice versa).
+    //
+    // For the simplest connection, a single server with one client, you would
+    // have:
+    //  - the server has a single 'mServers' and a thread listening on this
+    //  - the client has a single 'mClients' and makes calls to this
+    //  - here, when the client makes a call, the server can call back into it
+    //    (nested calls), but outside of this, the client will only ever read
+    //    calls from the server when it makes a call itself.
+    //
+    // For a more complicated case, the client might itself open up a thread to
+    // serve calls to the server at all times (e.g. if it hosts a callback)
+
+    wp<RpcServer> mForServer; // maybe null, for client connections
+
+    std::unique_ptr<RpcState> mState;
+
+    base::unique_fd mServer; // socket we are accepting connections on
+
+    std::mutex mSocketMutex;           // for all below
+    std::condition_variable mSocketCv; // for mWaitingThreads
+    size_t mWaitingThreads = 0;
+    size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
+    std::vector<sp<ConnectionSocket>> mClients;
+    std::vector<sp<ConnectionSocket>> mServers;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
new file mode 100644
index 0000000..a2c2aee
--- /dev/null
+++ b/libs/binder/include/binder/RpcServer.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcConnection.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+/**
+ * This represents a server of an interface, which may be connected to by any
+ * number of clients over sockets.
+ *
+ * This object is not (currently) thread safe. All calls to it are expected to
+ * happen at process startup.
+ */
+class RpcServer final : public virtual RefBase {
+public:
+    static sp<RpcServer> make();
+
+    void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+    /**
+     * Setup a static connection, when the number of clients are known.
+     *
+     * Each call to this function corresponds to a different client, and clients
+     * each have their own threadpools.
+     *
+     * TODO(b/167966510): support dynamic creation of connections/threads
+     */
+    sp<RpcConnection> addClientConnection();
+
+    /**
+     * Allowing a server to explicitly drop clients would be easy to add here,
+     * but it is not currently implemented, since users of this functionality
+     * could not use similar functionality if they are running under real
+     * binder.
+     */
+    // void drop(const sp<RpcConnection>& connection);
+
+    /**
+     * The root object can be retrieved by any client, without any
+     * authentication.
+     */
+    void setRootObject(const sp<IBinder>& binder);
+
+    /**
+     * Root object set with setRootObject
+     */
+    sp<IBinder> getRootObject();
+
+    ~RpcServer();
+
+private:
+    RpcServer();
+
+    bool mAgreedExperimental = false;
+
+    sp<IBinder> mRootObject;
+
+    std::vector<sp<RpcConnection>> mConnections; // per-client
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 12272ba..a09e587 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -49,10 +49,17 @@
 // that it knows how to process. The summary of stability of a binder is
 // represented by a Stability::Category object.
 
-// WARNING: These APIs are only ever expected to be called by auto-generated code.
-//     Instead of calling them, you should set the stability of a .aidl interface
 class Stability final {
 public:
+    // Given a binder interface at a certain stability, there may be some
+    // requirements associated with that higher stability level. For instance, a
+    // VINTF stability binder is required to be in the VINTF manifest. This API
+    // can be called to use that same interface within a partition.
+    static void forceDowngradeCompilationUnit(const sp<IBinder>& binder);
+
+    // WARNING: Below APIs are only ever expected to be called by auto-generated code.
+    //     Instead of calling them, you should set the stability of a .aidl interface
+
     // WARNING: This is only ever expected to be called by auto-generated code. You likely want to
     // change or modify the stability class of the interface you are using.
     // This must be called as soon as the binder in question is constructed. No thread safety
@@ -139,9 +146,14 @@
     // returns the stability according to how this was built
     static Level getLocalLevel();
 
+    enum {
+      REPR_NONE = 0,
+      REPR_LOG = 1,
+      REPR_ALLOW_DOWNGRADE = 2,
+    };
     // applies stability to binder if stability level is known
     __attribute__((warn_unused_result))
-    static status_t setRepr(IBinder* binder, int32_t representation, bool log);
+    static status_t setRepr(IBinder* binder, int32_t representation, uint32_t flags);
 
     // get stability information as encoded on the wire
     static Category getCategory(IBinder* binder);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index a44c261..8941e49 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,11 +36,6 @@
 
 __BEGIN_DECLS
 
-#ifndef __ANDROID_API__
-#error Android builds must be compiled against a specific API. If this is an \
- android platform host build, you must use libbinder_ndk_host_user.
-#endif
-
 typedef uint32_t binder_flags_t;
 enum {
     /**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 05b25e7..b4dc08a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -32,6 +32,16 @@
 
 __BEGIN_DECLS
 
+#ifndef __ANDROID_API__
+#error Android builds must be compiled against a specific API. If this is an \
+ android platform host build, you must use libbinder_ndk_host_user.
+#endif
+
+/**
+ * Low-level status types for use in binder. This is the least preferable way to
+ * return an error for binder services (where binder_exception_t should be used,
+ * particularly EX_SERVICE_SPECIFIC).
+ */
 enum {
     STATUS_OK = 0,
 
@@ -62,6 +72,10 @@
  */
 typedef int32_t binder_status_t;
 
+/**
+ * Top level exceptions types for Android binder errors, mapping to Java
+ * exceptions. Also see Parcel.java.
+ */
 enum {
     EX_NONE = 0,
     EX_SECURITY = -1,
@@ -170,7 +184,8 @@
 /**
  * New status with binder_status_t. This is typically for low level failures when a binder_status_t
  * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning
- * an AStatus instance.
+ * an AStatus instance. This is the least preferable way to return errors.
+ * Prefer exceptions (particularly service-specific errors) when possible.
  *
  * Available since API level 29.
  *
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 5df0012..0668472 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -26,6 +26,9 @@
  * This registers the service with the default service manager under this instance name. This does
  * not take ownership of binder.
  *
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
  * \param binder object to register globally with the service manager.
  * \param instance identifier of the service. This will be used to lookup the service.
  *
@@ -39,6 +42,9 @@
  * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this
  * function is responsible for calling AIBinder_decStrong).
  *
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
  * \param instance identifier of the service used to lookup the service.
  */
 __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
@@ -48,6 +54,9 @@
  * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
  * for calling AIBinder_decStrong).
  *
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
  * \param instance identifier of the service used to lookup the service.
  */
 __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
@@ -78,6 +87,9 @@
  * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
  * for calling AIBinder_decStrong).
  *
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
  * \param instance identifier of the service used to lookup the service.
  *
  * \return service if registered, null if not.
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index d54c1a1..6372449 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -20,9 +20,7 @@
 
 __BEGIN_DECLS
 
-#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
-#error this is only for platform code
-#endif
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
 
 /**
  * Gets whether or not FDs are allowed by this AParcel
@@ -33,6 +31,9 @@
  */
 bool AParcel_getAllowFds(const AParcel*);
 
+#endif
+
+#if !defined(__ANDROID_APEX__)
 /**
  * Data written to the parcel will be zero'd before being deleted or realloced.
  *
@@ -43,5 +44,6 @@
  * \param parcel The parcel to clear associated data from.
  */
 void AParcel_markSensitive(const AParcel* parcel);
+#endif
 
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 8d08275..f1db653 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -117,6 +117,7 @@
     ABinderProcess_setupPolling; # apex
     AIBinder_getCallingSid; # apex
     AIBinder_setRequestingSid; # apex
+    AParcel_markSensitive; # llndk
     AServiceManager_isDeclared; # apex llndk
     AServiceManager_forEachDeclaredInstance; # apex llndk
     AServiceManager_registerLazyService; # llndk
@@ -139,7 +140,6 @@
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;
-    AParcel_markSensitive;
     extern "C++" {
         AIBinder_fromPlatformBinder*;
         AIBinder_toPlatformBinder*;
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 3899d47..321b422 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -56,6 +56,26 @@
     }
 }
 
+/// Interface stability promise
+///
+/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
+/// makes no stability guarantees ([`Local`]). [`Local`] is
+/// currently the default stability.
+pub enum Stability {
+    /// Default stability, visible to other modules in the same compilation
+    /// context (e.g. modules on system.img)
+    Local,
+
+    /// A Vendor Interface Object, which promises to be stable
+    Vintf,
+}
+
+impl Default for Stability {
+    fn default() -> Self {
+        Stability::Local
+    }
+}
+
 /// A local service that can be remotable via Binder.
 ///
 /// An object that implement this interface made be made into a Binder service
@@ -94,6 +114,8 @@
 pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
 /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
 pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
+/// Set to the vendor flag if we are building for the VNDK, 0 otherwise
+pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL;
 
 /// Internal interface of binder local or remote objects for making
 /// transactions.
@@ -602,6 +624,23 @@
             $interface[$descriptor] {
                 native: $native($on_transact),
                 proxy: $proxy {},
+                stability: $crate::Stability::default(),
+            }
+        }
+    };
+
+    {
+        $interface:path[$descriptor:expr] {
+            native: $native:ident($on_transact:path),
+            proxy: $proxy:ident,
+            stability: $stability:expr,
+        }
+    } => {
+        $crate::declare_binder_interface! {
+            $interface[$descriptor] {
+                native: $native($on_transact),
+                proxy: $proxy {},
+                stability: $stability,
             }
         }
     };
@@ -616,12 +655,33 @@
     } => {
         $crate::declare_binder_interface! {
             $interface[$descriptor] {
+                native: $native($on_transact),
+                proxy: $proxy {
+                    $($fname: $fty = $finit),*
+                },
+                stability: $crate::Stability::default(),
+            }
+        }
+    };
+
+    {
+        $interface:path[$descriptor:expr] {
+            native: $native:ident($on_transact:path),
+            proxy: $proxy:ident {
+                $($fname:ident: $fty:ty = $finit:expr),*
+            },
+            stability: $stability:expr,
+        }
+    } => {
+        $crate::declare_binder_interface! {
+            $interface[$descriptor] {
                 @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
                 native: $native($on_transact),
                 @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
                 proxy: $proxy {
                     $($fname: $fty = $finit),*
                 },
+                stability: $stability,
             }
         }
     };
@@ -635,6 +695,8 @@
             proxy: $proxy:ident {
                 $($fname:ident: $fty:ty = $finit:expr),*
             },
+
+            stability: $stability:expr,
         }
     } => {
         #[doc = $proxy_doc]
@@ -669,7 +731,7 @@
         impl $native {
             /// Create a new binder service.
             pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
-                let binder = $crate::Binder::new($native(Box::new(inner)));
+                let binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
                 $crate::Strong::new(Box::new(binder))
             }
         }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 5bbd2a3..30928a5 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,8 +107,9 @@
 pub mod parcel;
 
 pub use crate::binder::{
-    FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, Strong,
-    TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+    FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
+    Stability, Strong, TransactionCode, TransactionFlags, Weak,
+    FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
     LAST_CALL_TRANSACTION,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 185645e..3b3fd08 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode};
 use crate::error::{status_result, status_t, Result, StatusCode};
 use crate::parcel::{Parcel, Serialize};
 use crate::proxy::SpIBinder;
@@ -49,11 +49,19 @@
 unsafe impl<T: Remotable> Send for Binder<T> {}
 
 impl<T: Remotable> Binder<T> {
-    /// Create a new Binder remotable object.
+    /// Create a new Binder remotable object with default stability
     ///
     /// This moves the `rust_object` into an owned [`Box`] and Binder will
     /// manage its lifetime.
     pub fn new(rust_object: T) -> Binder<T> {
+        Self::new_with_stability(rust_object, Stability::default())
+    }
+
+    /// Create a new Binder remotable object with the given stability
+    ///
+    /// This moves the `rust_object` into an owned [`Box`] and Binder will
+    /// manage its lifetime.
+    pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
         let class = T::get_class();
         let rust_object = Box::into_raw(Box::new(rust_object));
         let ibinder = unsafe {
@@ -65,10 +73,12 @@
             // ends.
             sys::AIBinder_new(class.into(), rust_object as *mut c_void)
         };
-        Binder {
+        let mut binder = Binder {
             ibinder,
             rust_object,
-        }
+        };
+        binder.mark_stability(stability);
+        binder
     }
 
     /// Set the extension of a binder interface. This allows a downstream
@@ -161,6 +171,42 @@
     pub fn get_descriptor() -> &'static str {
         T::get_descriptor()
     }
+
+    /// Mark this binder object with the given stability guarantee
+    fn mark_stability(&mut self, stability: Stability) {
+        match stability {
+            Stability::Local => self.mark_local_stability(),
+            Stability::Vintf => {
+                unsafe {
+                    // Safety: Self always contains a valid `AIBinder` pointer, so
+                    // we can always call this C API safely.
+                    sys::AIBinder_markVintfStability(self.as_native_mut());
+                }
+            }
+        }
+    }
+
+    /// Mark this binder object with local stability, which is vendor if we are
+    /// building for the VNDK and system otherwise.
+    #[cfg(vendor_ndk)]
+    fn mark_local_stability(&mut self) {
+        unsafe {
+            // Safety: Self always contains a valid `AIBinder` pointer, so
+            // we can always call this C API safely.
+            sys::AIBinder_markVendorStability(self.as_native_mut());
+        }
+    }
+
+    /// Mark this binder object with local stability, which is vendor if we are
+    /// building for the VNDK and system otherwise.
+    #[cfg(not(vendor_ndk))]
+    fn mark_local_stability(&mut self) {
+        unsafe {
+            // Safety: Self always contains a valid `AIBinder` pointer, so
+            // we can always call this C API safely.
+            sys::AIBinder_markSystemStability(self.as_native_mut());
+        }
+    }
 }
 
 impl<T: Remotable> Interface for Binder<T> {
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index ef142b5..65fa2ca 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -21,6 +21,7 @@
 #include <android/binder_parcel_platform.h>
 #include <android/binder_process.h>
 #include <android/binder_shell.h>
+#include <android/binder_stability.h>
 #include <android/binder_status.h>
 
 namespace android {
@@ -80,6 +81,7 @@
 enum {
     FLAG_ONEWAY = FLAG_ONEWAY,
     FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
+    FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL,
 };
 
 } // namespace consts
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 3bbb0b5..a44cddf 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -105,6 +105,26 @@
 }
 
 cc_test {
+    name: "binderRpcTest",
+    defaults: ["binder_test_defaults"],
+
+    srcs: [
+        "IBinderRpcSession.aidl",
+        "IBinderRpcTest.aidl",
+        "binderRpcTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libbase",
+        "libutils",
+        "libcutils",
+        "liblog",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
+cc_test {
     name: "binderThroughputTest",
     defaults: ["binder_test_defaults"],
     srcs: ["binderThroughputTest.cpp"],
diff --git a/libs/binder/tests/IBinderRpcSession.aidl b/libs/binder/tests/IBinderRpcSession.aidl
new file mode 100644
index 0000000..cf5f318
--- /dev/null
+++ b/libs/binder/tests/IBinderRpcSession.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IBinderRpcSession {
+    @utf8InCpp String getName();
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
new file mode 100644
index 0000000..2bdb264
--- /dev/null
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IBinderRpcTest {
+    oneway void sendString(@utf8InCpp String str);
+    @utf8InCpp String doubleString(@utf8InCpp String str);
+
+    // number of known RPC binders to process, RpcState::countBinders
+    int countBinders();
+
+    // Caller sends server, callee pings caller's server and returns error code.
+    int pingMe(IBinder binder);
+    @nullable IBinder repeatBinder(@nullable IBinder binder);
+
+    void holdBinder(@nullable IBinder binder);
+    @nullable IBinder getHeldBinder();
+
+    // Idea is client creates its own instance of IBinderRpcTest and calls this,
+    // and the server calls 'binder' with (calls - 1) passing itself as 'binder',
+    // going back and forth until calls = 0
+    void nestMe(IBinderRpcTest binder, int calls);
+
+    // should always return the same binder
+    IBinder alwaysGiveMeTheSameBinder();
+
+    // Idea is that the server will not hold onto the session, the remote connection
+    // object must. This is to test lifetimes of binder objects, and consequently, also
+    // identity (since by assigning sessions names, we can make sure a section always
+    // references the session it was originally opened with).
+    IBinderRpcSession openSession(@utf8InCpp String name);
+
+    // Decremented in ~IBinderRpcSession
+    int getNumOpenSessions();
+
+    // primitives to test threading behavior
+    void lock();
+    oneway void unlockInMsAsync(int ms);
+    void lockUnlock(); // locks and unlocks a mutex
+
+    // take up binder thread for some time
+    void sleepMs(int ms);
+    oneway void sleepMsAsync(int ms);
+
+    void die(boolean cleanup);
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
new file mode 100644
index 0000000..6fa5333
--- /dev/null
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#include <BnBinderRpcSession.h>
+#include <BnBinderRpcTest.h>
+#include <android-base/logging.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/RpcConnection.h>
+#include <binder/RpcServer.h>
+#include <gtest/gtest.h>
+
+#include "../RpcState.h" // for debugging
+
+namespace android {
+
+using android::binder::Status;
+
+#define EXPECT_OK(status)                 \
+    do {                                  \
+        Status stat = (status);           \
+        EXPECT_TRUE(stat.isOk()) << stat; \
+    } while (false)
+
+class MyBinderRpcSession : public BnBinderRpcSession {
+public:
+    static std::atomic<int32_t> gNum;
+
+    MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
+    Status getName(std::string* name) override {
+        *name = mName;
+        return Status::ok();
+    }
+    ~MyBinderRpcSession() { gNum--; }
+
+private:
+    std::string mName;
+};
+std::atomic<int32_t> MyBinderRpcSession::gNum;
+
+class MyBinderRpcTest : public BnBinderRpcTest {
+public:
+    sp<RpcConnection> connection;
+
+    Status sendString(const std::string& str) override {
+        std::cout << "Child received string: " << str << std::endl;
+        return Status::ok();
+    }
+    Status doubleString(const std::string& str, std::string* strstr) override {
+        std::cout << "Child received string to double: " << str << std::endl;
+        *strstr = str + str;
+        return Status::ok();
+    }
+    Status countBinders(int32_t* out) override {
+        if (connection == nullptr) {
+            return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        }
+        *out = connection->state()->countBinders();
+        if (*out != 1) {
+            connection->state()->dump();
+        }
+        return Status::ok();
+    }
+    Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
+        if (binder == nullptr) {
+            std::cout << "Received null binder!" << std::endl;
+            return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+        }
+        *out = binder->pingBinder();
+        return Status::ok();
+    }
+    Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
+        *out = binder;
+        return Status::ok();
+    }
+    static sp<IBinder> mHeldBinder;
+    Status holdBinder(const sp<IBinder>& binder) override {
+        mHeldBinder = binder;
+        return Status::ok();
+    }
+    Status getHeldBinder(sp<IBinder>* held) override {
+        *held = mHeldBinder;
+        return Status::ok();
+    }
+    Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
+        if (count <= 0) return Status::ok();
+        return binder->nestMe(this, count - 1);
+    }
+    Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
+        static sp<IBinder> binder = new BBinder;
+        *out = binder;
+        return Status::ok();
+    }
+    Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
+        *out = new MyBinderRpcSession(name);
+        return Status::ok();
+    }
+    Status getNumOpenSessions(int32_t* out) override {
+        *out = MyBinderRpcSession::gNum;
+        return Status::ok();
+    }
+
+    std::mutex blockMutex;
+    Status lock() override {
+        blockMutex.lock();
+        return Status::ok();
+    }
+    Status unlockInMsAsync(int32_t ms) override {
+        usleep(ms * 1000);
+        blockMutex.unlock();
+        return Status::ok();
+    }
+    Status lockUnlock() override {
+        std::lock_guard<std::mutex> _l(blockMutex);
+        return Status::ok();
+    }
+
+    Status sleepMs(int32_t ms) override {
+        usleep(ms * 1000);
+        return Status::ok();
+    }
+
+    Status sleepMsAsync(int32_t ms) override {
+        // In-process binder calls are asynchronous, but the call to this method
+        // is synchronous wrt its client. This in/out-process threading model
+        // diffentiation is a classic binder leaky abstraction (for better or
+        // worse) and is preserved here the way binder sockets plugs itself
+        // into BpBinder, as nothing is changed at the higher levels
+        // (IInterface) which result in this behavior.
+        return sleepMs(ms);
+    }
+
+    Status die(bool cleanup) override {
+        if (cleanup) {
+            exit(1);
+        } else {
+            _exit(1);
+        }
+    }
+};
+sp<IBinder> MyBinderRpcTest::mHeldBinder;
+
+class Process {
+public:
+    Process(const std::function<void()>& f) {
+        if (0 == (mPid = fork())) {
+            // racey: assume parent doesn't crash before this is set
+            prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+            f();
+        }
+    }
+    ~Process() {
+        if (mPid != 0) {
+            kill(mPid, SIGKILL);
+        }
+    }
+
+private:
+    pid_t mPid = 0;
+};
+
+static std::string allocateSocketAddress() {
+    static size_t id = 0;
+
+    return "/dev/binderRpcTest_" + std::to_string(id++);
+};
+
+struct ProcessConnection {
+    // reference to process hosting a socket server
+    Process host;
+
+    // client connection object associated with other process
+    sp<RpcConnection> connection;
+
+    // pre-fetched root object
+    sp<IBinder> rootBinder;
+
+    // whether connection should be invalidated by end of run
+    bool expectInvalid = false;
+
+    ~ProcessConnection() {
+        rootBinder = nullptr;
+        EXPECT_NE(nullptr, connection);
+        EXPECT_NE(nullptr, connection->state());
+        EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+
+        wp<RpcConnection> weakConnection = connection;
+        connection = nullptr;
+        EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+    }
+};
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+ProcessConnection createRpcTestSocketServerProcess(
+        size_t numThreads,
+        const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
+    CHECK_GT(numThreads, 0);
+
+    std::string addr = allocateSocketAddress();
+    unlink(addr.c_str());
+
+    auto ret = ProcessConnection{
+            .host = Process([&] {
+                sp<RpcServer> server = RpcServer::make();
+
+                server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+                // server supporting one client on one socket
+                sp<RpcConnection> connection = server->addClientConnection();
+                CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+
+                configure(server, connection);
+
+                // accept 'numThreads' connections
+                std::vector<std::thread> pool;
+                for (size_t i = 0; i + 1 < numThreads; i++) {
+                    pool.push_back(std::thread([=] { connection->join(); }));
+                }
+                connection->join();
+                for (auto& t : pool) t.join();
+            }),
+            .connection = RpcConnection::make(),
+    };
+
+    // wait up to 1s for sockets to be created
+    constexpr useconds_t kMaxWaitUs = 1000000;
+    constexpr useconds_t kWaitDivision = 100;
+    for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) {
+        usleep(kMaxWaitUs / kWaitDivision);
+    }
+
+    // create remainder of connections
+    for (size_t i = 0; i < numThreads; i++) {
+        // Connection refused sometimes after file created but before listening.
+        CHECK(ret.connection->addUnixDomainClient(addr.c_str()) ||
+              (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str())))
+                << i;
+    }
+
+    ret.rootBinder = ret.connection->getRootObject();
+    return ret;
+}
+
+// Process connection where the process hosts IBinderRpcTest, the server used
+// for most testing here
+struct BinderRpcTestProcessConnection {
+    ProcessConnection proc;
+
+    // pre-fetched root object
+    sp<IBinder> rootBinder;
+
+    // pre-casted root object
+    sp<IBinderRpcTest> rootIface;
+
+    ~BinderRpcTestProcessConnection() {
+        if (!proc.expectInvalid) {
+            int32_t remoteBinders = 0;
+            EXPECT_OK(rootIface->countBinders(&remoteBinders));
+            // should only be the root binder object, iface
+            EXPECT_EQ(remoteBinders, 1);
+        }
+
+        rootIface = nullptr;
+        rootBinder = nullptr;
+    }
+};
+
+BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+    BinderRpcTestProcessConnection ret{
+            .proc = createRpcTestSocketServerProcess(numThreads,
+                                                     [&](const sp<RpcServer>& server,
+                                                         const sp<RpcConnection>& connection) {
+                                                         sp<MyBinderRpcTest> service =
+                                                                 new MyBinderRpcTest;
+                                                         server->setRootObject(service);
+                                                         service->connection =
+                                                                 connection; // for testing only
+                                                     }),
+    };
+
+    ret.rootBinder = ret.proc.rootBinder;
+    ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+    return ret;
+}
+
+TEST(BinderRpc, RootObjectIsNull) {
+    auto proc = createRpcTestSocketServerProcess(1,
+                                                 [](const sp<RpcServer>& server,
+                                                    const sp<RpcConnection>&) {
+                                                     // this is the default, but to be explicit
+                                                     server->setRootObject(nullptr);
+                                                 });
+
+    // retrieved by getRootObject when process is created above
+    EXPECT_EQ(nullptr, proc.rootBinder);
+
+    // make sure we can retrieve it again (process doesn't crash)
+    EXPECT_EQ(nullptr, proc.connection->getRootObject());
+}
+
+TEST(BinderRpc, Ping) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    ASSERT_NE(proc.rootBinder, nullptr);
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST(BinderRpc, TransactionsMustBeMarkedRpc) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    Parcel data;
+    Parcel reply;
+    EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST(BinderRpc, UnknownTransaction) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    Parcel data;
+    data.markForBinder(proc.rootBinder);
+    Parcel reply;
+    EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
+}
+
+TEST(BinderRpc, SendSomethingOneway) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST(BinderRpc, SendAndGetResultBack) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    std::string doubled;
+    EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+    EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST(BinderRpc, SendAndGetResultBackBig) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    std::string single = std::string(1024, 'a');
+    std::string doubled;
+    EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
+    EXPECT_EQ(single + single, doubled);
+}
+
+TEST(BinderRpc, CallMeBack) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    int32_t pingResult;
+    EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
+    EXPECT_EQ(OK, pingResult);
+
+    EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST(BinderRpc, RepeatBinder) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> inBinder = new MyBinderRpcSession("foo");
+    sp<IBinder> outBinder;
+    EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+    EXPECT_EQ(inBinder, outBinder);
+
+    wp<IBinder> weak = inBinder;
+    inBinder = nullptr;
+    outBinder = nullptr;
+
+    // Force reading a reply, to process any pending dec refs from the other
+    // process (the other process will process dec refs there before processing
+    // the ping here).
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    EXPECT_EQ(nullptr, weak.promote());
+
+    EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST(BinderRpc, RepeatTheirBinder) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinderRpcSession> session;
+    EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+
+    sp<IBinder> inBinder = IInterface::asBinder(session);
+    sp<IBinder> outBinder;
+    EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+    EXPECT_EQ(inBinder, outBinder);
+
+    wp<IBinder> weak = inBinder;
+    session = nullptr;
+    inBinder = nullptr;
+    outBinder = nullptr;
+
+    // Force reading a reply, to process any pending dec refs from the other
+    // process (the other process will process dec refs there before processing
+    // the ping here).
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST(BinderRpc, RepeatBinderNull) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> outBinder;
+    EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+    EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST(BinderRpc, HoldBinder) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    IBinder* ptr = nullptr;
+    {
+        sp<IBinder> binder = new BBinder();
+        ptr = binder.get();
+        EXPECT_OK(proc.rootIface->holdBinder(binder));
+    }
+
+    sp<IBinder> held;
+    EXPECT_OK(proc.rootIface->getHeldBinder(&held));
+
+    EXPECT_EQ(held.get(), ptr);
+
+    // stop holding binder, because we test to make sure references are cleaned
+    // up
+    EXPECT_OK(proc.rootIface->holdBinder(nullptr));
+    // and flush ref counts
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+// START TESTS FOR LIMITATIONS OF SOCKET BINDER
+// These are behavioral differences form regular binder, where certain usecases
+// aren't supported.
+
+TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+    auto proc1 = createRpcTestSocketServerProcess(1);
+    auto proc2 = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> outBinder;
+    EXPECT_EQ(INVALID_OPERATION,
+              proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
+}
+
+TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+    sp<IBinder> outBinder;
+    EXPECT_EQ(INVALID_OPERATION,
+              proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
+}
+
+TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    // for historical reasons, IServiceManager interface only returns the
+    // exception code
+    EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
+              defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
+}
+
+// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+
+TEST(BinderRpc, RepeatRootObject) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> outBinder;
+    EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+    EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST(BinderRpc, NestedTransactions) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    auto nastyNester = sp<MyBinderRpcTest>::make();
+    EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
+
+    wp<IBinder> weak = nastyNester;
+    nastyNester = nullptr;
+    EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST(BinderRpc, SameBinderEquality) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> a;
+    EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+
+    sp<IBinder> b;
+    EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+    EXPECT_EQ(a, b);
+}
+
+TEST(BinderRpc, SameBinderEqualityWeak) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinder> a;
+    EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+    wp<IBinder> weak = a;
+    a = nullptr;
+
+    sp<IBinder> b;
+    EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+    // this is the wrong behavior, since BpBinder
+    // doesn't implement onIncStrongAttempted
+    // but make sure there is no crash
+    EXPECT_EQ(nullptr, weak.promote());
+
+    GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
+
+    // In order to fix this:
+    // - need to have incStrongAttempted reflected across IPC boundary (wait for
+    //   response to promote - round trip...)
+    // - sendOnLastWeakRef, to delete entries out of RpcState table
+    EXPECT_EQ(b, weak.promote());
+}
+
+#define expectSessions(expected, iface)                   \
+    do {                                                  \
+        int session;                                      \
+        EXPECT_OK((iface)->getNumOpenSessions(&session)); \
+        EXPECT_EQ(expected, session);                     \
+    } while (false)
+
+TEST(BinderRpc, SingleSession) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    sp<IBinderRpcSession> session;
+    EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+    std::string out;
+    EXPECT_OK(session->getName(&out));
+    EXPECT_EQ("aoeu", out);
+
+    expectSessions(1, proc.rootIface);
+    session = nullptr;
+    expectSessions(0, proc.rootIface);
+}
+
+TEST(BinderRpc, ManySessions) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    std::vector<sp<IBinderRpcSession>> sessions;
+
+    for (size_t i = 0; i < 15; i++) {
+        expectSessions(i, proc.rootIface);
+        sp<IBinderRpcSession> session;
+        EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
+        sessions.push_back(session);
+    }
+    expectSessions(sessions.size(), proc.rootIface);
+    for (size_t i = 0; i < sessions.size(); i++) {
+        std::string out;
+        EXPECT_OK(sessions.at(i)->getName(&out));
+        EXPECT_EQ(std::to_string(i), out);
+    }
+    expectSessions(sessions.size(), proc.rootIface);
+
+    while (!sessions.empty()) {
+        sessions.pop_back();
+        expectSessions(sessions.size(), proc.rootIface);
+    }
+    expectSessions(0, proc.rootIface);
+}
+
+size_t epochMillis() {
+    using std::chrono::duration_cast;
+    using std::chrono::milliseconds;
+    using std::chrono::seconds;
+    using std::chrono::system_clock;
+    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
+TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+    constexpr size_t kNumThreads = 10;
+
+    auto proc = createRpcTestSocketServerProcess(kNumThreads);
+
+    EXPECT_OK(proc.rootIface->lock());
+
+    // block all but one thread taking locks
+    std::vector<std::thread> ts;
+    for (size_t i = 0; i < kNumThreads - 1; i++) {
+        ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); }));
+    }
+
+    usleep(100000); // give chance for calls on other threads
+
+    // other calls still work
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    constexpr size_t blockTimeMs = 500;
+    size_t epochMsBefore = epochMillis();
+    // after this, we should never see a response within this time
+    EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs));
+
+    // this call should be blocked for blockTimeMs
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    size_t epochMsAfter = epochMillis();
+    EXPECT_GE(epochMsAfter, epochMsBefore + blockTimeMs) << epochMsBefore;
+
+    for (auto& t : ts) t.join();
+}
+
+TEST(BinderRpc, ThreadPoolOverSaturated) {
+    constexpr size_t kNumThreads = 10;
+    constexpr size_t kNumCalls = kNumThreads + 3;
+    constexpr size_t kSleepMs = 500;
+
+    auto proc = createRpcTestSocketServerProcess(kNumThreads);
+
+    size_t epochMsBefore = epochMillis();
+
+    std::vector<std::thread> ts;
+    for (size_t i = 0; i < kNumCalls; i++) {
+        ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); }));
+    }
+
+    for (auto& t : ts) t.join();
+
+    size_t epochMsAfter = epochMillis();
+
+    EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs);
+
+    // Potential flake, but make sure calls are handled in parallel.
+    EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
+}
+
+TEST(BinderRpc, ThreadingStressTest) {
+    constexpr size_t kNumClientThreads = 10;
+    constexpr size_t kNumServerThreads = 10;
+    constexpr size_t kNumCalls = 100;
+
+    auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+
+    std::vector<std::thread> threads;
+    for (size_t i = 0; i < kNumClientThreads; i++) {
+        threads.push_back(std::thread([&] {
+            for (size_t j = 0; j < kNumCalls; j++) {
+                sp<IBinder> out;
+                proc.rootIface->repeatBinder(proc.rootBinder, &out);
+                EXPECT_EQ(proc.rootBinder, out);
+            }
+        }));
+    }
+
+    for (auto& t : threads) t.join();
+}
+
+TEST(BinderRpc, OnewayCallDoesNotWait) {
+    constexpr size_t kReallyLongTimeMs = 100;
+    constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+
+    // more than one thread, just so this doesn't deadlock
+    auto proc = createRpcTestSocketServerProcess(2);
+
+    size_t epochMsBefore = epochMillis();
+
+    EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+
+    size_t epochMsAfter = epochMillis();
+    EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+}
+
+TEST(BinderRpc, OnewayCallQueueing) {
+    constexpr size_t kNumSleeps = 10;
+    constexpr size_t kNumExtraServerThreads = 4;
+    constexpr size_t kSleepMs = 50;
+
+    // make sure calls to the same object happen on the same thread
+    auto proc = createRpcTestSocketServerProcess(1 + kNumExtraServerThreads);
+
+    EXPECT_OK(proc.rootIface->lock());
+
+    for (size_t i = 0; i < kNumSleeps; i++) {
+        // these should be processed serially
+        proc.rootIface->sleepMsAsync(kSleepMs);
+    }
+    // should also be processesed serially
+    EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
+
+    size_t epochMsBefore = epochMillis();
+    EXPECT_OK(proc.rootIface->lockUnlock());
+    size_t epochMsAfter = epochMillis();
+
+    EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+}
+
+TEST(BinderRpc, Die) {
+    // TODO(b/183141167): handle this in library
+    signal(SIGPIPE, SIG_IGN);
+
+    for (bool doDeathCleanup : {true, false}) {
+        auto proc = createRpcTestSocketServerProcess(1);
+
+        // make sure there is some state during crash
+        // 1. we hold their binder
+        sp<IBinderRpcSession> session;
+        EXPECT_OK(proc.rootIface->openSession("happy", &session));
+        // 2. they hold our binder
+        sp<IBinder> binder = new BBinder();
+        EXPECT_OK(proc.rootIface->holdBinder(binder));
+
+        EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
+                << "Do death cleanup: " << doDeathCleanup;
+
+        proc.proc.expectInvalid = true;
+    }
+}
+
+ssize_t countFds() {
+    DIR* dir = opendir("/proc/self/fd/");
+    if (dir == nullptr) return -1;
+    ssize_t ret = 0;
+    dirent* ent;
+    while ((ent = readdir(dir)) != nullptr) ret++;
+    closedir(dir);
+    return ret;
+}
+
+TEST(BinderRpc, Fds) {
+    ssize_t beforeFds = countFds();
+    ASSERT_GE(beforeFds, 0);
+    {
+        auto proc = createRpcTestSocketServerProcess(10);
+        ASSERT_EQ(OK, proc.rootBinder->pingBinder());
+    }
+    ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
+}
+
+extern "C" int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+    return RUN_ALL_TESTS();
+}
+
+} // namespace android
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 1f2779a..4270540 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -131,6 +131,17 @@
     EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
 }
 
+TEST(BinderStability, ForceDowngradeStability) {
+    sp<IBinder> someBinder = BadStableBinder::vintf();
+
+    EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
+
+    // silly to do this after already using the binder, but it's for the test
+    Stability::forceDowngradeCompilationUnit(someBinder);
+
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
 TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
     sp<IBinder> vintfServer = BadStableBinder::vintf();
 
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 2972454..1976b49 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -399,7 +399,7 @@
     t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
 
     if (!mNextFrameTimelineInfoQueue.empty()) {
-        t->setFrameTimelineInfo(mSurfaceControl, mNextFrameTimelineInfoQueue.front());
+        t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
         mNextFrameTimelineInfoQueue.pop();
     }
 
@@ -606,7 +606,9 @@
         // Apply the transaction since we have already acquired the desired frame.
         t->apply();
     } else {
-        mPendingTransactions.emplace_back(frameNumber, std::move(*t));
+        mPendingTransactions.emplace_back(frameNumber, *t);
+        // Clear the transaction so it can't be applied elsewhere.
+        t->clear();
     }
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ceab6ec..97f8f47 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -477,6 +477,26 @@
         return reply.readInt32();
     }
 
+    virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+                                      const std::vector<ui::Hdr>& hdrTypes) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, display);
+
+        std::vector<int32_t> hdrTypesVector;
+        for (ui::Hdr i : hdrTypes) {
+            hdrTypesVector.push_back(static_cast<int32_t>(i));
+        }
+        SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector);
+
+        status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("overrideHdrTypes failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
     status_t enableVSyncInjections(bool enable) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1963,6 +1983,20 @@
             SAFE_PARCEL(reply->writeInt32, extraBuffers);
             return NO_ERROR;
         }
+        case OVERRIDE_HDR_TYPES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            SAFE_PARCEL(data.readStrongBinder, &display);
+
+            std::vector<int32_t> hdrTypes;
+            SAFE_PARCEL(data.readInt32Vector, &hdrTypes);
+
+            std::vector<ui::Hdr> hdrTypesVector;
+            for (int i : hdrTypes) {
+                hdrTypesVector.push_back(static_cast<ui::Hdr>(i));
+            }
+            return overrideHdrTypes(display, hdrTypesVector);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 665086f..d653ae7 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -63,7 +63,6 @@
         shouldBeSeamless(true),
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
-        frameTimelineInfo(),
         autoRefresh(false),
         releaseBufferListener(nullptr) {
     matrix.dsdx = matrix.dtdy = 1.0f;
@@ -152,7 +151,6 @@
     SAFE_PARCEL(output.writeBool, shouldBeSeamless);
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeUint64, frameNumber);
-    SAFE_PARCEL(frameTimelineInfo.write, output);
     SAFE_PARCEL(output.writeBool, autoRefresh);
     SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
 
@@ -275,7 +273,6 @@
     SAFE_PARCEL(input.readUint32, &tmpUint32);
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readUint64, &frameNumber);
-    SAFE_PARCEL(frameTimelineInfo.read, input);
     SAFE_PARCEL(input.readBool, &autoRefresh);
 
     tmpBinder = nullptr;
@@ -544,10 +541,6 @@
         what |= eFrameNumberChanged;
         frameNumber = other.frameNumber;
     }
-    if (other.what & eFrameTimelineInfoChanged) {
-        what |= eFrameTimelineInfoChanged;
-        frameTimelineInfo.merge(other.frameTimelineInfo);
-    }
     if (other.what & eAutoRefreshChanged) {
         what |= eAutoRefreshChanged;
         autoRefresh = other.autoRefresh;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7dc5e85..0b01084 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -292,8 +292,11 @@
                 // callback and a release buffer callback happening at the same time to avoid an
                 // additional ipc call from the server.
                 if (surfaceStats.previousBufferId) {
-                    ReleaseBufferCallback callback =
-                            popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+                    ReleaseBufferCallback callback;
+                    {
+                        std::scoped_lock<std::mutex> lock(mMutex);
+                        callback = popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+                    }
                     if (callback) {
                         callback(surfaceStats.previousBufferId,
                                  surfaceStats.previousReleaseFence
@@ -1618,20 +1621,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
         const FrameTimelineInfo& frameTimelineInfo) {
-    mFrameTimelineInfo = frameTimelineInfo;
-    return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
-        const sp<SurfaceControl>& sc, const FrameTimelineInfo& frameTimelineInfo) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-
-    s->what |= layer_state_t::eFrameTimelineInfoChanged;
-    s->frameTimelineInfo = frameTimelineInfo;
+    mFrameTimelineInfo.merge(frameTimelineInfo);
     return *this;
 }
 
@@ -1995,6 +1985,11 @@
     return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
 }
 
+status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display,
+                                                 const std::vector<ui::Hdr>& hdrTypes) {
+    return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
+}
+
 status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
                                                                       ui::PixelFormat* outFormat,
                                                                       ui::Dataspace* outDataspace,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 35990d1..9f9ca74 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -267,6 +267,13 @@
      */
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
 
+    /* Overrides the supported HDR modes for the given display device.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+                                      const std::vector<ui::Hdr>& hdrTypes) = 0;
+
     virtual status_t enableVSyncInjections(bool enable) = 0;
 
     virtual status_t injectVSync(nsecs_t when) = 0;
@@ -570,6 +577,7 @@
         GET_DYNAMIC_DISPLAY_INFO,
         ADD_FPS_LISTENER,
         REMOVE_FPS_LISTENER,
+        OVERRIDE_HDR_TYPES,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 99b3ea5..9274777 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -117,10 +117,9 @@
         eProducerDisconnect = 0x100'00000000,
         eFixedTransformHintChanged = 0x200'00000000,
         eFrameNumberChanged = 0x400'00000000,
-        eFrameTimelineInfoChanged = 0x800'00000000,
-        eBlurRegionsChanged = 0x1000'00000000,
-        eAutoRefreshChanged = 0x2000'00000000,
-        eStretchChanged = 0x4000'00000000,
+        eBlurRegionsChanged = 0x800'00000000,
+        eAutoRefreshChanged = 0x1000'00000000,
+        eStretchChanged = 0x2000'00000000,
     };
 
     layer_state_t();
@@ -223,8 +222,6 @@
     // graphics producer.
     uint64_t frameNumber;
 
-    FrameTimelineInfo frameTimelineInfo;
-
     // Indicates that the consumer should acquire the next frame as soon as it
     // can and not wait for a frame to become available. This is only relevant
     // in shared buffer mode.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index a91de9c..c38375c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -529,9 +529,6 @@
         // to the transaction, and the input event id that identifies the input event that caused
         // the current frame.
         Transaction& setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo);
-        // Variant that only applies to a specific SurfaceControl.
-        Transaction& setFrameTimelineInfo(const sp<SurfaceControl>& sc,
-                                          const FrameTimelineInfo& frameTimelineInfo);
 
         // Indicates that the consumer should acquire the next frame as soon as it
         // can and not wait for a frame to become available. This is only relevant
@@ -575,6 +572,9 @@
     static status_t clearAnimationFrameStats();
     static status_t getAnimationFrameStats(FrameStats* outStats);
 
+    static status_t overrideHdrTypes(const sp<IBinder>& display,
+                                     const std::vector<ui::Hdr>& hdrTypes);
+
     static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
                                      const Rect& layerStackRect, const Rect& displayRect);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8d7f8c9..5ac3f19 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -772,6 +772,10 @@
     status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
         return NO_ERROR;
     }
+    status_t overrideHdrTypes(const sp<IBinder>& /*display*/,
+                              const std::vector<ui::Hdr>& /*hdrTypes*/) override {
+        return NO_ERROR;
+    }
     status_t enableVSyncInjections(bool /*enable*/) override {
         return NO_ERROR;
     }
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index a375d43..79b47a1 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -51,13 +51,13 @@
             std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
 
     status_t err = gbuffer->initCheck();
-    if (err != 0 || gbuffer->handle == 0) {
+    if (err != 0 || gbuffer->handle == nullptr) {
         if (err == NO_MEMORY) {
             GraphicBuffer::dumpAllocationsToSystemLog();
         }
         ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
                 desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle);
-        return err;
+        return err == 0 ? UNKNOWN_ERROR : err;
     }
 
     *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 4fdae74..69c6a09 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -28,8 +28,11 @@
 
 namespace android::renderengine::skia {
 
-static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                            sp<GraphicBuffer> dstBuffer) {
+// Warming shader cache, not framebuffer cache.
+constexpr bool kUseFrameBufferCache = false;
+
+static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                             sp<GraphicBuffer> dstBuffer) {
     // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
     // on actual use.
     FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
@@ -39,6 +42,7 @@
                             .boundaries = rect,
                             .roundedCornersCrop = rect,
                     },
+            // drawShadow ignores alpha
             .shadow =
                     ShadowSettings{
                             .ambientColor = vec4(0, 0, 0, 0.00935997f),
@@ -55,13 +59,15 @@
     // generate the slower (more general case) version. If we also need a
     // slow version without color correction, we should use this matrix with
     // display.outputDataspace set to SRGB.
+    bool identity = true;
     for (const mat4 transform : { mat4(), mat4(0.728872f,   0.f,          0.f, 0.f,
                                                0.f,         0.727627f,    0.f, 0.f,
                                                0.f,         0.f,          1.f, 0.f,
                                                167.355743f, 1852.257812f, 0.f, 1.f) }) {
         layer.geometry.positionTransform = transform;
-        renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/,
+        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
                                  base::unique_fd(), nullptr);
+        identity = false;
     }
 }
 
@@ -73,14 +79,6 @@
             .geometry =
                     Geometry{
                             .boundaries = rect,
-                            // This matrix is based on actual data seen when opening the dialer.
-                            // What seems to be important in matching the actual use cases are:
-                            // - it is not identity
-                            // - the layer will be drawn (not clipped out etc)
-                            .positionTransform = mat4(.19f,     .0f, .0f,   .0f,
-                                                      .0f,     .19f, .0f,   .0f,
-                                                      .0f,      .0f, 1.f,   .0f,
-                                                      169.f, 1527.f, .0f,   1.f),
                             .roundedCornersCrop = rect,
                     },
             .source = PixelSource{.buffer =
@@ -91,26 +89,60 @@
                                           }},
     };
 
+    // This matrix is based on actual data seen when opening the dialer.
+    //  translate and scale creates new shaders when combined with rounded corners
+    // clang-format off
+    auto scale_and_translate = mat4(.19f,    .0f,  .0f,  .0f,
+                                     .0f,   .19f,  .0f,  .0f,
+                                     .0f,    .0f,  1.f,  .0f,
+                                   169.f, 1527.f,  .0f,  1.f);
+    // clang-format on
+
     // Test both drawRect and drawRRect
     auto layers = std::vector<const LayerSettings*>{&layer};
-    for (float roundedCornersRadius : {0.0f, 500.f}) {
-        layer.geometry.roundedCornersRadius = roundedCornersRadius;
-        // No need to check UNKNOWN, which is treated as SRGB.
-        for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
-            layer.sourceDataspace = dataspace;
-            for (bool isOpaque : {true, false}) {
-                layer.source.buffer.isOpaque = isOpaque;
-                for (auto alpha : {half(.23999f), half(1.0f)}) {
-                    layer.alpha = alpha;
-                    renderengine->drawLayers(display, layers, dstBuffer,
-                                             false /* useFrameBufferCache*/, base::unique_fd(),
-                                             nullptr);
+    for (auto transform : {mat4(), scale_and_translate}) {
+        layer.geometry.positionTransform = transform;
+        // fractional corner radius creates a shader that is used during home button swipe
+        for (float roundedCornersRadius : {0.0f, 0.05f, 500.f}) {
+            // roundedCornersCrop is always set, but it is this radius that triggers the behavior
+            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            // No need to check UNKNOWN, which is treated as SRGB.
+            for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+                layer.sourceDataspace = dataspace;
+                for (bool isOpaque : {true, false}) {
+                    layer.source.buffer.isOpaque = isOpaque;
+                    for (auto alpha : {half(.23999f), half(1.0f)}) {
+                        layer.alpha = alpha;
+                        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
+                                                 base::unique_fd(), nullptr);
+                    }
                 }
             }
         }
     }
 }
 
+static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            sp<GraphicBuffer> dstBuffer) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                    },
+            .alpha = 1,
+            .source =
+                    PixelSource{
+                            .solidColor = half3(0.1f, 0.2f, 0.3f),
+                    },
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(),
+                             nullptr);
+}
+
 void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
     const nsecs_t timeBefore = systemTime();
     // The dimensions should not matter, so long as we draw inside them.
@@ -129,14 +161,13 @@
                               usage, "primeShaderCache_dst");
     // This buffer will be the source for the call to drawImageLayers. Draw
     // something to it as a placeholder for what an app draws. We should draw
-    // something, but the details are not important. We only need one version of
-    // the shadow's SkSL, so draw it here, giving us both a placeholder image
-    // and a chance to compile the shadow's SkSL.
+    // something, but the details are not important. Make use of the shadow layer drawing step
+    // to populate it.
     sp<GraphicBuffer> srcBuffer =
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usage, "drawImageLayer_src");
-    drawShadowLayer(renderengine, display, srcBuffer);
-
+    drawSolidLayers(renderengine, display, dstBuffer);
+    drawShadowLayers(renderengine, display, srcBuffer);
     drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
     const nsecs_t timeAfter = systemTime();
     const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index afdcd76..c5ee15d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -268,6 +268,23 @@
     return config;
 }
 
+sk_sp<SkData> SkiaGLRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
+    // This "cache" does not actually cache anything. It just allows us to
+    // monitor Skia's internal cache. So this method always returns null.
+    return nullptr;
+}
+
+void SkiaGLRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
+                                                 const SkString& description) {
+    mShadersCachedSinceLastCall++;
+}
+
+void SkiaGLRenderEngine::assertShadersCompiled(int numShaders) {
+    const int cached = mSkSLCacheMonitor.shadersCachedSinceLastCall();
+    LOG_ALWAYS_FATAL_IF(cached != numShaders, "Attempted to cache %i shaders; cached %i",
+                        numShaders, cached);
+}
+
 SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
                                        EGLContext ctxt, EGLSurface placeholder,
                                        EGLContext protectedContext, EGLSurface protectedPlaceholder)
@@ -284,6 +301,7 @@
     GrContextOptions options;
     options.fPreferExternalImagesOverES3 = true;
     options.fDisableDistanceFieldPaths = true;
+    options.fPersistentCache = &mSkSLCacheMonitor;
     mGrContext = GrDirectContext::MakeGL(glInterface, options);
     if (useProtectedContext(true)) {
         mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
@@ -461,18 +479,31 @@
     }
     ATRACE_CALL();
 
-    std::lock_guard<std::mutex> lock(mRenderingMutex);
-    auto iter = mTextureCache.find(buffer->getId());
-    if (iter != mTextureCache.end()) {
-        ALOGV("Texture already exists in cache.");
+    // We need to switch the currently bound context if the buffer is protected but the current
+    // context is not. The current state must then be restored after the buffer is cached.
+    const bool protectedContextState = mInProtectedContext;
+    if (!useProtectedContext(protectedContextState ||
+                             (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
+        ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
         return;
+    }
+
+    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+    auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    auto iter = cache.find(buffer->getId());
+    if (iter != cache.end()) {
+        ALOGV("Texture already exists in cache.");
     } else {
         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
                 std::make_shared<AutoBackendTexture::LocalRef>();
         imageTextureRef->setTexture(
-                new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), false));
-        mTextureCache.insert({buffer->getId(), imageTextureRef});
+                new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), false));
+        cache.insert({buffer->getId(), imageTextureRef});
     }
+    // restore the original state of the protected context if necessary
+    useProtectedContext(protectedContextState);
 }
 
 void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
@@ -824,15 +855,15 @@
             validateInputBufferUsage(layer->source.buffer.buffer);
             const auto& item = layer->source.buffer;
             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
-            auto iter = mTextureCache.find(item.buffer->getId());
-            if (iter != mTextureCache.end()) {
+            auto iter = cache.find(item.buffer->getId());
+            if (iter != cache.end()) {
                 imageTextureRef = iter->second;
             } else {
                 imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
                 imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(),
                                                                    item.buffer->toAHardwareBuffer(),
                                                                    false));
-                mTextureCache.insert({item.buffer->getId(), imageTextureRef});
+                cache.insert({item.buffer->getId(), imageTextureRef});
             }
 
             sk_sp<SkImage> image =
@@ -1168,6 +1199,8 @@
     StringAppendF(&result, "RenderEngine supports protected context: %d\n",
                   supportsProtectedContent());
     StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
+    StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
+                  mSkSLCacheMonitor.shadersCachedSinceLastCall());
 
     {
         std::lock_guard<std::mutex> lock(mRenderingMutex);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 15d834d..7605df9 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -31,6 +31,7 @@
 
 #include "AutoBackendTexture.h"
 #include "EGL/egl.h"
+#include "GrContextOptions.h"
 #include "SkImageInfo.h"
 #include "SkiaRenderEngine.h"
 #include "android-base/macros.h"
@@ -63,6 +64,7 @@
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+    void assertShadersCompiled(int numShaders) override;
 
 protected:
     void dump(std::string& result) override;
@@ -131,6 +133,29 @@
     bool mInProtectedContext = false;
     // Object to capture commands send to Skia.
     std::unique_ptr<SkiaCapture> mCapture;
+
+    // Implements PersistentCache as a way to monitor what SkSL shaders Skia has
+    // cached.
+    class SkSLCacheMonitor : public GrContextOptions::PersistentCache {
+    public:
+        SkSLCacheMonitor() = default;
+        ~SkSLCacheMonitor() override = default;
+
+        sk_sp<SkData> load(const SkData& key) override;
+
+        void store(const SkData& key, const SkData& data, const SkString& description) override;
+
+        int shadersCachedSinceLastCall() {
+            const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall;
+            mShadersCachedSinceLastCall = 0;
+            return shadersCachedSinceLastCall;
+        }
+
+    private:
+        int mShadersCachedSinceLastCall = 0;
+    };
+
+    SkSLCacheMonitor mSkSLCacheMonitor;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index f403725..59d7e2f 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -58,6 +58,7 @@
     };
     virtual bool cleanupPostRender(CleanupMode) override { return true; };
     virtual int getContextPriority() override { return 0; }
+    virtual void assertShadersCompiled(int numShaders) {}
 };
 
 } // namespace skia
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index df0551d..7694328 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -73,7 +73,7 @@
     /* ------------------------------------------------------------------------
      * Threading
      */
-    const char* const mThreadName = "RenderEngineThread";
+    const char* const mThreadName = "RenderEngine";
     // Protects the creation and destruction of mThread.
     mutable std::mutex mThreadMutex;
     std::thread mThread GUARDED_BY(mThreadMutex);
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 393f649..9750ef9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -42,6 +42,7 @@
         "InputTarget.cpp",
         "Monitor.cpp",
         "TouchState.cpp",
+        "DragState.cpp",
     ],
 }
 
diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp
new file mode 100644
index 0000000..2e2df43
--- /dev/null
+++ b/services/inputflinger/dispatcher/DragState.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DragState.h"
+#include <android-base/stringprintf.h>
+#include <input/InputWindow.h>
+
+using android::InputWindowHandle;
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+void DragState::dump(std::string& dump, const char* prefix) {
+    dump += prefix + StringPrintf("Drag Window: %s\n", dragWindow->getName().c_str());
+    if (dragHoverWindowHandle) {
+        dump += prefix +
+                StringPrintf("Drag Hover Window: %s\n", dragHoverWindowHandle->getName().c_str());
+    }
+    dump += prefix + StringPrintf("isStartDrag: %s\n", isStartDrag ? "true" : "false");
+    dump += prefix +
+            StringPrintf("isStylusButtonDownAtStart: %s\n",
+                         isStylusButtonDownAtStart ? "true" : "false");
+}
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
new file mode 100644
index 0000000..06453d8
--- /dev/null
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+
+#include <utils/RefBase.h>
+#include <string>
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+struct DragState {
+    DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {}
+    void dump(std::string& dump, const char* prefix = "");
+
+    // The window being dragged.
+    const sp<InputWindowHandle> dragWindow;
+    // The last drag hover window which could receive the drag event.
+    sp<InputWindowHandle> dragHoverWindowHandle;
+    // Indicates the if received first event to check for button state.
+    bool isStartDrag = false;
+    // Indicate if the stylus button is down at the start of the drag.
+    bool isStylusButtonDownAtStart = false;
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 397a9d7..6977396 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -961,7 +961,7 @@
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
-        if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+        if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
             continue;
         }
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
@@ -2061,7 +2061,7 @@
             goto Failed;
         }
 
-        addDragEventLocked(entry, tempTouchState);
+        addDragEventLocked(entry);
 
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
@@ -2318,39 +2318,61 @@
     return injectionResult;
 }
 
-void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
-    if (entry.pointerCount != 1 || !state.dragWindow) {
+void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+    const sp<InputWindowHandle> dropWindow =
+            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
+                                      false /*addOutsideTargets*/, false /*addPortalWindows*/,
+                                      true /*ignoreDragWindow*/);
+    if (dropWindow) {
+        vec2 local = dropWindow->getInfo()->transform.transform(x, y);
+        notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+    }
+    mDragState.reset();
+}
+
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
+    if (entry.pointerCount != 1 || !mDragState) {
         return;
     }
 
+    if (!mDragState->isStartDrag) {
+        mDragState->isStartDrag = true;
+        mDragState->isStylusButtonDownAtStart =
+                (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+    }
+
     int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
     int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
     int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
     if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+        // Handle the special case : stylus button no longer pressed.
+        bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+        if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) {
+            finishDragAndDrop(entry.displayId, x, y);
+            return;
+        }
+
         const sp<InputWindowHandle> hoverWindowHandle =
-                findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
                                           false /*addOutsideTargets*/, false /*addPortalWindows*/,
                                           true /*ignoreDragWindow*/);
         // enqueue drag exit if needed.
-        if (hoverWindowHandle != state.dragHoverWindowHandle &&
-            !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
-            if (state.dragHoverWindowHandle != nullptr) {
-                enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+        if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
+            !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
+            if (mDragState->dragHoverWindowHandle != nullptr) {
+                enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/,
+                                       entry);
             }
-            state.dragHoverWindowHandle = hoverWindowHandle;
+            mDragState->dragHoverWindowHandle = hoverWindowHandle;
         }
         // enqueue drag location if needed.
         if (hoverWindowHandle != nullptr) {
             enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
         }
-    } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
-               maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-        if (state.dragHoverWindowHandle && maskedAction == AMOTION_EVENT_ACTION_UP) {
-            vec2 local = state.dragHoverWindowHandle->getInfo()->transform.transform(x, y);
-            notifyDropWindowLocked(state.dragHoverWindowHandle->getToken(), local.x, local.y);
-        }
-        state.dragWindow = nullptr;
-        state.dragHoverWindowHandle = nullptr;
+    } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
+        finishDragAndDrop(entry.displayId, x, y);
+    } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+        mDragState.reset();
     }
 }
 
@@ -4445,13 +4467,12 @@
             }
         }
 
-        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
         // could just clear the state here.
-        if (state.dragWindow &&
-            std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+        if (mDragState &&
+            std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
                     windowHandles.end()) {
-            state.dragWindow = nullptr;
-            state.dragHoverWindowHandle = nullptr;
+            mDragState.reset();
         }
     }
 
@@ -4690,7 +4711,7 @@
 
                     // Store the dragging window.
                     if (isDragDrop) {
-                        state.dragWindow = toWindowHandle;
+                        mDragState = std::make_unique<DragState>(toWindowHandle);
                     }
 
                     found = true;
@@ -4833,6 +4854,11 @@
         dump += INDENT "TouchStates: <no displays touched>\n";
     }
 
+    if (mDragState) {
+        dump += StringPrintf(INDENT "DragState:\n");
+        mDragState->dump(dump, INDENT2);
+    }
+
     if (!mWindowHandlesByDisplay.empty()) {
         for (auto& it : mWindowHandlesByDisplay) {
             const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 593ec23..5708fac 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -19,6 +19,7 @@
 
 #include "AnrTracker.h"
 #include "CancelationOptions.h"
+#include "DragState.h"
 #include "Entry.h"
 #include "FocusResolver.h"
 #include "InjectionState.h"
@@ -340,6 +341,7 @@
             REQUIRES(mLock);
 
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+    std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
 
     // Focused applications.
     std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
@@ -499,7 +501,8 @@
                                   const InjectionState* injectionState);
     // Enqueue a drag event if needed, and update the touch state.
     // Uses findTouchedWindowTargetsLocked to make the decision
-    void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
+    void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
+    void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock);
 
     struct TouchOcclusionInfo {
         bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 4165f49..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,8 +49,6 @@
     windows = other.windows;
     portalWindows = other.portalWindows;
     gestureMonitors = other.gestureMonitors;
-    dragHoverWindowHandle = other.dragHoverWindowHandle;
-    dragWindow = other.dragWindow;
 }
 
 void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index d7a561c..623c6a8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,11 +41,6 @@
 
     std::vector<TouchedMonitor> gestureMonitors;
 
-    // The last drag hover window which could receive the drag event.
-    sp<InputWindowHandle> dragHoverWindowHandle;
-    // The window being dragged.
-    sp<InputWindowHandle> dragWindow;
-
     TouchState();
     ~TouchState();
     void reset();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index cedda6e..485fc99 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1165,8 +1165,8 @@
         return *this;
     }
 
-    MotionEventBuilder& buttonState(int32_t actionButton) {
-        mActionButton = actionButton;
+    MotionEventBuilder& buttonState(int32_t buttonState) {
+        mButtonState = buttonState;
         return *this;
     }
 
@@ -4761,6 +4761,32 @@
         mWindow->consumeMotionCancel();
         mDragWindow->consumeMotionDown();
     }
+
+    // Start performing drag, we will create a drag window and transfer touch to it.
+    void performStylusDrag() {
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  injectMotionEvent(mDispatcher,
+                                    MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+                                                       AINPUT_SOURCE_STYLUS)
+                                            .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+                                            .pointer(PointerBuilder(0,
+                                                                    AMOTION_EVENT_TOOL_TYPE_STYLUS)
+                                                             .x(50)
+                                                             .y(50))
+                                            .build()));
+        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+        // The drag window covers the entire display
+        mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows(
+                {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+        // Transfer touch focus to the drag window
+        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+                                        true /* isDragDrop */);
+        mWindow->consumeMotionCancel();
+        mDragWindow->consumeMotionDown();
+    }
 };
 
 TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
@@ -4833,4 +4859,51 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
+    performStylusDrag();
+
+    // Move on window and keep button pressed.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+                                        .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+                                                         .x(50)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window and release button, expect to drop item.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+                                        .buttonState(0)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+                                                         .x(150)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+
+    // nothing to the window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
+                                        .buttonState(0)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+                                                         .x(150)
+                                                         .y(50))
+                                        .build()))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 5225dd7..9ce8d9b 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -287,29 +287,6 @@
     }
 }
 
-// TODO(b/179649922): A better algorithm to guarantee that capped connections will get a sampling
-// rate close to 200 Hz. With the current algorithm, apps might be punished unfairly: E.g.,two apps
-// make requests to the sensor service at the same time, one is not capped and uses 250 Hz, and one
-//is capped, the capped connection will only get 125 Hz.
-void SensorService::SensorEventConnection::addSensorEventsToBuffer(bool shouldResample,
-    const sensors_event_t& sensorEvent, sensors_event_t* buffer, int* index) {
-    if (!shouldResample || !mService->isSensorInCappedSet(sensorEvent.type)) {
-        buffer[(*index)++] = sensorEvent;
-    } else {
-        int64_t lastTimestamp = -1;
-        auto entry = mSensorLastTimestamp.find(sensorEvent.sensor);
-        if (entry != mSensorLastTimestamp.end()) {
-            lastTimestamp = entry->second;
-        }
-        // Allow 10% headroom here because the clocks are not perfect.
-        if (lastTimestamp == -1  || sensorEvent.timestamp - lastTimestamp
-                                        >= 0.9 * SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
-            mSensorLastTimestamp[sensorEvent.sensor] = sensorEvent.timestamp;
-            buffer[(*index)++] = sensorEvent;
-        }
-    }
-}
-
 status_t SensorService::SensorEventConnection::sendEvents(
         sensors_event_t const* buffer, size_t numEvents,
         sensors_event_t* scratch,
@@ -318,8 +295,6 @@
 
     std::unique_ptr<sensors_event_t[]> sanitizedBuffer;
 
-    bool shouldResample = mService->isMicSensorPrivacyEnabledForUid(mUid) ||
-                            mIsRateCappedBasedOnPermission;
     int count = 0;
     Mutex::Autolock _l(mConnectionLock);
     if (scratch) {
@@ -373,7 +348,7 @@
                     // Regular sensor event, just copy it to the scratch buffer after checking
                     // the AppOp.
                     if (hasSensorAccess() && noteOpIfRequired(buffer[i])) {
-                        addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count);
+                        scratch[count++] = buffer[i];
                     }
                 }
                 i++;
@@ -383,13 +358,12 @@
                                         buffer[i].meta_data.sensor == sensor_handle)));
         }
     } else {
-        sanitizedBuffer.reset(new sensors_event_t[numEvents]);
-        scratch = sanitizedBuffer.get();
         if (hasSensorAccess()) {
-            for (size_t i = 0; i < numEvents; i++) {
-                addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count);
-            }
+            scratch = const_cast<sensors_event_t *>(buffer);
+            count = numEvents;
         } else {
+            sanitizedBuffer.reset(new sensors_event_t[numEvents]);
+            scratch = sanitizedBuffer.get();
             for (size_t i = 0; i < numEvents; i++) {
                 if (buffer[i].type == SENSOR_TYPE_META_DATA) {
                     scratch[count++] = buffer[i++];
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 4e3f120..909053b 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -145,16 +145,10 @@
     void capRates();
     // Recover sensor connection previously capped by capRates().
     void uncapRates();
-
-    // Add sensorEvent to buffer at position index if the sensorEvent satisfies throttling rules.
-    void addSensorEventsToBuffer(bool shouldResample, const sensors_event_t& sensorEvent,
-                        sensors_event_t* buffer, int* index);
     sp<SensorService> const mService;
     sp<BitTube> mChannel;
     uid_t mUid;
     std::atomic_bool mIsRateCappedBasedOnPermission;
-    // Store a mapping of sensor to the timestamp of their last sensor event.
-    std::unordered_map<int, int64_t> mSensorLastTimestamp;
     mutable Mutex mConnectionLock;
     // Number of events from wake up sensors which are still pending and haven't been delivered to
     // the corresponding application. It is incremented by one unit for each write to the socket.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 942b7ae..9955cdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2129,12 +2129,14 @@
 }
 
 void SensorService::SensorPrivacyPolicy::registerSelf() {
+    AutoCallerClear acc;
     SensorPrivacyManager spm;
     mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
     spm.addSensorPrivacyListener(this);
 }
 
 void SensorService::SensorPrivacyPolicy::unregisterSelf() {
+    AutoCallerClear acc;
     SensorPrivacyManager spm;
     spm.removeSensorPrivacyListener(this);
 }
@@ -2167,7 +2169,7 @@
 
 status_t SensorService::SensorPrivacyPolicy::registerSelfForIndividual(int userId) {
     Mutex::Autolock _l(mSensorPrivacyLock);
-
+    AutoCallerClear acc;
     SensorPrivacyManager spm;
     status_t err = spm.addIndividualSensorPrivacyListener(userId,
             SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 9c5060a..a563a60 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -242,6 +242,22 @@
             userid_t mUserId;
     };
 
+    // A class automatically clearing and restoring binder caller identity inside
+    // a code block (scoped variable).
+    // Declare one systematically before calling SensorPrivacyManager methods so that they are
+    // executed with the same level of privilege as the SensorService process.
+    class AutoCallerClear {
+        public:
+            AutoCallerClear() :
+                mToken(IPCThreadState::self()->clearCallingIdentity()) {}
+            ~AutoCallerClear() {
+                IPCThreadState::self()->restoreCallingIdentity(mToken);
+            }
+
+        private:
+            const int64_t mToken;
+    };
+
     enum Mode {
        // The regular operating mode where any application can register/unregister/call flush on
        // sensors.
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index eac3d95..4ace4c2 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -355,6 +355,13 @@
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                clientCompositionTimestamp,
                                                FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+        // Update the SurfaceFrames in the drawing state
+        if (mDrawingState.bufferSurfaceFrameTX) {
+            mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+        }
+        for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+            surfaceFrame->setGpuComposition();
+        }
     }
 
     std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
@@ -424,24 +431,6 @@
     return isBufferDue(expectedPresentTime);
 }
 
-bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
-    // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
-    // vsync period. We can do this change as soon as ag/13100772 is merged.
-    constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
-
-    const auto presentTime = nextPredictedPresentTime(vsyncId);
-    if (!presentTime.has_value()) {
-        return false;
-    }
-
-    if (std::abs(*presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
-        return false;
-    }
-
-    return *presentTime >= expectedPresentTime &&
-            *presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
-}
-
 bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 5fed79f..b8d3f12 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -189,10 +189,6 @@
     // specific logic
     virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
 
-    // Returns true if the next frame is considered too early to present
-    // at the given expectedPresentTime
-    bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
-
     std::atomic<bool> mAutoRefresh{false};
     std::atomic<bool> mSidebandStreamChanged{false};
 
@@ -236,13 +232,6 @@
     std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-
-    // Returns the predicted present time of the next frame if available
-    virtual std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const = 0;
-
-    // The amount of time SF can delay a frame if it is considered early based
-    // on the VsyncModulator::VsyncConfig::appWorkDuration
-    static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index c0ce84f..e7f373f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -215,21 +215,6 @@
     return mQueuedFrames > 0;
 }
 
-std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime(int64_t /*vsyncId*/) const {
-    Mutex::Autolock lock(mQueueItemLock);
-    if (mQueueItems.empty()) {
-        return std::nullopt;
-    }
-
-    const auto& bufferData = mQueueItems[0];
-
-    if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
-        return std::nullopt;
-    }
-
-    return bufferData.surfaceFrame->getPredictions().presentTime;
-}
-
 status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                           nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 3a34b95..b3b7948 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -117,8 +117,6 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
-    std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const override;
-
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b8aad61..a974dc4 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -653,16 +653,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime(int64_t vsyncId) const {
-    const auto prediction =
-            mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
-    if (!prediction.has_value()) {
-        return std::nullopt;
-    }
-
-    return prediction->presentTime;
-}
-
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
                                           nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
@@ -702,7 +692,6 @@
         // are processing the next state.
         addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
                                           mDrawingState.acquireFence->getSignalTime(), latchTime);
-        bufferSurfaceFrame.reset();
     }
 
     mCurrentStateModified = false;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 036e8d2..7a3da6f 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -156,8 +156,6 @@
 
     bool bufferNeedsFiltering() const override;
 
-    std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const override;
-
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f4a2a3f..b7b2cc6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -354,8 +354,20 @@
     return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
 }
 
-const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
-    return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+void DisplayDevice::overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes) {
+    mOverrideHdrTypes = hdrTypes;
+}
+
+HdrCapabilities DisplayDevice::getHdrCapabilities() const {
+    const HdrCapabilities& capabilities =
+            mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+    std::vector<ui::Hdr> hdrTypes = capabilities.getSupportedHdrTypes();
+    if (!mOverrideHdrTypes.empty()) {
+        hdrTypes = mOverrideHdrTypes;
+    }
+    return HdrCapabilities(hdrTypes, capabilities.getDesiredMaxLuminance(),
+                           capabilities.getDesiredMaxAverageLuminance(),
+                           capabilities.getDesiredMinLuminance());
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7156613..68846d3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -126,13 +126,15 @@
     bool hasHLGSupport() const;
     bool hasDolbyVisionSupport() const;
 
+    void overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes);
+
     // The returned HdrCapabilities is the combination of HDR capabilities from
     // hardware composer and RenderEngine. When the DisplayDevice supports wide
     // color gamut, RenderEngine is able to simulate HDR support in Display P3
     // color space for both PQ and HLG HDR contents. The minimum and maximum
     // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
     // respectively if hardware composer doesn't return meaningful values.
-    const HdrCapabilities& getHdrCapabilities() const;
+    HdrCapabilities getHdrCapabilities() const;
 
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
@@ -217,6 +219,8 @@
     const bool mIsPrimary;
 
     std::optional<DeviceProductInfo> mDeviceProductInfo;
+
+    std::vector<ui::Hdr> mOverrideHdrTypes;
 };
 
 struct DisplayDeviceState {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b1dff8d..2784861 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -348,6 +348,11 @@
     mRenderRate = renderRate;
 }
 
+void SurfaceFrame::setGpuComposition() {
+    std::scoped_lock lock(mMutex);
+    mGpuComposition = true;
+}
+
 std::optional<int32_t> SurfaceFrame::getJankType() const {
     std::scoped_lock lock(mMutex);
     if (mPresentState == PresentState::Dropped) {
@@ -828,10 +833,14 @@
 }
 
 void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
-                                 const std::shared_ptr<FenceTime>& presentFence) {
+                                 const std::shared_ptr<FenceTime>& presentFence,
+                                 bool gpuComposition) {
     ATRACE_CALL();
     std::scoped_lock lock(mMutex);
     mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
+    if (gpuComposition) {
+        mCurrentDisplayFrame->setGpuComposition();
+    }
     mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
     flushPendingPresentFences();
     finalizeCurrentDisplayFrame();
@@ -869,6 +878,10 @@
     mSurfaceFlingerActuals.endTime = actualEndTime;
 }
 
+void FrameTimeline::DisplayFrame::setGpuComposition() {
+    mGpuComposition = true;
+}
+
 void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
     if (mPredictionState == PredictionState::Expired ||
         mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 3cf35f0..3f04592 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -180,6 +180,7 @@
     void setDropTime(nsecs_t dropTime);
     void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
     void setRenderRate(Fps renderRate);
+    void setGpuComposition();
 
     // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
     // isBuffer.
@@ -292,11 +293,11 @@
     // the token and sets the actualSfWakeTime for the current DisplayFrame.
     virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0;
 
-    // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
-    // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
-    // that vsync.
-    virtual void setSfPresent(nsecs_t sfPresentTime,
-                              const std::shared_ptr<FenceTime>& presentFence) = 0;
+    // Sets the sfPresentTime, gpuComposition and finalizes the current DisplayFrame. Tracks the
+    // given present fence until it's signaled, and updates the present timestamps of all presented
+    // SurfaceFrames in that vsync.
+    virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+                              bool gpuComposition) = 0;
 
     // Args:
     // -jank : Dumps only the Display Frames that are either janky themselves
@@ -375,6 +376,7 @@
         void setPredictions(PredictionState predictionState, TimelineItem predictions);
         void setActualStartTime(nsecs_t actualStartTime);
         void setActualEndTime(nsecs_t actualEndTime);
+        void setGpuComposition();
 
         // BaseTime is the smallest timestamp in a DisplayFrame.
         // Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -444,8 +446,8 @@
             int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
-    void setSfPresent(nsecs_t sfPresentTime,
-                      const std::shared_ptr<FenceTime>& presentFence) override;
+    void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+                      bool gpuComposition = false) override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
     float computeFps(const std::unordered_set<int32_t>& layerIds) override;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 85ff479..1c5d6ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -930,10 +930,6 @@
 
     pid_t getOwnerPid() { return mOwnerPid; }
 
-    virtual bool frameIsEarly(nsecs_t /*expectedPresentTime*/, int64_t /*vsyncId*/) const {
-        return false;
-    }
-
     // This layer is not a clone, but it's the parent to the cloned hierarchy. The
     // variable mClonedChild represents the top layer that will be cloned so this
     // layer will be the parent of mClonedChild.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index e06bc88..d0032ac 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -176,7 +176,7 @@
         mScheduler(scheduler),
         mTunables(tunables),
         mIdleTimer(
-                "RegionSamplingIdleTimer",
+                "RegSampIdle",
                 std::chrono::duration_cast<std::chrono::milliseconds>(
                         mTunables.mSamplingTimerTimeout),
                 [] {}, [this] { checkForStaleLuma(); }),
@@ -184,7 +184,7 @@
                                                                 tunables.mSamplingDuration)),
         lastSampleTime(0ns) {
     mThread = std::thread([this]() { threadMain(); });
-    pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+    pthread_setname_np(mThread.native_handle(), "RegionSampling");
     mIdleTimer.start();
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f8b25f7..525b043 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1287,6 +1287,21 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
+                                          const std::vector<ui::Hdr>& hdrTypes) {
+    Mutex::Autolock lock(mStateLock);
+
+    auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+
+    display->overrideHdrTypes(hdrTypes);
+    dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                                ui::PixelFormat* outFormat,
                                                                ui::Dataspace* outDataspace,
@@ -2111,7 +2126,8 @@
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
     mFrameTimeline->setSfPresent(systemTime(),
-                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]),
+                                 glCompositionDoneFenceTime != FenceTime::NO_FENCE);
 
     nsecs_t dequeueReadyTime = systemTime();
     for (const auto& layer : mLayersWithQueuedFrames) {
@@ -3380,6 +3396,28 @@
     return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
 }
 
+bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
+    // The amount of time SF can delay a frame if it is considered early based
+    // on the VsyncModulator::VsyncConfig::appWorkDuration
+    constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
+
+    const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
+    const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
+
+    const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
+    if (!prediction.has_value()) {
+        return false;
+    }
+
+    if (std::abs(prediction->presentTime - expectedPresentTime) >=
+        kEarlyLatchMaxThreshold.count()) {
+        return false;
+    }
+
+    return prediction->presentTime >= expectedPresentTime &&
+            prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
+}
+
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
@@ -3400,6 +3438,13 @@
         ready = false;
     }
 
+    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
+    // present time of this transaction.
+    if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
+        ATRACE_NAME("frameIsEarly");
+        ready = false;
+    }
+
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
         const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
@@ -3421,13 +3466,6 @@
 
         ATRACE_NAME(layer->getName().c_str());
 
-        const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
-        const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
-        if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
-            ATRACE_NAME("frameIsEarly()");
-            ready = false;
-        }
-
         if (acquireFenceChanged) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
             // transaction in the queue.
@@ -3954,12 +3992,6 @@
             flags |= eTraversalNeeded;
         }
     }
-    FrameTimelineInfo info;
-    if (what & layer_state_t::eFrameTimelineInfoChanged) {
-        info = s.frameTimelineInfo;
-    } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
-        info = frameTimelineInfo;
-    }
     if (what & layer_state_t::eFixedTransformHintChanged) {
         if (layer->setFixedTransformHint(s.fixedTransformHint)) {
             flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
@@ -4022,12 +4054,12 @@
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
         if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
-                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info,
+                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
                              s.releaseBufferListener)) {
             flags |= eTraversalNeeded;
         }
-    } else if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
-        layer->setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+    } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+        layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
@@ -4997,6 +5029,7 @@
         case DESTROY_DISPLAY:
         case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
+        case OVERRIDE_HDR_TYPES:
         case GET_HDR_CAPABILITIES:
         case SET_DESIRED_DISPLAY_MODE_SPECS:
         case GET_DESIRED_DISPLAY_MODE_SPECS:
@@ -5013,9 +5046,11 @@
         case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
-            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
-            // necessary permission dynamically. Don't use the permission cache for this check.
-            bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
+            // which acquire the necessary permission dynamically. Don't use the permission cache
+            // for this check.
+            bool usePermissionCache =
+                    code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
             if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 95777ba..e4ff6c9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -642,6 +642,8 @@
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
     status_t clearAnimationFrameStats() override;
     status_t getAnimationFrameStats(FrameStats* outStats) const override;
+    status_t overrideHdrTypes(const sp<IBinder>& displayToken,
+                              const std::vector<ui::Hdr>& hdrTypes) override;
     status_t enableVSyncInjections(bool enable) override;
     status_t injectVSync(nsecs_t when) override;
     status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
@@ -844,6 +846,7 @@
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
+    bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
     /*
      * Layer management
      */
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 100354a..974ae84 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -1070,6 +1070,9 @@
     mTimeStats.renderEngineTimingLegacy.hist.clear();
     mTimeStats.refreshRateStatsLegacy.clear();
     mPowerTime.prevTime = systemTime();
+    for (auto& globalRecord : mTimeStats.stats) {
+        globalRecord.second.clearGlobals();
+    }
     mGlobalRecord.prevPresentTime = 0;
     mGlobalRecord.presentFences.clear();
     ALOGD("Cleared global stats");
@@ -1079,7 +1082,10 @@
     ATRACE_CALL();
 
     mTimeStatsTracker.clear();
-    mTimeStats.stats.clear();
+
+    for (auto& globalRecord : mTimeStats.stats) {
+        globalRecord.second.stats.clear();
+    }
     ALOGD("Cleared layer stats");
 }
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 4556bad..5ee28ce 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -127,6 +127,12 @@
         Histogram displayDeadlineDeltas;
         Histogram displayPresentDeltas;
         std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
+
+        void clearGlobals() {
+            jankPayload = {};
+            displayDeadlineDeltas = {};
+            displayPresentDeltas = {};
+        }
     };
 
     class TimeStatsGlobal {
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index f010786..fa1a5ed 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -114,34 +114,5 @@
     ASSERT_EQ(results.result, results2.result);
 }
 
-/**
- * Parcel a layer_state_t struct, and then unparcel. Ensure that the object that was parceled
- * matches the object that's unparceled.
- */
-TEST(LayerStateTest, ParcelUnparcelLayerStateT) {
-    layer_state_t input;
-    input.frameTimelineInfo.vsyncId = 1;
-    input.frameTimelineInfo.inputEventId = 2;
-    Parcel p;
-    input.write(p);
-    layer_state_t output;
-    p.setDataPosition(0);
-    output.read(p);
-    ASSERT_EQ(input.frameTimelineInfo.vsyncId, output.frameTimelineInfo.vsyncId);
-    ASSERT_EQ(input.frameTimelineInfo.inputEventId, output.frameTimelineInfo.inputEventId);
-}
-
-TEST(LayerStateTest, LayerStateMerge_SelectsValidInputEvent) {
-    layer_state_t layer1;
-    layer1.frameTimelineInfo.inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID;
-    layer_state_t layer2;
-    layer2.frameTimelineInfo.inputEventId = 1;
-    layer2.what |= layer_state_t::eFrameTimelineInfoChanged;
-
-    layer1.merge(layer2);
-
-    ASSERT_EQ(1, layer1.frameTimelineInfo.inputEventId);
-}
-
 } // namespace test
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index e903664..a72ab42 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -48,6 +48,7 @@
 using testing::Contains;
 using testing::HasSubstr;
 using testing::InSequence;
+using testing::Not;
 using testing::SizeIs;
 using testing::StrEq;
 using testing::UnorderedElementsAre;
@@ -855,6 +856,24 @@
     EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
     EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
     EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -1074,6 +1093,46 @@
     EXPECT_EQ(0, globalProto.missed_frames());
     EXPECT_EQ(0, globalProto.client_composition_frames());
     EXPECT_EQ(0, globalProto.present_to_present_size());
+
+    // also check dump-only stats: expect that global stats are indeed dropped but there should
+    // still be stats for the layer
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "totalTimelineFrames = " + std::to_string(8);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(7);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrames = " + std::to_string(2);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
@@ -1205,6 +1264,31 @@
     ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
 
     EXPECT_EQ(0, globalProto.stats_size());
+
+    // also check dump-only stats: expect that layer stats are indeed dropped but there should still
+    // be global stats
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(8);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(7);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrames = " + std::to_string(2);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    std::string expectedMissing = "uid = " + std::to_string(UID_0);
+    EXPECT_THAT(result, Not(HasSubstr(expectedMissing)));
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index b9d1794..5707978 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -32,7 +32,7 @@
     MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
     MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
-    MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD3(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&, bool));
     MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
 };
 
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 2de2e0e..c1795f5 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -87,45 +87,6 @@
 
 // -------------------------------------------------------------------------------------------------
 
-static constexpr int MAX_RETRIES = 1;
-
-template <typename T>
-HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
-    if (result.isFailed()) {
-        ALOGE("%s failed: %s", functionName, result.errorMessage());
-        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
-        mConnectedHal->tryReconnect();
-    }
-    return result;
-}
-
-template <typename T>
-HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
-    std::shared_ptr<HalWrapper> hal = nullptr;
-    {
-        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
-        if (mConnectedHal == nullptr) {
-            // Init was never called, so connect to HAL for the first time during this call.
-            mConnectedHal = mConnector(mCallbackScheduler);
-
-            if (mConnectedHal == nullptr) {
-                ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
-                return HalResult<T>::unsupported();
-            }
-        }
-        hal = mConnectedHal;
-    }
-
-    HalResult<T> ret = processHalResult(halFn(hal), functionName);
-    for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
-        ret = processHalResult(halFn(hal), functionName);
-    }
-
-    return ret;
-}
-
-// -------------------------------------------------------------------------------------------------
-
 bool HalController::init() {
     std::lock_guard<std::mutex> lock(mConnectedHalMutex);
     if (mConnectedHal == nullptr) {
@@ -134,11 +95,6 @@
     return mConnectedHal != nullptr;
 }
 
-HalResult<void> HalController::ping() {
-    hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
-    return apply(pingFn, "ping");
-}
-
 void HalController::tryReconnect() {
     std::lock_guard<std::mutex> lock(mConnectedHalMutex);
     if (mConnectedHal == nullptr) {
@@ -148,96 +104,6 @@
     }
 }
 
-HalResult<void> HalController::on(milliseconds timeout,
-                                  const std::function<void()>& completionCallback) {
-    hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->on(timeout, completionCallback);
-    };
-    return apply(onFn, "on");
-}
-
-HalResult<void> HalController::off() {
-    hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
-    return apply(offFn, "off");
-}
-
-HalResult<void> HalController::setAmplitude(int32_t amplitude) {
-    hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->setAmplitude(amplitude);
-    };
-    return apply(setAmplitudeFn, "setAmplitude");
-}
-
-HalResult<void> HalController::setExternalControl(bool enabled) {
-    hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->setExternalControl(enabled);
-    };
-    return apply(setExternalControlFn, "setExternalControl");
-}
-
-HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
-    hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->alwaysOnEnable(id, effect, strength);
-    };
-    return apply(alwaysOnEnableFn, "alwaysOnEnable");
-}
-
-HalResult<void> HalController::alwaysOnDisable(int32_t id) {
-    hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->alwaysOnDisable(id);
-    };
-    return apply(alwaysOnDisableFn, "alwaysOnDisable");
-}
-
-HalResult<Capabilities> HalController::getCapabilities() {
-    hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getCapabilities();
-    };
-    return apply(getCapabilitiesFn, "getCapabilities");
-}
-
-HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
-    hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getSupportedEffects();
-    };
-    return apply(getSupportedEffectsFn, "getSupportedEffects");
-}
-
-HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
-    hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
-            [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
-    return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
-}
-
-HalResult<float> HalController::getResonantFrequency() {
-    hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getResonantFrequency();
-    };
-    return apply(getResonantFrequencyFn, "getResonantFrequency");
-}
-
-HalResult<float> HalController::getQFactor() {
-    hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
-    return apply(getQFactorFn, "getQFactor");
-}
-
-HalResult<milliseconds> HalController::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->performEffect(effect, strength, completionCallback);
-    };
-    return apply(performEffectFn, "performEffect");
-}
-
-HalResult<milliseconds> HalController::performComposedEffect(
-        const std::vector<CompositeEffect>& primitiveEffects,
-        const std::function<void()>& completionCallback) {
-    hal_fn<milliseconds> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->performComposedEffect(primitiveEffects, completionCallback);
-    };
-    return apply(performComposedEffectFn, "performComposedEffect");
-}
-
 }; // namespace vibrator
 
 }; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 3d20fa1..1010aa5 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -19,16 +19,19 @@
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <android/hardware/vibrator/IVibrator.h>
 #include <hardware/vibrator.h>
+#include <cmath>
 
 #include <utils/Log.h>
 
 #include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
+using android::hardware::vibrator::Braking;
 using android::hardware::vibrator::CompositeEffect;
 using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::PrimitivePwle;
 
 using std::chrono::milliseconds;
 
@@ -45,20 +48,6 @@
 // -------------------------------------------------------------------------------------------------
 
 template <class T>
-HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
-    if (cache.has_value()) {
-        // Return copy of cached value.
-        return HalResult<T>::ok(*cache);
-    }
-    HalResult<T> ret = loadFn();
-    if (ret.isOk()) {
-        // Cache copy of returned value.
-        cache.emplace(ret.value());
-    }
-    return ret;
-}
-
-template <class T>
 bool isStaticCastValid(Effect effect) {
     T castEffect = static_cast<T>(effect);
     auto iter = hardware::hidl_enum_range<T>();
@@ -133,6 +122,117 @@
 
 // -------------------------------------------------------------------------------------------------
 
+Info HalWrapper::getInfo() {
+    getCapabilities();
+    getPrimitiveDurations();
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mSupportedEffects.isFailed()) {
+        mInfoCache.mSupportedEffects = getSupportedEffectsInternal();
+    }
+    if (mInfoCache.mSupportedBraking.isFailed()) {
+        mInfoCache.mSupportedBraking = getSupportedBrakingInternal();
+    }
+    if (mInfoCache.mMinFrequency.isFailed()) {
+        mInfoCache.mMinFrequency = getMinFrequencyInternal();
+    }
+    if (mInfoCache.mResonantFrequency.isFailed()) {
+        mInfoCache.mResonantFrequency = getResonantFrequencyInternal();
+    }
+    if (mInfoCache.mFrequencyResolution.isFailed()) {
+        mInfoCache.mFrequencyResolution = getFrequencyResolutionInternal();
+    }
+    if (mInfoCache.mQFactor.isFailed()) {
+        mInfoCache.mQFactor = getQFactorInternal();
+    }
+    if (mInfoCache.mMaxAmplitudes.isFailed()) {
+        mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal();
+    }
+    return mInfoCache.get();
+}
+
+HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&,
+                                                          const std::function<void()>&) {
+    ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL");
+    return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&,
+                                              const std::function<void()>&) {
+    ALOGV("Skipped performPwleEffect because it's not available in Vibrator HAL");
+    return HalResult<void>::unsupported();
+}
+
+HalResult<Capabilities> HalWrapper::getCapabilities() {
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mCapabilities.isFailed()) {
+        mInfoCache.mCapabilities = getCapabilitiesInternal();
+    }
+    return mInfoCache.mCapabilities;
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurations() {
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mSupportedPrimitives.isFailed()) {
+        mInfoCache.mSupportedPrimitives = getSupportedPrimitivesInternal();
+        if (mInfoCache.mSupportedPrimitives.isUnsupported()) {
+            mInfoCache.mPrimitiveDurations = HalResult<std::vector<milliseconds>>::unsupported();
+        }
+    }
+    if (mInfoCache.mPrimitiveDurations.isFailed() && mInfoCache.mSupportedPrimitives.isOk()) {
+        mInfoCache.mPrimitiveDurations =
+                getPrimitiveDurationsInternal(mInfoCache.mSupportedPrimitives.value());
+    }
+    return mInfoCache.mPrimitiveDurations;
+}
+
+HalResult<std::vector<Effect>> HalWrapper::getSupportedEffectsInternal() {
+    ALOGV("Skipped getSupportedEffects because it's not available in Vibrator HAL");
+    return HalResult<std::vector<Effect>>::unsupported();
+}
+
+HalResult<std::vector<Braking>> HalWrapper::getSupportedBrakingInternal() {
+    ALOGV("Skipped getSupportedBraking because it's not available in Vibrator HAL");
+    return HalResult<std::vector<Braking>>::unsupported();
+}
+
+HalResult<std::vector<CompositePrimitive>> HalWrapper::getSupportedPrimitivesInternal() {
+    ALOGV("Skipped getSupportedPrimitives because it's not available in Vibrator HAL");
+    return HalResult<std::vector<CompositePrimitive>>::unsupported();
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurationsInternal(
+        const std::vector<CompositePrimitive>&) {
+    ALOGV("Skipped getPrimitiveDurations because it's not available in Vibrator HAL");
+    return HalResult<std::vector<milliseconds>>::unsupported();
+}
+
+HalResult<float> HalWrapper::getMinFrequencyInternal() {
+    ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getResonantFrequencyInternal() {
+    ALOGV("Skipped getResonantFrequency because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getFrequencyResolutionInternal() {
+    ALOGV("Skipped getFrequencyResolution because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getQFactorInternal() {
+    ALOGV("Skipped getQFactor because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() {
+    ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL");
+    return HalResult<std::vector<float>>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
 HalResult<void> AidlHalWrapper::ping() {
     return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
 }
@@ -168,9 +268,8 @@
     return HalResult<void>::fromStatus(getHal()->off());
 }
 
-HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
-    float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
-    return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
+HalResult<void> AidlHalWrapper::setAmplitude(float amplitude) {
+    return HalResult<void>::fromStatus(getHal()->setAmplitude(amplitude));
 }
 
 HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
@@ -185,37 +284,6 @@
     return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
 }
 
-HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
-    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
-                                    mCapabilities);
-}
-
-HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
-    std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
-    return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
-                                                     this),
-                                           mSupportedEffects);
-}
-
-HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
-    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
-    return loadCached<std::vector<
-            CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
-                                 mSupportedPrimitives);
-}
-
-HalResult<float> AidlHalWrapper::getResonantFrequency() {
-    std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
-    return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
-                             mResonantFrequency);
-}
-
-HalResult<float> AidlHalWrapper::getQFactor() {
-    std::lock_guard<std::mutex> lock(mQFactorMutex);
-    return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
-}
-
 HalResult<milliseconds> AidlHalWrapper::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     HalResult<Capabilities> capabilities = getCapabilities();
@@ -236,44 +304,32 @@
 }
 
 HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
-        const std::vector<CompositeEffect>& primitiveEffects,
+        const std::vector<CompositeEffect>& primitives,
         const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = new HalCallbackWrapper(completionCallback);
+
+    auto durations = getPrimitiveDurations().valueOr({});
     milliseconds duration(0);
-    for (const auto& effect : primitiveEffects) {
-        auto durationResult = getPrimitiveDuration(effect.primitive);
-        if (durationResult.isOk()) {
-            duration += durationResult.value();
+    for (const auto& effect : primitives) {
+        auto primitiveIdx = static_cast<size_t>(effect.primitive);
+        if (primitiveIdx < durations.size()) {
+            duration += durations[primitiveIdx];
+        } else {
+            // Make sure the returned duration is positive to indicate successful vibration.
+            duration += milliseconds(1);
         }
         duration += milliseconds(effect.delayMs);
     }
-    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitiveEffects, cb), duration);
+
+    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration);
 }
 
-HalResult<milliseconds> AidlHalWrapper::getPrimitiveDuration(CompositePrimitive primitive) {
-    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
-    if (mPrimitiveDurations.empty()) {
-        constexpr auto primitiveRange = enum_range<CompositePrimitive>();
-        constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
-        mPrimitiveDurations.resize(primitiveCount);
-    }
-    auto primitiveIdx = static_cast<size_t>(primitive);
-    if (primitiveIdx >= mPrimitiveDurations.size()) {
-        // Safety check, should not happen if enum_range is correct.
-        return HalResult<milliseconds>::unsupported();
-    }
-    auto& cache = mPrimitiveDurations[primitiveIdx];
-    if (cache.has_value()) {
-        return HalResult<milliseconds>::ok(*cache);
-    }
-    int32_t duration;
-    auto result = getHal()->getPrimitiveDuration(primitive, &duration);
-    if (result.isOk()) {
-        // Cache copy of returned value.
-        cache.emplace(duration);
-    }
-    return HalResult<milliseconds>::fromStatus(result, milliseconds(duration));
+HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives,
+                                                  const std::function<void()>& completionCallback) {
+    // This method should always support callbacks, so no need to double check.
+    auto cb = new HalCallbackWrapper(completionCallback);
+    return HalResult<void>::fromStatus(getHal()->composePwle(primitives, cb));
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
@@ -288,24 +344,72 @@
     return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
 }
 
+HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() {
+    std::vector<Braking> supportedBraking;
+    auto result = getHal()->getSupportedBraking(&supportedBraking);
+    return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking);
+}
+
 HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
     std::vector<CompositePrimitive> supportedPrimitives;
     auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
     return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
 }
 
+HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal(
+        const std::vector<CompositePrimitive>& supportedPrimitives) {
+    std::vector<milliseconds> durations;
+    constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+    constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+    durations.resize(primitiveCount);
+
+    for (auto primitive : supportedPrimitives) {
+        auto primitiveIdx = static_cast<size_t>(primitive);
+        if (primitiveIdx >= durations.size()) {
+            // Safety check, should not happen if enum_range is correct.
+            continue;
+        }
+        int32_t duration = 0;
+        auto status = getHal()->getPrimitiveDuration(primitive, &duration);
+        if (!status.isOk()) {
+            return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str());
+        }
+        durations[primitiveIdx] = milliseconds(duration);
+    }
+
+    return HalResult<std::vector<milliseconds>>::ok(durations);
+}
+
+HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
+    float minFrequency = 0;
+    auto result = getHal()->getFrequencyMinimum(&minFrequency);
+    return HalResult<float>::fromStatus(result, minFrequency);
+}
+
 HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
     float f0 = 0;
     auto result = getHal()->getResonantFrequency(&f0);
     return HalResult<float>::fromStatus(result, f0);
 }
 
+HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() {
+    float frequencyResolution = 0;
+    auto result = getHal()->getFrequencyResolution(&frequencyResolution);
+    return HalResult<float>::fromStatus(result, frequencyResolution);
+}
+
 HalResult<float> AidlHalWrapper::getQFactorInternal() {
     float qFactor = 0;
     auto result = getHal()->getQFactor(&qFactor);
     return HalResult<float>::fromStatus(result, qFactor);
 }
 
+HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() {
+    std::vector<float> amplitudes;
+    auto result = getHal()->getBandwidthAmplitudeMap(&amplitudes);
+    return HalResult<std::vector<float>>::fromStatus(result, amplitudes);
+}
+
 sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
@@ -346,8 +450,9 @@
 }
 
 template <typename I>
-HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
-    auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
+HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
+    uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
+    auto result = getHal()->setAmplitude(amp);
     return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
@@ -370,44 +475,6 @@
 }
 
 template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
-    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
-                                    mCapabilities);
-}
-
-template <typename I>
-HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
-    ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
-    return HalResult<std::vector<Effect>>::unsupported();
-}
-
-template <typename I>
-HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
-    ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
-    return HalResult<std::vector<CompositePrimitive>>::unsupported();
-}
-
-template <typename I>
-HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
-    ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
-    return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<float> HidlHalWrapper<I>::getQFactor() {
-    ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
-    return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
-        const std::vector<CompositeEffect>&, const std::function<void()>&) {
-    ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
-    return HalResult<std::chrono::milliseconds>::unsupported();
-}
-
-template <typename I>
 HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
     hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
     Capabilities capabilities =
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 8a08e5b..a9d499d 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -30,7 +30,8 @@
 const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
 
 HalResult<void> LegacyManagerHalWrapper::ping() {
-    return mController->ping();
+    auto pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+    return mController->doWithRetry<void>(pingFn, "ping");
 }
 
 void LegacyManagerHalWrapper::tryReconnect() {
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 0acff06..53f3daf 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -20,6 +20,10 @@
 #include <vibratorservice/VibratorHalController.h>
 
 using ::android::enum_range;
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::Effect;
+using ::android::hardware::vibrator::EffectStrength;
 using ::benchmark::Counter;
 using ::benchmark::Fixture;
 using ::benchmark::kMicrosecond;
@@ -33,7 +37,7 @@
 public:
     void SetUp(State& /*state*/) override { mController.init(); }
 
-    void TearDown(State& /*state*/) override { mController.off(); }
+    void TearDown(State& state) override { turnVibratorOff(state); }
 
     static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
 
@@ -46,8 +50,8 @@
 
     auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
 
-    bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result,
-                         vibrator::Capabilities&& query, State& state) {
+    bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+        auto result = mController.getInfo().capabilities;
         if (result.isFailed()) {
             state.SkipWithError(result.errorMessage());
             return false;
@@ -58,6 +62,10 @@
         return (result.value() & query) == query;
     }
 
+    void turnVibratorOff(State& state) {
+        checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state);
+    }
+
     template <class R>
     bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
         if (result.isFailed()) {
@@ -66,6 +74,12 @@
         }
         return true;
     }
+
+    template <class R>
+    vibrator::HalResult<R> halCall(vibrator::HalController& controller,
+                                   const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) {
+        return controller.doWithRetry<R>(halFn, "benchmark");
+    }
 };
 
 #define BENCHMARK_WRAPPER(fixt, test, code)                \
@@ -93,7 +107,7 @@
 BENCHMARK_WRAPPER(VibratorBench, ping, {
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.ping();
+        auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
         state.PauseTiming();
         checkHalResult(ret, state);
     }
@@ -111,10 +125,11 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.on(duration, callback);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
@@ -125,65 +140,66 @@
 
     for (auto _ : state) {
         state.PauseTiming();
-        if (!checkHalResult(mController.on(duration, callback), state)) {
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+        if (!checkHalResult(ret, state)) {
             continue;
         }
         state.ResumeTiming();
-        checkHalResult(mController.off(), state);
+        turnVibratorOff(state);
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
         return;
     }
 
     auto duration = 60s;
     auto callback = []() {};
-    auto amplitude = UINT8_MAX;
+    auto amplitude = 1.0f;
 
     for (auto _ : state) {
         state.PauseTiming();
         vibrator::HalController controller;
         controller.init();
-        if (!checkHalResult(controller.on(duration, callback), state)) {
+        auto result =
+                halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
+        if (!checkHalResult(result, state)) {
             continue;
         }
         state.ResumeTiming();
-        auto ret = controller.setAmplitude(amplitude);
+        auto ret =
+                halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(controller.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
         return;
     }
 
     auto duration = 6000s;
     auto callback = []() {};
-    auto amplitude = UINT8_MAX;
+    auto amplitude = 1.0f;
 
-    checkHalResult(mController.on(duration, callback), state);
+    auto onResult =
+            halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+    checkHalResult(onResult, state);
 
     for (auto _ : state) {
-        checkHalResult(mController.setAmplitude(amplitude), state);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+        checkHalResult(ret, state);
     }
-
-    checkHalResult(mController.off(), state);
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
         return;
     }
 
@@ -192,103 +208,85 @@
         vibrator::HalController controller;
         controller.init();
         state.ResumeTiming();
-        auto ret = controller.setExternalControl(true);
+        auto ret =
+                halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(controller.setExternalControl(false), state);
+            auto result = halCall<void>(controller,
+                                        [](auto hal) { return hal->setExternalControl(false); });
+            checkHalResult(result, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
         return;
     }
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.setExternalControl(true);
+        auto result =
+                halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
         state.PauseTiming();
-        if (checkHalResult(ret, state)) {
-            checkHalResult(mController.setExternalControl(false), state);
+        if (checkHalResult(result, state)) {
+            auto ret = halCall<void>(mController,
+                                     [](auto hal) { return hal->setExternalControl(false); });
+            checkHalResult(ret, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
         return;
     }
 
-    auto amplitude = UINT8_MAX;
+    auto amplitude = 1.0f;
 
-    checkHalResult(mController.setExternalControl(true), state);
+    auto onResult =
+            halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
+    checkHalResult(onResult, state);
 
     for (auto _ : state) {
-        checkHalResult(mController.setAmplitude(amplitude), state);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+        checkHalResult(ret, state);
     }
 
-    checkHalResult(mController.setExternalControl(false), state);
+    auto offResult =
+            halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
+    checkHalResult(offResult, state);
 });
 
-BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+BENCHMARK_WRAPPER(VibratorBench, getInfo, {
     for (auto _ : state) {
         state.PauseTiming();
         vibrator::HalController controller;
         controller.init();
         state.ResumeTiming();
-        checkHalResult(controller.getCapabilities(), state);
+        auto result = controller.getInfo();
+        checkHalResult(result.capabilities, state);
+        checkHalResult(result.supportedEffects, state);
+        checkHalResult(result.supportedPrimitives, state);
+        checkHalResult(result.primitiveDurations, state);
+        checkHalResult(result.resonantFrequency, state);
+        checkHalResult(result.qFactor, state);
     }
 });
 
-BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
     // First call to cache values.
-    checkHalResult(mController.getCapabilities(), state);
+    mController.getInfo();
 
     for (auto _ : state) {
-        checkHalResult(mController.getCapabilities(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
-    for (auto _ : state) {
-        state.PauseTiming();
-        vibrator::HalController controller;
-        controller.init();
-        state.ResumeTiming();
-        checkHalResult(controller.getSupportedEffects(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
-    // First call to cache values.
-    checkHalResult(mController.getSupportedEffects(), state);
-
-    for (auto _ : state) {
-        checkHalResult(mController.getSupportedEffects(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
-    for (auto _ : state) {
-        state.PauseTiming();
-        vibrator::HalController controller;
-        controller.init();
-        state.ResumeTiming();
-        checkHalResult(controller.getSupportedPrimitives(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
-    // First call to cache values.
-    checkHalResult(mController.getSupportedPrimitives(), state);
-
-    for (auto _ : state) {
-        checkHalResult(mController.getSupportedPrimitives(), state);
+        auto result = mController.getInfo();
+        checkHalResult(result.capabilities, state);
+        checkHalResult(result.supportedEffects, state);
+        checkHalResult(result.supportedPrimitives, state);
+        checkHalResult(result.primitiveDurations, state);
+        checkHalResult(result.resonantFrequency, state);
+        checkHalResult(result.qFactor, state);
     }
 });
 
@@ -296,12 +294,12 @@
 public:
     static void DefaultArgs(Benchmark* b) {
         vibrator::HalController controller;
-        auto effectsResult = controller.getSupportedEffects();
+        auto effectsResult = controller.getInfo().supportedEffects;
         if (!effectsResult.isOk()) {
             return;
         }
 
-        std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+        std::vector<Effect> supported = effectsResult.value();
         b->ArgNames({"Effect", "Strength"});
 
         if (supported.empty()) {
@@ -309,11 +307,11 @@
             return;
         }
 
-        for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+        for (const auto& effect : enum_range<Effect>()) {
             if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
                 continue;
             }
-            for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+            for (const auto& strength : enum_range<EffectStrength>()) {
                 b->Args({static_cast<long>(effect), static_cast<long>(strength)});
             }
         }
@@ -323,18 +321,16 @@
     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
 
     auto getEffect(const State& state) const {
-        return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+        return static_cast<Effect>(this->getOtherArg(state, 0));
     }
 
     auto getStrength(const State& state) const {
-        return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+        return static_cast<EffectStrength>(this->getOtherArg(state, 1));
     }
 };
 
 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
         return;
     }
     if (!hasArgs(state)) {
@@ -347,18 +343,20 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.alwaysOnEnable(id, effect, strength);
+        auto ret = halCall<void>(mController, [&](auto hal) {
+            return hal->alwaysOnEnable(id, effect, strength);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.alwaysOnDisable(id), state);
+            auto disableResult =
+                    halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+            checkHalResult(disableResult, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
         return;
     }
     if (!hasArgs(state)) {
@@ -371,11 +369,16 @@
 
     for (auto _ : state) {
         state.PauseTiming();
-        if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) {
+        auto enableResult = halCall<void>(mController, [&](auto hal) {
+            return hal->alwaysOnEnable(id, effect, strength);
+        });
+        if (!checkHalResult(enableResult, state)) {
             continue;
         }
         state.ResumeTiming();
-        checkHalResult(mController.alwaysOnDisable(id), state);
+        auto disableResult =
+                halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+        checkHalResult(disableResult, state);
     }
 });
 
@@ -390,10 +393,12 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.performEffect(effect, strength, callback);
+        auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+            return hal->performEffect(effect, strength, callback);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
@@ -402,12 +407,12 @@
 public:
     static void DefaultArgs(Benchmark* b) {
         vibrator::HalController controller;
-        auto primitivesResult = controller.getSupportedPrimitives();
+        auto primitivesResult = controller.getInfo().supportedPrimitives;
         if (!primitivesResult.isOk()) {
             return;
         }
 
-        std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+        std::vector<CompositePrimitive> supported = primitivesResult.value();
         b->ArgNames({"Primitive"});
 
         if (supported.empty()) {
@@ -415,11 +420,11 @@
             return;
         }
 
-        for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+        for (const auto& primitive : enum_range<CompositePrimitive>()) {
             if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
                 continue;
             }
-            if (primitive == hardware::vibrator::CompositePrimitive::NOOP) {
+            if (primitive == CompositePrimitive::NOOP) {
                 continue;
             }
             b->Args({static_cast<long>(primitive)});
@@ -430,35 +435,35 @@
     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
 
     auto getPrimitive(const State& state) const {
-        return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+        return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
     }
 };
 
 BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
         return;
     }
     if (!hasArgs(state)) {
         return;
     }
 
-    hardware::vibrator::CompositeEffect effect;
+    CompositeEffect effect;
     effect.primitive = getPrimitive(state);
     effect.scale = 1.0f;
     effect.delayMs = static_cast<int32_t>(0);
 
-    std::vector<hardware::vibrator::CompositeEffect> effects;
+    std::vector<CompositeEffect> effects;
     effects.push_back(effect);
     auto callback = []() {};
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.performComposedEffect(effects, callback);
+        auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+            return hal->performComposedEffect(effects, callback);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 14ec7b2..354e56c 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -29,19 +29,22 @@
 
 std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
 
+template <typename T>
+using HalFunction = std::function<T(std::shared_ptr<HalWrapper>)>;
+
 // Controller for Vibrator HAL handle.
 // This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
 // after each failed api call. This also ensures connecting to the service is thread-safe.
-class HalController : public HalWrapper {
+class HalController {
 public:
     using Connector =
             std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>;
 
     HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {}
     HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
-          : HalWrapper(std::move(callbackScheduler)),
-            mConnector(connector),
-            mConnectedHal(nullptr) {}
+          : mConnector(connector),
+            mConnectedHal(nullptr),
+            mCallbackScheduler(std::move(callbackScheduler)) {}
     virtual ~HalController() = default;
 
     /* Connects to the newest HAL version available, possibly waiting for the registered service to
@@ -51,53 +54,65 @@
      */
     virtual bool init();
 
-    /* reloads HAL service instance without waiting. This relies on the HAL version found by init()
+    /* Reloads HAL service instance without waiting. This relies on the HAL version found by init()
      * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
      */
-    virtual void tryReconnect() override;
+    virtual void tryReconnect();
 
-    virtual HalResult<void> ping() override;
-    HalResult<void> on(std::chrono::milliseconds timeout,
-                       const std::function<void()>& completionCallback) final override;
-    HalResult<void> off() final override;
+    /* Returns info loaded from the connected HAL. This allows partial results to be returned if any
+     * of the Info fields has failed, but also retried on any failure.
+     */
+    Info getInfo() {
+        static Info sDefaultInfo = InfoCache().get();
+        return apply<Info>([](std::shared_ptr<HalWrapper> hal) { return hal->getInfo(); },
+                           sDefaultInfo, "getInfo");
+    }
 
-    HalResult<void> setAmplitude(int32_t amplitude) final override;
-    HalResult<void> setExternalControl(bool enabled) final override;
-
-    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
-                                   hardware::vibrator::EffectStrength strength) final override;
-    HalResult<void> alwaysOnDisable(int32_t id) final override;
-
-    HalResult<Capabilities> getCapabilities() final override;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            final override;
-
-    HalResult<float> getResonantFrequency() final override;
-    HalResult<float> getQFactor() final override;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) final override;
-
-    HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) final override;
+    /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+     * result has failed. Parameter functionName is for logging purposes.
+     */
+    template <typename T>
+    HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) {
+        return apply(halFn, HalResult<T>::unsupported(), functionName);
+    }
 
 private:
+    static constexpr int MAX_RETRIES = 1;
+
     Connector mConnector;
     std::mutex mConnectedHalMutex;
     // Shared pointer to allow local copies to be used by different threads.
     std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+    // Shared pointer to allow copies to be passed to possible recreated mConnectedHal instances.
+    std::shared_ptr<CallbackScheduler> mCallbackScheduler;
 
+    /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+     * result has failed. Given default value is returned when no HAL is available, and given
+     * function name is for logging purposes.
+     */
     template <typename T>
-    HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+    T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) {
+        if (!init()) {
+            ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+            return defaultValue;
+        }
+        std::shared_ptr<HalWrapper> hal;
+        {
+            std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+            hal = mConnectedHal;
+        }
 
-    template <typename T>
-    using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+        for (int i = 0; i < MAX_RETRIES; i++) {
+            T result = halFn(hal);
+            if (result.checkAndLogFailure(functionName)) {
+                tryReconnect();
+            } else {
+                return result;
+            }
+        }
 
-    template <typename T>
-    HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+        return halFn(hal);
+    }
 };
 
 }; // namespace vibrator
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 039a2d9..8720d9d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -48,7 +48,7 @@
         if (status.isOk()) {
             return HalResult<T>::ok(data);
         }
-        return HalResult<T>::failed(std::string(status.toString8().c_str()));
+        return HalResult<T>::failed(status.toString8().c_str());
     }
     static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
 
@@ -61,10 +61,18 @@
 
     // This will throw std::bad_optional_access if this result is not ok.
     const T& value() const { return mValue.value(); }
+    const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); }
     bool isOk() const { return !mUnsupported && mValue.has_value(); }
     bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
+    bool checkAndLogFailure(const char* functionName) const {
+        if (isFailed()) {
+            ALOGE("%s failed: %s", functionName, errorMessage());
+            return true;
+        }
+        return false;
+    }
 
 private:
     std::optional<T> mValue;
@@ -96,6 +104,13 @@
     bool isFailed() const { return !mUnsupported && mFailed; }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
+    bool checkAndLogFailure(const char* functionName) const {
+        if (isFailed()) {
+            ALOGE("%s failed: %s", functionName, errorMessage());
+            return true;
+        }
+        return false;
+    }
 
 private:
     std::string mErrorMessage;
@@ -133,7 +148,8 @@
     EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
     EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
     COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
-    ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+    COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS,
+    ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL,
 };
 
 inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
@@ -156,6 +172,62 @@
 
 // -------------------------------------------------------------------------------------------------
 
+class Info {
+public:
+    const HalResult<Capabilities> capabilities;
+    const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects;
+    const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
+    const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
+    const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
+    const HalResult<float> minFrequency;
+    const HalResult<float> resonantFrequency;
+    const HalResult<float> frequencyResolution;
+    const HalResult<float> qFactor;
+    const HalResult<std::vector<float>> maxAmplitudes;
+
+    bool checkAndLogFailure(const char*) const {
+        return capabilities.checkAndLogFailure("getCapabilities") ||
+                supportedEffects.checkAndLogFailure("getSupportedEffects") ||
+                supportedBraking.checkAndLogFailure("getSupportedBraking") ||
+                supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
+                primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
+                minFrequency.checkAndLogFailure("getMinFrequency") ||
+                resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
+                frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
+                qFactor.checkAndLogFailure("getQFactor") ||
+                maxAmplitudes.checkAndLogFailure("getMaxAmplitudes");
+    }
+};
+
+class InfoCache {
+public:
+    Info get() {
+        return {mCapabilities,        mSupportedEffects,    mSupportedBraking,
+                mSupportedPrimitives, mPrimitiveDurations,  mMinFrequency,
+                mResonantFrequency,   mFrequencyResolution, mQFactor,
+                mMaxAmplitudes};
+    }
+
+private:
+    static const constexpr char* MSG = "never loaded";
+    HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects =
+            HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking =
+            HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives =
+            HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
+    HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
+            HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+    HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
+    HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
+    HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
+    HalResult<float> mQFactor = HalResult<float>::failed(MSG);
+    HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG);
+
+    friend class HalWrapper;
+};
+
 // Wrapper for Vibrator HAL handlers.
 class HalWrapper {
 public:
@@ -168,37 +240,57 @@
      */
     virtual void tryReconnect() = 0;
 
+    Info getInfo();
+
     virtual HalResult<void> ping() = 0;
     virtual HalResult<void> on(std::chrono::milliseconds timeout,
                                const std::function<void()>& completionCallback) = 0;
     virtual HalResult<void> off() = 0;
 
-    virtual HalResult<void> setAmplitude(int32_t amplitude) = 0;
+    virtual HalResult<void> setAmplitude(float amplitude) = 0;
     virtual HalResult<void> setExternalControl(bool enabled) = 0;
 
     virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
                                            hardware::vibrator::EffectStrength strength) = 0;
     virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
 
-    virtual HalResult<Capabilities> getCapabilities() = 0;
-    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
-    virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
-    getSupportedPrimitives() = 0;
-
-    virtual HalResult<float> getResonantFrequency() = 0;
-    virtual HalResult<float> getQFactor() = 0;
-
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) = 0;
 
     virtual HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) = 0;
+            const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+            const std::function<void()>& completionCallback);
+
+    virtual HalResult<void> performPwleEffect(
+            const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+            const std::function<void()>& completionCallback);
 
 protected:
     // Shared pointer to allow CallbackScheduler to outlive this wrapper.
     const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+
+    // Load and cache vibrator info, returning cached result is present.
+    HalResult<Capabilities> getCapabilities();
+    HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurations();
+
+    // Request vibrator info to HAL skipping cache.
+    virtual HalResult<Capabilities> getCapabilitiesInternal() = 0;
+    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+    virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal();
+    virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+    getSupportedPrimitivesInternal();
+    virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+            const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+    virtual HalResult<float> getMinFrequencyInternal();
+    virtual HalResult<float> getResonantFrequencyInternal();
+    virtual HalResult<float> getFrequencyResolutionInternal();
+    virtual HalResult<float> getQFactorInternal();
+    virtual HalResult<std::vector<float>> getMaxAmplitudesInternal();
+
+private:
+    std::mutex mInfoMutex;
+    InfoCache mInfoCache GUARDED_BY(mInfoMutex);
 };
 
 // Wrapper for the AIDL Vibrator HAL.
@@ -223,59 +315,45 @@
                        const std::function<void()>& completionCallback) override final;
     HalResult<void> off() override final;
 
-    HalResult<void> setAmplitude(int32_t amplitude) override final;
+    HalResult<void> setAmplitude(float amplitude) override final;
     HalResult<void> setExternalControl(bool enabled) override final;
 
     HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
                                    hardware::vibrator::EffectStrength strength) override final;
     HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    HalResult<Capabilities> getCapabilities() override final;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            override final;
-
-    HalResult<float> getResonantFrequency() override final;
-    HalResult<float> getQFactor() override final;
-
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) override final;
 
     HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+            const std::vector<hardware::vibrator::CompositeEffect>& primitives,
             const std::function<void()>& completionCallback) override final;
 
+    HalResult<void> performPwleEffect(
+            const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+            const std::function<void()>& completionCallback) override final;
+
+protected:
+    HalResult<Capabilities> getCapabilitiesInternal() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final;
+    HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal()
+            override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal()
+            override final;
+    HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+            const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
+            override final;
+    HalResult<float> getMinFrequencyInternal() override final;
+    HalResult<float> getResonantFrequencyInternal() override final;
+    HalResult<float> getFrequencyResolutionInternal() override final;
+    HalResult<float> getQFactorInternal() override final;
+    HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
+
 private:
     const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn;
     std::mutex mHandleMutex;
-    std::mutex mCapabilitiesMutex;
-    std::mutex mSupportedEffectsMutex;
-    std::mutex mSupportedPrimitivesMutex;
-    std::mutex mResonantFrequencyMutex;
-    std::mutex mQFactorMutex;
     sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
-    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
-    std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
-            GUARDED_BY(mSupportedEffectsMutex);
-    std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
-            GUARDED_BY(mSupportedPrimitivesMutex);
-    std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
-            GUARDED_BY(mSupportedPrimitivesMutex);
-    std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
-    std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
-
-    // Loads and caches from IVibrator.
-    HalResult<std::chrono::milliseconds> getPrimitiveDuration(
-            hardware::vibrator::CompositePrimitive primitive);
-
-    // Loads directly from IVibrator handle, skipping caches.
-    HalResult<Capabilities> getCapabilitiesInternal();
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
-
-    HalResult<float> getResonantFrequencyInternal();
-    HalResult<float> getQFactorInternal();
 
     sp<hardware::vibrator::IVibrator> getHal();
 };
@@ -295,33 +373,18 @@
                        const std::function<void()>& completionCallback) override final;
     HalResult<void> off() override final;
 
-    HalResult<void> setAmplitude(int32_t amplitude) override final;
+    HalResult<void> setAmplitude(float amplitude) override final;
     virtual HalResult<void> setExternalControl(bool enabled) override;
 
     HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
                                    hardware::vibrator::EffectStrength strength) override final;
     HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    HalResult<Capabilities> getCapabilities() override final;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            override final;
-
-    HalResult<float> getResonantFrequency() override final;
-    HalResult<float> getQFactor() override final;
-
-    HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) override final;
-
 protected:
     std::mutex mHandleMutex;
-    std::mutex mCapabilitiesMutex;
     sp<I> mHandle GUARDED_BY(mHandleMutex);
-    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
 
-    // Loads directly from IVibrator handle, skipping the mCapabilities cache.
-    virtual HalResult<Capabilities> getCapabilitiesInternal();
+    virtual HalResult<Capabilities> getCapabilitiesInternal() override;
 
     template <class T>
     using perform_fn =
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 0c39247..279496a 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -31,8 +31,6 @@
 
 #include "test_utils.h"
 
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 
@@ -42,7 +40,11 @@
 using namespace std::chrono_literals;
 using namespace testing;
 
-static constexpr int MAX_ATTEMPTS = 2;
+static const auto ON_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) {
+    return hal->on(10ms, []() {});
+};
+static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+static const auto PING_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); };
 
 // -------------------------------------------------------------------------------------------------
 
@@ -58,26 +60,16 @@
                 (milliseconds timeout, const std::function<void()>& completionCallback),
                 (override));
     MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
-    MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override));
+    MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override));
     MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
     MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
                 (int32_t id, Effect effect, EffectStrength strength), (override));
     MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
-    MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
-    MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
-    MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
-                (override));
-
-    MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
-    MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
-
     MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
                 (Effect effect, EffectStrength strength,
                  const std::function<void()>& completionCallback),
                 (override));
-    MOCK_METHOD(vibrator::HalResult<milliseconds>, performComposedEffect,
-                (const std::vector<CompositeEffect>& primitiveEffects,
-                 const std::function<void()>& completionCallback),
+    MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
                 (override));
 
     vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
@@ -104,64 +96,6 @@
     int32_t mConnectCounter;
     std::shared_ptr<MockHalWrapper> mMockHal;
     std::unique_ptr<vibrator::HalController> mController;
-
-    void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
-                            vibrator::HalResult<void> voidResult,
-                            vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
-                            vibrator::HalResult<std::vector<Effect>> effectsResult,
-                            vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
-                            vibrator::HalResult<float> resonantFrequencyResult,
-                            vibrator::HalResult<float> qFactorResult,
-                            vibrator::HalResult<milliseconds> durationResult) {
-        EXPECT_CALL(*mMockHal.get(), ping())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), off())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(),
-                    alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), getCapabilities())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(capabilitiesResult));
-        EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(effectsResult));
-        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(primitivesResult));
-        EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(resonantFrequencyResult));
-        EXPECT_CALL(*mMockHal.get(), getQFactor())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(qFactorResult));
-        EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(durationResult));
-        EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(durationResult));
-
-        if (cardinality > 1) {
-            // One reconnection call after each failure.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
-        }
-    }
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -175,127 +109,52 @@
     ASSERT_EQ(1, mConnectCounter);
 }
 
+TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+    EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
+            .Times(Exactly(2))
+            .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message")))
+            .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok(
+                    vibrator::Capabilities::ON_CALLBACK)));
+
+    auto result = mController->getInfo();
+    ASSERT_FALSE(result.capabilities.isFailed());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
 TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
-    std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
-    std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
-                                                  CompositePrimitive::THUD};
-    constexpr float F0 = 123.f;
-    constexpr float Q_FACTOR = 12.f;
-    const std::vector<CompositeEffect> compositeEffects =
-            {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
-             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
+    EXPECT_CALL(*mMockHal.get(), on(_, _))
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
 
-    setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
-                       vibrator::HalResult<vibrator::Capabilities>::ok(
-                               vibrator::Capabilities::ON_CALLBACK),
-                       vibrator::HalResult<std::vector<Effect>>::ok(effects),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
-                       vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
-                       vibrator::HalResult<milliseconds>::ok(100ms));
-
-    ASSERT_TRUE(mController->ping().isOk());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
-    ASSERT_TRUE(mController->off().isOk());
-    ASSERT_TRUE(mController->setAmplitude(255).isOk());
-    ASSERT_TRUE(mController->setExternalControl(true).isOk());
-    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
-
-    auto getCapabilitiesResult = mController->getCapabilities();
-    ASSERT_TRUE(getCapabilitiesResult.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
-
-    auto getSupportedEffectsResult = mController->getSupportedEffects();
-    ASSERT_TRUE(getSupportedEffectsResult.isOk());
-    ASSERT_EQ(effects, getSupportedEffectsResult.value());
-
-    auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
-    ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
-    ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
-
-    auto getResonantFrequencyResult = mController->getResonantFrequency();
-    ASSERT_TRUE(getResonantFrequencyResult.isOk());
-    ASSERT_EQ(F0, getResonantFrequencyResult.value());
-
-    auto getQFactorResult = mController->getQFactor();
-    ASSERT_TRUE(getQFactorResult.isOk());
-    ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
-
-    auto performEffectResult =
-            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
-    ASSERT_TRUE(performEffectResult.isOk());
-    ASSERT_EQ(100ms, performEffectResult.value());
-
-    auto performComposedEffectResult =
-            mController->performComposedEffect(compositeEffects, []() {});
-    ASSERT_TRUE(performComposedEffectResult.isOk());
-    ASSERT_EQ(100ms, performComposedEffectResult.value());
+    auto result = mController->doWithRetry<void>(ON_FN, "on");
+    ASSERT_TRUE(result.isOk());
 
     ASSERT_EQ(1, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
-    setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
-                       vibrator::HalResult<void>::unsupported(),
-                       vibrator::HalResult<vibrator::Capabilities>::unsupported(),
-                       vibrator::HalResult<std::vector<Effect>>::unsupported(),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
-                       vibrator::HalResult<float>::unsupported(),
-                       vibrator::HalResult<float>::unsupported(),
-                       vibrator::HalResult<milliseconds>::unsupported());
+    EXPECT_CALL(*mMockHal.get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported()));
 
     ASSERT_EQ(0, mConnectCounter);
-
-    ASSERT_TRUE(mController->ping().isUnsupported());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
-    ASSERT_TRUE(mController->off().isUnsupported());
-    ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
-    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(
-            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
-    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mController->getQFactor().isUnsupported());
-    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
-                        .isUnsupported());
-    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
-                        .isUnsupported());
-
+    auto result = mController->doWithRetry<void>(OFF_FN, "off");
+    ASSERT_TRUE(result.isUnsupported());
     ASSERT_EQ(1, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
-    setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
-                       vibrator::HalResult<void>::failed("message"),
-                       vibrator::HalResult<vibrator::Capabilities>::failed("message"),
-                       vibrator::HalResult<std::vector<Effect>>::failed("message"),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
-                       vibrator::HalResult<float>::failed("message"),
-                       vibrator::HalResult<float>::failed("message"),
-                       vibrator::HalResult<milliseconds>::failed("message"));
+    EXPECT_CALL(*mMockHal.get(), on(_, _))
+            .Times(Exactly(2))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
 
     ASSERT_EQ(0, mConnectCounter);
 
-    ASSERT_TRUE(mController->ping().isFailed());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
-    ASSERT_TRUE(mController->off().isFailed());
-    ASSERT_TRUE(mController->setAmplitude(255).isFailed());
-    ASSERT_TRUE(mController->setExternalControl(true).isFailed());
-    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
-    ASSERT_TRUE(mController->getCapabilities().isFailed());
-    ASSERT_TRUE(mController->getSupportedEffects().isFailed());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
-    ASSERT_TRUE(mController->getResonantFrequency().isFailed());
-    ASSERT_TRUE(mController->getQFactor().isFailed());
-    ASSERT_TRUE(
-            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
-    ASSERT_TRUE(
-            mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
-
+    auto result = mController->doWithRetry<void>(ON_FN, "on");
+    ASSERT_TRUE(result.isFailed());
     ASSERT_EQ(1, mConnectCounter);
 }
 
@@ -312,7 +171,9 @@
     }
 
     ASSERT_EQ(0, mConnectCounter);
-    ASSERT_TRUE(mController->ping().isOk());
+
+    auto result = mController->doWithRetry<void>(PING_FN, "ping");
+    ASSERT_TRUE(result.isOk());
     ASSERT_EQ(1, mConnectCounter);
 }
 
@@ -325,7 +186,10 @@
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+        threads.push_back(std::thread([&]() {
+            auto result = mController->doWithRetry<void>(PING_FN, "ping");
+            ASSERT_TRUE(result.isOk());
+        }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
@@ -341,33 +205,17 @@
     });
     ASSERT_EQ(0, mConnectCounter);
 
-    ASSERT_FALSE(mController->init());
-    ASSERT_TRUE(mController->ping().isUnsupported());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
-    ASSERT_TRUE(mController->off().isUnsupported());
-    ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
-    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(
-            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
-    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mController->getQFactor().isUnsupported());
-    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
-                        .isUnsupported());
-    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
-                        .isUnsupported());
+    ASSERT_TRUE(mController->doWithRetry<void>(OFF_FN, "off").isUnsupported());
+    ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isUnsupported());
 
     // One connection attempt per api call.
-    ASSERT_EQ(15, mConnectCounter);
+    ASSERT_EQ(2, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+        EXPECT_CALL(*mMockHal.get(), on(_, _))
                 .Times(Exactly(1))
                 .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
                     mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
@@ -380,14 +228,14 @@
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
-        EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
     }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
-    ASSERT_TRUE(mController->on(10ms, callback).isOk());
-    ASSERT_TRUE(mController->ping().isFailed());
+    auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(10ms, callback); };
+    ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
+    ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
     mMockHal.reset();
     ASSERT_EQ(0, *callbackCounter.get());
 
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 5d77595..d1db82b 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -31,12 +31,14 @@
 
 using android::binder::Status;
 
+using android::hardware::vibrator::Braking;
 using android::hardware::vibrator::CompositeEffect;
 using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 using android::hardware::vibrator::IVibrator;
 using android::hardware::vibrator::IVibratorCallback;
+using android::hardware::vibrator::PrimitivePwle;
 
 using namespace android;
 using namespace std::chrono_literals;
@@ -74,11 +76,19 @@
     MOCK_METHOD(Status, compose,
                 (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
                 (override));
+    MOCK_METHOD(Status, composePwle,
+                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
     MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
     MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
     MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
     MOCK_METHOD(Status, getQFactor, (float * ret), (override));
     MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
+    MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+    MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
+    MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
+    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
     MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -215,19 +225,19 @@
 TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(
                         Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2)))
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
     }
 
-    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
-    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
-    ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+    ASSERT_TRUE(mWrapper->setAmplitude(0.1f).isOk());
+    ASSERT_TRUE(mWrapper->setAmplitude(0.2f).isUnsupported());
+    ASSERT_TRUE(mWrapper->setAmplitude(0.5f).isFailed());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
@@ -289,204 +299,143 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) {
+    constexpr float F_MIN = 100.f;
+    constexpr float F0 = 123.f;
+    constexpr float F_RESOLUTION = 0.5f;
+    constexpr float Q_FACTOR = 123.f;
+    std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
+    std::vector<Braking> supportedBraking = {Braking::CLAB};
+    std::vector<float> amplitudes = {0.f, 1.f, 0.f};
+
+    std::vector<std::chrono::milliseconds> primitiveDurations;
+    constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+    constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+    primitiveDurations.resize(primitiveCount);
+    primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms;
+
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .Times(Exactly(2))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
             .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status())));
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    vibrator::Info failed = mWrapper->getInfo();
+    ASSERT_TRUE(failed.capabilities.isFailed());
+    ASSERT_TRUE(failed.supportedEffects.isFailed());
+    ASSERT_TRUE(failed.supportedBraking.isFailed());
+    ASSERT_TRUE(failed.supportedPrimitives.isFailed());
+    ASSERT_TRUE(failed.primitiveDurations.isFailed());
+    ASSERT_TRUE(failed.minFrequency.isFailed());
+    ASSERT_TRUE(failed.resonantFrequency.isFailed());
+    ASSERT_TRUE(failed.frequencyResolution.isFailed());
+    ASSERT_TRUE(failed.qFactor.isFailed());
+    ASSERT_TRUE(failed.maxAmplitudes.isFailed());
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+    vibrator::Info successful = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
+    ASSERT_EQ(supportedEffects, successful.supportedEffects.value());
+    ASSERT_EQ(supportedBraking, successful.supportedBraking.value());
+    ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value());
+    ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value());
+    ASSERT_EQ(F_MIN, successful.minFrequency.value());
+    ASSERT_EQ(F0, successful.resonantFrequency.value());
+    ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
+    ASSERT_EQ(Q_FACTOR, successful.qFactor.value());
+    ASSERT_EQ(amplitudes, successful.maxAmplitudes.value());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
+    constexpr float F_MIN = 100.f;
+    constexpr float F0 = 123.f;
+    std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
-    std::vector<Effect> supportedEffects;
-    supportedEffects.push_back(Effect::CLICK);
-    supportedEffects.push_back(Effect::TICK);
-
-    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
-
-    auto result = mWrapper->getSupportedEffects();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
-    std::vector<Effect> supportedEffects;
-    supportedEffects.push_back(Effect::CLICK);
-    supportedEffects.push_back(Effect::TICK);
-
     EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getSupportedEffects();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(supportedEffects, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getSupportedEffects();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
-    std::vector<CompositePrimitive> supportedPrimitives;
-    supportedPrimitives.push_back(CompositePrimitive::CLICK);
-    supportedPrimitives.push_back(CompositePrimitive::THUD);
-
-    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
-
-    auto result = mWrapper->getSupportedPrimitives();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedPrimitives, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
-    std::vector<CompositePrimitive> supportedPrimitives;
-    supportedPrimitives.push_back(CompositePrimitive::CLICK);
-    supportedPrimitives.push_back(CompositePrimitive::THUD);
-
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
     EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getSupportedPrimitives();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(supportedPrimitives, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getSupportedPrimitives();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedPrimitives, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
-    constexpr float F0 = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
-
-    auto result = mWrapper->getResonantFrequency();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
-    constexpr float F0 = 123.f;
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
     EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getResonantFrequency();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(F0, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getResonantFrequency();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
-    constexpr float Q_FACTOR = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
-    ASSERT_TRUE(mWrapper->getQFactor().isFailed());
-
-    auto result = mWrapper->getQFactor();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(Q_FACTOR, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
-    constexpr float Q_FACTOR = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+    EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getQFactor();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(Q_FACTOR, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getQFactor();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(Q_FACTOR, result.value());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, info.capabilities.value());
+    ASSERT_EQ(supportedEffects, info.supportedEffects.value());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_EQ(F_MIN, info.minFrequency.value());
+    ASSERT_EQ(F0, info.resonantFrequency.value());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
@@ -569,6 +518,9 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK,
+                                                           CompositePrimitive::SPIN,
+                                                           CompositePrimitive::THUD};
     std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
     singleEffect.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
@@ -579,24 +531,26 @@
 
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
-
+                .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
-
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
+
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -622,38 +576,91 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAndIgnoresFailures) {
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::SPIN,
+                                                           CompositePrimitive::THUD};
     std::vector<CompositeEffect> multipleEffects;
     multipleEffects.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 10ms, 0.5f));
     multipleEffects.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 100ms, 1.0f));
 
-    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
-    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
-            .Times(Exactly(2))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
-    EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
-            .Times(Exactly(3))
-            .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+                .Times(Exactly(2))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
     auto result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(111ms, result.value()); // Failed primitive duration counted as 0.
+    ASSERT_EQ(112ms, result.value()); // Failed primitive durations counted as 1.
     ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(113ms, result.value()); // Second fetch succeeds and returns primitive duration.
+    ASSERT_EQ(114ms, result.value()); // Second fetch succeeds and returns primitive duration.
     ASSERT_EQ(2, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(113ms, result.value()); // Cached durations not fetched again, same duration returned.
+    ASSERT_EQ(114ms, result.value()); // Cached durations not fetched again, same duration returned.
     ASSERT_EQ(3, *callbackCounter.get());
 }
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) {
+    std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+    multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+    multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _))
+                .Times(Exactly(2))
+                .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+        ;
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    auto result = mWrapper->performPwleEffect(emptyPrimitives, callback);
+    ASSERT_TRUE(result.isUnsupported());
+    // Callback not triggered on failure
+    ASSERT_EQ(0, *callbackCounter.get());
+
+    result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+    ASSERT_TRUE(result.isFailed());
+    // Callback not triggered for unsupported
+    ASSERT_EQ(0, *callbackCounter.get());
+
+    result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 06aa36f..96b2582 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -31,11 +31,13 @@
 
 namespace V1_0 = android::hardware::vibrator::V1_0;
 
+using android::hardware::vibrator::Braking;
 using android::hardware::vibrator::CompositeEffect;
 using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::PrimitivePwle;
 
 using namespace android;
 using namespace std::chrono_literals;
@@ -147,7 +149,7 @@
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1))))
                 .Times(Exactly(1))
                 .WillRepeatedly(
                         [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
@@ -168,10 +170,11 @@
                 });
     }
 
-    ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
-    ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
-    ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
-    ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+    auto maxAmplitude = std::numeric_limits<uint8_t>::max();
+    ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk());
+    ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported());
+    ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed());
+    ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
@@ -187,7 +190,7 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
             .Times(Exactly(2))
             .WillOnce([]() {
@@ -195,49 +198,52 @@
             })
             .WillRepeatedly([]() { return hardware::Return<bool>(true); });
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+    ASSERT_TRUE(info.supportedEffects.isUnsupported());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_TRUE(info.minFrequency.isUnsupported());
+    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
         return hardware::Return<bool>(false);
     });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
         return hardware::Return<bool>(true);
     });
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+    ASSERT_TRUE(info.supportedEffects.isUnsupported());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_TRUE(info.minFrequency.isUnsupported());
+    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
@@ -324,3 +330,18 @@
     // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
 }
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
+    std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+    multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+    multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
+
+    // No callback is triggered.
+    ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index 08652f4..a6f1a74 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -105,7 +105,7 @@
     ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -116,14 +116,12 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
     ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
                       vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
-              result.value());
+              mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -134,12 +132,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -150,12 +146,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -166,12 +160,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -190,11 +182,11 @@
                 });
     }
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -207,20 +199,15 @@
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -247,22 +234,16 @@
     }
 
     // Call to supportsAmplitudeControl failed.
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
     // Call to supportsExternalControl failed.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
     // Returns successful result from third call.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 
     // Returns cached successful result.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index bcfd15d..3de1576 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -27,6 +27,7 @@
 
 using android::binder::Status;
 
+using android::hardware::vibrator::Braking;
 using android::hardware::vibrator::CompositeEffect;
 using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
@@ -34,10 +35,13 @@
 using android::hardware::vibrator::IVibrator;
 using android::hardware::vibrator::IVibratorCallback;
 using android::hardware::vibrator::IVibratorManager;
+using android::hardware::vibrator::PrimitivePwle;
 
 using namespace android;
 using namespace testing;
 
+static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+
 class MockBinder : public BBinder {
 public:
     MOCK_METHOD(status_t, linkToDeath,
@@ -68,11 +72,19 @@
     MOCK_METHOD(Status, compose,
                 (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
                 (override));
+    MOCK_METHOD(Status, composePwle,
+                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
     MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
     MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
     MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
     MOCK_METHOD(Status, getQFactor, (float * ret), (override));
     MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
+    MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+    MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
+    MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
+    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
     MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -245,12 +257,11 @@
                             Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))))
             .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
 
-    EXPECT_CALL(*mMockVibrator.get(), getCapabilities(_))
+    EXPECT_CALL(*mMockVibrator.get(), off())
             .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(Return(Status()));
 
     // Get vibrator controller is successful even if first getVibrator.
     auto result = mWrapper->getVibrator(kVibratorId);
@@ -260,10 +271,10 @@
     auto vibrator = result.value();
     // First getVibrator call fails.
     ASSERT_FALSE(vibrator->init());
-    // First and second getCapabilities calls fail, reload IVibrator with getVibrator.
-    ASSERT_FALSE(vibrator->getCapabilities().isOk());
-    // Third call to getCapabilities worked after IVibrator reloaded.
-    ASSERT_TRUE(vibrator->getCapabilities().isOk());
+    // First and second off() calls fail, reload IVibrator with getVibrator.
+    ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isFailed());
+    // Third call to off() worked after IVibrator reloaded.
+    ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isOk());
 }
 
 TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 6c2aabb..0850ef3 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -41,7 +41,6 @@
     virtual ~MockHalController() = default;
 
     MOCK_METHOD(bool, init, (), (override));
-    MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
     MOCK_METHOD(void, tryReconnect, (), (override));
 };
 
@@ -63,13 +62,9 @@
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
-    EXPECT_CALL(*mMockController.get(), ping())
-            .Times(Exactly(2))
-            .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
-            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+    EXPECT_CALL(*mMockController.get(), init()).Times(Exactly(1)).WillOnce(Return(false));
 
-    ASSERT_TRUE(mWrapper->ping().isFailed());
-    ASSERT_TRUE(mWrapper->ping().isOk());
+    ASSERT_TRUE(mWrapper->ping().isUnsupported());
 }
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
@@ -85,8 +80,7 @@
 }
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
-    std::vector<int32_t> expectedIds;
-    expectedIds.push_back(0);
+    std::vector<int> expectedIds = {0};
 
     EXPECT_CALL(*mMockController.get(), init())
             .Times(Exactly(2))
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 8d0b22e..1933a11 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -25,8 +25,12 @@
 
 namespace vibrator {
 
+using ::android::hardware::vibrator::ActivePwle;
+using ::android::hardware::vibrator::Braking;
+using ::android::hardware::vibrator::BrakingPwle;
 using ::android::hardware::vibrator::CompositeEffect;
 using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::PrimitivePwle;
 
 // -------------------------------------------------------------------------------------------------
 
@@ -53,6 +57,25 @@
         return effect;
     }
 
+    static PrimitivePwle createActivePwle(float startAmplitude, float startFrequency,
+                                          float endAmplitude, float endFrequency,
+                                          std::chrono::milliseconds duration) {
+        ActivePwle pwle;
+        pwle.startAmplitude = startAmplitude;
+        pwle.endAmplitude = endAmplitude;
+        pwle.startFrequency = startFrequency;
+        pwle.endFrequency = endFrequency;
+        pwle.duration = duration.count();
+        return pwle;
+    }
+
+    static PrimitivePwle createBrakingPwle(Braking braking, std::chrono::milliseconds duration) {
+        BrakingPwle pwle;
+        pwle.braking = braking;
+        pwle.duration = duration.count();
+        return pwle;
+    }
+
     static std::function<void()> createCountingCallback(int32_t* counter) {
         return [counter]() { *counter += 1; };
     }