Merge "Add variadic logging to libdebuggerd internal."
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b1c22c9..14d534a 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -85,5 +85,10 @@
return true;
}
+// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
+// there are matches if `all == true`.
+[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
+ std::string_view to, bool all);
+
} // namespace base
} // namespace android
diff --git a/base/strings.cpp b/base/strings.cpp
index bb3167e..40b2bf2 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -116,5 +116,24 @@
return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
+std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
+ bool all) {
+ if (from.empty()) return std::string(s);
+
+ std::string result;
+ std::string_view::size_type start_pos = 0;
+ do {
+ std::string_view::size_type pos = s.find(from, start_pos);
+ if (pos == std::string_view::npos) break;
+
+ result.append(s.data() + start_pos, pos - start_pos);
+ result.append(to.data(), to.size());
+
+ start_pos = pos + from.size();
+ } while (all);
+ result.append(s.data() + start_pos, s.size() - start_pos);
+ return result;
+}
+
} // namespace base
} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index ca3c0b8..5ae3094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -311,3 +311,46 @@
ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
ASSERT_EQ("foo", s);
}
+
+TEST(strings, StringReplace_false) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
+
+ // Equal lengths.
+ ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
+ ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
+ ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
+ ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
+ ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
+ ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
+ ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
+}
+
+TEST(strings, StringReplace_true) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
+
+ // Equal lengths.
+ ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
+ ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
+ ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
+ ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
+ ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
+ ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1993840..236fcf7 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -428,7 +428,7 @@
std::vector<std::pair<std::string, uint64_t>> special_row;
#if defined(__arm__) || defined(__aarch64__)
- static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+ static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc", "pst"};
#elif defined(__i386__)
static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
#elif defined(__x86_64__)
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 02a887e..a757d56 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -137,12 +137,14 @@
"libhidlbase",
"liblog",
"liblp",
+ "libprotobuf-cpp-lite",
"libsparse",
"libutils",
],
static_libs: [
"libhealthhalutils",
+ "libsnapshot_nobinder",
],
header_libs: [
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index a2c95d6..1a745ab 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -19,6 +19,8 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <unordered_set>
+
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
@@ -33,6 +35,7 @@
#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
#include <uuid/uuid.h>
#include "constants.h"
@@ -48,6 +51,7 @@
using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using android::snapshot::SnapshotManager;
using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
struct VariableHandlers {
@@ -57,6 +61,24 @@
std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
};
+static bool IsSnapshotUpdateInProgress(FastbootDevice* device) {
+ auto hal = device->boot1_1();
+ if (!hal) {
+ return false;
+ }
+ auto merge_status = hal->getSnapshotMergeStatus();
+ return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+}
+
+static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {
+ static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {
+ "userdata", "metadata", "misc"};
+ if (ProtectedPartitionsDuringMerge.count(name) == 0) {
+ return false;
+ }
+ return IsSnapshotUpdateInProgress(device);
+}
+
static void GetAllVars(FastbootDevice* device, const std::string& name,
const VariableHandlers& handlers) {
if (!handlers.get_all_args) {
@@ -143,8 +165,14 @@
return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
}
+ const auto& partition_name = args[1];
+ if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+ auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress";
+ return device->WriteFail(message);
+ }
+
PartitionHandle handle;
- if (!OpenPartition(device, args[1], &handle)) {
+ if (!OpenPartition(device, partition_name, &handle)) {
return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
}
if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
@@ -209,9 +237,9 @@
"set_active command is not allowed on locked devices");
}
- // Slot suffix needs to be between 'a' and 'z'.
Slot slot;
if (!GetSlotNumber(args[1], &slot)) {
+ // Slot suffix needs to be between 'a' and 'z'.
return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
}
@@ -224,6 +252,32 @@
if (slot >= boot_control_hal->getNumberSlots()) {
return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
}
+
+ // If the slot is not changing, do nothing.
+ if (slot == boot_control_hal->getCurrentSlot()) {
+ return device->WriteOkay("");
+ }
+
+ // Check how to handle the current snapshot state.
+ if (auto hal11 = device->boot1_1()) {
+ auto merge_status = hal11->getSnapshotMergeStatus();
+ if (merge_status == MergeStatus::MERGING) {
+ return device->WriteFail("Cannot change slots while a snapshot update is in progress");
+ }
+ // Note: we allow the slot change if the state is SNAPSHOTTED. First-
+ // stage init does not have access to the HAL, and uses the slot number
+ // and /metadata OTA state to determine whether a slot change occurred.
+ // Booting into the old slot would erase the OTA, and switching A->B->A
+ // would simply resume it if no boots occur in between. Re-flashing
+ // partitions implicitly cancels the OTA, so leaving the state as-is is
+ // safe.
+ if (merge_status == MergeStatus::SNAPSHOTTED) {
+ device->WriteInfo(
+ "Changing the active slot with a snapshot applied may cancel the"
+ " update.");
+ }
+ }
+
CommandResult ret;
auto cb = [&ret](CommandResult result) { ret = result; };
auto result = boot_control_hal->setActiveBootSlot(slot, cb);
@@ -467,6 +521,11 @@
}
const auto& partition_name = args[1];
+ if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+ auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress";
+ return device->WriteFail(message);
+ }
+
if (LogicalPartitionExists(device, partition_name)) {
CancelPartitionSnapshot(device, partition_name);
}
@@ -556,12 +615,9 @@
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
- auto hal = device->boot_control_hal();
+ auto hal = device->boot1_1();
if (!hal) return device->WriteFail("Not supported");
- android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
- if (!hal11) return device->WriteFail("Not supported");
-
// If no arguments, return the same thing as a getvar. Note that we get the
// HAL first so we can return "not supported" before we return the less
// specific error message below.
@@ -574,18 +630,34 @@
return device->WriteOkay("");
}
- if (args.size() != 2 || args[1] != "cancel") {
+ MergeStatus status = hal->getSnapshotMergeStatus();
+
+ if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
}
+ if (args[1] == "cancel") {
+ switch (status) {
+ case MergeStatus::SNAPSHOTTED:
+ case MergeStatus::MERGING:
+ hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+ break;
+ default:
+ break;
+ }
+ } else if (args[1] == "merge") {
+ if (status != MergeStatus::MERGING) {
+ return device->WriteFail("No snapshot merge is in progress");
+ }
- MergeStatus status = hal11->getSnapshotMergeStatus();
- switch (status) {
- case MergeStatus::SNAPSHOTTED:
- case MergeStatus::MERGING:
- hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
- break;
- default:
- break;
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ return device->WriteFail("Unable to create SnapshotManager");
+ }
+ if (!sm->HandleImminentDataWipe()) {
+ return device->WriteFail("Unable to finish snapshot merge");
+ }
+ } else {
+ return device->WriteFail("Invalid parameter to snapshot-update");
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index d3c2bda..31fc359 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -60,7 +60,11 @@
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
- active_slot_("") {}
+ active_slot_("") {
+ if (boot_control_hal_) {
+ boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
+ }
+}
FastbootDevice::~FastbootDevice() {
CloseDevice();
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 091aadf..bbe8172 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,7 @@
#include <vector>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <android/hardware/fastboot/1.0/IFastboot.h>
#include <android/hardware/health/2.0/IHealth.h>
@@ -51,6 +52,7 @@
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
return boot_control_hal_;
}
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
@@ -63,6 +65,7 @@
std::unique_ptr<Transport> transport_;
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 717db06..10eac01 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -432,19 +432,13 @@
std::string* message) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
- auto hal = device->boot_control_hal();
+ auto hal = device->boot1_1();
if (!hal) {
*message = "not supported";
return false;
}
- android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
- if (!hal11) {
- *message = "not supported";
- return false;
- }
-
- MergeStatus status = hal11->getSnapshotMergeStatus();
+ MergeStatus status = hal->getSnapshotMergeStatus();
switch (status) {
case MergeStatus::SNAPSHOTTED:
*message = "snapshotted";
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7ce7c7c..cbd42b1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -399,6 +399,9 @@
" snapshot-update cancel On devices that support snapshot-based updates, cancel\n"
" an in-progress update. This may make the device\n"
" unbootable until it is reflashed.\n"
+ " snapshot-update merge On devices that support snapshot-based updates, finish\n"
+ " an in-progress update if it is in the \"merging\"\n"
+ " phase.\n"
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@@ -2089,8 +2092,8 @@
if (!args.empty()) {
arg = next_arg(&args);
}
- if (!arg.empty() && arg != "cancel") {
- syntax_error("expected: snapshot-update [cancel]");
+ if (!arg.empty() && (arg != "cancel" && arg != "merge")) {
+ syntax_error("expected: snapshot-update [cancel|merge]");
}
fb->SnapshotUpdateCommand(arg);
} else {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 6a5ad20..8d534ea 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -124,8 +124,11 @@
RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
std::vector<std::string>* info) {
+ prolog_(StringPrintf("Snapshot %s", command.c_str()));
std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
- return RawCommand(raw, response, info);
+ auto result = RawCommand(raw, response, info);
+ epilog_(result);
+ return result;
}
RetCode FastBootDriver::FlashPartition(const std::string& partition,
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 9256a16..1d72c70 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -72,6 +72,7 @@
name: "libsnapshot_sources",
srcs: [
"android/snapshot/snapshot.proto",
+ "device_info.cpp",
"snapshot.cpp",
"snapshot_metadata_updater.cpp",
"partition_cow_creator.cpp",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
new file mode 100644
index 0000000..bacb41c
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -0,0 +1,123 @@
+// 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 "device_info.h"
+
+#include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
+
+namespace android {
+namespace snapshot {
+
+#ifdef LIBSNAPSHOT_USE_HAL
+using android::hardware::boot::V1_0::CommandResult;
+#endif
+
+using namespace std::string_literals;
+
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
+std::string DeviceInfo::GetGsidDir() const {
+ return "ota"s;
+}
+
+std::string DeviceInfo::GetMetadataDir() const {
+ return "/metadata/ota"s;
+}
+
+std::string DeviceInfo::GetSlotSuffix() const {
+ return fs_mgr_get_slot_suffix();
+}
+
+std::string DeviceInfo::GetOtherSlotSuffix() const {
+ return fs_mgr_get_other_slot_suffix();
+}
+
+const android::fs_mgr::IPartitionOpener& DeviceInfo::GetPartitionOpener() const {
+ return opener_;
+}
+
+std::string DeviceInfo::GetSuperDevice(uint32_t slot) const {
+ return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DeviceInfo::IsOverlayfsSetup() const {
+ return fs_mgr_overlayfs_is_setup();
+}
+
+#ifdef LIBSNAPSHOT_USE_HAL
+bool DeviceInfo::EnsureBootHal() {
+ if (!boot_control_) {
+ auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+ if (!hal) {
+ LOG(ERROR) << "Could not find IBootControl HAL";
+ return false;
+ }
+ boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
+ if (!boot_control_) {
+ LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
+ return false;
+ }
+ }
+ return true;
+}
+#endif
+
+bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
+#ifdef LIBSNAPSHOT_USE_HAL
+ if (!EnsureBootHal()) {
+ return false;
+ }
+ if (!boot_control_->setSnapshotMergeStatus(status)) {
+ LOG(ERROR) << "Unable to set the snapshot merge status";
+ return false;
+ }
+ return true;
+#else
+ LOG(ERROR) << "HAL support not enabled.";
+ return false;
+#endif
+}
+
+bool DeviceInfo::IsRecovery() const {
+ return kIsRecovery;
+}
+
+bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
+#ifdef LIBSNAPSHOT_USE_HAL
+ if (!EnsureBootHal()) {
+ return false;
+ }
+
+ CommandResult result = {};
+ auto cb = [&](CommandResult r) -> void { result = r; };
+ boot_control_->setSlotAsUnbootable(slot, cb);
+ if (!result.success) {
+ LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
+ return false;
+ }
+ return true;
+#else
+ LOG(ERROR) << "HAL support not enabled.";
+ return false;
+#endif
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
new file mode 100644
index 0000000..d8d3d91
--- /dev/null
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -0,0 +1,53 @@
+// 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.
+
+#pragma once
+
+#include <string>
+
+#ifdef LIBSNAPSHOT_USE_HAL
+#include <android/hardware/boot/1.1/IBootControl.h>
+#endif
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class DeviceInfo final : public SnapshotManager::IDeviceInfo {
+ using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+
+ public:
+ std::string GetGsidDir() const override;
+ std::string GetMetadataDir() const override;
+ std::string GetSlotSuffix() const override;
+ std::string GetOtherSlotSuffix() const override;
+ const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override;
+ std::string GetSuperDevice(uint32_t slot) const override;
+ bool IsOverlayfsSetup() const override;
+ bool SetBootControlMergeStatus(MergeStatus status) override;
+ bool SetSlotAsUnbootable(unsigned int slot) override;
+ bool IsRecovery() const override;
+
+ private:
+ bool EnsureBootHal();
+
+ android::fs_mgr::PartitionOpener opener_;
+#ifdef LIBSNAPSHOT_USE_HAL
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+#endif
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
index d5ceb0e..73450db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -27,6 +27,8 @@
virtual ~AutoDevice(){};
void Release();
+ bool HasDevice() const { return !name_.empty(); }
+
protected:
AutoDevice(const std::string& name) : name_(name) {}
std::string name_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index c820395..7411e5a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -122,6 +122,7 @@
virtual const IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+ virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
};
@@ -133,7 +134,7 @@
static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
// This is similar to New(), except designed specifically for first-stage
- // init.
+ // init or recovery.
static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
// Helper function for first-stage init to check whether a SnapshotManager
@@ -180,7 +181,10 @@
//
// MergeCompleted indicates that the update has fully completed.
// GetUpdateState will return None, and a new update can begin.
- UpdateState ProcessUpdateState();
+ //
+ // The optional callback allows the caller to periodically check the
+ // progress with GetUpdateState().
+ UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
public:
// Initiate the merge if necessary, then wait for the merge to finish.
@@ -219,6 +223,19 @@
// call to CreateLogicalPartitions when snapshots are present.
bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
+ // This method should be called preceding any wipe or flash of metadata or
+ // userdata. It is only valid in recovery or fastbootd, and it ensures that
+ // a merge has been completed.
+ //
+ // When userdata will be wiped or flashed, it is necessary to clean up any
+ // snapshot state. If a merge is in progress, the merge must be finished.
+ // If a snapshot is present but not yet merged, the slot must be marked as
+ // unbootable.
+ //
+ // Returns true on success (or nothing to do), false on failure. The
+ // optional callback fires periodically to query progress via GetUpdateState.
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+
// Dump debug information.
bool Dump(std::ostream& os);
@@ -231,7 +248,7 @@
// is not found in fstab.
// Note: if this function is called the second time before the AutoDevice returned from the
// first call is destroyed, the device will be unmounted when any of these AutoDevices is
- // destroyed. FOr example:
+ // destroyed. For example:
// auto a = mgr->EnsureMetadataMounted(); // mounts
// auto b = mgr->EnsureMetadataMounted(); // does nothing
// b.reset() // unmounts
@@ -250,7 +267,10 @@
FRIEND_TEST(SnapshotTest, Merge);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
+ FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
+ FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
@@ -464,6 +484,10 @@
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
+ // Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.
+ // This should only be called in recovery.
+ bool UnmapAllPartitions();
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2c516a2..008ece7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -29,19 +29,16 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
-#endif
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
-#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
#include <android/snapshot/snapshot.pb.h>
+#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"
@@ -77,57 +74,6 @@
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
-
-class DeviceInfo final : public SnapshotManager::IDeviceInfo {
- public:
- std::string GetGsidDir() const override { return "ota"s; }
- std::string GetMetadataDir() const override { return "/metadata/ota"s; }
- std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
- std::string GetOtherSlotSuffix() const override { return fs_mgr_get_other_slot_suffix(); }
- const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
- std::string GetSuperDevice(uint32_t slot) const override {
- return fs_mgr_get_super_partition_name(slot);
- }
- bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
- bool SetBootControlMergeStatus(MergeStatus status) override;
- bool IsRecovery() const override { return kIsRecovery; }
-
- private:
- android::fs_mgr::PartitionOpener opener_;
-#ifdef LIBSNAPSHOT_USE_HAL
- android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
-#endif
-};
-
-bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
-#ifdef LIBSNAPSHOT_USE_HAL
- if (!boot_control_) {
- auto hal = android::hardware::boot::V1_0::IBootControl::getService();
- if (!hal) {
- LOG(ERROR) << "Could not find IBootControl HAL";
- return false;
- }
- boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
- if (!boot_control_) {
- LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
- return false;
- }
- }
- if (!boot_control_->setSnapshotMergeStatus(status)) {
- LOG(ERROR) << "Unable to set the snapshot merge status";
- return false;
- }
- return true;
-#else
- return false;
-#endif
-}
-
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -512,6 +458,14 @@
return false;
}
+ // We can't delete snapshots in recovery. The only way we'd try is it we're
+ // completing or canceling a merge in preparation for a data wipe, in which
+ // case, we don't care if the file sticks around.
+ if (device_->IsRecovery()) {
+ LOG(INFO) << "Skipping delete of snapshot " << name << " in recovery.";
+ return true;
+ }
+
auto cow_image_name = GetCowImageDeviceName(name);
if (images_->BackingImageExists(cow_image_name)) {
if (!images_->DeleteBackingImage(cow_image_name)) {
@@ -744,7 +698,7 @@
// Note that when a merge fails, we will *always* try again to complete the
// merge each time the device boots. There is no harm in doing so, and if
// the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::ProcessUpdateState() {
+UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
while (true) {
UpdateState state = CheckMergeState();
if (state == UpdateState::MergeFailed) {
@@ -756,6 +710,10 @@
return state;
}
+ if (callback) {
+ callback();
+ }
+
// This wait is not super time sensitive, so we have a relatively
// low polling frequency.
std::this_thread::sleep_for(2s);
@@ -1718,9 +1676,9 @@
case UpdateState::None:
case UpdateState::MergeNeedsReboot:
case UpdateState::MergeCompleted:
+ case UpdateState::Initiated:
merge_status = MergeStatus::NONE;
break;
- case UpdateState::Initiated:
case UpdateState::Unverified:
merge_status = MergeStatus::SNAPSHOTTED;
break;
@@ -2119,6 +2077,27 @@
return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
}
+bool SnapshotManager::UnmapAllPartitions() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ const auto& opener = device_->GetPartitionOpener();
+ uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_device = device_->GetSuperDevice(slot);
+ auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+ return false;
+ }
+
+ bool ok = true;
+ for (const auto& partition : metadata->partitions) {
+ auto partition_name = GetPartitionName(partition);
+ ok &= UnmapPartitionWithSnapshot(lock.get(), partition_name);
+ }
+ return ok;
+}
+
bool SnapshotManager::Dump(std::ostream& os) {
// Don't actually lock. Dump() is for debugging purposes only, so it is okay
// if it is racy.
@@ -2192,5 +2171,75 @@
return state;
}
+bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << "Data wipes are only allowed in recovery.";
+ return false;
+ }
+
+ auto mount = EnsureMetadataMounted();
+ if (!mount || !mount->HasDevice()) {
+ // We allow the wipe to continue, because if we can't mount /metadata,
+ // it is unlikely the device would have booted anyway. If there is no
+ // metadata partition, then the device predates Virtual A/B.
+ return true;
+ }
+
+ // Check this early, so we don't accidentally start trying to populate
+ // the state file in recovery. Note we don't call GetUpdateState since
+ // we want errors in acquiring the lock to be propagated, instead of
+ // returning UpdateState::None.
+ auto state_file = GetStateFilePath();
+ if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+ return true;
+ }
+
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_path = device_->GetSuperDevice(slot_number);
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+ LOG(ERROR) << "Unable to map partitions to complete merge.";
+ return false;
+ }
+
+ UpdateState state = ProcessUpdateState(callback);
+ LOG(INFO) << "Update state in recovery: " << state;
+ switch (state) {
+ case UpdateState::MergeFailed:
+ LOG(ERROR) << "Unrecoverable merge failure detected.";
+ return false;
+ case UpdateState::Unverified: {
+ // If an OTA was just applied but has not yet started merging, we
+ // have no choice but to revert slots, because the current slot will
+ // immediately become unbootable. Rather than wait for the device
+ // to reboot N times until a rollback, we proactively disable the
+ // new slot instead.
+ //
+ // Since the rollback is inevitable, we don't treat a HAL failure
+ // as an error here.
+ std::string old_slot;
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (android::base::ReadFileToString(boot_file, &old_slot) &&
+ device_->GetSlotSuffix() != old_slot) {
+ LOG(ERROR) << "Reverting to slot " << old_slot << " since update will be deleted.";
+ device_->SetSlotAsUnbootable(slot_number);
+ }
+ break;
+ }
+ case UpdateState::MergeNeedsReboot:
+ // We shouldn't get here, because nothing is depending on
+ // logical partitions.
+ LOG(ERROR) << "Unexpected merge-needs-reboot state in recovery.";
+ break;
+ default:
+ break;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index af268f9..8783526 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1230,6 +1230,80 @@
EXPECT_FALSE(IsMetadataMounted());
}
+// Test that during a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, MergeInRecovery) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+ init = nullptr;
+
+ // Initiate the merge and then immediately stop it to simulate a reboot.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ test_device->set_recovery(true);
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+ ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+}
+
+// Test that after an OTA, before a merge, we can wipe data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = new TestDeviceInfo(fake_super, "_b");
+ test_device->set_recovery(true);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+ ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
+ EXPECT_TRUE(test_device->IsSlotUnbootable(1));
+ EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
+// Test that after an OTA and a bootloader rollback with no merge, we can wipe
+// data in recovery.
+TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a rollback, with reboot into recovery.
+ auto test_device = new TestDeviceInfo(fake_super, "_a");
+ test_device->set_recovery(true);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+ ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+ EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+ EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 0f70afe..9083843 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -16,6 +16,7 @@
#include <optional>
#include <string>
+#include <unordered_set>
#include <android/hardware/boot/1.1/IBootControl.h>
#include <gmock/gmock.h>
@@ -89,6 +90,12 @@
}
bool IsOverlayfsSetup() const override { return false; }
bool IsRecovery() const override { return recovery_; }
+ bool SetSlotAsUnbootable(unsigned int slot) override {
+ unbootable_slots_.insert(slot);
+ return true;
+ }
+
+ bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
void set_fake_super(const std::string& path) {
@@ -102,6 +109,7 @@
std::unique_ptr<TestPartitionOpener> opener_;
MergeStatus merge_status_;
bool recovery_ = false;
+ std::unordered_set<uint32_t> unbootable_slots_;
};
class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
diff --git a/init/Android.bp b/init/Android.bp
index c7021c3..9529617 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -152,6 +152,7 @@
whole_static_libs: [
"libcap",
"com.android.sysprop.apex",
+ "com.android.sysprop.init",
],
header_libs: ["bootimg_headers"],
proto: {
diff --git a/init/action.cpp b/init/action.cpp
index 69e40d0..f05fa7c 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -180,21 +180,24 @@
// It takes an optional (name, value) pair, which if provided must
// be present in property_triggers_; it skips the check of the current
// property value for this pair.
-bool Action::CheckPropertyTriggers(const std::string& name,
- const std::string& value) const {
+bool Action::CheckPropertyTriggers(const std::string& name, const std::string& value) const {
if (property_triggers_.empty()) {
return true;
}
- bool found = name.empty();
+ if (!name.empty()) {
+ auto it = property_triggers_.find(name);
+ if (it == property_triggers_.end()) {
+ return false;
+ }
+ const auto& trigger_value = it->second;
+ if (trigger_value != "*" && trigger_value != value) {
+ return false;
+ }
+ }
+
for (const auto& [trigger_name, trigger_value] : property_triggers_) {
- if (trigger_name == name) {
- if (trigger_value != "*" && trigger_value != value) {
- return false;
- } else {
- found = true;
- }
- } else {
+ if (trigger_name != name) {
std::string prop_value = android::base::GetProperty(trigger_name, "");
if (trigger_value == "*" && !prop_value.empty()) {
continue;
@@ -202,7 +205,7 @@
if (trigger_value != prop_value) return false;
}
}
- return found;
+ return true;
}
bool Action::CheckEvent(const EventTrigger& event_trigger) const {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a55514b..485806b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -45,6 +45,7 @@
#include <memory>
#include <ApexProperties.sysprop.h>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -65,6 +66,7 @@
#include "action_manager.h"
#include "bootchart.h"
+#include "builtin_arguments.h"
#include "fscrypt_init_extensions.h"
#include "init.h"
#include "mount_namespace.h"
@@ -1216,6 +1218,17 @@
}
}
+static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) {
+ LOG(INFO) << "Userspace reboot successfully finished";
+ boot_clock::time_point now = boot_clock::now();
+ property_set("sys.init.userspace_reboot.last_finished",
+ std::to_string(now.time_since_epoch().count()));
+ if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
+ return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+ }
+ return {};
+}
+
// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1237,6 +1250,7 @@
{"exec_background", {1, kMax, {false, do_exec_background}}},
{"exec_start", {1, 1, {false, do_exec_start}}},
{"export", {2, 2, {false, do_export}}},
+ {"finish_userspace_reboot", {0, 0, {false, do_finish_userspace_reboot}}},
{"hostname", {1, 1, {true, do_hostname}}},
{"ifup", {1, 1, {true, do_ifup}}},
{"init_user0", {0, 0, {false, do_init_user0}}},
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 64ec1fb..13cebcc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -38,6 +38,7 @@
#include <thread>
#include <vector>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -69,10 +70,13 @@
using namespace std::literals;
+using android::base::boot_clock;
using android::base::GetBoolProperty;
+using android::base::SetProperty;
using android::base::Split;
using android::base::Timer;
using android::base::unique_fd;
+using android::base::WaitForProperty;
using android::base::WriteStringToFile;
namespace android {
@@ -728,16 +732,21 @@
static Result<void> DoUserspaceReboot() {
LOG(INFO) << "Userspace reboot initiated";
+ boot_clock::time_point now = boot_clock::now();
+ property_set("sys.init.userspace_reboot.last_started",
+ std::to_string(now.time_since_epoch().count()));
auto guard = android::base::make_scope_guard([] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
trigger_shutdown("reboot,abort-userspace-reboot");
});
- // Triggering userspace-reboot-requested will result in a bunch of set_prop
+ // Triggering userspace-reboot-requested will result in a bunch of setprop
// actions. We should make sure, that all of them are propagated before
- // proceeding with userspace reboot.
- // TODO(b/135984674): implement proper synchronization logic.
- std::this_thread::sleep_for(500ms);
+ // proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
+ // is not perfect, but it should do the trick.
+ if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+ }
EnterShutdown();
std::vector<Service*> stop_first;
// Remember the services that were enabled. We will need to manually enable them again otherwise
@@ -752,6 +761,12 @@
were_enabled.push_back(s);
}
}
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() before terminating services...";
+ sync();
+ LOG(INFO) << "sync() took " << sync_timer;
+ }
// TODO(b/135984674): do we need shutdown animation for userspace reboot?
// TODO(b/135984674): control userspace timeout via read-only property?
StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
@@ -767,6 +782,12 @@
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() after stopping services...";
+ sync();
+ LOG(INFO) << "sync() took " << sync_timer;
+ }
if (auto result = UnmountAllApexes(); !result) {
return result;
}
@@ -784,7 +805,38 @@
return {};
}
+static void UserspaceRebootWatchdogThread() {
+ if (!WaitForProperty("sys.init.userspace_reboot_in_progress", "1", 20s)) {
+ // TODO(b/135984674): should we reboot instead?
+ LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
+ return;
+ }
+ LOG(INFO) << "Starting userspace reboot watchdog";
+ // TODO(b/135984674): this should be configured via a read-only sysprop.
+ std::chrono::milliseconds timeout = 60s;
+ if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
+ LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+ // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
+ RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered");
+ }
+ LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
+}
+
static void HandleUserspaceReboot() {
+ // Spinnig up a separate thread will fail the setns call later in the boot sequence.
+ // Fork a new process to monitor userspace reboot while we are investigating a better solution.
+ pid_t pid = fork();
+ if (pid < 0) {
+ PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
+ << "reboot";
+ trigger_shutdown("reboot,userspace-reboot-failed-to-fork");
+ return;
+ }
+ if (pid == 0) {
+ // Child
+ UserspaceRebootWatchdogThread();
+ _exit(EXIT_SUCCESS);
+ }
LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
auto& am = ActionManager::GetInstance();
am.ClearQueue();
diff --git a/init/sysprop/Android.bp b/init/sysprop/Android.bp
new file mode 100644
index 0000000..7582875
--- /dev/null
+++ b/init/sysprop/Android.bp
@@ -0,0 +1,7 @@
+sysprop_library {
+ name: "com.android.sysprop.init",
+ srcs: ["InitProperties.sysprop"],
+ property_owner: "Platform",
+ api_packages: ["android.sysprop"],
+ recovery_available: true,
+}
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
new file mode 100644
index 0000000..d6a1ab6
--- /dev/null
+++ b/init/sysprop/InitProperties.sysprop
@@ -0,0 +1,27 @@
+# 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.
+
+owner: Platform
+module: "android.sysprop.InitProperties"
+
+# Serves as a signal to all processes that userspace reboot is happening.
+prop {
+ api_name: "userspace_reboot_in_progress"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+}
+
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
new file mode 100644
index 0000000..8da50e0
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -0,0 +1,9 @@
+props {
+ module: "android.sysprop.InitProperties"
+ prop {
+ api_name: "userspace_reboot_in_progress"
+ access: ReadWrite
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+ }
+}
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
new file mode 100644
index 0000000..c835b95
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -0,0 +1,9 @@
+props {
+ module: "android.sysprop.InitProperties"
+ prop {
+ api_name: "userspace_reboot_in_progress"
+ scope: Public
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+ }
+}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 22cf43b..2886289 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -494,7 +494,7 @@
// Cache miss, go to logd to acquire a public reference.
// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
// call event tag service to arrange for a new tag
char* buf = NULL;
// Can not use android::base::StringPrintf, asprintf + free instead.
@@ -515,8 +515,9 @@
} else {
size = ret;
}
+#ifdef __ANDROID__
// Ask event log tag service for an existing entry
- if (__send_log_msg(buf, size) >= 0) {
+ if (SendLogdControlMessage(buf, size) >= 0) {
buf[size - 1] = '\0';
char* ep;
unsigned long val = strtoul(buf, &ep, 10); // return size
@@ -529,6 +530,7 @@
}
}
}
+#endif
free(buf);
}
return NULL;
@@ -618,8 +620,9 @@
} else {
size = ret;
}
+#ifdef __ANDROID__
// Ask event log tag service for an allocation
- if (__send_log_msg(buf, size) >= 0) {
+ if (SendLogdControlMessage(buf, size) >= 0) {
buf[size - 1] = '\0';
unsigned long val = strtoul(buf, &cp, 10); // return size
if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
@@ -635,6 +638,7 @@
}
}
}
+#endif
free(buf);
}
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 428a482..f61bbdc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -650,10 +650,6 @@
return fd;
}
-ssize_t __send_log_msg(char*, size_t) {
- return -ENODEV;
-}
-
int __android_log_is_loggable(int prio, const char*, int def) {
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index ce54db2..bd2256c 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -29,7 +29,6 @@
int fakeLogClose(int fd);
ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
-ssize_t __send_log_msg(char*, size_t);
int __android_log_is_loggable(int prio, const char*, int def);
int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
int __android_log_is_debuggable();
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index 7882c96..e9f4a32 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -47,9 +47,6 @@
uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
};
-// TODO(tomcherry): real C++ structs.
-typedef struct android_log_context_internal android_log_context_internal;
-
static void init_context(android_log_context_internal* context, uint32_t tag) {
context->tag = tag;
context->read_write_flag = kAndroidLoggerWrite;
@@ -110,11 +107,9 @@
return 0;
}
-int android_log_reset(android_log_context ctx) {
- android_log_context_internal* context;
+int android_log_reset(android_log_context context) {
uint32_t tag;
- context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -126,10 +121,7 @@
return 0;
}
-int android_log_parser_reset(android_log_context ctx, const char* msg, size_t len) {
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
return -EBADF;
}
@@ -140,10 +132,7 @@
return 0;
}
-int android_log_write_list_begin(android_log_context ctx) {
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+int android_log_write_list_begin(android_log_context context) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -174,8 +163,7 @@
return 0;
}
-int android_log_write_int32(android_log_context ctx, int32_t value) {
- android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int32(android_log_context context, int32_t value) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -195,8 +183,7 @@
return 0;
}
-int android_log_write_int64(android_log_context ctx, int64_t value) {
- android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_int64(android_log_context context, int64_t value) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -216,8 +203,7 @@
return 0;
}
-int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
- android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -252,8 +238,7 @@
return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
}
-int android_log_write_float32(android_log_context ctx, float value) {
- android_log_context_internal* context = (android_log_context_internal*)ctx;
+int android_log_write_float32(android_log_context context, float value) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -273,10 +258,7 @@
return 0;
}
-int android_log_write_list_end(android_log_context ctx) {
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
+int android_log_write_list_end(android_log_context context) {
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -303,8 +285,7 @@
/*
* Logs the list of elements to the event log.
*/
-int android_log_write_list(android_log_context ctx, log_id_t id) {
- android_log_context_internal* context;
+int android_log_write_list(android_log_context context, log_id_t id) {
const char* msg;
ssize_t len;
@@ -312,7 +293,6 @@
return -EINVAL;
}
- context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -337,12 +317,10 @@
: __android_log_security_bwrite(context->tag, msg, len));
}
-int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
- android_log_context_internal* context;
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
const char* msg;
ssize_t len;
- context = (android_log_context_internal*)ctx;
if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
return -EBADF;
}
@@ -375,12 +353,10 @@
* this and continues to call this function, the behavior is undefined
* (although it won't crash).
*/
-static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+ int peek) {
android_log_list_element elem;
unsigned pos;
- android_log_context_internal* context;
-
- context = (android_log_context_internal*)ctx;
memset(&elem, 0, sizeof(elem));
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 619cf8c..4e2dc66 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -39,47 +39,20 @@
#include "logd_reader.h"
#include "logger.h"
-static int logdAvailable(log_id_t LogId);
-static int logdVersion(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static int logdRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, struct log_msg* log_msg);
-static int logdPoll(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp);
-static void logdClose(struct android_log_logger_list* logger_list,
+static int LogdAvailable(log_id_t LogId);
+static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+ struct log_msg* log_msg);
+static void LogdClose(struct logger_list* logger_list,
struct android_log_transport_context* transp);
-static int logdClear(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t logdSetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp, size_t size);
-static ssize_t logdGetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t logdGetPrune(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdSetPrune(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t logdGetStats(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
struct android_log_transport_read logdLoggerRead = {
.name = "logd",
- .available = logdAvailable,
- .version = logdVersion,
- .close = logdClose,
- .read = logdRead,
- .poll = logdPoll,
- .clear = logdClear,
- .setSize = logdSetSize,
- .getSize = logdGetSize,
- .getReadableSize = logdGetReadableSize,
- .getPrune = logdGetPrune,
- .setPrune = logdSetPrune,
- .getStats = logdGetStats,
+ .available = LogdAvailable,
+ .close = LogdClose,
+ .read = LogdRead,
};
-static int logdAvailable(log_id_t logId) {
+static int LogdAvailable(log_id_t logId) {
if (logId >= LOG_ID_MAX) {
return -EINVAL;
}
@@ -120,8 +93,7 @@
}
/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
- size_t buf_size) {
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
ssize_t ret;
size_t len;
char* cp;
@@ -131,10 +103,6 @@
return sock;
}
- if (msg) {
- snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
- }
-
len = strlen(buf) + 1;
ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
if (ret <= 0) {
@@ -185,10 +153,6 @@
return ret;
}
-ssize_t __send_log_msg(char* buf, size_t buf_size) {
- return send_log_msg(NULL, NULL, buf, buf_size);
-}
-
static int check_log_success(char* buf, ssize_t ret) {
if (ret < 0) {
return ret;
@@ -202,19 +166,28 @@
return 0;
}
-static int logdClear(struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused) {
+int android_logger_clear(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+ uint32_t log_id = android_logger_get_id(logger);
char buf[512];
+ snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
- return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
/* returns the total size of the log's ring buffer */
-static ssize_t logdGetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused) {
- char buf[512];
+long android_logger_get_log_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
- ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -226,24 +199,32 @@
return atol(buf);
}
-static ssize_t logdSetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused, size_t size) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+
+ uint32_t log_id = android_logger_get_id(logger);
char buf[512];
+ snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
- snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
-
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
/*
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-static ssize_t logdGetReadableSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused) {
- char buf[512];
+long android_logger_get_log_readable_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
- ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -255,22 +236,15 @@
return atol(buf);
}
-/*
- * returns the logger version
- */
-static int logdVersion(struct android_log_logger* logger __unused,
- struct android_log_transport_context* transp __unused) {
- uid_t uid = __android_log_uid();
- return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+int android_logger_get_log_version(struct logger*) {
+ return 4;
}
-/*
- * returns statistics
- */
-static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp __unused, char* buf,
- size_t len) {
- struct android_log_logger* logger;
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
char* cp = buf;
size_t remaining = len;
size_t n;
@@ -280,29 +254,35 @@
remaining -= n;
cp += n;
- logger_for_each(logger, logger_list) {
- n = snprintf(cp, remaining, " %d", logger->logId);
- n = MIN(n, remaining);
- remaining -= n;
- cp += n;
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ n = snprintf(cp, remaining, " %zu", log_id);
+ n = MIN(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
}
if (logger_list->pid) {
snprintf(cp, remaining, " pid=%u", logger_list->pid);
}
- return send_log_msg(NULL, NULL, buf, len);
+ return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
+ snprintf(buf, len, "getPruneList");
+ return SendLogdControlMessage(buf, len);
}
-static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp __unused, char* buf,
- size_t len) {
- return send_log_msg(NULL, "getPruneList", buf, len);
-}
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
-static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp __unused, char* buf,
- size_t len) {
const char cmd[] = "setPruneList ";
const size_t cmdlen = sizeof(cmd) - 1;
@@ -313,12 +293,10 @@
buf[len - 1] = '\0';
memcpy(buf, cmd, cmdlen);
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+ return check_log_success(buf, SendLogdControlMessage(buf, len));
}
-static int logdOpen(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp) {
- struct android_log_logger* logger;
+static int logdOpen(struct logger_list* logger_list, struct android_log_transport_context* transp) {
char buffer[256], *cp, c;
int ret, remaining, sock;
@@ -346,12 +324,15 @@
cp += 5;
c = '=';
remaining = sizeof(buffer) - (cp - buffer);
- logger_for_each(logger, logger_list) {
- ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
- ret = MIN(ret, remaining);
- remaining -= ret;
- cp += ret;
- c = ',';
+
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ c = ',';
+ }
}
if (logger_list->tail) {
@@ -404,8 +385,8 @@
}
/* Read from the selected logs */
-static int logdRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, struct log_msg* log_msg) {
+static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+ struct log_msg* log_msg) {
int ret = logdOpen(logger_list, transp);
if (ret < 0) {
return ret;
@@ -425,31 +406,8 @@
return ret;
}
-static int logdPoll(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp) {
- struct pollfd p;
-
- int ret = logdOpen(logger_list, transp);
- if (ret < 0) {
- return ret;
- }
-
- memset(&p, 0, sizeof(p));
- p.fd = ret;
- p.events = POLLIN;
- ret = poll(&p, 1, 20);
- if ((ret > 0) && !(p.revents & POLLIN)) {
- ret = 0;
- }
- if ((ret == -1) && errno) {
- return -errno;
- }
- return ret;
-}
-
/* Close all the logs */
-static void logdClose(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp) {
+static void LogdClose(struct logger_list*, struct android_log_transport_context* transp) {
int sock = atomic_exchange(&transp->context.sock, -1);
if (sock > 0) {
close(sock);
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 7c53cbb..09f8627 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -22,6 +22,6 @@
__BEGIN_DECLS
-ssize_t __send_log_msg(char* buf, size_t buf_size);
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
__END_DECLS
diff --git a/liblog/logger.h b/liblog/logger.h
index 02cad22..f0a4a92 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -46,84 +46,52 @@
size_t nr);
};
-struct android_log_logger_list;
struct android_log_transport_context;
-struct android_log_logger;
struct android_log_transport_read {
const char* name; /* human name to describe the transport */
/* Does not cause resources to be taken */
int (*available)(log_id_t logId);
- int (*version)(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
/* Release resources taken by the following interfaces */
- void (*close)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp);
+ void (*close)(struct logger_list* logger_list, struct android_log_transport_context* transp);
/*
* Expect all to instantiate open automagically on any call,
* so we do not have an explicit open call.
*/
- int (*read)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
+ int (*read)(struct logger_list* logger_list, struct android_log_transport_context* transp,
struct log_msg* log_msg);
- /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
- int (*poll)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp);
-
- int (*clear)(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
- ssize_t (*setSize)(struct android_log_logger* logger,
- struct android_log_transport_context* transp, size_t size);
- ssize_t (*getSize)(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
- ssize_t (*getReadableSize)(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-
- ssize_t (*getPrune)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, char* buf,
- size_t len);
- ssize_t (*setPrune)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, char* buf,
- size_t len);
- ssize_t (*getStats)(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, char* buf,
- size_t len);
};
struct android_log_transport_context {
union android_log_context_union context; /* zero init per-transport context */
struct android_log_transport_read* transport;
- unsigned logMask; /* mask of requested log buffers */
};
-struct android_log_logger_list {
- struct listnode logger;
+struct logger_list {
android_log_transport_context transport_context;
bool transport_initialized;
int mode;
unsigned int tail;
log_time start;
pid_t pid;
+ uint32_t log_mask;
};
-struct android_log_logger {
- struct listnode node;
- struct android_log_logger_list* parent;
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
- log_id_t logId;
-};
+#define LOGGER_LOGD (1U << 31)
+#define LOGGER_PMSG (1U << 30)
+#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
-/* assumes caller has structures read-locked, single threaded, or fenced */
-#define logger_for_each(logp, logger_list) \
- for ((logp) = node_to_item((logger_list)->logger.next, \
- struct android_log_logger, node); \
- ((logp) != node_to_item(&(logger_list)->logger, \
- struct android_log_logger, node)) && \
- ((logp)->parent == (logger_list)); \
- (logp) = \
- node_to_item((logp)->node.next, struct android_log_logger, node))
+inline bool android_logger_is_logd(struct logger* logger) {
+ return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
/* OS specific dribs and drabs */
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4b4012a..0ce7a46 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -32,221 +33,53 @@
#include "log_portability.h"
#include "logger.h"
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger* logger) {
- struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
-
- if (!logger_internal) {
- return;
- }
-
- list_remove(&logger_internal->node);
-
- free(logger_internal);
-}
-
-/* android_logger_alloc unimplemented, no use case */
-
/* method for getting the associated sublog id */
log_id_t android_logger_get_id(struct logger* logger) {
- return ((struct android_log_logger*)logger)->logId;
+ return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
}
-static int init_transport_context(struct android_log_logger_list* logger_list) {
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+ log_time start, pid_t pid) {
+ auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
if (!logger_list) {
- return -EINVAL;
+ return nullptr;
}
- if (list_empty(&logger_list->logger)) {
- return -EINVAL;
- }
-
- if (logger_list->transport_initialized) {
- return 0;
- }
+ logger_list->mode = mode;
+ logger_list->start = start;
+ logger_list->tail = tail;
+ logger_list->pid = pid;
#if (FAKE_LOG_DEVICE == 0)
extern struct android_log_transport_read logdLoggerRead;
extern struct android_log_transport_read pmsgLoggerRead;
- struct android_log_transport_read* transport;
- transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
-
- struct android_log_logger* logger;
- unsigned logMask = 0;
-
- logger_for_each(logger, logger_list) {
- log_id_t logId = logger->logId;
-
- if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
- continue;
- }
- if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
- logMask |= 1 << logId;
- }
- }
- if (!logMask) {
- return -ENODEV;
- }
-
- logger_list->transport_context.transport = transport;
- logger_list->transport_context.logMask = logMask;
+ logger_list->transport_context.transport =
+ (mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
#endif
- return 0;
-}
-#define LOGGER_FUNCTION(logger, def, func, args...) \
- ssize_t ret = -EINVAL; \
- android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger); \
- \
- if (!logger_internal) { \
- return ret; \
- } \
- ret = init_transport_context(logger_internal->parent); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
- if (transport_context->logMask & (1 << logger_internal->logId) && \
- transport_context->transport && transport_context->transport->func) { \
- ssize_t retval = \
- (transport_context->transport->func)(logger_internal, transport_context, ##args); \
- if (ret >= 0 || ret == (def)) { \
- ret = retval; \
- } \
- } \
- return ret
-
-int android_logger_clear(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, clear);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, getSize);
-}
-
-int android_logger_set_log_size(struct logger* logger, unsigned long size) {
- LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger* logger) {
- LOGGER_FUNCTION(logger, 4, version);
-}
-
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
- android_log_logger_list* logger_list_internal = \
- reinterpret_cast<android_log_logger_list*>(logger_list); \
- \
- ssize_t ret = init_transport_context(logger_list_internal); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
- if (transport_context->transport && transport_context->transport->func) { \
- ssize_t retval = \
- (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
- if (ret >= 0 || ret == (def)) { \
- ret = retval; \
- } \
- } \
- return ret
-
-/*
- * returns statistics
- */
-ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
-}
-
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
+ return logger_list;
}
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
- struct android_log_logger_list* logger_list;
-
- logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->logger);
- logger_list->mode = mode;
- logger_list->tail = tail;
- logger_list->pid = pid;
-
- return (struct logger_list*)logger_list;
+ return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
}
struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
- struct android_log_logger_list* logger_list;
-
- logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->logger);
- logger_list->mode = mode;
- logger_list->start = start;
- logger_list->pid = pid;
-
- return (struct logger_list*)logger_list;
+ return android_logger_list_alloc_internal(mode, 0, start, pid);
}
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
/* Open the named log and add it to the logger list */
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
- struct android_log_logger_list* logger_list_internal =
- (struct android_log_logger_list*)logger_list;
- struct android_log_logger* logger;
-
- if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+ if (!logger_list || (logId >= LOG_ID_MAX)) {
return nullptr;
}
- logger_for_each(logger, logger_list_internal) {
- if (logger->logId == logId) {
- return reinterpret_cast<struct logger*>(logger);
- }
- }
+ logger_list->log_mask |= 1 << logId;
- logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
- if (!logger) {
- return nullptr;
- }
-
- logger->logId = logId;
- list_add_tail(&logger_list_internal->logger, &logger->node);
- logger->parent = logger_list_internal;
-
- // Reset known transport to re-evaluate, since we added a new logger.
- logger_list_internal->transport_initialized = false;
-
- return (struct logger*)logger;
+ uintptr_t logger = logId;
+ logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+ return reinterpret_cast<struct logger*>(logger);
}
/* Open the single named log and make it part of a new logger list */
@@ -266,13 +99,17 @@
return logger_list;
}
-/* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+ if (logger_list == nullptr || logger_list->transport_context.transport == nullptr ||
+ logger_list->log_mask == 0) {
+ return -EINVAL;
+ }
+
+ android_log_transport_context* transp = &logger_list->transport_context;
+
int ret = (*transp->transport->read)(logger_list, transp, log_msg);
- if (ret < 0) {
+ if (ret <= 0) {
return ret;
}
@@ -295,40 +132,17 @@
return ret;
}
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
- struct android_log_logger_list* logger_list_internal =
- (struct android_log_logger_list*)logger_list;
-
- int ret = init_transport_context(logger_list_internal);
- if (ret < 0) {
- return ret;
- }
-
- android_log_transport_context* transport_context = &logger_list_internal->transport_context;
- return android_transport_read(logger_list_internal, transport_context, log_msg);
-}
-
/* Close all the logs */
void android_logger_list_free(struct logger_list* logger_list) {
- struct android_log_logger_list* logger_list_internal =
- (struct android_log_logger_list*)logger_list;
-
- if (logger_list_internal == NULL) {
+ if (logger_list == NULL) {
return;
}
- android_log_transport_context* transport_context = &logger_list_internal->transport_context;
+ android_log_transport_context* transport_context = &logger_list->transport_context;
if (transport_context->transport && transport_context->transport->close) {
- (*transport_context->transport->close)(logger_list_internal, transport_context);
+ (*transport_context->transport->close)(logger_list, transport_context);
}
- while (!list_empty(&logger_list_internal->logger)) {
- struct listnode* node = list_head(&logger_list_internal->logger);
- struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
- android_logger_free((struct logger*)logger);
- }
-
- free(logger_list_internal);
+ free(logger_list);
}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index f43ce3a..9f603e9 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -26,33 +26,20 @@
#include "logger.h"
-static int pmsgAvailable(log_id_t logId);
-static int pmsgVersion(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static int pmsgRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, struct log_msg* log_msg);
-static void pmsgClose(struct android_log_logger_list* logger_list,
+static int PmsgAvailable(log_id_t logId);
+static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+ struct log_msg* log_msg);
+static void PmsgClose(struct logger_list* logger_list,
struct android_log_transport_context* transp);
-static int pmsgClear(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
struct android_log_transport_read pmsgLoggerRead = {
.name = "pmsg",
- .available = pmsgAvailable,
- .version = pmsgVersion,
- .close = pmsgClose,
- .read = pmsgRead,
- .poll = NULL,
- .clear = pmsgClear,
- .setSize = NULL,
- .getSize = NULL,
- .getReadableSize = NULL,
- .getPrune = NULL,
- .setPrune = NULL,
- .getStats = NULL,
+ .available = PmsgAvailable,
+ .close = PmsgClose,
+ .read = PmsgRead,
};
-static int pmsgAvailable(log_id_t logId) {
+static int PmsgAvailable(log_id_t logId) {
if (logId > LOG_ID_SECURITY) {
return -EINVAL;
}
@@ -62,21 +49,8 @@
return -EBADF;
}
-static int pmsgClear(struct android_log_logger* logger __unused,
- struct android_log_transport_context* transp __unused) {
- return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-}
-
-/*
- * returns the logger version
- */
-static int pmsgVersion(struct android_log_logger* logger __unused,
- struct android_log_transport_context* transp __unused) {
- return 4;
-}
-
-static int pmsgRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp, struct log_msg* log_msg) {
+static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
+ struct log_msg* log_msg) {
ssize_t ret;
off_t current, next;
struct __attribute__((__packed__)) {
@@ -138,7 +112,7 @@
}
preread_count = 0;
- if ((transp->logMask & (1 << buf.l.id)) &&
+ if ((logger_list->log_mask & (1 << buf.l.id)) &&
((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
@@ -192,8 +166,7 @@
}
}
-static void pmsgClose(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp) {
+static void PmsgClose(struct logger_list*, struct android_log_transport_context* transp) {
int fd = atomic_exchange(&transp->context.fd, 0);
if (fd > 0) {
close(fd);
@@ -211,7 +184,7 @@
ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
__android_log_pmsg_file_read_fn fn, void* arg) {
ssize_t ret;
- struct android_log_logger_list logger_list;
+ struct logger_list logger_list;
struct android_log_transport_context transp;
struct content {
struct listnode node;
@@ -237,12 +210,12 @@
memset(&transp, 0, sizeof(transp));
logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
- transp.logMask = (unsigned)-1;
+ logger_list.log_mask = (unsigned)-1;
if (logId != LOG_ID_ANY) {
- transp.logMask = (1 << logId);
+ logger_list.log_mask = (1 << logId);
}
- transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
- if (!transp.logMask) {
+ logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+ if (!logger_list.log_mask) {
return -EINVAL;
}
@@ -268,7 +241,7 @@
/* Read the file content */
log_msg log_msg;
- while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
+ while (PmsgRead(&logger_list, &transp, &log_msg) > 0) {
const char* cp;
size_t hdr_size = log_msg.entry.hdr_size;
@@ -426,7 +399,7 @@
}
list_add_head(node, &content->node);
}
- pmsgClose(&logger_list, &transp);
+ PmsgClose(&logger_list, &transp);
/* Progress through all the collected files */
list_for_each_safe(node, n, &name_list) {
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 56892a2..39ac7a5 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -913,7 +913,7 @@
}
BENCHMARK(BM_lookupEventTagNum);
-// Must be functionally identical to liblog internal __send_log_msg.
+// Must be functionally identical to liblog internal SendLogdControlMessage()
static void send_to_control(char* buf, size_t len) {
int sock =
socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index e9787aa..2e8af20 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -103,6 +103,7 @@
fn("sp", regs_[ARM64_REG_SP]);
fn("lr", regs_[ARM64_REG_LR]);
fn("pc", regs_[ARM64_REG_PC]);
+ fn("pst", regs_[ARM64_REG_PSTATE]);
}
Regs* RegsArm64::Read(void* remote_data) {
@@ -113,6 +114,7 @@
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
reg_data[ARM64_REG_PC] = user->pc;
reg_data[ARM64_REG_SP] = user->sp;
+ reg_data[ARM64_REG_PSTATE] = user->pstate;
return regs;
}
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e8b778b..e953335 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -55,6 +55,7 @@
ARM64_REG_R30,
ARM64_REG_R31,
ARM64_REG_PC,
+ ARM64_REG_PSTATE,
ARM64_REG_LAST,
ARM64_REG_SP = ARM64_REG_R31,
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 7e36953..bc95851 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -114,6 +114,7 @@
result.push_back({"sp", ARM64_REG_SP});
result.push_back({"lr", ARM64_REG_LR});
result.push_back({"pc", ARM64_REG_PC});
+ result.push_back({"pst", ARM64_REG_PSTATE});
return result;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 98921be..efa4c41 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -125,6 +125,7 @@
native_bridge_supported: true,
srcs: [
+ "Errors.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
"NativeHandle.cpp",
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
new file mode 100644
index 0000000..2dfd138
--- /dev/null
+++ b/libutils/Errors.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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/Errors.h>
+
+namespace android {
+
+std::string statusToString(status_t s) {
+#define STATUS_CASE(STATUS) \
+ case STATUS: \
+ return #STATUS
+
+ switch (s) {
+ STATUS_CASE(OK);
+ STATUS_CASE(UNKNOWN_ERROR);
+ STATUS_CASE(NO_MEMORY);
+ STATUS_CASE(INVALID_OPERATION);
+ STATUS_CASE(BAD_VALUE);
+ STATUS_CASE(BAD_TYPE);
+ STATUS_CASE(NAME_NOT_FOUND);
+ STATUS_CASE(PERMISSION_DENIED);
+ STATUS_CASE(NO_INIT);
+ STATUS_CASE(ALREADY_EXISTS);
+ STATUS_CASE(DEAD_OBJECT);
+ STATUS_CASE(FAILED_TRANSACTION);
+ STATUS_CASE(BAD_INDEX);
+ STATUS_CASE(NOT_ENOUGH_DATA);
+ STATUS_CASE(WOULD_BLOCK);
+ STATUS_CASE(TIMED_OUT);
+ STATUS_CASE(UNKNOWN_TRANSACTION);
+ STATUS_CASE(FDS_NOT_ALLOWED);
+ STATUS_CASE(UNEXPECTED_NULL);
+#undef STATUS_CASE
+ }
+
+ return std::to_string(s) + ' ' + strerror(-s);
+}
+
+} // namespace android
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 1e03677..d14d223 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -19,6 +19,7 @@
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
+#include <string>
namespace android {
@@ -72,6 +73,9 @@
UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
};
+// Human readable name of error
+std::string statusToString(status_t status);
+
// Restore define; enumeration is in "android" namespace, so the value defined
// there won't work for Win32 code in a different namespace.
#ifdef _WIN32
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 56f594a..11b575e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -448,6 +448,7 @@
static const struct option opts[] = {
{"help", no_argument, 0, 'h'},
+ {},
};
if (role == kUnzip) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 70ccb80..2d14bf3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -969,6 +969,16 @@
}
}
+ if (mode & ANDROID_LOG_PSTORE) {
+ if (clearLog) {
+ unlink("/sys/fs/pstore/pmsg-ramoops-0");
+ return EXIT_SUCCESS;
+ }
+ if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+ LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+ }
+ }
+
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
nullptr, &android_logger_list_free};
if (tail_time != log_time::EPOCH) {
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 33da1f1..f4a846f 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -375,9 +375,11 @@
{"audio_hal.period_size", "u:object_r:default_prop:s0"},
{"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
{"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.boot-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
@@ -388,6 +390,7 @@
{"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.image-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 674b737..6b61472 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -935,7 +935,6 @@
on userspace-reboot-requested
# TODO(b/135984674): reset all necessary properties here.
- setprop sys.init.userspace_reboot_in_progress 1
setprop sys.boot_completed 0
setprop sys.init.updatable_crashing 0
setprop apexd.status ""
@@ -955,3 +954,6 @@
trigger zygote-start
trigger early-boot
trigger boot
+
+on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
+ finish_userspace_reboot