Merge "init: boot into thermal shutdown target on supported devices"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 7dff1b8..ba6df7a 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -337,9 +337,12 @@
case ADB_AUTH_SIGNATURE: {
// TODO: Switch to string_view.
std::string signature(p->payload.begin(), p->payload.end());
- if (adbd_auth_verify(t->token, sizeof(t->token), signature)) {
+ std::string auth_key;
+ if (adbd_auth_verify(t->token, sizeof(t->token), signature, &auth_key)) {
adbd_auth_verified(t);
t->failed_auth_attempts = 0;
+ t->auth_key = auth_key;
+ adbd_notify_framework_connected_key(t);
} else {
if (t->failed_auth_attempts++ > 256) std::this_thread::sleep_for(1s);
send_auth_request(t);
@@ -348,7 +351,8 @@
}
case ADB_AUTH_RSAPUBLICKEY:
- adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
+ t->auth_key = std::string(p->payload.data());
+ adbd_auth_confirm_key(t);
break;
#endif
default:
diff --git a/adb/adb.h b/adb/adb.h
index 352b2fe..c6cb06a 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -33,6 +33,7 @@
constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
constexpr size_t MAX_PAYLOAD = 1024 * 1024;
+constexpr size_t MAX_FRAMEWORK_PAYLOAD = 64 * 1024;
constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 2fc8478..2be9a76 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -50,8 +50,10 @@
void adbd_auth_verified(atransport *t);
void adbd_cloexec_auth_socket();
-bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig);
-void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
+ std::string* auth_key);
+void adbd_auth_confirm_key(atransport* t);
+void adbd_notify_framework_connected_key(atransport* t);
void send_auth_request(atransport *t);
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 2b8f461..7a3a4f5 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -26,7 +26,9 @@
#include <resolv.h>
#include <stdio.h>
#include <string.h>
+#include <iomanip>
+#include <algorithm>
#include <memory>
#include <android-base/file.h>
@@ -38,22 +40,24 @@
static fdevent* listener_fde = nullptr;
static fdevent* framework_fde = nullptr;
-static int framework_fd = -1;
+static auto& framework_mutex = *new std::mutex();
+static int framework_fd GUARDED_BY(framework_mutex) = -1;
+static auto& connected_keys GUARDED_BY(framework_mutex) = *new std::vector<std::string>;
-static void usb_disconnected(void* unused, atransport* t);
-static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
-static atransport* usb_transport;
+static void adb_disconnected(void* unused, atransport* t);
+static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
+static atransport* adb_transport;
static bool needs_retry = false;
bool auth_required = true;
-bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig) {
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
+ std::string* auth_key) {
static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
for (const auto& path : key_paths) {
if (access(path, R_OK) == 0) {
LOG(INFO) << "Loading keys from " << path;
-
std::string content;
if (!android::base::ReadFileToString(path, &content)) {
PLOG(ERROR) << "Couldn't read " << path;
@@ -61,6 +65,8 @@
}
for (const auto& line : android::base::Split(content, "\n")) {
+ if (line.empty()) continue;
+ *auth_key = line;
// TODO: do we really have to support both ' ' and '\t'?
char* sep = strpbrk(const_cast<char*>(line.c_str()), " \t");
if (sep) *sep = '\0';
@@ -88,9 +94,31 @@
}
}
}
+ auth_key->clear();
return false;
}
+static bool adbd_send_key_message_locked(std::string_view msg_type, std::string_view key)
+ REQUIRES(framework_mutex) {
+ if (framework_fd < 0) {
+ LOG(ERROR) << "Client not connected to send msg_type " << msg_type;
+ return false;
+ }
+ std::string msg = std::string(msg_type) + std::string(key);
+ int msg_len = msg.length();
+ if (msg_len >= static_cast<int>(MAX_FRAMEWORK_PAYLOAD)) {
+ LOG(ERROR) << "Key too long (" << msg_len << ")";
+ return false;
+ }
+
+ LOG(DEBUG) << "Sending '" << msg << "'";
+ if (!WriteFdExactly(framework_fd, msg.c_str(), msg_len)) {
+ PLOG(ERROR) << "Failed to write " << msg_type;
+ return false;
+ }
+ return true;
+}
+
static bool adbd_auth_generate_token(void* token, size_t token_size) {
FILE* fp = fopen("/dev/urandom", "re");
if (!fp) return false;
@@ -99,17 +127,28 @@
return okay;
}
-static void usb_disconnected(void* unused, atransport* t) {
- LOG(INFO) << "USB disconnect";
- usb_transport = nullptr;
+static void adb_disconnected(void* unused, atransport* t) {
+ LOG(INFO) << "ADB disconnect";
+ adb_transport = nullptr;
needs_retry = false;
+ {
+ std::lock_guard<std::mutex> lock(framework_mutex);
+ if (framework_fd >= 0) {
+ adbd_send_key_message_locked("DC", t->auth_key);
+ }
+ connected_keys.erase(std::remove(connected_keys.begin(), connected_keys.end(), t->auth_key),
+ connected_keys.end());
+ }
}
static void framework_disconnected() {
LOG(INFO) << "Framework disconnect";
if (framework_fde) {
fdevent_destroy(framework_fde);
- framework_fd = -1;
+ {
+ std::lock_guard<std::mutex> lock(framework_mutex);
+ framework_fd = -1;
+ }
}
}
@@ -120,41 +159,28 @@
if (ret <= 0) {
framework_disconnected();
} else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
- if (usb_transport) {
- adbd_auth_verified(usb_transport);
+ if (adb_transport) {
+ adbd_auth_verified(adb_transport);
}
}
}
}
-void adbd_auth_confirm_key(const char* key, size_t len, atransport* t) {
- if (!usb_transport) {
- usb_transport = t;
- t->AddDisconnect(&usb_disconnect);
+void adbd_auth_confirm_key(atransport* t) {
+ if (!adb_transport) {
+ adb_transport = t;
+ t->AddDisconnect(&adb_disconnect);
}
- if (framework_fd < 0) {
- LOG(ERROR) << "Client not connected";
- needs_retry = true;
- return;
- }
+ {
+ std::lock_guard<std::mutex> lock(framework_mutex);
+ if (framework_fd < 0) {
+ LOG(ERROR) << "Client not connected";
+ needs_retry = true;
+ return;
+ }
- if (key[len - 1] != '\0') {
- LOG(ERROR) << "Key must be a null-terminated string";
- return;
- }
-
- char msg[MAX_PAYLOAD_V1];
- int msg_len = snprintf(msg, sizeof(msg), "PK%s", key);
- if (msg_len >= static_cast<int>(sizeof(msg))) {
- LOG(ERROR) << "Key too long (" << msg_len << ")";
- return;
- }
- LOG(DEBUG) << "Sending '" << msg << "'";
-
- if (unix_write(framework_fd, msg, msg_len) == -1) {
- PLOG(ERROR) << "Failed to write PK";
- return;
+ adbd_send_key_message_locked("PK", t->auth_key);
}
}
@@ -165,18 +191,46 @@
return;
}
- if (framework_fd >= 0) {
- LOG(WARNING) << "adb received framework auth socket connection again";
- framework_disconnected();
+ {
+ std::lock_guard<std::mutex> lock(framework_mutex);
+ if (framework_fd >= 0) {
+ LOG(WARNING) << "adb received framework auth socket connection again";
+ framework_disconnected();
+ }
+
+ framework_fd = s;
+ framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
+ fdevent_add(framework_fde, FDE_READ);
+
+ if (needs_retry) {
+ needs_retry = false;
+ send_auth_request(adb_transport);
+ }
+
+ // if a client connected before the framework was available notify the framework of the
+ // connected key now.
+ if (!connected_keys.empty()) {
+ for (const auto& key : connected_keys) {
+ adbd_send_key_message_locked("CK", key);
+ }
+ }
}
+}
- framework_fd = s;
- framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
- fdevent_add(framework_fde, FDE_READ);
-
- if (needs_retry) {
- needs_retry = false;
- send_auth_request(usb_transport);
+void adbd_notify_framework_connected_key(atransport* t) {
+ if (!adb_transport) {
+ adb_transport = t;
+ t->AddDisconnect(&adb_disconnect);
+ }
+ {
+ std::lock_guard<std::mutex> lock(framework_mutex);
+ if (std::find(connected_keys.begin(), connected_keys.end(), t->auth_key) ==
+ connected_keys.end()) {
+ connected_keys.push_back(t->auth_key);
+ }
+ if (framework_fd >= 0) {
+ adbd_send_key_message_locked("CK", t->auth_key);
+ }
}
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f4aa9fb..1abae87 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -509,16 +509,14 @@
}
if (id.direction == TransferDirection::READ) {
- if (!HandleRead(id, event.res)) {
- return;
- }
+ HandleRead(id, event.res);
} else {
HandleWrite(id);
}
}
}
- bool HandleRead(TransferId id, int64_t size) {
+ void HandleRead(TransferId id, int64_t size) {
uint64_t read_idx = id.id % kUsbReadQueueDepth;
IoBlock* block = &read_requests_[read_idx];
block->pending = false;
@@ -528,7 +526,7 @@
if (block->id().id != needed_read_id_) {
LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
<< needed_read_id_;
- return true;
+ return;
}
for (uint64_t id = needed_read_id_;; ++id) {
@@ -537,22 +535,15 @@
if (current_block->pending) {
break;
}
- if (!ProcessRead(current_block)) {
- return false;
- }
+ ProcessRead(current_block);
++needed_read_id_;
}
-
- return true;
}
- bool ProcessRead(IoBlock* block) {
+ void ProcessRead(IoBlock* block) {
if (!block->payload->empty()) {
if (!incoming_header_.has_value()) {
- if (block->payload->size() != sizeof(amessage)) {
- HandleError("received packet of unexpected length while reading header");
- return false;
- }
+ CHECK_EQ(sizeof(amessage), block->payload->size());
amessage msg;
memcpy(&msg, block->payload->data(), sizeof(amessage));
LOG(DEBUG) << "USB read:" << dump_header(&msg);
@@ -560,10 +551,7 @@
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
Block payload = std::move(*block->payload);
- if (block->payload->size() > bytes_left) {
- HandleError("received too many bytes while waiting for payload");
- return false;
- }
+ CHECK_LE(payload.size(), bytes_left);
incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
}
@@ -582,7 +570,6 @@
PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
SubmitRead(block);
- return true;
}
bool SubmitRead(IoBlock* block) {
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
new file mode 100644
index 0000000..71e32b7
--- /dev/null
+++ b/adb/tools/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary_host {
+ name: "check_ms_os_desc",
+
+ defaults: ["adb_defaults"],
+
+ srcs: [
+ "check_ms_os_desc.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libusb",
+ ],
+
+ stl: "libc++_static",
+
+ dist: {
+ targets: [
+ "sdk",
+ ],
+ },
+}
diff --git a/adb/tools/check_ms_os_desc.cpp b/adb/tools/check_ms_os_desc.cpp
new file mode 100644
index 0000000..d4cfb61
--- /dev/null
+++ b/adb/tools/check_ms_os_desc.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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 <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <libusb/libusb.h>
+
+static bool is_adb_device(libusb_device* device) {
+ libusb_device_descriptor device_desc;
+ libusb_get_device_descriptor(device, &device_desc);
+ if (device_desc.bDeviceClass != 0) {
+ return false;
+ }
+
+ libusb_config_descriptor* config_desc;
+ int rc = libusb_get_active_config_descriptor(device, &config_desc);
+ if (rc != 0) {
+ fprintf(stderr, "failed to get config descriptor for device %u:%u: %s\n",
+ libusb_get_bus_number(device), libusb_get_port_number(device),
+ libusb_error_name(rc));
+ return false;
+ }
+
+ for (size_t i = 0; i < config_desc->bNumInterfaces; ++i) {
+ const libusb_interface* interface = &config_desc->interface[i];
+ for (int j = 0; j < interface->num_altsetting; ++j) {
+ const libusb_interface_descriptor* interface_descriptor = &interface->altsetting[j];
+ if (interface_descriptor->bInterfaceClass == 0xff &&
+ interface_descriptor->bInterfaceSubClass == 0x42 &&
+ interface_descriptor->bInterfaceProtocol == 1) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static std::optional<std::vector<uint8_t>> get_descriptor(libusb_device_handle* handle,
+ uint8_t type, uint8_t index,
+ uint16_t length) {
+ std::vector<uint8_t> result;
+ result.resize(length);
+ int rc = libusb_get_descriptor(handle, type, index, result.data(), result.size());
+ if (rc < 0) {
+ fprintf(stderr, "libusb_get_descriptor failed: %s\n", libusb_error_name(rc));
+ return std::nullopt;
+ }
+ result.resize(rc);
+ return result;
+}
+
+static std::optional<std::string> get_string_descriptor(libusb_device_handle* handle,
+ uint8_t index) {
+ std::string result;
+ result.resize(4096);
+ int rc = libusb_get_string_descriptor_ascii(
+ handle, index, reinterpret_cast<uint8_t*>(result.data()), result.size());
+ if (rc < 0) {
+ fprintf(stderr, "libusb_get_string_descriptor_ascii failed: %s\n", libusb_error_name(rc));
+ return std::nullopt;
+ }
+ result.resize(rc);
+ return result;
+}
+
+static void check_ms_os_desc_v1(libusb_device_handle* device_handle, const std::string& serial) {
+ auto os_desc = get_descriptor(device_handle, 0x03, 0xEE, 0x12);
+ if (!os_desc) {
+ errx(1, "failed to retrieve MS OS descriptor");
+ }
+
+ if (os_desc->size() != 0x12) {
+ errx(1, "os descriptor size mismatch");
+ }
+
+ if (memcmp(os_desc->data() + 2, u"MSFT100\0", 14) != 0) {
+ errx(1, "os descriptor signature mismatch");
+ }
+
+ uint8_t vendor_code = (*os_desc)[16];
+ uint8_t pad = (*os_desc)[17];
+
+ if (pad != 0) {
+ errx(1, "os descriptor padding non-zero");
+ }
+
+ std::vector<uint8_t> data;
+ data.resize(0x10);
+ int rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
+ data.size(), 0);
+ if (rc != 0x10) {
+ errx(1, "failed to retrieve MS OS v1 compat descriptor header: %s", libusb_error_name(rc));
+ }
+
+ struct __attribute__((packed)) ms_os_desc_v1_header {
+ uint32_t dwLength;
+ uint16_t bcdVersion;
+ uint16_t wIndex;
+ uint8_t bCount;
+ uint8_t reserved[7];
+ };
+ static_assert(sizeof(ms_os_desc_v1_header) == 0x10);
+
+ ms_os_desc_v1_header hdr;
+ memcpy(&hdr, data.data(), data.size());
+
+ data.resize(hdr.dwLength);
+ rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
+ data.size(), 0);
+ if (static_cast<size_t>(rc) != data.size()) {
+ errx(1, "failed to retrieve MS OS v1 compat descriptor: %s", libusb_error_name(rc));
+ }
+
+ memcpy(&hdr, data.data(), data.size());
+
+ struct __attribute__((packed)) ms_os_desc_v1_function {
+ uint8_t bFirstInterfaceNumber;
+ uint8_t reserved1;
+ uint8_t compatibleID[8];
+ uint8_t subCompatibleID[8];
+ uint8_t reserved2[6];
+ };
+
+ if (sizeof(ms_os_desc_v1_header) + hdr.bCount * sizeof(ms_os_desc_v1_function) != data.size()) {
+ errx(1, "MS OS v1 compat descriptor size mismatch");
+ }
+
+ for (int i = 0; i < hdr.bCount; ++i) {
+ ms_os_desc_v1_function function;
+ memcpy(&function,
+ data.data() + sizeof(ms_os_desc_v1_header) + i * sizeof(ms_os_desc_v1_function),
+ sizeof(function));
+ if (memcmp("WINUSB\0\0", function.compatibleID, 8) == 0) {
+ return;
+ }
+ }
+
+ errx(1, "failed to find v1 MS OS descriptor specifying WinUSB for device %s", serial.c_str());
+}
+
+int main(int argc, char** argv) {
+ libusb_context* ctx;
+ if (libusb_init(&ctx) != 0) {
+ errx(1, "failed to initialize libusb context");
+ }
+
+ libusb_device** device_list = nullptr;
+ ssize_t device_count = libusb_get_device_list(ctx, &device_list);
+ if (device_count < 0) {
+ errx(1, "libusb_get_device_list failed");
+ }
+
+ const char* expected_serial = getenv("ANDROID_SERIAL");
+ bool found = false;
+
+ for (ssize_t i = 0; i < device_count; ++i) {
+ libusb_device* device = device_list[i];
+ if (!is_adb_device(device)) {
+ continue;
+ }
+
+ libusb_device_handle* device_handle = nullptr;
+ int rc = libusb_open(device, &device_handle);
+ if (rc != 0) {
+ fprintf(stderr, "failed to open device %u:%u: %s\n", libusb_get_bus_number(device),
+ libusb_get_port_number(device), libusb_error_name(rc));
+ continue;
+ }
+
+ libusb_device_descriptor device_desc;
+ libusb_get_device_descriptor(device, &device_desc);
+
+ std::optional<std::string> serial =
+ get_string_descriptor(device_handle, device_desc.iSerialNumber);
+ if (!serial) {
+ errx(1, "failed to get serial for device %u:%u", libusb_get_bus_number(device),
+ libusb_get_port_number(device));
+ }
+
+ if (expected_serial && *serial != expected_serial) {
+ fprintf(stderr, "skipping %s (wanted %s)\n", serial->c_str(), expected_serial);
+ continue;
+ }
+
+ // Check for MS OS Descriptor v1.
+ // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeusb/c2f351f9-84d2-4a1b-9fe3-a6ca195f84d0
+ fprintf(stderr, "fetching v1 OS descriptor from device %s\n", serial->c_str());
+ check_ms_os_desc_v1(device_handle, *serial);
+ fprintf(stderr, "found v1 OS descriptor for device %s\n", serial->c_str());
+
+ // TODO: Read BOS for MS OS Descriptor 2.0 descriptors:
+ // http://download.microsoft.com/download/3/5/6/3563ED4A-F318-4B66-A181-AB1D8F6FD42D/MS_OS_2_0_desc.docx
+
+ found = true;
+ }
+
+ if (expected_serial && !found) {
+ errx(1, "failed to find device with serial %s", expected_serial);
+ }
+ return 0;
+}
diff --git a/adb/transport.h b/adb/transport.h
index 245037e..61a3d9a 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -275,6 +275,9 @@
std::string device;
std::string devpath;
+ // Used to provide the key to the framework.
+ std::string auth_key;
+
bool IsTcpDevice() const { return type == kTransportLocal; }
#if ADB_HOST
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
index 6799c1f..330040d 100644
--- a/base/include/android-base/format.h
+++ b/base/include/android-base/format.h
@@ -18,8 +18,11 @@
// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
// It is accessed through its normal fmt:: namespace.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#include <fmt/chrono.h>
+#pragma clang diagnostic pop
#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/printf.h>
-#include <fmt/time.h>
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 6e11b4e..1605daf 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -120,7 +120,7 @@
// Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
bool operator!() const = delete;
- bool ok() const { return get() != -1; }
+ bool ok() const { return get() >= 0; }
int release() __attribute__((warn_unused_result)) {
tag(fd_, this, nullptr);
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index cd9fda3..dd24aac 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -1093,8 +1093,8 @@
void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
double time_since_last_boot_sec) {
- const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
- const auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
+ auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
+ auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
system_reason.c_str(), end_time.count(), total_duration.count(),
(int64_t)bootloader_duration_ms,
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2e226da..0602e0a 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -194,6 +194,7 @@
cc_test {
name: "debuggerd_test",
defaults: ["debuggerd_defaults"],
+ require_root: true,
cflags: ["-Wno-missing-field-initializers"],
srcs: [
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 716fe95..978eed0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -134,8 +134,6 @@
"libfs_mgr",
"libgsi",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"liblp",
"libsparse",
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 409ef70..4c77c75 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -194,23 +194,6 @@
return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
}
-bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- if (args.size() < 2) {
- return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
- }
-
- if (GetDeviceLockStatus()) {
- return device->WriteStatus(FastbootResult::FAIL,
- "Flashing is not allowed on locked devices");
- }
-
- int ret = Flash(device, args[1]);
- if (ret < 0) {
- return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
- }
- return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
-}
-
bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
@@ -440,6 +423,11 @@
if (!partition) {
return device->WriteFail("Partition does not exist");
}
+
+ // Remove the updated flag to cancel any snapshots.
+ uint32_t attrs = partition->attributes();
+ partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
if (!builder->ResizePartition(partition, partition_size)) {
return device->WriteFail("Not enough space to resize partition");
}
@@ -449,6 +437,42 @@
return device->WriteOkay("Partition resized");
}
+void CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {
+ PartitionBuilder builder(device, partition_name);
+ if (!builder.Valid()) return;
+
+ auto partition = builder->FindPartition(partition_name);
+ if (!partition) return;
+
+ // Remove the updated flag to cancel any snapshots.
+ uint32_t attrs = partition->attributes();
+ partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
+ builder.Write();
+}
+
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() < 2) {
+ return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+ }
+
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL,
+ "Flashing is not allowed on locked devices");
+ }
+
+ const auto& partition_name = args[1];
+ if (LogicalPartitionExists(device, partition_name)) {
+ CancelPartitionSnapshot(device, partition_name);
+ }
+
+ int ret = Flash(device, partition_name);
+ if (ret < 0) {
+ return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
+ }
+ return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
+}
+
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteFail("Invalid arguments");
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8923f40..4737ae4 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -160,6 +160,10 @@
"vbmeta_system",
true, ImageType::BootCritical },
{ "vendor", "vendor.img", "vendor.sig", "vendor", true, ImageType::Normal },
+ { "vendor_boot",
+ "vendor_boot.img", "vendor_boot.sig",
+ "vendor_boot",
+ true, ImageType::BootCritical },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
@@ -2054,10 +2058,10 @@
if (partition == "userdata" && set_fbe_marker) {
fprintf(stderr, "setting FBE marker on initial userdata...\n");
std::string initial_userdata_dir = create_fbemarker_tmpdir();
- fb_perform_format(partition, 1, "", "", initial_userdata_dir);
+ fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir);
delete_fbemarker_tmpdir(initial_userdata_dir);
} else {
- fb_perform_format(partition, 1, "", "", "");
+ fb_perform_format(partition, 1, partition_type, "", "");
}
}
}
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index fea0a77..b897182 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -291,7 +291,7 @@
RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
std::vector<std::string>* info) {
RetCode ret;
- int dsize;
+ int dsize = 0;
if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
error_ = "Upload request failed: " + error_;
return ret;
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index fe2e052..bb63df8 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -1,33 +1,27 @@
-Android Overlayfs integration with adb remount
+Android OverlayFS Integration with adb Remount
==============================================
Introduction
------------
-Users working with userdebug or eng builds expect to be able to
-remount the system partition as read-write and then add or modify
-any number of files without reflashing the system image, which is
-understandably efficient for a development cycle.
-Limited memory systems that chose to use readonly filesystems like
-*squashfs*, or *Logical Resizable Android Partitions* which land
-system partition images right-sized, and with filesystem that have
-been deduped on the block level to compress the content; means that
-either a remount is not possible directly, or when done offers
-little or no utility because of remaining space limitations or
-support logistics.
+Users working with userdebug or eng builds expect to be able to remount the
+system partition as read-write and then add or modify any number of files
+without reflashing the system image, which is efficient for a development cycle.
-*Overlayfs* comes to the rescue for these debug scenarios, and logic
-will _automatically_ setup backing storage for a writable filesystem
-as an upper reference, and mount overtop the lower. These actions
-will be performed in the **adb disable-verity** and **adb remount**
-requests.
+Limited memory systems use read-only types of file systems or logical resizable
+Android partitions (LRAPs). These file systems land system partition images
+right-sized, and have been deduped at the block level to compress the content.
+This means that a remount either isn’t possible, or isn't useful because of
+space limitations or support logistics.
-Operations
-----------
+OverlayFS resolves these debug scenarios with the _adb disable-verity_ and
+_adb remount_ commands, which set up backing storage for a writable file
+system as an upper reference, and mount the lower reference on top.
-### Cookbook
+Performing a remount
+--------------------
-The typical action to utilize the remount facility is:
+Use the following sequence to perform the remount.
$ adb root
$ adb disable-verity
@@ -36,7 +30,7 @@
$ adb root
$ adb remount
-Followed by one of the following:
+Then enter one of the following sequences:
$ adb stop
$ adb sync
@@ -48,75 +42,67 @@
$ adb push <source> <destination>
$ adb reboot
-Note that the sequence above:
+Note that you can replace these two lines:
$ adb disable-verity
$ adb reboot
-*or*
-
- $ adb remount
-
-can be replaced in both places with:
+with this line:
$ adb remount -R
-which will not reboot if everything is already prepared and ready
-to go.
+**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
-None of this changes if *overlayfs* needs to be engaged.
-The decisions whether to use traditional direct filesystem remount,
-or one wrapped by *overlayfs* is automatically determined based on
-a probe of the filesystem types and space remaining.
+None of this changes if OverlayFS needs to be engaged.
+The decisions whether to use traditional direct file-system remount,
+or one wrapped by OverlayFS is automatically determined based on
+a probe of the file-system types and space remaining.
### Backing Storage
-When *overlayfs* logic is feasible, it will use either the
+When *OverlayFS* logic is feasible, it uses either the
**/cache/overlay/** directory for non-A/B devices, or the
**/mnt/scratch/overlay** directory for A/B devices that have
-access to *Logical Resizable Android Partitions*.
+access to *LRAP*.
+It is also possible for an A/B device to use the system_<other> partition
+for backing storage. eg: if booting off system_a+vendor_a, use system_b.
The backing store is used as soon as possible in the boot
-process and can occur at first stage init, or at the
-mount_all init rc commands.
+process and can occur at first stage init, or when the
+*mount_all* commands are run in init RC scripts.
-This early as possible attachment of *overlayfs* means that
-*sepolicy* or *init* itself can also be pushed and used after
-the exec phases that accompany each stage.
+By attaching OverlayFS early, SEpolicy or init can be pushed and used after the exec phases of each stage.
Caveats
-------
-- Space used in the backing storage is on a file by file basis
- and will require more space than if updated in place. As such
- it is important to be mindful of any wasted space, for instance
- **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
- will have a negative impact on the overall right-sizing of images
- and thus free dynamic partition space.
-- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
- with "*overlayfs: override_creds=off option bypass creator_cred*"
- if kernel is 4.4 or higher.
+- Backing storage requires more space than immutable storage, as backing is
+ done file by file. Be mindful of wasted space. For example, defining
+ **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the
+ right-sizing of images and requires more free dynamic partition space.
+- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher
+ than 4.4, it requires source to be in line with android-common kernels.
The patch series is available on the upstream mailing list and the latest as
- of Jul 24 2019 is https://lore.kernel.org/patchwork/patch/1104577/.
- This patch adds an override_creds _mount_ option to overlayfs that
+ of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html
+ This patch adds an override_creds _mount_ option to OverlayFS that
permits legacy behavior for systems that do not have overlapping
sepolicy rules, principals of least privilege, which is how Android behaves.
-- *adb enable-verity* will free up overlayfs and as a bonus the
- device will be reverted pristine to before any content was updated.
- Update engine does not take advantage of this, will perform a full OTA.
-- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
- true as adb remount overrides are incompatible with an OTA resources.
+ For 4.19 and higher a rework of the xattr handling to deal with recursion
+ is required. https://patchwork.kernel.org/patch/11117145/ is a start of that
+ adjustment.
+- _adb enable-verity_ frees up OverlayFS and reverts the device to the state
+ prior to content updates. The update engine performs a full OTA.
+- _adb remount_ overrides are incompatible with OTA resources, so the update
+ engine may not run if fs_mgr_overlayfs_is_setup() returns true.
+- If a dynamic partition runs out of space, making a logical partition larger
+ may fail because of the scratch partition. If this happens, clear the scratch
+ storage by running either either _fastboot flashall_ or _adb enable-verity_.
+ Then reinstate the overrides and continue.
- For implementation simplicity on retrofit dynamic partition devices,
take the whole alternate super (eg: if "*a*" slot, then the whole of
"*system_b*").
Since landing a filesystem on the alternate super physical device
without differentiating if it is setup to support logical or physical,
the alternate slot metadata and previous content will be lost.
-- If dynamic partitions runs out of space, resizing a logical
- partition larger may fail because of the scratch partition.
- If this happens, either fastboot flashall or adb enable-verity can
- be used to clear scratch storage to permit the flash.
- Then reinstate the overrides and continue.
-- File bugs or submit fixes for review.
- There are other subtle caveats requiring complex logic to solve.
Have evaluated them as too complex or not worth the trouble, please
File a bug if a use case needs to be covered.
@@ -125,7 +111,7 @@
out and we reserve the right to not inform, if the layering
does not prevent any messaging.
- Space remaining threshold is hard coded. If 1% or more space
- still remains, overlayfs will not be used, yet that amount of
+ still remains, OverlayFS will not be used, yet that amount of
space remaining is problematic.
- Flashing a partition via bootloader fastboot, as opposed to user
space fastbootd, is not detected, thus a partition may have
@@ -139,3 +125,4 @@
to confusion. When debugging using **adb remount** it is
currently advised to confirm update is present after a reboot
to develop confidence.
+- File bugs or submit fixes for review.
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index fa756a0..ea799ce 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -79,9 +79,9 @@
return true;
}
-static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
- const LpMetadataPartition& partition, const std::string& super_device,
- DmTable* table) {
+bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, const std::string& super_device,
+ DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -154,61 +154,77 @@
return true;
}
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
- const LpMetadata* metadata = params.metadata;
+bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {
+ if (block_device.empty()) {
+ LOG(ERROR) << "block_device is required for CreateLogicalPartition";
+ return false;
+ }
// Read metadata if needed.
- std::unique_ptr<LpMetadata> local_metadata;
if (!metadata) {
- if (!params.metadata_slot) {
+ if (!metadata_slot) {
LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
return false;
}
- auto slot = *params.metadata_slot;
- if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
- LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+ auto slot = *metadata_slot;
+ if (owned->metadata = ReadMetadata(block_device, slot); !owned->metadata) {
+ LOG(ERROR) << "Could not read partition table for: " << block_device;
return false;
}
- metadata = local_metadata.get();
+ metadata = owned->metadata.get();
}
// Find the partition by name if needed.
- const LpMetadataPartition* partition = params.partition;
if (!partition) {
- for (const auto& iter : metadata->partitions) {
- if (GetPartitionName(iter) == params.partition_name) {
- partition = &iter;
+ for (const auto& metadata_partition : metadata->partitions) {
+ if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {
+ partition = &metadata_partition;
break;
}
}
- if (!partition) {
- LERROR << "Could not find any partition with name: " << params.partition_name;
- return false;
- }
+ }
+ if (!partition) {
+ LERROR << "Could not find any partition with name: " << partition_name;
+ return false;
+ }
+ if (partition_name.empty()) {
+ partition_name = android::fs_mgr::GetPartitionName(*partition);
+ } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {
+ LERROR << "Inconsistent partition_name " << partition_name << " with partition "
+ << android::fs_mgr::GetPartitionName(*partition);
+ return false;
}
- PartitionOpener default_opener;
- const IPartitionOpener* opener =
- params.partition_opener ? params.partition_opener : &default_opener;
+ if (!partition_opener) {
+ owned->partition_opener = std::make_unique<PartitionOpener>();
+ partition_opener = owned->partition_opener.get();
+ }
+
+ if (device_name.empty()) {
+ device_name = partition_name;
+ }
+
+ return true;
+}
+
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
+ CreateLogicalPartitionParams::OwnedData owned_data;
+ if (!params.InitDefaults(&owned_data)) return false;
DmTable table;
- if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
+ if (!CreateDmTable(*params.partition_opener, *params.metadata, *params.partition,
+ params.block_device, &table)) {
return false;
}
if (params.force_writable) {
table.set_readonly(false);
}
- std::string device_name = params.device_name;
- if (device_name.empty()) {
- device_name = GetPartitionName(*partition);
- }
-
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+ if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
return false;
}
- LINFO << "Created logical partition " << device_name << " on device " << *path;
+ LINFO << "Created logical partition " << params.device_name << " on device " << *path;
return true;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index bc197cd..4dbacd7 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -706,10 +706,12 @@
// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
-// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
-// GSI will be used.
+// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
+// only common files for all targets can be put into system partition. It is under
+// /system/system_ext because GSI is a single system.img that includes the contents of system_ext
+// partition and product partition under /system/system_ext and /system/product, respectively.
bool SkipMountingPartitions(Fstab* fstab) {
- constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+ constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
std::string skip_config;
auto save_errno = errno;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index d7ea81d..358c980 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -713,7 +713,7 @@
}
report = report + ")=";
- auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+ auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
options.c_str());
if (ret) {
retval = false;
@@ -776,7 +776,7 @@
entry.fs_type = mnt_type;
if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
- entry.flags = MS_RELATIME;
+ entry.flags = MS_NOATIME;
if (readonly) {
entry.flags |= MS_RDONLY;
} else {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 2054fa1..a912208 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -81,14 +81,34 @@
// Helpers for determining the effective partition and device name.
std::string GetPartitionName() const;
std::string GetDeviceName() const;
+
+ // Specify ownership of fields. The ownership of these fields are managed
+ // by the caller of InitDefaults().
+ // These are not declared in CreateLogicalPartitionParams so that the
+ // copy constructor is not deleted.
+ struct OwnedData {
+ std::unique_ptr<LpMetadata> metadata;
+ std::unique_ptr<IPartitionOpener> partition_opener;
+ };
+
+ // Fill in default values for |params| that CreateLogicalPartition assumes. Caller does
+ // not need to call this before calling CreateLogicalPartition; CreateLogicalPartition sets
+ // values when they are missing.
+ // Caller is responsible for destroying owned_data when |this| is not used.
+ bool InitDefaults(OwnedData* owned);
};
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path);
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
bool DestroyLogicalPartition(const std::string& name);
+// Helper for populating a DmTable for a logical partition.
+bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, const std::string& super_device,
+ android::dm::DmTable* table);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 77c9c28..c5d6a3b 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -45,7 +45,7 @@
return true;
}
-Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
: name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
@@ -232,7 +232,7 @@
memset(&header_, 0, sizeof(header_));
header_.magic = LP_METADATA_HEADER_MAGIC;
header_.major_version = LP_METADATA_MAJOR_VERSION;
- header_.minor_version = LP_METADATA_MINOR_VERSION;
+ header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
header_.header_size = sizeof(header_);
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
@@ -426,7 +426,7 @@
return true;
}
-bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+bool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {
if (FindGroup(group_name)) {
LERROR << "Group already exists: " << group_name;
return false;
@@ -436,10 +436,10 @@
}
Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
- return AddPartition(name, std::string(kDefaultGroup), attributes);
+ return AddPartition(name, kDefaultGroup, attributes);
}
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
+Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,
uint32_t attributes) {
if (name.empty()) {
LERROR << "Partition must have a non-empty name.";
@@ -457,7 +457,7 @@
return partitions_.back().get();
}
-Partition* MetadataBuilder::FindPartition(const std::string& name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) {
for (const auto& partition : partitions_) {
if (partition->name() == name) {
return partition.get();
@@ -579,12 +579,38 @@
return true;
}
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+Interval Interval::Intersect(const Interval& a, const Interval& b) {
+ Interval ret = a;
+ if (a.device_index != b.device_index) {
+ ret.start = ret.end = a.start; // set length to 0 to indicate no intersection.
+ return ret;
+ }
+ ret.start = std::max(a.start, b.start);
+ ret.end = std::max(ret.start, std::min(a.end, b.end));
+ return ret;
+}
+
+std::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,
+ const std::vector<Interval>& b) {
+ std::vector<Interval> ret;
+ for (const Interval& a_interval : a) {
+ for (const Interval& b_interval : b) {
+ auto intersect = Intersect(a_interval, b_interval);
+ if (intersect.length() > 0) ret.emplace_back(std::move(intersect));
+ }
+ }
+ return ret;
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,
+ const std::vector<Interval>& free_region_hint) {
uint64_t space_needed = aligned_size - partition->size();
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
std::vector<Interval> free_regions = GetFreeRegions();
+ if (!free_region_hint.empty())
+ free_regions = Interval::Intersect(free_regions, free_region_hint);
const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
CHECK_NE(sectors_per_block, 0);
@@ -650,7 +676,7 @@
return true;
}
-std::vector<MetadataBuilder::Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
const std::vector<Interval>& free_list) {
const auto& super = block_devices_[0];
uint64_t first_sector = super.first_logical_sector;
@@ -796,6 +822,11 @@
return nullptr;
}
+ if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
+ static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
+ metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
+ }
+
strncpy(part.name, partition->name().c_str(), sizeof(part.name));
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
@@ -921,7 +952,8 @@
return true;
}
-bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
+ const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
@@ -931,7 +963,7 @@
}
if (aligned_size > old_size) {
- if (!GrowPartition(partition, aligned_size)) {
+ if (!GrowPartition(partition, aligned_size, free_region_hint)) {
return false;
}
} else if (aligned_size < partition->size()) {
@@ -953,7 +985,7 @@
return names;
}
-void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+void MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {
if (group_name == kDefaultGroup) {
// Cannot remove the default group.
return;
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index dde6d07..bd41f59 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -349,7 +349,7 @@
const LpMetadataHeader& header = exported->header;
EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
- EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+ EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);
ASSERT_EQ(exported->partitions.size(), 2);
ASSERT_EQ(exported->extents.size(), 3);
@@ -887,3 +887,38 @@
std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
}
+
+// Interval has operator< defined; it is not appropriate to re-define Interval::operator== that
+// compares device index.
+namespace android {
+namespace fs_mgr {
+bool operator==(const Interval& a, const Interval& b) {
+ return a.device_index == b.device_index && a.start == b.start && a.end == b.end;
+}
+} // namespace fs_mgr
+} // namespace android
+
+TEST_F(BuilderTest, Interval) {
+ EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 100)).length());
+ EXPECT_EQ(Interval(0, 100, 150),
+ Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 150)));
+ EXPECT_EQ(Interval(0, 100, 200),
+ Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 200)));
+ EXPECT_EQ(Interval(0, 100, 200),
+ Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 250)));
+ EXPECT_EQ(Interval(0, 100, 200),
+ Interval::Intersect(Interval(0, 100, 200), Interval(0, 100, 200)));
+ EXPECT_EQ(Interval(0, 150, 200),
+ Interval::Intersect(Interval(0, 100, 200), Interval(0, 150, 250)));
+ EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 200, 250)).length());
+
+ auto v = Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50), Interval(0, 100, 150)},
+ std::vector<Interval>{Interval(0, 25, 125)});
+ ASSERT_EQ(2, v.size());
+ EXPECT_EQ(Interval(0, 25, 50), v[0]);
+ EXPECT_EQ(Interval(0, 100, 125), v[1]);
+
+ EXPECT_EQ(0u, Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50)},
+ std::vector<Interval>{Interval(0, 100, 150)})
+ .size());
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index c3f6e91..6f2ab75 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -91,7 +91,7 @@
friend class MetadataBuilder;
public:
- explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+ explicit PartitionGroup(std::string_view name, uint64_t maximum_size)
: name_(name), maximum_size_(maximum_size) {}
const std::string& name() const { return name_; }
@@ -108,7 +108,7 @@
friend class MetadataBuilder;
public:
- Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
+ Partition(std::string_view name, std::string_view group_name, uint32_t attributes);
// Add a raw extent.
void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -123,6 +123,7 @@
const std::string& name() const { return name_; }
const std::string& group_name() const { return group_name_; }
uint32_t attributes() const { return attributes_; }
+ void set_attributes(uint32_t attributes) { attributes_ = attributes; }
const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
uint64_t size() const { return size_; }
@@ -137,6 +138,33 @@
uint64_t size_;
};
+// An interval in the metadata. This is similar to a LinearExtent with one difference.
+// LinearExtent represents a "used" region in the metadata, while Interval can also represent
+// an "unused" region.
+struct Interval {
+ uint32_t device_index;
+ uint64_t start;
+ uint64_t end;
+
+ Interval(uint32_t device_index, uint64_t start, uint64_t end)
+ : device_index(device_index), start(start), end(end) {}
+ uint64_t length() const { return end - start; }
+
+ // Note: the device index is not included in sorting (intervals are
+ // sorted in per-device lists).
+ bool operator<(const Interval& other) const {
+ return (start == other.start) ? end < other.end : start < other.start;
+ }
+
+ // Intersect |a| with |b|.
+ // If no intersection, result has 0 length().
+ static Interval Intersect(const Interval& a, const Interval& b);
+
+ // Intersect two lists of intervals, and store result to |a|.
+ static std::vector<Interval> Intersect(const std::vector<Interval>& a,
+ const std::vector<Interval>& b);
+};
+
class MetadataBuilder {
public:
// Construct an empty logical partition table builder given the specified
@@ -205,7 +233,7 @@
// total space used by all partitions in the group.
//
// This can fail and return false if the group already exists.
- bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+ bool AddGroup(std::string_view group_name, uint64_t maximum_size);
// Export metadata so it can be serialized to an image, to disk, or mounted
// via device-mapper.
@@ -213,7 +241,7 @@
// Add a partition, returning a handle so it can be sized as needed. If a
// partition with the given name already exists, nullptr is returned.
- Partition* AddPartition(const std::string& name, const std::string& group_name,
+ Partition* AddPartition(std::string_view name, std::string_view group_name,
uint32_t attributes);
// Same as AddPartition above, but uses the default partition group which
@@ -224,7 +252,7 @@
void RemovePartition(const std::string& name);
// Find a partition by name. If no partition is found, nullptr is returned.
- Partition* FindPartition(const std::string& name);
+ Partition* FindPartition(std::string_view name);
// Find a group by name. If no group is found, nullptr is returned.
PartitionGroup* FindGroup(std::string_view name);
@@ -243,7 +271,11 @@
//
// Note, this is an in-memory operation, and it does not alter the
// underlying filesystem or contents of the partition on disk.
- bool ResizePartition(Partition* partition, uint64_t requested_size);
+ //
+ // If |free_region_hint| is not empty, it will only try to allocate extents
+ // in regions within the list.
+ bool ResizePartition(Partition* partition, uint64_t requested_size,
+ const std::vector<Interval>& free_region_hint = {});
// Return the list of partitions belonging to a group.
std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
@@ -268,7 +300,7 @@
std::vector<std::string> ListGroups() const;
// Remove all partitions belonging to a group, then remove the group.
- void RemoveGroupAndPartitions(const std::string& group_name);
+ void RemoveGroupAndPartitions(std::string_view group_name);
// Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
void SetAutoSlotSuffixing();
@@ -290,6 +322,9 @@
// Return the name of the block device at |index|.
std::string GetBlockDevicePartitionName(uint64_t index) const;
+ // Return the list of free regions not occupied by extents in the metadata.
+ std::vector<Interval> GetFreeRegions() const;
+
private:
MetadataBuilder();
MetadataBuilder(const MetadataBuilder&) = delete;
@@ -299,7 +334,8 @@
bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
- bool GrowPartition(Partition* partition, uint64_t aligned_size);
+ bool GrowPartition(Partition* partition, uint64_t aligned_size,
+ const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
@@ -322,22 +358,6 @@
bool ValidatePartitionGroups() const;
- struct Interval {
- uint32_t device_index;
- uint64_t start;
- uint64_t end;
-
- Interval(uint32_t device_index, uint64_t start, uint64_t end)
- : device_index(device_index), start(start), end(end) {}
- uint64_t length() const { return end - start; }
-
- // Note: the device index is not included in sorting (intervals are
- // sorted in per-device lists).
- bool operator<(const Interval& other) const {
- return (start == other.start) ? end < other.end : start < other.start;
- }
- };
- std::vector<Interval> GetFreeRegions() const;
bool IsAnyRegionCovered(const std::vector<Interval>& regions,
const LinearExtent& candidate) const;
bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8934aaf..6e928b4 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -39,7 +39,11 @@
/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 10
-#define LP_METADATA_MINOR_VERSION 0
+#define LP_METADATA_MINOR_VERSION_MIN 0
+#define LP_METADATA_MINOR_VERSION_MAX 1
+
+/* Metadata version needed to use the UPDATED partition attribute. */
+#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
/* Attributes for the LpMetadataPartition::attributes field.
*
@@ -58,8 +62,20 @@
*/
#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
-/* Mask that defines all valid attributes. */
-#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
+ * It signals that the partition was created (or modified) for a snapshot-based
+ * update. If this flag is not present, the partition was likely flashed via
+ * fastboot.
+ */
+#define LP_PARTITION_ATTR_UPDATED (1 << 2)
+
+/* Mask that defines all valid attributes. When changing this, make sure to
+ * update ParseMetadata().
+ */
+#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
+ (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
+#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index df89e9c..22f6746 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -713,3 +713,32 @@
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}
+
+TEST_F(LiblpTest, UpdateVirtualAB) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ DefaultPartitionOpener opener(fd);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *updated.get(), 1));
+
+ // Validate old slot.
+ auto metadata = ReadMetadata(opener, "super", 0);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->header.minor_version, 0);
+ ASSERT_GE(metadata->partitions.size(), 1);
+ ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+
+ // Validate new slot.
+ metadata = ReadMetadata(opener, "super", 1);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->header.minor_version, 1);
+ ASSERT_GE(metadata->partitions.size(), 1);
+ ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 8dbe955..aecf685 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -181,7 +181,7 @@
}
// Check that the version is compatible.
if (header.major_version != LP_METADATA_MAJOR_VERSION ||
- header.minor_version > LP_METADATA_MINOR_VERSION) {
+ header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
LERROR << "Logical partition metadata has incompatible version.";
return false;
}
@@ -245,6 +245,13 @@
return nullptr;
}
+ uint32_t valid_attributes = 0;
+ if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
+ valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
+ } else {
+ valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+ }
+
// ValidateTableSize ensured that |cursor| is valid for the number of
// entries in the table.
uint8_t* cursor = buffer.get() + header.partitions.offset;
@@ -253,7 +260,7 @@
memcpy(&partition, cursor, sizeof(partition));
cursor += header.partitions.entry_size;
- if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+ if (partition.attributes & ~valid_attributes) {
LERROR << "Logical partition has invalid attribute set.";
return nullptr;
}
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 17f05aa..afcce8f 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -269,6 +269,7 @@
<< "; this partition should not belong to this group!";
continue; // not adding to new_partition_ptrs
}
+ partition.attributes |= LP_PARTITION_ATTR_UPDATED;
partition.group_index = std::distance(new_group_ptrs.begin(), it);
new_partition_ptrs.push_back(&partition);
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index b320db8..8df9c52 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -49,6 +49,7 @@
name: "libsnapshot_sources",
srcs: [
"snapshot.cpp",
+ "utility.cpp",
],
}
@@ -86,6 +87,7 @@
"libcutils",
"libcrypto",
"libfs_mgr",
+ "libgmock",
"liblp",
"libsnapshot",
],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 0c0355d..c41a951 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -66,7 +66,11 @@
MergeCompleted,
// Merging failed due to an unrecoverable error.
- MergeFailed
+ MergeFailed,
+
+ // The update was implicitly cancelled, either by a rollback or a flash
+ // operation via fastboot. This state can only be returned by WaitForMerge.
+ Cancelled
};
class SnapshotManager final {
@@ -82,6 +86,7 @@
virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
virtual std::string GetSlotSuffix() const = 0;
+ virtual std::string GetSuperDevice(uint32_t slot) const = 0;
virtual const IPartitionOpener& GetPartitionOpener() const = 0;
};
@@ -117,12 +122,15 @@
// update has been marked successful after booting.
bool InitiateMerge();
- // Wait for the current merge to finish, then perform cleanup when it
- // completes. It is necessary to call this after InitiateMerge(), or when
- // a merge state is detected during boot.
+ // Perform any necessary post-boot actions. This should be run soon after
+ // /data is mounted.
//
- // Note that after calling WaitForMerge(), GetUpdateState() may still return
- // that a merge is in progress:
+ // If a merge is in progress, this function will block until the merge is
+ // completed. If a merge or update was cancelled, this will clean up any
+ // update artifacts and return.
+ //
+ // Note that after calling this, GetUpdateState() may still return that a
+ // merge is in progress:
// MergeFailed indicates that a fatal error occurred. WaitForMerge() may
// called any number of times again to attempt to make more progress, but
// we do not expect it to succeed if a catastrophic error occurred.
@@ -135,7 +143,7 @@
//
// MergeCompleted indicates that the update has fully completed.
// GetUpdateState will return None, and a new update can begin.
- UpdateState WaitForMerge();
+ UpdateState ProcessUpdateState();
// Find the status of the current update, if any.
//
@@ -158,6 +166,8 @@
FRIEND_TEST(SnapshotTest, CreateSnapshot);
FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+ FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
+ FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, Merge);
@@ -201,25 +211,44 @@
std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
bool Truncate(LockedFile* file);
+ enum class SnapshotState : int { None, Created, Merging, MergeCompleted };
+ static std::string to_string(SnapshotState state);
+
+ // This state is persisted per-snapshot in /metadata/ota/snapshots/.
+ struct SnapshotStatus {
+ SnapshotState state = SnapshotState::None;
+ uint64_t device_size = 0;
+ uint64_t snapshot_size = 0;
+ uint64_t cow_partition_size = 0;
+ uint64_t cow_file_size = 0;
+
+ // These are non-zero when merging.
+ uint64_t sectors_allocated = 0;
+ uint64_t metadata_sectors = 0;
+ };
+
// Create a new snapshot record. This creates the backing COW store and
// persists information needed to map the device. The device can be mapped
// with MapSnapshot().
//
- // |device_size| should be the size of the base_device that will be passed
- // via MapDevice(). |snapshot_size| should be the number of bytes in the
- // base device, starting from 0, that will be snapshotted. The cow_size
+ // |status|.device_size should be the size of the base_device that will be passed
+ // via MapDevice(). |status|.snapshot_size should be the number of bytes in the
+ // base device, starting from 0, that will be snapshotted. |status|.cow_file_size
// should be the amount of space that will be allocated to store snapshot
// deltas.
//
- // If |snapshot_size| < device_size, then the device will always
+ // If |status|.snapshot_size < |status|.device_size, then the device will always
// be mapped with two table entries: a dm-snapshot range covering
// snapshot_size, and a dm-linear range covering the remainder.
//
- // All sizes are specified in bytes, and the device and snapshot sizes
- // must be a multiple of the sector size (512 bytes). |cow_size| will
- // be rounded up to the nearest sector.
- bool CreateSnapshot(LockedFile* lock, const std::string& name, uint64_t device_size,
- uint64_t snapshot_size, uint64_t cow_size);
+ // All sizes are specified in bytes, and the device, snapshot and COW partition sizes
+ // must be a multiple of the sector size (512 bytes). COW file size will be rounded up
+ // to the nearest sector.
+ bool CreateSnapshot(LockedFile* lock, const std::string& name, SnapshotStatus status);
+
+ // |name| should be the base partition name (e.g. "system_a"). Create the
+ // backing COW image using the size previously passed to CreateSnapshot().
+ bool CreateCowImage(LockedFile* lock, const std::string& name);
// Map a snapshot device that was previously created with CreateSnapshot.
// If a merge was previously initiated, the device-mapper table will have a
@@ -229,21 +258,37 @@
// timeout_ms is 0, then no wait will occur and |dev_path| may not yet
// exist on return.
bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
- const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+ const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
+ std::string* dev_path);
- // Remove the backing copy-on-write image for the named snapshot. The
+ // Map a COW image that was previous created with CreateCowImage.
+ bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+ std::string* cow_image_device);
+
+ // Remove the backing copy-on-write image and snapshot states for the named snapshot. The
// caller is responsible for ensuring that the snapshot is unmapped.
bool DeleteSnapshot(LockedFile* lock, const std::string& name);
// Unmap a snapshot device previously mapped with MapSnapshotDevice().
bool UnmapSnapshot(LockedFile* lock, const std::string& name);
+ // Unmap a COW image device previously mapped with MapCowImage().
+ bool UnmapCowImage(const std::string& name);
+
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
// List the known snapshot names.
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
+ // Check for a cancelled or rolled back merge, returning true if such a
+ // condition was detected and handled.
+ bool HandleCancelledUpdate(LockedFile* lock);
+
+ // Remove artifacts created by the update process, such as snapshots, and
+ // set the update state to None.
+ bool RemoveAllUpdateState(LockedFile* lock);
+
// Interact with /metadata/ota/state.
std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
std::unique_ptr<LockedFile> LockShared();
@@ -252,25 +297,13 @@
bool WriteUpdateState(LockedFile* file, UpdateState state);
std::string GetStateFilePath() const;
- enum class SnapshotState : int { Created, Merging, MergeCompleted };
- static std::string to_string(SnapshotState state);
-
- // This state is persisted per-snapshot in /metadata/ota/snapshots/.
- struct SnapshotStatus {
- SnapshotState state;
- uint64_t device_size;
- uint64_t snapshot_size;
- // These are non-zero when merging.
- uint64_t sectors_allocated = 0;
- uint64_t metadata_sectors = 0;
- };
-
// Helpers for merging.
bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
bool RewriteSnapshotDeviceTable(const std::string& dm_name);
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
void AcknowledgeMergeFailure();
+ bool IsCancelledSnapshot(const std::string& snapshot_name);
// Note that these require the name of the device containing the snapshot,
// which may be the "inner" device. Use GetsnapshotDeviecName().
@@ -307,6 +340,10 @@
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status);
+ // Map the base device, COW devices, and snapshot device.
+ bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
+ std::string* path);
+
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 c975c03..f00129a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/unistd.h>
+#include <optional>
#include <thread>
#include <unordered_set>
@@ -28,12 +29,15 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
+#include "utility.h"
+
namespace android {
namespace snapshot {
@@ -46,11 +50,13 @@
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
using android::fiemap::IImageManager;
+using android::fs_mgr::CreateDmTable;
using android::fs_mgr::CreateLogicalPartition;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::LpMetadata;
using android::fs_mgr::SlotNumberForSlotSuffix;
+using std::chrono::duration_cast;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -64,6 +70,9 @@
std::string GetMetadataDir() const override { return "/metadata/ota"s; }
std::string GetSlotSuffix() const override { return fs_mgr_get_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);
+ }
private:
android::fs_mgr::PartitionOpener opener_;
@@ -93,14 +102,22 @@
metadata_dir_ = device_->GetMetadataDir();
}
-static std::string GetCowName(const std::string& snapshot_name) {
+[[maybe_unused]] static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
}
+static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
+ return snapshot_name + "-cow-img";
+}
+
static std::string GetBaseDeviceName(const std::string& partition_name) {
return partition_name + "-base";
}
+static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
+ return snapshot_name + "-inner";
+}
+
bool SnapshotManager::BeginUpdate() {
auto file = LockExclusive();
if (!file) return false;
@@ -123,24 +140,33 @@
LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
return false;
}
+ return RemoveAllUpdateState(file.get());
+}
- if (!RemoveAllSnapshots(file.get())) {
+bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
+ if (!RemoveAllSnapshots(lock)) {
LOG(ERROR) << "Could not remove all snapshots";
return false;
}
- if (!WriteUpdateState(file.get(), UpdateState::None)) {
- LOG(ERROR) << "Could not write new update state";
- return false;
- }
- return true;
+ RemoveSnapshotBootIndicator();
+
+ // If this fails, we'll keep trying to remove the update state (as the
+ // device reboots or starts a new update) until it finally succeeds.
+ return WriteUpdateState(lock, UpdateState::None);
}
bool SnapshotManager::FinishedSnapshotWrites() {
auto lock = LockExclusive();
if (!lock) return false;
- if (ReadUpdateState(lock.get()) != UpdateState::Initiated) {
+ auto update_state = ReadUpdateState(lock.get());
+ if (update_state == UpdateState::Unverified) {
+ LOG(INFO) << "FinishedSnapshotWrites already called before. Ignored.";
+ return true;
+ }
+
+ if (update_state != UpdateState::Initiated) {
LOG(ERROR) << "Can only transition to the Unverified state from the Initiated state.";
return false;
}
@@ -158,52 +184,83 @@
}
bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
- uint64_t device_size, uint64_t snapshot_size,
- uint64_t cow_size) {
+ SnapshotManager::SnapshotStatus status) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
-
+ CHECK(lock->lock_mode() == LOCK_EX);
// Sanity check these sizes. Like liblp, we guarantee the partition size
// is respected, which means it has to be sector-aligned. (This guarantee
// is useful for locating avb footers correctly). The COW size, however,
// can be arbitrarily larger than specified, so we can safely round it up.
- if (device_size % kSectorSize != 0) {
+ if (status.device_size % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name
- << " device size is not a multiple of the sector size: " << device_size;
+ << " device size is not a multiple of the sector size: " << status.device_size;
return false;
}
- if (snapshot_size % kSectorSize != 0) {
- LOG(ERROR) << "Snapshot " << name
- << " snapshot size is not a multiple of the sector size: " << snapshot_size;
+ if (status.snapshot_size % kSectorSize != 0) {
+ LOG(ERROR) << "Snapshot " << name << " snapshot size is not a multiple of the sector size: "
+ << status.snapshot_size;
return false;
}
// Round the COW size up to the nearest sector.
- cow_size += kSectorSize - 1;
- cow_size &= ~(kSectorSize - 1);
+ status.cow_file_size += kSectorSize - 1;
+ status.cow_file_size &= ~(kSectorSize - 1);
- LOG(INFO) << "Snapshot " << name << " will have COW size " << cow_size;
+ status.state = SnapshotState::Created;
+ status.sectors_allocated = 0;
+ status.metadata_sectors = 0;
- // Note, we leave the status file hanging around if we fail to create the
- // actual backing image. This is harmless, since it'll get removed when
- // CancelUpdate is called.
- SnapshotStatus status = {
- .state = SnapshotState::Created,
- .device_size = device_size,
- .snapshot_size = snapshot_size,
- };
if (!WriteSnapshotStatus(lock, name, status)) {
PLOG(ERROR) << "Could not write snapshot status: " << name;
return false;
}
+ return true;
+}
- auto cow_name = GetCowName(name);
- int cow_flags = IImageManager::CREATE_IMAGE_ZERO_FILL;
- return images_->CreateBackingImage(cow_name, cow_size, cow_flags);
+bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+ CHECK(lock);
+ CHECK(lock->lock_mode() == LOCK_EX);
+ if (!EnsureImageManager()) return false;
+
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, name, &status)) {
+ return false;
+ }
+
+ // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
+ // Sanity check this.
+ if (status.cow_file_size % kSectorSize != 0) {
+ LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
+ << status.cow_file_size;
+ return false;
+ }
+
+ std::string cow_image_name = GetCowImageDeviceName(name);
+ int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
+ if (!images_->CreateBackingImage(cow_image_name, status.cow_file_size, cow_flags)) {
+ return false;
+ }
+
+ // when the kernel creates a persistent dm-snapshot, it requires a CoW file
+ // to store the modifications. The kernel interface does not specify how
+ // the CoW is used, and there is no standard associated.
+ // By looking at the current implementation, the CoW file is treated as:
+ // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created
+ // dm-snapshot device will look like a perfect copy of the origin device;
+ // - an _EXISTING_ snapshot if the first 32 bits are equal to a
+ // kernel-specified magic number and the CoW file metadata is set as valid,
+ // so it can be used to resume the last state of a snapshot device;
+ // - an _INVALID_ snapshot otherwise.
+ // To avoid zero-filling the whole CoW file when a new dm-snapshot is
+ // created, here we zero-fill only the first 32 bits. This is a temporary
+ // workaround that will be discussed again when the kernel API gets
+ // consolidated.
+ ssize_t dm_snap_magic_size = 4; // 32 bit
+ return images_->ZeroFillNewImage(cow_image_name, dm_snap_magic_size);
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
- const std::string& base_device,
+ const std::string& base_device, const std::string& cow_device,
const std::chrono::milliseconds& timeout_ms,
std::string* dev_path) {
CHECK(lock);
@@ -249,22 +306,7 @@
uint64_t snapshot_sectors = status.snapshot_size / kSectorSize;
uint64_t linear_sectors = (status.device_size - status.snapshot_size) / kSectorSize;
- auto cow_name = GetCowName(name);
- bool ok;
- std::string cow_dev;
- if (has_local_image_manager_) {
- // If we forced a local image manager, it means we don't have binder,
- // which means first-stage init. We must use device-mapper.
- const auto& opener = device_->GetPartitionOpener();
- ok = images_->MapImageWithDeviceMapper(opener, cow_name, &cow_dev);
- } else {
- ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
- }
- if (!ok) {
- LOG(ERROR) << "Could not map image device: " << cow_name;
- return false;
- }
auto& dm = DeviceMapper::Instance();
@@ -293,14 +335,13 @@
// and a linear target in the same table. Instead, we stack them, and give the
// snapshot device a different name. It is not exposed to the caller in this
// case.
- auto snap_name = (linear_sectors > 0) ? name + "-inner" : name;
+ auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
DmTable table;
- table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_dev, mode,
+ table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create snapshot device: " << snap_name;
- images_->UnmapImageDevice(cow_name);
return false;
}
@@ -316,7 +357,6 @@
if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create outer snapshot device: " << name;
dm.DeleteDevice(snap_name);
- images_->UnmapImageDevice(cow_name);
return false;
}
}
@@ -327,9 +367,29 @@
return true;
}
+bool SnapshotManager::MapCowImage(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* cow_dev) {
+ if (!EnsureImageManager()) return false;
+ auto cow_image_name = GetCowImageDeviceName(name);
+
+ bool ok;
+ if (has_local_image_manager_) {
+ // If we forced a local image manager, it means we don't have binder,
+ // which means first-stage init. We must use device-mapper.
+ const auto& opener = device_->GetPartitionOpener();
+ ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, cow_dev);
+ } else {
+ ok = images_->MapImageDevice(cow_image_name, timeout_ms, cow_dev);
+ }
+ if (!ok) {
+ LOG(ERROR) << "Could not map image device: " << cow_image_name;
+ }
+ return ok;
+}
+
bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
@@ -337,7 +397,7 @@
}
auto& dm = DeviceMapper::Instance();
- if (dm.GetState(name) != DmDeviceState::INVALID && !dm.DeleteDevice(name)) {
+ if (!dm.DeleteDeviceIfExists(name)) {
LOG(ERROR) << "Could not delete snapshot device: " << name;
return false;
}
@@ -345,32 +405,32 @@
// There may be an extra device, since the kernel doesn't let us have a
// snapshot and linear target in the same table.
auto dm_name = GetSnapshotDeviceName(name, status);
- if (name != dm_name && dm.GetState(dm_name) != DmDeviceState::INVALID &&
- !dm.DeleteDevice(dm_name)) {
+ if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
return false;
}
- auto cow_name = GetCowName(name);
- if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
- return false;
- }
return true;
}
+bool SnapshotManager::UnmapCowImage(const std::string& name) {
+ if (!EnsureImageManager()) return false;
+ return images_->UnmapImageIfExists(GetCowImageDeviceName(name));
+}
+
bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
+ CHECK(lock->lock_mode() == LOCK_EX);
if (!EnsureImageManager()) return false;
- auto cow_name = GetCowName(name);
- if (!images_->BackingImageExists(cow_name)) {
- return true;
- }
- if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
- return false;
- }
- if (!images_->DeleteBackingImage(cow_name)) {
- return false;
+ auto cow_image_name = GetCowImageDeviceName(name);
+ if (images_->BackingImageExists(cow_image_name)) {
+ if (!images_->UnmapImageIfExists(cow_image_name)) {
+ return false;
+ }
+ if (!images_->DeleteBackingImage(cow_image_name)) {
+ return false;
+ }
}
std::string error;
@@ -576,9 +636,12 @@
// 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::WaitForMerge() {
+UpdateState SnapshotManager::ProcessUpdateState() {
while (true) {
UpdateState state = CheckMergeState();
+ if (state == UpdateState::MergeFailed) {
+ AcknowledgeMergeFailure();
+ }
if (state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need
// to keep waiting.
@@ -594,15 +657,16 @@
UpdateState SnapshotManager::CheckMergeState() {
auto lock = LockExclusive();
if (!lock) {
- AcknowledgeMergeFailure();
return UpdateState::MergeFailed;
}
- auto state = CheckMergeState(lock.get());
+ UpdateState state = CheckMergeState(lock.get());
if (state == UpdateState::MergeCompleted) {
+ // Do this inside the same lock. Failures get acknowledged without the
+ // lock, because flock() might have failed.
AcknowledgeMergeSuccess(lock.get());
- } else if (state == UpdateState::MergeFailed) {
- AcknowledgeMergeFailure();
+ } else if (state == UpdateState::Cancelled) {
+ RemoveAllUpdateState(lock.get());
}
return state;
}
@@ -624,10 +688,17 @@
// run.
break;
+ case UpdateState::Unverified:
+ // This is an edge case. Normally cancelled updates are detected
+ // via the merge poll below, but if we never started a merge, we
+ // need to also check here.
+ if (HandleCancelledUpdate(lock)) {
+ return UpdateState::Cancelled;
+ }
+ return state;
+
default:
- LOG(ERROR) << "No merge exists, cannot wait. Update state: "
- << static_cast<uint32_t>(state);
- return UpdateState::None;
+ return state;
}
std::vector<std::string> snapshots;
@@ -635,6 +706,7 @@
return UpdateState::MergeFailed;
}
+ bool cancelled = false;
bool failed = false;
bool merging = false;
bool needs_reboot = false;
@@ -652,6 +724,9 @@
break;
case UpdateState::MergeCompleted:
break;
+ case UpdateState::Cancelled:
+ cancelled = true;
+ break;
default:
LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
failed = true;
@@ -674,6 +749,14 @@
WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
return UpdateState::MergeNeedsReboot;
}
+ if (cancelled) {
+ // This is an edge case, that we handle as correctly as we sensibly can.
+ // The underlying partition has changed behind update_engine, and we've
+ // removed the snapshot as a result. The exact state of the update is
+ // undefined now, but this can only happen on an unlocked device where
+ // partitions can be flashed without wiping userdata.
+ return UpdateState::Cancelled;
+ }
return UpdateState::MergeCompleted;
}
@@ -685,17 +768,30 @@
std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
- // During a check, we decided the merge was complete, but we were unable to
- // collapse the device-mapper stack and perform COW cleanup. If we haven't
- // rebooted after this check, the device will still be a snapshot-merge
- // target. If the have rebooted, the device will now be a linear target,
- // and we can try cleanup again.
- if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
- // NB: It's okay if this fails now, we gave cleanup our best effort.
- OnSnapshotMergeComplete(lock, name, snapshot_status);
- return UpdateState::MergeCompleted;
+ if (!IsSnapshotDevice(dm_name)) {
+ if (IsCancelledSnapshot(name)) {
+ DeleteSnapshot(lock, name);
+ return UpdateState::Cancelled;
+ }
+
+ // During a check, we decided the merge was complete, but we were unable to
+ // collapse the device-mapper stack and perform COW cleanup. If we haven't
+ // rebooted after this check, the device will still be a snapshot-merge
+ // target. If the have rebooted, the device will now be a linear target,
+ // and we can try cleanup again.
+ if (snapshot_status.state == SnapshotState::MergeCompleted) {
+ // NB: It's okay if this fails now, we gave cleanup our best effort.
+ OnSnapshotMergeComplete(lock, name, snapshot_status);
+ return UpdateState::MergeCompleted;
+ }
+
+ LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+ return UpdateState::MergeFailed;
}
+ // This check is expensive so it is only enabled for debugging.
+ DCHECK(!IsCancelledSnapshot(name));
+
std::string target_type;
DmTargetSnapshot::Status status;
if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
@@ -751,12 +847,7 @@
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
- RemoveSnapshotBootIndicator();
-
- if (!WriteUpdateState(lock, UpdateState::None)) {
- // We'll try again next reboot, ad infinitum.
- return;
- }
+ RemoveAllUpdateState(lock);
}
void SnapshotManager::AcknowledgeMergeFailure() {
@@ -815,25 +906,16 @@
bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
const SnapshotStatus& status) {
- // Ideally, we would complete the following steps to collapse the device:
- // (1) Rewrite the snapshot table to be identical to the base device table.
- // (2) Rewrite the verity table to use the "snapshot" (now linear) device.
- // (3) Delete the base device.
- //
- // This should be possible once libsnapshot understands LpMetadata. In the
- // meantime, we implement a simpler solution: rewriting the snapshot table
- // to be a single dm-linear segment against the base device. While not as
- // ideal, it still lets us remove the COW device. We can remove this
- // implementation once the new method has been tested.
auto& dm = DeviceMapper::Instance();
auto dm_name = GetSnapshotDeviceName(name, status);
+ // Verify we have a snapshot-merge device.
DeviceMapper::TargetInfo target;
if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
return false;
}
if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
- // This should be impossible, it was checked above.
+ // This should be impossible, it was checked earlier.
LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
return false;
}
@@ -862,7 +944,7 @@
return false;
}
if (outer_table.size() != 2) {
- LOG(ERROR) << "Expected 2 dm-linear targets for tabble " << name
+ LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
<< ", got: " << outer_table.size();
return false;
}
@@ -882,31 +964,91 @@
}
}
- // Note: we are replacing the OUTER table here, so we do not use dm_name.
- DmTargetLinear new_target(0, num_sectors, base_device, 0);
- LOG(INFO) << "Replacing snapshot device " << name
- << " table with: " << new_target.GetParameterString();
+ // Grab the partition metadata for the snapshot.
+ uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_device = device_->GetSuperDevice(slot);
+ const auto& opener = device_->GetPartitionOpener();
+ auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read super partition metadata.";
+ return false;
+ }
+ auto partition = android::fs_mgr::FindPartition(*metadata.get(), name);
+ if (!partition) {
+ LOG(ERROR) << "Snapshot does not have a partition in super: " << name;
+ return false;
+ }
+ // Create a DmTable that is identical to the base device.
DmTable table;
- table.Emplace<DmTargetLinear>(new_target);
+ if (!CreateDmTable(opener, *metadata.get(), *partition, super_device, &table)) {
+ LOG(ERROR) << "Could not create a DmTable for partition: " << name;
+ return false;
+ }
+
+ // Note: we are replacing the *outer* table here, so we do not use dm_name.
if (!dm.LoadTableAndActivate(name, table)) {
return false;
}
- if (dm_name != name) {
- // Attempt to delete the snapshot device. Nothing should be depending on
- // the device, and device-mapper should have flushed remaining I/O. We
- // could in theory replace with dm-zero (or re-use the table above), but
- // for now it's better to know why this would fail.
- if (!dm.DeleteDevice(dm_name)) {
- LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
- << "reclaimed until after reboot.";
- return false;
- }
+ // Attempt to delete the snapshot device if one still exists. Nothing
+ // should be depending on the device, and device-mapper should have
+ // flushed remaining I/O. We could in theory replace with dm-zero (or
+ // re-use the table above), but for now it's better to know why this
+ // would fail.
+ if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
+ LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
+ << "reclaimed until after reboot.";
+ return false;
+ }
+
+ // Cleanup the base device as well, since it is no longer used. This does
+ // not block cleanup.
+ auto base_name = GetBaseDeviceName(name);
+ if (!dm.DeleteDeviceIfExists(base_name)) {
+ LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
}
return true;
}
+bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
+ std::string old_slot;
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+ PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+ return false;
+ }
+ if (device_->GetSlotSuffix() != old_slot) {
+ // We're booted into the target slot, which means we just rebooted
+ // after applying the update.
+ return false;
+ }
+
+ // The only way we can get here is if:
+ // (1) The device rolled back to the previous slot.
+ // (2) This function was called prematurely before rebooting the device.
+ // (3) fastboot set_active was used.
+ //
+ // In any case, delete the snapshots. It may be worth using the boot_control
+ // HAL to differentiate case (2).
+ RemoveAllUpdateState(lock);
+ return true;
+}
+
+bool SnapshotManager::IsCancelledSnapshot(const std::string& snapshot_name) {
+ 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;
+ }
+ auto partition = android::fs_mgr::FindPartition(*metadata.get(), snapshot_name);
+ if (!partition) return false;
+ return (partition->attributes & LP_PARTITION_ATTR_UPDATED) == 0;
+}
+
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
@@ -1009,22 +1151,6 @@
auto lock = LockExclusive();
if (!lock) return false;
- std::vector<std::string> snapshot_list;
- if (!ListSnapshots(lock.get(), &snapshot_list)) {
- return false;
- }
-
- std::unordered_set<std::string> live_snapshots;
- for (const auto& snapshot : snapshot_list) {
- SnapshotStatus status;
- if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
- return false;
- }
- if (status.state != SnapshotState::MergeCompleted) {
- live_snapshots.emplace(snapshot);
- }
- }
-
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
@@ -1033,53 +1159,154 @@
return false;
}
- // Map logical partitions.
- auto& dm = DeviceMapper::Instance();
for (const auto& partition : metadata->partitions) {
- auto partition_name = GetPartitionName(partition);
- if (!partition.num_extents) {
- LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
- continue;
- }
-
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),
.partition = &partition,
.partition_opener = &opener,
};
-
- if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
- // If the device has a snapshot, it'll need to be writable, and
- // we'll need to create the logical partition with a marked-up name
- // (since the snapshot will use the partition name).
- params.force_writable = true;
- params.device_name = GetBaseDeviceName(partition_name);
- }
-
std::string ignore_path;
- if (!CreateLogicalPartition(params, &ignore_path)) {
- LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
- << params.GetDeviceName();
- return false;
- }
- if (!params.force_writable) {
- // No snapshot.
- continue;
- }
-
- // We don't have ueventd in first-stage init, so use device major:minor
- // strings instead.
- std::string base_device;
- if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
- LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
- return false;
- }
- if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
- LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
+ if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
return false;
}
}
+
+ LOG(INFO) << "Created logical partitions with snapshot.";
+ return true;
+}
+
+static std::chrono::milliseconds GetRemainingTime(
+ const std::chrono::milliseconds& timeout,
+ const std::chrono::time_point<std::chrono::steady_clock>& begin) {
+ // If no timeout is specified, execute all commands without specifying any timeout.
+ if (timeout.count() == 0) return std::chrono::milliseconds(0);
+ auto passed_time = std::chrono::steady_clock::now() - begin;
+ auto remaining_time = timeout - duration_cast<std::chrono::milliseconds>(passed_time);
+ if (remaining_time.count() <= 0) {
+ LOG(ERROR) << "MapPartitionWithSnapshot has reached timeout " << timeout.count() << "ms ("
+ << remaining_time.count() << "ms remaining)";
+ // Return min() instead of remaining_time here because 0 is treated as a special value for
+ // no timeout, where the rest of the commands will still be executed.
+ return std::chrono::milliseconds::min();
+ }
+ return remaining_time;
+}
+
+bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
+ CreateLogicalPartitionParams params,
+ std::string* path) {
+ auto begin = std::chrono::steady_clock::now();
+
+ CHECK(lock);
+ path->clear();
+
+ // Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by
+ // reading super partition metadata).
+ CreateLogicalPartitionParams::OwnedData params_owned_data;
+ if (!params.InitDefaults(¶ms_owned_data)) {
+ return false;
+ }
+
+ if (!params.partition->num_extents) {
+ LOG(INFO) << "Skipping zero-length logical partition: " << params.GetPartitionName();
+ return true; // leave path empty to indicate that nothing is mapped.
+ }
+
+ // Determine if there is a live snapshot for the SnapshotStatus of the partition; i.e. if the
+ // partition still has a snapshot that needs to be mapped. If no live snapshot or merge
+ // completed, live_snapshot_status is set to nullopt.
+ std::optional<SnapshotStatus> live_snapshot_status;
+ do {
+ if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+ LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
+ << params.GetPartitionName();
+ break;
+ }
+ auto file_path = GetSnapshotStatusFilePath(params.GetPartitionName());
+ if (access(file_path.c_str(), F_OK) != 0) {
+ if (errno != ENOENT) {
+ PLOG(INFO) << "Can't map snapshot for " << params.GetPartitionName()
+ << ": Can't access " << file_path;
+ return false;
+ }
+ break;
+ }
+ live_snapshot_status = std::make_optional<SnapshotStatus>();
+ if (!ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status)) {
+ return false;
+ }
+ // No live snapshot if merge is completed.
+ if (live_snapshot_status->state == SnapshotState::MergeCompleted) {
+ live_snapshot_status.reset();
+ }
+ } while (0);
+
+ if (live_snapshot_status.has_value()) {
+ // dm-snapshot requires the base device to be writable.
+ params.force_writable = true;
+ // Map the base device with a different name to avoid collision.
+ params.device_name = GetBaseDeviceName(params.GetPartitionName());
+ }
+
+ AutoDeviceList created_devices;
+
+ // Create the base device for the snapshot, or if there is no snapshot, the
+ // device itself. This device consists of the real blocks in the super
+ // partition that this logical partition occupies.
+ auto& dm = DeviceMapper::Instance();
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
+ LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
+ << " as device " << params.GetDeviceName();
+ return false;
+ }
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+
+ if (!live_snapshot_status.has_value()) {
+ created_devices.Release();
+ return true;
+ }
+
+ // We don't have ueventd in first-stage init, so use device major:minor
+ // strings instead.
+ std::string base_device;
+ if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+ LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+ return false;
+ }
+
+ // If there is a timeout specified, compute the remaining time to call Map* functions.
+ // init calls CreateLogicalAndSnapshotPartitions, which has no timeout specified. Still call
+ // Map* functions in this case.
+ auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
+ std::string cow_image_device;
+ if (!MapCowImage(params.GetPartitionName(), remaining_time, &cow_image_device)) {
+ LOG(ERROR) << "Could not map cow image for partition: " << params.GetPartitionName();
+ return false;
+ }
+ created_devices.EmplaceBack<AutoUnmapImage>(images_.get(),
+ GetCowImageDeviceName(params.partition_name));
+
+ // TODO: map cow linear device here
+ std::string cow_device = cow_image_device;
+
+ remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
+ if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
+ path)) {
+ LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
+ return false;
+ }
+ // No need to add params.GetPartitionName() to created_devices since it is immediately released.
+
+ created_devices.Release();
+
+ LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
+
return true;
}
@@ -1216,12 +1443,14 @@
return false;
}
auto pieces = android::base::Split(contents, " ");
- if (pieces.size() != 5) {
+ if (pieces.size() != 7) {
LOG(ERROR) << "Invalid status line for snapshot: " << path;
return false;
}
- if (pieces[0] == "created") {
+ if (pieces[0] == "none") {
+ status->state = SnapshotState::None;
+ } else if (pieces[0] == "created") {
status->state = SnapshotState::Created;
} else if (pieces[0] == "merging") {
status->state = SnapshotState::Merging;
@@ -1229,6 +1458,7 @@
status->state = SnapshotState::MergeCompleted;
} else {
LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
+ return false;
}
if (!android::base::ParseUint(pieces[1], &status->device_size)) {
@@ -1239,11 +1469,19 @@
LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
return false;
}
- if (!android::base::ParseUint(pieces[3], &status->sectors_allocated)) {
+ if (!android::base::ParseUint(pieces[3], &status->cow_partition_size)) {
+ LOG(ERROR) << "Invalid cow linear size in status line for: " << path;
+ return false;
+ }
+ if (!android::base::ParseUint(pieces[4], &status->cow_file_size)) {
+ LOG(ERROR) << "Invalid cow file size in status line for: " << path;
+ return false;
+ }
+ if (!android::base::ParseUint(pieces[5], &status->sectors_allocated)) {
LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
return false;
}
- if (!android::base::ParseUint(pieces[4], &status->metadata_sectors)) {
+ if (!android::base::ParseUint(pieces[6], &status->metadata_sectors)) {
LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
return false;
}
@@ -1252,6 +1490,8 @@
std::string SnapshotManager::to_string(SnapshotState state) {
switch (state) {
+ case SnapshotState::None:
+ return "none";
case SnapshotState::Created:
return "created";
case SnapshotState::Merging:
@@ -1281,6 +1521,8 @@
to_string(status.state),
std::to_string(status.device_size),
std::to_string(status.snapshot_size),
+ std::to_string(status.cow_partition_size),
+ std::to_string(status.cow_file_size),
std::to_string(status.sectors_allocated),
std::to_string(status.metadata_sectors),
};
@@ -1308,7 +1550,7 @@
std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status) {
if (status.device_size != status.snapshot_size) {
- return snapshot_name + "-inner";
+ return GetSnapshotExtraDeviceName(snapshot_name);
}
return snapshot_name;
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f4eb1ac..429fd8e 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -31,6 +31,7 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
+#include <liblp/mock_property_fetcher.h>
#include "test_helpers.h"
@@ -43,7 +44,11 @@
using android::fiemap::IImageManager;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::GetPartitionName;
using android::fs_mgr::MetadataBuilder;
+using namespace ::testing;
+using namespace android::fs_mgr::testing;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -67,6 +72,7 @@
protected:
void SetUp() override {
+ ResetMockPropertyFetcher();
InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
@@ -78,6 +84,7 @@
lock_ = nullptr;
CleanupTestArtifacts();
+ ResetMockPropertyFetcher();
}
void InitializeState() {
@@ -95,10 +102,11 @@
// get an accurate list to remove.
lock_ = nullptr;
- std::vector<std::string> snapshots = {"test-snapshot", "test_partition_b"};
+ std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
+ "test_partition_b"};
for (const auto& snapshot : snapshots) {
DeleteSnapshotDevice(snapshot);
- DeleteBackingImage(image_manager_, snapshot + "-cow");
+ DeleteBackingImage(image_manager_, snapshot + "-cow-img");
auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
android::base::RemoveFileIfExists(status_file);
@@ -154,11 +162,10 @@
return false;
}
- // Update both slots for convenience.
+ // Update the source slot.
auto metadata = builder->Export();
if (!metadata) return false;
- if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0) ||
- !UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
return false;
}
@@ -174,9 +181,40 @@
return CreateLogicalPartition(params, path);
}
+ bool MapUpdatePartitions() {
+ TestPartitionOpener opener(fake_super);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ if (!builder) return false;
+
+ auto metadata = builder->Export();
+ if (!metadata) return false;
+
+ // Update the destination slot, mark it as updated.
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+ return false;
+ }
+
+ for (const auto& partition : metadata->partitions) {
+ CreateLogicalPartitionParams params = {
+ .block_device = fake_super,
+ .metadata = metadata.get(),
+ .partition = &partition,
+ .device_name = GetPartitionName(partition) + "-base",
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
void DeleteSnapshotDevice(const std::string& snapshot) {
DeleteDevice(snapshot);
DeleteDevice(snapshot + "-inner");
+ ASSERT_TRUE(image_manager_->UnmapImageIfExists(snapshot + "-cow-img"));
}
void DeleteDevice(const std::string& device) {
if (dm_.GetState(device) != DmDeviceState::INVALID) {
@@ -194,8 +232,11 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::vector<std::string> snapshots;
ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
@@ -212,6 +253,7 @@
}
ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
+ ASSERT_TRUE(sm->UnmapCowImage("test-snapshot"));
ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
}
@@ -219,14 +261,21 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::string base_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+ std::string cow_device;
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+
std::string snap_device;
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
}
@@ -235,14 +284,21 @@
static const uint64_t kSnapshotSize = 1024 * 1024;
static const uint64_t kDeviceSize = 1024 * 1024 * 2;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kSnapshotSize,
- kSnapshotSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kSnapshotSize,
+ .cow_file_size = kSnapshotSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::string base_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+ std::string cow_device;
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+
std::string snap_device;
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
}
@@ -273,15 +329,25 @@
}
TEST_F(SnapshotTest, Merge) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
- std::string base_device, snap_device;
- ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ std::string base_device, cow_device, snap_device;
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+ ASSERT_TRUE(sm->MapCowImage("test_partition_b", 10s, &cow_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+ &snap_device));
std::string test_string = "This is a test string.";
{
@@ -290,10 +356,10 @@
ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
}
- // Note: we know the name of the device is test-snapshot because we didn't
- // request a linear segment.
+ // Note: we know there is no inner/outer dm device since we didn't request
+ // a linear segment.
DeviceMapper::TargetInfo target;
- ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+ ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
// Release the lock.
@@ -306,20 +372,22 @@
ASSERT_TRUE(sm->InitiateMerge());
// The device should have been switched to a snapshot-merge target.
- ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+ ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
// We should not be able to cancel an update now.
ASSERT_FALSE(sm->CancelUpdate());
- ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
// The device should no longer be a snapshot or snapshot-merge.
- ASSERT_FALSE(sm->IsSnapshotDevice("test-snapshot"));
+ ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
- // Test that we can read back the string we wrote to the snapshot.
- unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+ // Test that we can read back the string we wrote to the snapshot. Note
+ // that the base device is gone now. |snap_device| contains the correct
+ // partition.
+ unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
std::string buffer(test_string.size(), '\0');
@@ -331,16 +399,21 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
- std::string base_device, snap_device;
+ std::string base_device, cow_device, snap_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
// Keep an open handle to the cow device. This should cause the merge to
// be incomplete.
- auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", "");
+ auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow-img", "");
unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
@@ -353,34 +426,48 @@
ASSERT_TRUE(sm->InitiateMerge());
// COW cannot be removed due to open fd, so expect a soft failure.
- ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeNeedsReboot);
+ // Release the handle to the COW device to fake a reboot.
+ fd.reset();
+ // Wait 1s, otherwise DeleteSnapshotDevice may fail with EBUSY.
+ sleep(1);
// Forcefully delete the snapshot device, so it looks like we just rebooted.
DeleteSnapshotDevice("test-snapshot");
// Map snapshot should fail now, because we're in a merge-complete state.
ASSERT_TRUE(AcquireLock());
- ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+ ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
// Release everything and now the merge should complete.
fd = {};
lock_ = nullptr;
- ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
}
TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
// Simulate a reboot into the new slot.
lock_ = nullptr;
ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
auto rebooted = new TestDeviceInfo(fake_super);
rebooted->set_slot_suffix("_b");
@@ -403,6 +490,99 @@
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
}
+TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ ASSERT_TRUE(AcquireLock());
+
+ static const uint64_t kDeviceSize = 1024 * 1024;
+
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+
+ // Simulate a reboot into the new slot.
+ lock_ = nullptr;
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+ // Reflash the super partition.
+ FormatFakeSuper();
+ ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+
+ auto rebooted = new TestDeviceInfo(fake_super);
+ rebooted->set_slot_suffix("_b");
+
+ auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ ASSERT_TRUE(AcquireLock());
+
+ SnapshotManager::SnapshotStatus status;
+ ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
+
+ // We should not get a snapshot device now.
+ DeviceMapper::TargetInfo target;
+ auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+ ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+
+ // We should see a cancelled update as well.
+ lock_ = nullptr;
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+}
+
+TEST_F(SnapshotTest, FlashSuperDuringMerge) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ ASSERT_TRUE(AcquireLock());
+
+ static const uint64_t kDeviceSize = 1024 * 1024;
+
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+
+ // Simulate a reboot into the new slot.
+ lock_ = nullptr;
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+ auto rebooted = new TestDeviceInfo(fake_super);
+ rebooted->set_slot_suffix("_b");
+
+ auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+ ASSERT_TRUE(init->InitiateMerge());
+
+ // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
+ // status is still Merging.
+ DeleteSnapshotDevice("test_partition_b");
+ ASSERT_TRUE(init->image_manager()->UnmapImageIfExists("test_partition_b-cow-img"));
+ FormatFakeSuper();
+ ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ // Because the status is Merging, we must call ProcessUpdateState, which should
+ // detect a cancelled update.
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+ ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index c87f118..9f582d9 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -47,6 +47,7 @@
std::string GetGsidDir() const override { return "ota/test"s; }
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
+ std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
return *opener_.get();
}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
new file mode 100644
index 0000000..164b472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -0,0 +1,56 @@
+// 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 "utility.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+void AutoDevice::Release() {
+ name_.clear();
+}
+
+AutoDeviceList::~AutoDeviceList() {
+ // Destroy devices in the reverse order because newer devices may have dependencies
+ // on older devices.
+ for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
+ it->reset();
+ }
+}
+
+void AutoDeviceList::Release() {
+ for (auto&& p : devices_) {
+ p->Release();
+ }
+}
+
+AutoUnmapDevice::~AutoUnmapDevice() {
+ if (name_.empty()) return;
+ if (!dm_->DeleteDeviceIfExists(name_)) {
+ LOG(ERROR) << "Failed to auto unmap device " << name_;
+ }
+}
+
+AutoUnmapImage::~AutoUnmapImage() {
+ if (name_.empty()) return;
+ if (!images_->UnmapImageIfExists(name_)) {
+ LOG(ERROR) << "Failed to auto unmap cow image " << name_;
+ }
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
new file mode 100644
index 0000000..cbab472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.h
@@ -0,0 +1,85 @@
+// 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>
+
+#include <android-base/macros.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+namespace android {
+namespace snapshot {
+
+struct AutoDevice {
+ virtual ~AutoDevice(){};
+ void Release();
+
+ protected:
+ AutoDevice(const std::string& name) : name_(name) {}
+ std::string name_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+ AutoDevice(AutoDevice&& other) = delete;
+};
+
+// A list of devices we created along the way.
+// - Whenever a device is created that is subject to GC'ed at the end of
+// this function, add it to this list.
+// - If any error has occurred, the list is destroyed, and all these devices
+// are cleaned up.
+// - Upon success, Release() should be called so that the created devices
+// are kept.
+struct AutoDeviceList {
+ ~AutoDeviceList();
+ template <typename T, typename... Args>
+ void EmplaceBack(Args&&... args) {
+ devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
+ }
+ void Release();
+
+ private:
+ std::vector<std::unique_ptr<AutoDevice>> devices_;
+};
+
+// Automatically unmap a device upon deletion.
+struct AutoUnmapDevice : AutoDevice {
+ // On destruct, delete |name| from device mapper.
+ AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+ : AutoDevice(name), dm_(dm) {}
+ AutoUnmapDevice(AutoUnmapDevice&& other) = default;
+ ~AutoUnmapDevice();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
+ android::dm::DeviceMapper* dm_ = nullptr;
+};
+
+// Automatically unmap an image upon deletion.
+struct AutoUnmapImage : AutoDevice {
+ // On destruct, delete |name| from image manager.
+ AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
+ : AutoDevice(name), images_(images) {}
+ AutoUnmapImage(AutoUnmapImage&& other) = default;
+ ~AutoUnmapImage();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);
+ android::fiemap::IImageManager* images_ = nullptr;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index ed6d0e3..f445703 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1190,6 +1190,11 @@
skip_unrelated_mounts |
grep " overlay ro,") ||
die "remount overlayfs missed a spot (ro)"
+ !(adb_sh grep -v noatime /proc/mounts </dev/null |
+ skip_administrative_mounts data |
+ skip_unrelated_mounts |
+ grep -v ' ro,') ||
+ die "mounts are not noatime"
D=`adb_sh grep " rw," /proc/mounts </dev/null |
skip_administrative_mounts data`
if echo "${D}" | grep /dev/root >/dev/null; then
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 778e08c..27a6452 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -38,8 +38,6 @@
"libkeystore_aidl",
"libkeystore_binder",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
],
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 53be526..4f89bfb 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -42,8 +42,6 @@
"libbase",
"libcutils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"libutils",
"android.hardware.health@2.0",
diff --git a/healthd/Android.mk b/healthd/Android.mk
index b87f3c7..66ff399 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,9 +21,7 @@
android.hardware.health@1.0-convert \
libbinderthreadstate \
libcharger_sysprop \
- libhidltransport \
libhidlbase \
- libhwbinder_noltopgo \
libhealthstoragedefault \
libminui \
libvndksupport \
@@ -75,9 +73,7 @@
android.hardware.health@1.0-convert \
libbinderthreadstate \
libcharger_sysprop \
- libhidltransport \
libhidlbase \
- libhwbinder_noltopgo \
libhealthstoragedefault \
libvndksupport \
libhealthd_charger_nops \
diff --git a/init/Android.bp b/init/Android.bp
index 37e359c..57555f6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -210,6 +210,8 @@
cc_test {
name: "CtsInitTestCases",
defaults: ["init_defaults"],
+ require_root: true,
+
compile_multilib: "both",
multilib: {
lib32: {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7c66de5..d2c73cb 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -80,6 +80,7 @@
using namespace std::literals::string_literals;
using android::base::Basename;
+using android::base::StringPrintf;
using android::base::unique_fd;
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadFstabFromFile;
@@ -1062,34 +1063,59 @@
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<void> ExecWithRebootOnFailure(const std::string& reboot_reason,
- const BuiltinArguments& args) {
- auto service = Service::MakeTemporaryOneshotService(args.args);
+static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args,
+ std::function<void(const std::string&)> function) {
+ auto service = Service::MakeTemporaryOneshotService(args);
if (!service) {
- return Error() << "Could not create exec service: " << service.error();
+ function("MakeTemporaryOneshotService failed: " + service.error().message());
}
- (*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+ (*service)->AddReapCallback([function](const siginfo_t& siginfo) {
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- // TODO (b/122850122): support this in gsi
- if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
- LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
- if (auto result = reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
- !result) {
- LOG(FATAL) << "Could not reboot into recovery: " << result.error();
- }
- } else {
- LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
- }
+ function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
}
});
if (auto result = (*service)->ExecStart(); !result) {
- return Error() << "Could not start exec service: " << result.error();
+ function("ExecStart failed: " + result.error().message());
}
ServiceList::GetInstance().AddService(std::move(*service));
return {};
}
+static Result<void> do_exec_reboot_on_failure(const BuiltinArguments& args) {
+ auto reboot_reason = args[1];
+ auto reboot = [reboot_reason](const std::string& message) {
+ property_set(LAST_REBOOT_REASON_PROPERTY, reboot_reason);
+ sync();
+ LOG(FATAL) << message << ": rebooting into bootloader, reason: " << reboot_reason;
+ };
+
+ std::vector<std::string> remaining_args(args.begin() + 1, args.end());
+ remaining_args[0] = args[0];
+
+ return ExecWithFunctionOnFailure(remaining_args, reboot);
+}
+
+static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
+ auto reboot_reason = vdc_arg + "_failed";
+
+ auto reboot = [reboot_reason](const std::string& message) {
+ // TODO (b/122850122): support this in gsi
+ if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+ LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+ if (auto result = reboot_into_recovery(
+ {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+ !result) {
+ LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+ }
+ } else {
+ LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+ }
+ };
+
+ std::vector<std::string> args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", vdc_arg};
+ return ExecWithFunctionOnFailure(args, reboot);
+}
+
static Result<void> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return {};
@@ -1097,15 +1123,11 @@
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
- return ExecWithRebootOnFailure(
- "enablefilecrypto_failed",
- {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
+ return ExecVdcRebootOnFailure("enablefilecrypto");
}
static Result<void> do_init_user0(const BuiltinArguments& args) {
- return ExecWithRebootOnFailure(
- "init_user0_failed",
- {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
+ return ExecVdcRebootOnFailure("init_user0");
}
static Result<void> do_mark_post_data(const BuiltinArguments& args) {
@@ -1180,6 +1202,7 @@
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
{"exec_background", {1, kMax, {false, do_exec_background}}},
+ {"exec_reboot_on_failure", {2, kMax, {false, do_exec_reboot_on_failure}}},
{"exec_start", {1, 1, {false, do_exec_start}}},
{"export", {2, 2, {false, do_export}}},
{"hostname", {1, 1, {true, do_hostname}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 771f1d7..2efaeea 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -81,6 +81,15 @@
return check_exec(std::move(args));
}
+Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {
+ BuiltinArguments remaining_args(args.context);
+
+ remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());
+ remaining_args.args[0] = args[0];
+
+ return check_exec(remaining_args);
+}
+
Result<void> check_interface_restart(const BuiltinArguments& args) {
if (auto result = IsKnownInterface(args[1]); !result) {
return result.error();
diff --git a/init/check_builtins.h b/init/check_builtins.h
index 4ff0d0c..fb34556 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -25,6 +25,7 @@
Result<void> check_chown(const BuiltinArguments& args);
Result<void> check_exec(const BuiltinArguments& args);
Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args);
Result<void> check_interface_restart(const BuiltinArguments& args);
Result<void> check_interface_start(const BuiltinArguments& args);
Result<void> check_interface_stop(const BuiltinArguments& args);
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 01d8867..17d63fa 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -69,19 +69,24 @@
return {};
}
-Result<void> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+Result<std::vector<std::function<void()>*>> Epoll::Wait(
+ std::optional<std::chrono::milliseconds> timeout) {
int timeout_ms = -1;
if (timeout && timeout->count() < INT_MAX) {
timeout_ms = timeout->count();
}
- epoll_event ev;
- auto nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, &ev, 1, timeout_ms));
- if (nr == -1) {
+ const auto max_events = epoll_handlers_.size();
+ epoll_event ev[max_events];
+ auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, ev, max_events, timeout_ms));
+ if (num_events == -1) {
return ErrnoError() << "epoll_wait failed";
- } else if (nr == 1) {
- std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
}
- return {};
+ std::vector<std::function<void()>*> pending_functions;
+ for (int i = 0; i < num_events; ++i) {
+ pending_functions.emplace_back(reinterpret_cast<std::function<void()>*>(ev[i].data.ptr));
+ }
+
+ return pending_functions;
}
} // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index ca84266..c32a661 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_EPOLL_H
-#define _INIT_EPOLL_H
+#pragma once
#include <stdint.h>
#include <sys/epoll.h>
@@ -24,6 +23,7 @@
#include <functional>
#include <map>
#include <optional>
+#include <vector>
#include <android-base/unique_fd.h>
@@ -39,7 +39,8 @@
Result<void> Open();
Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
Result<void> UnregisterHandler(int fd);
- Result<void> Wait(std::optional<std::chrono::milliseconds> timeout);
+ Result<std::vector<std::function<void()>*>> Wait(
+ std::optional<std::chrono::milliseconds> timeout);
private:
android::base::unique_fd epoll_fd_;
@@ -48,5 +49,3 @@
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index 0f5a864..9c2ca75 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -175,7 +175,6 @@
return -1;
}
- LOG(INFO) << "Setting policy on " << dir;
int result =
fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
diff --git a/init/init.cpp b/init/init.cpp
index 18fb0c3..d4cbb5f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -305,9 +305,6 @@
process_cmdline = "unknown process";
}
- LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
- << " (" << process_cmdline << ")";
-
const ControlMessageFunction& function = it->second;
Service* svc = nullptr;
@@ -320,20 +317,25 @@
svc = ServiceList::GetInstance().FindInterface(name);
break;
default:
- LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
+ LOG(ERROR) << "Invalid function target from static map key ctl." << msg << ": "
<< static_cast<std::underlying_type<ControlTarget>::type>(function.target);
return false;
}
if (svc == nullptr) {
- LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
+ LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
+ << " from pid: " << pid << " (" << process_cmdline << ")";
return false;
}
if (auto result = function.action(svc); !result) {
- LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
+ LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
+ << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error();
return false;
}
+
+ LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
+ << "' from pid: " << pid << " (" << process_cmdline << ")";
return true;
}
@@ -715,6 +717,7 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
+ am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -722,7 +725,6 @@
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
- am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
@@ -785,8 +787,17 @@
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
- if (auto result = epoll.Wait(epoll_timeout); !result) {
- LOG(ERROR) << result.error();
+ auto pending_functions = epoll.Wait(epoll_timeout);
+ if (!pending_functions) {
+ LOG(ERROR) << pending_functions.error();
+ } else if (!pending_functions->empty()) {
+ // We always reap children before responding to the other pending functions. This is to
+ // prevent a race where other daemons see that a service has exited and ask init to
+ // start it again via ctl.start before init has reaped it.
+ ReapAnyOutstandingChildren();
+ for (const auto& function : *pending_functions) {
+ (*function)();
+ }
}
}
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 33373d4..6e9b337 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -212,7 +212,11 @@
}
void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
- epoll_.Wait(wait);
+ auto pending_functions = epoll_.Wait(wait);
+ ASSERT_TRUE(pending_functions) << pending_functions.error();
+ for (const auto& function : *pending_functions) {
+ (*function)();
+ }
}
void TestFrame::SetChord(int key, bool value) {
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 12144c1..0745148 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -91,22 +91,22 @@
return false;
}
- // Special casing for the runtime APEX
- constexpr const char kRuntimeApexMountPath[] = "/system/apex/com.android.runtime";
- static const std::vector<std::string> kRuntimeApexDirNames = {"com.android.runtime.release",
- "com.android.runtime.debug"};
+ // Special casing for the ART APEX
+ constexpr const char kArtApexMountPath[] = "/system/apex/com.android.art";
+ static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
+ "com.android.art.debug"};
bool success = false;
- for (const auto& name : kRuntimeApexDirNames) {
+ for (const auto& name : kArtApexDirNames) {
std::string path = std::string(kSystemApex) + "/" + name;
if (access(path.c_str(), F_OK) == 0) {
- if (mount(path.c_str(), kRuntimeApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
+ if (mount(path.c_str(), kArtApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
success = true;
break;
}
}
}
if (!success) {
- PLOG(ERROR) << "Failed to bind mount the runtime APEX to " << kRuntimeApexMountPath;
+ PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
}
return success;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd42256..4852cd0 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -51,6 +51,8 @@
#include <android/api-level.h>
#include <fcntl.h>
+#include <linux/audit.h>
+#include <linux/netlink.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -437,7 +439,8 @@
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
- PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+ PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
+ << ") failed";
}
}
@@ -446,6 +449,35 @@
}
}
+constexpr size_t kKlogMessageSize = 1024;
+
+void SelinuxAvcLog(char* buf, size_t buf_len) {
+ CHECK_GT(buf_len, 0u);
+
+ size_t str_len = strnlen(buf, buf_len);
+ // trim newline at end of string
+ if (buf[str_len - 1] == '\n') {
+ buf[str_len - 1] = '\0';
+ }
+
+ struct NetlinkMessage {
+ nlmsghdr hdr;
+ char buf[kKlogMessageSize];
+ } request = {};
+
+ request.hdr.nlmsg_flags = NLM_F_REQUEST;
+ request.hdr.nlmsg_type = AUDIT_USER_AVC;
+ request.hdr.nlmsg_len = sizeof(request);
+ strlcpy(request.buf, buf, sizeof(request.buf));
+
+ auto fd = unique_fd{socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT)};
+ if (!fd.ok()) {
+ return;
+ }
+
+ TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0));
+}
+
} // namespace
// The files and directories that were created before initial sepolicy load or
@@ -478,12 +510,19 @@
} else if (type == SELINUX_INFO) {
severity = android::base::INFO;
}
- char buf[1024];
+ char buf[kKlogMessageSize];
va_list ap;
va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
+ int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+ if (length_written <= 0) {
+ return 0;
+ }
+ if (type == SELINUX_AVC) {
+ SelinuxAvcLog(buf, sizeof(buf));
+ } else {
+ android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+ }
return 0;
}
diff --git a/init/service.cpp b/init/service.cpp
index 9537843..7a20966 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -116,9 +116,10 @@
return execv(c_strings[0], c_strings.data()) == 0;
}
-static bool IsRuntimeApexReady() {
+static bool AreRuntimeApexesReady() {
struct stat buf;
- return stat("/apex/com.android.runtime/", &buf) == 0;
+ return stat("/apex/com.android.art/", &buf) == 0 &&
+ stat("/apex/com.android.runtime/", &buf) == 0;
}
unsigned long Service::next_start_order_ = 1;
@@ -406,11 +407,11 @@
scon = *result;
}
- if (!IsRuntimeApexReady() && !pre_apexd_) {
- // If this service is started before the runtime APEX gets available,
- // mark it as pre-apexd one. Note that this marking is permanent. So
- // for example, if the service is re-launched (e.g., due to crash),
- // it is still recognized as pre-apexd... for consistency.
+ if (!AreRuntimeApexesReady() && !pre_apexd_) {
+ // If this service is started before the Runtime and ART APEXes get
+ // available, mark it as pre-apexd one. Note that this marking is
+ // permanent. So for example, if the service is re-launched (e.g., due
+ // to crash), it is still recognized as pre-apexd... for consistency.
pre_apexd_ = true;
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8b2cf62..cffc1b9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -17,12 +17,15 @@
#include "ueventd.h"
#include <ctype.h>
+#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <set>
#include <thread>
@@ -121,8 +124,9 @@
void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
void RegenerateUevents();
void ForkSubProcesses();
- void DoRestoreCon();
void WaitForSubProcesses();
+ void RestoreConHandler(unsigned int process_num, unsigned int total_processes);
+ void GenerateRestoreCon(const std::string& directory);
UeventListener& uevent_listener_;
std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
@@ -131,6 +135,8 @@
std::vector<Uevent> uevent_queue_;
std::set<pid_t> subprocess_pids_;
+
+ std::vector<std::string> restorecon_queue_;
};
void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -141,9 +147,38 @@
uevent_handler->HandleUevent(uevent);
}
}
+}
+
+void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
+ for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
+ auto& dir = restorecon_queue_[i];
+
+ selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ }
_exit(EXIT_SUCCESS);
}
+void ColdBoot::GenerateRestoreCon(const std::string& directory) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
+
+ if (!dir) return;
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get())) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;
+
+ struct stat st;
+ if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ std::string fullpath = directory + "/" + dent->d_name;
+ if (fullpath != "/sys/devices") {
+ restorecon_queue_.emplace_back(fullpath);
+ }
+ }
+ }
+}
+
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
uevent_queue_.emplace_back(uevent);
@@ -160,16 +195,13 @@
if (pid == 0) {
UeventHandlerMain(i, num_handler_subprocesses_);
+ RestoreConHandler(i, num_handler_subprocesses_);
}
subprocess_pids_.emplace(pid);
}
}
-void ColdBoot::DoRestoreCon() {
- selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
void ColdBoot::WaitForSubProcesses() {
// Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
// stuck.
@@ -208,9 +240,13 @@
RegenerateUevents();
- ForkSubProcesses();
+ selinux_android_restorecon("/sys", 0);
+ selinux_android_restorecon("/sys/devices", 0);
+ GenerateRestoreCon("/sys");
+ // takes long time for /sys/devices, parallelize it
+ GenerateRestoreCon("/sys/devices");
- DoRestoreCon();
+ ForkSubProcesses();
WaitForSubProcesses();
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index f1ca446..f71d0c3 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -86,6 +86,7 @@
const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;
const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;
+ last_state_ = state_;
last_device_events_.events = 0;
last_proxy_events_.events = 0;
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index 32446d4..6c7655a 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -34,6 +34,29 @@
#include <utils/Compat.h>
+static bool ashmem_validate_stat(int fd, struct stat* buf) {
+ int result = fstat(fd, buf);
+ if (result == -1) {
+ return false;
+ }
+
+ /*
+ * Check if this is an "ashmem" region.
+ * TODO: This is very hacky, and can easily break.
+ * We need some reliable indicator.
+ */
+ if (!(buf->st_nlink == 0 && S_ISREG(buf->st_mode))) {
+ errno = ENOTTY;
+ return false;
+ }
+ return true;
+}
+
+int ashmem_valid(int fd) {
+ struct stat buf;
+ return ashmem_validate_stat(fd, &buf);
+}
+
int ashmem_create_region(const char* /*ignored*/, size_t size) {
char pattern[PATH_MAX];
snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
@@ -65,18 +88,7 @@
int ashmem_get_size_region(int fd)
{
struct stat buf;
- int result = fstat(fd, &buf);
- if (result == -1) {
- return -1;
- }
-
- /*
- * Check if this is an "ashmem" region.
- * TODO: This is very hacky, and can easily break.
- * We need some reliable indicator.
- */
- if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
- errno = ENOTTY;
+ if (!ashmem_validate_stat(fd, &buf)) {
return -1;
}
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index f6cae36..4f07456 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -69,10 +69,11 @@
/*
* native_handle_create
- *
+ *
* creates a native_handle_t and initializes it. must be destroyed with
- * native_handle_delete().
- *
+ * native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,
+ * numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.
+ *
*/
native_handle_t* native_handle_create(int numFds, int numInts);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 53d3ab3..c38d8cd 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
//
liblog_sources = [
- "config_read.cpp",
"config_write.cpp",
"log_event_list.cpp",
"log_event_write.cpp",
diff --git a/liblog/config_read.cpp b/liblog/config_read.cpp
deleted file mode 100644
index 3139f78..0000000
--- a/liblog/config_read.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 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 <log/log_transport.h>
-
-#include "config_read.h"
-#include "logger.h"
-
-struct listnode __android_log_transport_read = {&__android_log_transport_read,
- &__android_log_transport_read};
-struct listnode __android_log_persist_read = {&__android_log_persist_read,
- &__android_log_persist_read};
-
-static void __android_log_add_transport(struct listnode* list,
- struct android_log_transport_read* transport) {
- uint32_t i;
-
- /* Try to keep one functioning transport for each log buffer id */
- for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
- struct android_log_transport_read* transp;
-
- if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
- list_add_tail(list, &transport->node);
- return;
- }
- } else {
- read_transport_for_each(transp, list) {
- if (!transp->available) {
- return;
- }
- if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
- (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
- list_add_tail(list, &transport->node);
- return;
- }
- }
- }
- }
-}
-
-void __android_log_config_read() {
-#if (FAKE_LOG_DEVICE == 0)
- if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
- extern struct android_log_transport_read logdLoggerRead;
- extern struct android_log_transport_read pmsgLoggerRead;
-
- __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
- __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
- }
-#endif
-}
-
-void __android_log_config_read_close() {
- struct android_log_transport_read* transport;
- struct listnode* n;
-
- read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
- list_remove(&transport->node);
- }
- read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
- list_remove(&transport->node);
- }
-}
diff --git a/liblog/config_read.h b/liblog/config_read.h
deleted file mode 100644
index 212b8a0..0000000
--- a/liblog/config_read.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 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 <cutils/list.h>
-
-#include "log_portability.h"
-
-__BEGIN_DECLS
-
-extern struct listnode __android_log_transport_read;
-extern struct listnode __android_log_persist_read;
-
-#define read_transport_for_each(transp, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node); \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_read, node)) && \
- ((transp) != node_to_item((transp)->node.next, \
- struct android_log_transport_read, node)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_read, node))
-
-#define read_transport_for_each_safe(transp, n, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node), \
- (n) = (transp)->node.next; \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_read, node)) && \
- ((transp) != \
- node_to_item((n), struct android_log_transport_read, node)); \
- (transp) = node_to_item((n), struct android_log_transport_read, node), \
- (n) = (transp)->node.next)
-
-void __android_log_config_read();
-void __android_log_config_read_close();
-
-__END_DECLS
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index b7ba782..eba305f 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,7 +35,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h"
#include "log_portability.h"
#include "logd_reader.h"
#include "logger.h"
diff --git a/liblog/logger.h b/liblog/logger.h
index accb6e7..4b4ef5f 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -95,9 +95,19 @@
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 */
+ int ret; /* return value associated with following data */
+ struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
+};
+
struct android_log_logger_list {
struct listnode logger;
- struct listnode transport;
+ android_log_transport_context transport_context;
+ bool transport_initialized;
int mode;
unsigned int tail;
log_time start;
@@ -111,27 +121,7 @@
log_id_t logId;
};
-struct android_log_transport_context {
- struct listnode node;
- union android_log_context_union context; /* zero init per-transport context */
- struct android_log_logger_list* parent;
-
- struct android_log_transport_read* transport;
- unsigned logMask; /* mask of requested log buffers */
- int ret; /* return value associated with following data */
- struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
-};
-
/* assumes caller has structures read-locked, single threaded, or fenced */
-#define transport_context_for_each(transp, logger_list) \
- for ((transp) = node_to_item((logger_list)->transport.next, \
- struct android_log_transport_context, node); \
- ((transp) != node_to_item(&(logger_list)->transport, \
- struct android_log_transport_context, node)) && \
- ((transp)->parent == (logger_list)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_context, node))
-
#define logger_for_each(logp, logger_list) \
for ((logp) = node_to_item((logger_list)->logger.next, \
struct android_log_logger, node); \
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4cf0846..ff816b7 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -29,7 +29,6 @@
#include <cutils/list.h>
#include <private/android_filesystem_config.h>
-#include "config_read.h"
#include "log_portability.h"
#include "logger.h"
@@ -55,9 +54,6 @@
}
static int init_transport_context(struct android_log_logger_list* logger_list) {
- struct android_log_transport_read* transport;
- struct listnode* node;
-
if (!logger_list) {
return -EINVAL;
}
@@ -66,77 +62,63 @@
return -EINVAL;
}
- if (!list_empty(&logger_list->transport)) {
+ if (logger_list->transport_initialized) {
return 0;
}
- __android_log_lock();
- /* mini __write_to_log_initialize() to populate transports */
- if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
- __android_log_config_read();
- }
- __android_log_unlock();
+#if (FAKE_LOG_DEVICE == 0)
+ extern struct android_log_transport_read logdLoggerRead;
+ extern struct android_log_transport_read pmsgLoggerRead;
- node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
- : &__android_log_transport_read;
+ struct android_log_transport_read* transport;
+ transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
- read_transport_for_each(transport, node) {
- struct android_log_transport_context* transp;
- struct android_log_logger* logger;
- unsigned logMask = 0;
+ struct android_log_logger* logger;
+ unsigned logMask = 0;
- logger_for_each(logger, logger_list) {
- log_id_t logId = logger->logId;
+ 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) {
+ if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
continue;
}
- transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
- if (!transp) {
- return -ENOMEM;
+ if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
+ logMask |= 1 << logId;
}
- transp->parent = logger_list;
- transp->transport = transport;
- transp->logMask = logMask;
- transp->ret = 1;
- list_add_tail(&logger_list->transport, &transp->node);
}
- if (list_empty(&logger_list->transport)) {
+ if (!logMask) {
return -ENODEV;
}
+
+ logger_list->transport_context.transport = transport;
+ logger_list->transport_context.logMask = logMask;
+ logger_list->transport_context.ret = 1;
+#endif
return 0;
}
-#define LOGGER_FUNCTION(logger, def, func, args...) \
- ssize_t ret = -EINVAL; \
- struct android_log_transport_context* transp; \
- struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
- \
- if (!logger_internal) { \
- return ret; \
- } \
- ret = init_transport_context(logger_internal->parent); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_internal->parent) { \
- if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport && \
- transp->transport->func) { \
- ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#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) {
@@ -167,25 +149,24 @@
LOGGER_FUNCTION(logger, 4, version);
}
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
- struct android_log_transport_context* transp; \
- struct android_log_logger_list* logger_list_internal = \
- (struct android_log_logger_list*)(logger_list); \
- \
- ssize_t ret = init_transport_context(logger_list_internal); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_list_internal) { \
- if (transp->transport && (transp->transport->func)) { \
- ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#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
/*
@@ -212,7 +193,6 @@
}
list_init(&logger_list->logger);
- list_init(&logger_list->transport);
logger_list->mode = mode;
logger_list->tail = tail;
logger_list->pid = pid;
@@ -229,7 +209,6 @@
}
list_init(&logger_list->logger);
- list_init(&logger_list->transport);
logger_list->mode = mode;
logger_list->start = start;
logger_list->pid = pid;
@@ -247,38 +226,27 @@
struct android_log_logger* logger;
if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
- goto err;
+ return nullptr;
}
logger_for_each(logger, logger_list_internal) {
if (logger->logId == logId) {
- goto ok;
+ return reinterpret_cast<struct logger*>(logger);
}
}
logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
if (!logger) {
- goto err;
+ return nullptr;
}
logger->logId = logId;
list_add_tail(&logger_list_internal->logger, &logger->node);
logger->parent = logger_list_internal;
- /* Reset known transports to re-evaluate, we just added one */
- while (!list_empty(&logger_list_internal->transport)) {
- struct listnode* node = list_head(&logger_list_internal->transport);
- struct android_log_transport_context* transp =
- node_to_item(node, struct android_log_transport_context, node);
+ // Reset known transport to re-evaluate, since we added a new logger.
+ logger_list_internal->transport_initialized = false;
- list_remove(&transp->node);
- free(transp);
- }
- goto ok;
-
-err:
- logger = NULL;
-ok:
return (struct logger*)logger;
}
@@ -340,7 +308,6 @@
/* Read from the selected logs */
int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
- struct android_log_transport_context* transp;
struct android_log_logger_list* logger_list_internal =
(struct android_log_logger_list*)logger_list;
@@ -349,84 +316,8 @@
return ret;
}
- /* at least one transport */
- transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
- node);
-
- /* more than one transport? */
- if (transp->node.next != &logger_list_internal->transport) {
- /* Poll and merge sort the entries if from multiple transports */
- struct android_log_transport_context* oldest = NULL;
- int ret;
- int polled = 0;
- do {
- if (polled) {
- sched_yield();
- }
- ret = -1000;
- polled = 0;
- do {
- int retval = transp->ret;
- if ((retval > 0) && !transp->logMsg.entry.len) {
- if (!transp->transport->read) {
- retval = transp->ret = 0;
- } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
- !transp->transport->poll) {
- retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
- } else {
- int pollval = (*transp->transport->poll)(logger_list_internal, transp);
- if (pollval <= 0) {
- sched_yield();
- pollval = (*transp->transport->poll)(logger_list_internal, transp);
- }
- polled = 1;
- if (pollval < 0) {
- if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
- return -EAGAIN;
- }
- retval = transp->ret = pollval;
- } else if (pollval > 0) {
- retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
- }
- }
- }
- if (ret < retval) {
- ret = retval;
- }
- if ((transp->ret > 0) && transp->logMsg.entry.len &&
- (!oldest || (oldest->logMsg.entry.sec > transp->logMsg.entry.sec) ||
- ((oldest->logMsg.entry.sec == transp->logMsg.entry.sec) &&
- (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
- oldest = transp;
- }
- transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
- } while (transp != node_to_item(&logger_list_internal->transport,
- struct android_log_transport_context, node));
- if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
- return (ret < 0) ? ret : -EAGAIN;
- }
- transp = node_to_item(logger_list_internal->transport.next,
- struct android_log_transport_context, node);
- } while (!oldest && (ret > 0));
- if (!oldest) {
- return ret;
- }
- // ret is a positive value less than sizeof(struct log_msg)
- ret = oldest->ret;
- if (ret < oldest->logMsg.entry.hdr_size) {
- // zero truncated header fields.
- memset(
- log_msg, 0,
- (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
- : oldest->logMsg.entry.hdr_size));
- }
- memcpy(log_msg, &oldest->logMsg, ret);
- oldest->logMsg.entry.len = 0; /* Mark it as copied */
- return ret;
- }
-
- /* if only one, no need to copy into transport_context and merge-sort */
- return android_transport_read(logger_list_internal, transp, log_msg);
+ 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 */
@@ -438,16 +329,10 @@
return;
}
- while (!list_empty(&logger_list_internal->transport)) {
- struct listnode* node = list_head(&logger_list_internal->transport);
- struct android_log_transport_context* transp =
- node_to_item(node, struct android_log_transport_context, node);
+ android_log_transport_context* transport_context = &logger_list_internal->transport_context;
- if (transp->transport && transp->transport->close) {
- (*transp->transport->close)(logger_list_internal, transp);
- }
- list_remove(&transp->node);
- free(transp);
+ if (transport_context->transport && transport_context->transport->close) {
+ (*transport_context->transport->close)(logger_list_internal, transport_context);
}
while (!list_empty(&logger_list_internal->logger)) {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index a4b3cd7..4fbab4b 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -29,7 +29,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h" /* __android_log_config_read_close() definition */
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
@@ -624,7 +623,6 @@
if (__android_log_transport != transport_flag) {
__android_log_transport = transport_flag;
__android_log_config_write_close();
- __android_log_config_read_close();
write_to_log = __write_to_log_init;
/* generically we only expect these two values for write_to_log */
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index eaac97f..81563bc 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -24,7 +24,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h"
#include "logger.h"
static int pmsgAvailable(log_id_t logId);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d931ed1..5a73dc0 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1062,6 +1062,7 @@
when you depart from me, sorrow abides and happiness\n\
takes his leave.";
+#ifdef USING_LOGGER_DEFAULT
TEST(liblog, max_payload) {
#ifdef TEST_PREFIX
TEST_PREFIX
@@ -1130,6 +1131,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
TEST(liblog, __android_log_buf_print__maxtag) {
#ifdef TEST_PREFIX
@@ -1271,6 +1273,7 @@
#endif
}
+#ifdef USING_LOGGER_DEFAULT
TEST(liblog, dual_reader) {
#ifdef TEST_PREFIX
TEST_PREFIX
@@ -1334,6 +1337,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
#ifdef USING_LOGGER_DEFAULT // Do not retest logprint
static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
@@ -1743,22 +1747,21 @@
return count;
}
-
-// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
-static testing::AssertionResult IsOk(bool ok, std::string& message) {
- return ok ? testing::AssertionSuccess()
- : (testing::AssertionFailure() << message);
-}
#endif // TEST_PREFIX
TEST(liblog, enoent) {
#ifdef TEST_PREFIX
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
TEST_PREFIX
log_time ts(CLOCK_MONOTONIC);
EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
- // This call will fail if we are setuid(AID_SYSTEM), beware of any
+ // This call will fail unless we are root, beware of any
// test prior to this one playing with setuid and causing interference.
// We need to run before these tests so that they do not interfere with
// this test.
@@ -1770,20 +1773,7 @@
// liblog.android_logger_get_ is one of those tests that has no recourse
// and that would be adversely affected by emptying the log if it was run
// right after this test.
- if (getuid() != AID_ROOT) {
- fprintf(
- stderr,
- "WARNING: test conditions request being run as root and not AID=%d\n",
- getuid());
- if (!__android_log_is_debuggable()) {
- fprintf(
- stderr,
- "WARNING: can not run test on a \"user\" build, bypassing test\n");
- return;
- }
- }
-
- system((getuid() == AID_ROOT) ? "stop logd" : "su 0 stop logd");
+ system("stop logd");
usleep(1000000);
// A clean stop like we are testing returns -ENOENT, but in the _real_
@@ -1795,19 +1785,15 @@
std::string content = android::base::StringPrintf(
"__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
- EXPECT_TRUE(
- IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
- content));
+ EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
content = android::base::StringPrintf(
"__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
- EXPECT_TRUE(
- IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
- content));
+ EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
EXPECT_EQ(0, count_matching_ts(ts));
- system((getuid() == AID_ROOT) ? "start logd" : "su 0 start logd");
+ system("start logd");
usleep(1000000);
EXPECT_EQ(0, count_matching_ts(ts));
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 4e4554a..c7dff5a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -15,8 +15,6 @@
"liblog",
"libbase",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"libutils",
"android.hardware.memtrack@1.0",
],
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 62a7266..f20df19 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -105,13 +105,15 @@
cc_test {
name: "memunreachable_binder_test",
defaults: ["libmemunreachable_defaults"],
+ require_root: true,
+
srcs: [
"tests/Binder_test.cpp",
],
static_libs: ["libmemunreachable"],
shared_libs: [
"libbinder",
- "libhwbinder",
+ "libhidlbase",
"libutils",
],
test_suites: ["device-tests"],
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 6d3c057..60d462f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -63,6 +63,10 @@
LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
"Error finding namespace of apex: no dot in apex name %s", caller_location);
std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+ // TODO(b/139408016): Rename the runtime namespace to "art".
+ if (name == "art") {
+ name = "runtime";
+ }
android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
"Error finding namespace of apex: no namespace called %s", name.c_str());
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 3694360..93df1d0 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -49,12 +49,12 @@
constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
-const std::vector<const std::string> kRuntimePublicLibraries = {
+const std::vector<const std::string> kArtApexPublicLibraries = {
"libicuuc.so",
"libicui18n.so",
};
-constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" LIB;
+constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
@@ -182,8 +182,8 @@
// For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
// Unfortunately, it does not have stable C symbols, and default namespace should only use
// stable symbols in libandroidicu.so. http://b/120786417
- for (const std::string& lib_name : kRuntimePublicLibraries) {
- std::string path(kRuntimeApexLibPath);
+ for (const std::string& lib_name : kArtApexPublicLibraries) {
+ std::string path(kArtApexLibPath);
path.append("/").append(lib_name);
struct stat s;
@@ -207,9 +207,9 @@
return android::base::Join(*sonames, ':');
}
-static std::string InitRuntimePublicLibraries() {
- CHECK(sizeof(kRuntimePublicLibraries) > 0);
- std::string list = android::base::Join(kRuntimePublicLibraries, ":");
+static std::string InitArtPublicLibraries() {
+ CHECK(sizeof(kArtApexPublicLibraries) > 0);
+ std::string list = android::base::Join(kArtApexPublicLibraries, ":");
std::string additional_libs = additional_public_libraries();
if (!additional_libs.empty()) {
@@ -277,7 +277,7 @@
}
const std::string& runtime_public_libraries() {
- static std::string list = InitRuntimePublicLibraries();
+ static std::string list = InitArtPublicLibraries();
return list;
}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 15f8139..c83df1a 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -134,15 +134,27 @@
switch (policy) {
case SP_BACKGROUND:
- return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
+ return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"},
+ true)
+ ? 0
+ : -1;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"},
+ true)
+ ? 0
+ : -1;
case SP_TOP_APP:
- return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"},
+ true)
+ ? 0
+ : -1;
case SP_RT_APP:
- return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ return SetTaskProfiles(
+ tid, {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"}, true)
+ ? 0
+ : -1;
default:
return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index b778f92..b1c05ea 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -109,6 +109,11 @@
if (sock < 0) {
ret = -errno;
} else {
+ int sndbuf = 1 * 1024 * 1024; // set max send buffer size 1MB
+ socklen_t bufLen = sizeof(sndbuf);
+ // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
+ // Proceed to connect even setsockopt fails.
+ setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
struct sockaddr_un un;
memset(&un, 0, sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
diff --git a/libsystem/include/system/graphics-base-v1.2.h b/libsystem/include/system/graphics-base-v1.2.h
new file mode 100644
index 0000000..2194f5e
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.2.h
@@ -0,0 +1,31 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.2
+// Location: hardware/interfaces/graphics/common/1.2/
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ HAL_HDR_HDR10_PLUS = 4,
+} android_hdr_v1_2_t;
+
+typedef enum {
+ HAL_DATASPACE_DISPLAY_BT2020 = 142999552 /* ((STANDARD_BT2020 | TRANSFER_SRGB) | RANGE_FULL) */,
+ HAL_DATASPACE_DYNAMIC_DEPTH = 4098 /* 0x1002 */,
+ HAL_DATASPACE_JPEG_APP_SEGMENTS = 4099 /* 0x1003 */,
+ HAL_DATASPACE_HEIF = 4100 /* 0x1004 */,
+} android_dataspace_v1_2_t;
+
+typedef enum {
+ HAL_PIXEL_FORMAT_HSV_888 = 55 /* 0x37 */,
+} android_pixel_format_v1_2_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
index ea92007..92ee077 100644
--- a/libsystem/include/system/graphics-base.h
+++ b/libsystem/include/system/graphics-base.h
@@ -3,5 +3,6 @@
#include "graphics-base-v1.0.h"
#include "graphics-base-v1.1.h"
+#include "graphics-base-v1.2.h"
#endif // SYSTEM_CORE_GRAPHICS_BASE_H_
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 73237e6..da18af6 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -153,12 +153,12 @@
shared_libs: [
"libunwindstack",
],
+ relative_install_path: "libunwindstack_test",
}
-cc_test {
- name: "libunwindstack_test",
+cc_defaults {
+ name: "libunwindstack_testlib_flags",
defaults: ["libunwindstack_flags"],
- isolated: true,
srcs: [
"tests/ArmExidxDecodeTest.cpp",
@@ -183,7 +183,6 @@
"tests/ElfTestUtils.cpp",
"tests/IsolatedSettings.cpp",
"tests/JitDebugTest.cpp",
- "tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetBuildIDTest.cpp",
@@ -211,6 +210,7 @@
"tests/UnwindOfflineTest.cpp",
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
+ "tests/VerifyBionicTerminationTest.cpp",
],
cflags: [
@@ -252,11 +252,28 @@
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
+}
+
+cc_test {
+ name: "libunwindstack_test",
+ defaults: ["libunwindstack_testlib_flags"],
+ isolated: true,
+
+ srcs: [
+ "tests/LocalUnwinderTest.cpp",
+ ],
required: [
"libunwindstack_local",
],
}
+// Skip LocalUnwinderTest until atest understands required properly.
+cc_test {
+ name: "libunwindstack_unit_test",
+ defaults: ["libunwindstack_testlib_flags"],
+ isolated: true,
+}
+
//-------------------------------------------------------------------------
// Tools
//-------------------------------------------------------------------------
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
new file mode 100644
index 0000000..55771c0
--- /dev/null
+++ b/libunwindstack/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libunwindstack_unit_test"
+ }
+ ]
+}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 7556482..0b9b85c 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -309,7 +309,7 @@
}
}
-std::string Unwinder::FormatFrame(const FrameData& frame) {
+std::string Unwinder::FormatFrame(const FrameData& frame) const {
std::string data;
if (regs_->Is32Bit()) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
@@ -355,7 +355,7 @@
return data;
}
-std::string Unwinder::FormatFrame(size_t frame_num) {
+std::string Unwinder::FormatFrame(size_t frame_num) const {
if (frame_num >= frames_.size()) {
return "";
}
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 52b3578..11ad9de 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -77,7 +77,7 @@
void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
- size_t NumFrames() { return frames_.size(); }
+ size_t NumFrames() const { return frames_.size(); }
const std::vector<FrameData>& frames() { return frames_; }
@@ -87,8 +87,8 @@
return frames;
}
- std::string FormatFrame(size_t frame_num);
- std::string FormatFrame(const FrameData& frame);
+ std::string FormatFrame(size_t frame_num) const;
+ std::string FormatFrame(const FrameData& frame) const;
void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
index 56a18cd..9936f7a 100644
--- a/libunwindstack/tests/LocalUnwinderTest.cpp
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -170,10 +170,10 @@
std::string testlib(testing::internal::GetArgvs()[0]);
auto const value = testlib.find_last_of('/');
- if (value == std::string::npos) {
- testlib = "../";
+ if (value != std::string::npos) {
+ testlib = testlib.substr(0, value + 1);
} else {
- testlib = testlib.substr(0, value + 1) + "../";
+ testlib = "";
}
testlib += "libunwindstack_local.so";
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
new file mode 100644
index 0000000..6a3e91a
--- /dev/null
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+
+#if defined(__BIONIC__)
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+// This test is specific to bionic to verify that __libc_init is
+// properly setting the return address to undefined so that the
+// unwind properly terminates.
+
+namespace unwindstack {
+
+static std::string DumpFrames(const UnwinderFromPid& unwinder) {
+ std::string unwind;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ unwind += unwinder.FormatFrame(i) + '\n';
+ }
+ return unwind;
+}
+
+static DwarfLocationEnum GetReturnAddressLocation(uint64_t rel_pc, DwarfSection* section) {
+ if (section == nullptr) {
+ return DWARF_LOCATION_INVALID;
+ }
+
+ const DwarfFde* fde = section->GetFdeFromPc(rel_pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ return DWARF_LOCATION_INVALID;
+ }
+ dwarf_loc_regs_t regs;
+ if (!section->GetCfaLocationInfo(rel_pc, fde, ®s)) {
+ return DWARF_LOCATION_INVALID;
+ }
+
+ auto reg_entry = regs.find(fde->cie->return_address_register);
+ if (reg_entry == regs.end()) {
+ return DWARF_LOCATION_INVALID;
+ }
+ return reg_entry->second.type;
+}
+
+static void VerifyReturnAddress(const FrameData& frame) {
+ // Now go and find information about the register data and verify that the relative pc results in
+ // an undefined register.
+ Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
+ ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name;
+ ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name << " is not valid.";
+ ElfInterface* interface = elf.interface();
+
+ // Only check the eh_frame and the debug_frame since the undefined register
+ // is set using a cfi directive.
+ // Check debug_frame first, then eh_frame since debug_frame always
+ // contains the most specific data.
+ DwarfLocationEnum location = GetReturnAddressLocation(frame.rel_pc, interface->debug_frame());
+ if (location == DWARF_LOCATION_UNDEFINED) {
+ return;
+ }
+
+ location = GetReturnAddressLocation(frame.rel_pc, interface->eh_frame());
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location);
+}
+
+// This test assumes that it starts from the main thread, and that the
+// libc.so on device will include symbols so that function names can
+// be resolved.
+TEST(VerifyBionicTermination, local_terminate) {
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+
+ UnwinderFromPid unwinder(512, getpid());
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ RegsGetLocal(regs.get());
+ unwinder.Unwind();
+ ASSERT_LT(0U, unwinder.NumFrames());
+
+ SCOPED_TRACE(DumpFrames(unwinder));
+
+ // Look for the frame that includes __libc_init, there should only
+ // be one and it should be the last.
+ bool found = false;
+ const std::vector<FrameData>& frames = unwinder.frames();
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ const FrameData& frame = frames[i];
+ if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
+ std::string("libc.so") == basename(frame.map_name.c_str())) {
+ ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
+ ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found) << "Unable to find libc.so:__libc_init frame\n";
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index d0562d9..0cbcac5 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -165,8 +165,8 @@
}
}
-int GetInfo(const char* file, uint64_t pc) {
- Elf elf(Memory::CreateFileMemory(file, pc).release());
+int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
@@ -243,12 +243,14 @@
} // namespace unwindstack
int main(int argc, char** argv) {
- if (argc != 3) {
- printf("Usage: unwind_reg_info ELF_FILE PC\n");
+ if (argc != 3 && argc != 4) {
+ printf("Usage: unwind_reg_info ELF_FILE PC [OFFSET]\n");
printf(" ELF_FILE\n");
printf(" The path to an elf file.\n");
printf(" PC\n");
printf(" The pc for which the register information should be obtained.\n");
+ printf(" OFFSET\n");
+ printf(" Use the offset into the ELF file as the beginning of the elf.\n");
return 1;
}
@@ -270,5 +272,15 @@
return 1;
}
- return unwindstack::GetInfo(argv[1], pc);
+ uint64_t offset = 0;
+ if (argc == 4) {
+ char* end;
+ offset = strtoull(argv[3], &end, 16);
+ if (*end != '\0') {
+ printf("Malformed OFFSET value: %s\n", argv[3]);
+ return 1;
+ }
+ }
+
+ return unwindstack::GetInfo(argv[1], offset, pc);
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 8be4dd0..98921be 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,6 +205,7 @@
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
"String8_test.cpp",
+ "String16_test.cpp",
"StrongPointer_test.cpp",
"Unicode_test.cpp",
"Vector_test.cpp",
@@ -289,3 +290,9 @@
],
shared_libs: ["libutils_test_singleton1"],
}
+
+cc_benchmark {
+ name: "libutils_benchmark",
+ srcs: ["Vector_benchmark.cpp"],
+ shared_libs: ["libutils"],
+}
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 7910c6e..3e703db 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -41,6 +41,7 @@
// The following is OK on Android-supported platforms.
sb->mRefs.store(1, std::memory_order_relaxed);
sb->mSize = size;
+ sb->mClientMetadata = 0;
}
return sb;
}
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index fdf13a9..476c842 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -102,7 +102,12 @@
// Must be sized to preserve correct alignment.
mutable std::atomic<int32_t> mRefs;
size_t mSize;
- uint32_t mReserved[2];
+ uint32_t mReserved;
+public:
+ // mClientMetadata is reserved for client use. It is initialized to 0
+ // and the clients can do whatever they want with it. Note that this is
+ // placed last so that it is adjcent to the buffer allocated.
+ uint32_t mClientMetadata;
};
static_assert(sizeof(SharedBuffer) % 8 == 0
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 818b171..5c3cf32 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,21 +24,21 @@
namespace android {
+static const StaticString16 emptyString(u"");
static inline char16_t* getEmptyString() {
- static SharedBuffer* gEmptyStringBuf = [] {
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
- char16_t* str = static_cast<char16_t*>(buf->data());
- *str = 0;
- return buf;
- }();
-
- gEmptyStringBuf->acquire();
- return static_cast<char16_t*>(gEmptyStringBuf->data());
+ return const_cast<char16_t*>(emptyString.string());
}
// ---------------------------------------------------------------------------
-static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
+void* String16::alloc(size_t size)
+{
+ SharedBuffer* buf = SharedBuffer::alloc(size);
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ return buf;
+}
+
+char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)
{
if (u8len == 0) return getEmptyString();
@@ -49,7 +49,7 @@
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));
if (buf) {
u8cur = (const uint8_t*) u8str;
char16_t* u16str = (char16_t*)buf->data();
@@ -66,13 +66,13 @@
return getEmptyString();
}
-static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
+char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {
if (u16len >= SIZE_MAX / sizeof(char16_t)) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
- SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (buf) {
char16_t* str = (char16_t*)buf->data();
@@ -97,8 +97,8 @@
// having run. In this case we always allocate an empty string. It's less
// efficient than using getEmptyString(), but we assume it's uncommon.
- char16_t* data = static_cast<char16_t*>(
- SharedBuffer::alloc(sizeof(char16_t))->data());
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
+ char16_t* data = static_cast<char16_t*>(buf->data());
data[0] = 0;
mString = data;
}
@@ -106,7 +106,7 @@
String16::String16(const String16& o)
: mString(o.mString)
{
- SharedBuffer::bufferFromData(mString)->acquire();
+ acquire();
}
String16::String16(const String16& o, size_t len, size_t begin)
@@ -136,26 +136,30 @@
String16::~String16()
{
- SharedBuffer::bufferFromData(mString)->release();
+ release();
}
size_t String16::size() const
{
- return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+ if (isStaticString()) {
+ return staticStringSize();
+ } else {
+ return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
+ }
}
void String16::setTo(const String16& other)
{
- SharedBuffer::bufferFromData(other.mString)->acquire();
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = other.mString;
+ acquire();
}
status_t String16::setTo(const String16& other, size_t len, size_t begin)
{
const size_t N = other.size();
if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = getEmptyString();
return OK;
}
@@ -184,8 +188,7 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memmove(str, other, len*sizeof(char16_t));
@@ -212,8 +215,8 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
@@ -238,8 +241,8 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
@@ -273,8 +276,8 @@
len, myLen, String8(chrs, len).string());
#endif
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+len+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
if (pos < myLen) {
@@ -338,23 +341,87 @@
return strstr16(mString, chrs) != nullptr;
}
+void* String16::edit() {
+ SharedBuffer* buf;
+ if (isStaticString()) {
+ buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
+ if (buf) {
+ buf->acquire();
+ memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
+ }
+ } else {
+ buf = SharedBuffer::bufferFromData(mString)->edit();
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ }
+ return buf;
+}
+
+void* String16::editResize(size_t newSize) {
+ SharedBuffer* buf;
+ if (isStaticString()) {
+ size_t copySize = (size() + 1) * sizeof(char16_t);
+ if (newSize < copySize) {
+ copySize = newSize;
+ }
+ buf = static_cast<SharedBuffer*>(alloc(newSize));
+ if (buf) {
+ buf->acquire();
+ memcpy(buf->data(), mString, copySize);
+ }
+ } else {
+ buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ }
+ return buf;
+}
+
+void String16::acquire()
+{
+ if (!isStaticString()) {
+ SharedBuffer::bufferFromData(mString)->acquire();
+ }
+}
+
+void String16::release()
+{
+ if (!isStaticString()) {
+ SharedBuffer::bufferFromData(mString)->release();
+ }
+}
+
+bool String16::isStaticString() const {
+ // See String16.h for notes on the memory layout of String16::StaticData and
+ // SharedBuffer.
+ static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+ const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+ return (*(p - 1) & kIsSharedBufferAllocated) == 0;
+}
+
+size_t String16::staticStringSize() const {
+ // See String16.h for notes on the memory layout of String16::StaticData and
+ // SharedBuffer.
+ static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+ const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+ return static_cast<size_t>(*(p - 1));
+}
+
status_t String16::makeLower()
{
const size_t N = size();
const char16_t* str = string();
- char16_t* edit = nullptr;
+ char16_t* edited = nullptr;
for (size_t i=0; i<N; i++) {
const char16_t v = str[i];
if (v >= 'A' && v <= 'Z') {
- if (!edit) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!edited) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
if (!buf) {
return NO_MEMORY;
}
- edit = (char16_t*)buf->data();
- mString = str = edit;
+ edited = (char16_t*)buf->data();
+ mString = str = edited;
}
- edit[i] = tolower((char)v);
+ edited[i] = tolower((char)v);
}
}
return OK;
@@ -364,18 +431,18 @@
{
const size_t N = size();
const char16_t* str = string();
- char16_t* edit = nullptr;
+ char16_t* edited = nullptr;
for (size_t i=0; i<N; i++) {
if (str[i] == replaceThis) {
- if (!edit) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!edited) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
if (!buf) {
return NO_MEMORY;
}
- edit = (char16_t*)buf->data();
- mString = str = edit;
+ edited = (char16_t*)buf->data();
+ mString = str = edited;
}
- edit[i] = withThis;
+ edited[i] = withThis;
}
}
return OK;
@@ -385,7 +452,7 @@
{
const size_t N = size();
if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = getEmptyString();
return OK;
}
@@ -395,8 +462,7 @@
}
if (begin > 0) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((N+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
if (!buf) {
return NO_MEMORY;
}
@@ -404,8 +470,7 @@
memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
mString = str;
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
str[len] = 0;
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
new file mode 100644
index 0000000..f1f24c3
--- /dev/null
+++ b/libutils/String16_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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/String16.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
+ if (strcmp16(a, b) != 0) {
+ return ::testing::AssertionFailure()
+ << "\"" << String8(a).c_str() << "\" not equal to \"" << String8(b).c_str() << "\"";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))
+
+TEST(String16Test, FromChar16_t) {
+ String16 tmp(u"Verify me");
+ EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromChar16_tSized) {
+ String16 tmp(u"Verify me", 7);
+ EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, FromChar) {
+ String16 tmp("Verify me");
+ EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromCharSized) {
+ String16 tmp("Verify me", 7);
+ EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, Copy) {
+ String16 tmp("Verify me");
+ String16 another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Move) {
+ String16 tmp("Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Size) {
+ String16 tmp("Verify me");
+ EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, setTo) {
+ String16 tmp("Verify me");
+ tmp.setTo(u"New content");
+ EXPECT_EQ(11U, tmp.size());
+ EXPECT_STR16EQ(u"New content", tmp);
+}
+
+TEST(String16Test, Append) {
+ String16 tmp("Verify me");
+ tmp.append(String16("Hello"));
+ EXPECT_EQ(14U, tmp.size());
+ EXPECT_STR16EQ(u"Verify meHello", tmp);
+}
+
+TEST(String16Test, Insert) {
+ String16 tmp("Verify me");
+ tmp.insert(6, u"Insert");
+ EXPECT_EQ(15U, tmp.size());
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+}
+
+TEST(String16Test, Remove) {
+ String16 tmp("Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+}
+
+TEST(String16Test, MakeLower) {
+ String16 tmp("Verify Me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+}
+
+TEST(String16Test, ReplaceAll) {
+ String16 tmp("Verify verify Verify");
+ tmp.replaceAll(u'r', u'!');
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+}
+
+TEST(String16Test, Compare) {
+ String16 tmp("Verify me");
+ EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StaticString) {
+ String16 nonStaticString("NonStatic");
+ StaticString16 staticString(u"Static");
+
+ EXPECT_TRUE(staticString.isStaticString());
+ EXPECT_FALSE(nonStaticString.isStaticString());
+}
+
+TEST(String16Test, StaticStringCopy) {
+ StaticString16 tmp(u"Verify me");
+ String16 another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(tmp.isStaticString());
+ EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringMove) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringSize) {
+ StaticString16 tmp(u"Verify me");
+ EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, StaticStringSetTo) {
+ StaticString16 tmp(u"Verify me");
+ tmp.setTo(u"New content");
+ EXPECT_EQ(11U, tmp.size());
+ EXPECT_STR16EQ(u"New content", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringAppend) {
+ StaticString16 tmp(u"Verify me");
+ tmp.append(String16("Hello"));
+ EXPECT_EQ(14U, tmp.size());
+ EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringInsert) {
+ StaticString16 tmp(u"Verify me");
+ tmp.insert(6, u"Insert");
+ EXPECT_EQ(15U, tmp.size());
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringRemove) {
+ StaticString16 tmp(u"Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringMakeLower) {
+ StaticString16 tmp(u"Verify me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringReplaceAll) {
+ StaticString16 tmp(u"Verify verify Verify");
+ tmp.replaceAll(u'r', u'!');
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringCompare) {
+ StaticString16 tmp(u"Verify me");
+ EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StringSetToStaticString) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(u"nonstatic");
+ another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, StringMoveFromStaticString) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, EmptyStringIsStatic) {
+ String16 tmp("");
+ EXPECT_TRUE(tmp.isStaticString());
+}
+
+} // namespace android
diff --git a/libutils/Vector_benchmark.cpp b/libutils/Vector_benchmark.cpp
new file mode 100644
index 0000000..c23d499
--- /dev/null
+++ b/libutils/Vector_benchmark.cpp
@@ -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.
+ */
+
+#include <benchmark/benchmark.h>
+#include <utils/Vector.h>
+#include <vector>
+
+void BM_fill_android_vector(benchmark::State& state) {
+ android::Vector<char> v;
+ while (state.KeepRunning()) {
+ v.push('A');
+ }
+}
+BENCHMARK(BM_fill_android_vector);
+
+void BM_fill_std_vector(benchmark::State& state) {
+ std::vector<char> v;
+ while (state.KeepRunning()) {
+ v.push_back('A');
+ }
+}
+BENCHMARK(BM_fill_std_vector);
+
+void BM_prepend_android_vector(benchmark::State& state) {
+ android::Vector<char> v;
+ while (state.KeepRunning()) {
+ v.insertAt('A', 0);
+ }
+}
+BENCHMARK(BM_prepend_android_vector);
+
+void BM_prepend_std_vector(benchmark::State& state) {
+ std::vector<char> v;
+ while (state.KeepRunning()) {
+ v.insert(v.begin(), 'A');
+ }
+}
+BENCHMARK(BM_prepend_std_vector);
+
+BENCHMARK_MAIN();
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index afbc2ed..adc3e7d 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -37,13 +37,17 @@
class String8;
+template <size_t N>
+class StaticString16;
+
// DO NOT USE: please use std::u16string
//! This is a string holding UTF-16 characters.
class String16
{
public:
- /* use String16(StaticLinkage) if you're statically linking against
+ /*
+ * Use String16(StaticLinkage) if you're statically linking against
* libutils and declaring an empty static String16, e.g.:
*
* static String16 sAStaticEmptyString(String16::kEmptyString);
@@ -123,8 +127,76 @@
inline operator const char16_t*() const;
-private:
- const char16_t* mString;
+ // Static and non-static String16 behave the same for the users, so
+ // this method isn't of much use for the users. It is public for testing.
+ bool isStaticString() const;
+
+ private:
+ /*
+ * A flag indicating the type of underlying buffer.
+ */
+ static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;
+
+ /*
+ * alloc() returns void* so that SharedBuffer class is not exposed.
+ */
+ static void* alloc(size_t size);
+ static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
+ static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);
+
+ /*
+ * edit() and editResize() return void* so that SharedBuffer class
+ * is not exposed.
+ */
+ void* edit();
+ void* editResize(size_t new_size);
+
+ void acquire();
+ void release();
+
+ size_t staticStringSize() const;
+
+ const char16_t* mString;
+
+protected:
+ /*
+ * Data structure used to allocate static storage for static String16.
+ *
+ * Note that this data structure and SharedBuffer are used interchangably
+ * as the underlying data structure for a String16. Therefore, the layout
+ * of this data structure must match the part in SharedBuffer that is
+ * visible to String16.
+ */
+ template <size_t N>
+ struct StaticData {
+ // The high bit of 'size' is used as a flag.
+ static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
+ constexpr StaticData() : size(N - 1), data{0} {}
+ const uint32_t size;
+ char16_t data[N];
+
+ constexpr StaticData(const StaticData<N>&) = default;
+ };
+
+ /*
+ * Helper function for constructing a StaticData object.
+ */
+ template <size_t N>
+ static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
+ StaticData<N> r;
+ // The 'size' field is at the same location where mClientMetadata would
+ // be for a SharedBuffer. We do NOT set kIsSharedBufferAllocated flag
+ // here.
+ for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
+ return r;
+ }
+
+ template <size_t N>
+ explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+public:
+ template <size_t N>
+ explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
};
// String16 can be trivially moved using memcpy() because moving does not
@@ -132,6 +204,42 @@
ANDROID_TRIVIAL_MOVE_TRAIT(String16)
// ---------------------------------------------------------------------------
+
+/*
+ * A StaticString16 object is a specialized String16 object. Instead of holding
+ * the string data in a ref counted SharedBuffer object, it holds data in a
+ * buffer within StaticString16 itself. Note that this buffer is NOT ref
+ * counted and is assumed to be available for as long as there is at least a
+ * String16 object using it. Therefore, one must be extra careful to NEVER
+ * assign a StaticString16 to a String16 that outlives the StaticString16
+ * object.
+ *
+ * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
+ *
+ * A StaticString16 SHOULD NEVER APPEAR IN APIs. USE String16 INSTEAD.
+ */
+template <size_t N>
+class StaticString16 : public String16 {
+public:
+ constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}
+
+ constexpr StaticString16(const StaticString16<N>& other)
+ : String16(mData), mData(other.mData) {}
+
+ constexpr StaticString16(const StaticString16<N>&&) = delete;
+
+ // There is no reason why one would want to 'new' a StaticString16. Delete
+ // it to discourage misuse.
+ static void* operator new(std::size_t) = delete;
+
+private:
+ const StaticData<N> mData;
+};
+
+template <typename F>
+StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;
+
+// ---------------------------------------------------------------------------
// No user servicable parts below.
inline int compare_type(const String16& lhs, const String16& rhs)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 42af751..d17da12 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1433,8 +1433,8 @@
set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
inc_killcnt(procp->oomadj);
- ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
- taskname, pid, uid, procp->oomadj, tasksize * page_k);
+ ALOGE("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj,
+ tasksize * page_k);
TRACE_KILL_END();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 4559050..df1d929 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -214,7 +214,7 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-# Start of runtime APEX compatibility.
+# Start of i18n and ART APEX compatibility.
#
# Meta-comment:
# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
@@ -226,32 +226,21 @@
# come to rely on them.
# http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# /apex/com.android.i18n/etc/icu so that apps can find the ICU .dat file.
# A symlink can't overwrite a directory and the /system/usr/icu directory once
# existed so the required structure must be created whatever we find.
LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.i18n/etc/icu $(TARGET_OUT)/usr/icu
# TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES := \
- dalvikvm \
- dalvikvm32 \
- dalvikvm64 \
- dex2oat \
- dexdiag \
- dexdump \
- dexlist \
- dexoptanalyzer \
- oatdump \
- profman \
-
+ART_BINARIES := dalvikvm dex2oat
LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
$(foreach b,$(ART_BINARIES), \
$(eval LOCAL_POST_INSTALL_CMD += \
- && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+ && ln -sf /apex/com.android.art/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
)
-# End of runtime APEX compatibilty.
+# End of i18n and ART APEX compatibilty.
ifeq ($(_enforce_vndk_at_runtime),true)
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index f0b1fd2..e598f05 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -85,8 +85,11 @@
# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# Need allow_all_shared_libs because libart.so can dlopen oat files in
# /system/framework and /data.
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index a639592..e1da587 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -166,8 +166,11 @@
# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# Need allow_all_shared_libs because libart.so can dlopen oat files in
# /system/framework and /data.
@@ -187,6 +190,7 @@
namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
namespace.media.links = default,neuralnetworks
namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
@@ -328,7 +332,7 @@
namespace.rs.asan.permitted.paths += /vendor/${LIB}
namespace.rs.asan.permitted.paths += /data
-namespace.rs.links = default,vndk,neuralnetworks
+namespace.rs.links = default,neuralnetworks
namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -336,8 +340,6 @@
# namespace because RS framework libs are using them.
namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
-namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
# LLNDK library moved into apex
namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
@@ -490,8 +492,11 @@
###############################################################################
namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = system
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
@@ -697,8 +702,11 @@
# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
@@ -716,6 +724,7 @@
namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
namespace.media.links = default,neuralnetworks
namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 0bb60ab..4beabd6 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -105,8 +105,11 @@
# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# Need allow_all_shared_libs because libart.so can dlopen oat files in
# /system/framework and /data.
@@ -265,7 +268,7 @@
namespace.rs.asan.permitted.paths += /vendor/${LIB}
namespace.rs.asan.permitted.paths += /data
-namespace.rs.links = default,vndk,neuralnetworks
+namespace.rs.links = default,neuralnetworks
namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -273,8 +276,6 @@
# namespace because RS framework libs are using them.
namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
-namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
# LLNDK library moved into apex
namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
@@ -421,8 +422,11 @@
###############################################################################
namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
@@ -505,8 +509,11 @@
# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
# TODO(b/130340935): Use a dynamically created linker namespace similar to
# classloader-namespace for oat files, and tighten this up.
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index d8f6095..27e855f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
+libamidi.so
libbinder_ndk.so
libc.so
libcamera2ndk.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 20905bf..b565340 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -2,6 +2,7 @@
libandroid.so
libandroidthings.so
libaaudio.so
+libamidi.so
libbinder_ndk.so
libc.so
libcamera2ndk.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 4ece5b5..7cbda08 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
+libamidi.so
libbinder_ndk.so
libc.so
libcamera2ndk.so
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 93b7f43..17f6596 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,7 +5,7 @@
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
- export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+ export ANDROID_RUNTIME_ROOT /apex/com.android.art
export ANDROID_I18N_ROOT /apex/com.android.i18n
export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
export EXTERNAL_STORAGE /sdcard
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0f61a61..afa1a86 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -431,10 +431,6 @@
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
- # Check and mark a successful boot, before mounting userdata with mount_all.
- # No-op for non-A/B device.
- exec_start update_verifier_nonencrypted
-
on post-fs-data
mark_post_data
@@ -536,6 +532,7 @@
mkdir /data/misc/profiles/ref 0771 system system
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
+ mkdir /data/misc/installd 0700 root root
mkdir /data/preloads 0775 system system
@@ -668,16 +665,22 @@
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
@@ -701,6 +704,12 @@
chown root system /sys/module/lowmemorykiller/parameters/minfree
chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
+ # System server manages zram writeback
+ chown root system /sys/block/zram0/idle
+ chmod 0664 /sys/block/zram0/idle
+ chown root system /sys/block/zram0/writeback
+ chmod 0664 /sys/block/zram0/writeback
+
# Tweak background writeout
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
@@ -800,6 +809,8 @@
trigger zygote-start
on property:vold.decrypt=trigger_restart_min_framework
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
@@ -867,6 +878,8 @@
chmod 0773 /data/misc/trace
# Give reads to anyone for the window trace folder on debug builds.
chmod 0775 /data/misc/wmtrace
+
+on init && property:ro.debuggable=1
start console
service flash_recovery /system/bin/install-recovery.sh
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index f0681d2..b6cba90 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -14,7 +14,7 @@
# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
class core
- socket adbd stream 660 system system
+ socket adbd seqpacket 660 system system
disabled
seclabel u:r:adbd:s0
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index f8e680d..bf3fb42 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,7 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
- socket blastula_pool stream 660 root system
+ socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 0235370..1bab588 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,7 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
- socket blastula_pool stream 660 root system
+ socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -20,6 +20,6 @@
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
- socket blastula_pool_secondary stream 660 root system
+ socket usap_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 3f3cc15..6fa210a 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,7 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
- socket blastula_pool stream 660 root system
+ socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index fae38c9..48461ec 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,7 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
- socket blastula_pool stream 660 root system
+ socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -20,6 +20,6 @@
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
- socket blastula_pool_secondary stream 660 root system
+ socket usap_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 2b35819..622de5b 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -315,7 +315,8 @@
PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
}
- while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+ while ((fs_read_atomic_int("/data/misc/installd/layout_version", &fs_version) == -1) ||
+ (fs_version < 3)) {
LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
sleep(1);
}
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 733b60f..cc19481 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,8 +24,6 @@
"libbinder",
"libcutils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"libprotobuf-cpp-lite",
"libsysutils",
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 1666cfb..e553af1 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -42,7 +42,6 @@
"android.hardware.gatekeeper@1.0",
"libbase",
"libhidlbase",
- "libhidltransport",
"libgatekeeper",
"libutils",
"liblog",
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index d107b78..3a9beaf 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -101,7 +101,6 @@
"libutils",
"libhardware",
"libhidlbase",
- "libhidltransport",
"libtrusty",
"libkeymaster_messages",
"libkeymaster3device",
@@ -132,7 +131,6 @@
"libutils",
"libhardware",
"libhidlbase",
- "libhidltransport",
"libtrusty",
"libkeymaster_messages",
"libkeymaster4",
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 3afa7a9..6a339a1 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -5,7 +5,6 @@
shared_libs: [
"libbase",
"libhidlbase",
- "libhidltransport",
"liblog",
"libutils",
"libhardware",