Merge "libsnapshot: Wait for daemon to terminate after snapshot unamp"
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index f66b01d..6d50fa4 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -283,15 +283,17 @@
srcs: [
"bootimg_utils.cpp",
+ "fastboot_driver.cpp",
"fastboot.cpp",
+ "filesystem.cpp",
"fs.cpp",
"socket.cpp",
+ "storage.cpp",
"super_flash_helper.cpp",
"tcp.cpp",
"udp.cpp",
"util.cpp",
"vendor_boot_img_utils.cpp",
- "fastboot_driver.cpp",
],
// Only version the final binaries
diff --git a/fastboot/filesystem.cpp b/fastboot/filesystem.cpp
new file mode 100644
index 0000000..a58ba00
--- /dev/null
+++ b/fastboot/filesystem.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _WIN32
+#include <android-base/utf8.h>
+#include <direct.h>
+#include <shlobj.h>
+#else
+#include <pwd.h>
+#endif
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "filesystem.h"
+
+namespace {
+
+int LockFile(int fd) {
+#ifdef _WIN32
+ HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
+ OVERLAPPED overlapped = {};
+ const BOOL locked = LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0,
+ MAXDWORD, MAXDWORD, &overlapped);
+ return locked ? 0 : -1;
+#else
+ return flock(fd, LOCK_EX);
+#endif
+}
+
+}
+
+// inspired by adb implementation:
+// cs.android.com/android/platform/superproject/+/master:packages/modules/adb/adb_utils.cpp;l=275
+std::string GetHomeDirPath() {
+#ifdef _WIN32
+ WCHAR path[MAX_PATH];
+ const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+ if (FAILED(hr)) {
+ return {};
+ }
+ std::string home_str;
+ if (!android::base::WideToUTF8(path, &home_str)) {
+ return {};
+ }
+ return home_str;
+#else
+ if (const char* const home = getenv("HOME")) {
+ return home;
+ }
+
+ struct passwd pwent;
+ struct passwd* result;
+ int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (pwent_max == -1) {
+ pwent_max = 16384;
+ }
+ std::vector<char> buf(pwent_max);
+ int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
+ if (rc == 0 && result) {
+ return result->pw_dir;
+ }
+#endif
+
+ return {};
+}
+
+bool FileExists(const std::string& path) {
+ return access(path.c_str(), F_OK) == 0;
+}
+
+bool EnsureDirectoryExists(const std::string& directory_path) {
+ const int result =
+#ifdef _WIN32
+ _mkdir(directory_path.c_str());
+#else
+ mkdir(directory_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+#endif
+
+ return result == 0 || errno == EEXIST;
+}
+
+FileLock::FileLock(const std::string& path) : fd_(open(path.c_str(), O_CREAT | O_WRONLY, 0644)) {
+ if (LockFile(fd_.get()) != 0) {
+ LOG(FATAL) << "Failed to acquire a lock on " << path;
+ }
+}
\ No newline at end of file
diff --git a/fastboot/filesystem.h b/fastboot/filesystem.h
new file mode 100644
index 0000000..3261496
--- /dev/null
+++ b/fastboot/filesystem.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <string>
+
+using android::base::unique_fd;
+
+// TODO(b/175635923): remove after enabling libc++fs for windows
+const char kPathSeparator =
+#ifdef _WIN32
+ '\\';
+#else
+ '/';
+#endif
+
+std::string GetHomeDirPath();
+bool EnsureDirectoryExists(const std::string& directory_path);
+
+class FileLock {
+ public:
+ FileLock() = delete;
+ FileLock(const std::string& path);
+
+ private:
+ unique_fd fd_;
+};
\ No newline at end of file
diff --git a/fastboot/storage.cpp b/fastboot/storage.cpp
new file mode 100644
index 0000000..db13dd6
--- /dev/null
+++ b/fastboot/storage.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <fstream>
+
+#include "storage.h"
+#include "util.h"
+
+ConnectedDevicesStorage::ConnectedDevicesStorage() {
+ const std::string home_path = GetHomeDirPath();
+ if (home_path.empty()) {
+ return;
+ }
+
+ const std::string home_fastboot_path = home_path + kPathSeparator + ".fastboot";
+
+ if (!EnsureDirectoryExists(home_fastboot_path)) {
+ LOG(FATAL) << "Cannot create directory: " << home_fastboot_path;
+ }
+
+ // We're using a separate file for locking because the Windows LockFileEx does not
+ // permit opening a file stream for the locked file, even within the same process. So,
+ // we have to use fd or handle API to manipulate the storage files, which makes it
+ // nearly impossible to fully rewrite a file content without having to recreate it.
+ // Unfortunately, this is not an option during holding a lock.
+ devices_path_ = home_fastboot_path + kPathSeparator + "devices";
+ devices_lock_path_ = home_fastboot_path + kPathSeparator + "devices.lock";
+}
+
+void ConnectedDevicesStorage::WriteDevices(const std::set<std::string>& devices) {
+ std::ofstream devices_stream(devices_path_);
+ std::copy(devices.begin(), devices.end(),
+ std::ostream_iterator<std::string>(devices_stream, "\n"));
+}
+
+std::set<std::string> ConnectedDevicesStorage::ReadDevices() {
+ std::ifstream devices_stream(devices_path_);
+ std::istream_iterator<std::string> start(devices_stream), end;
+ std::set<std::string> devices(start, end);
+ return devices;
+}
+
+void ConnectedDevicesStorage::Clear() {
+ if (!android::base::RemoveFileIfExists(devices_path_)) {
+ LOG(FATAL) << "Failed to clear connected device list: " << devices_path_;
+ }
+}
+
+FileLock ConnectedDevicesStorage::Lock() const {
+ return FileLock(devices_lock_path_);
+}
\ No newline at end of file
diff --git a/fastboot/storage.h b/fastboot/storage.h
new file mode 100644
index 0000000..1cce950
--- /dev/null
+++ b/fastboot/storage.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "filesystem.h"
+
+class ConnectedDevicesStorage {
+ public:
+ ConnectedDevicesStorage();
+ void WriteDevices(const std::set<std::string>& devices);
+ std::set<std::string> ReadDevices();
+ void Clear();
+
+ FileLock Lock() const;
+ private:
+ std::string devices_path_;
+ std::string devices_lock_path_;
+};
\ No newline at end of file
diff --git a/libstats/expresslog/Android.bp b/libstats/expresslog/Android.bp
index 9cdc2c3..004f8b9 100644
--- a/libstats/expresslog/Android.bp
+++ b/libstats/expresslog/Android.bp
@@ -18,11 +18,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_library {
- name: "libexpresslog",
+cc_defaults {
+ name: "expresslog_defaults",
srcs: [
"Counter.cpp",
+ "Histogram.cpp",
],
+}
+
+cc_library {
+ name: "libexpresslog",
+ defaults: ["expresslog_defaults"],
cflags: [
"-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
"-Wall",
@@ -37,6 +43,7 @@
],
shared_libs: [
"libbase",
+ "liblog",
"libstatssocket",
],
export_include_dirs: ["include"],
@@ -69,3 +76,38 @@
"libstatssocket",
],
}
+
+cc_test {
+ name: "expresslog_test",
+ defaults: ["expresslog_defaults"],
+ test_suites: [
+ "general-tests",
+ ],
+ srcs: [
+ "tests/Histogram_test.cpp",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
+ cflags: [
+ "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Wpedantic",
+ "-Werror",
+ ],
+ header_libs: [
+ "libtextclassifier_hash_headers",
+ ],
+ static_libs: [
+ "libgmock",
+ "libbase",
+ "liblog",
+ "libstatslog_express",
+ "libtextclassifier_hash_static",
+ ],
+ shared_libs: [
+ "libstatssocket",
+ ]
+}
diff --git a/libstats/expresslog/Histogram.cpp b/libstats/expresslog/Histogram.cpp
new file mode 100644
index 0000000..c90282d
--- /dev/null
+++ b/libstats/expresslog/Histogram.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "include/Histogram.h"
+
+#define LOG_TAG "tex"
+
+#include <log/log.h>
+#include <statslog_express.h>
+#include <string.h>
+#include <utils/hash/farmhash.h>
+
+namespace android {
+namespace expresslog {
+
+Histogram::UniformOptions* Histogram::UniformOptions::create(int binCount, float minValue,
+ float exclusiveMaxValue) {
+ if (binCount < 1) {
+ ALOGE("Bin count should be positive number");
+ return nullptr;
+ }
+
+ if (exclusiveMaxValue <= minValue) {
+ ALOGE("Bins range invalid (maxValue < minValue)");
+ return nullptr;
+ }
+
+ return new UniformOptions(binCount, minValue, exclusiveMaxValue);
+}
+
+Histogram::UniformOptions::UniformOptions(int binCount, float minValue, float exclusiveMaxValue)
+ : // Implicitly add 2 for the extra undeflow & overflow bins
+ mBinCount(binCount + 2),
+ mMinValue(minValue),
+ mExclusiveMaxValue(exclusiveMaxValue),
+ mBinSize((exclusiveMaxValue - minValue) / binCount) {
+}
+
+int Histogram::UniformOptions::getBinForSample(float sample) const {
+ if (sample < mMinValue) {
+ // goes to underflow
+ return 0;
+ } else if (sample >= mExclusiveMaxValue) {
+ // goes to overflow
+ return mBinCount - 1;
+ }
+ return (int)((sample - mMinValue) / mBinSize + 1);
+}
+
+Histogram::Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions)
+ : mMetricIdHash(farmhash::Fingerprint64(metricName, strlen(metricName))),
+ mBinOptions(std::move(binOptions)) {
+}
+
+void Histogram::logSample(float sample) const {
+ const int binIndex = mBinOptions->getBinForSample(sample);
+ stats_write(EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, /*count*/ 1, binIndex);
+}
+
+} // namespace expresslog
+} // namespace android
diff --git a/libstats/expresslog/include/Histogram.h b/libstats/expresslog/include/Histogram.h
new file mode 100644
index 0000000..aba2786
--- /dev/null
+++ b/libstats/expresslog/include/Histogram.h
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+#include <stdint.h>
+
+#include <memory>
+
+namespace android {
+namespace expresslog {
+
+/** Histogram encapsulates StatsD write API calls */
+class Histogram final {
+public:
+ class BinOptions {
+ public:
+ virtual ~BinOptions() = default;
+ /**
+ * Returns bins count to be used by a Histogram
+ *
+ * @return bins count used to initialize Options, including overflow & underflow bins
+ */
+ virtual int getBinsCount() const = 0;
+
+ /**
+ * @return zero based index
+ * Calculates bin index for the input sample value
+ * index == 0 stands for underflow
+ * index == getBinsCount() - 1 stands for overflow
+ */
+ virtual int getBinForSample(float sample) const = 0;
+ };
+
+ /** Used by Histogram to map data sample to corresponding bin for uniform bins */
+ class UniformOptions : public BinOptions {
+ UniformOptions(int binCount, float minValue, float exclusiveMaxValue);
+
+ public:
+ static UniformOptions* create(int binCount, float minValue, float exclusiveMaxValue);
+
+ int getBinsCount() const override {
+ return mBinCount;
+ }
+
+ int getBinForSample(float sample) const override;
+
+ private:
+ const int mBinCount;
+ const float mMinValue;
+ const float mExclusiveMaxValue;
+ const float mBinSize;
+ };
+
+ Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions);
+
+ /**
+ * Logs increment sample count for automatically calculated bin
+ */
+ void logSample(float sample) const;
+
+private:
+ const int64_t mMetricIdHash;
+ const std::shared_ptr<BinOptions> mBinOptions;
+};
+
+} // namespace expresslog
+} // namespace android
diff --git a/libstats/expresslog/tests/Histogram_test.cpp b/libstats/expresslog/tests/Histogram_test.cpp
new file mode 100644
index 0000000..813c997
--- /dev/null
+++ b/libstats/expresslog/tests/Histogram_test.cpp
@@ -0,0 +1,128 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "Histogram.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace expresslog {
+
+#ifdef __ANDROID__
+TEST(UniformOptions, getBinsCount) {
+ const std::shared_ptr<Histogram::UniformOptions> options1(
+ Histogram::UniformOptions::create(1, 100, 1000));
+ ASSERT_EQ(3, options1->getBinsCount());
+
+ const std::shared_ptr<Histogram::UniformOptions> options10(
+ Histogram::UniformOptions::create(10, 100, 1000));
+ ASSERT_EQ(12, options10->getBinsCount());
+}
+
+TEST(UniformOptions, constructZeroBinsCount) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(0, 100, 1000));
+ ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, constructNegativeBinsCount) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(-1, 100, 1000));
+ ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, constructMaxValueLessThanMinValue) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(10, 1000, 100));
+ ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual1) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(10, 1, 11));
+ for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {
+ ASSERT_EQ(i, options->getBinForSample(i));
+ }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual2) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(10, 1, 21));
+ for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {
+ ASSERT_EQ(i, options->getBinForSample(i * 2));
+ ASSERT_EQ(i, options->getBinForSample(i * 2 - 1));
+ }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual5) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(2, 0, 10));
+ ASSERT_EQ(4, options->getBinsCount());
+ for (int i = 0; i < 2; i++) {
+ for (int sample = 0; sample < 5; sample++) {
+ ASSERT_EQ(i + 1, options->getBinForSample(i * 5 + sample));
+ }
+ }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual10) {
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(10, 1, 101));
+ ASSERT_EQ(0, options->getBinForSample(0));
+ ASSERT_EQ(options->getBinsCount() - 2, options->getBinForSample(100));
+ ASSERT_EQ(options->getBinsCount() - 1, options->getBinForSample(101));
+
+ const float binSize = (101 - 1) / 10.f;
+ for (int i = 1, bins = options->getBinsCount() - 1; i < bins; i++) {
+ ASSERT_EQ(i, options->getBinForSample(i * binSize));
+ }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual90) {
+ const int binCount = 10;
+ const int minValue = 100;
+ const int maxValue = 100000;
+
+ const std::shared_ptr<Histogram::UniformOptions> options(
+ Histogram::UniformOptions::create(binCount, minValue, maxValue));
+
+ // logging underflow sample
+ ASSERT_EQ(0, options->getBinForSample(minValue - 1));
+
+ // logging overflow sample
+ ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue));
+ ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue + 1));
+
+ // logging min edge sample
+ ASSERT_EQ(1, options->getBinForSample(minValue));
+
+ // logging max edge sample
+ ASSERT_EQ(binCount, options->getBinForSample(maxValue - 1));
+
+ // logging single valid sample per bin
+ const int binSize = (maxValue - minValue) / binCount;
+
+ for (int i = 0; i < binCount; i++) {
+ ASSERT_EQ(i + 1, options->getBinForSample(minValue + binSize * i));
+ }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace expresslog
+} // namespace android