Merge changes from topic "health_storage_aidl"

* changes:
  health storage AIDL VTS test.
  health storage: refactor common code for test
  Add default impl for health storage AIDL HAL
  Add health.storage AIDL HAL
  Refactor common implementation for health storage HAL.
This commit is contained in:
Yifan Hong
2021-01-20 19:45:15 +00:00
committed by Gerrit Code Review
26 changed files with 936 additions and 146 deletions

View File

@@ -266,6 +266,14 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.health.storage</name>
<version>1</version>
<interface>
<name>IStorage</name>
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.identity</name>
<version>1-2</version>

View File

@@ -38,6 +38,7 @@ cc_binary {
],
static_libs: [
"libhealth_storage_impl_common",
"libfstab",
],

View File

@@ -18,11 +18,8 @@
#include <sstream>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fstab/fstab.h>
#include <health-storage-impl/common.h>
namespace android {
namespace hardware {
@@ -31,69 +28,9 @@ namespace storage {
namespace V1_0 {
namespace implementation {
using base::ReadFileToString;
using base::Timer;
using base::Trim;
using base::WriteStringToFd;
using base::WriteStringToFile;
using fs_mgr::Fstab;
using fs_mgr::ReadDefaultFstab;
std::string getGarbageCollectPath() {
Fstab fstab;
ReadDefaultFstab(&fstab);
for (const auto& entry : fstab) {
if (!entry.sysfs_path.empty()) {
return entry.sysfs_path + "/manual_gc";
}
}
return "";
}
Return<void> Storage::garbageCollect(uint64_t timeoutSeconds,
const sp<IGarbageCollectCallback>& cb) {
Result result = Result::SUCCESS;
std::string path = getGarbageCollectPath();
if (path.empty()) {
LOG(WARNING) << "Cannot find Dev GC path";
result = Result::UNKNOWN_ERROR;
} else {
Timer timer;
LOG(INFO) << "Start Dev GC on " << path;
while (1) {
std::string require;
if (!ReadFileToString(path, &require)) {
PLOG(WARNING) << "Reading manual_gc failed in " << path;
result = Result::IO_ERROR;
break;
}
require = Trim(require);
if (require == "" || require == "off" || require == "disabled") {
LOG(DEBUG) << "No more to do Dev GC";
break;
}
LOG(DEBUG) << "Trigger Dev GC on " << path;
if (!WriteStringToFile("1", path)) {
PLOG(WARNING) << "Start Dev GC failed on " << path;
result = Result::IO_ERROR;
break;
}
if (timer.duration() >= std::chrono::seconds(timeoutSeconds)) {
LOG(WARNING) << "Dev GC timeout";
// Timeout is not treated as an error. Try next time.
break;
}
sleep(2);
}
LOG(INFO) << "Stop Dev GC on " << path;
if (!WriteStringToFile("0", path)) {
PLOG(WARNING) << "Stop Dev GC failed on " << path;
result = Result::IO_ERROR;
}
}
Result result = GarbageCollect(timeoutSeconds);
if (cb != nullptr) {
auto ret = cb->onFinish(result);
@@ -110,28 +47,7 @@ Return<void> Storage::debug(const hidl_handle& handle, const hidl_vec<hidl_strin
}
int fd = handle->data[0];
std::stringstream output;
std::string path = getGarbageCollectPath();
if (path.empty()) {
output << "Cannot find Dev GC path";
} else {
std::string require;
if (ReadFileToString(path, &require)) {
output << path << ":" << require << std::endl;
}
if (WriteStringToFile("0", path)) {
output << "stop success" << std::endl;
}
}
if (!WriteStringToFd(output.str(), fd)) {
PLOG(WARNING) << "debug: cannot write to fd";
}
fsync(fd);
DebugDump(fd);
return Void();
}

View File

@@ -19,6 +19,9 @@ cc_test {
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalHealthStorageV1_0TargetTest.cpp"],
static_libs: ["android.hardware.health.storage@1.0"],
header_libs: [
"libhealth_storage_test_common_headers",
],
shared_libs: [
"libhidlbase",
],

View File

@@ -14,14 +14,17 @@
* limitations under the License.
*/
#include <unistd.h>
#include <thread>
#include <android-base/logging.h>
#include <android/hardware/health/storage/1.0/IStorage.h>
#include <gtest/gtest.h>
#include <health-storage-test/common.h>
#include <hidl/GtestPrinter.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/ServiceManagement.h>
#include <unistd.h>
#include <thread>
namespace android {
namespace hardware {
@@ -29,61 +32,17 @@ namespace health {
namespace storage {
namespace V1_0 {
using namespace ::android::hardware::health::storage::test;
using ::std::literals::chrono_literals::operator""ms;
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
// Dev GC timeout. This is the timeout used by vold.
const uint64_t kDevGcTimeoutSec = 120;
const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
// Dev GC timeout tolerance. The HAL may not immediately return after the
// timeout, so include an acceptable tolerance.
const std::chrono::seconds kDevGcTolerance{3};
// Time accounted for RPC calls.
const std::chrono::milliseconds kRpcTime{1000};
template <typename R>
std::string toString(std::chrono::duration<R, std::milli> time) {
return std::to_string(time.count()) + "ms";
}
/** An atomic boolean flag that indicates whether a task has finished. */
class Flag {
public:
void onFinish() {
std::unique_lock<std::mutex> lock(mMutex);
onFinishLocked(&lock);
}
template <typename R, typename P>
bool wait(std::chrono::duration<R, P> duration) {
std::unique_lock<std::mutex> lock(mMutex);
return waitLocked(&lock, duration);
}
protected:
/** Will unlock. */
void onFinishLocked(std::unique_lock<std::mutex>* lock) {
mFinished = true;
lock->unlock();
mCv.notify_all();
}
template <typename R, typename P>
bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
mCv.wait_for(*lock, duration, [this] { return mFinished; });
return mFinished;
}
bool mFinished{false};
std::mutex mMutex;
std::condition_variable mCv;
};
class GcCallback : public IGarbageCollectCallback, public Flag {
public:
public:
Return<void> onFinish(Result result) override {
std::unique_lock<std::mutex> lock(mMutex);
mResult = result;
Flag::onFinishLocked(&lock);
std::unique_lock<std::mutex> lock(mutex_);
result_ = result;
Flag::OnFinishLocked(&lock);
return Void();
}
@@ -93,13 +52,13 @@ class GcCallback : public IGarbageCollectCallback, public Flag {
*/
template <typename R, typename P>
void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
std::unique_lock<std::mutex> lock(mMutex);
ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
EXPECT_EQ(expected, mResult);
std::unique_lock<std::mutex> lock(mutex_);
ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
EXPECT_EQ(expected, result_);
}
private:
Result mResult{Result::UNKNOWN_ERROR};
private:
Result result_{Result::UNKNOWN_ERROR};
};
class HealthStorageHidlTest : public ::testing::TestWithParam<std::string> {
@@ -127,10 +86,10 @@ class HealthStorageHidlTest : public ::testing::TestWithParam<std::string> {
auto pingFlag = std::make_shared<Flag>();
std::thread([service, pingFlag] {
service->ping();
pingFlag->onFinish();
pingFlag->OnFinish();
})
.detach();
return pingFlag->wait(timeout);
return pingFlag->Wait(timeout);
}
sp<IStorage> fs;
@@ -147,7 +106,7 @@ TEST_P(HealthStorageHidlTest, GcNullCallback) {
// Hold test process because HAL can be single-threaded and doing GC.
ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
<< "Service must be available after "
<< toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
<< to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
}
/**

View File

@@ -0,0 +1,33 @@
// 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.
aidl_interface {
name: "android.hardware.health.storage",
vendor_available: true,
srcs: ["android/hardware/health/storage/*.aidl"],
stability: "vintf",
backend: {
cpp: {
enabled: false,
},
java: {
enabled: false,
},
ndk: {
vndk: {
enabled: true,
},
},
},
}

View File

@@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.health.storage;
@VintfStability
interface IGarbageCollectCallback {
oneway void onFinish(in android.hardware.health.storage.Result result);
}

View File

@@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.health.storage;
@VintfStability
interface IStorage {
oneway void garbageCollect(in long timeoutSeconds, in android.hardware.health.storage.IGarbageCollectCallback callback);
}

View File

@@ -0,0 +1,25 @@
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.health.storage;
@Backing(type="int") @VintfStability
enum Result {
SUCCESS = 0,
IO_ERROR = 1,
UNKNOWN_ERROR = 2,
}

View File

@@ -0,0 +1,34 @@
/*
* 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.
*/
package android.hardware.health.storage;
import android.hardware.health.storage.Result;
/**
* Callback interface to IStorage.garbageCollect.
*/
@VintfStability
interface IGarbageCollectCallback {
/**
* When garbage collection has finished, the implementation must
* invoke this function to indicate the result of the garbage collection.
*
* @param out result Execution result. See documentation for Result for
* details.
*/
oneway void onFinish(in Result result);
}

View File

@@ -0,0 +1,49 @@
/*
* 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.
*/
package android.hardware.health.storage;
import android.hardware.health.storage.IGarbageCollectCallback;
/**
* IStorage is an interface that provides operations on underlying storage
* devices, including flash memory.
*/
@VintfStability
interface IStorage {
/**
* Start garbage collection on the driver of storage devices.
*
* Garbage collection must be started at regular intervals when it is a good
* time for a longer-running cleanup tasks, roughly daily.
*
* When garbage collection finishes or encounters an error before the
* specified timeout, the implementation must call IGarbageCollect.finish
* immediately with appropriate result.
*
* If garbage collection does not finish within the specified timeout,
* the implementation must stop garbage collection, and must not call
* IGarbageCollect.finish.
*
* @param timeoutSeconds timeout in seconds. The implementation must
* return after the timeout is reached.
*
* @param callback callback interface. Callback must be null if the client
* does not need to receive any callbacks.
*
*/
oneway void garbageCollect(in long timeoutSeconds, in IGarbageCollectCallback callback);
}

View File

@@ -0,0 +1,37 @@
/*
* 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.
*/
package android.hardware.health.storage;
/**
* Status values for HAL methods.
*/
@VintfStability
@Backing(type="int")
enum Result {
/**
* Execution of the method is successful.
*/
SUCCESS = 0,
/**
* An IO error is encountered when the HAL communicates with the device.
*/
IO_ERROR,
/**
* An unknown error is encountered.
*/
UNKNOWN_ERROR,
}

View File

@@ -0,0 +1,53 @@
/*
* 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.
*/
cc_defaults {
name: "libhealth_storage_impl_defaults",
vendor: true,
shared_libs: [
"libbase",
"libbinder_ndk",
"android.hardware.health.storage-unstable-ndk_platform",
],
static_libs: [
"libfstab",
"libhealth_storage_impl_common",
],
}
cc_library_static {
name: "libhealth_storage_default_impl",
defaults: ["libhealth_storage_impl_defaults"],
srcs: [
"Storage.cpp",
],
visibility: [
":__subpackages__",
"//hardware/interfaces/tests/extension/health/storage:__subpackages__",
],
}
cc_binary {
name: "android.hardware.health.storage-service.default",
defaults: ["libhealth_storage_impl_defaults"],
relative_install_path: "hw",
init_rc: ["health-storage-default.rc"],
vintf_fragments: ["health-storage-default.xml"],
srcs: ["main.cpp"],
static_libs: [
"libhealth_storage_default_impl",
],
}

View File

@@ -0,0 +1,54 @@
/*
* 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 "Storage.h"
#include <sstream>
#include <android-base/logging.h>
#include <health-storage-impl/common.h>
using ::android::hardware::health::storage::DebugDump;
using ::android::hardware::health::storage::GarbageCollect;
using HResult = android::hardware::health::storage::V1_0::Result;
using AResult = aidl::android::hardware::health::storage::Result;
// Ensure static_cast<AResult>(any HResult) works
static_assert(static_cast<AResult>(HResult::SUCCESS) == AResult::SUCCESS);
static_assert(static_cast<AResult>(HResult::IO_ERROR) == AResult::IO_ERROR);
static_assert(static_cast<AResult>(HResult::UNKNOWN_ERROR) == AResult::UNKNOWN_ERROR);
namespace aidl::android::hardware::health::storage {
ndk::ScopedAStatus Storage::garbageCollect(
int64_t timeout_seconds, const std::shared_ptr<IGarbageCollectCallback>& callback) {
AResult result = static_cast<AResult>(GarbageCollect(static_cast<uint64_t>(timeout_seconds)));
if (callback != nullptr) {
auto status = callback->onFinish(result);
if (!status.isOk()) {
LOG(WARNING) << "Cannot return result " << toString(result)
<< " to callback: " << status.getDescription();
}
}
return ndk::ScopedAStatus::ok();
}
binder_status_t Storage::dump(int fd, const char**, uint32_t) {
DebugDump(fd);
return STATUS_OK;
}
} // namespace aidl::android::hardware::health::storage

View File

@@ -0,0 +1,30 @@
/*
* 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.
*/
#pragma once
#include <aidl/android/hardware/health/storage/BnStorage.h>
namespace aidl::android::hardware::health::storage {
class Storage : public BnStorage {
ndk::ScopedAStatus garbageCollect(
int64_t timeout_seconds,
const std::shared_ptr<IGarbageCollectCallback>& callback) override;
binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
};
} // namespace aidl::android::hardware::health::storage

View File

@@ -0,0 +1,7 @@
service vendor.health-storage-default /vendor/bin/hw/android.hardware.health.storage-service.default
interface aidl android.hardware.health.storage.IStorage/default
oneshot
disabled
class hal
user system
group system

View File

@@ -0,0 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.health.storage</name>
<version>1</version>
<fqname>IStorage/default</fqname>
</hal>
</manifest>

View File

@@ -0,0 +1,37 @@
/*
* 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 <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "Storage.h"
using aidl::android::hardware::health::storage::Storage;
using std::string_literals::operator""s;
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
// make a default storage service
auto storage = ndk::SharedRefBase::make<Storage>();
const std::string name = Storage::descriptor + "/default"s;
CHECK_EQ(STATUS_OK,
AServiceManager_registerLazyService(storage->asBinder().get(), name.c_str()));
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View File

@@ -0,0 +1,37 @@
// 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.
cc_test {
name: "VtsHalHealthStorageTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
"VtsHalHealthStorageTargetTest.cpp",
],
shared_libs: [
"libbinder_ndk",
],
static_libs: [
"android.hardware.health.storage-ndk_platform",
],
header_libs: [
"libhealth_storage_test_common_headers",
],
test_suites: [
"vts",
],
test_config: "VtsHalHealthStorageTargetTest.xml",
}

View File

@@ -0,0 +1,136 @@
/*
* 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 <unistd.h>
#include <chrono>
#include <set>
#include <string>
#include <thread>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/health/storage/BnGarbageCollectCallback.h>
#include <aidl/android/hardware/health/storage/IStorage.h>
#include <android-base/logging.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <health-storage-test/common.h>
namespace aidl::android::hardware::health::storage {
using namespace ::android::hardware::health::storage::test;
using std::chrono_literals::operator""ms;
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.getDescription()
#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) << ret.getDescription()
class GcCallback : public BnGarbageCollectCallback, public Flag {
public:
ndk::ScopedAStatus onFinish(Result result) override {
std::unique_lock<std::mutex> lock(mutex_);
result_ = result;
OnFinishLocked(&lock);
return ndk::ScopedAStatus::ok();
}
/**
* Wait for a specific "timeout". If GC has finished, test that the result
* is equal to the "expected" value.
*/
template <typename R, typename P>
void WaitForResult(std::chrono::duration<R, P> timeout, Result expected) {
std::unique_lock<std::mutex> lock(mutex_);
ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
EXPECT_EQ(expected, result_);
}
private:
Result result_{Result::UNKNOWN_ERROR};
};
class HealthStorageAidl : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
std::string name = GetParam();
ASSERT_TRUE(AServiceManager_isDeclared(name.c_str())) << name;
ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
ASSERT_NE(binder, nullptr);
storage_ = IStorage::fromBinder(binder);
ASSERT_NE(storage_, nullptr);
}
virtual void TearDown() override {
EXPECT_TRUE(ping(kRpcTime))
<< "Service is not responsive; expect subsequent tests to fail.";
}
/**
* Ping the service and expect it to return after "timeout". Return true
* iff the service is responsive within "timeout".
*/
template <typename R, typename P>
bool ping(std::chrono::duration<R, P> timeout) {
// Ensure the service is responsive after the test.
std::shared_ptr<IStorage> service = storage_;
auto ping_flag = std::make_shared<Flag>();
std::thread([service, ping_flag] {
EXPECT_EQ(STATUS_OK, AIBinder_ping(service->asBinder().get()));
ping_flag->OnFinish();
}).detach();
return ping_flag->Wait(timeout);
}
std::shared_ptr<IStorage> storage_;
};
/**
* Ensure garbage collection works on null callback.
*/
TEST_P(HealthStorageAidl, GcNullCallback) {
ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, nullptr));
// Hold test process because HAL can be single-threaded and doing GC.
ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
<< "Service must be available after "
<< to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
}
/**
* Ensure garbage collection works on non-null callback.
*/
TEST_P(HealthStorageAidl, GcNonNullCallback) {
std::shared_ptr<GcCallback> cb = ndk::SharedRefBase::make<GcCallback>();
ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, cb));
cb->WaitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthStorageAidl);
INSTANTIATE_TEST_SUITE_P(
HealthStorage, HealthStorageAidl,
testing::ValuesIn(::android::getAidlHalInstanceNames(IStorage::descriptor)),
::android::PrintInstanceNameToString);
} // namespace aidl::android::hardware::health::storage
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<configuration description="Runs VtsHalHealthStorageTargetTest.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="VtsHalHealthStorageTargetTest->/data/local/tmp/VtsHalHealthStorageTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalHealthStorageTargetTest" />
<option name="native-test-timeout" value="3m" />
</test>
</configuration>

View File

@@ -0,0 +1,47 @@
/*
* 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.
*/
// Common implementation between HIDL and AIDL HAL.
cc_library_static {
name: "libhealth_storage_impl_common",
vendor: true,
srcs: [
"impl_common.cpp",
],
export_include_dirs: [
"include",
],
cflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"libbase",
"libhidlbase",
"liblog",
"android.hardware.health.storage@1.0",
],
static_libs: [
"libfstab",
],
export_shared_lib_headers: [
"android.hardware.health.storage@1.0",
],
}

View File

@@ -0,0 +1,118 @@
/*
* 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 <health-storage-impl/common.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fstab/fstab.h>
using ::android::base::ReadFileToString;
using ::android::base::Timer;
using ::android::base::Trim;
using ::android::base::WriteStringToFd;
using ::android::base::WriteStringToFile;
using ::android::fs_mgr::Fstab;
using ::android::fs_mgr::ReadDefaultFstab;
using ::android::hardware::health::storage::V1_0::Result;
namespace android::hardware::health::storage {
static std::string GetGarbageCollectPath() {
Fstab fstab;
ReadDefaultFstab(&fstab);
for (const auto& entry : fstab) {
if (!entry.sysfs_path.empty()) {
return entry.sysfs_path + "/manual_gc";
}
}
return "";
}
Result GarbageCollect(uint64_t timeout_seconds) {
std::string path = GetGarbageCollectPath();
if (path.empty()) {
LOG(WARNING) << "Cannot find Dev GC path";
return Result::UNKNOWN_ERROR;
}
Result result = Result::SUCCESS;
Timer timer;
LOG(INFO) << "Start Dev GC on " << path;
while (1) {
std::string require;
if (!ReadFileToString(path, &require)) {
PLOG(WARNING) << "Reading manual_gc failed in " << path;
result = Result::IO_ERROR;
break;
}
require = Trim(require);
if (require == "" || require == "off" || require == "disabled") {
LOG(DEBUG) << "No more to do Dev GC";
break;
}
LOG(DEBUG) << "Trigger Dev GC on " << path;
if (!WriteStringToFile("1", path)) {
PLOG(WARNING) << "Start Dev GC failed on " << path;
result = Result::IO_ERROR;
break;
}
if (timer.duration() >= std::chrono::seconds(timeout_seconds)) {
LOG(WARNING) << "Dev GC timeout";
// Timeout is not treated as an error. Try next time.
break;
}
sleep(2);
}
LOG(INFO) << "Stop Dev GC on " << path;
if (!WriteStringToFile("0", path)) {
PLOG(WARNING) << "Stop Dev GC failed on " << path;
result = Result::IO_ERROR;
}
return result;
}
void DebugDump(int fd) {
std::stringstream output;
std::string path = GetGarbageCollectPath();
if (path.empty()) {
output << "Cannot find Dev GC path";
} else {
std::string require;
if (ReadFileToString(path, &require)) {
output << path << ":" << require << std::endl;
}
if (WriteStringToFile("0", path)) {
output << "stop success" << std::endl;
}
}
if (!WriteStringToFd(output.str(), fd)) {
PLOG(WARNING) << "debug: cannot write to fd";
}
fsync(fd);
}
} // namespace android::hardware::health::storage

View File

@@ -0,0 +1,31 @@
/*
* 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.
*/
#pragma once
#include <android/hardware/health/storage/1.0/types.h>
#include <string>
namespace android::hardware::health::storage {
// Run debug on fd
void DebugDump(int fd);
// Run garbage collection on GetGarbageCollectPath(). Blocks until garbage
// collect finishes or |timeout_seconds| has reached.
V1_0::Result GarbageCollect(uint64_t timeout_seconds);
} // namespace android::hardware::health::storage

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
cc_library_headers {
name: "libhealth_storage_test_common_headers",
export_include_dirs: ["include"],
}

View File

@@ -0,0 +1,69 @@
/*
* 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.
*/
#pragma once
#include <chrono>
#include <string>
namespace android::hardware::health::storage::test {
// Dev GC timeout. This is the timeout used by vold.
const uint64_t kDevGcTimeoutSec = 120;
const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
// Dev GC timeout tolerance. The HAL may not immediately return after the
// timeout, so include an acceptable tolerance.
const std::chrono::seconds kDevGcTolerance{3};
// Time accounted for RPC calls.
const std::chrono::milliseconds kRpcTime{1000};
template <typename R>
std::string to_string(std::chrono::duration<R, std::milli> time) {
return std::to_string(time.count()) + "ms";
}
/** An atomic boolean flag that indicates whether a task has finished. */
class Flag {
public:
void OnFinish() {
std::unique_lock<std::mutex> lock(mutex_);
OnFinishLocked(&lock);
}
template <typename R, typename P>
bool Wait(std::chrono::duration<R, P> duration) {
std::unique_lock<std::mutex> lock(mutex_);
return WaitLocked(&lock, duration);
}
protected:
/** Will unlock. */
void OnFinishLocked(std::unique_lock<std::mutex>* lock) {
finished_ = true;
lock->unlock();
cv_.notify_all();
}
template <typename R, typename P>
bool WaitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
cv_.wait_for(*lock, duration, [this] { return finished_; });
return finished_;
}
bool finished_{false};
std::mutex mutex_;
std::condition_variable cv_;
};
} // namespace android::hardware::health::storage::test