blob: c2d2aa9bd366951dc4a8cfc13289b9d6529da126 [file] [log] [blame]
/*
* Copyright (C) 2025 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 <gtest/gtest.h>
#include <aidl/android/hardware/weaver/IWeaver.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <chrono>
#include <csignal>
#include <future>
#include <string>
#include <thread>
using aidl::android::hardware::weaver::IWeaver;
using aidl::android::hardware::weaver::WeaverConfig;
constexpr char SECURE_ELEMENT_SERVICE_NAME[]
= "android.se.omapi.ISecureElementService/default";
constexpr char WEAVER_SERVICE_NAME[] = "android.hardware.weaver.IWeaver/default";
// Helper function to run a command and capture its standard output.
// Returns true on success (exit code 0), false otherwise.
static bool RunCommand(const std::string& command, std::string& stdout_str) {
stdout_str.clear();
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
PLOG(ERROR) << "Failed to popen command: " << command;
return false;
}
if (!android::base::ReadFdToString(fileno(pipe), &stdout_str)) {
LOG(ERROR) << "Failed to read command output.";
return false;
}
int status = pclose(pipe);
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
// Gets the PID of the Secure Element service using dumpsys.
// Returns the PID as an integer, or <=0 if the service is not running or on error.
static int GetSecureElementServicePid() {
std::string pid_str;
const std::string command = "dumpsys --pid " + std::string(SECURE_ELEMENT_SERVICE_NAME);
if (RunCommand(command, pid_str)) {
pid_str.erase(pid_str.find_last_not_of(" \n\r\t") + 1);
if (!pid_str.empty() && std::all_of(pid_str.begin(), pid_str.end(), ::isdigit)) {
return atoi(pid_str.c_str());
}
}
return -1;
}
// Verifies that the Weaver HAL is responsive by calling its getConfig method.
// Returns an AssertionResult to indicate success or failure.
testing::AssertionResult CheckWeaverHalIsResponding() {
LOG(INFO) << "Checking if Weaver HAL is responding..";
ndk::SpAIBinder binder(AServiceManager_waitForService(WEAVER_SERVICE_NAME));
std::shared_ptr<IWeaver> weaver_service = IWeaver::fromBinder(binder);
if (weaver_service == nullptr) {
return testing::AssertionFailure() << "Failed to get Weaver AIDL service.";
}
WeaverConfig config;
ndk::ScopedAStatus status = weaver_service->getConfig(&config);
if (!status.isOk()) {
return testing::AssertionFailure()
<< "Weaver HAL's getConfig() returned an error: "
<< status.getMessage();
}
LOG(INFO) << "Weaver HAL is responding without errors.";
return testing::AssertionSuccess();
}
static void OnBinderDied(void* promise_cookie) {
LOG(INFO) << "Received a binder died notification for the service.";
// Set the promise the test is waiting on.
std::promise<void>* promise = static_cast<std::promise<void>*>(promise_cookie);
promise->set_value();
}
static void OnBinderUnlinked(void* promise_cookie) {
LOG(INFO) << "Cleaning-up the cookie on unlinking death recipient..";
delete static_cast<std::promise<void>*>(promise_cookie);
}
class WeaverKillSecureElementParamTest : public ::testing::TestWithParam<int> {};
TEST_P(WeaverKillSecureElementParamTest, WeaverRecoversAfterSecureElementServiceRestarts) {
SCOPED_TRACE(::testing::Message() << "Iteration " << GetParam());
LOG(INFO) << "Starting iteration " << GetParam();
// Get the initial PID of the Secure Element Service
const int initial_pid = GetSecureElementServicePid();
ASSERT_GT(initial_pid, 0) << "The Secure Element Service is not running.";
LOG(INFO) << "The Secure Element Service's PID: " << initial_pid;
// Register a death recipient with the Secure Element Service to confirm
// that the service triggers a death notification after it is killed
ndk::SpAIBinder se_binder(AServiceManager_getService(SECURE_ELEMENT_SERVICE_NAME));
ASSERT_NE(se_binder.get(), nullptr)
<< "Failed to get the Secure Element Service binder.";
// Use promise/future objects to communicate if a death notification is received.
// The death recipient will own and set the promise object while the test will
// wait on the associated future object.
std::promise<void> *death_promise = new std::promise<void>();
auto death_future = death_promise->get_future();
auto death_recipient = ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(OnBinderDied));
AIBinder_DeathRecipient_setOnUnlinked(death_recipient.get(), OnBinderUnlinked);
auto link_status = AIBinder_linkToDeath(se_binder.get(), death_recipient.get(),
static_cast<void*>(death_promise));
ASSERT_EQ(link_status, STATUS_OK) << "AIBinder_linkToDeath() failed.";
// Kill the Secure Element Service
LOG(INFO) << "Killing the Secure Element Service..";
ASSERT_EQ(kill(initial_pid, SIGKILL), 0) << "Failed to kill the service.";
// Verify that a death notification is received
LOG(INFO) << "Waiting for a death notification..";
const int kDeathNotificationTimeoutSec = 5;
auto future_status = death_future.wait_for(
std::chrono::seconds(kDeathNotificationTimeoutSec));
ASSERT_EQ(future_status, std::future_status::ready)
<< "Did not receive a death notification within "
<< kDeathNotificationTimeoutSec << " seconds.";
LOG(INFO) << "Successfully received a death notification.";
// Confirm that the Secure Element Service recovers with a new PID
int new_pid = 0;
const int kMaxRetries = 100; // 100 * 100ms = 10 seconds timeout
constexpr auto kRetryDelay = std::chrono::milliseconds(100);
for (int i = 0; i < kMaxRetries; ++i) {
std::this_thread::sleep_for(kRetryDelay);
new_pid = GetSecureElementServicePid();
if (new_pid > 0 && new_pid != initial_pid) {
break;
}
}
ASSERT_GT(new_pid, 0) << "The Secure Element Service did not restart.";
ASSERT_NE(new_pid, initial_pid)
<< "The Secure Element Service did not restart (has the same PID).";
LOG(INFO) << "The Secure Element Service recovered with new PID: " << new_pid;
// Check that the Weaver HAL is still responding, with a 5-second timeout.
// A small delay to allow Weaver to process the death notification.
std::this_thread::sleep_for(std::chrono::seconds(1));
const int kWeaverHalTimeoutSec = 5;
auto future = std::async(std::launch::async, CheckWeaverHalIsResponding);
if (future.wait_for(std::chrono::seconds(kWeaverHalTimeoutSec))
== std::future_status::timeout) {
FAIL() << "The Weaver HAL did not respond for too long (timeout="
<< kWeaverHalTimeoutSec << "seconds)";
} else {
ASSERT_TRUE(future.get());
}
}
INSTANTIATE_TEST_SUITE_P(Repeat5Times, WeaverKillSecureElementParamTest, ::testing::Range(1, 6));
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}